Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_rat.h"
31 : #include "gdal_utils.h"
32 : #include "cpl_safemaths.hpp"
33 : #include "memmultidim.h"
34 : #include "ogrsf_frmts.h"
35 : #include "gdalmultidim_priv.h"
36 :
37 : #if defined(__clang__) || defined(_MSC_VER)
38 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
39 : #endif
40 :
41 : /************************************************************************/
42 : /* GDALMDArrayUnscaled */
43 : /************************************************************************/
44 :
45 : class GDALMDArrayUnscaled final : public GDALPamMDArray
46 : {
47 : private:
48 : std::shared_ptr<GDALMDArray> m_poParent{};
49 : const GDALExtendedDataType m_dt;
50 : bool m_bHasNoData;
51 : const double m_dfScale;
52 : const double m_dfOffset;
53 : std::vector<GByte> m_abyRawNoData{};
54 :
55 : protected:
56 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
57 : double dfScale, double dfOffset,
58 : double dfOverriddenDstNodata, GDALDataType eDT)
59 26 : : GDALAbstractMDArray(std::string(),
60 26 : "Unscaled view of " + poParent->GetFullName()),
61 : GDALPamMDArray(
62 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
63 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
64 13 : m_poParent(std::move(poParent)),
65 : m_dt(GDALExtendedDataType::Create(eDT)),
66 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
67 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
68 : {
69 13 : m_abyRawNoData.resize(m_dt.GetSize());
70 : const auto eNonComplexDT =
71 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
72 26 : GDALCopyWords64(
73 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
74 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
75 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
76 13 : }
77 :
78 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
79 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
80 : const GDALExtendedDataType &bufferDataType,
81 : void *pDstBuffer) const override;
82 :
83 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
84 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
85 : const GDALExtendedDataType &bufferDataType,
86 : const void *pSrcBuffer) override;
87 :
88 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
89 : CSLConstList papszOptions) const override
90 : {
91 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
92 : }
93 :
94 : public:
95 : static std::shared_ptr<GDALMDArrayUnscaled>
96 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
97 : double dfOffset, double dfDstNodata, GDALDataType eDT)
98 : {
99 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
100 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
101 13 : newAr->SetSelf(newAr);
102 13 : return newAr;
103 : }
104 :
105 1 : bool IsWritable() const override
106 : {
107 1 : return m_poParent->IsWritable();
108 : }
109 :
110 15 : const std::string &GetFilename() const override
111 : {
112 15 : return m_poParent->GetFilename();
113 : }
114 :
115 : const std::vector<std::shared_ptr<GDALDimension>> &
116 220 : GetDimensions() const override
117 : {
118 220 : return m_poParent->GetDimensions();
119 : }
120 :
121 103 : const GDALExtendedDataType &GetDataType() const override
122 : {
123 103 : return m_dt;
124 : }
125 :
126 1 : const std::string &GetUnit() const override
127 : {
128 1 : return m_poParent->GetUnit();
129 : }
130 :
131 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
132 : {
133 1 : return m_poParent->GetSpatialRef();
134 : }
135 :
136 6 : const void *GetRawNoDataValue() const override
137 : {
138 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
139 : }
140 :
141 1 : bool SetRawNoDataValue(const void *pRawNoData) override
142 : {
143 1 : m_bHasNoData = true;
144 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
145 1 : return true;
146 : }
147 :
148 4 : std::vector<GUInt64> GetBlockSize() const override
149 : {
150 4 : return m_poParent->GetBlockSize();
151 : }
152 :
153 : std::shared_ptr<GDALAttribute>
154 0 : GetAttribute(const std::string &osName) const override
155 : {
156 0 : return m_poParent->GetAttribute(osName);
157 : }
158 :
159 : std::vector<std::shared_ptr<GDALAttribute>>
160 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
161 : {
162 1 : return m_poParent->GetAttributes(papszOptions);
163 : }
164 :
165 0 : bool SetUnit(const std::string &osUnit) override
166 : {
167 0 : return m_poParent->SetUnit(osUnit);
168 : }
169 :
170 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
171 : {
172 0 : return m_poParent->SetSpatialRef(poSRS);
173 : }
174 :
175 : std::shared_ptr<GDALAttribute>
176 1 : CreateAttribute(const std::string &osName,
177 : const std::vector<GUInt64> &anDimensions,
178 : const GDALExtendedDataType &oDataType,
179 : CSLConstList papszOptions = nullptr) override
180 : {
181 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
182 1 : papszOptions);
183 : }
184 : };
185 :
186 : /************************************************************************/
187 : /* ~GDALIHasAttribute() */
188 : /************************************************************************/
189 :
190 : GDALIHasAttribute::~GDALIHasAttribute() = default;
191 :
192 : /************************************************************************/
193 : /* GetAttribute() */
194 : /************************************************************************/
195 :
196 : /** Return an attribute by its name.
197 : *
198 : * If the attribute does not exist, nullptr should be silently returned.
199 : *
200 : * @note Driver implementation: this method will fallback to
201 : * GetAttributeFromAttributes() is not explicitly implemented
202 : *
203 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
204 : *
205 : * This is the same as the C function GDALGroupGetAttribute() or
206 : * GDALMDArrayGetAttribute().
207 : *
208 : * @param osName Attribute name
209 : * @return the attribute, or nullptr if it does not exist or an error occurred.
210 : */
211 : std::shared_ptr<GDALAttribute>
212 1057 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
213 : {
214 1057 : return GetAttributeFromAttributes(osName);
215 : }
216 :
217 : /************************************************************************/
218 : /* GetAttributeFromAttributes() */
219 : /************************************************************************/
220 :
221 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
222 : */
223 : std::shared_ptr<GDALAttribute>
224 1057 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
225 : {
226 2114 : auto attrs(GetAttributes());
227 5493 : for (const auto &attr : attrs)
228 : {
229 5174 : if (attr->GetName() == osName)
230 738 : return attr;
231 : }
232 319 : return nullptr;
233 : }
234 :
235 : /************************************************************************/
236 : /* GetAttributes() */
237 : /************************************************************************/
238 :
239 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
240 : *
241 : * If the attribute does not exist, nullptr should be silently returned.
242 : *
243 : * @note Driver implementation: optionally implemented. If implemented,
244 : * GetAttribute() should also be implemented.
245 : *
246 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
247 : *
248 : * This is the same as the C function GDALGroupGetAttributes() or
249 : * GDALMDArrayGetAttributes().
250 :
251 : * @param papszOptions Driver specific options determining how attributes
252 : * should be retrieved. Pass nullptr for default behavior.
253 : *
254 : * @return the attributes.
255 : */
256 : std::vector<std::shared_ptr<GDALAttribute>>
257 44 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
258 : {
259 44 : return {};
260 : }
261 :
262 : /************************************************************************/
263 : /* CreateAttribute() */
264 : /************************************************************************/
265 :
266 : /** Create an attribute within a GDALMDArray or GDALGroup.
267 : *
268 : * The attribute might not be "physically" created until a value is written
269 : * into it.
270 : *
271 : * Optionally implemented.
272 : *
273 : * Drivers known to implement it: MEM, netCDF
274 : *
275 : * This is the same as the C function GDALGroupCreateAttribute() or
276 : * GDALMDArrayCreateAttribute()
277 : *
278 : * @param osName Attribute name.
279 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
280 : * dimension first to the fastest varying dimension last.
281 : * Empty for a scalar attribute (common case)
282 : * @param oDataType Attribute data type.
283 : * @param papszOptions Driver specific options determining how the attribute.
284 : * should be created.
285 : *
286 : * @return the new attribute, or nullptr if case of error
287 : */
288 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
289 : CPL_UNUSED const std::string &osName,
290 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
291 : CPL_UNUSED const GDALExtendedDataType &oDataType,
292 : CPL_UNUSED CSLConstList papszOptions)
293 : {
294 0 : CPLError(CE_Failure, CPLE_NotSupported,
295 : "CreateAttribute() not implemented");
296 0 : return nullptr;
297 : }
298 :
299 : /************************************************************************/
300 : /* DeleteAttribute() */
301 : /************************************************************************/
302 :
303 : /** Delete an attribute from a GDALMDArray or GDALGroup.
304 : *
305 : * Optionally implemented.
306 : *
307 : * After this call, if a previously obtained instance of the deleted object
308 : * is still alive, no method other than for freeing it should be invoked.
309 : *
310 : * Drivers known to implement it: MEM, netCDF
311 : *
312 : * This is the same as the C function GDALGroupDeleteAttribute() or
313 : * GDALMDArrayDeleteAttribute()
314 : *
315 : * @param osName Attribute name.
316 : * @param papszOptions Driver specific options determining how the attribute.
317 : * should be deleted.
318 : *
319 : * @return true in case of success
320 : * @since GDAL 3.8
321 : */
322 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
323 : CPL_UNUSED CSLConstList papszOptions)
324 : {
325 0 : CPLError(CE_Failure, CPLE_NotSupported,
326 : "DeleteAttribute() not implemented");
327 0 : return false;
328 : }
329 :
330 : /************************************************************************/
331 : /* GDALGroup() */
332 : /************************************************************************/
333 :
334 : //! @cond Doxygen_Suppress
335 6963 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
336 6963 : const std::string &osContext)
337 6963 : : m_osName(osParentName.empty() ? "/" : osName),
338 : m_osFullName(
339 13926 : !osParentName.empty()
340 10784 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
341 : : "/"),
342 17747 : m_osContext(osContext)
343 : {
344 6963 : }
345 :
346 : //! @endcond
347 :
348 : /************************************************************************/
349 : /* ~GDALGroup() */
350 : /************************************************************************/
351 :
352 : GDALGroup::~GDALGroup() = default;
353 :
354 : /************************************************************************/
355 : /* GetMDArrayNames() */
356 : /************************************************************************/
357 :
358 : /** Return the list of multidimensional array names contained in this group.
359 : *
360 : * @note Driver implementation: optionally implemented. If implemented,
361 : * OpenMDArray() should also be implemented.
362 : *
363 : * Drivers known to implement it: MEM, netCDF.
364 : *
365 : * This is the same as the C function GDALGroupGetMDArrayNames().
366 : *
367 : * @param papszOptions Driver specific options determining how arrays
368 : * should be retrieved. Pass nullptr for default behavior.
369 : *
370 : * @return the array names.
371 : */
372 : std::vector<std::string>
373 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
374 : {
375 0 : return {};
376 : }
377 :
378 : /************************************************************************/
379 : /* GetMDArrayFullNamesRecursive() */
380 : /************************************************************************/
381 :
382 : /** Return the list of multidimensional array full names contained in this
383 : * group and its subgroups.
384 : *
385 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
386 : *
387 : * @param papszGroupOptions Driver specific options determining how groups
388 : * should be retrieved. Pass nullptr for default behavior.
389 : * @param papszArrayOptions Driver specific options determining how arrays
390 : * should be retrieved. Pass nullptr for default behavior.
391 : *
392 : * @return the array full names.
393 : *
394 : * @since 3.11
395 : */
396 : std::vector<std::string>
397 3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
398 : CSLConstList papszArrayOptions) const
399 : {
400 3 : std::vector<std::string> ret;
401 6 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
402 3 : stackGroups.push_back(nullptr); // nullptr means this
403 9 : while (!stackGroups.empty())
404 : {
405 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
406 6 : stackGroups.erase(stackGroups.begin());
407 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
408 10 : for (const std::string &arrayName :
409 26 : poCurGroup->GetMDArrayNames(papszArrayOptions))
410 : {
411 20 : std::string osFullName = poCurGroup->GetFullName();
412 10 : if (!osFullName.empty() && osFullName.back() != '/')
413 3 : osFullName += '/';
414 10 : osFullName += arrayName;
415 10 : ret.push_back(std::move(osFullName));
416 : }
417 6 : auto insertionPoint = stackGroups.begin();
418 3 : for (const auto &osSubGroup :
419 12 : poCurGroup->GetGroupNames(papszGroupOptions))
420 : {
421 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
422 3 : if (poSubGroup)
423 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
424 : }
425 : }
426 :
427 6 : return ret;
428 : }
429 :
430 : /************************************************************************/
431 : /* OpenMDArray() */
432 : /************************************************************************/
433 :
434 : /** Open and return a multidimensional array.
435 : *
436 : * @note Driver implementation: optionally implemented. If implemented,
437 : * GetMDArrayNames() should also be implemented.
438 : *
439 : * Drivers known to implement it: MEM, netCDF.
440 : *
441 : * This is the same as the C function GDALGroupOpenMDArray().
442 : *
443 : * @param osName Array name.
444 : * @param papszOptions Driver specific options determining how the array should
445 : * be opened. Pass nullptr for default behavior.
446 : *
447 : * @return the array, or nullptr.
448 : */
449 : std::shared_ptr<GDALMDArray>
450 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
451 : CPL_UNUSED CSLConstList papszOptions) const
452 : {
453 0 : return nullptr;
454 : }
455 :
456 : /************************************************************************/
457 : /* GetGroupNames() */
458 : /************************************************************************/
459 :
460 : /** Return the list of sub-groups contained in this group.
461 : *
462 : * @note Driver implementation: optionally implemented. If implemented,
463 : * OpenGroup() should also be implemented.
464 : *
465 : * Drivers known to implement it: MEM, netCDF.
466 : *
467 : * This is the same as the C function GDALGroupGetGroupNames().
468 : *
469 : * @param papszOptions Driver specific options determining how groups
470 : * should be retrieved. Pass nullptr for default behavior.
471 : *
472 : * @return the group names.
473 : */
474 : std::vector<std::string>
475 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
476 : {
477 4 : return {};
478 : }
479 :
480 : /************************************************************************/
481 : /* OpenGroup() */
482 : /************************************************************************/
483 :
484 : /** Open and return a sub-group.
485 : *
486 : * @note Driver implementation: optionally implemented. If implemented,
487 : * GetGroupNames() should also be implemented.
488 : *
489 : * Drivers known to implement it: MEM, netCDF.
490 : *
491 : * This is the same as the C function GDALGroupOpenGroup().
492 : *
493 : * @param osName Sub-group name.
494 : * @param papszOptions Driver specific options determining how the sub-group
495 : * should be opened. Pass nullptr for default behavior.
496 : *
497 : * @return the group, or nullptr.
498 : */
499 : std::shared_ptr<GDALGroup>
500 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
501 : CPL_UNUSED CSLConstList papszOptions) const
502 : {
503 4 : return nullptr;
504 : }
505 :
506 : /************************************************************************/
507 : /* GetVectorLayerNames() */
508 : /************************************************************************/
509 :
510 : /** Return the list of layer names contained in this group.
511 : *
512 : * @note Driver implementation: optionally implemented. If implemented,
513 : * OpenVectorLayer() should also be implemented.
514 : *
515 : * Drivers known to implement it: OpenFileGDB, FileGDB
516 : *
517 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
518 : * GDALDataset::GetLayer() should then be used.
519 : *
520 : * This is the same as the C function GDALGroupGetVectorLayerNames().
521 : *
522 : * @param papszOptions Driver specific options determining how layers
523 : * should be retrieved. Pass nullptr for default behavior.
524 : *
525 : * @return the vector layer names.
526 : * @since GDAL 3.4
527 : */
528 : std::vector<std::string>
529 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
530 : {
531 1 : return {};
532 : }
533 :
534 : /************************************************************************/
535 : /* OpenVectorLayer() */
536 : /************************************************************************/
537 :
538 : /** Open and return a vector layer.
539 : *
540 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
541 : * lifetime of the returned OGRLayer* is linked to the one of the owner
542 : * dataset (contrary to the general design of this class where objects can be
543 : * used independently of the object that returned them)
544 : *
545 : * @note Driver implementation: optionally implemented. If implemented,
546 : * GetVectorLayerNames() should also be implemented.
547 : *
548 : * Drivers known to implement it: MEM, netCDF.
549 : *
550 : * This is the same as the C function GDALGroupOpenVectorLayer().
551 : *
552 : * @param osName Vector layer name.
553 : * @param papszOptions Driver specific options determining how the layer should
554 : * be opened. Pass nullptr for default behavior.
555 : *
556 : * @return the group, or nullptr.
557 : */
558 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
559 : CPL_UNUSED CSLConstList papszOptions) const
560 : {
561 2 : return nullptr;
562 : }
563 :
564 : /************************************************************************/
565 : /* GetDimensions() */
566 : /************************************************************************/
567 :
568 : /** Return the list of dimensions contained in this group and used by its
569 : * arrays.
570 : *
571 : * This is for dimensions that can potentially be used by several arrays.
572 : * Not all drivers might implement this. To retrieve the dimensions used by
573 : * a specific array, use GDALMDArray::GetDimensions().
574 : *
575 : * Drivers known to implement it: MEM, netCDF
576 : *
577 : * This is the same as the C function GDALGroupGetDimensions().
578 : *
579 : * @param papszOptions Driver specific options determining how groups
580 : * should be retrieved. Pass nullptr for default behavior.
581 : *
582 : * @return the dimensions.
583 : */
584 : std::vector<std::shared_ptr<GDALDimension>>
585 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
586 : {
587 11 : return {};
588 : }
589 :
590 : /************************************************************************/
591 : /* GetStructuralInfo() */
592 : /************************************************************************/
593 :
594 : /** Return structural information on the group.
595 : *
596 : * This may be the compression, etc..
597 : *
598 : * The return value should not be freed and is valid until GDALGroup is
599 : * released or this function called again.
600 : *
601 : * This is the same as the C function GDALGroupGetStructuralInfo().
602 : */
603 42 : CSLConstList GDALGroup::GetStructuralInfo() const
604 : {
605 42 : return nullptr;
606 : }
607 :
608 : /************************************************************************/
609 : /* CreateGroup() */
610 : /************************************************************************/
611 :
612 : /** Create a sub-group within a group.
613 : *
614 : * Optionally implemented by drivers.
615 : *
616 : * Drivers known to implement it: MEM, netCDF
617 : *
618 : * This is the same as the C function GDALGroupCreateGroup().
619 : *
620 : * @param osName Sub-group name.
621 : * @param papszOptions Driver specific options determining how the sub-group
622 : * should be created.
623 : *
624 : * @return the new sub-group, or nullptr in case of error.
625 : */
626 : std::shared_ptr<GDALGroup>
627 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
628 : CPL_UNUSED CSLConstList papszOptions)
629 : {
630 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
631 0 : return nullptr;
632 : }
633 :
634 : /************************************************************************/
635 : /* DeleteGroup() */
636 : /************************************************************************/
637 :
638 : /** Delete a sub-group from a group.
639 : *
640 : * Optionally implemented.
641 : *
642 : * After this call, if a previously obtained instance of the deleted object
643 : * is still alive, no method other than for freeing it should be invoked.
644 : *
645 : * Drivers known to implement it: MEM, Zarr
646 : *
647 : * This is the same as the C function GDALGroupDeleteGroup().
648 : *
649 : * @param osName Sub-group name.
650 : * @param papszOptions Driver specific options determining how the group.
651 : * should be deleted.
652 : *
653 : * @return true in case of success
654 : * @since GDAL 3.8
655 : */
656 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
657 : CPL_UNUSED CSLConstList papszOptions)
658 : {
659 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
660 0 : return false;
661 : }
662 :
663 : /************************************************************************/
664 : /* CreateDimension() */
665 : /************************************************************************/
666 :
667 : /** Create a dimension within a group.
668 : *
669 : * @note Driver implementation: drivers supporting CreateDimension() should
670 : * implement this method, but do not have necessarily to implement
671 : * GDALGroup::GetDimensions().
672 : *
673 : * Drivers known to implement it: MEM, netCDF
674 : *
675 : * This is the same as the C function GDALGroupCreateDimension().
676 : *
677 : * @param osName Dimension name.
678 : * @param osType Dimension type (might be empty, and ignored by drivers)
679 : * @param osDirection Dimension direction (might be empty, and ignored by
680 : * drivers)
681 : * @param nSize Number of values indexed by this dimension. Should be > 0.
682 : * @param papszOptions Driver specific options determining how the dimension
683 : * should be created.
684 : *
685 : * @return the new dimension, or nullptr if case of error
686 : */
687 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
688 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
689 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
690 : CPL_UNUSED CSLConstList papszOptions)
691 : {
692 0 : CPLError(CE_Failure, CPLE_NotSupported,
693 : "CreateDimension() not implemented");
694 0 : return nullptr;
695 : }
696 :
697 : /************************************************************************/
698 : /* CreateMDArray() */
699 : /************************************************************************/
700 :
701 : /** Create a multidimensional array within a group.
702 : *
703 : * It is recommended that the GDALDimension objects passed in aoDimensions
704 : * belong to this group, either by retrieving them with GetDimensions()
705 : * or creating a new one with CreateDimension().
706 : *
707 : * Optionally implemented.
708 : *
709 : * Drivers known to implement it: MEM, netCDF
710 : *
711 : * This is the same as the C function GDALGroupCreateMDArray().
712 : *
713 : * @note Driver implementation: drivers should take into account the possibility
714 : * that GDALDimension object passed in aoDimensions might belong to a different
715 : * group / dataset / driver and act accordingly.
716 : *
717 : * @param osName Array name.
718 : * @param aoDimensions List of dimensions, ordered from the slowest varying
719 : * dimension first to the fastest varying dimension last.
720 : * Might be empty for a scalar array (if supported by
721 : * driver)
722 : * @param oDataType Array data type.
723 : * @param papszOptions Driver specific options determining how the array
724 : * should be created.
725 : *
726 : * @return the new array, or nullptr in case of error
727 : */
728 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
729 : CPL_UNUSED const std::string &osName,
730 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
731 : CPL_UNUSED const GDALExtendedDataType &oDataType,
732 : CPL_UNUSED CSLConstList papszOptions)
733 : {
734 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
735 0 : return nullptr;
736 : }
737 :
738 : /************************************************************************/
739 : /* DeleteMDArray() */
740 : /************************************************************************/
741 :
742 : /** Delete an array from a group.
743 : *
744 : * Optionally implemented.
745 : *
746 : * After this call, if a previously obtained instance of the deleted object
747 : * is still alive, no method other than for freeing it should be invoked.
748 : *
749 : * Drivers known to implement it: MEM, Zarr
750 : *
751 : * This is the same as the C function GDALGroupDeleteMDArray().
752 : *
753 : * @param osName Arrayname.
754 : * @param papszOptions Driver specific options determining how the array.
755 : * should be deleted.
756 : *
757 : * @return true in case of success
758 : * @since GDAL 3.8
759 : */
760 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
761 : CPL_UNUSED CSLConstList papszOptions)
762 : {
763 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
764 0 : return false;
765 : }
766 :
767 : /************************************************************************/
768 : /* GetTotalCopyCost() */
769 : /************************************************************************/
770 :
771 : /** Return a total "cost" to copy the group.
772 : *
773 : * Used as a parameter for CopFrom()
774 : */
775 26 : GUInt64 GDALGroup::GetTotalCopyCost() const
776 : {
777 26 : GUInt64 nCost = COPY_COST;
778 26 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
779 :
780 52 : auto groupNames = GetGroupNames();
781 32 : for (const auto &name : groupNames)
782 : {
783 12 : auto subGroup = OpenGroup(name);
784 6 : if (subGroup)
785 : {
786 6 : nCost += subGroup->GetTotalCopyCost();
787 : }
788 : }
789 :
790 26 : auto arrayNames = GetMDArrayNames();
791 73 : for (const auto &name : arrayNames)
792 : {
793 94 : auto array = OpenMDArray(name);
794 47 : if (array)
795 : {
796 47 : nCost += array->GetTotalCopyCost();
797 : }
798 : }
799 52 : return nCost;
800 : }
801 :
802 : /************************************************************************/
803 : /* CopyFrom() */
804 : /************************************************************************/
805 :
806 : /** Copy the content of a group into a new (generally empty) group.
807 : *
808 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
809 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
810 : * of some output drivers this is not recommended)
811 : * @param poSrcGroup Source group. Must NOT be nullptr.
812 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
813 : * stop the copy. In relaxed mode, the copy will be attempted to
814 : * be pursued.
815 : * @param nCurCost Should be provided as a variable initially set to 0.
816 : * @param nTotalCost Total cost from GetTotalCopyCost().
817 : * @param pfnProgress Progress callback, or nullptr.
818 : * @param pProgressData Progress user data, or nulptr.
819 : * @param papszOptions Creation options. Currently, only array creation
820 : * options are supported. They must be prefixed with
821 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
822 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
823 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
824 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
825 : * Restriction to arrays of a given name is done with adding
826 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
827 : * a full qualified name.
828 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
829 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
830 : * scaled to UInt16 with scale and offset values being computed from the minimum
831 : * and maximum of the source array. The integer data type used can be set with
832 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
833 : *
834 : * @return true in case of success (or partial success if bStrict == false).
835 : */
836 26 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
837 : GDALDataset *poSrcDS,
838 : const std::shared_ptr<GDALGroup> &poSrcGroup,
839 : bool bStrict, GUInt64 &nCurCost,
840 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
841 : void *pProgressData, CSLConstList papszOptions)
842 : {
843 26 : if (pfnProgress == nullptr)
844 0 : pfnProgress = GDALDummyProgress;
845 :
846 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
847 : if (!(x)) \
848 : { \
849 : if (bStrict) \
850 : return false; \
851 : continue; \
852 : } \
853 : (void)0
854 :
855 : try
856 : {
857 26 : nCurCost += GDALGroup::COPY_COST;
858 :
859 52 : const auto srcDims = poSrcGroup->GetDimensions();
860 : std::map<std::string, std::shared_ptr<GDALDimension>>
861 52 : mapExistingDstDims;
862 52 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
863 65 : for (const auto &dim : srcDims)
864 : {
865 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
866 39 : dim->GetDirection(), dim->GetSize());
867 39 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
868 39 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
869 78 : auto poIndexingVarSrc(dim->GetIndexingVariable());
870 39 : if (poIndexingVarSrc)
871 : {
872 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
873 20 : ->GetName()] =
874 40 : dim->GetName();
875 : }
876 : }
877 :
878 52 : auto attrs = poSrcGroup->GetAttributes();
879 36 : for (const auto &attr : attrs)
880 : {
881 : auto dstAttr =
882 10 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
883 20 : attr->GetDataType());
884 10 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
885 10 : auto raw(attr->ReadAsRaw());
886 10 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
887 0 : return false;
888 : }
889 26 : if (!attrs.empty())
890 : {
891 6 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
892 6 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
893 0 : return false;
894 : }
895 :
896 : const auto CopyArray =
897 47 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
898 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
899 : papszOptions, bStrict, &nCurCost,
900 424 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
901 : {
902 : // Map source dimensions to target dimensions
903 94 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
904 47 : const auto &srcArrayDims(srcArray->GetDimensions());
905 120 : for (const auto &dim : srcArrayDims)
906 : {
907 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
908 73 : dim->GetFullName());
909 73 : if (dstDim && dstDim->GetSize() == dim->GetSize())
910 : {
911 63 : dstArrayDims.emplace_back(dstDim);
912 : }
913 : else
914 : {
915 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
916 19 : if (oIter != mapExistingDstDims.end() &&
917 9 : oIter->second->GetSize() == dim->GetSize())
918 : {
919 8 : dstArrayDims.emplace_back(oIter->second);
920 : }
921 : else
922 : {
923 2 : std::string newDimName;
924 2 : if (oIter == mapExistingDstDims.end())
925 : {
926 1 : newDimName = dim->GetName();
927 : }
928 : else
929 : {
930 1 : std::string newDimNamePrefix(srcArray->GetName() +
931 3 : '_' + dim->GetName());
932 1 : newDimName = newDimNamePrefix;
933 1 : int nIterCount = 2;
934 0 : while (
935 1 : cpl::contains(mapExistingDstDims, newDimName))
936 : {
937 0 : newDimName = newDimNamePrefix +
938 0 : CPLSPrintf("_%d", nIterCount);
939 0 : nIterCount++;
940 : }
941 : }
942 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
943 : dim->GetDirection(),
944 4 : dim->GetSize());
945 2 : if (!dstDim)
946 0 : return false;
947 2 : mapExistingDstDims[newDimName] = dstDim;
948 2 : dstArrayDims.emplace_back(dstDim);
949 : }
950 : }
951 : }
952 :
953 94 : CPLStringList aosArrayCO;
954 47 : bool bAutoScale = false;
955 47 : GDALDataType eAutoScaleType = GDT_UInt16;
956 54 : for (const char *pszItem : cpl::Iterate(papszOptions))
957 : {
958 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
959 : {
960 7 : const char *pszOption = pszItem + strlen("ARRAY:");
961 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
962 : {
963 1 : const char *pszNext = strchr(pszOption, ':');
964 1 : if (pszNext != nullptr)
965 : {
966 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
967 1 : if (static_cast<size_t>(nDim) ==
968 1 : dstArrayDims.size())
969 : {
970 1 : pszOption = pszNext + 1;
971 : }
972 : else
973 : {
974 0 : pszOption = nullptr;
975 : }
976 : }
977 : }
978 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
979 : {
980 2 : const char *pszName = pszOption + strlen("IF(NAME=");
981 2 : const char *pszNext = strchr(pszName, ':');
982 2 : if (pszNext != nullptr && pszNext > pszName &&
983 2 : pszNext[-1] == ')')
984 : {
985 4 : CPLString osName;
986 2 : osName.assign(pszName, pszNext - pszName - 1);
987 3 : if (osName == srcArray->GetName() ||
988 1 : osName == srcArray->GetFullName())
989 : {
990 2 : pszOption = pszNext + 1;
991 : }
992 : else
993 : {
994 0 : pszOption = nullptr;
995 : }
996 : }
997 : }
998 7 : if (pszOption)
999 : {
1000 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1001 : {
1002 : bAutoScale =
1003 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1004 : }
1005 5 : else if (STARTS_WITH_CI(pszOption,
1006 : "AUTOSCALE_DATA_TYPE="))
1007 : {
1008 1 : const char *pszDataType =
1009 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1010 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1011 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1012 1 : GDALDataTypeIsFloating(eAutoScaleType))
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_NotSupported,
1015 : "Unsupported value for "
1016 : "AUTOSCALE_DATA_TYPE");
1017 0 : return false;
1018 : }
1019 : }
1020 : else
1021 : {
1022 4 : aosArrayCO.AddString(pszOption);
1023 : }
1024 : }
1025 : }
1026 : }
1027 :
1028 : auto oIterDimName =
1029 47 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1030 47 : const auto &srcArrayType = srcArray->GetDataType();
1031 :
1032 47 : std::shared_ptr<GDALMDArray> dstArray;
1033 :
1034 : // Only autoscale non-indexing variables
1035 47 : bool bHasOffset = false;
1036 47 : bool bHasScale = false;
1037 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1038 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1039 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1040 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1041 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1042 51 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1043 49 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1044 : {
1045 2 : constexpr bool bApproxOK = false;
1046 2 : constexpr bool bForce = true;
1047 2 : double dfMin = 0.0;
1048 2 : double dfMax = 0.0;
1049 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1050 : nullptr, nullptr, nullptr, nullptr,
1051 2 : nullptr) != CE_None)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "Could not retrieve statistics for array %s",
1055 0 : srcArray->GetName().c_str());
1056 0 : return false;
1057 : }
1058 2 : double dfDTMin = 0;
1059 2 : double dfDTMax = 0;
1060 : #define setDTMinMax(ctype) \
1061 : do \
1062 : { \
1063 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1064 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1065 : } while (0)
1066 :
1067 2 : switch (eAutoScaleType)
1068 : {
1069 0 : case GDT_Byte:
1070 0 : setDTMinMax(GByte);
1071 0 : break;
1072 0 : case GDT_Int8:
1073 0 : setDTMinMax(GInt8);
1074 0 : break;
1075 1 : case GDT_UInt16:
1076 1 : setDTMinMax(GUInt16);
1077 1 : break;
1078 1 : case GDT_Int16:
1079 1 : setDTMinMax(GInt16);
1080 1 : break;
1081 0 : case GDT_UInt32:
1082 0 : setDTMinMax(GUInt32);
1083 0 : break;
1084 0 : case GDT_Int32:
1085 0 : setDTMinMax(GInt32);
1086 0 : break;
1087 0 : case GDT_UInt64:
1088 0 : setDTMinMax(std::uint64_t);
1089 0 : break;
1090 0 : case GDT_Int64:
1091 0 : setDTMinMax(std::int64_t);
1092 0 : break;
1093 0 : case GDT_Float16:
1094 : case GDT_Float32:
1095 : case GDT_Float64:
1096 : case GDT_Unknown:
1097 : case GDT_CInt16:
1098 : case GDT_CInt32:
1099 : case GDT_CFloat16:
1100 : case GDT_CFloat32:
1101 : case GDT_CFloat64:
1102 : case GDT_TypeCount:
1103 0 : CPLAssert(false);
1104 : }
1105 :
1106 : dstArray =
1107 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1108 4 : GDALExtendedDataType::Create(eAutoScaleType),
1109 4 : aosArrayCO.List());
1110 2 : if (!dstArray)
1111 0 : return !bStrict;
1112 :
1113 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1114 : {
1115 : // If there's a nodata value in the source array, reserve
1116 : // DTMax for that purpose in the target scaled array
1117 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AppDefined,
1120 : "Cannot set nodata value");
1121 0 : return false;
1122 : }
1123 1 : dfDTMax--;
1124 : }
1125 2 : const double dfScale =
1126 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1127 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1128 :
1129 4 : if (!dstArray->SetOffset(dfOffset) ||
1130 2 : !dstArray->SetScale(dfScale))
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Cannot set scale/offset");
1134 0 : return false;
1135 : }
1136 :
1137 2 : auto poUnscaled = dstArray->GetUnscaled();
1138 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1139 : {
1140 1 : poUnscaled->SetNoDataValue(
1141 : srcArray->GetNoDataValueAsDouble());
1142 : }
1143 :
1144 : // Copy source array into unscaled array
1145 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1146 : nCurCost, nTotalCost, pfnProgress,
1147 2 : pProgressData))
1148 : {
1149 0 : return false;
1150 : }
1151 : }
1152 : else
1153 : {
1154 90 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1155 90 : srcArrayType, aosArrayCO.List());
1156 45 : if (!dstArray)
1157 0 : return !bStrict;
1158 :
1159 90 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1160 : nCurCost, nTotalCost, pfnProgress,
1161 45 : pProgressData))
1162 : {
1163 0 : return false;
1164 : }
1165 : }
1166 :
1167 : // If this array is the indexing variable of a dimension, link them
1168 : // together.
1169 47 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1170 : {
1171 : auto oCorrespondingDimIter =
1172 20 : mapExistingDstDims.find(oIterDimName->second);
1173 20 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1174 : {
1175 : CPLErrorStateBackuper oErrorStateBackuper(
1176 20 : CPLQuietErrorHandler);
1177 40 : oCorrespondingDimIter->second->SetIndexingVariable(
1178 20 : std::move(dstArray));
1179 : }
1180 : }
1181 :
1182 47 : return true;
1183 26 : };
1184 :
1185 52 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1186 :
1187 : // Start by copying arrays that are indexing variables of dimensions
1188 73 : for (const auto &name : arrayNames)
1189 : {
1190 47 : auto srcArray = poSrcGroup->OpenMDArray(name);
1191 47 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1192 :
1193 47 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1194 47 : srcArray->GetName()))
1195 : {
1196 20 : if (!CopyArray(srcArray))
1197 0 : return false;
1198 : }
1199 : }
1200 :
1201 : // Then copy regular arrays
1202 73 : for (const auto &name : arrayNames)
1203 : {
1204 47 : auto srcArray = poSrcGroup->OpenMDArray(name);
1205 47 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1206 :
1207 47 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1208 47 : srcArray->GetName()))
1209 : {
1210 27 : if (!CopyArray(srcArray))
1211 0 : return false;
1212 : }
1213 : }
1214 :
1215 52 : const auto groupNames = poSrcGroup->GetGroupNames();
1216 32 : for (const auto &name : groupNames)
1217 : {
1218 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1219 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1220 6 : auto dstSubGroup = CreateGroup(name);
1221 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1222 12 : if (!dstSubGroup->CopyFrom(
1223 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1224 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1225 0 : return false;
1226 : }
1227 :
1228 26 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1229 0 : return false;
1230 :
1231 26 : return true;
1232 : }
1233 0 : catch (const std::exception &e)
1234 : {
1235 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1236 0 : return false;
1237 : }
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* GetInnerMostGroup() */
1242 : /************************************************************************/
1243 :
1244 : //! @cond Doxygen_Suppress
1245 : const GDALGroup *
1246 1303 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1247 : std::shared_ptr<GDALGroup> &curGroupHolder,
1248 : std::string &osLastPart) const
1249 : {
1250 1303 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1251 6 : return nullptr;
1252 1297 : const GDALGroup *poCurGroup = this;
1253 : CPLStringList aosTokens(
1254 2594 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1255 1297 : if (aosTokens.size() == 0)
1256 : {
1257 0 : return nullptr;
1258 : }
1259 :
1260 1638 : for (int i = 0; i < aosTokens.size() - 1; i++)
1261 : {
1262 353 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1263 353 : if (!curGroupHolder)
1264 : {
1265 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1266 : aosTokens[i]);
1267 12 : return nullptr;
1268 : }
1269 341 : poCurGroup = curGroupHolder.get();
1270 : }
1271 1285 : osLastPart = aosTokens[aosTokens.size() - 1];
1272 1285 : return poCurGroup;
1273 : }
1274 :
1275 : //! @endcond
1276 :
1277 : /************************************************************************/
1278 : /* OpenMDArrayFromFullname() */
1279 : /************************************************************************/
1280 :
1281 : /** Get an array from its fully qualified name */
1282 : std::shared_ptr<GDALMDArray>
1283 554 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1284 : CSLConstList papszOptions) const
1285 : {
1286 1108 : std::string osName;
1287 554 : std::shared_ptr<GDALGroup> curGroupHolder;
1288 554 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1289 554 : if (poGroup == nullptr)
1290 12 : return nullptr;
1291 542 : return poGroup->OpenMDArray(osName, papszOptions);
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* OpenAttributeFromFullname() */
1296 : /************************************************************************/
1297 :
1298 : /** Get an attribute from its fully qualified name */
1299 : std::shared_ptr<GDALAttribute>
1300 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1301 : CSLConstList papszOptions) const
1302 : {
1303 9 : const auto pos = osFullName.rfind('/');
1304 9 : if (pos == std::string::npos)
1305 0 : return nullptr;
1306 18 : const std::string attrName = osFullName.substr(pos + 1);
1307 9 : if (pos == 0)
1308 2 : return GetAttribute(attrName);
1309 14 : const std::string container = osFullName.substr(0, pos);
1310 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1311 7 : if (poArray)
1312 4 : return poArray->GetAttribute(attrName);
1313 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1314 3 : if (poGroup)
1315 1 : return poGroup->GetAttribute(attrName);
1316 2 : return nullptr;
1317 : }
1318 :
1319 : /************************************************************************/
1320 : /* ResolveMDArray() */
1321 : /************************************************************************/
1322 :
1323 : /** Locate an array in a group and its subgroups by name.
1324 : *
1325 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1326 : * used
1327 : * Otherwise the search will start from the group identified by osStartingPath,
1328 : * and an array whose name is osName will be looked for in this group (if
1329 : * osStartingPath is empty or "/", then the current group is used). If there
1330 : * is no match, then a recursive descendent search will be made in its
1331 : * subgroups. If there is no match in the subgroups, then the parent (if
1332 : * existing) of the group pointed by osStartingPath will be used as the new
1333 : * starting point for the search.
1334 : *
1335 : * @param osName name, qualified or not
1336 : * @param osStartingPath fully qualified name of the (sub-)group from which
1337 : * the search should be started. If this is a non-empty
1338 : * string, the group on which this method is called should
1339 : * nominally be the root group (otherwise the path will
1340 : * be interpreted as from the current group)
1341 : * @param papszOptions options to pass to OpenMDArray()
1342 : * @since GDAL 3.2
1343 : */
1344 : std::shared_ptr<GDALMDArray>
1345 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1346 : const std::string &osStartingPath,
1347 : CSLConstList papszOptions) const
1348 : {
1349 19 : if (!osName.empty() && osName[0] == '/')
1350 : {
1351 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1352 1 : if (poArray)
1353 1 : return poArray;
1354 : }
1355 36 : std::string osPath(osStartingPath);
1356 36 : std::set<std::string> oSetAlreadyVisited;
1357 :
1358 : while (true)
1359 : {
1360 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1361 0 : std::shared_ptr<GDALGroup> poGroup;
1362 :
1363 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1364 22 : bool goOn = false;
1365 22 : if (osPath.empty() || osPath == "/")
1366 : {
1367 11 : goOn = true;
1368 : }
1369 : else
1370 : {
1371 22 : std::string osLastPart;
1372 : const GDALGroup *poGroupPtr =
1373 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1374 11 : if (poGroupPtr)
1375 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1376 22 : if (poGroup &&
1377 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1378 : {
1379 11 : oQueue.push(poGroup);
1380 11 : goOn = true;
1381 : }
1382 : }
1383 :
1384 22 : if (goOn)
1385 : {
1386 17 : do
1387 : {
1388 : const GDALGroup *groupPtr;
1389 39 : if (!oQueue.empty())
1390 : {
1391 28 : poGroup = oQueue.front();
1392 28 : oQueue.pop();
1393 28 : groupPtr = poGroup.get();
1394 : }
1395 : else
1396 : {
1397 11 : groupPtr = this;
1398 : }
1399 :
1400 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1401 39 : if (poArray)
1402 16 : return poArray;
1403 :
1404 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1405 47 : for (const auto &osGroupName : aosGroupNames)
1406 : {
1407 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1408 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1409 48 : poSubGroup->GetFullName()))
1410 : {
1411 24 : oQueue.push(poSubGroup);
1412 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1413 : }
1414 : }
1415 23 : } while (!oQueue.empty());
1416 : }
1417 :
1418 6 : if (osPath.empty() || osPath == "/")
1419 2 : break;
1420 :
1421 4 : const auto nPos = osPath.rfind('/');
1422 4 : if (nPos == 0)
1423 1 : osPath = "/";
1424 : else
1425 : {
1426 3 : if (nPos == std::string::npos)
1427 0 : break;
1428 3 : osPath.resize(nPos);
1429 : }
1430 4 : }
1431 2 : return nullptr;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* OpenGroupFromFullname() */
1436 : /************************************************************************/
1437 :
1438 : /** Get a group from its fully qualified name.
1439 : * @since GDAL 3.2
1440 : */
1441 : std::shared_ptr<GDALGroup>
1442 569 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1443 : CSLConstList papszOptions) const
1444 : {
1445 1138 : std::string osName;
1446 569 : std::shared_ptr<GDALGroup> curGroupHolder;
1447 569 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1448 569 : if (poGroup == nullptr)
1449 4 : return nullptr;
1450 565 : return poGroup->OpenGroup(osName, papszOptions);
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* OpenDimensionFromFullname() */
1455 : /************************************************************************/
1456 :
1457 : /** Get a dimension from its fully qualified name */
1458 : std::shared_ptr<GDALDimension>
1459 169 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1460 : {
1461 338 : std::string osName;
1462 169 : std::shared_ptr<GDALGroup> curGroupHolder;
1463 169 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1464 169 : if (poGroup == nullptr)
1465 2 : return nullptr;
1466 334 : auto dims(poGroup->GetDimensions());
1467 288 : for (auto &dim : dims)
1468 : {
1469 242 : if (dim->GetName() == osName)
1470 121 : return dim;
1471 : }
1472 46 : return nullptr;
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* ClearStatistics() */
1477 : /************************************************************************/
1478 :
1479 : /**
1480 : * \brief Clear statistics.
1481 : *
1482 : * @since GDAL 3.4
1483 : */
1484 0 : void GDALGroup::ClearStatistics()
1485 : {
1486 0 : auto groupNames = GetGroupNames();
1487 0 : for (const auto &name : groupNames)
1488 : {
1489 0 : auto subGroup = OpenGroup(name);
1490 0 : if (subGroup)
1491 : {
1492 0 : subGroup->ClearStatistics();
1493 : }
1494 : }
1495 :
1496 0 : auto arrayNames = GetMDArrayNames();
1497 0 : for (const auto &name : arrayNames)
1498 : {
1499 0 : auto array = OpenMDArray(name);
1500 0 : if (array)
1501 : {
1502 0 : array->ClearStatistics();
1503 : }
1504 : }
1505 0 : }
1506 :
1507 : /************************************************************************/
1508 : /* Rename() */
1509 : /************************************************************************/
1510 :
1511 : /** Rename the group.
1512 : *
1513 : * This is not implemented by all drivers.
1514 : *
1515 : * Drivers known to implement it: MEM, netCDF, ZARR.
1516 : *
1517 : * This is the same as the C function GDALGroupRename().
1518 : *
1519 : * @param osNewName New name.
1520 : *
1521 : * @return true in case of success
1522 : * @since GDAL 3.8
1523 : */
1524 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1525 : {
1526 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1527 0 : return false;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* BaseRename() */
1532 : /************************************************************************/
1533 :
1534 : //! @cond Doxygen_Suppress
1535 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1536 : {
1537 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1538 8 : m_osFullName += osNewName;
1539 8 : m_osName = osNewName;
1540 :
1541 8 : NotifyChildrenOfRenaming();
1542 8 : }
1543 :
1544 : //! @endcond
1545 :
1546 : /************************************************************************/
1547 : /* ParentRenamed() */
1548 : /************************************************************************/
1549 :
1550 : //! @cond Doxygen_Suppress
1551 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1552 : {
1553 7 : m_osFullName = osNewParentFullName;
1554 7 : m_osFullName += "/";
1555 7 : m_osFullName += m_osName;
1556 :
1557 7 : NotifyChildrenOfRenaming();
1558 7 : }
1559 :
1560 : //! @endcond
1561 :
1562 : /************************************************************************/
1563 : /* Deleted() */
1564 : /************************************************************************/
1565 :
1566 : //! @cond Doxygen_Suppress
1567 22 : void GDALGroup::Deleted()
1568 : {
1569 22 : m_bValid = false;
1570 :
1571 22 : NotifyChildrenOfDeletion();
1572 22 : }
1573 :
1574 : //! @endcond
1575 :
1576 : /************************************************************************/
1577 : /* ParentDeleted() */
1578 : /************************************************************************/
1579 :
1580 : //! @cond Doxygen_Suppress
1581 3 : void GDALGroup::ParentDeleted()
1582 : {
1583 3 : Deleted();
1584 3 : }
1585 :
1586 : //! @endcond
1587 :
1588 : /************************************************************************/
1589 : /* CheckValidAndErrorOutIfNot() */
1590 : /************************************************************************/
1591 :
1592 : //! @cond Doxygen_Suppress
1593 16833 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1594 : {
1595 16833 : if (!m_bValid)
1596 : {
1597 14 : CPLError(CE_Failure, CPLE_AppDefined,
1598 : "This object has been deleted. No action on it is possible");
1599 : }
1600 16833 : return m_bValid;
1601 : }
1602 :
1603 : //! @endcond
1604 :
1605 : /************************************************************************/
1606 : /* ~GDALAbstractMDArray() */
1607 : /************************************************************************/
1608 :
1609 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1610 :
1611 : /************************************************************************/
1612 : /* GDALAbstractMDArray() */
1613 : /************************************************************************/
1614 :
1615 : //! @cond Doxygen_Suppress
1616 21058 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1617 21058 : const std::string &osName)
1618 : : m_osName(osName),
1619 : m_osFullName(
1620 21058 : !osParentName.empty()
1621 40413 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1622 82529 : : osName)
1623 : {
1624 21058 : }
1625 :
1626 : //! @endcond
1627 :
1628 : /************************************************************************/
1629 : /* GetDimensions() */
1630 : /************************************************************************/
1631 :
1632 : /** \fn GDALAbstractMDArray::GetDimensions() const
1633 : * \brief Return the dimensions of an attribute/array.
1634 : *
1635 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1636 : * similar to GDALAttributeGetDimensionsSize().
1637 : */
1638 :
1639 : /************************************************************************/
1640 : /* GetDataType() */
1641 : /************************************************************************/
1642 :
1643 : /** \fn GDALAbstractMDArray::GetDataType() const
1644 : * \brief Return the data type of an attribute/array.
1645 : *
1646 : * This is the same as the C functions GDALMDArrayGetDataType() and
1647 : * GDALAttributeGetDataType()
1648 : */
1649 :
1650 : /************************************************************************/
1651 : /* GetDimensionCount() */
1652 : /************************************************************************/
1653 :
1654 : /** Return the number of dimensions.
1655 : *
1656 : * Default implementation is GetDimensions().size(), and may be overridden by
1657 : * drivers if they have a faster / less expensive implementations.
1658 : *
1659 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1660 : * GDALAttributeGetDimensionCount().
1661 : *
1662 : */
1663 23900 : size_t GDALAbstractMDArray::GetDimensionCount() const
1664 : {
1665 23900 : return GetDimensions().size();
1666 : }
1667 :
1668 : /************************************************************************/
1669 : /* Rename() */
1670 : /************************************************************************/
1671 :
1672 : /** Rename the attribute/array.
1673 : *
1674 : * This is not implemented by all drivers.
1675 : *
1676 : * Drivers known to implement it: MEM, netCDF, Zarr.
1677 : *
1678 : * This is the same as the C functions GDALMDArrayRename() or
1679 : * GDALAttributeRename().
1680 : *
1681 : * @param osNewName New name.
1682 : *
1683 : * @return true in case of success
1684 : * @since GDAL 3.8
1685 : */
1686 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1689 0 : return false;
1690 : }
1691 :
1692 : /************************************************************************/
1693 : /* CopyValue() */
1694 : /************************************************************************/
1695 :
1696 : /** Convert a value from a source type to a destination type.
1697 : *
1698 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1699 : * that must be freed with CPLFree().
1700 : */
1701 81016 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1702 : const GDALExtendedDataType &srcType,
1703 : void *pDst,
1704 : const GDALExtendedDataType &dstType)
1705 : {
1706 157948 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1707 76932 : dstType.GetClass() == GEDTC_NUMERIC)
1708 : {
1709 76467 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1710 : dstType.GetNumericDataType(), 0, 1);
1711 76467 : return true;
1712 : }
1713 8417 : if (srcType.GetClass() == GEDTC_STRING &&
1714 3868 : dstType.GetClass() == GEDTC_STRING)
1715 : {
1716 : const char *srcStrPtr;
1717 3435 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1718 3435 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1719 3435 : *reinterpret_cast<void **>(pDst) = pszDup;
1720 3435 : return true;
1721 : }
1722 1579 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1723 465 : dstType.GetClass() == GEDTC_STRING)
1724 : {
1725 465 : const char *str = nullptr;
1726 465 : switch (srcType.GetNumericDataType())
1727 : {
1728 0 : case GDT_Unknown:
1729 0 : break;
1730 0 : case GDT_Byte:
1731 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1732 0 : break;
1733 3 : case GDT_Int8:
1734 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1735 3 : break;
1736 48 : case GDT_UInt16:
1737 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1738 48 : break;
1739 0 : case GDT_Int16:
1740 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1741 0 : break;
1742 8 : case GDT_UInt32:
1743 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1744 8 : break;
1745 60 : case GDT_Int32:
1746 60 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1747 60 : break;
1748 0 : case GDT_UInt64:
1749 : str =
1750 0 : CPLSPrintf(CPL_FRMT_GUIB,
1751 : static_cast<GUIntBig>(
1752 : *static_cast<const std::uint64_t *>(pSrc)));
1753 0 : break;
1754 24 : case GDT_Int64:
1755 24 : str = CPLSPrintf(CPL_FRMT_GIB,
1756 : static_cast<GIntBig>(
1757 : *static_cast<const std::int64_t *>(pSrc)));
1758 24 : break;
1759 0 : case GDT_Float16:
1760 0 : str = CPLSPrintf("%.5g",
1761 : double(*static_cast<const GFloat16 *>(pSrc)));
1762 0 : break;
1763 17 : case GDT_Float32:
1764 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1765 17 : break;
1766 303 : case GDT_Float64:
1767 303 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1768 303 : break;
1769 2 : case GDT_CInt16:
1770 : {
1771 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1772 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1773 2 : break;
1774 : }
1775 0 : case GDT_CInt32:
1776 : {
1777 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1778 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1779 0 : break;
1780 : }
1781 0 : case GDT_CFloat16:
1782 : {
1783 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1784 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1785 0 : break;
1786 : }
1787 0 : case GDT_CFloat32:
1788 : {
1789 0 : const float *src = static_cast<const float *>(pSrc);
1790 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1791 0 : break;
1792 : }
1793 0 : case GDT_CFloat64:
1794 : {
1795 0 : const double *src = static_cast<const double *>(pSrc);
1796 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1797 0 : break;
1798 : }
1799 0 : case GDT_TypeCount:
1800 0 : CPLAssert(false);
1801 : break;
1802 : }
1803 465 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1804 465 : *reinterpret_cast<void **>(pDst) = pszDup;
1805 465 : return true;
1806 : }
1807 1082 : if (srcType.GetClass() == GEDTC_STRING &&
1808 433 : dstType.GetClass() == GEDTC_NUMERIC)
1809 : {
1810 : const char *srcStrPtr;
1811 433 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1812 433 : if (dstType.GetNumericDataType() == GDT_Int64)
1813 : {
1814 2 : *(static_cast<int64_t *>(pDst)) =
1815 2 : srcStrPtr == nullptr ? 0
1816 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1817 : }
1818 431 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1819 : {
1820 2 : *(static_cast<uint64_t *>(pDst)) =
1821 2 : srcStrPtr == nullptr
1822 2 : ? 0
1823 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1824 : }
1825 : else
1826 : {
1827 : // FIXME GDT_UInt64
1828 429 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1829 429 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1830 : dstType.GetNumericDataType(), 0, 1);
1831 : }
1832 433 : return true;
1833 : }
1834 432 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1835 216 : dstType.GetClass() == GEDTC_COMPOUND)
1836 : {
1837 216 : const auto &srcComponents = srcType.GetComponents();
1838 216 : const auto &dstComponents = dstType.GetComponents();
1839 216 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1840 216 : GByte *pabyDst = static_cast<GByte *>(pDst);
1841 :
1842 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1843 432 : srcComponentMap;
1844 1031 : for (const auto &srcComp : srcComponents)
1845 : {
1846 815 : srcComponentMap[srcComp->GetName()] = &srcComp;
1847 : }
1848 586 : for (const auto &dstComp : dstComponents)
1849 : {
1850 370 : auto oIter = srcComponentMap.find(dstComp->GetName());
1851 370 : if (oIter == srcComponentMap.end())
1852 0 : return false;
1853 370 : const auto &srcComp = *(oIter->second);
1854 1110 : if (!GDALExtendedDataType::CopyValue(
1855 370 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1856 370 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1857 : {
1858 0 : return false;
1859 : }
1860 : }
1861 216 : return true;
1862 : }
1863 :
1864 0 : return false;
1865 : }
1866 :
1867 : /************************************************************************/
1868 : /* CopyValues() */
1869 : /************************************************************************/
1870 :
1871 : /** Convert severals value from a source type to a destination type.
1872 : *
1873 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1874 : * that must be freed with CPLFree().
1875 : */
1876 365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1877 : const GDALExtendedDataType &srcType,
1878 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1879 : const GDALExtendedDataType &dstType,
1880 : GPtrDiff_t nDstStrideInElts,
1881 : size_t nValues)
1882 : {
1883 : const auto nSrcStrideInBytes =
1884 365 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1885 : const auto nDstStrideInBytes =
1886 365 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1887 631 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1888 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1889 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1890 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1891 897 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1892 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1893 : {
1894 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1895 : static_cast<int>(nSrcStrideInBytes), pDst,
1896 : dstType.GetNumericDataType(),
1897 : static_cast<int>(nDstStrideInBytes), nValues);
1898 : }
1899 : else
1900 : {
1901 99 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1902 99 : GByte *pabyDst = static_cast<GByte *>(pDst);
1903 198 : for (size_t i = 0; i < nValues; ++i)
1904 : {
1905 99 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1906 0 : return false;
1907 99 : pabySrc += nSrcStrideInBytes;
1908 99 : pabyDst += nDstStrideInBytes;
1909 : }
1910 : }
1911 365 : return true;
1912 : }
1913 :
1914 : /************************************************************************/
1915 : /* CheckReadWriteParams() */
1916 : /************************************************************************/
1917 : //! @cond Doxygen_Suppress
1918 8518 : bool GDALAbstractMDArray::CheckReadWriteParams(
1919 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1920 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1921 : const void *buffer, const void *buffer_alloc_start,
1922 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1923 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1924 : {
1925 0 : const auto lamda_error = []()
1926 : {
1927 0 : CPLError(CE_Failure, CPLE_AppDefined,
1928 : "Not all elements pointed by buffer will fit in "
1929 : "[buffer_alloc_start, "
1930 : "buffer_alloc_start + buffer_alloc_size]");
1931 0 : };
1932 :
1933 8518 : const auto &dims = GetDimensions();
1934 8518 : if (dims.empty())
1935 : {
1936 3400 : if (buffer_alloc_start)
1937 : {
1938 3015 : const size_t elementSize = bufferDataType.GetSize();
1939 3015 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1940 3015 : const GByte *paby_buffer_alloc_start =
1941 : static_cast<const GByte *>(buffer_alloc_start);
1942 3015 : const GByte *paby_buffer_alloc_end =
1943 : paby_buffer_alloc_start + buffer_alloc_size;
1944 :
1945 3015 : if (paby_buffer < paby_buffer_alloc_start ||
1946 3015 : paby_buffer + elementSize > paby_buffer_alloc_end)
1947 : {
1948 0 : lamda_error();
1949 0 : return false;
1950 : }
1951 : }
1952 3400 : return true;
1953 : }
1954 :
1955 5118 : if (arrayStep == nullptr)
1956 : {
1957 1409 : tmp_arrayStep.resize(dims.size(), 1);
1958 1409 : arrayStep = tmp_arrayStep.data();
1959 : }
1960 14315 : for (size_t i = 0; i < dims.size(); i++)
1961 : {
1962 9197 : assert(count);
1963 9197 : if (count[i] == 0)
1964 : {
1965 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1966 : static_cast<unsigned>(i));
1967 0 : return false;
1968 : }
1969 : }
1970 5118 : bool bufferStride_all_positive = true;
1971 5118 : if (bufferStride == nullptr)
1972 : {
1973 1110 : GPtrDiff_t stride = 1;
1974 1110 : assert(dims.empty() || count != nullptr);
1975 : // To compute strides we must proceed from the fastest varying dimension
1976 : // (the last one), and then reverse the result
1977 2515 : for (size_t i = dims.size(); i != 0;)
1978 : {
1979 1405 : --i;
1980 1405 : tmp_bufferStride.push_back(stride);
1981 1405 : GUInt64 newStride = 0;
1982 : bool bOK;
1983 : try
1984 : {
1985 1405 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1986 2810 : CPLSM(static_cast<uint64_t>(count[i])))
1987 1405 : .v();
1988 1405 : bOK = static_cast<size_t>(newStride) == newStride &&
1989 1405 : newStride < std::numeric_limits<size_t>::max() / 2;
1990 : }
1991 0 : catch (...)
1992 : {
1993 0 : bOK = false;
1994 : }
1995 1405 : if (!bOK)
1996 : {
1997 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1998 0 : return false;
1999 : }
2000 1405 : stride = static_cast<GPtrDiff_t>(newStride);
2001 : }
2002 1110 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2003 1110 : bufferStride = tmp_bufferStride.data();
2004 : }
2005 : else
2006 : {
2007 11798 : for (size_t i = 0; i < dims.size(); i++)
2008 : {
2009 7791 : if (bufferStride[i] < 0)
2010 : {
2011 1 : bufferStride_all_positive = false;
2012 1 : break;
2013 : }
2014 : }
2015 : }
2016 14286 : for (size_t i = 0; i < dims.size(); i++)
2017 : {
2018 9178 : assert(arrayStartIdx);
2019 9178 : assert(count);
2020 9178 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2021 : {
2022 2 : CPLError(CE_Failure, CPLE_AppDefined,
2023 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2024 : static_cast<unsigned>(i),
2025 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2026 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2027 2 : return false;
2028 : }
2029 : bool bOverflow;
2030 9176 : if (arrayStep[i] >= 0)
2031 : {
2032 : try
2033 : {
2034 8581 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2035 8583 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2036 34327 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2037 8581 : .v() >= dims[i]->GetSize();
2038 : }
2039 1 : catch (...)
2040 : {
2041 1 : bOverflow = true;
2042 : }
2043 8582 : if (bOverflow)
2044 : {
2045 5 : CPLError(CE_Failure, CPLE_AppDefined,
2046 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2047 : ">= " CPL_FRMT_GUIB,
2048 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2049 : static_cast<unsigned>(i),
2050 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2051 5 : return false;
2052 : }
2053 : }
2054 : else
2055 : {
2056 : try
2057 : {
2058 594 : bOverflow =
2059 594 : arrayStartIdx[i] <
2060 594 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2061 1188 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2062 : ? (static_cast<uint64_t>(1) << 63)
2063 1188 : : static_cast<uint64_t>(-arrayStep[i])))
2064 594 : .v();
2065 : }
2066 0 : catch (...)
2067 : {
2068 0 : bOverflow = true;
2069 : }
2070 594 : if (bOverflow)
2071 : {
2072 3 : CPLError(
2073 : CE_Failure, CPLE_AppDefined,
2074 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2075 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2076 : static_cast<unsigned>(i));
2077 3 : return false;
2078 : }
2079 : }
2080 : }
2081 :
2082 5108 : if (buffer_alloc_start)
2083 : {
2084 2632 : const size_t elementSize = bufferDataType.GetSize();
2085 2632 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2086 2632 : const GByte *paby_buffer_alloc_start =
2087 : static_cast<const GByte *>(buffer_alloc_start);
2088 2632 : const GByte *paby_buffer_alloc_end =
2089 : paby_buffer_alloc_start + buffer_alloc_size;
2090 2632 : if (bufferStride_all_positive)
2091 : {
2092 2632 : if (paby_buffer < paby_buffer_alloc_start)
2093 : {
2094 0 : lamda_error();
2095 0 : return false;
2096 : }
2097 2632 : GUInt64 nOffset = elementSize;
2098 7514 : for (size_t i = 0; i < dims.size(); i++)
2099 : {
2100 : try
2101 : {
2102 4882 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2103 4882 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2104 9764 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2105 19528 : CPLSM(static_cast<uint64_t>(elementSize)))
2106 4882 : .v();
2107 : }
2108 0 : catch (...)
2109 : {
2110 0 : lamda_error();
2111 0 : return false;
2112 : }
2113 : }
2114 : #if SIZEOF_VOIDP == 4
2115 : if (static_cast<size_t>(nOffset) != nOffset)
2116 : {
2117 : lamda_error();
2118 : return false;
2119 : }
2120 : #endif
2121 2632 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2122 : {
2123 0 : lamda_error();
2124 0 : return false;
2125 : }
2126 : }
2127 0 : else if (dims.size() < 31)
2128 : {
2129 : // Check all corners of the hypercube
2130 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2131 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2132 : {
2133 0 : const GByte *paby = paby_buffer;
2134 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2135 : i++)
2136 : {
2137 0 : if (iCornerCode & (1U << i))
2138 : {
2139 : // We should check for integer overflows
2140 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2141 : }
2142 : }
2143 0 : if (paby < paby_buffer_alloc_start ||
2144 0 : paby + elementSize > paby_buffer_alloc_end)
2145 : {
2146 0 : lamda_error();
2147 0 : return false;
2148 : }
2149 : }
2150 : }
2151 : }
2152 :
2153 5108 : return true;
2154 : }
2155 :
2156 : //! @endcond
2157 :
2158 : /************************************************************************/
2159 : /* Read() */
2160 : /************************************************************************/
2161 :
2162 : /** Read part or totality of a multidimensional array or attribute.
2163 : *
2164 : * This will extract the content of a hyper-rectangle from the array into
2165 : * a user supplied buffer.
2166 : *
2167 : * If bufferDataType is of type string, the values written in pDstBuffer
2168 : * will be char* pointers and the strings should be freed with CPLFree().
2169 : *
2170 : * This is the same as the C function GDALMDArrayRead().
2171 : *
2172 : * @param arrayStartIdx Values representing the starting index to read
2173 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2174 : * Array of GetDimensionCount() values. Must not be
2175 : * nullptr, unless for a zero-dimensional array.
2176 : *
2177 : * @param count Values representing the number of values to extract in
2178 : * each dimension.
2179 : * Array of GetDimensionCount() values. Must not be
2180 : * nullptr, unless for a zero-dimensional array.
2181 : *
2182 : * @param arrayStep Spacing between values to extract in each dimension.
2183 : * The spacing is in number of array elements, not bytes.
2184 : * If provided, must contain GetDimensionCount() values.
2185 : * If set to nullptr, [1, 1, ... 1] will be used as a
2186 : * default to indicate consecutive elements.
2187 : *
2188 : * @param bufferStride Spacing between values to store in pDstBuffer.
2189 : * The spacing is in number of array elements, not bytes.
2190 : * If provided, must contain GetDimensionCount() values.
2191 : * Negative values are possible (for example to reorder
2192 : * from bottom-to-top to top-to-bottom).
2193 : * If set to nullptr, will be set so that pDstBuffer is
2194 : * written in a compact way, with elements of the last /
2195 : * fastest varying dimension being consecutive.
2196 : *
2197 : * @param bufferDataType Data type of values in pDstBuffer.
2198 : *
2199 : * @param pDstBuffer User buffer to store the values read. Should be big
2200 : * enough to store the number of values indicated by
2201 : * count[] and with the spacing of bufferStride[].
2202 : *
2203 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2204 : * validity of pDstBuffer. pDstBufferAllocStart
2205 : * should be the pointer returned by the malloc() or equivalent call used to
2206 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2207 : * bufferStride[] values are all positive), but not necessarily. If specified,
2208 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2209 : * validation is needed, nullptr can be passed.
2210 : *
2211 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2212 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2213 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2214 : * set to the appropriate value.
2215 : * If no validation is needed, 0 can be passed.
2216 : *
2217 : * @return true in case of success.
2218 : */
2219 2732 : bool GDALAbstractMDArray::Read(
2220 : const GUInt64 *arrayStartIdx, const size_t *count,
2221 : const GInt64 *arrayStep, // step in elements
2222 : const GPtrDiff_t *bufferStride, // stride in elements
2223 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2224 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2225 : {
2226 2732 : if (!GetDataType().CanConvertTo(bufferDataType))
2227 : {
2228 0 : CPLError(CE_Failure, CPLE_AppDefined,
2229 : "Array data type is not convertible to buffer data type");
2230 0 : return false;
2231 : }
2232 :
2233 5464 : std::vector<GInt64> tmp_arrayStep;
2234 5464 : std::vector<GPtrDiff_t> tmp_bufferStride;
2235 2732 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2236 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2237 : nDstBufferAllocSize, tmp_arrayStep,
2238 : tmp_bufferStride))
2239 : {
2240 0 : return false;
2241 : }
2242 :
2243 2732 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2244 2732 : pDstBuffer);
2245 : }
2246 :
2247 : /************************************************************************/
2248 : /* IWrite() */
2249 : /************************************************************************/
2250 :
2251 : //! @cond Doxygen_Suppress
2252 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2253 : const GInt64 *, const GPtrDiff_t *,
2254 : const GDALExtendedDataType &, const void *)
2255 : {
2256 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2257 1 : return false;
2258 : }
2259 :
2260 : //! @endcond
2261 :
2262 : /************************************************************************/
2263 : /* Write() */
2264 : /************************************************************************/
2265 :
2266 : /** Write part or totality of a multidimensional array or attribute.
2267 : *
2268 : * This will set the content of a hyper-rectangle into the array from
2269 : * a user supplied buffer.
2270 : *
2271 : * If bufferDataType is of type string, the values read from pSrcBuffer
2272 : * will be char* pointers.
2273 : *
2274 : * This is the same as the C function GDALMDArrayWrite().
2275 : *
2276 : * @param arrayStartIdx Values representing the starting index to write
2277 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2278 : * Array of GetDimensionCount() values. Must not be
2279 : * nullptr, unless for a zero-dimensional array.
2280 : *
2281 : * @param count Values representing the number of values to write in
2282 : * each dimension.
2283 : * Array of GetDimensionCount() values. Must not be
2284 : * nullptr, unless for a zero-dimensional array.
2285 : *
2286 : * @param arrayStep Spacing between values to write in each dimension.
2287 : * The spacing is in number of array elements, not bytes.
2288 : * If provided, must contain GetDimensionCount() values.
2289 : * If set to nullptr, [1, 1, ... 1] will be used as a
2290 : * default to indicate consecutive elements.
2291 : *
2292 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2293 : * The spacing is in number of array elements, not bytes.
2294 : * If provided, must contain GetDimensionCount() values.
2295 : * Negative values are possible (for example to reorder
2296 : * from bottom-to-top to top-to-bottom).
2297 : * If set to nullptr, will be set so that pSrcBuffer is
2298 : * written in a compact way, with elements of the last /
2299 : * fastest varying dimension being consecutive.
2300 : *
2301 : * @param bufferDataType Data type of values in pSrcBuffer.
2302 : *
2303 : * @param pSrcBuffer User buffer to read the values from. Should be big
2304 : * enough to store the number of values indicated by
2305 : * count[] and with the spacing of bufferStride[].
2306 : *
2307 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2308 : * validity of pSrcBuffer. pSrcBufferAllocStart
2309 : * should be the pointer returned by the malloc() or equivalent call used to
2310 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2311 : * bufferStride[] values are all positive), but not necessarily. If specified,
2312 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2313 : * validation is needed, nullptr can be passed.
2314 : *
2315 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2316 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2317 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2318 : * set to the appropriate value.
2319 : * If no validation is needed, 0 can be passed.
2320 : *
2321 : * @return true in case of success.
2322 : */
2323 1843 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2324 : const size_t *count, const GInt64 *arrayStep,
2325 : const GPtrDiff_t *bufferStride,
2326 : const GDALExtendedDataType &bufferDataType,
2327 : const void *pSrcBuffer,
2328 : const void *pSrcBufferAllocStart,
2329 : size_t nSrcBufferAllocSize)
2330 : {
2331 1843 : if (!bufferDataType.CanConvertTo(GetDataType()))
2332 : {
2333 0 : CPLError(CE_Failure, CPLE_AppDefined,
2334 : "Buffer data type is not convertible to array data type");
2335 0 : return false;
2336 : }
2337 :
2338 3686 : std::vector<GInt64> tmp_arrayStep;
2339 3686 : std::vector<GPtrDiff_t> tmp_bufferStride;
2340 1843 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2341 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2342 : nSrcBufferAllocSize, tmp_arrayStep,
2343 : tmp_bufferStride))
2344 : {
2345 0 : return false;
2346 : }
2347 :
2348 1843 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2349 1843 : pSrcBuffer);
2350 : }
2351 :
2352 : /************************************************************************/
2353 : /* GetTotalElementsCount() */
2354 : /************************************************************************/
2355 :
2356 : /** Return the total number of values in the array.
2357 : *
2358 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2359 : * and GDALAttributeGetTotalElementsCount().
2360 : *
2361 : */
2362 1201 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2363 : {
2364 1201 : const auto &dims = GetDimensions();
2365 1201 : if (dims.empty())
2366 594 : return 1;
2367 607 : GUInt64 nElts = 1;
2368 1336 : for (const auto &dim : dims)
2369 : {
2370 : try
2371 : {
2372 729 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2373 2187 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2374 729 : .v();
2375 : }
2376 0 : catch (...)
2377 : {
2378 0 : return 0;
2379 : }
2380 : }
2381 607 : return nElts;
2382 : }
2383 :
2384 : /************************************************************************/
2385 : /* GetBlockSize() */
2386 : /************************************************************************/
2387 :
2388 : /** Return the "natural" block size of the array along all dimensions.
2389 : *
2390 : * Some drivers might organize the array in tiles/blocks and reading/writing
2391 : * aligned on those tile/block boundaries will be more efficient.
2392 : *
2393 : * The returned number of elements in the vector is the same as
2394 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2395 : * the natural block size along the considered dimension.
2396 : * "Flat" arrays will typically return a vector of values set to 0.
2397 : *
2398 : * The default implementation will return a vector of values set to 0.
2399 : *
2400 : * This method is used by GetProcessingChunkSize().
2401 : *
2402 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2403 : * theoretical case of a 32-bit platform, this might exceed its size_t
2404 : * allocation capabilities.
2405 : *
2406 : * This is the same as the C function GDALMDArrayGetBlockSize().
2407 : *
2408 : * @return the block size, in number of elements along each dimension.
2409 : */
2410 280 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2411 : {
2412 280 : return std::vector<GUInt64>(GetDimensionCount());
2413 : }
2414 :
2415 : /************************************************************************/
2416 : /* GetProcessingChunkSize() */
2417 : /************************************************************************/
2418 :
2419 : /** \brief Return an optimal chunk size for read/write operations, given the
2420 : * natural block size and memory constraints specified.
2421 : *
2422 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2423 : * multiple of those returned by GetBlockSize() (unless the block define by
2424 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2425 : * returned by this method).
2426 : *
2427 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2428 : *
2429 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2430 : * chunk.
2431 : *
2432 : * @return the chunk size, in number of elements along each dimension.
2433 : */
2434 : std::vector<size_t>
2435 71 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2436 : {
2437 71 : const auto &dims = GetDimensions();
2438 71 : const auto &nDTSize = GetDataType().GetSize();
2439 71 : std::vector<size_t> anChunkSize;
2440 142 : auto blockSize = GetBlockSize();
2441 71 : CPLAssert(blockSize.size() == dims.size());
2442 71 : size_t nChunkSize = nDTSize;
2443 71 : bool bOverflow = false;
2444 71 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2445 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2446 : // [1, min(sizet_max, dim_size[i])]
2447 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2448 201 : for (size_t i = 0; i < dims.size(); i++)
2449 : {
2450 : const auto sizeDimI =
2451 260 : std::max(static_cast<size_t>(1),
2452 260 : static_cast<size_t>(
2453 260 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2454 130 : std::min(blockSize[i], dims[i]->GetSize()))));
2455 130 : anChunkSize.push_back(sizeDimI);
2456 130 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2457 : {
2458 4 : bOverflow = true;
2459 : }
2460 : else
2461 : {
2462 126 : nChunkSize *= sizeDimI;
2463 : }
2464 : }
2465 71 : if (nChunkSize == 0)
2466 0 : return anChunkSize;
2467 :
2468 : // If the product of all anChunkSize[i] does not fit on size_t, then
2469 : // set lowest anChunkSize[i] to 1.
2470 71 : if (bOverflow)
2471 : {
2472 2 : nChunkSize = nDTSize;
2473 2 : bOverflow = false;
2474 8 : for (size_t i = dims.size(); i > 0;)
2475 : {
2476 6 : --i;
2477 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2478 : {
2479 4 : bOverflow = true;
2480 4 : anChunkSize[i] = 1;
2481 : }
2482 : else
2483 : {
2484 2 : nChunkSize *= anChunkSize[i];
2485 : }
2486 : }
2487 : }
2488 :
2489 71 : nChunkSize = nDTSize;
2490 142 : std::vector<size_t> anAccBlockSizeFromStart;
2491 201 : for (size_t i = 0; i < dims.size(); i++)
2492 : {
2493 130 : nChunkSize *= anChunkSize[i];
2494 130 : anAccBlockSizeFromStart.push_back(nChunkSize);
2495 : }
2496 71 : if (nChunkSize <= nMaxChunkMemory / 2)
2497 : {
2498 67 : size_t nVoxelsFromEnd = 1;
2499 189 : for (size_t i = dims.size(); i > 0;)
2500 : {
2501 122 : --i;
2502 : const auto nCurBlockSize =
2503 122 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2504 122 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2505 122 : if (nMul >= 2)
2506 : {
2507 114 : const auto nSizeThisDim(dims[i]->GetSize());
2508 : const auto nBlocksThisDim =
2509 114 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2510 114 : anChunkSize[i] = static_cast<size_t>(std::min(
2511 114 : anChunkSize[i] *
2512 228 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2513 114 : nSizeThisDim));
2514 : }
2515 122 : nVoxelsFromEnd *= anChunkSize[i];
2516 : }
2517 : }
2518 71 : return anChunkSize;
2519 : }
2520 :
2521 : /************************************************************************/
2522 : /* BaseRename() */
2523 : /************************************************************************/
2524 :
2525 : //! @cond Doxygen_Suppress
2526 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2527 : {
2528 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2529 18 : m_osFullName += osNewName;
2530 18 : m_osName = osNewName;
2531 :
2532 18 : NotifyChildrenOfRenaming();
2533 18 : }
2534 :
2535 : //! @endcond
2536 :
2537 : //! @cond Doxygen_Suppress
2538 : /************************************************************************/
2539 : /* ParentRenamed() */
2540 : /************************************************************************/
2541 :
2542 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2543 : {
2544 50 : m_osFullName = osNewParentFullName;
2545 50 : m_osFullName += "/";
2546 50 : m_osFullName += m_osName;
2547 :
2548 50 : NotifyChildrenOfRenaming();
2549 50 : }
2550 :
2551 : //! @endcond
2552 :
2553 : /************************************************************************/
2554 : /* Deleted() */
2555 : /************************************************************************/
2556 :
2557 : //! @cond Doxygen_Suppress
2558 52 : void GDALAbstractMDArray::Deleted()
2559 : {
2560 52 : m_bValid = false;
2561 :
2562 52 : NotifyChildrenOfDeletion();
2563 52 : }
2564 :
2565 : //! @endcond
2566 :
2567 : /************************************************************************/
2568 : /* ParentDeleted() */
2569 : /************************************************************************/
2570 :
2571 : //! @cond Doxygen_Suppress
2572 28 : void GDALAbstractMDArray::ParentDeleted()
2573 : {
2574 28 : Deleted();
2575 28 : }
2576 :
2577 : //! @endcond
2578 :
2579 : /************************************************************************/
2580 : /* CheckValidAndErrorOutIfNot() */
2581 : /************************************************************************/
2582 :
2583 : //! @cond Doxygen_Suppress
2584 6158 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2585 : {
2586 6158 : if (!m_bValid)
2587 : {
2588 26 : CPLError(CE_Failure, CPLE_AppDefined,
2589 : "This object has been deleted. No action on it is possible");
2590 : }
2591 6158 : return m_bValid;
2592 : }
2593 :
2594 : //! @endcond
2595 :
2596 : /************************************************************************/
2597 : /* SetUnit() */
2598 : /************************************************************************/
2599 :
2600 : /** Set the variable unit.
2601 : *
2602 : * Values should conform as much as possible with those allowed by
2603 : * the NetCDF CF conventions:
2604 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2605 : * but others might be returned.
2606 : *
2607 : * Few examples are "meter", "degrees", "second", ...
2608 : * Empty value means unknown.
2609 : *
2610 : * This is the same as the C function GDALMDArraySetUnit()
2611 : *
2612 : * @note Driver implementation: optionally implemented.
2613 : *
2614 : * @param osUnit unit name.
2615 : * @return true in case of success.
2616 : */
2617 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2618 : {
2619 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2620 0 : return false;
2621 : }
2622 :
2623 : /************************************************************************/
2624 : /* GetUnit() */
2625 : /************************************************************************/
2626 :
2627 : /** Return the array unit.
2628 : *
2629 : * Values should conform as much as possible with those allowed by
2630 : * the NetCDF CF conventions:
2631 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2632 : * but others might be returned.
2633 : *
2634 : * Few examples are "meter", "degrees", "second", ...
2635 : * Empty value means unknown.
2636 : *
2637 : * This is the same as the C function GDALMDArrayGetUnit()
2638 : */
2639 5 : const std::string &GDALMDArray::GetUnit() const
2640 : {
2641 5 : static const std::string emptyString;
2642 5 : return emptyString;
2643 : }
2644 :
2645 : /************************************************************************/
2646 : /* SetSpatialRef() */
2647 : /************************************************************************/
2648 :
2649 : /** Assign a spatial reference system object to the array.
2650 : *
2651 : * This is the same as the C function GDALMDArraySetSpatialRef().
2652 : */
2653 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2654 : {
2655 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2656 0 : return false;
2657 : }
2658 :
2659 : /************************************************************************/
2660 : /* GetSpatialRef() */
2661 : /************************************************************************/
2662 :
2663 : /** Return the spatial reference system object associated with the array.
2664 : *
2665 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2666 : */
2667 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2668 : {
2669 4 : return nullptr;
2670 : }
2671 :
2672 : /************************************************************************/
2673 : /* GetRawNoDataValue() */
2674 : /************************************************************************/
2675 :
2676 : /** Return the nodata value as a "raw" value.
2677 : *
2678 : * The value returned might be nullptr in case of no nodata value. When
2679 : * a nodata value is registered, a non-nullptr will be returned whose size in
2680 : * bytes is GetDataType().GetSize().
2681 : *
2682 : * The returned value should not be modified or freed. It is valid until
2683 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2684 : * SetRawNoDataValue(), or any similar methods.
2685 : *
2686 : * @note Driver implementation: this method shall be implemented if nodata
2687 : * is supported.
2688 : *
2689 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2690 : *
2691 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2692 : */
2693 5 : const void *GDALMDArray::GetRawNoDataValue() const
2694 : {
2695 5 : return nullptr;
2696 : }
2697 :
2698 : /************************************************************************/
2699 : /* GetNoDataValueAsDouble() */
2700 : /************************************************************************/
2701 :
2702 : /** Return the nodata value as a double.
2703 : *
2704 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2705 : *
2706 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2707 : * a nodata value exists and can be converted to double. Might be nullptr.
2708 : *
2709 : * @return the nodata value as a double. A 0.0 value might also indicate the
2710 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2711 : * set to false then).
2712 : */
2713 22478 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2714 : {
2715 22478 : const void *pNoData = GetRawNoDataValue();
2716 22478 : double dfNoData = 0.0;
2717 22478 : const auto &eDT = GetDataType();
2718 22478 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2719 22478 : if (ok)
2720 : {
2721 22188 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2722 : GDT_Float64, 0, 1);
2723 : }
2724 22478 : if (pbHasNoData)
2725 439 : *pbHasNoData = ok;
2726 22478 : return dfNoData;
2727 : }
2728 :
2729 : /************************************************************************/
2730 : /* GetNoDataValueAsInt64() */
2731 : /************************************************************************/
2732 :
2733 : /** Return the nodata value as a Int64.
2734 : *
2735 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2736 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2737 : *
2738 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2739 : *
2740 : * @return the nodata value as a Int64
2741 : *
2742 : * @since GDAL 3.5
2743 : */
2744 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2745 : {
2746 12 : const void *pNoData = GetRawNoDataValue();
2747 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2748 12 : const auto &eDT = GetDataType();
2749 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2750 12 : if (ok)
2751 : {
2752 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2753 : GDT_Int64, 0, 1);
2754 : }
2755 12 : if (pbHasNoData)
2756 12 : *pbHasNoData = ok;
2757 12 : return nNoData;
2758 : }
2759 :
2760 : /************************************************************************/
2761 : /* GetNoDataValueAsUInt64() */
2762 : /************************************************************************/
2763 :
2764 : /** Return the nodata value as a UInt64.
2765 : *
2766 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2767 :
2768 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2769 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2770 : *
2771 : * @return the nodata value as a UInt64
2772 : *
2773 : * @since GDAL 3.5
2774 : */
2775 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2776 : {
2777 8 : const void *pNoData = GetRawNoDataValue();
2778 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2779 8 : const auto &eDT = GetDataType();
2780 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2781 8 : if (ok)
2782 : {
2783 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2784 : GDT_UInt64, 0, 1);
2785 : }
2786 8 : if (pbHasNoData)
2787 8 : *pbHasNoData = ok;
2788 8 : return nNoData;
2789 : }
2790 :
2791 : /************************************************************************/
2792 : /* SetRawNoDataValue() */
2793 : /************************************************************************/
2794 :
2795 : /** Set the nodata value as a "raw" value.
2796 : *
2797 : * The value passed might be nullptr in case of no nodata value. When
2798 : * a nodata value is registered, a non-nullptr whose size in
2799 : * bytes is GetDataType().GetSize() must be passed.
2800 : *
2801 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2802 : *
2803 : * @note Driver implementation: this method shall be implemented if setting
2804 : nodata
2805 : * is supported.
2806 :
2807 : * @return true in case of success.
2808 : */
2809 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2810 : {
2811 0 : CPLError(CE_Failure, CPLE_NotSupported,
2812 : "SetRawNoDataValue() not implemented");
2813 0 : return false;
2814 : }
2815 :
2816 : /************************************************************************/
2817 : /* SetNoDataValue() */
2818 : /************************************************************************/
2819 :
2820 : /** Set the nodata value as a double.
2821 : *
2822 : * If the natural data type of the attribute/array is not double, type
2823 : * conversion will occur to the type returned by GetDataType().
2824 : *
2825 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2826 : *
2827 : * @return true in case of success.
2828 : */
2829 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2830 : {
2831 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2832 57 : bool bRet = false;
2833 57 : if (GDALExtendedDataType::CopyValue(
2834 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2835 57 : GetDataType()))
2836 : {
2837 57 : bRet = SetRawNoDataValue(pRawNoData);
2838 : }
2839 57 : CPLFree(pRawNoData);
2840 57 : return bRet;
2841 : }
2842 :
2843 : /************************************************************************/
2844 : /* SetNoDataValue() */
2845 : /************************************************************************/
2846 :
2847 : /** Set the nodata value as a Int64.
2848 : *
2849 : * If the natural data type of the attribute/array is not Int64, type conversion
2850 : * will occur to the type returned by GetDataType().
2851 : *
2852 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2853 : *
2854 : * @return true in case of success.
2855 : *
2856 : * @since GDAL 3.5
2857 : */
2858 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2859 : {
2860 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2861 3 : bool bRet = false;
2862 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2863 6 : GDALExtendedDataType::Create(GDT_Int64),
2864 3 : pRawNoData, GetDataType()))
2865 : {
2866 3 : bRet = SetRawNoDataValue(pRawNoData);
2867 : }
2868 3 : CPLFree(pRawNoData);
2869 3 : return bRet;
2870 : }
2871 :
2872 : /************************************************************************/
2873 : /* SetNoDataValue() */
2874 : /************************************************************************/
2875 :
2876 : /** Set the nodata value as a Int64.
2877 : *
2878 : * If the natural data type of the attribute/array is not Int64, type conversion
2879 : * will occur to the type returned by GetDataType().
2880 : *
2881 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2882 : *
2883 : * @return true in case of success.
2884 : *
2885 : * @since GDAL 3.5
2886 : */
2887 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2888 : {
2889 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2890 1 : bool bRet = false;
2891 1 : if (GDALExtendedDataType::CopyValue(
2892 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2893 1 : GetDataType()))
2894 : {
2895 1 : bRet = SetRawNoDataValue(pRawNoData);
2896 : }
2897 1 : CPLFree(pRawNoData);
2898 1 : return bRet;
2899 : }
2900 :
2901 : /************************************************************************/
2902 : /* Resize() */
2903 : /************************************************************************/
2904 :
2905 : /** Resize an array to new dimensions.
2906 : *
2907 : * Not all drivers may allow this operation, and with restrictions (e.g.
2908 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2909 : *
2910 : * Resizing a dimension used in other arrays will cause those other arrays
2911 : * to be resized.
2912 : *
2913 : * This is the same as the C function GDALMDArrayResize().
2914 : *
2915 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2916 : * new size of each indexing dimension.
2917 : * @param papszOptions Options. (Driver specific)
2918 : * @return true in case of success.
2919 : * @since GDAL 3.7
2920 : */
2921 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2922 : CPL_UNUSED CSLConstList papszOptions)
2923 : {
2924 0 : CPLError(CE_Failure, CPLE_NotSupported,
2925 : "Resize() is not supported for this array");
2926 0 : return false;
2927 : }
2928 :
2929 : /************************************************************************/
2930 : /* SetScale() */
2931 : /************************************************************************/
2932 :
2933 : /** Set the scale value to apply to raw values.
2934 : *
2935 : * unscaled_value = raw_value * GetScale() + GetOffset()
2936 : *
2937 : * This is the same as the C function GDALMDArraySetScale() /
2938 : * GDALMDArraySetScaleEx().
2939 : *
2940 : * @note Driver implementation: this method shall be implemented if setting
2941 : * scale is supported.
2942 : *
2943 : * @param dfScale scale
2944 : * @param eStorageType Data type to which create the potential attribute that
2945 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2946 : * implementation will decide automatically the data type. Note that changing
2947 : * the data type after initial setting might not be supported.
2948 : * @return true in case of success.
2949 : */
2950 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2951 : CPL_UNUSED GDALDataType eStorageType)
2952 : {
2953 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2954 0 : return false;
2955 : }
2956 :
2957 : /************************************************************************/
2958 : /* SetOffset) */
2959 : /************************************************************************/
2960 :
2961 : /** Set the offset value to apply to raw values.
2962 : *
2963 : * unscaled_value = raw_value * GetScale() + GetOffset()
2964 : *
2965 : * This is the same as the C function GDALMDArraySetOffset() /
2966 : * GDALMDArraySetOffsetEx().
2967 : *
2968 : * @note Driver implementation: this method shall be implemented if setting
2969 : * offset is supported.
2970 : *
2971 : * @param dfOffset Offset
2972 : * @param eStorageType Data type to which create the potential attribute that
2973 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2974 : * implementation will decide automatically the data type. Note that changing
2975 : * the data type after initial setting might not be supported.
2976 : * @return true in case of success.
2977 : */
2978 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2979 : CPL_UNUSED GDALDataType eStorageType)
2980 : {
2981 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2982 0 : return false;
2983 : }
2984 :
2985 : /************************************************************************/
2986 : /* GetScale() */
2987 : /************************************************************************/
2988 :
2989 : /** Get the scale value to apply to raw values.
2990 : *
2991 : * unscaled_value = raw_value * GetScale() + GetOffset()
2992 : *
2993 : * This is the same as the C function GDALMDArrayGetScale().
2994 : *
2995 : * @note Driver implementation: this method shall be implemented if getting
2996 : * scale is supported.
2997 : *
2998 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2999 : * a scale value exists. Might be nullptr.
3000 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3001 : * the storage type of the scale value, when known/relevant. Otherwise will be
3002 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3003 : *
3004 : * @return the scale value. A 1.0 value might also indicate the
3005 : * absence of a scale value.
3006 : */
3007 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3008 : CPL_UNUSED GDALDataType *peStorageType) const
3009 : {
3010 20 : if (pbHasScale)
3011 20 : *pbHasScale = false;
3012 20 : return 1.0;
3013 : }
3014 :
3015 : /************************************************************************/
3016 : /* GetOffset() */
3017 : /************************************************************************/
3018 :
3019 : /** Get the offset value to apply to raw values.
3020 : *
3021 : * unscaled_value = raw_value * GetScale() + GetOffset()
3022 : *
3023 : * This is the same as the C function GDALMDArrayGetOffset().
3024 : *
3025 : * @note Driver implementation: this method shall be implemented if getting
3026 : * offset is supported.
3027 : *
3028 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3029 : * a offset value exists. Might be nullptr.
3030 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3031 : * the storage type of the offset value, when known/relevant. Otherwise will be
3032 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3033 : *
3034 : * @return the offset value. A 0.0 value might also indicate the
3035 : * absence of a offset value.
3036 : */
3037 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3038 : CPL_UNUSED GDALDataType *peStorageType) const
3039 : {
3040 20 : if (pbHasOffset)
3041 20 : *pbHasOffset = false;
3042 20 : return 0.0;
3043 : }
3044 :
3045 : /************************************************************************/
3046 : /* ProcessPerChunk() */
3047 : /************************************************************************/
3048 :
3049 : namespace
3050 : {
3051 : enum class Caller
3052 : {
3053 : CALLER_END_OF_LOOP,
3054 : CALLER_IN_LOOP,
3055 : };
3056 : }
3057 :
3058 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3059 : *
3060 : * This method is to be used when doing operations on an array, or a subset of
3061 : * it, in a chunk by chunk way.
3062 : *
3063 : * @param arrayStartIdx Values representing the starting index to use
3064 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3065 : * Array of GetDimensionCount() values. Must not be
3066 : * nullptr, unless for a zero-dimensional array.
3067 : *
3068 : * @param count Values representing the number of values to use in
3069 : * each dimension.
3070 : * Array of GetDimensionCount() values. Must not be
3071 : * nullptr, unless for a zero-dimensional array.
3072 : *
3073 : * @param chunkSize Values representing the chunk size in each dimension.
3074 : * Might typically the output of GetProcessingChunkSize().
3075 : * Array of GetDimensionCount() values. Must not be
3076 : * nullptr, unless for a zero-dimensional array.
3077 : *
3078 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3079 : * Must NOT be nullptr.
3080 : *
3081 : * @param pUserData Pointer to pass as the value of the pUserData argument
3082 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3083 : *
3084 : * @return true in case of success.
3085 : */
3086 69 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3087 : const GUInt64 *count,
3088 : const size_t *chunkSize,
3089 : FuncProcessPerChunkType pfnFunc,
3090 : void *pUserData)
3091 : {
3092 69 : const auto &dims = GetDimensions();
3093 69 : if (dims.empty())
3094 : {
3095 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3096 : }
3097 :
3098 : // Sanity check
3099 67 : size_t nTotalChunkSize = 1;
3100 174 : for (size_t i = 0; i < dims.size(); i++)
3101 : {
3102 114 : const auto nSizeThisDim(dims[i]->GetSize());
3103 114 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3104 112 : arrayStartIdx[i] > nSizeThisDim - count[i])
3105 : {
3106 4 : CPLError(CE_Failure, CPLE_AppDefined,
3107 : "Inconsistent arrayStartIdx[] / count[] values "
3108 : "regarding array size");
3109 4 : return false;
3110 : }
3111 218 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3112 108 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3113 : {
3114 3 : CPLError(CE_Failure, CPLE_AppDefined,
3115 : "Inconsistent chunkSize[] values");
3116 3 : return false;
3117 : }
3118 107 : nTotalChunkSize *= chunkSize[i];
3119 : }
3120 :
3121 60 : size_t dimIdx = 0;
3122 120 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3123 120 : std::vector<size_t> chunkCount(dims.size());
3124 :
3125 : struct Stack
3126 : {
3127 : GUInt64 nBlockCounter = 0;
3128 : GUInt64 nBlocksMinusOne = 0;
3129 : size_t first_count = 0; // only used if nBlocks > 1
3130 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3131 : };
3132 :
3133 120 : std::vector<Stack> stack(dims.size());
3134 60 : GUInt64 iCurChunk = 0;
3135 60 : GUInt64 nChunkCount = 1;
3136 166 : for (size_t i = 0; i < dims.size(); i++)
3137 : {
3138 106 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3139 106 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3140 106 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3141 106 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3142 106 : if (stack[i].nBlocksMinusOne == 0)
3143 : {
3144 101 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3145 101 : chunkCount[i] = static_cast<size_t>(count[i]);
3146 : }
3147 : else
3148 : {
3149 5 : stack[i].first_count = static_cast<size_t>(
3150 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3151 : }
3152 : }
3153 :
3154 60 : lbl_next_depth:
3155 276 : if (dimIdx == dims.size())
3156 : {
3157 93 : ++iCurChunk;
3158 93 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3159 : iCurChunk, nChunkCount, pUserData))
3160 : {
3161 0 : return false;
3162 : }
3163 : }
3164 : else
3165 : {
3166 183 : if (stack[dimIdx].nBlocksMinusOne != 0)
3167 : {
3168 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3169 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3170 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3171 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3172 : while (true)
3173 : {
3174 33 : dimIdx++;
3175 33 : goto lbl_next_depth;
3176 33 : lbl_return_to_caller_in_loop:
3177 33 : --stack[dimIdx].nBlockCounter;
3178 33 : if (stack[dimIdx].nBlockCounter == 0)
3179 11 : break;
3180 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3181 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3182 : }
3183 :
3184 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3185 22 : chunkCount[dimIdx] =
3186 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3187 11 : chunkArrayStartIdx[dimIdx]);
3188 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3189 : }
3190 183 : dimIdx++;
3191 183 : goto lbl_next_depth;
3192 183 : lbl_return_to_caller_end_of_loop:
3193 183 : if (dimIdx == 0)
3194 60 : goto end;
3195 : }
3196 :
3197 216 : assert(dimIdx > 0);
3198 216 : dimIdx--;
3199 : // cppcheck-suppress negativeContainerIndex
3200 216 : switch (stack[dimIdx].return_point)
3201 : {
3202 183 : case Caller::CALLER_END_OF_LOOP:
3203 183 : goto lbl_return_to_caller_end_of_loop;
3204 33 : case Caller::CALLER_IN_LOOP:
3205 33 : goto lbl_return_to_caller_in_loop;
3206 : }
3207 60 : end:
3208 60 : return true;
3209 : }
3210 :
3211 : /************************************************************************/
3212 : /* GDALAttribute() */
3213 : /************************************************************************/
3214 :
3215 : //! @cond Doxygen_Suppress
3216 14459 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3217 0 : CPL_UNUSED const std::string &osName)
3218 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3219 14459 : : GDALAbstractMDArray(osParentName, osName)
3220 : #endif
3221 : {
3222 14459 : }
3223 :
3224 : GDALAttribute::~GDALAttribute() = default;
3225 :
3226 : //! @endcond
3227 :
3228 : /************************************************************************/
3229 : /* GetDimensionSize() */
3230 : /************************************************************************/
3231 :
3232 : /** Return the size of the dimensions of the attribute.
3233 : *
3234 : * This will be an empty array for a scalar (single value) attribute.
3235 : *
3236 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3237 : */
3238 611 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3239 : {
3240 611 : const auto &dims = GetDimensions();
3241 611 : std::vector<GUInt64> ret;
3242 611 : ret.reserve(dims.size());
3243 761 : for (const auto &dim : dims)
3244 150 : ret.push_back(dim->GetSize());
3245 611 : return ret;
3246 : }
3247 :
3248 : /************************************************************************/
3249 : /* GDALRawResult() */
3250 : /************************************************************************/
3251 :
3252 : //! @cond Doxygen_Suppress
3253 166 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3254 166 : size_t nEltCount)
3255 332 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3256 166 : m_raw(raw)
3257 : {
3258 166 : }
3259 :
3260 : //! @endcond
3261 :
3262 : /************************************************************************/
3263 : /* GDALRawResult() */
3264 : /************************************************************************/
3265 :
3266 : /** Move constructor. */
3267 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3268 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3269 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3270 : {
3271 0 : other.m_nEltCount = 0;
3272 0 : other.m_nSize = 0;
3273 0 : other.m_raw = nullptr;
3274 0 : }
3275 :
3276 : /************************************************************************/
3277 : /* FreeMe() */
3278 : /************************************************************************/
3279 :
3280 166 : void GDALRawResult::FreeMe()
3281 : {
3282 166 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3283 : {
3284 60 : GByte *pabyPtr = m_raw;
3285 60 : const auto nDTSize(m_dt.GetSize());
3286 120 : for (size_t i = 0; i < m_nEltCount; ++i)
3287 : {
3288 60 : m_dt.FreeDynamicMemory(pabyPtr);
3289 60 : pabyPtr += nDTSize;
3290 : }
3291 : }
3292 166 : VSIFree(m_raw);
3293 166 : }
3294 :
3295 : /************************************************************************/
3296 : /* operator=() */
3297 : /************************************************************************/
3298 :
3299 : /** Move assignment. */
3300 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3301 : {
3302 0 : FreeMe();
3303 0 : m_dt = std::move(other.m_dt);
3304 0 : m_nEltCount = other.m_nEltCount;
3305 0 : m_nSize = other.m_nSize;
3306 0 : m_raw = other.m_raw;
3307 0 : other.m_nEltCount = 0;
3308 0 : other.m_nSize = 0;
3309 0 : other.m_raw = nullptr;
3310 0 : return *this;
3311 : }
3312 :
3313 : /************************************************************************/
3314 : /* ~GDALRawResult() */
3315 : /************************************************************************/
3316 :
3317 : /** Destructor. */
3318 166 : GDALRawResult::~GDALRawResult()
3319 : {
3320 166 : FreeMe();
3321 166 : }
3322 :
3323 : /************************************************************************/
3324 : /* StealData() */
3325 : /************************************************************************/
3326 :
3327 : //! @cond Doxygen_Suppress
3328 : /** Return buffer to caller which becomes owner of it.
3329 : * Only to be used by GDALAttributeReadAsRaw().
3330 : */
3331 6 : GByte *GDALRawResult::StealData()
3332 : {
3333 6 : GByte *ret = m_raw;
3334 6 : m_raw = nullptr;
3335 6 : m_nEltCount = 0;
3336 6 : m_nSize = 0;
3337 6 : return ret;
3338 : }
3339 :
3340 : //! @endcond
3341 :
3342 : /************************************************************************/
3343 : /* ReadAsRaw() */
3344 : /************************************************************************/
3345 :
3346 : /** Return the raw value of an attribute.
3347 : *
3348 : *
3349 : * This is the same as the C function GDALAttributeReadAsRaw().
3350 : */
3351 166 : GDALRawResult GDALAttribute::ReadAsRaw() const
3352 : {
3353 166 : const auto nEltCount(GetTotalElementsCount());
3354 166 : const auto &dt(GetDataType());
3355 166 : const auto nDTSize(dt.GetSize());
3356 : GByte *res = static_cast<GByte *>(
3357 166 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3358 166 : if (!res)
3359 0 : return GDALRawResult(nullptr, dt, 0);
3360 166 : const auto &dims = GetDimensions();
3361 166 : const auto nDims = GetDimensionCount();
3362 332 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3363 332 : std::vector<size_t> count(1 + nDims);
3364 189 : for (size_t i = 0; i < nDims; i++)
3365 : {
3366 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3367 : }
3368 166 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3369 166 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3370 : {
3371 0 : VSIFree(res);
3372 0 : return GDALRawResult(nullptr, dt, 0);
3373 : }
3374 166 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3375 : }
3376 :
3377 : /************************************************************************/
3378 : /* ReadAsString() */
3379 : /************************************************************************/
3380 :
3381 : /** Return the value of an attribute as a string.
3382 : *
3383 : * The returned string should not be freed, and its lifetime does not
3384 : * excess a next call to ReadAsString() on the same object, or the deletion
3385 : * of the object itself.
3386 : *
3387 : * This function will only return the first element if there are several.
3388 : *
3389 : * This is the same as the C function GDALAttributeReadAsString()
3390 : *
3391 : * @return a string, or nullptr.
3392 : */
3393 1529 : const char *GDALAttribute::ReadAsString() const
3394 : {
3395 1529 : const auto nDims = GetDimensionCount();
3396 3058 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3397 3058 : std::vector<size_t> count(1 + nDims, 1);
3398 1529 : char *szRet = nullptr;
3399 1529 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3400 1529 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3401 4586 : sizeof(szRet)) ||
3402 1528 : szRet == nullptr)
3403 : {
3404 5 : return nullptr;
3405 : }
3406 1524 : m_osCachedVal = szRet;
3407 1524 : CPLFree(szRet);
3408 1524 : return m_osCachedVal.c_str();
3409 : }
3410 :
3411 : /************************************************************************/
3412 : /* ReadAsInt() */
3413 : /************************************************************************/
3414 :
3415 : /** Return the value of an attribute as a integer.
3416 : *
3417 : * This function will only return the first element if there are several.
3418 : *
3419 : * It can fail if its value can not be converted to integer.
3420 : *
3421 : * This is the same as the C function GDALAttributeReadAsInt()
3422 : *
3423 : * @return a integer, or INT_MIN in case of error.
3424 : */
3425 226 : int GDALAttribute::ReadAsInt() const
3426 : {
3427 226 : const auto nDims = GetDimensionCount();
3428 452 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3429 226 : std::vector<size_t> count(1 + nDims, 1);
3430 226 : int nRet = INT_MIN;
3431 226 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3432 452 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3433 452 : return nRet;
3434 : }
3435 :
3436 : /************************************************************************/
3437 : /* ReadAsInt64() */
3438 : /************************************************************************/
3439 :
3440 : /** Return the value of an attribute as an int64_t.
3441 : *
3442 : * This function will only return the first element if there are several.
3443 : *
3444 : * It can fail if its value can not be converted to long.
3445 : *
3446 : * This is the same as the C function GDALAttributeReadAsInt64()
3447 : *
3448 : * @return an int64_t, or INT64_MIN in case of error.
3449 : */
3450 102 : int64_t GDALAttribute::ReadAsInt64() const
3451 : {
3452 102 : const auto nDims = GetDimensionCount();
3453 204 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3454 102 : std::vector<size_t> count(1 + nDims, 1);
3455 102 : int64_t nRet = INT64_MIN;
3456 102 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3457 204 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3458 204 : return nRet;
3459 : }
3460 :
3461 : /************************************************************************/
3462 : /* ReadAsDouble() */
3463 : /************************************************************************/
3464 :
3465 : /** Return the value of an attribute as a double.
3466 : *
3467 : * This function will only return the first element if there are several.
3468 : *
3469 : * It can fail if its value can not be converted to double.
3470 : *
3471 : * This is the same as the C function GDALAttributeReadAsInt()
3472 : *
3473 : * @return a double value.
3474 : */
3475 355 : double GDALAttribute::ReadAsDouble() const
3476 : {
3477 355 : const auto nDims = GetDimensionCount();
3478 710 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3479 355 : std::vector<size_t> count(1 + nDims, 1);
3480 355 : double dfRet = 0;
3481 355 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3482 355 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3483 355 : sizeof(dfRet));
3484 710 : return dfRet;
3485 : }
3486 :
3487 : /************************************************************************/
3488 : /* ReadAsStringArray() */
3489 : /************************************************************************/
3490 :
3491 : /** Return the value of an attribute as an array of strings.
3492 : *
3493 : * This is the same as the C function GDALAttributeReadAsStringArray()
3494 : */
3495 141 : CPLStringList GDALAttribute::ReadAsStringArray() const
3496 : {
3497 141 : const auto nElts = GetTotalElementsCount();
3498 141 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3499 0 : return CPLStringList();
3500 : char **papszList = static_cast<char **>(
3501 141 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3502 141 : const auto &dims = GetDimensions();
3503 141 : const auto nDims = GetDimensionCount();
3504 282 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3505 282 : std::vector<size_t> count(1 + nDims);
3506 225 : for (size_t i = 0; i < nDims; i++)
3507 : {
3508 84 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3509 : }
3510 141 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3511 141 : GDALExtendedDataType::CreateString(), papszList, papszList,
3512 141 : sizeof(char *) * static_cast<int>(nElts));
3513 577 : for (int i = 0; i < static_cast<int>(nElts); i++)
3514 : {
3515 436 : if (papszList[i] == nullptr)
3516 13 : papszList[i] = CPLStrdup("");
3517 : }
3518 141 : return CPLStringList(papszList);
3519 : }
3520 :
3521 : /************************************************************************/
3522 : /* ReadAsIntArray() */
3523 : /************************************************************************/
3524 :
3525 : /** Return the value of an attribute as an array of integers.
3526 : *
3527 : * This is the same as the C function GDALAttributeReadAsIntArray().
3528 : */
3529 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3530 : {
3531 15 : const auto nElts = GetTotalElementsCount();
3532 : #if SIZEOF_VOIDP == 4
3533 : if (nElts > static_cast<size_t>(nElts))
3534 : return {};
3535 : #endif
3536 15 : std::vector<int> res(static_cast<size_t>(nElts));
3537 15 : const auto &dims = GetDimensions();
3538 15 : const auto nDims = GetDimensionCount();
3539 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3540 30 : std::vector<size_t> count(1 + nDims);
3541 32 : for (size_t i = 0; i < nDims; i++)
3542 : {
3543 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3544 : }
3545 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3546 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3547 15 : res.size() * sizeof(res[0]));
3548 30 : return res;
3549 : }
3550 :
3551 : /************************************************************************/
3552 : /* ReadAsInt64Array() */
3553 : /************************************************************************/
3554 :
3555 : /** Return the value of an attribute as an array of int64_t.
3556 : *
3557 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3558 : */
3559 62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3560 : {
3561 62 : const auto nElts = GetTotalElementsCount();
3562 : #if SIZEOF_VOIDP == 4
3563 : if (nElts > static_cast<size_t>(nElts))
3564 : return {};
3565 : #endif
3566 62 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3567 62 : const auto &dims = GetDimensions();
3568 62 : const auto nDims = GetDimensionCount();
3569 124 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3570 124 : std::vector<size_t> count(1 + nDims);
3571 124 : for (size_t i = 0; i < nDims; i++)
3572 : {
3573 62 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3574 : }
3575 62 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3576 124 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3577 62 : res.size() * sizeof(res[0]));
3578 124 : return res;
3579 : }
3580 :
3581 : /************************************************************************/
3582 : /* ReadAsDoubleArray() */
3583 : /************************************************************************/
3584 :
3585 : /** Return the value of an attribute as an array of double.
3586 : *
3587 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3588 : */
3589 94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3590 : {
3591 94 : const auto nElts = GetTotalElementsCount();
3592 : #if SIZEOF_VOIDP == 4
3593 : if (nElts > static_cast<size_t>(nElts))
3594 : return {};
3595 : #endif
3596 94 : std::vector<double> res(static_cast<size_t>(nElts));
3597 94 : const auto &dims = GetDimensions();
3598 94 : const auto nDims = GetDimensionCount();
3599 188 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3600 188 : std::vector<size_t> count(1 + nDims);
3601 172 : for (size_t i = 0; i < nDims; i++)
3602 : {
3603 78 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3604 : }
3605 94 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3606 188 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3607 94 : res.size() * sizeof(res[0]));
3608 188 : return res;
3609 : }
3610 :
3611 : /************************************************************************/
3612 : /* Write() */
3613 : /************************************************************************/
3614 :
3615 : /** Write an attribute from raw values expressed in GetDataType()
3616 : *
3617 : * The values should be provided in the type of GetDataType() and there should
3618 : * be exactly GetTotalElementsCount() of them.
3619 : * If GetDataType() is a string, each value should be a char* pointer.
3620 : *
3621 : * This is the same as the C function GDALAttributeWriteRaw().
3622 : *
3623 : * @param pabyValue Buffer of nLen bytes.
3624 : * @param nLen Size of pabyValue in bytes. Should be equal to
3625 : * GetTotalElementsCount() * GetDataType().GetSize()
3626 : * @return true in case of success.
3627 : */
3628 106 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3629 : {
3630 106 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3631 : {
3632 0 : CPLError(CE_Failure, CPLE_AppDefined,
3633 : "Length is not of expected value");
3634 0 : return false;
3635 : }
3636 106 : const auto &dims = GetDimensions();
3637 106 : const auto nDims = GetDimensionCount();
3638 212 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3639 212 : std::vector<size_t> count(1 + nDims);
3640 129 : for (size_t i = 0; i < nDims; i++)
3641 : {
3642 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3643 : }
3644 106 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3645 106 : pabyValue, pabyValue, nLen);
3646 : }
3647 :
3648 : /************************************************************************/
3649 : /* Write() */
3650 : /************************************************************************/
3651 :
3652 : /** Write an attribute from a string value.
3653 : *
3654 : * Type conversion will be performed if needed. If the attribute contains
3655 : * multiple values, only the first one will be updated.
3656 : *
3657 : * This is the same as the C function GDALAttributeWriteString().
3658 : *
3659 : * @param pszValue Pointer to a string.
3660 : * @return true in case of success.
3661 : */
3662 312 : bool GDALAttribute::Write(const char *pszValue)
3663 : {
3664 312 : const auto nDims = GetDimensionCount();
3665 624 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3666 312 : std::vector<size_t> count(1 + nDims, 1);
3667 312 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3668 624 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3669 624 : sizeof(pszValue));
3670 : }
3671 :
3672 : /************************************************************************/
3673 : /* WriteInt() */
3674 : /************************************************************************/
3675 :
3676 : /** Write an attribute from a integer value.
3677 : *
3678 : * Type conversion will be performed if needed. If the attribute contains
3679 : * multiple values, only the first one will be updated.
3680 : *
3681 : * This is the same as the C function GDALAttributeWriteInt().
3682 : *
3683 : * @param nVal Value.
3684 : * @return true in case of success.
3685 : */
3686 22 : bool GDALAttribute::WriteInt(int nVal)
3687 : {
3688 22 : const auto nDims = GetDimensionCount();
3689 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3690 22 : std::vector<size_t> count(1 + nDims, 1);
3691 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3692 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3693 44 : sizeof(nVal));
3694 : }
3695 :
3696 : /************************************************************************/
3697 : /* WriteInt64() */
3698 : /************************************************************************/
3699 :
3700 : /** Write an attribute from an int64_t value.
3701 : *
3702 : * Type conversion will be performed if needed. If the attribute contains
3703 : * multiple values, only the first one will be updated.
3704 : *
3705 : * This is the same as the C function GDALAttributeWriteInt().
3706 : *
3707 : * @param nVal Value.
3708 : * @return true in case of success.
3709 : */
3710 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3711 : {
3712 11 : const auto nDims = GetDimensionCount();
3713 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3714 11 : std::vector<size_t> count(1 + nDims, 1);
3715 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3716 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3717 22 : sizeof(nVal));
3718 : }
3719 :
3720 : /************************************************************************/
3721 : /* Write() */
3722 : /************************************************************************/
3723 :
3724 : /** Write an attribute from a double value.
3725 : *
3726 : * Type conversion will be performed if needed. If the attribute contains
3727 : * multiple values, only the first one will be updated.
3728 : *
3729 : * This is the same as the C function GDALAttributeWriteDouble().
3730 : *
3731 : * @param dfVal Value.
3732 : * @return true in case of success.
3733 : */
3734 38 : bool GDALAttribute::Write(double dfVal)
3735 : {
3736 38 : const auto nDims = GetDimensionCount();
3737 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3738 38 : std::vector<size_t> count(1 + nDims, 1);
3739 38 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3740 76 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3741 76 : sizeof(dfVal));
3742 : }
3743 :
3744 : /************************************************************************/
3745 : /* Write() */
3746 : /************************************************************************/
3747 :
3748 : /** Write an attribute from an array of strings.
3749 : *
3750 : * Type conversion will be performed if needed.
3751 : *
3752 : * Exactly GetTotalElementsCount() strings must be provided
3753 : *
3754 : * This is the same as the C function GDALAttributeWriteStringArray().
3755 : *
3756 : * @param vals Array of strings.
3757 : * @return true in case of success.
3758 : */
3759 8 : bool GDALAttribute::Write(CSLConstList vals)
3760 : {
3761 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3762 : {
3763 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3764 1 : return false;
3765 : }
3766 7 : const auto nDims = GetDimensionCount();
3767 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3768 7 : std::vector<size_t> count(1 + nDims);
3769 7 : const auto &dims = GetDimensions();
3770 15 : for (size_t i = 0; i < nDims; i++)
3771 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3772 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3773 7 : GDALExtendedDataType::CreateString(), vals, vals,
3774 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3775 : }
3776 :
3777 : /************************************************************************/
3778 : /* Write() */
3779 : /************************************************************************/
3780 :
3781 : /** Write an attribute from an array of int.
3782 : *
3783 : * Type conversion will be performed if needed.
3784 : *
3785 : * Exactly GetTotalElementsCount() strings must be provided
3786 : *
3787 : * This is the same as the C function GDALAttributeWriteIntArray()
3788 : *
3789 : * @param vals Array of int.
3790 : * @param nVals Should be equal to GetTotalElementsCount().
3791 : * @return true in case of success.
3792 : */
3793 11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3794 : {
3795 11 : if (nVals != GetTotalElementsCount())
3796 : {
3797 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3798 1 : return false;
3799 : }
3800 10 : const auto nDims = GetDimensionCount();
3801 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3802 10 : std::vector<size_t> count(1 + nDims);
3803 10 : const auto &dims = GetDimensions();
3804 20 : for (size_t i = 0; i < nDims; i++)
3805 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3806 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3807 10 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3808 20 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3809 : }
3810 :
3811 : /************************************************************************/
3812 : /* Write() */
3813 : /************************************************************************/
3814 :
3815 : /** Write an attribute from an array of int64_t.
3816 : *
3817 : * Type conversion will be performed if needed.
3818 : *
3819 : * Exactly GetTotalElementsCount() strings must be provided
3820 : *
3821 : * This is the same as the C function GDALAttributeWriteLongArray()
3822 : *
3823 : * @param vals Array of int64_t.
3824 : * @param nVals Should be equal to GetTotalElementsCount().
3825 : * @return true in case of success.
3826 : */
3827 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3828 : {
3829 10 : if (nVals != GetTotalElementsCount())
3830 : {
3831 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3832 0 : return false;
3833 : }
3834 10 : const auto nDims = GetDimensionCount();
3835 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3836 10 : std::vector<size_t> count(1 + nDims);
3837 10 : const auto &dims = GetDimensions();
3838 20 : for (size_t i = 0; i < nDims; i++)
3839 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3840 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3841 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3842 10 : static_cast<size_t>(GetTotalElementsCount()) *
3843 10 : sizeof(int64_t));
3844 : }
3845 :
3846 : /************************************************************************/
3847 : /* Write() */
3848 : /************************************************************************/
3849 :
3850 : /** Write an attribute from an array of double.
3851 : *
3852 : * Type conversion will be performed if needed.
3853 : *
3854 : * Exactly GetTotalElementsCount() strings must be provided
3855 : *
3856 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3857 : *
3858 : * @param vals Array of double.
3859 : * @param nVals Should be equal to GetTotalElementsCount().
3860 : * @return true in case of success.
3861 : */
3862 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3863 : {
3864 7 : if (nVals != GetTotalElementsCount())
3865 : {
3866 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3867 1 : return false;
3868 : }
3869 6 : const auto nDims = GetDimensionCount();
3870 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3871 6 : std::vector<size_t> count(1 + nDims);
3872 6 : const auto &dims = GetDimensions();
3873 13 : for (size_t i = 0; i < nDims; i++)
3874 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3875 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3876 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3877 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3878 : }
3879 :
3880 : /************************************************************************/
3881 : /* GDALMDArray() */
3882 : /************************************************************************/
3883 :
3884 : //! @cond Doxygen_Suppress
3885 6599 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3886 : CPL_UNUSED const std::string &osName,
3887 0 : const std::string &osContext)
3888 : :
3889 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3890 : GDALAbstractMDArray(osParentName, osName),
3891 : #endif
3892 6599 : m_osContext(osContext)
3893 : {
3894 6599 : }
3895 :
3896 : //! @endcond
3897 :
3898 : /************************************************************************/
3899 : /* GetTotalCopyCost() */
3900 : /************************************************************************/
3901 :
3902 : /** Return a total "cost" to copy the array.
3903 : *
3904 : * Used as a parameter for CopyFrom()
3905 : */
3906 51 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3907 : {
3908 102 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3909 102 : GetTotalElementsCount() * GetDataType().GetSize();
3910 : }
3911 :
3912 : /************************************************************************/
3913 : /* CopyFromAllExceptValues() */
3914 : /************************************************************************/
3915 :
3916 : //! @cond Doxygen_Suppress
3917 :
3918 177 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3919 : bool bStrict, GUInt64 &nCurCost,
3920 : const GUInt64 nTotalCost,
3921 : GDALProgressFunc pfnProgress,
3922 : void *pProgressData)
3923 : {
3924 : // Nodata setting must be one of the first things done for TileDB
3925 177 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3926 177 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3927 : {
3928 13 : SetRawNoDataValue(pNoData);
3929 : }
3930 :
3931 177 : const bool bThisIsUnscaledArray =
3932 177 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3933 354 : auto attrs = poSrcArray->GetAttributes();
3934 228 : for (const auto &attr : attrs)
3935 : {
3936 51 : const auto &osAttrName = attr->GetName();
3937 51 : if (bThisIsUnscaledArray)
3938 : {
3939 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3940 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3941 1 : osAttrName == "valid_range")
3942 : {
3943 1 : continue;
3944 : }
3945 : }
3946 :
3947 50 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3948 100 : attr->GetDataType());
3949 50 : if (!dstAttr)
3950 : {
3951 0 : if (bStrict)
3952 0 : return false;
3953 0 : continue;
3954 : }
3955 50 : auto raw = attr->ReadAsRaw();
3956 50 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3957 0 : return false;
3958 : }
3959 177 : if (!attrs.empty())
3960 : {
3961 28 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3962 49 : if (pfnProgress &&
3963 21 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3964 0 : return false;
3965 : }
3966 :
3967 177 : auto srcSRS = poSrcArray->GetSpatialRef();
3968 177 : if (srcSRS)
3969 : {
3970 11 : SetSpatialRef(srcSRS.get());
3971 : }
3972 :
3973 177 : const std::string &osUnit(poSrcArray->GetUnit());
3974 177 : if (!osUnit.empty())
3975 : {
3976 20 : SetUnit(osUnit);
3977 : }
3978 :
3979 177 : bool bGotValue = false;
3980 177 : GDALDataType eOffsetStorageType = GDT_Unknown;
3981 : const double dfOffset =
3982 177 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3983 177 : if (bGotValue)
3984 : {
3985 3 : SetOffset(dfOffset, eOffsetStorageType);
3986 : }
3987 :
3988 177 : bGotValue = false;
3989 177 : GDALDataType eScaleStorageType = GDT_Unknown;
3990 177 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3991 177 : if (bGotValue)
3992 : {
3993 3 : SetScale(dfScale, eScaleStorageType);
3994 : }
3995 :
3996 177 : return true;
3997 : }
3998 :
3999 : //! @endcond
4000 :
4001 : /************************************************************************/
4002 : /* CopyFrom() */
4003 : /************************************************************************/
4004 :
4005 : /** Copy the content of an array into a new (generally empty) array.
4006 : *
4007 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4008 : * of some output drivers this is not recommended)
4009 : * @param poSrcArray Source array. Should NOT be nullptr.
4010 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4011 : * stop the copy. In relaxed mode, the copy will be attempted to
4012 : * be pursued.
4013 : * @param nCurCost Should be provided as a variable initially set to 0.
4014 : * @param nTotalCost Total cost from GetTotalCopyCost().
4015 : * @param pfnProgress Progress callback, or nullptr.
4016 : * @param pProgressData Progress user data, or nulptr.
4017 : *
4018 : * @return true in case of success (or partial success if bStrict == false).
4019 : */
4020 49 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4021 : const GDALMDArray *poSrcArray, bool bStrict,
4022 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4023 : GDALProgressFunc pfnProgress, void *pProgressData)
4024 : {
4025 49 : if (pfnProgress == nullptr)
4026 4 : pfnProgress = GDALDummyProgress;
4027 :
4028 49 : nCurCost += GDALMDArray::COPY_COST;
4029 :
4030 49 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4031 : pfnProgress, pProgressData))
4032 : {
4033 0 : return false;
4034 : }
4035 :
4036 49 : const auto &dims = poSrcArray->GetDimensions();
4037 49 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4038 49 : if (dims.empty())
4039 : {
4040 2 : std::vector<GByte> abyTmp(nDTSize);
4041 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4042 2 : GetDataType(), &abyTmp[0]) &&
4043 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4044 4 : &abyTmp[0])) &&
4045 : bStrict)
4046 : {
4047 0 : return false;
4048 : }
4049 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4050 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4051 0 : return false;
4052 : }
4053 : else
4054 : {
4055 47 : std::vector<GUInt64> arrayStartIdx(dims.size());
4056 47 : std::vector<GUInt64> count(dims.size());
4057 127 : for (size_t i = 0; i < dims.size(); i++)
4058 : {
4059 80 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4060 : }
4061 :
4062 : struct CopyFunc
4063 : {
4064 : GDALMDArray *poDstArray = nullptr;
4065 : std::vector<GByte> abyTmp{};
4066 : GDALProgressFunc pfnProgress = nullptr;
4067 : void *pProgressData = nullptr;
4068 : GUInt64 nCurCost = 0;
4069 : GUInt64 nTotalCost = 0;
4070 : GUInt64 nTotalBytesThisArray = 0;
4071 : bool bStop = false;
4072 :
4073 65 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4074 : const GUInt64 *chunkArrayStartIdx,
4075 : const size_t *chunkCount, GUInt64 iCurChunk,
4076 : GUInt64 nChunkCount, void *pUserData)
4077 : {
4078 65 : const auto &dt(l_poSrcArray->GetDataType());
4079 65 : auto data = static_cast<CopyFunc *>(pUserData);
4080 65 : auto poDstArray = data->poDstArray;
4081 65 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4082 65 : nullptr, dt, &data->abyTmp[0]))
4083 : {
4084 0 : return false;
4085 : }
4086 : bool bRet =
4087 65 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4088 65 : nullptr, dt, &data->abyTmp[0]);
4089 65 : if (dt.NeedsFreeDynamicMemory())
4090 : {
4091 4 : const auto l_nDTSize = dt.GetSize();
4092 4 : GByte *ptr = &data->abyTmp[0];
4093 4 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4094 4 : size_t nEltCount = 1;
4095 8 : for (size_t i = 0; i < l_nDims; ++i)
4096 : {
4097 4 : nEltCount *= chunkCount[i];
4098 : }
4099 20 : for (size_t i = 0; i < nEltCount; i++)
4100 : {
4101 16 : dt.FreeDynamicMemory(ptr);
4102 16 : ptr += l_nDTSize;
4103 : }
4104 : }
4105 65 : if (!bRet)
4106 : {
4107 0 : return false;
4108 : }
4109 :
4110 65 : double dfCurCost =
4111 65 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4112 65 : data->nTotalBytesThisArray;
4113 65 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4114 : data->pProgressData))
4115 : {
4116 0 : data->bStop = true;
4117 0 : return false;
4118 : }
4119 :
4120 65 : return true;
4121 : }
4122 : };
4123 :
4124 47 : CopyFunc copyFunc;
4125 47 : copyFunc.poDstArray = this;
4126 47 : copyFunc.nCurCost = nCurCost;
4127 47 : copyFunc.nTotalCost = nTotalCost;
4128 47 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4129 47 : copyFunc.pfnProgress = pfnProgress;
4130 47 : copyFunc.pProgressData = pProgressData;
4131 : const char *pszSwathSize =
4132 47 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4133 : const size_t nMaxChunkSize =
4134 : pszSwathSize
4135 47 : ? static_cast<size_t>(
4136 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4137 1 : CPLAtoGIntBig(pszSwathSize)))
4138 : : static_cast<size_t>(
4139 46 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4140 46 : GDALGetCacheMax64() / 4));
4141 47 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4142 47 : size_t nRealChunkSize = nDTSize;
4143 127 : for (const auto &nChunkSize : anChunkSizes)
4144 : {
4145 80 : nRealChunkSize *= nChunkSize;
4146 : }
4147 : try
4148 : {
4149 47 : copyFunc.abyTmp.resize(nRealChunkSize);
4150 : }
4151 0 : catch (const std::exception &)
4152 : {
4153 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4154 : "Cannot allocate temporary buffer");
4155 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4156 0 : return false;
4157 : }
4158 140 : if (copyFunc.nTotalBytesThisArray != 0 &&
4159 46 : !const_cast<GDALMDArray *>(poSrcArray)
4160 46 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4161 : anChunkSizes.data(), CopyFunc::f,
4162 93 : ©Func) &&
4163 0 : (bStrict || copyFunc.bStop))
4164 : {
4165 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4166 0 : return false;
4167 : }
4168 47 : nCurCost += copyFunc.nTotalBytesThisArray;
4169 : }
4170 :
4171 49 : return true;
4172 : }
4173 :
4174 : /************************************************************************/
4175 : /* GetStructuralInfo() */
4176 : /************************************************************************/
4177 :
4178 : /** Return structural information on the array.
4179 : *
4180 : * This may be the compression, etc..
4181 : *
4182 : * The return value should not be freed and is valid until GDALMDArray is
4183 : * released or this function called again.
4184 : *
4185 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4186 : */
4187 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4188 : {
4189 95 : return nullptr;
4190 : }
4191 :
4192 : /************************************************************************/
4193 : /* AdviseRead() */
4194 : /************************************************************************/
4195 :
4196 : /** Advise driver of upcoming read requests.
4197 : *
4198 : * Some GDAL drivers operate more efficiently if they know in advance what
4199 : * set of upcoming read requests will be made. The AdviseRead() method allows
4200 : * an application to notify the driver of the region of interest.
4201 : *
4202 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4203 : * accelerate access via some drivers. One such case is when reading through
4204 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4205 : * with the region of interest defined by AdviseRead())
4206 : *
4207 : * This is the same as the C function GDALMDArrayAdviseRead().
4208 : *
4209 : * @param arrayStartIdx Values representing the starting index to read
4210 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4211 : * Array of GetDimensionCount() values.
4212 : * Can be nullptr as a synonymous for [0 for i in
4213 : * range(GetDimensionCount() ]
4214 : *
4215 : * @param count Values representing the number of values to extract in
4216 : * each dimension.
4217 : * Array of GetDimensionCount() values.
4218 : * Can be nullptr as a synonymous for
4219 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4220 : * range(GetDimensionCount() ]
4221 : *
4222 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4223 : * documentation.
4224 : *
4225 : * @return true in case of success (ignoring the advice is a success)
4226 : *
4227 : * @since GDAL 3.2
4228 : */
4229 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4230 : CSLConstList papszOptions) const
4231 : {
4232 25 : const auto nDimCount = GetDimensionCount();
4233 25 : if (nDimCount == 0)
4234 2 : return true;
4235 :
4236 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4237 23 : if (arrayStartIdx == nullptr)
4238 : {
4239 0 : tmp_arrayStartIdx.resize(nDimCount);
4240 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4241 : }
4242 :
4243 46 : std::vector<size_t> tmp_count;
4244 23 : if (count == nullptr)
4245 : {
4246 0 : tmp_count.resize(nDimCount);
4247 0 : const auto &dims = GetDimensions();
4248 0 : for (size_t i = 0; i < nDimCount; i++)
4249 : {
4250 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4251 : #if SIZEOF_VOIDP < 8
4252 : if (nSize != static_cast<size_t>(nSize))
4253 : {
4254 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4255 : return false;
4256 : }
4257 : #endif
4258 0 : tmp_count[i] = static_cast<size_t>(nSize);
4259 : }
4260 0 : count = tmp_count.data();
4261 : }
4262 :
4263 46 : std::vector<GInt64> tmp_arrayStep;
4264 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4265 23 : const GInt64 *arrayStep = nullptr;
4266 23 : const GPtrDiff_t *bufferStride = nullptr;
4267 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4268 46 : GDALExtendedDataType::Create(GDT_Unknown),
4269 : nullptr, nullptr, 0, tmp_arrayStep,
4270 : tmp_bufferStride))
4271 : {
4272 1 : return false;
4273 : }
4274 :
4275 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4276 : }
4277 :
4278 : /************************************************************************/
4279 : /* IAdviseRead() */
4280 : /************************************************************************/
4281 :
4282 : //! @cond Doxygen_Suppress
4283 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4284 : CSLConstList /* papszOptions*/) const
4285 : {
4286 3 : return true;
4287 : }
4288 :
4289 : //! @endcond
4290 :
4291 : /************************************************************************/
4292 : /* MassageName() */
4293 : /************************************************************************/
4294 :
4295 : //! @cond Doxygen_Suppress
4296 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4297 : {
4298 32 : std::string ret;
4299 604 : for (const char ch : inputName)
4300 : {
4301 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4302 138 : ret += '_';
4303 : else
4304 434 : ret += ch;
4305 : }
4306 32 : return ret;
4307 : }
4308 :
4309 : //! @endcond
4310 :
4311 : /************************************************************************/
4312 : /* GetCacheRootGroup() */
4313 : /************************************************************************/
4314 :
4315 : //! @cond Doxygen_Suppress
4316 : std::shared_ptr<GDALGroup>
4317 1447 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4318 : std::string &osCacheFilenameOut) const
4319 : {
4320 1447 : const auto &osFilename = GetFilename();
4321 1447 : if (osFilename.empty())
4322 : {
4323 1 : CPLError(CE_Failure, CPLE_AppDefined,
4324 : "Cannot cache an array with an empty filename");
4325 1 : return nullptr;
4326 : }
4327 :
4328 1446 : osCacheFilenameOut = osFilename + ".gmac";
4329 1446 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4330 : {
4331 0 : const auto nPosQuestionMark = osFilename.find('?');
4332 0 : if (nPosQuestionMark != std::string::npos)
4333 : {
4334 : osCacheFilenameOut =
4335 0 : osFilename.substr(0, nPosQuestionMark)
4336 0 : .append(".gmac")
4337 0 : .append(osFilename.substr(nPosQuestionMark));
4338 : }
4339 : }
4340 1446 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4341 1446 : if (pszProxy != nullptr)
4342 7 : osCacheFilenameOut = pszProxy;
4343 :
4344 1446 : std::unique_ptr<GDALDataset> poDS;
4345 : VSIStatBufL sStat;
4346 1446 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4347 : {
4348 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4349 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4350 : nullptr, nullptr, nullptr));
4351 : }
4352 1446 : if (poDS)
4353 : {
4354 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4355 28 : return poDS->GetRootGroup();
4356 : }
4357 :
4358 1418 : if (bCanCreate)
4359 : {
4360 4 : const char *pszDrvName = "netCDF";
4361 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4362 4 : if (poDrv == nullptr)
4363 : {
4364 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4365 : pszDrvName);
4366 0 : return nullptr;
4367 : }
4368 : {
4369 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4370 8 : CPLErrorStateBackuper oErrorStateBackuper;
4371 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4372 : nullptr, nullptr));
4373 : }
4374 4 : if (!poDS)
4375 : {
4376 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4377 1 : if (pszProxy)
4378 : {
4379 1 : osCacheFilenameOut = pszProxy;
4380 1 : poDS.reset(poDrv->CreateMultiDimensional(
4381 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4382 : }
4383 : }
4384 4 : if (poDS)
4385 : {
4386 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4387 4 : return poDS->GetRootGroup();
4388 : }
4389 : else
4390 : {
4391 0 : CPLError(CE_Failure, CPLE_AppDefined,
4392 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4393 : "configuration option to write the cache in "
4394 : "another directory",
4395 : osCacheFilenameOut.c_str());
4396 : }
4397 : }
4398 :
4399 1414 : return nullptr;
4400 : }
4401 :
4402 : //! @endcond
4403 :
4404 : /************************************************************************/
4405 : /* Cache() */
4406 : /************************************************************************/
4407 :
4408 : /** Cache the content of the array into an auxiliary filename.
4409 : *
4410 : * The main purpose of this method is to be able to cache views that are
4411 : * expensive to compute, such as transposed arrays.
4412 : *
4413 : * The array will be stored in a file whose name is the one of
4414 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4415 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4416 : *
4417 : * If the .gmac file cannot be written next to the dataset, the
4418 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4419 : * directory.
4420 : *
4421 : * The GDALMDArray::Read() method will automatically use the cache when it
4422 : * exists. There is no timestamp checks between the source array and the cached
4423 : * array. If the source arrays changes, the cache must be manually deleted.
4424 : *
4425 : * This is the same as the C function GDALMDArrayCache()
4426 : *
4427 : * @note Driver implementation: optionally implemented.
4428 : *
4429 : * @param papszOptions List of options, null terminated, or NULL. Currently
4430 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4431 : * to specify the block size of the cached array.
4432 : * @return true in case of success.
4433 : */
4434 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4435 : {
4436 14 : std::string osCacheFilename;
4437 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4438 7 : if (!poRG)
4439 1 : return false;
4440 :
4441 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4442 6 : if (poRG->OpenMDArray(osCachedArrayName))
4443 : {
4444 2 : CPLError(CE_Failure, CPLE_NotSupported,
4445 : "An array with same name %s already exists in %s",
4446 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4447 2 : return false;
4448 : }
4449 :
4450 8 : CPLStringList aosOptions;
4451 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4452 4 : const auto &aoDims = GetDimensions();
4453 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4454 4 : if (!aoDims.empty())
4455 : {
4456 : std::string osBlockSize(
4457 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4458 4 : if (osBlockSize.empty())
4459 : {
4460 6 : const auto anBlockSize = GetBlockSize();
4461 3 : int idxDim = 0;
4462 10 : for (auto nBlockSize : anBlockSize)
4463 : {
4464 7 : if (idxDim > 0)
4465 4 : osBlockSize += ',';
4466 7 : if (nBlockSize == 0)
4467 7 : nBlockSize = 256;
4468 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4469 : osBlockSize +=
4470 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4471 7 : idxDim++;
4472 : }
4473 : }
4474 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4475 :
4476 4 : int idxDim = 0;
4477 13 : for (const auto &poDim : aoDims)
4478 : {
4479 9 : auto poNewDim = poRG->CreateDimension(
4480 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4481 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4482 9 : if (!poNewDim)
4483 0 : return false;
4484 9 : aoNewDims.emplace_back(poNewDim);
4485 9 : idxDim++;
4486 : }
4487 : }
4488 :
4489 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4490 8 : GetDataType(), aosOptions.List());
4491 4 : if (!poCachedArray)
4492 : {
4493 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4494 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4495 0 : return false;
4496 : }
4497 :
4498 4 : GUInt64 nCost = 0;
4499 8 : return poCachedArray->CopyFrom(nullptr, this,
4500 : false, // strict
4501 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4502 : }
4503 :
4504 : /************************************************************************/
4505 : /* Read() */
4506 : /************************************************************************/
4507 :
4508 3920 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4509 : const GInt64 *arrayStep, // step in elements
4510 : const GPtrDiff_t *bufferStride, // stride in elements
4511 : const GDALExtendedDataType &bufferDataType,
4512 : void *pDstBuffer, const void *pDstBufferAllocStart,
4513 : size_t nDstBufferAllocSize) const
4514 : {
4515 3920 : if (!m_bHasTriedCachedArray)
4516 : {
4517 1755 : m_bHasTriedCachedArray = true;
4518 1755 : if (IsCacheable())
4519 : {
4520 1755 : const auto &osFilename = GetFilename();
4521 2998 : if (!osFilename.empty() &&
4522 2998 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4523 : {
4524 2466 : std::string osCacheFilename;
4525 2466 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4526 1233 : if (poRG)
4527 : {
4528 : const std::string osCachedArrayName(
4529 32 : MassageName(GetFullName()));
4530 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4531 16 : if (m_poCachedArray)
4532 : {
4533 6 : const auto &dims = GetDimensions();
4534 : const auto &cachedDims =
4535 6 : m_poCachedArray->GetDimensions();
4536 6 : const size_t nDims = dims.size();
4537 : bool ok =
4538 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4539 6 : cachedDims.size() == nDims;
4540 19 : for (size_t i = 0; ok && i < nDims; ++i)
4541 : {
4542 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4543 : }
4544 6 : if (ok)
4545 : {
4546 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4547 : osCachedArrayName.c_str(),
4548 : osCacheFilename.c_str());
4549 : }
4550 : else
4551 : {
4552 0 : CPLError(CE_Warning, CPLE_AppDefined,
4553 : "Cached array %s in %s has incompatible "
4554 : "characteristics with current array.",
4555 : osCachedArrayName.c_str(),
4556 : osCacheFilename.c_str());
4557 0 : m_poCachedArray.reset();
4558 : }
4559 : }
4560 : }
4561 : }
4562 : }
4563 : }
4564 :
4565 3920 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4566 3920 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4567 : {
4568 0 : CPLError(CE_Failure, CPLE_AppDefined,
4569 : "Array data type is not convertible to buffer data type");
4570 0 : return false;
4571 : }
4572 :
4573 7840 : std::vector<GInt64> tmp_arrayStep;
4574 7840 : std::vector<GPtrDiff_t> tmp_bufferStride;
4575 3920 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4576 : bufferStride, bufferDataType, pDstBuffer,
4577 : pDstBufferAllocStart, nDstBufferAllocSize,
4578 : tmp_arrayStep, tmp_bufferStride))
4579 : {
4580 9 : return false;
4581 : }
4582 :
4583 3911 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4584 3911 : bufferDataType, pDstBuffer);
4585 : }
4586 :
4587 : /************************************************************************/
4588 : /* GetRootGroup() */
4589 : /************************************************************************/
4590 :
4591 : /** Return the root group to which this arrays belongs too.
4592 : *
4593 : * Note that arrays may be free standing and some drivers may not implement
4594 : * this method, hence nullptr may be returned.
4595 : *
4596 : * It is used internally by the GetResampled() method to detect if GLT
4597 : * orthorectification is available.
4598 : *
4599 : * @return the root group, or nullptr.
4600 : * @since GDAL 3.8
4601 : */
4602 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4603 : {
4604 0 : return nullptr;
4605 : }
4606 :
4607 : //! @cond Doxygen_Suppress
4608 :
4609 : /************************************************************************/
4610 : /* IsTransposedRequest() */
4611 : /************************************************************************/
4612 :
4613 789 : bool GDALMDArray::IsTransposedRequest(
4614 : const size_t *count,
4615 : const GPtrDiff_t *bufferStride) const // stride in elements
4616 : {
4617 : /*
4618 : For example:
4619 : count = [2,3,4]
4620 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4621 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4622 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4623 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4624 : */
4625 789 : const size_t nDims(GetDimensionCount());
4626 789 : size_t nCurStrideForRowMajorStrides = 1;
4627 789 : bool bRowMajorStrides = true;
4628 789 : size_t nElts = 1;
4629 789 : size_t nLastIdx = 0;
4630 2171 : for (size_t i = nDims; i > 0;)
4631 : {
4632 1382 : --i;
4633 1382 : if (bufferStride[i] < 0)
4634 0 : return false;
4635 1382 : if (static_cast<size_t>(bufferStride[i]) !=
4636 : nCurStrideForRowMajorStrides)
4637 : {
4638 258 : bRowMajorStrides = false;
4639 : }
4640 : // Integer overflows have already been checked in CheckReadWriteParams()
4641 1382 : nCurStrideForRowMajorStrides *= count[i];
4642 1382 : nElts *= count[i];
4643 1382 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4644 : }
4645 789 : if (bRowMajorStrides)
4646 603 : return false;
4647 186 : return nLastIdx == nElts - 1;
4648 : }
4649 :
4650 : /************************************************************************/
4651 : /* CopyToFinalBufferSameDataType() */
4652 : /************************************************************************/
4653 :
4654 : template <size_t N>
4655 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4656 : size_t nDims, const size_t *count,
4657 : const GPtrDiff_t *bufferStride)
4658 : {
4659 120 : std::vector<size_t> anStackCount(nDims);
4660 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4661 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4662 : #if defined(__GNUC__)
4663 : #pragma GCC diagnostic push
4664 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4665 : #endif
4666 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4667 : #if defined(__GNUC__)
4668 : #pragma GCC diagnostic pop
4669 : #endif
4670 60 : size_t iDim = 0;
4671 :
4672 749 : lbl_next_depth:
4673 749 : if (iDim == nDims - 1)
4674 : {
4675 661 : size_t n = count[iDim];
4676 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4677 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4678 29186 : while (n > 0)
4679 : {
4680 28525 : --n;
4681 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4682 28525 : pabyDstBuffer += bufferStrideLastDim;
4683 28525 : pabySrcBuffer += N;
4684 : }
4685 : }
4686 : else
4687 : {
4688 88 : anStackCount[iDim] = count[iDim];
4689 : while (true)
4690 : {
4691 689 : ++iDim;
4692 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4693 689 : goto lbl_next_depth;
4694 689 : lbl_return_to_caller_in_loop:
4695 689 : --iDim;
4696 689 : --anStackCount[iDim];
4697 689 : if (anStackCount[iDim] == 0)
4698 88 : break;
4699 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4700 : }
4701 : }
4702 749 : if (iDim > 0)
4703 689 : goto lbl_return_to_caller_in_loop;
4704 60 : }
4705 :
4706 : /************************************************************************/
4707 : /* CopyToFinalBuffer() */
4708 : /************************************************************************/
4709 :
4710 166 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4711 : const GDALExtendedDataType &eSrcDataType,
4712 : void *pDstBuffer,
4713 : const GDALExtendedDataType &eDstDataType,
4714 : size_t nDims, const size_t *count,
4715 : const GPtrDiff_t *bufferStride)
4716 : {
4717 166 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4718 : // Use specialized implementation for well-known data types when no
4719 : // type conversion is needed
4720 166 : if (eSrcDataType == eDstDataType)
4721 : {
4722 110 : if (nSrcDataTypeSize == 1)
4723 : {
4724 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4725 : count, bufferStride);
4726 60 : return;
4727 : }
4728 69 : else if (nSrcDataTypeSize == 2)
4729 : {
4730 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4731 : count, bufferStride);
4732 1 : return;
4733 : }
4734 68 : else if (nSrcDataTypeSize == 4)
4735 : {
4736 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4737 : count, bufferStride);
4738 14 : return;
4739 : }
4740 54 : else if (nSrcDataTypeSize == 8)
4741 : {
4742 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4743 : count, bufferStride);
4744 4 : return;
4745 : }
4746 : }
4747 :
4748 106 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4749 212 : std::vector<size_t> anStackCount(nDims);
4750 212 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4751 106 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4752 106 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4753 106 : size_t iDim = 0;
4754 :
4755 375 : lbl_next_depth:
4756 375 : if (iDim == nDims - 1)
4757 : {
4758 365 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4759 365 : pabyDstBufferStack[iDim], eDstDataType,
4760 365 : bufferStride[iDim], count[iDim]);
4761 365 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4762 : }
4763 : else
4764 : {
4765 10 : anStackCount[iDim] = count[iDim];
4766 : while (true)
4767 : {
4768 269 : ++iDim;
4769 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4770 269 : goto lbl_next_depth;
4771 269 : lbl_return_to_caller_in_loop:
4772 269 : --iDim;
4773 269 : --anStackCount[iDim];
4774 269 : if (anStackCount[iDim] == 0)
4775 10 : break;
4776 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4777 : }
4778 : }
4779 375 : if (iDim > 0)
4780 269 : goto lbl_return_to_caller_in_loop;
4781 : }
4782 :
4783 : /************************************************************************/
4784 : /* TransposeLast2Dims() */
4785 : /************************************************************************/
4786 :
4787 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4788 : const GDALExtendedDataType &eDT,
4789 : const size_t nDims, const size_t *count,
4790 : const size_t nEltsNonLast2Dims)
4791 : {
4792 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4793 19 : const auto nDTSize = eDT.GetSize();
4794 : void *pTempBufferForLast2DimsTranspose =
4795 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4796 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4797 0 : return false;
4798 :
4799 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4800 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4801 : {
4802 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4803 : pTempBufferForLast2DimsTranspose,
4804 39 : eDT.GetNumericDataType(), count[nDims - 1],
4805 39 : count[nDims - 2]);
4806 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4807 : nDTSize * nEltsLast2Dims);
4808 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4809 : }
4810 :
4811 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4812 :
4813 19 : return true;
4814 : }
4815 :
4816 : /************************************************************************/
4817 : /* ReadForTransposedRequest() */
4818 : /************************************************************************/
4819 :
4820 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4821 : // transposed view yield to extremely poor/unusable performance. This fixes
4822 : // this by using temporary memory to read in a contiguous buffer in a
4823 : // row-major order, and then do the transposition to the final buffer.
4824 :
4825 185 : bool GDALMDArray::ReadForTransposedRequest(
4826 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4827 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4828 : void *pDstBuffer) const
4829 : {
4830 185 : const size_t nDims(GetDimensionCount());
4831 185 : if (nDims == 0)
4832 : {
4833 0 : CPLAssert(false);
4834 : return false; // shouldn't happen
4835 : }
4836 185 : size_t nElts = 1;
4837 492 : for (size_t i = 0; i < nDims; ++i)
4838 307 : nElts *= count[i];
4839 :
4840 370 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4841 185 : tmpBufferStrides.back() = 1;
4842 307 : for (size_t i = nDims - 1; i > 0;)
4843 : {
4844 122 : --i;
4845 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4846 : }
4847 :
4848 185 : const auto &eDT = GetDataType();
4849 185 : const auto nDTSize = eDT.GetSize();
4850 314 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4851 330 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4852 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4853 : {
4854 : // Optimization of the optimization if only the last 2 dims are
4855 : // transposed that saves on temporary buffer allocation
4856 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4857 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4858 23 : bool bRowMajorStridesForNonLast2Dims = true;
4859 23 : size_t nEltsNonLast2Dims = 1;
4860 40 : for (size_t i = nDims - 2; i > 0;)
4861 : {
4862 17 : --i;
4863 17 : if (static_cast<size_t>(bufferStride[i]) !=
4864 : nCurStrideForRowMajorStrides)
4865 : {
4866 4 : bRowMajorStridesForNonLast2Dims = false;
4867 : }
4868 : // Integer overflows have already been checked in
4869 : // CheckReadWriteParams()
4870 17 : nCurStrideForRowMajorStrides *= count[i];
4871 17 : nEltsNonLast2Dims *= count[i];
4872 : }
4873 23 : if (bRowMajorStridesForNonLast2Dims)
4874 : {
4875 : // We read in the final buffer!
4876 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4877 19 : eDT, pDstBuffer))
4878 : {
4879 0 : return false;
4880 : }
4881 :
4882 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4883 19 : nEltsNonLast2Dims);
4884 : }
4885 : }
4886 :
4887 166 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4888 166 : if (pTempBuffer == nullptr)
4889 0 : return false;
4890 :
4891 166 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4892 166 : pTempBuffer))
4893 : {
4894 0 : VSIFree(pTempBuffer);
4895 0 : return false;
4896 : }
4897 166 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4898 : count, bufferStride);
4899 :
4900 166 : if (eDT.NeedsFreeDynamicMemory())
4901 : {
4902 95 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4903 190 : for (size_t i = 0; i < nElts; ++i)
4904 : {
4905 95 : eDT.FreeDynamicMemory(pabyPtr);
4906 95 : pabyPtr += nDTSize;
4907 : }
4908 : }
4909 :
4910 166 : VSIFree(pTempBuffer);
4911 166 : return true;
4912 : }
4913 :
4914 : /************************************************************************/
4915 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4916 : /************************************************************************/
4917 :
4918 : // Returns true if at all following conditions are met:
4919 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4920 : // defines a row-major ordered contiguous buffer.
4921 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4922 : const size_t *count, const GInt64 *arrayStep,
4923 : const GPtrDiff_t *bufferStride,
4924 : const GDALExtendedDataType &bufferDataType) const
4925 : {
4926 78 : if (bufferDataType != GetDataType())
4927 5 : return false;
4928 73 : size_t nExpectedStride = 1;
4929 166 : for (size_t i = GetDimensionCount(); i > 0;)
4930 : {
4931 96 : --i;
4932 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4933 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4934 : {
4935 3 : return false;
4936 : }
4937 93 : nExpectedStride *= count[i];
4938 : }
4939 70 : return true;
4940 : }
4941 :
4942 : /************************************************************************/
4943 : /* ReadUsingContiguousIRead() */
4944 : /************************************************************************/
4945 :
4946 : // Used for example by the TileDB driver when requesting it with
4947 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4948 : // not defining a row-major ordered contiguous buffer.
4949 : // Should only be called when at least one of the above conditions are true,
4950 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4951 : // returning none.
4952 : // This method will call IRead() again with arrayStep[] == 1,
4953 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4954 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4955 : // content of that temporary buffer onto pDstBuffer.
4956 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4957 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4958 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4959 : void *pDstBuffer) const
4960 : {
4961 7 : const size_t nDims(GetDimensionCount());
4962 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4963 14 : std::vector<size_t> anTmpCount(nDims);
4964 7 : const auto &oType = GetDataType();
4965 7 : size_t nMemArraySize = oType.GetSize();
4966 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4967 7 : GPtrDiff_t nStride = 1;
4968 18 : for (size_t i = nDims; i > 0;)
4969 : {
4970 11 : --i;
4971 11 : if (arrayStep[i] > 0)
4972 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4973 : else
4974 2 : anTmpStartIdx[i] =
4975 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4976 : const uint64_t nCount =
4977 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4978 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4979 : {
4980 0 : CPLError(CE_Failure, CPLE_AppDefined,
4981 : "Read() failed due to too large memory requirement");
4982 0 : return false;
4983 : }
4984 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4985 11 : nMemArraySize *= anTmpCount[i];
4986 11 : anTmpStride[i] = nStride;
4987 11 : nStride *= anTmpCount[i];
4988 : }
4989 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4990 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4991 7 : if (!pTmpBuffer)
4992 0 : return false;
4993 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4994 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4995 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4996 : {
4997 0 : return false;
4998 : }
4999 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5000 18 : for (size_t i = 0; i < nDims; ++i)
5001 : {
5002 11 : if (arrayStep[i] > 0)
5003 9 : anTmpStartIdx[i] = 0;
5004 : else
5005 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5006 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5007 22 : std::string(), std::string(), std::string(), std::string(),
5008 22 : anTmpCount[i]);
5009 : }
5010 : auto poMEMArray =
5011 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5012 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5013 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5014 7 : bufferStride, bufferDataType, pDstBuffer);
5015 : }
5016 :
5017 : //! @endcond
5018 :
5019 : /************************************************************************/
5020 : /* GDALSlicedMDArray */
5021 : /************************************************************************/
5022 :
5023 : class GDALSlicedMDArray final : public GDALPamMDArray
5024 : {
5025 : private:
5026 : std::shared_ptr<GDALMDArray> m_poParent{};
5027 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5028 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5029 : std::vector<Range>
5030 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5031 :
5032 : mutable std::vector<GUInt64> m_parentStart;
5033 : mutable std::vector<size_t> m_parentCount;
5034 : mutable std::vector<GInt64> m_parentStep;
5035 : mutable std::vector<GPtrDiff_t> m_parentStride;
5036 :
5037 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5038 : const GInt64 *arrayStep,
5039 : const GPtrDiff_t *bufferStride) const;
5040 :
5041 : protected:
5042 582 : explicit GDALSlicedMDArray(
5043 : const std::shared_ptr<GDALMDArray> &poParent,
5044 : const std::string &viewExpr,
5045 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5046 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5047 : std::vector<Range> &&parentRanges)
5048 1746 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5049 1746 : poParent->GetFullName() +
5050 1164 : " (" + viewExpr + ")"),
5051 1164 : GDALPamMDArray(std::string(),
5052 1164 : "Sliced view of " + poParent->GetFullName() + " (" +
5053 1164 : viewExpr + ")",
5054 1164 : GDALPamMultiDim::GetPAM(poParent),
5055 : poParent->GetContext()),
5056 1164 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5057 582 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5058 582 : m_parentRanges(std::move(parentRanges)),
5059 582 : m_parentStart(m_poParent->GetDimensionCount()),
5060 582 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5061 582 : m_parentStep(m_poParent->GetDimensionCount()),
5062 4656 : m_parentStride(m_poParent->GetDimensionCount())
5063 : {
5064 582 : }
5065 :
5066 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5067 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5068 : const GDALExtendedDataType &bufferDataType,
5069 : void *pDstBuffer) const override;
5070 :
5071 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5072 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5073 : const GDALExtendedDataType &bufferDataType,
5074 : const void *pSrcBuffer) override;
5075 :
5076 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5077 : CSLConstList papszOptions) const override;
5078 :
5079 : public:
5080 : static std::shared_ptr<GDALSlicedMDArray>
5081 582 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5082 : const std::string &viewExpr,
5083 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5084 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5085 : std::vector<Range> &&parentRanges)
5086 : {
5087 582 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5088 582 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5089 :
5090 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5091 582 : poParent, viewExpr, std::move(dims),
5092 582 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5093 582 : newAr->SetSelf(newAr);
5094 582 : return newAr;
5095 : }
5096 :
5097 56 : bool IsWritable() const override
5098 : {
5099 56 : return m_poParent->IsWritable();
5100 : }
5101 :
5102 990 : const std::string &GetFilename() const override
5103 : {
5104 990 : return m_poParent->GetFilename();
5105 : }
5106 :
5107 : const std::vector<std::shared_ptr<GDALDimension>> &
5108 3687 : GetDimensions() const override
5109 : {
5110 3687 : return m_dims;
5111 : }
5112 :
5113 1402 : const GDALExtendedDataType &GetDataType() const override
5114 : {
5115 1402 : return m_poParent->GetDataType();
5116 : }
5117 :
5118 2 : const std::string &GetUnit() const override
5119 : {
5120 2 : return m_poParent->GetUnit();
5121 : }
5122 :
5123 : // bool SetUnit(const std::string& osUnit) override { return
5124 : // m_poParent->SetUnit(osUnit); }
5125 :
5126 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5127 : {
5128 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5129 2 : if (!poSrcSRS)
5130 1 : return nullptr;
5131 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5132 2 : std::vector<int> dstMapping;
5133 3 : for (int srcAxis : srcMapping)
5134 : {
5135 2 : bool bFound = false;
5136 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5137 : {
5138 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5139 3 : srcAxis - 1)
5140 : {
5141 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5142 2 : bFound = true;
5143 2 : break;
5144 : }
5145 : }
5146 2 : if (!bFound)
5147 : {
5148 0 : dstMapping.push_back(0);
5149 : }
5150 : }
5151 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5152 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5153 1 : return poClone;
5154 : }
5155 :
5156 59 : const void *GetRawNoDataValue() const override
5157 : {
5158 59 : return m_poParent->GetRawNoDataValue();
5159 : }
5160 :
5161 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5162 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5163 :
5164 2 : double GetOffset(bool *pbHasOffset,
5165 : GDALDataType *peStorageType) const override
5166 : {
5167 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5168 : }
5169 :
5170 2 : double GetScale(bool *pbHasScale,
5171 : GDALDataType *peStorageType) const override
5172 : {
5173 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5174 : }
5175 :
5176 : // bool SetOffset(double dfOffset) override { return
5177 : // m_poParent->SetOffset(dfOffset); }
5178 :
5179 : // bool SetScale(double dfScale) override { return
5180 : // m_poParent->SetScale(dfScale); }
5181 :
5182 198 : std::vector<GUInt64> GetBlockSize() const override
5183 : {
5184 198 : std::vector<GUInt64> ret(GetDimensionCount());
5185 396 : const auto parentBlockSize(m_poParent->GetBlockSize());
5186 598 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5187 : {
5188 400 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5189 400 : if (iOldAxis != static_cast<size_t>(-1))
5190 : {
5191 400 : ret[i] = parentBlockSize[iOldAxis];
5192 : }
5193 : }
5194 396 : return ret;
5195 : }
5196 :
5197 : std::shared_ptr<GDALAttribute>
5198 6 : GetAttribute(const std::string &osName) const override
5199 : {
5200 6 : return m_poParent->GetAttribute(osName);
5201 : }
5202 :
5203 : std::vector<std::shared_ptr<GDALAttribute>>
5204 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5205 : {
5206 24 : return m_poParent->GetAttributes(papszOptions);
5207 : }
5208 : };
5209 :
5210 : /************************************************************************/
5211 : /* PrepareParentArrays() */
5212 : /************************************************************************/
5213 :
5214 480 : void GDALSlicedMDArray::PrepareParentArrays(
5215 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5216 : const GPtrDiff_t *bufferStride) const
5217 : {
5218 480 : const size_t nParentDimCount = m_parentRanges.size();
5219 1496 : for (size_t i = 0; i < nParentDimCount; i++)
5220 : {
5221 : // For dimensions in parent that have no existence in sliced array
5222 1016 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5223 : }
5224 :
5225 1265 : for (size_t i = 0; i < m_dims.size(); i++)
5226 : {
5227 785 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5228 785 : if (iParent != static_cast<size_t>(-1))
5229 : {
5230 783 : m_parentStart[iParent] =
5231 783 : m_parentRanges[iParent].m_nIncr >= 0
5232 783 : ? m_parentRanges[iParent].m_nStartIdx +
5233 749 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5234 34 : : m_parentRanges[iParent].m_nStartIdx -
5235 68 : arrayStartIdx[i] *
5236 34 : static_cast<GUInt64>(
5237 34 : -m_parentRanges[iParent].m_nIncr);
5238 783 : m_parentCount[iParent] = count[i];
5239 783 : if (arrayStep)
5240 : {
5241 782 : m_parentStep[iParent] =
5242 782 : count[i] == 1 ? 1 :
5243 : // other checks should have ensured this does
5244 : // not overflow
5245 592 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5246 : }
5247 783 : if (bufferStride)
5248 : {
5249 782 : m_parentStride[iParent] = bufferStride[i];
5250 : }
5251 : }
5252 : }
5253 480 : }
5254 :
5255 : /************************************************************************/
5256 : /* IRead() */
5257 : /************************************************************************/
5258 :
5259 447 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5260 : const GInt64 *arrayStep,
5261 : const GPtrDiff_t *bufferStride,
5262 : const GDALExtendedDataType &bufferDataType,
5263 : void *pDstBuffer) const
5264 : {
5265 447 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5266 894 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5267 447 : m_parentStep.data(), m_parentStride.data(),
5268 447 : bufferDataType, pDstBuffer);
5269 : }
5270 :
5271 : /************************************************************************/
5272 : /* IWrite() */
5273 : /************************************************************************/
5274 :
5275 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5276 : const size_t *count, const GInt64 *arrayStep,
5277 : const GPtrDiff_t *bufferStride,
5278 : const GDALExtendedDataType &bufferDataType,
5279 : const void *pSrcBuffer)
5280 : {
5281 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5282 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5283 32 : m_parentStep.data(), m_parentStride.data(),
5284 32 : bufferDataType, pSrcBuffer);
5285 : }
5286 :
5287 : /************************************************************************/
5288 : /* IAdviseRead() */
5289 : /************************************************************************/
5290 :
5291 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5292 : const size_t *count,
5293 : CSLConstList papszOptions) const
5294 : {
5295 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5296 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5297 1 : papszOptions);
5298 : }
5299 :
5300 : /************************************************************************/
5301 : /* CreateSlicedArray() */
5302 : /************************************************************************/
5303 :
5304 : static std::shared_ptr<GDALMDArray>
5305 600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5306 : const std::string &viewExpr, const std::string &activeSlice,
5307 : bool bRenameDimensions,
5308 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5309 : {
5310 600 : const auto &srcDims(self->GetDimensions());
5311 600 : if (srcDims.empty())
5312 : {
5313 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5314 2 : return nullptr;
5315 : }
5316 :
5317 1196 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5318 598 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5319 :
5320 1196 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5321 1196 : std::vector<size_t> mapDimIdxToParentDimIdx;
5322 1196 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5323 598 : newDims.reserve(nTokens);
5324 598 : mapDimIdxToParentDimIdx.reserve(nTokens);
5325 598 : parentRanges.reserve(nTokens);
5326 :
5327 598 : bool bGotEllipsis = false;
5328 598 : size_t nCurSrcDim = 0;
5329 1768 : for (size_t i = 0; i < nTokens; i++)
5330 : {
5331 1186 : const char *pszIdxSpec = aosTokens[i];
5332 1186 : if (EQUAL(pszIdxSpec, "..."))
5333 : {
5334 38 : if (bGotEllipsis)
5335 : {
5336 2 : CPLError(CE_Failure, CPLE_AppDefined,
5337 : "Only one single ellipsis is supported");
5338 2 : return nullptr;
5339 : }
5340 36 : bGotEllipsis = true;
5341 36 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5342 79 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5343 : {
5344 43 : parentRanges.emplace_back(0, 1);
5345 43 : newDims.push_back(srcDims[nCurSrcDim]);
5346 43 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5347 : }
5348 36 : continue;
5349 : }
5350 1148 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5351 1145 : EQUAL(pszIdxSpec, "np.newaxis"))
5352 : {
5353 3 : newDims.push_back(std::make_shared<GDALDimension>(
5354 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5355 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5356 3 : continue;
5357 : }
5358 1145 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5359 : {
5360 325 : if (nCurSrcDim >= srcDims.size())
5361 : {
5362 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5363 : activeSlice.c_str());
5364 7 : return nullptr;
5365 : }
5366 :
5367 323 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5368 323 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5369 323 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5370 319 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5371 : {
5372 5 : CPLError(CE_Failure, CPLE_AppDefined,
5373 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5374 5 : return nullptr;
5375 : }
5376 318 : if (nVal < 0)
5377 0 : nVal += nDimSize;
5378 318 : parentRanges.emplace_back(nVal, 0);
5379 : }
5380 : else
5381 : {
5382 820 : if (nCurSrcDim >= srcDims.size())
5383 : {
5384 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5385 : activeSlice.c_str());
5386 7 : return nullptr;
5387 : }
5388 :
5389 : CPLStringList aosRangeTokens(
5390 819 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5391 819 : int nRangeTokens = aosRangeTokens.size();
5392 819 : if (nRangeTokens > 3)
5393 : {
5394 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5395 : pszIdxSpec);
5396 1 : return nullptr;
5397 : }
5398 818 : if (nRangeTokens <= 1)
5399 : {
5400 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5401 : pszIdxSpec);
5402 1 : return nullptr;
5403 : }
5404 817 : const char *pszStart = aosRangeTokens[0];
5405 817 : const char *pszEnd = aosRangeTokens[1];
5406 817 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5407 817 : GDALSlicedMDArray::Range range;
5408 817 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5409 817 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5410 1633 : if (range.m_nIncr == 0 ||
5411 816 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5412 : {
5413 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5414 1 : return nullptr;
5415 : }
5416 816 : auto startIdx(CPLAtoGIntBig(pszStart));
5417 816 : if (startIdx < 0)
5418 : {
5419 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5420 0 : startIdx = 0;
5421 : else
5422 0 : startIdx = nDimSize + startIdx;
5423 : }
5424 816 : const bool bPosIncr = range.m_nIncr > 0;
5425 816 : range.m_nStartIdx = startIdx;
5426 1632 : range.m_nStartIdx = EQUAL(pszStart, "")
5427 816 : ? (bPosIncr ? 0 : nDimSize - 1)
5428 : : range.m_nStartIdx;
5429 816 : if (range.m_nStartIdx >= nDimSize - 1)
5430 186 : range.m_nStartIdx = nDimSize - 1;
5431 816 : auto endIdx(CPLAtoGIntBig(pszEnd));
5432 816 : if (endIdx < 0)
5433 : {
5434 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5435 1 : if (nDimSize < positiveEndIdx)
5436 0 : endIdx = 0;
5437 : else
5438 1 : endIdx = nDimSize - positiveEndIdx;
5439 : }
5440 816 : GUInt64 nEndIdx = endIdx;
5441 816 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5442 816 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5443 814 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5444 : {
5445 3 : CPLError(CE_Failure, CPLE_AppDefined,
5446 : "Output dimension of size 0 is not allowed");
5447 3 : return nullptr;
5448 : }
5449 813 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5450 813 : const auto nAbsIncr = std::abs(range.m_nIncr);
5451 813 : const GUInt64 newSize =
5452 : bPosIncr
5453 847 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5454 34 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5455 1341 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5456 528 : newSize == srcDims[nCurSrcDim]->GetSize())
5457 : {
5458 159 : newDims.push_back(srcDims[nCurSrcDim]);
5459 : }
5460 : else
5461 : {
5462 654 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5463 654 : if (bRenameDimensions)
5464 : {
5465 : osNewDimName =
5466 1212 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5467 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5468 : "_" CPL_FRMT_GUIB,
5469 606 : static_cast<GUIntBig>(range.m_nStartIdx),
5470 606 : static_cast<GIntBig>(range.m_nIncr),
5471 606 : static_cast<GUIntBig>(newSize));
5472 : }
5473 654 : newDims.push_back(std::make_shared<GDALDimension>(
5474 1308 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5475 1308 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5476 : : std::string(),
5477 : newSize));
5478 : }
5479 813 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5480 813 : parentRanges.emplace_back(range);
5481 : }
5482 :
5483 1131 : nCurSrcDim++;
5484 : }
5485 655 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5486 : {
5487 73 : parentRanges.emplace_back(0, 1);
5488 73 : newDims.push_back(srcDims[nCurSrcDim]);
5489 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5490 : }
5491 :
5492 582 : GDALMDArray::ViewSpec viewSpec;
5493 582 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5494 582 : viewSpec.m_parentRanges = parentRanges;
5495 582 : viewSpecs.emplace_back(std::move(viewSpec));
5496 :
5497 1164 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5498 582 : std::move(mapDimIdxToParentDimIdx),
5499 1164 : std::move(parentRanges));
5500 : }
5501 :
5502 : /************************************************************************/
5503 : /* GDALExtractFieldMDArray */
5504 : /************************************************************************/
5505 :
5506 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5507 : {
5508 : private:
5509 : std::shared_ptr<GDALMDArray> m_poParent{};
5510 : GDALExtendedDataType m_dt;
5511 : std::string m_srcCompName;
5512 : mutable std::vector<GByte> m_pabyNoData{};
5513 :
5514 : protected:
5515 82 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5516 : const std::string &fieldName,
5517 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5518 328 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5519 164 : " of " +
5520 82 : poParent->GetFullName()),
5521 : GDALPamMDArray(
5522 164 : std::string(),
5523 164 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5524 164 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5525 : m_poParent(poParent), m_dt(srcComp->GetType()),
5526 410 : m_srcCompName(srcComp->GetName())
5527 : {
5528 82 : m_pabyNoData.resize(m_dt.GetSize());
5529 82 : }
5530 :
5531 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5532 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5533 : const GDALExtendedDataType &bufferDataType,
5534 : void *pDstBuffer) const override;
5535 :
5536 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5537 : CSLConstList papszOptions) const override
5538 : {
5539 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5540 : }
5541 :
5542 : public:
5543 : static std::shared_ptr<GDALExtractFieldMDArray>
5544 82 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5545 : const std::string &fieldName,
5546 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5547 : {
5548 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5549 82 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5550 82 : newAr->SetSelf(newAr);
5551 82 : return newAr;
5552 : }
5553 :
5554 164 : ~GDALExtractFieldMDArray()
5555 82 : {
5556 82 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5557 164 : }
5558 :
5559 41 : bool IsWritable() const override
5560 : {
5561 41 : return m_poParent->IsWritable();
5562 : }
5563 :
5564 247 : const std::string &GetFilename() const override
5565 : {
5566 247 : return m_poParent->GetFilename();
5567 : }
5568 :
5569 : const std::vector<std::shared_ptr<GDALDimension>> &
5570 351 : GetDimensions() const override
5571 : {
5572 351 : return m_poParent->GetDimensions();
5573 : }
5574 :
5575 295 : const GDALExtendedDataType &GetDataType() const override
5576 : {
5577 295 : return m_dt;
5578 : }
5579 :
5580 2 : const std::string &GetUnit() const override
5581 : {
5582 2 : return m_poParent->GetUnit();
5583 : }
5584 :
5585 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5586 : {
5587 2 : return m_poParent->GetSpatialRef();
5588 : }
5589 :
5590 60 : const void *GetRawNoDataValue() const override
5591 : {
5592 60 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5593 60 : if (parentNoData == nullptr)
5594 1 : return nullptr;
5595 :
5596 59 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5597 59 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5598 :
5599 118 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5600 118 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5601 118 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5602 59 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5603 177 : std::move(comps)));
5604 :
5605 59 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5606 59 : &m_pabyNoData[0], tmpDT);
5607 :
5608 59 : return &m_pabyNoData[0];
5609 : }
5610 :
5611 2 : double GetOffset(bool *pbHasOffset,
5612 : GDALDataType *peStorageType) const override
5613 : {
5614 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5615 : }
5616 :
5617 2 : double GetScale(bool *pbHasScale,
5618 : GDALDataType *peStorageType) const override
5619 : {
5620 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5621 : }
5622 :
5623 42 : std::vector<GUInt64> GetBlockSize() const override
5624 : {
5625 42 : return m_poParent->GetBlockSize();
5626 : }
5627 : };
5628 :
5629 : /************************************************************************/
5630 : /* IRead() */
5631 : /************************************************************************/
5632 :
5633 88 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5634 : const size_t *count,
5635 : const GInt64 *arrayStep,
5636 : const GPtrDiff_t *bufferStride,
5637 : const GDALExtendedDataType &bufferDataType,
5638 : void *pDstBuffer) const
5639 : {
5640 176 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5641 176 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5642 176 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5643 : auto tmpDT(GDALExtendedDataType::Create(
5644 176 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5645 :
5646 88 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5647 176 : tmpDT, pDstBuffer);
5648 : }
5649 :
5650 : /************************************************************************/
5651 : /* CreateFieldNameExtractArray() */
5652 : /************************************************************************/
5653 :
5654 : static std::shared_ptr<GDALMDArray>
5655 83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5656 : const std::string &fieldName)
5657 : {
5658 83 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5659 83 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5660 202 : for (const auto &comp : self->GetDataType().GetComponents())
5661 : {
5662 201 : if (comp->GetName() == fieldName)
5663 : {
5664 82 : srcComp = ∁
5665 82 : break;
5666 : }
5667 : }
5668 83 : if (srcComp == nullptr)
5669 : {
5670 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5671 : fieldName.c_str());
5672 1 : return nullptr;
5673 : }
5674 82 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5675 : }
5676 :
5677 : /************************************************************************/
5678 : /* GetView() */
5679 : /************************************************************************/
5680 :
5681 : // clang-format off
5682 : /** Return a view of the array using slicing or field access.
5683 : *
5684 : * The slice expression uses the same syntax as NumPy basic slicing and
5685 : * indexing. See
5686 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5687 : * Or it can use field access by name. See
5688 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5689 : *
5690 : * Multiple [] bracket elements can be concatenated, with a slice expression
5691 : * or field name inside each.
5692 : *
5693 : * For basic slicing and indexing, inside each [] bracket element, a list of
5694 : * indexes that apply to successive source dimensions, can be specified, using
5695 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5696 : * or newaxis, using a comma separator.
5697 : *
5698 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5699 : * <ul>
5700 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5701 : * at index 1 in the first dimension, and index 2 in the second dimension
5702 : * from the source array. That is 5</li>
5703 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5704 : * implemented internally doing this intermediate slicing approach.</li>
5705 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5706 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5707 : * first dimension. That is [4,5,6,7].</li>
5708 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5709 : * second dimension. That is [2,6].</li>
5710 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5711 : * the second dimension. That is [[2],[6]].</li>
5712 : * <li>GetView("[::,2]"): Same as
5713 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5714 : * ellipsis only expands to one dimension here.</li>
5715 : * <li>GetView("[:,::2]"):
5716 : * returns a 2-dimensional array, with even-indexed elements of the second
5717 : * dimension. That is [[0,2],[4,6]].</li>
5718 : * <li>GetView("[:,1::2]"): returns a
5719 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5720 : * is [[1,3],[5,7]].</li>
5721 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5722 : * array, with elements of the second dimension with index in the range [1,3[.
5723 : * That is [[1,2],[5,6]].</li>
5724 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5725 : * array, with the values in first dimension reversed. That is
5726 : * [[4,5,6,7],[0,1,2,3]].</li>
5727 : * <li>GetView("[newaxis,...]"): returns a
5728 : * 3-dimensional array, with an additional dimension of size 1 put at the
5729 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5730 : * </ul>
5731 : *
5732 : * One difference with NumPy behavior is that ranges that would result in
5733 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5734 : * GDAL multidimensional model).
5735 : *
5736 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5737 : * Multiple field specification is not supported currently.
5738 : *
5739 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5740 : *
5741 : * \note When using the GDAL Python bindings, natural Python syntax can be
5742 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5743 : * ar.GetView("[0,::,1]['foo']")
5744 : * \note When using the C++ API and integer indexing only, you may use the
5745 : * at(idx0, idx1, ...) method.
5746 : *
5747 : * The returned array holds a reference to the original one, and thus is
5748 : * a view of it (not a copy). If the content of the original array changes,
5749 : * the content of the view array too. When using basic slicing and indexing,
5750 : * the view can be written if the underlying array is writable.
5751 : *
5752 : * This is the same as the C function GDALMDArrayGetView()
5753 : *
5754 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5755 : * access.
5756 : * @return a new array, that holds a reference to the original one, and thus is
5757 : * a view of it (not a copy), or nullptr in case of error.
5758 : */
5759 : // clang-format on
5760 :
5761 : std::shared_ptr<GDALMDArray>
5762 619 : GDALMDArray::GetView(const std::string &viewExpr) const
5763 : {
5764 1238 : std::vector<ViewSpec> viewSpecs;
5765 1238 : return GetView(viewExpr, true, viewSpecs);
5766 : }
5767 :
5768 : //! @cond Doxygen_Suppress
5769 : std::shared_ptr<GDALMDArray>
5770 689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5771 : std::vector<ViewSpec> &viewSpecs) const
5772 : {
5773 1378 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5774 689 : if (!self)
5775 : {
5776 1 : CPLError(CE_Failure, CPLE_AppDefined,
5777 : "Driver implementation issue: m_pSelf not set !");
5778 1 : return nullptr;
5779 : }
5780 688 : std::string curExpr(viewExpr);
5781 : while (true)
5782 : {
5783 691 : if (curExpr.empty() || curExpr[0] != '[')
5784 : {
5785 2 : CPLError(CE_Failure, CPLE_AppDefined,
5786 : "Slice string should start with ['");
5787 688 : return nullptr;
5788 : }
5789 :
5790 689 : std::string fieldName;
5791 : size_t endExpr;
5792 689 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5793 : {
5794 87 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5795 : {
5796 2 : CPLError(CE_Failure, CPLE_AppDefined,
5797 : "Field access not allowed on non-compound data type");
5798 2 : return nullptr;
5799 : }
5800 85 : size_t idx = 2;
5801 768 : for (; idx < curExpr.size(); idx++)
5802 : {
5803 767 : const char ch = curExpr[idx];
5804 767 : if (ch == curExpr[1])
5805 84 : break;
5806 683 : if (ch == '\\' && idx + 1 < curExpr.size())
5807 : {
5808 1 : fieldName += curExpr[idx + 1];
5809 1 : idx++;
5810 : }
5811 : else
5812 : {
5813 682 : fieldName += ch;
5814 : }
5815 : }
5816 85 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5817 : {
5818 2 : CPLError(CE_Failure, CPLE_AppDefined,
5819 : "Invalid field access specification");
5820 2 : return nullptr;
5821 : }
5822 83 : endExpr = idx + 1;
5823 : }
5824 : else
5825 : {
5826 602 : endExpr = curExpr.find(']');
5827 : }
5828 685 : if (endExpr == std::string::npos)
5829 : {
5830 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5831 1 : return nullptr;
5832 : }
5833 684 : if (endExpr == 1)
5834 : {
5835 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5836 1 : return nullptr;
5837 : }
5838 683 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5839 :
5840 683 : if (!fieldName.empty())
5841 : {
5842 166 : ViewSpec viewSpec;
5843 83 : viewSpec.m_osFieldName = fieldName;
5844 83 : viewSpecs.emplace_back(std::move(viewSpec));
5845 : }
5846 :
5847 683 : auto newArray = !fieldName.empty()
5848 : ? CreateFieldNameExtractArray(self, fieldName)
5849 : : CreateSlicedArray(self, viewExpr, activeSlice,
5850 683 : bRenameDimensions, viewSpecs);
5851 :
5852 683 : if (endExpr == curExpr.size() - 1)
5853 : {
5854 680 : return newArray;
5855 : }
5856 3 : self = std::move(newArray);
5857 3 : curExpr = curExpr.substr(endExpr + 1);
5858 3 : }
5859 : }
5860 :
5861 : //! @endcond
5862 :
5863 : std::shared_ptr<GDALMDArray>
5864 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5865 : {
5866 19 : std::string osExpr("[");
5867 19 : bool bFirst = true;
5868 45 : for (const auto &idx : indices)
5869 : {
5870 26 : if (!bFirst)
5871 7 : osExpr += ',';
5872 26 : bFirst = false;
5873 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5874 : }
5875 57 : return GetView(osExpr + ']');
5876 : }
5877 :
5878 : /************************************************************************/
5879 : /* operator[] */
5880 : /************************************************************************/
5881 :
5882 : /** Return a view of the array using field access
5883 : *
5884 : * Equivalent of GetView("['fieldName']")
5885 : *
5886 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5887 : */
5888 : std::shared_ptr<GDALMDArray>
5889 2 : GDALMDArray::operator[](const std::string &fieldName) const
5890 : {
5891 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5892 4 : .replaceAll('\\', "\\\\")
5893 4 : .replaceAll('\'', "\\\'")
5894 6 : .c_str()));
5895 : }
5896 :
5897 : /************************************************************************/
5898 : /* GDALMDArrayTransposed */
5899 : /************************************************************************/
5900 :
5901 : class GDALMDArrayTransposed final : public GDALPamMDArray
5902 : {
5903 : private:
5904 : std::shared_ptr<GDALMDArray> m_poParent{};
5905 : std::vector<int> m_anMapNewAxisToOldAxis{};
5906 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5907 :
5908 : mutable std::vector<GUInt64> m_parentStart;
5909 : mutable std::vector<size_t> m_parentCount;
5910 : mutable std::vector<GInt64> m_parentStep;
5911 : mutable std::vector<GPtrDiff_t> m_parentStride;
5912 :
5913 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5914 : const GInt64 *arrayStep,
5915 : const GPtrDiff_t *bufferStride) const;
5916 :
5917 : static std::string
5918 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5919 : {
5920 84 : std::string ret;
5921 84 : ret += '[';
5922 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5923 : {
5924 228 : if (i > 0)
5925 144 : ret += ',';
5926 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5927 : }
5928 84 : ret += ']';
5929 84 : return ret;
5930 : }
5931 :
5932 : protected:
5933 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5934 : const std::vector<int> &anMapNewAxisToOldAxis,
5935 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5936 84 : : GDALAbstractMDArray(std::string(),
5937 84 : "Transposed view of " + poParent->GetFullName() +
5938 84 : " along " +
5939 42 : MappingToStr(anMapNewAxisToOldAxis)),
5940 84 : GDALPamMDArray(std::string(),
5941 84 : "Transposed view of " + poParent->GetFullName() +
5942 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5943 84 : GDALPamMultiDim::GetPAM(poParent),
5944 : poParent->GetContext()),
5945 42 : m_poParent(std::move(poParent)),
5946 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5947 42 : m_dims(std::move(dims)),
5948 42 : m_parentStart(m_poParent->GetDimensionCount()),
5949 42 : m_parentCount(m_poParent->GetDimensionCount()),
5950 42 : m_parentStep(m_poParent->GetDimensionCount()),
5951 336 : m_parentStride(m_poParent->GetDimensionCount())
5952 : {
5953 42 : }
5954 :
5955 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5956 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5957 : const GDALExtendedDataType &bufferDataType,
5958 : void *pDstBuffer) const override;
5959 :
5960 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5961 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5962 : const GDALExtendedDataType &bufferDataType,
5963 : const void *pSrcBuffer) override;
5964 :
5965 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5966 : CSLConstList papszOptions) const override;
5967 :
5968 : public:
5969 : static std::shared_ptr<GDALMDArrayTransposed>
5970 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5971 : const std::vector<int> &anMapNewAxisToOldAxis)
5972 : {
5973 42 : const auto &parentDims(poParent->GetDimensions());
5974 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5975 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5976 : {
5977 114 : if (iOldAxis < 0)
5978 : {
5979 1 : dims.push_back(std::make_shared<GDALDimension>(
5980 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5981 : }
5982 : else
5983 : {
5984 113 : dims.emplace_back(parentDims[iOldAxis]);
5985 : }
5986 : }
5987 :
5988 : auto newAr(
5989 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5990 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5991 42 : newAr->SetSelf(newAr);
5992 84 : return newAr;
5993 : }
5994 :
5995 1 : bool IsWritable() const override
5996 : {
5997 1 : return m_poParent->IsWritable();
5998 : }
5999 :
6000 84 : const std::string &GetFilename() const override
6001 : {
6002 84 : return m_poParent->GetFilename();
6003 : }
6004 :
6005 : const std::vector<std::shared_ptr<GDALDimension>> &
6006 358 : GetDimensions() const override
6007 : {
6008 358 : return m_dims;
6009 : }
6010 :
6011 141 : const GDALExtendedDataType &GetDataType() const override
6012 : {
6013 141 : return m_poParent->GetDataType();
6014 : }
6015 :
6016 4 : const std::string &GetUnit() const override
6017 : {
6018 4 : return m_poParent->GetUnit();
6019 : }
6020 :
6021 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6022 : {
6023 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6024 5 : if (!poSrcSRS)
6025 2 : return nullptr;
6026 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6027 6 : std::vector<int> dstMapping;
6028 9 : for (int srcAxis : srcMapping)
6029 : {
6030 6 : bool bFound = false;
6031 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6032 : {
6033 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6034 : {
6035 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6036 6 : bFound = true;
6037 6 : break;
6038 : }
6039 : }
6040 6 : if (!bFound)
6041 : {
6042 0 : dstMapping.push_back(0);
6043 : }
6044 : }
6045 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6046 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6047 3 : return poClone;
6048 : }
6049 :
6050 4 : const void *GetRawNoDataValue() const override
6051 : {
6052 4 : return m_poParent->GetRawNoDataValue();
6053 : }
6054 :
6055 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6056 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6057 :
6058 4 : double GetOffset(bool *pbHasOffset,
6059 : GDALDataType *peStorageType) const override
6060 : {
6061 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6062 : }
6063 :
6064 4 : double GetScale(bool *pbHasScale,
6065 : GDALDataType *peStorageType) const override
6066 : {
6067 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6068 : }
6069 :
6070 : // bool SetOffset(double dfOffset) override { return
6071 : // m_poParent->SetOffset(dfOffset); }
6072 :
6073 : // bool SetScale(double dfScale) override { return
6074 : // m_poParent->SetScale(dfScale); }
6075 :
6076 3 : std::vector<GUInt64> GetBlockSize() const override
6077 : {
6078 3 : std::vector<GUInt64> ret(GetDimensionCount());
6079 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6080 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6081 : {
6082 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6083 8 : if (iOldAxis >= 0)
6084 : {
6085 7 : ret[i] = parentBlockSize[iOldAxis];
6086 : }
6087 : }
6088 6 : return ret;
6089 : }
6090 :
6091 : std::shared_ptr<GDALAttribute>
6092 1 : GetAttribute(const std::string &osName) const override
6093 : {
6094 1 : return m_poParent->GetAttribute(osName);
6095 : }
6096 :
6097 : std::vector<std::shared_ptr<GDALAttribute>>
6098 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6099 : {
6100 6 : return m_poParent->GetAttributes(papszOptions);
6101 : }
6102 : };
6103 :
6104 : /************************************************************************/
6105 : /* PrepareParentArrays() */
6106 : /************************************************************************/
6107 :
6108 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6109 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6110 : const GPtrDiff_t *bufferStride) const
6111 : {
6112 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6113 : {
6114 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6115 129 : if (iOldAxis >= 0)
6116 : {
6117 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6118 128 : m_parentCount[iOldAxis] = count[i];
6119 128 : if (arrayStep) // only null when called from IAdviseRead()
6120 : {
6121 126 : m_parentStep[iOldAxis] = arrayStep[i];
6122 : }
6123 128 : if (bufferStride) // only null when called from IAdviseRead()
6124 : {
6125 126 : m_parentStride[iOldAxis] = bufferStride[i];
6126 : }
6127 : }
6128 : }
6129 47 : }
6130 :
6131 : /************************************************************************/
6132 : /* IRead() */
6133 : /************************************************************************/
6134 :
6135 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6136 : const size_t *count, const GInt64 *arrayStep,
6137 : const GPtrDiff_t *bufferStride,
6138 : const GDALExtendedDataType &bufferDataType,
6139 : void *pDstBuffer) const
6140 : {
6141 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6142 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6143 44 : m_parentStep.data(), m_parentStride.data(),
6144 44 : bufferDataType, pDstBuffer);
6145 : }
6146 :
6147 : /************************************************************************/
6148 : /* IWrite() */
6149 : /************************************************************************/
6150 :
6151 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6152 : const size_t *count, const GInt64 *arrayStep,
6153 : const GPtrDiff_t *bufferStride,
6154 : const GDALExtendedDataType &bufferDataType,
6155 : const void *pSrcBuffer)
6156 : {
6157 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6158 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6159 2 : m_parentStep.data(), m_parentStride.data(),
6160 2 : bufferDataType, pSrcBuffer);
6161 : }
6162 :
6163 : /************************************************************************/
6164 : /* IAdviseRead() */
6165 : /************************************************************************/
6166 :
6167 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6168 : const size_t *count,
6169 : CSLConstList papszOptions) const
6170 : {
6171 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6172 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6173 1 : papszOptions);
6174 : }
6175 :
6176 : /************************************************************************/
6177 : /* Transpose() */
6178 : /************************************************************************/
6179 :
6180 : /** Return a view of the array whose axis have been reordered.
6181 : *
6182 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6183 : * and GetDimensionCount() - 1, and each only once.
6184 : * -1 can be used as a special index value to ask for the insertion of a new
6185 : * axis of size 1.
6186 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6187 : * index of one of its dimension, it corresponds to the axis of index
6188 : * anMapNewAxisToOldAxis[i] from the current array.
6189 : *
6190 : * This is similar to the numpy.transpose() method
6191 : *
6192 : * The returned array holds a reference to the original one, and thus is
6193 : * a view of it (not a copy). If the content of the original array changes,
6194 : * the content of the view array too. The view can be written if the underlying
6195 : * array is writable.
6196 : *
6197 : * Note that I/O performance in such a transposed view might be poor.
6198 : *
6199 : * This is the same as the C function GDALMDArrayTranspose().
6200 : *
6201 : * @return a new array, that holds a reference to the original one, and thus is
6202 : * a view of it (not a copy), or nullptr in case of error.
6203 : */
6204 : std::shared_ptr<GDALMDArray>
6205 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6206 : {
6207 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6208 50 : if (!self)
6209 : {
6210 0 : CPLError(CE_Failure, CPLE_AppDefined,
6211 : "Driver implementation issue: m_pSelf not set !");
6212 0 : return nullptr;
6213 : }
6214 50 : const int nDims = static_cast<int>(GetDimensionCount());
6215 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6216 50 : int nCountOldAxis = 0;
6217 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6218 : {
6219 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6220 : {
6221 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6222 4 : return nullptr;
6223 : }
6224 130 : if (iOldAxis >= 0)
6225 : {
6226 128 : if (alreadyUsedOldAxis[iOldAxis])
6227 : {
6228 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6229 : iOldAxis);
6230 1 : return nullptr;
6231 : }
6232 127 : alreadyUsedOldAxis[iOldAxis] = true;
6233 127 : nCountOldAxis++;
6234 : }
6235 : }
6236 46 : if (nCountOldAxis != nDims)
6237 : {
6238 4 : CPLError(CE_Failure, CPLE_AppDefined,
6239 : "One or several original axis missing");
6240 4 : return nullptr;
6241 : }
6242 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6243 : }
6244 :
6245 : /************************************************************************/
6246 : /* IRead() */
6247 : /************************************************************************/
6248 :
6249 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6250 : const size_t *count, const GInt64 *arrayStep,
6251 : const GPtrDiff_t *bufferStride,
6252 : const GDALExtendedDataType &bufferDataType,
6253 : void *pDstBuffer) const
6254 : {
6255 16 : const double dfScale = m_dfScale;
6256 16 : const double dfOffset = m_dfOffset;
6257 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6258 : const auto dtDouble =
6259 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6260 16 : const size_t nDTSize = dtDouble.GetSize();
6261 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6262 :
6263 16 : double adfSrcNoData[2] = {0, 0};
6264 16 : if (m_bHasNoData)
6265 : {
6266 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6267 9 : m_poParent->GetDataType(),
6268 : &adfSrcNoData[0], dtDouble);
6269 : }
6270 :
6271 16 : const auto nDims = GetDimensions().size();
6272 16 : if (nDims == 0)
6273 : {
6274 : double adfVal[2];
6275 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6276 : dtDouble, &adfVal[0]))
6277 : {
6278 0 : return false;
6279 : }
6280 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6281 : {
6282 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6283 6 : if (bDTIsComplex)
6284 : {
6285 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6286 : }
6287 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6288 : bufferDataType);
6289 : }
6290 : else
6291 : {
6292 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6293 : pDstBuffer, bufferDataType);
6294 : }
6295 9 : return true;
6296 : }
6297 :
6298 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6299 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6300 7 : void *pTempBuffer = pDstBuffer;
6301 7 : if (bTempBufferNeeded)
6302 : {
6303 2 : size_t nElts = 1;
6304 2 : actualBufferStrideVector.resize(nDims);
6305 7 : for (size_t i = 0; i < nDims; i++)
6306 5 : nElts *= count[i];
6307 2 : actualBufferStrideVector.back() = 1;
6308 5 : for (size_t i = nDims - 1; i > 0;)
6309 : {
6310 3 : --i;
6311 3 : actualBufferStrideVector[i] =
6312 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6313 : }
6314 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6315 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6316 2 : if (!pTempBuffer)
6317 0 : return false;
6318 : }
6319 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6320 : actualBufferStridePtr, dtDouble, pTempBuffer))
6321 : {
6322 0 : if (bTempBufferNeeded)
6323 0 : VSIFree(pTempBuffer);
6324 0 : return false;
6325 : }
6326 :
6327 : struct Stack
6328 : {
6329 : size_t nIters = 0;
6330 : double *src_ptr = nullptr;
6331 : GByte *dst_ptr = nullptr;
6332 : GPtrDiff_t src_inc_offset = 0;
6333 : GPtrDiff_t dst_inc_offset = 0;
6334 : };
6335 :
6336 7 : std::vector<Stack> stack(nDims);
6337 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6338 23 : for (size_t i = 0; i < nDims; i++)
6339 : {
6340 32 : stack[i].src_inc_offset =
6341 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6342 16 : stack[i].dst_inc_offset =
6343 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6344 : }
6345 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6346 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6347 :
6348 7 : size_t dimIdx = 0;
6349 7 : const size_t nDimsMinus1 = nDims - 1;
6350 : GByte abyDstNoData[16];
6351 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6352 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6353 : bufferDataType);
6354 :
6355 37 : lbl_next_depth:
6356 37 : if (dimIdx == nDimsMinus1)
6357 : {
6358 25 : auto nIters = count[dimIdx];
6359 25 : double *padfVal = stack[dimIdx].src_ptr;
6360 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6361 : while (true)
6362 : {
6363 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6364 : {
6365 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6366 88 : if (bDTIsComplex)
6367 : {
6368 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6369 : }
6370 88 : if (bTempBufferNeeded)
6371 : {
6372 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6373 : dst_ptr, bufferDataType);
6374 : }
6375 : }
6376 : else
6377 : {
6378 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6379 : }
6380 :
6381 92 : if ((--nIters) == 0)
6382 25 : break;
6383 67 : padfVal += stack[dimIdx].src_inc_offset;
6384 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6385 : }
6386 : }
6387 : else
6388 : {
6389 12 : stack[dimIdx].nIters = count[dimIdx];
6390 : while (true)
6391 : {
6392 30 : dimIdx++;
6393 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6394 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6395 30 : goto lbl_next_depth;
6396 30 : lbl_return_to_caller:
6397 30 : dimIdx--;
6398 30 : if ((--stack[dimIdx].nIters) == 0)
6399 12 : break;
6400 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6401 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6402 : }
6403 : }
6404 37 : if (dimIdx > 0)
6405 30 : goto lbl_return_to_caller;
6406 :
6407 7 : if (bTempBufferNeeded)
6408 2 : VSIFree(pTempBuffer);
6409 7 : return true;
6410 : }
6411 :
6412 : /************************************************************************/
6413 : /* IWrite() */
6414 : /************************************************************************/
6415 :
6416 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6417 : const size_t *count, const GInt64 *arrayStep,
6418 : const GPtrDiff_t *bufferStride,
6419 : const GDALExtendedDataType &bufferDataType,
6420 : const void *pSrcBuffer)
6421 : {
6422 16 : const double dfScale = m_dfScale;
6423 16 : const double dfOffset = m_dfOffset;
6424 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6425 : const auto dtDouble =
6426 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6427 16 : const size_t nDTSize = dtDouble.GetSize();
6428 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6429 : const bool bSelfAndParentHaveNoData =
6430 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6431 16 : double dfNoData = 0;
6432 16 : if (m_bHasNoData)
6433 : {
6434 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6435 : &dfNoData, GDT_Float64, 0, 1);
6436 : }
6437 :
6438 16 : double adfSrcNoData[2] = {0, 0};
6439 16 : if (bSelfAndParentHaveNoData)
6440 : {
6441 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6442 7 : m_poParent->GetDataType(),
6443 : &adfSrcNoData[0], dtDouble);
6444 : }
6445 :
6446 16 : const auto nDims = GetDimensions().size();
6447 16 : if (nDims == 0)
6448 : {
6449 : double adfVal[2];
6450 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6451 : dtDouble);
6452 16 : if (bSelfAndParentHaveNoData &&
6453 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6454 : {
6455 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6456 2 : bufferStride, m_poParent->GetDataType(),
6457 4 : m_poParent->GetRawNoDataValue());
6458 : }
6459 : else
6460 : {
6461 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6462 8 : if (bDTIsComplex)
6463 : {
6464 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6465 : }
6466 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6467 8 : bufferStride, dtDouble, &adfVal[0]);
6468 : }
6469 : }
6470 :
6471 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6472 6 : size_t nElts = 1;
6473 6 : tmpBufferStrideVector.resize(nDims);
6474 20 : for (size_t i = 0; i < nDims; i++)
6475 14 : nElts *= count[i];
6476 6 : tmpBufferStrideVector.back() = 1;
6477 14 : for (size_t i = nDims - 1; i > 0;)
6478 : {
6479 8 : --i;
6480 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6481 : }
6482 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6483 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6484 6 : if (!pTempBuffer)
6485 0 : return false;
6486 :
6487 : struct Stack
6488 : {
6489 : size_t nIters = 0;
6490 : double *dst_ptr = nullptr;
6491 : const GByte *src_ptr = nullptr;
6492 : GPtrDiff_t src_inc_offset = 0;
6493 : GPtrDiff_t dst_inc_offset = 0;
6494 : };
6495 :
6496 6 : std::vector<Stack> stack(nDims);
6497 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6498 20 : for (size_t i = 0; i < nDims; i++)
6499 : {
6500 28 : stack[i].dst_inc_offset =
6501 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6502 14 : stack[i].src_inc_offset =
6503 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6504 : }
6505 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6506 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6507 :
6508 6 : size_t dimIdx = 0;
6509 6 : const size_t nDimsMinus1 = nDims - 1;
6510 :
6511 34 : lbl_next_depth:
6512 34 : if (dimIdx == nDimsMinus1)
6513 : {
6514 23 : auto nIters = count[dimIdx];
6515 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6516 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6517 : while (true)
6518 : {
6519 : double adfVal[2];
6520 : const double *padfSrcVal;
6521 86 : if (bIsBufferDataTypeNativeDataType)
6522 : {
6523 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6524 : }
6525 : else
6526 : {
6527 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6528 : &adfVal[0], dtDouble);
6529 36 : padfSrcVal = adfVal;
6530 : }
6531 :
6532 148 : if (bSelfAndParentHaveNoData &&
6533 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6534 : {
6535 3 : dst_ptr[0] = adfSrcNoData[0];
6536 3 : if (bDTIsComplex)
6537 : {
6538 1 : dst_ptr[1] = adfSrcNoData[1];
6539 : }
6540 : }
6541 : else
6542 : {
6543 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6544 83 : if (bDTIsComplex)
6545 : {
6546 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6547 : }
6548 : }
6549 :
6550 86 : if ((--nIters) == 0)
6551 23 : break;
6552 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6553 63 : src_ptr += stack[dimIdx].src_inc_offset;
6554 63 : }
6555 : }
6556 : else
6557 : {
6558 11 : stack[dimIdx].nIters = count[dimIdx];
6559 : while (true)
6560 : {
6561 28 : dimIdx++;
6562 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6563 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6564 28 : goto lbl_next_depth;
6565 28 : lbl_return_to_caller:
6566 28 : dimIdx--;
6567 28 : if ((--stack[dimIdx].nIters) == 0)
6568 11 : break;
6569 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6570 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6571 : }
6572 : }
6573 34 : if (dimIdx > 0)
6574 28 : goto lbl_return_to_caller;
6575 :
6576 : // If the parent array is not double/complex-double, then convert the
6577 : // values to it, before calling Write(), as some implementations can be
6578 : // very slow when doing the type conversion.
6579 6 : const auto &eParentDT = m_poParent->GetDataType();
6580 6 : const size_t nParentDTSize = eParentDT.GetSize();
6581 6 : if (nParentDTSize <= nDTSize / 2)
6582 : {
6583 : // Copy in-place by making sure that source and target do not overlap
6584 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6585 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6586 :
6587 : // Copy first element
6588 : {
6589 6 : std::vector<GByte> abyTemp(nParentDTSize);
6590 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6591 6 : static_cast<int>(nDTSize), &abyTemp[0],
6592 : eParentNumericDT, static_cast<int>(nParentDTSize),
6593 : 1);
6594 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6595 : }
6596 : // Remaining elements
6597 86 : for (size_t i = 1; i < nElts; ++i)
6598 : {
6599 80 : GDALCopyWords64(
6600 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6601 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6602 : eParentNumericDT, 0, 1);
6603 : }
6604 : }
6605 :
6606 : const bool ret =
6607 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6608 : eParentDT, pTempBuffer);
6609 :
6610 6 : VSIFree(pTempBuffer);
6611 6 : return ret;
6612 : }
6613 :
6614 : /************************************************************************/
6615 : /* GetUnscaled() */
6616 : /************************************************************************/
6617 :
6618 : /** Return an array that is the unscaled version of the current one.
6619 : *
6620 : * That is each value of the unscaled array will be
6621 : * unscaled_value = raw_value * GetScale() + GetOffset()
6622 : *
6623 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6624 : * from unscaled values to raw values.
6625 : *
6626 : * This is the same as the C function GDALMDArrayGetUnscaled().
6627 : *
6628 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6629 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6630 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6631 : * @return a new array, that holds a reference to the original one, and thus is
6632 : * a view of it (not a copy), or nullptr in case of error.
6633 : */
6634 : std::shared_ptr<GDALMDArray>
6635 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6636 : double dfOverriddenDstNodata) const
6637 : {
6638 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6639 17 : if (!self)
6640 : {
6641 0 : CPLError(CE_Failure, CPLE_AppDefined,
6642 : "Driver implementation issue: m_pSelf not set !");
6643 0 : return nullptr;
6644 : }
6645 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6646 : {
6647 0 : CPLError(CE_Failure, CPLE_AppDefined,
6648 : "GetUnscaled() only supports numeric data type");
6649 0 : return nullptr;
6650 : }
6651 : const double dfScale =
6652 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6653 : const double dfOffset =
6654 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6655 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6656 4 : return self;
6657 :
6658 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6659 13 : ? GDT_CFloat64
6660 13 : : GDT_Float64;
6661 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6662 : {
6663 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6664 0 : eDT = GDT_Float16;
6665 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6666 1 : eDT = GDT_Float32;
6667 : }
6668 :
6669 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6670 13 : dfOverriddenDstNodata, eDT);
6671 : }
6672 :
6673 : /************************************************************************/
6674 : /* GDALMDArrayMask */
6675 : /************************************************************************/
6676 :
6677 : class GDALMDArrayMask final : public GDALPamMDArray
6678 : {
6679 : private:
6680 : std::shared_ptr<GDALMDArray> m_poParent{};
6681 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6682 : double m_dfMissingValue = 0.0;
6683 : bool m_bHasMissingValue = false;
6684 : double m_dfFillValue = 0.0;
6685 : bool m_bHasFillValue = false;
6686 : double m_dfValidMin = 0.0;
6687 : bool m_bHasValidMin = false;
6688 : double m_dfValidMax = 0.0;
6689 : bool m_bHasValidMax = false;
6690 : std::vector<uint32_t> m_anValidFlagMasks{};
6691 : std::vector<uint32_t> m_anValidFlagValues{};
6692 :
6693 : bool Init(CSLConstList papszOptions);
6694 :
6695 : template <typename Type>
6696 : void
6697 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6698 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6699 : const void *pTempBuffer,
6700 : const GDALExtendedDataType &oTmpBufferDT,
6701 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6702 :
6703 : protected:
6704 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6705 96 : : GDALAbstractMDArray(std::string(),
6706 96 : "Mask of " + poParent->GetFullName()),
6707 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6708 96 : GDALPamMultiDim::GetPAM(poParent),
6709 : poParent->GetContext()),
6710 240 : m_poParent(std::move(poParent))
6711 : {
6712 48 : }
6713 :
6714 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6715 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6716 : const GDALExtendedDataType &bufferDataType,
6717 : void *pDstBuffer) const override;
6718 :
6719 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6720 : CSLConstList papszOptions) const override
6721 : {
6722 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6723 : }
6724 :
6725 : public:
6726 : static std::shared_ptr<GDALMDArrayMask>
6727 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6728 : CSLConstList papszOptions);
6729 :
6730 1 : bool IsWritable() const override
6731 : {
6732 1 : return false;
6733 : }
6734 :
6735 54 : const std::string &GetFilename() const override
6736 : {
6737 54 : return m_poParent->GetFilename();
6738 : }
6739 :
6740 : const std::vector<std::shared_ptr<GDALDimension>> &
6741 382 : GetDimensions() const override
6742 : {
6743 382 : return m_poParent->GetDimensions();
6744 : }
6745 :
6746 138 : const GDALExtendedDataType &GetDataType() const override
6747 : {
6748 138 : return m_dt;
6749 : }
6750 :
6751 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6752 : {
6753 1 : return m_poParent->GetSpatialRef();
6754 : }
6755 :
6756 2 : std::vector<GUInt64> GetBlockSize() const override
6757 : {
6758 2 : return m_poParent->GetBlockSize();
6759 : }
6760 : };
6761 :
6762 : /************************************************************************/
6763 : /* GDALMDArrayMask::Create() */
6764 : /************************************************************************/
6765 :
6766 : /* static */ std::shared_ptr<GDALMDArrayMask>
6767 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6768 : CSLConstList papszOptions)
6769 : {
6770 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6771 48 : newAr->SetSelf(newAr);
6772 48 : if (!newAr->Init(papszOptions))
6773 6 : return nullptr;
6774 42 : return newAr;
6775 : }
6776 :
6777 : /************************************************************************/
6778 : /* GDALMDArrayMask::Init() */
6779 : /************************************************************************/
6780 :
6781 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6782 : {
6783 : const auto GetSingleValNumericAttr =
6784 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6785 : {
6786 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6787 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6788 : {
6789 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6790 21 : if (anDimSizes.empty() ||
6791 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6792 : {
6793 11 : bHasVal = true;
6794 11 : dfVal = poAttr->ReadAsDouble();
6795 : }
6796 : }
6797 192 : };
6798 :
6799 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6800 48 : m_dfMissingValue);
6801 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6802 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6803 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6804 :
6805 : {
6806 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6807 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6808 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6809 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6810 : {
6811 6 : m_bHasValidMin = true;
6812 6 : m_bHasValidMax = true;
6813 6 : auto vals = poValidRange->ReadAsDoubleArray();
6814 6 : CPLAssert(vals.size() == 2);
6815 6 : m_dfValidMin = vals[0];
6816 6 : m_dfValidMax = vals[1];
6817 : }
6818 : }
6819 :
6820 : // Take into account
6821 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6822 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6823 : const char *pszUnmaskFlags =
6824 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6825 48 : if (pszUnmaskFlags)
6826 : {
6827 : const auto IsScalarStringAttr =
6828 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6829 : {
6830 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6831 26 : (poAttr->GetDimensionsSize().empty() ||
6832 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6833 26 : poAttr->GetDimensionsSize()[0] == 1));
6834 : };
6835 :
6836 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6837 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6838 : {
6839 1 : CPLError(CE_Failure, CPLE_AppDefined,
6840 : "UNMASK_FLAGS option specified but array has no "
6841 : "flag_meanings attribute");
6842 1 : return false;
6843 : }
6844 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6845 13 : if (!pszFlagMeanings)
6846 : {
6847 1 : CPLError(CE_Failure, CPLE_AppDefined,
6848 : "Cannot read flag_meanings attribute");
6849 1 : return false;
6850 : }
6851 :
6852 : const auto IsSingleDimNumericAttr =
6853 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6854 : {
6855 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6856 26 : poAttr->GetDimensionsSize().size() == 1;
6857 : };
6858 :
6859 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6860 : const bool bHasFlagValues =
6861 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6862 :
6863 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6864 : const bool bHasFlagMasks =
6865 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6866 :
6867 12 : if (!bHasFlagValues && !bHasFlagMasks)
6868 : {
6869 1 : CPLError(CE_Failure, CPLE_AppDefined,
6870 : "Cannot find flag_values and/or flag_masks attribute");
6871 1 : return false;
6872 : }
6873 :
6874 : const CPLStringList aosUnmaskFlags(
6875 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6876 : const CPLStringList aosFlagMeanings(
6877 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6878 :
6879 11 : if (bHasFlagValues)
6880 : {
6881 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6882 : // We could support Int64 or UInt64, but more work...
6883 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6884 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6885 : {
6886 0 : CPLError(CE_Failure, CPLE_NotSupported,
6887 : "Unsupported data type for flag_values attribute: %s",
6888 : GDALGetDataTypeName(eType));
6889 0 : return false;
6890 : }
6891 : }
6892 :
6893 11 : if (bHasFlagMasks)
6894 : {
6895 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6896 : // We could support Int64 or UInt64, but more work...
6897 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6898 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6899 : {
6900 0 : CPLError(CE_Failure, CPLE_NotSupported,
6901 : "Unsupported data type for flag_masks attribute: %s",
6902 : GDALGetDataTypeName(eType));
6903 0 : return false;
6904 : }
6905 : }
6906 :
6907 : const std::vector<double> adfValues(
6908 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6909 11 : : std::vector<double>());
6910 : const std::vector<double> adfMasks(
6911 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6912 11 : : std::vector<double>());
6913 :
6914 18 : if (bHasFlagValues &&
6915 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6916 : {
6917 1 : CPLError(CE_Failure, CPLE_AppDefined,
6918 : "Number of values in flag_values attribute is different "
6919 : "from the one in flag_meanings");
6920 1 : return false;
6921 : }
6922 :
6923 16 : if (bHasFlagMasks &&
6924 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6925 : {
6926 1 : CPLError(CE_Failure, CPLE_AppDefined,
6927 : "Number of values in flag_masks attribute is different "
6928 : "from the one in flag_meanings");
6929 1 : return false;
6930 : }
6931 :
6932 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6933 : {
6934 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6935 11 : if (nIdxFlag < 0)
6936 : {
6937 1 : CPLError(
6938 : CE_Failure, CPLE_AppDefined,
6939 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6940 : aosUnmaskFlags[i], pszFlagMeanings);
6941 1 : return false;
6942 : }
6943 :
6944 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6945 : {
6946 0 : CPLError(CE_Failure, CPLE_AppDefined,
6947 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6948 0 : adfValues[nIdxFlag]);
6949 0 : return false;
6950 : }
6951 :
6952 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6953 : {
6954 0 : CPLError(CE_Failure, CPLE_AppDefined,
6955 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6956 0 : adfMasks[nIdxFlag]);
6957 0 : return false;
6958 : }
6959 :
6960 10 : if (bHasFlagValues)
6961 : {
6962 12 : m_anValidFlagValues.push_back(
6963 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6964 : }
6965 :
6966 10 : if (bHasFlagMasks)
6967 : {
6968 12 : m_anValidFlagMasks.push_back(
6969 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6970 : }
6971 : }
6972 : }
6973 :
6974 42 : return true;
6975 : }
6976 :
6977 : /************************************************************************/
6978 : /* IRead() */
6979 : /************************************************************************/
6980 :
6981 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6982 : const GInt64 *arrayStep,
6983 : const GPtrDiff_t *bufferStride,
6984 : const GDALExtendedDataType &bufferDataType,
6985 : void *pDstBuffer) const
6986 : {
6987 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
6988 : {
6989 0 : CPLError(CE_Failure, CPLE_AppDefined,
6990 : "%s: only reading to a numeric data type is supported",
6991 : __func__);
6992 0 : return false;
6993 : }
6994 51 : size_t nElts = 1;
6995 51 : const size_t nDims = GetDimensionCount();
6996 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6997 139 : for (size_t i = 0; i < nDims; i++)
6998 88 : nElts *= count[i];
6999 51 : if (nDims > 0)
7000 : {
7001 46 : tmpBufferStrideVector.back() = 1;
7002 88 : for (size_t i = nDims - 1; i > 0;)
7003 : {
7004 42 : --i;
7005 42 : tmpBufferStrideVector[i] =
7006 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7007 : }
7008 : }
7009 :
7010 : /* Optimized case: if we are an integer data type and that there is no */
7011 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7012 : /* directly */
7013 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7014 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7015 34 : m_anValidFlagMasks.empty() &&
7016 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7017 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7018 : {
7019 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7020 7 : if (bBufferDataTypeIsByte) // Byte case
7021 : {
7022 4 : bool bContiguous = true;
7023 10 : for (size_t i = 0; i < nDims; i++)
7024 : {
7025 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7026 : {
7027 1 : bContiguous = false;
7028 1 : break;
7029 : }
7030 : }
7031 4 : if (bContiguous)
7032 : {
7033 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7034 3 : memset(pDstBuffer, 1, nElts);
7035 3 : return true;
7036 : }
7037 : }
7038 :
7039 : struct Stack
7040 : {
7041 : size_t nIters = 0;
7042 : GByte *dst_ptr = nullptr;
7043 : GPtrDiff_t dst_inc_offset = 0;
7044 : };
7045 :
7046 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7047 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7048 13 : for (size_t i = 0; i < nDims; i++)
7049 : {
7050 9 : stack[i].dst_inc_offset =
7051 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7052 : }
7053 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7054 :
7055 4 : size_t dimIdx = 0;
7056 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7057 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7058 4 : CPLAssert(nBufferDTSize <= 16);
7059 4 : const GByte flag = 1;
7060 4 : GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7061 : bufferDataType.GetNumericDataType(), 0, 1);
7062 :
7063 28 : lbl_next_depth:
7064 28 : if (dimIdx == nDimsMinus1)
7065 : {
7066 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7067 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7068 :
7069 : while (true)
7070 : {
7071 : // cppcheck-suppress knownConditionTrueFalse
7072 73 : if (bBufferDataTypeIsByte)
7073 : {
7074 24 : *dst_ptr = flag;
7075 : }
7076 : else
7077 : {
7078 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7079 : }
7080 :
7081 73 : if ((--nIters) == 0)
7082 19 : break;
7083 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7084 : }
7085 : }
7086 : else
7087 : {
7088 9 : stack[dimIdx].nIters = count[dimIdx];
7089 : while (true)
7090 : {
7091 24 : dimIdx++;
7092 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7093 24 : goto lbl_next_depth;
7094 24 : lbl_return_to_caller:
7095 24 : dimIdx--;
7096 24 : if ((--stack[dimIdx].nIters) == 0)
7097 9 : break;
7098 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7099 : }
7100 : }
7101 28 : if (dimIdx > 0)
7102 24 : goto lbl_return_to_caller;
7103 :
7104 4 : return true;
7105 : }
7106 :
7107 : const auto oTmpBufferDT =
7108 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7109 : ? GDALExtendedDataType::Create(GDT_Float64)
7110 88 : : m_poParent->GetDataType();
7111 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7112 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7113 44 : if (!pTempBuffer)
7114 0 : return false;
7115 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7116 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7117 : pTempBuffer))
7118 : {
7119 0 : VSIFree(pTempBuffer);
7120 0 : return false;
7121 : }
7122 :
7123 44 : switch (oTmpBufferDT.GetNumericDataType())
7124 : {
7125 7 : case GDT_Byte:
7126 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7127 : pTempBuffer, oTmpBufferDT,
7128 : tmpBufferStrideVector);
7129 7 : break;
7130 :
7131 0 : case GDT_Int8:
7132 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7133 : pTempBuffer, oTmpBufferDT,
7134 : tmpBufferStrideVector);
7135 0 : break;
7136 :
7137 1 : case GDT_UInt16:
7138 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7139 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7140 : tmpBufferStrideVector);
7141 1 : break;
7142 :
7143 14 : case GDT_Int16:
7144 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7145 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7146 : tmpBufferStrideVector);
7147 14 : break;
7148 :
7149 1 : case GDT_UInt32:
7150 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7151 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7152 : tmpBufferStrideVector);
7153 1 : break;
7154 :
7155 5 : case GDT_Int32:
7156 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7157 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7158 : tmpBufferStrideVector);
7159 5 : break;
7160 :
7161 0 : case GDT_UInt64:
7162 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7163 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7164 : tmpBufferStrideVector);
7165 0 : break;
7166 :
7167 0 : case GDT_Int64:
7168 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7169 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7170 : tmpBufferStrideVector);
7171 0 : break;
7172 :
7173 0 : case GDT_Float16:
7174 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7175 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7176 : tmpBufferStrideVector);
7177 0 : break;
7178 :
7179 7 : case GDT_Float32:
7180 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7181 : pTempBuffer, oTmpBufferDT,
7182 : tmpBufferStrideVector);
7183 7 : break;
7184 :
7185 9 : case GDT_Float64:
7186 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7187 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7188 : tmpBufferStrideVector);
7189 9 : break;
7190 0 : case GDT_Unknown:
7191 : case GDT_CInt16:
7192 : case GDT_CInt32:
7193 : case GDT_CFloat16:
7194 : case GDT_CFloat32:
7195 : case GDT_CFloat64:
7196 : case GDT_TypeCount:
7197 0 : CPLAssert(false);
7198 : break;
7199 : }
7200 :
7201 44 : VSIFree(pTempBuffer);
7202 :
7203 44 : return true;
7204 : }
7205 :
7206 : /************************************************************************/
7207 : /* IsValidForDT() */
7208 : /************************************************************************/
7209 :
7210 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7211 : {
7212 40 : if (std::isnan(dfVal))
7213 0 : return false;
7214 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7215 0 : return false;
7216 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7217 0 : return false;
7218 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7219 : }
7220 :
7221 9 : template <> bool IsValidForDT<double>(double)
7222 : {
7223 9 : return true;
7224 : }
7225 :
7226 : /************************************************************************/
7227 : /* IsNan() */
7228 : /************************************************************************/
7229 :
7230 1438 : template <typename Type> inline bool IsNan(Type)
7231 : {
7232 1438 : return false;
7233 : }
7234 :
7235 65 : template <> bool IsNan<double>(double val)
7236 : {
7237 65 : return std::isnan(val);
7238 : }
7239 :
7240 26 : template <> bool IsNan<float>(float val)
7241 : {
7242 26 : return std::isnan(val);
7243 : }
7244 :
7245 : /************************************************************************/
7246 : /* ReadInternal() */
7247 : /************************************************************************/
7248 :
7249 : template <typename Type>
7250 44 : void GDALMDArrayMask::ReadInternal(
7251 : const size_t *count, const GPtrDiff_t *bufferStride,
7252 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7253 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7254 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7255 : {
7256 44 : const size_t nDims = GetDimensionCount();
7257 :
7258 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7259 : {
7260 220 : if (bHasVal)
7261 : {
7262 49 : if (IsValidForDT<Type>(dfVal))
7263 : {
7264 49 : return static_cast<Type>(dfVal);
7265 : }
7266 : else
7267 : {
7268 0 : bHasVal = false;
7269 : }
7270 : }
7271 171 : return 0;
7272 : };
7273 :
7274 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7275 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7276 : const Type nNoDataValue =
7277 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7278 44 : bool bHasMissingValue = m_bHasMissingValue;
7279 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7280 44 : bool bHasFillValue = m_bHasFillValue;
7281 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7282 44 : bool bHasValidMin = m_bHasValidMin;
7283 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7284 44 : bool bHasValidMax = m_bHasValidMax;
7285 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7286 44 : const bool bHasValidFlags =
7287 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7288 :
7289 351 : const auto IsValidFlag = [this](Type v)
7290 : {
7291 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7292 : {
7293 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7294 : {
7295 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7296 : m_anValidFlagValues[i])
7297 : {
7298 4 : return true;
7299 : }
7300 : }
7301 : }
7302 42 : else if (!m_anValidFlagValues.empty())
7303 : {
7304 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7305 : {
7306 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7307 : {
7308 4 : return true;
7309 : }
7310 : }
7311 : }
7312 : else /* if( !m_anValidFlagMasks.empty() ) */
7313 : {
7314 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7315 : {
7316 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7317 : {
7318 9 : return true;
7319 : }
7320 : }
7321 : }
7322 37 : return false;
7323 : };
7324 :
7325 : #define GET_MASK_FOR_SAMPLE(v) \
7326 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7327 : !(bHasMissingValue && v == nMissingValue) && \
7328 : !(bHasFillValue && v == nFillValue) && \
7329 : !(bHasValidMin && v < nValidMin) && \
7330 : !(bHasValidMax && v > nValidMax) && \
7331 : (!bHasValidFlags || IsValidFlag(v)));
7332 :
7333 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7334 : /* Optimized case: Byte output and output buffer is contiguous */
7335 44 : if (bBufferDataTypeIsByte)
7336 : {
7337 40 : bool bContiguous = true;
7338 103 : for (size_t i = 0; i < nDims; i++)
7339 : {
7340 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7341 : {
7342 1 : bContiguous = false;
7343 1 : break;
7344 : }
7345 : }
7346 40 : if (bContiguous)
7347 : {
7348 39 : size_t nElts = 1;
7349 102 : for (size_t i = 0; i < nDims; i++)
7350 63 : nElts *= count[i];
7351 :
7352 1113 : for (size_t i = 0; i < nElts; i++)
7353 : {
7354 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7355 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7356 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7357 : }
7358 39 : return;
7359 : }
7360 : }
7361 :
7362 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7363 :
7364 : struct Stack
7365 : {
7366 : size_t nIters = 0;
7367 : const GByte *src_ptr = nullptr;
7368 : GByte *dst_ptr = nullptr;
7369 : GPtrDiff_t src_inc_offset = 0;
7370 : GPtrDiff_t dst_inc_offset = 0;
7371 : };
7372 :
7373 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7374 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7375 15 : for (size_t i = 0; i < nDims; i++)
7376 : {
7377 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7378 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7379 10 : stack[i].dst_inc_offset =
7380 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7381 : }
7382 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7383 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7384 :
7385 5 : size_t dimIdx = 0;
7386 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7387 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7388 5 : CPLAssert(nBufferDTSize <= 16);
7389 15 : for (GByte flag = 0; flag <= 1; flag++)
7390 : {
7391 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7392 : bufferDataType.GetNumericDataType(), 0, 1);
7393 : }
7394 :
7395 43 : lbl_next_depth:
7396 43 : if (dimIdx == nDimsMinus1)
7397 : {
7398 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7399 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7400 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7401 :
7402 420 : while (true)
7403 : {
7404 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7405 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7406 :
7407 455 : if (bBufferDataTypeIsByte)
7408 : {
7409 24 : *dst_ptr = flag;
7410 : }
7411 : else
7412 : {
7413 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7414 : }
7415 :
7416 455 : if ((--nIters) == 0)
7417 35 : break;
7418 420 : src_ptr += stack[dimIdx].src_inc_offset;
7419 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7420 : }
7421 : }
7422 : else
7423 : {
7424 8 : stack[dimIdx].nIters = count[dimIdx];
7425 : while (true)
7426 : {
7427 38 : dimIdx++;
7428 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7429 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7430 38 : goto lbl_next_depth;
7431 38 : lbl_return_to_caller:
7432 38 : dimIdx--;
7433 38 : if ((--stack[dimIdx].nIters) == 0)
7434 8 : break;
7435 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7436 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7437 : }
7438 : }
7439 43 : if (dimIdx > 0)
7440 38 : goto lbl_return_to_caller;
7441 : }
7442 :
7443 : /************************************************************************/
7444 : /* GetMask() */
7445 : /************************************************************************/
7446 :
7447 : /** Return an array that is a mask for the current array
7448 :
7449 : This array will be of type Byte, with values set to 0 to indicate invalid
7450 : pixels of the current array, and values set to 1 to indicate valid pixels.
7451 :
7452 : The generic implementation honours the NoDataValue, as well as various
7453 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7454 : and valid_range.
7455 :
7456 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7457 : can be used to specify strings of the "flag_meanings" attribute
7458 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7459 : for which pixels matching any of those flags will be set at 1 in the mask array,
7460 : and pixels matching none of those flags will be set at 0.
7461 : For example, let's consider the following netCDF variable defined with:
7462 : \verbatim
7463 : l2p_flags:valid_min = 0s ;
7464 : l2p_flags:valid_max = 256s ;
7465 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7466 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7467 : \endverbatim
7468 :
7469 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7470 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7471 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7472 : will be 1.
7473 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7474 : will be 0.
7475 :
7476 : This is the same as the C function GDALMDArrayGetMask().
7477 :
7478 : @param papszOptions NULL-terminated list of options, or NULL.
7479 :
7480 : @return a new array, that holds a reference to the original one, and thus is
7481 : a view of it (not a copy), or nullptr in case of error.
7482 : */
7483 : std::shared_ptr<GDALMDArray>
7484 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7485 : {
7486 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7487 49 : if (!self)
7488 : {
7489 0 : CPLError(CE_Failure, CPLE_AppDefined,
7490 : "Driver implementation issue: m_pSelf not set !");
7491 0 : return nullptr;
7492 : }
7493 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7494 : {
7495 1 : CPLError(CE_Failure, CPLE_AppDefined,
7496 : "GetMask() only supports numeric data type");
7497 1 : return nullptr;
7498 : }
7499 48 : return GDALMDArrayMask::Create(self, papszOptions);
7500 : }
7501 :
7502 : /************************************************************************/
7503 : /* IsRegularlySpaced() */
7504 : /************************************************************************/
7505 :
7506 : /** Returns whether an array is a 1D regularly spaced array.
7507 : *
7508 : * @param[out] dfStart First value in the array
7509 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7510 : * @return true if the array is regularly spaced.
7511 : */
7512 193 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7513 : {
7514 193 : dfStart = 0;
7515 193 : dfIncrement = 0;
7516 193 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7517 0 : return false;
7518 193 : const auto nSize = GetDimensions()[0]->GetSize();
7519 193 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7520 2 : return false;
7521 :
7522 191 : size_t nCount = static_cast<size_t>(nSize);
7523 382 : std::vector<double> adfTmp;
7524 : try
7525 : {
7526 191 : adfTmp.resize(nCount);
7527 : }
7528 0 : catch (const std::exception &)
7529 : {
7530 0 : return false;
7531 : }
7532 :
7533 191 : GUInt64 anStart[1] = {0};
7534 191 : size_t anCount[1] = {nCount};
7535 :
7536 : const auto IsRegularlySpacedInternal =
7537 84274 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7538 : {
7539 265 : dfStart = adfTmp[0];
7540 265 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7541 265 : if (dfIncrement == 0)
7542 : {
7543 3 : return false;
7544 : }
7545 21000 : for (size_t i = 1; i < anCount[0]; i++)
7546 : {
7547 20738 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7548 20738 : 1e-3 * fabs(dfIncrement))
7549 : {
7550 0 : return false;
7551 : }
7552 : }
7553 262 : return true;
7554 191 : };
7555 :
7556 : // First try with the first block(s). This can avoid excessive processing
7557 : // time, for example with Zarr datasets.
7558 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7559 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7560 191 : const auto nBlockSize = GetBlockSize()[0];
7561 191 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7562 : {
7563 : size_t nReducedCount =
7564 77 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7565 440 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7566 363 : nReducedCount *= 2;
7567 77 : anCount[0] = nReducedCount;
7568 77 : if (!Read(anStart, anCount, nullptr, nullptr,
7569 154 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7570 : {
7571 0 : return false;
7572 : }
7573 77 : if (!IsRegularlySpacedInternal())
7574 : {
7575 3 : return false;
7576 : }
7577 :
7578 : // Get next values
7579 74 : anStart[0] = nReducedCount;
7580 74 : anCount[0] = nCount - nReducedCount;
7581 : }
7582 :
7583 188 : if (!Read(anStart, anCount, nullptr, nullptr,
7584 376 : GDALExtendedDataType::Create(GDT_Float64),
7585 188 : &adfTmp[static_cast<size_t>(anStart[0])]))
7586 : {
7587 0 : return false;
7588 : }
7589 :
7590 188 : return IsRegularlySpacedInternal();
7591 : }
7592 :
7593 : /************************************************************************/
7594 : /* GuessGeoTransform() */
7595 : /************************************************************************/
7596 :
7597 : /** Returns whether 2 specified dimensions form a geotransform
7598 : *
7599 : * @param nDimX Index of the X axis.
7600 : * @param nDimY Index of the Y axis.
7601 : * @param bPixelIsPoint Whether the geotransform should be returned
7602 : * with the pixel-is-point (pixel-center) convention
7603 : * (bPixelIsPoint = true), or with the pixel-is-area
7604 : * (top left corner convention)
7605 : * (bPixelIsPoint = false)
7606 : * @param[out] gt Computed geotransform
7607 : * @return true if a geotransform could be computed.
7608 : */
7609 232 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7610 : bool bPixelIsPoint,
7611 : GDALGeoTransform >) const
7612 : {
7613 232 : const auto &dims(GetDimensions());
7614 464 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7615 464 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7616 232 : double dfXStart = 0.0;
7617 232 : double dfXSpacing = 0.0;
7618 232 : double dfYStart = 0.0;
7619 232 : double dfYSpacing = 0.0;
7620 524 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7621 292 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7622 340 : poVarY && poVarY->GetDimensionCount() == 1 &&
7623 97 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7624 470 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7625 92 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7626 : {
7627 92 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7628 92 : gt[1] = dfXSpacing;
7629 92 : gt[2] = 0;
7630 92 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7631 92 : gt[4] = 0;
7632 92 : gt[5] = dfYSpacing;
7633 92 : return true;
7634 : }
7635 140 : return false;
7636 : }
7637 :
7638 : /** Returns whether 2 specified dimensions form a geotransform
7639 : *
7640 : * @param nDimX Index of the X axis.
7641 : * @param nDimY Index of the Y axis.
7642 : * @param bPixelIsPoint Whether the geotransform should be returned
7643 : * with the pixel-is-point (pixel-center) convention
7644 : * (bPixelIsPoint = true), or with the pixel-is-area
7645 : * (top left corner convention)
7646 : * (bPixelIsPoint = false)
7647 : * @param[out] adfGeoTransform Computed geotransform
7648 : * @return true if a geotransform could be computed.
7649 : */
7650 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7651 : bool bPixelIsPoint,
7652 : double adfGeoTransform[6]) const
7653 : {
7654 0 : GDALGeoTransform *gt =
7655 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7656 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7657 : }
7658 :
7659 : /************************************************************************/
7660 : /* GDALMDArrayResampled */
7661 : /************************************************************************/
7662 :
7663 : class GDALMDArrayResampledDataset;
7664 :
7665 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7666 : {
7667 : protected:
7668 : CPLErr IReadBlock(int, int, void *) override;
7669 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7670 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7671 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7672 : GSpacing nLineSpaceBuf,
7673 : GDALRasterIOExtraArg *psExtraArg) override;
7674 :
7675 : public:
7676 : explicit GDALMDArrayResampledDatasetRasterBand(
7677 : GDALMDArrayResampledDataset *poDSIn);
7678 :
7679 : double GetNoDataValue(int *pbHasNoData) override;
7680 : };
7681 :
7682 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7683 : {
7684 : friend class GDALMDArrayResampled;
7685 : friend class GDALMDArrayResampledDatasetRasterBand;
7686 :
7687 : std::shared_ptr<GDALMDArray> m_poArray;
7688 : const size_t m_iXDim;
7689 : const size_t m_iYDim;
7690 : GDALGeoTransform m_gt{};
7691 : bool m_bHasGT = false;
7692 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7693 :
7694 : std::vector<GUInt64> m_anOffset{};
7695 : std::vector<size_t> m_anCount{};
7696 : std::vector<GPtrDiff_t> m_anStride{};
7697 :
7698 : std::string m_osFilenameLong{};
7699 : std::string m_osFilenameLat{};
7700 :
7701 : public:
7702 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7703 : size_t iXDim, size_t iYDim)
7704 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7705 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7706 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7707 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7708 : {
7709 24 : const auto &dims(m_poArray->GetDimensions());
7710 :
7711 24 : nRasterYSize = static_cast<int>(
7712 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7713 24 : nRasterXSize = static_cast<int>(
7714 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7715 :
7716 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7717 :
7718 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7719 24 : }
7720 :
7721 : ~GDALMDArrayResampledDataset() override;
7722 :
7723 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7724 : {
7725 43 : gt = m_gt;
7726 43 : return m_bHasGT ? CE_None : CE_Failure;
7727 : }
7728 :
7729 105 : const OGRSpatialReference *GetSpatialRef() const override
7730 : {
7731 105 : m_poSRS = m_poArray->GetSpatialRef();
7732 105 : if (m_poSRS)
7733 : {
7734 79 : m_poSRS.reset(m_poSRS->Clone());
7735 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7736 237 : for (auto &m : axisMapping)
7737 : {
7738 158 : if (m == static_cast<int>(m_iXDim) + 1)
7739 79 : m = 1;
7740 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7741 79 : m = 2;
7742 : }
7743 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7744 : }
7745 105 : return m_poSRS.get();
7746 : }
7747 :
7748 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7749 : const std::string &osFilenameLat)
7750 : {
7751 5 : m_osFilenameLong = osFilenameLong;
7752 5 : m_osFilenameLat = osFilenameLat;
7753 10 : CPLStringList aosGeoLoc;
7754 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7755 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7756 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7757 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7758 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7759 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7760 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7761 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7762 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7763 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7764 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7765 5 : }
7766 : };
7767 :
7768 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7769 : {
7770 24 : if (!m_osFilenameLong.empty())
7771 5 : VSIUnlink(m_osFilenameLong.c_str());
7772 24 : if (!m_osFilenameLat.empty())
7773 5 : VSIUnlink(m_osFilenameLat.c_str());
7774 48 : }
7775 :
7776 : /************************************************************************/
7777 : /* GDALMDArrayResampledDatasetRasterBand() */
7778 : /************************************************************************/
7779 :
7780 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7781 24 : GDALMDArrayResampledDataset *poDSIn)
7782 : {
7783 24 : const auto &poArray(poDSIn->m_poArray);
7784 24 : const auto blockSize(poArray->GetBlockSize());
7785 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7786 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7787 13 : blockSize[poDSIn->m_iYDim]))
7788 24 : : 1;
7789 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7790 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7791 13 : blockSize[poDSIn->m_iXDim]))
7792 24 : : poDSIn->GetRasterXSize();
7793 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7794 24 : eAccess = poDSIn->eAccess;
7795 24 : }
7796 :
7797 : /************************************************************************/
7798 : /* GetNoDataValue() */
7799 : /************************************************************************/
7800 :
7801 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7802 : {
7803 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7804 50 : const auto &poArray(l_poDS->m_poArray);
7805 50 : bool bHasNodata = false;
7806 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7807 50 : if (pbHasNoData)
7808 46 : *pbHasNoData = bHasNodata;
7809 50 : return dfRes;
7810 : }
7811 :
7812 : /************************************************************************/
7813 : /* IReadBlock() */
7814 : /************************************************************************/
7815 :
7816 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7817 : int nBlockYOff,
7818 : void *pImage)
7819 : {
7820 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7821 0 : const int nXOff = nBlockXOff * nBlockXSize;
7822 0 : const int nYOff = nBlockYOff * nBlockYSize;
7823 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7824 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7825 : GDALRasterIOExtraArg sExtraArg;
7826 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7827 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7828 : nReqXSize, nReqYSize, eDataType, nDTSize,
7829 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7830 : }
7831 :
7832 : /************************************************************************/
7833 : /* IRasterIO() */
7834 : /************************************************************************/
7835 :
7836 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7837 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7838 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7839 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7840 : GDALRasterIOExtraArg *psExtraArg)
7841 : {
7842 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7843 32 : const auto &poArray(l_poDS->m_poArray);
7844 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7845 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7846 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7847 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7848 : {
7849 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7850 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7851 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7852 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7853 :
7854 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7855 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7856 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7857 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7858 :
7859 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7860 32 : l_poDS->m_anCount.data(), nullptr,
7861 32 : l_poDS->m_anStride.data(),
7862 64 : GDALExtendedDataType::Create(eBufType), pData)
7863 32 : ? CE_None
7864 32 : : CE_Failure;
7865 : }
7866 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7867 : pData, nBufXSize, nBufYSize, eBufType,
7868 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7869 : }
7870 :
7871 : class GDALMDArrayResampled final : public GDALPamMDArray
7872 : {
7873 : private:
7874 : std::shared_ptr<GDALMDArray> m_poParent{};
7875 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7876 : std::vector<GUInt64> m_anBlockSize;
7877 : GDALExtendedDataType m_dt;
7878 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7879 : std::shared_ptr<GDALMDArray> m_poVarX{};
7880 : std::shared_ptr<GDALMDArray> m_poVarY{};
7881 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7882 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7883 :
7884 : protected:
7885 21 : GDALMDArrayResampled(
7886 : const std::shared_ptr<GDALMDArray> &poParent,
7887 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7888 : const std::vector<GUInt64> &anBlockSize)
7889 42 : : GDALAbstractMDArray(std::string(),
7890 42 : "Resampled view of " + poParent->GetFullName()),
7891 : GDALPamMDArray(
7892 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7893 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7894 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7895 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7896 : {
7897 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7898 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7899 21 : }
7900 :
7901 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7902 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7903 : const GDALExtendedDataType &bufferDataType,
7904 : void *pDstBuffer) const override;
7905 :
7906 : public:
7907 : static std::shared_ptr<GDALMDArray>
7908 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7909 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7910 : GDALRIOResampleAlg resampleAlg,
7911 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7912 :
7913 42 : ~GDALMDArrayResampled()
7914 21 : {
7915 : // First close the warped VRT
7916 21 : m_poReprojectedDS.reset();
7917 21 : m_poParentDS.reset();
7918 42 : }
7919 :
7920 11 : bool IsWritable() const override
7921 : {
7922 11 : return false;
7923 : }
7924 :
7925 74 : const std::string &GetFilename() const override
7926 : {
7927 74 : return m_poParent->GetFilename();
7928 : }
7929 :
7930 : const std::vector<std::shared_ptr<GDALDimension>> &
7931 257 : GetDimensions() const override
7932 : {
7933 257 : return m_apoDims;
7934 : }
7935 :
7936 109 : const GDALExtendedDataType &GetDataType() const override
7937 : {
7938 109 : return m_dt;
7939 : }
7940 :
7941 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7942 : {
7943 21 : return m_poSRS;
7944 : }
7945 :
7946 12 : std::vector<GUInt64> GetBlockSize() const override
7947 : {
7948 12 : return m_anBlockSize;
7949 : }
7950 :
7951 : std::shared_ptr<GDALAttribute>
7952 1 : GetAttribute(const std::string &osName) const override
7953 : {
7954 1 : return m_poParent->GetAttribute(osName);
7955 : }
7956 :
7957 : std::vector<std::shared_ptr<GDALAttribute>>
7958 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7959 : {
7960 12 : return m_poParent->GetAttributes(papszOptions);
7961 : }
7962 :
7963 1 : const std::string &GetUnit() const override
7964 : {
7965 1 : return m_poParent->GetUnit();
7966 : }
7967 :
7968 1 : const void *GetRawNoDataValue() const override
7969 : {
7970 1 : return m_poParent->GetRawNoDataValue();
7971 : }
7972 :
7973 1 : double GetOffset(bool *pbHasOffset,
7974 : GDALDataType *peStorageType) const override
7975 : {
7976 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7977 : }
7978 :
7979 1 : double GetScale(bool *pbHasScale,
7980 : GDALDataType *peStorageType) const override
7981 : {
7982 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7983 : }
7984 : };
7985 :
7986 : /************************************************************************/
7987 : /* GDALMDArrayResampled::Create() */
7988 : /************************************************************************/
7989 :
7990 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7991 : const std::shared_ptr<GDALMDArray> &poParent,
7992 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7993 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7994 : CSLConstList /* papszOptions */)
7995 : {
7996 29 : const char *pszResampleAlg = "nearest";
7997 29 : bool unsupported = false;
7998 29 : switch (resampleAlg)
7999 : {
8000 16 : case GRIORA_NearestNeighbour:
8001 16 : pszResampleAlg = "nearest";
8002 16 : break;
8003 2 : case GRIORA_Bilinear:
8004 2 : pszResampleAlg = "bilinear";
8005 2 : break;
8006 5 : case GRIORA_Cubic:
8007 5 : pszResampleAlg = "cubic";
8008 5 : break;
8009 1 : case GRIORA_CubicSpline:
8010 1 : pszResampleAlg = "cubicspline";
8011 1 : break;
8012 1 : case GRIORA_Lanczos:
8013 1 : pszResampleAlg = "lanczos";
8014 1 : break;
8015 1 : case GRIORA_Average:
8016 1 : pszResampleAlg = "average";
8017 1 : break;
8018 1 : case GRIORA_Mode:
8019 1 : pszResampleAlg = "mode";
8020 1 : break;
8021 1 : case GRIORA_Gauss:
8022 1 : unsupported = true;
8023 1 : break;
8024 0 : case GRIORA_RESERVED_START:
8025 0 : unsupported = true;
8026 0 : break;
8027 0 : case GRIORA_RESERVED_END:
8028 0 : unsupported = true;
8029 0 : break;
8030 1 : case GRIORA_RMS:
8031 1 : pszResampleAlg = "rms";
8032 1 : break;
8033 : }
8034 29 : if (unsupported)
8035 : {
8036 1 : CPLError(CE_Failure, CPLE_NotSupported,
8037 : "Unsupported resample method for GetResampled()");
8038 1 : return nullptr;
8039 : }
8040 :
8041 28 : if (poParent->GetDimensionCount() < 2)
8042 : {
8043 1 : CPLError(CE_Failure, CPLE_NotSupported,
8044 : "GetResampled() only supports 2 dimensions or more");
8045 1 : return nullptr;
8046 : }
8047 :
8048 27 : const auto &aoParentDims = poParent->GetDimensions();
8049 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8050 : {
8051 2 : CPLError(CE_Failure, CPLE_AppDefined,
8052 : "GetResampled(): apoNewDims size should be the same as "
8053 : "GetDimensionCount()");
8054 2 : return nullptr;
8055 : }
8056 :
8057 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8058 25 : apoNewDims.reserve(apoNewDimsIn.size());
8059 :
8060 50 : std::vector<GUInt64> anBlockSize;
8061 25 : anBlockSize.reserve(apoNewDimsIn.size());
8062 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8063 :
8064 50 : auto apoParentDims = poParent->GetDimensions();
8065 : // Special case for NASA EMIT datasets
8066 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8067 7 : apoParentDims[0]->GetName() == "downtrack" &&
8068 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8069 2 : apoParentDims[2]->GetName() == "bands");
8070 :
8071 : const size_t iYDimParent =
8072 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8073 : const size_t iXDimParent =
8074 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8075 :
8076 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8077 : {
8078 53 : if (i == iYDimParent || i == iXDimParent)
8079 48 : continue;
8080 5 : if (apoNewDimsIn[i] == nullptr)
8081 : {
8082 3 : apoNewDims.emplace_back(aoParentDims[i]);
8083 : }
8084 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8085 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8086 : {
8087 1 : CPLError(CE_Failure, CPLE_AppDefined,
8088 : "GetResampled(): apoNewDims[%u] should be the same "
8089 : "as its parent",
8090 : i);
8091 1 : return nullptr;
8092 : }
8093 : else
8094 : {
8095 1 : apoNewDims.emplace_back(aoParentDims[i]);
8096 : }
8097 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8098 : }
8099 :
8100 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8101 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8102 :
8103 24 : double dfXStart = 0.0;
8104 24 : double dfXSpacing = 0.0;
8105 24 : bool gotXSpacing = false;
8106 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8107 24 : if (poNewDimX)
8108 : {
8109 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8110 : {
8111 0 : CPLError(CE_Failure, CPLE_NotSupported,
8112 : "Too big size for X dimension");
8113 0 : return nullptr;
8114 : }
8115 4 : auto var = poNewDimX->GetIndexingVariable();
8116 4 : if (var)
8117 : {
8118 2 : if (var->GetDimensionCount() != 1 ||
8119 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8120 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8121 : {
8122 0 : CPLError(CE_Failure, CPLE_NotSupported,
8123 : "New X dimension should be indexed by a regularly "
8124 : "spaced variable");
8125 0 : return nullptr;
8126 : }
8127 1 : gotXSpacing = true;
8128 : }
8129 : }
8130 :
8131 24 : double dfYStart = 0.0;
8132 24 : double dfYSpacing = 0.0;
8133 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8134 24 : bool gotYSpacing = false;
8135 24 : if (poNewDimY)
8136 : {
8137 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8138 : {
8139 0 : CPLError(CE_Failure, CPLE_NotSupported,
8140 : "Too big size for Y dimension");
8141 0 : return nullptr;
8142 : }
8143 4 : auto var = poNewDimY->GetIndexingVariable();
8144 4 : if (var)
8145 : {
8146 2 : if (var->GetDimensionCount() != 1 ||
8147 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8148 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8149 : {
8150 0 : CPLError(CE_Failure, CPLE_NotSupported,
8151 : "New Y dimension should be indexed by a regularly "
8152 : "spaced variable");
8153 0 : return nullptr;
8154 : }
8155 1 : gotYSpacing = true;
8156 : }
8157 : }
8158 :
8159 : // This limitation could probably be removed
8160 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8161 : {
8162 0 : CPLError(CE_Failure, CPLE_NotSupported,
8163 : "Either none of new X or Y dimension should have an indexing "
8164 : "variable, or both should both should have one.");
8165 0 : return nullptr;
8166 : }
8167 :
8168 48 : std::string osDstWKT;
8169 24 : if (poTargetSRS)
8170 : {
8171 2 : char *pszDstWKT = nullptr;
8172 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8173 : {
8174 0 : CPLFree(pszDstWKT);
8175 0 : return nullptr;
8176 : }
8177 2 : osDstWKT = pszDstWKT;
8178 2 : CPLFree(pszDstWKT);
8179 : }
8180 :
8181 : // Use coordinate variables for geolocation array
8182 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8183 24 : bool useGeolocationArray = false;
8184 24 : if (apoCoordinateVars.size() >= 2)
8185 : {
8186 0 : std::shared_ptr<GDALMDArray> poLongVar;
8187 0 : std::shared_ptr<GDALMDArray> poLatVar;
8188 15 : for (const auto &poCoordVar : apoCoordinateVars)
8189 : {
8190 10 : const auto &osName = poCoordVar->GetName();
8191 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8192 20 : std::string osStandardName;
8193 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8194 2 : poAttr->GetDimensionCount() == 0)
8195 : {
8196 2 : const char *pszStandardName = poAttr->ReadAsString();
8197 2 : if (pszStandardName)
8198 2 : osStandardName = pszStandardName;
8199 : }
8200 21 : if (osName == "lon" || osName == "longitude" ||
8201 21 : osName == "Longitude" || osStandardName == "longitude")
8202 : {
8203 5 : poLongVar = poCoordVar;
8204 : }
8205 6 : else if (osName == "lat" || osName == "latitude" ||
8206 6 : osName == "Latitude" || osStandardName == "latitude")
8207 : {
8208 5 : poLatVar = poCoordVar;
8209 : }
8210 : }
8211 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8212 : {
8213 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8214 5 : const auto &longDims = poLongVar->GetDimensions();
8215 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8216 5 : const auto &latDims = poLatVar->GetDimensions();
8217 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8218 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8219 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8220 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8221 : {
8222 : // Geolocation arrays are 1D, and of consistent size with
8223 : // the variable
8224 0 : useGeolocationArray = true;
8225 : }
8226 1 : else if ((longDimCount == 2 ||
8227 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8228 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8229 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8230 1 : (latDimCount == 2 ||
8231 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8232 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8233 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8234 :
8235 : {
8236 : // Geolocation arrays are 2D (or 3D with first dimension of
8237 : // size 1, as found in Sentinel 5P products), and of consistent
8238 : // size with the variable
8239 5 : useGeolocationArray = true;
8240 : }
8241 : else
8242 : {
8243 0 : CPLDebug(
8244 : "GDAL",
8245 : "Longitude and latitude coordinate variables found, "
8246 : "but their characteristics are not compatible of using "
8247 : "them as geolocation arrays");
8248 : }
8249 5 : if (useGeolocationArray)
8250 : {
8251 10 : CPLDebug("GDAL",
8252 : "Setting geolocation array from variables %s and %s",
8253 5 : poLongVar->GetName().c_str(),
8254 5 : poLatVar->GetName().c_str());
8255 : const std::string osFilenameLong =
8256 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8257 : const std::string osFilenameLat =
8258 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8259 : std::unique_ptr<GDALDataset> poTmpLongDS(
8260 : longDimCount == 1
8261 0 : ? poLongVar->AsClassicDataset(0, 0)
8262 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8263 15 : longDimCount - 2));
8264 5 : auto hTIFFLongDS = GDALTranslate(
8265 : osFilenameLong.c_str(),
8266 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8267 : std::unique_ptr<GDALDataset> poTmpLatDS(
8268 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8269 20 : : poLatVar->AsClassicDataset(
8270 15 : latDimCount - 1, latDimCount - 2));
8271 5 : auto hTIFFLatDS = GDALTranslate(
8272 : osFilenameLat.c_str(),
8273 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8274 5 : const bool bError =
8275 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8276 5 : GDALClose(hTIFFLongDS);
8277 5 : GDALClose(hTIFFLatDS);
8278 5 : if (bError)
8279 : {
8280 0 : VSIUnlink(osFilenameLong.c_str());
8281 0 : VSIUnlink(osFilenameLat.c_str());
8282 0 : return nullptr;
8283 : }
8284 :
8285 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8286 : }
8287 : }
8288 : else
8289 : {
8290 0 : CPLDebug("GDAL",
8291 : "Coordinate variables available for %s, but "
8292 : "longitude and/or latitude variables were not identified",
8293 0 : poParent->GetName().c_str());
8294 : }
8295 : }
8296 :
8297 : // Build gdalwarp arguments
8298 48 : CPLStringList aosArgv;
8299 :
8300 24 : aosArgv.AddString("-of");
8301 24 : aosArgv.AddString("VRT");
8302 :
8303 24 : aosArgv.AddString("-r");
8304 24 : aosArgv.AddString(pszResampleAlg);
8305 :
8306 24 : if (!osDstWKT.empty())
8307 : {
8308 2 : aosArgv.AddString("-t_srs");
8309 2 : aosArgv.AddString(osDstWKT.c_str());
8310 : }
8311 :
8312 24 : if (useGeolocationArray)
8313 5 : aosArgv.AddString("-geoloc");
8314 :
8315 24 : if (gotXSpacing && gotYSpacing)
8316 : {
8317 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8318 : const double dfXMax =
8319 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8320 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8321 : const double dfYMin =
8322 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8323 1 : aosArgv.AddString("-te");
8324 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8325 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8326 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8327 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8328 : }
8329 :
8330 24 : if (poNewDimX && poNewDimY)
8331 : {
8332 3 : aosArgv.AddString("-ts");
8333 : aosArgv.AddString(
8334 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8335 : aosArgv.AddString(
8336 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8337 : }
8338 21 : else if (poNewDimX)
8339 : {
8340 1 : aosArgv.AddString("-ts");
8341 : aosArgv.AddString(
8342 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8343 1 : aosArgv.AddString("0");
8344 : }
8345 20 : else if (poNewDimY)
8346 : {
8347 1 : aosArgv.AddString("-ts");
8348 1 : aosArgv.AddString("0");
8349 : aosArgv.AddString(
8350 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8351 : }
8352 :
8353 : // Create a warped VRT dataset
8354 : GDALWarpAppOptions *psOptions =
8355 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8356 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8357 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8358 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8359 24 : GDALWarpAppOptionsFree(psOptions);
8360 24 : if (poReprojectedDS == nullptr)
8361 3 : return nullptr;
8362 :
8363 : int nBlockXSize;
8364 : int nBlockYSize;
8365 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8366 21 : anBlockSize.emplace_back(nBlockYSize);
8367 21 : anBlockSize.emplace_back(nBlockXSize);
8368 :
8369 21 : GDALGeoTransform gt;
8370 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8371 21 : CPLAssert(eErr == CE_None);
8372 21 : CPL_IGNORE_RET_VAL(eErr);
8373 :
8374 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8375 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8376 42 : poReprojectedDS->GetRasterYSize());
8377 : auto varY = GDALMDArrayRegularlySpaced::Create(
8378 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8379 21 : poDimY->SetIndexingVariable(varY);
8380 :
8381 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8382 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8383 42 : poReprojectedDS->GetRasterXSize());
8384 : auto varX = GDALMDArrayRegularlySpaced::Create(
8385 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8386 21 : poDimX->SetIndexingVariable(varX);
8387 :
8388 21 : apoNewDims.emplace_back(poDimY);
8389 21 : apoNewDims.emplace_back(poDimX);
8390 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8391 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8392 21 : newAr->SetSelf(newAr);
8393 21 : if (poTargetSRS)
8394 : {
8395 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8396 : }
8397 : else
8398 : {
8399 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8400 : }
8401 21 : newAr->m_poVarX = varX;
8402 21 : newAr->m_poVarY = varY;
8403 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8404 21 : newAr->m_poParentDS = std::move(poParentDS);
8405 :
8406 : // If the input array is y,x,band ordered, the above newAr is
8407 : // actually band,y,x ordered as it is more convenient for
8408 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8409 : // array to the order of the input array
8410 21 : if (bYXBandOrder)
8411 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8412 :
8413 19 : return newAr;
8414 : }
8415 :
8416 : /************************************************************************/
8417 : /* GDALMDArrayResampled::IRead() */
8418 : /************************************************************************/
8419 :
8420 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8421 : const size_t *count, const GInt64 *arrayStep,
8422 : const GPtrDiff_t *bufferStride,
8423 : const GDALExtendedDataType &bufferDataType,
8424 : void *pDstBuffer) const
8425 : {
8426 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8427 0 : return false;
8428 :
8429 : struct Stack
8430 : {
8431 : size_t nIters = 0;
8432 : GByte *dst_ptr = nullptr;
8433 : GPtrDiff_t dst_inc_offset = 0;
8434 : };
8435 :
8436 29 : const auto nDims = GetDimensionCount();
8437 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8438 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8439 92 : for (size_t i = 0; i < nDims; i++)
8440 : {
8441 63 : stack[i].dst_inc_offset =
8442 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8443 : }
8444 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8445 :
8446 29 : size_t dimIdx = 0;
8447 29 : const size_t iDimY = nDims - 2;
8448 29 : const size_t iDimX = nDims - 1;
8449 : // Use an array to avoid a false positive warning from CLang Static
8450 : // Analyzer about flushCaches being never read
8451 29 : bool flushCaches[] = {false};
8452 : const bool bYXBandOrder =
8453 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8454 :
8455 38 : lbl_next_depth:
8456 38 : if (dimIdx == iDimY)
8457 : {
8458 33 : if (flushCaches[0])
8459 : {
8460 5 : flushCaches[0] = false;
8461 : // When changing of 2D slice, flush GDAL 2D buffers
8462 5 : m_poParentDS->FlushCache(false);
8463 5 : m_poReprojectedDS->FlushCache(false);
8464 : }
8465 :
8466 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8467 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8468 : arrayStep, bufferStride, bufferDataType,
8469 33 : stack[dimIdx].dst_ptr))
8470 : {
8471 0 : return false;
8472 : }
8473 : }
8474 : else
8475 : {
8476 5 : stack[dimIdx].nIters = count[dimIdx];
8477 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8478 5 : arrayStartIdx[dimIdx])
8479 : {
8480 1 : flushCaches[0] = true;
8481 : }
8482 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8483 5 : arrayStartIdx[dimIdx];
8484 : while (true)
8485 : {
8486 9 : dimIdx++;
8487 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8488 9 : goto lbl_next_depth;
8489 9 : lbl_return_to_caller:
8490 9 : dimIdx--;
8491 9 : if ((--stack[dimIdx].nIters) == 0)
8492 5 : break;
8493 4 : flushCaches[0] = true;
8494 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8495 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8496 : }
8497 : }
8498 38 : if (dimIdx > 0)
8499 9 : goto lbl_return_to_caller;
8500 :
8501 29 : return true;
8502 : }
8503 :
8504 : /************************************************************************/
8505 : /* GetResampled() */
8506 : /************************************************************************/
8507 :
8508 : /** Return an array that is a resampled / reprojected view of the current array
8509 : *
8510 : * This is the same as the C function GDALMDArrayGetResampled().
8511 : *
8512 : * Currently this method can only resample along the last 2 dimensions, unless
8513 : * orthorectifying a NASA EMIT dataset.
8514 : *
8515 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8516 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8517 : *
8518 : * Options available are:
8519 : * <ul>
8520 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8521 : * Can be set to NO to use generic reprojection method.
8522 : * </li>
8523 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8524 : * orthorectification to take into account the value of the
8525 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8526 : * current array along the band dimension are valid.</li>
8527 : * </ul>
8528 : *
8529 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8530 : * apoNewDims[i] can be NULL to let the method automatically
8531 : * determine it.
8532 : * @param resampleAlg Resampling algorithm
8533 : * @param poTargetSRS Target SRS, or nullptr
8534 : * @param papszOptions NULL-terminated list of options, or NULL.
8535 : *
8536 : * @return a new array, that holds a reference to the original one, and thus is
8537 : * a view of it (not a copy), or nullptr in case of error.
8538 : *
8539 : * @since 3.4
8540 : */
8541 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8542 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8543 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8544 : CSLConstList papszOptions) const
8545 : {
8546 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8547 38 : if (!self)
8548 : {
8549 0 : CPLError(CE_Failure, CPLE_AppDefined,
8550 : "Driver implementation issue: m_pSelf not set !");
8551 0 : return nullptr;
8552 : }
8553 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8554 : {
8555 0 : CPLError(CE_Failure, CPLE_AppDefined,
8556 : "GetResampled() only supports numeric data type");
8557 0 : return nullptr;
8558 : }
8559 :
8560 : // Special case for NASA EMIT datasets
8561 76 : auto apoDims = GetDimensions();
8562 36 : if (poTargetSRS == nullptr &&
8563 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8564 20 : apoDims[1]->GetName() == "crosstrack" &&
8565 10 : apoDims[2]->GetName() == "bands" &&
8566 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8567 1 : apoNewDims ==
8568 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8569 30 : apoDims[2]})) ||
8570 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8571 3 : apoDims[1]->GetName() == "crosstrack" &&
8572 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8573 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8574 : "EMIT_ORTHORECTIFICATION", "YES")))
8575 : {
8576 9 : auto poRootGroup = GetRootGroup();
8577 9 : if (poRootGroup)
8578 : {
8579 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8580 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8581 9 : if (poAttrGeotransform &&
8582 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8583 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8584 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8585 9 : poLocationGroup)
8586 : {
8587 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8588 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8589 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8590 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8591 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8592 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8593 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8594 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8595 : {
8596 : return CreateGLTOrthorectified(
8597 : self, poRootGroup, poGLT_X, poGLT_Y,
8598 : /* nGLTIndexOffset = */ -1,
8599 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8600 : }
8601 : }
8602 : }
8603 : }
8604 :
8605 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8606 : "EMIT_ORTHORECTIFICATION", "NO")))
8607 : {
8608 0 : CPLError(CE_Failure, CPLE_AppDefined,
8609 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8610 : "parameters are not compatible with it");
8611 0 : return nullptr;
8612 : }
8613 :
8614 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8615 29 : poTargetSRS, papszOptions);
8616 : }
8617 :
8618 : /************************************************************************/
8619 : /* GDALDatasetFromArray() */
8620 : /************************************************************************/
8621 :
8622 : class GDALDatasetFromArray;
8623 :
8624 : namespace
8625 : {
8626 : struct MetadataItem
8627 : {
8628 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8629 : std::string osName{};
8630 : std::string osDefinition{};
8631 : bool bDefinitionUsesPctForG = false;
8632 : };
8633 :
8634 : struct BandImageryMetadata
8635 : {
8636 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8637 : double dfCentralWavelengthToMicrometer = 1.0;
8638 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8639 : double dfFWHMToMicrometer = 1.0;
8640 : };
8641 :
8642 : } // namespace
8643 :
8644 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8645 : {
8646 : std::vector<GUInt64> m_anOffset{};
8647 : std::vector<size_t> m_anCount{};
8648 : std::vector<GPtrDiff_t> m_anStride{};
8649 :
8650 : protected:
8651 : CPLErr IReadBlock(int, int, void *) override;
8652 : CPLErr IWriteBlock(int, int, void *) override;
8653 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8654 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8655 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8656 : GSpacing nLineSpaceBuf,
8657 : GDALRasterIOExtraArg *psExtraArg) override;
8658 :
8659 : public:
8660 : explicit GDALRasterBandFromArray(
8661 : GDALDatasetFromArray *poDSIn,
8662 : const std::vector<GUInt64> &anOtherDimCoord,
8663 : const std::vector<std::vector<MetadataItem>>
8664 : &aoBandParameterMetadataItems,
8665 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8666 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8667 :
8668 : double GetNoDataValue(int *pbHasNoData) override;
8669 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8670 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8671 : double GetOffset(int *pbHasOffset) override;
8672 : double GetScale(int *pbHasScale) override;
8673 : const char *GetUnitType() override;
8674 : GDALColorInterp GetColorInterpretation() override;
8675 : };
8676 :
8677 : class GDALDatasetFromArray final : public GDALPamDataset
8678 : {
8679 : friend class GDALRasterBandFromArray;
8680 :
8681 : std::shared_ptr<GDALMDArray> m_poArray;
8682 : size_t m_iXDim;
8683 : size_t m_iYDim;
8684 : GDALGeoTransform m_gt{};
8685 : bool m_bHasGT = false;
8686 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8687 : GDALMultiDomainMetadata m_oMDD{};
8688 : std::string m_osOvrFilename{};
8689 :
8690 : public:
8691 208 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8692 : size_t iXDim, size_t iYDim)
8693 208 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8694 : {
8695 : // Initialize an overview filename from the filename of the array
8696 : // and its name.
8697 208 : const std::string &osFilename = m_poArray->GetFilename();
8698 208 : if (!osFilename.empty())
8699 : {
8700 183 : m_osOvrFilename = osFilename;
8701 183 : m_osOvrFilename += '.';
8702 6640 : for (char ch : m_poArray->GetName())
8703 : {
8704 6457 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8705 5723 : (ch >= 'a' && ch <= 'z') || ch == '_')
8706 : {
8707 5196 : m_osOvrFilename += ch;
8708 : }
8709 : else
8710 : {
8711 1261 : m_osOvrFilename += '_';
8712 : }
8713 : }
8714 183 : m_osOvrFilename += ".ovr";
8715 183 : oOvManager.Initialize(this);
8716 : }
8717 208 : }
8718 :
8719 : static GDALDatasetFromArray *
8720 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8721 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8722 : CSLConstList papszOptions);
8723 :
8724 : ~GDALDatasetFromArray() override;
8725 :
8726 342 : CPLErr Close() override
8727 : {
8728 342 : CPLErr eErr = CE_None;
8729 342 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8730 : {
8731 342 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8732 : CE_None)
8733 0 : eErr = CE_Failure;
8734 342 : m_poArray.reset();
8735 : }
8736 342 : return eErr;
8737 : }
8738 :
8739 54 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8740 : {
8741 54 : gt = m_gt;
8742 54 : return m_bHasGT ? CE_None : CE_Failure;
8743 : }
8744 :
8745 62 : const OGRSpatialReference *GetSpatialRef() const override
8746 : {
8747 62 : if (m_poArray->GetDimensionCount() < 2)
8748 3 : return nullptr;
8749 59 : m_poSRS = m_poArray->GetSpatialRef();
8750 59 : if (m_poSRS)
8751 : {
8752 20 : m_poSRS.reset(m_poSRS->Clone());
8753 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8754 60 : for (auto &m : axisMapping)
8755 : {
8756 40 : if (m == static_cast<int>(m_iXDim) + 1)
8757 20 : m = 1;
8758 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8759 20 : m = 2;
8760 : }
8761 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8762 : }
8763 59 : return m_poSRS.get();
8764 : }
8765 :
8766 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8767 : {
8768 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8769 : }
8770 :
8771 165 : char **GetMetadata(const char *pszDomain) override
8772 : {
8773 165 : return m_oMDD.GetMetadata(pszDomain);
8774 : }
8775 :
8776 233 : const char *GetMetadataItem(const char *pszName,
8777 : const char *pszDomain) override
8778 : {
8779 421 : if (!m_osOvrFilename.empty() && pszName &&
8780 435 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8781 14 : EQUAL(pszDomain, "OVERVIEWS"))
8782 : {
8783 14 : return m_osOvrFilename.c_str();
8784 : }
8785 219 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8786 : }
8787 : };
8788 :
8789 416 : GDALDatasetFromArray::~GDALDatasetFromArray()
8790 : {
8791 208 : GDALDatasetFromArray::Close();
8792 416 : }
8793 :
8794 : /************************************************************************/
8795 : /* GDALRasterBandFromArray() */
8796 : /************************************************************************/
8797 :
8798 282 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8799 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8800 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8801 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8802 282 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8803 : {
8804 282 : const auto &poArray(poDSIn->m_poArray);
8805 282 : const auto &dims(poArray->GetDimensions());
8806 282 : const auto nDimCount(dims.size());
8807 564 : const auto blockSize(poArray->GetBlockSize());
8808 269 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8809 551 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8810 140 : blockSize[poDSIn->m_iYDim]))
8811 : : 1;
8812 282 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8813 152 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8814 152 : blockSize[poDSIn->m_iXDim]))
8815 282 : : poDSIn->GetRasterXSize();
8816 282 : eDataType = poArray->GetDataType().GetNumericDataType();
8817 282 : eAccess = poDSIn->eAccess;
8818 282 : m_anOffset.resize(nDimCount);
8819 282 : m_anCount.resize(nDimCount, 1);
8820 282 : m_anStride.resize(nDimCount);
8821 959 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8822 : {
8823 677 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8824 : {
8825 252 : std::string dimName(dims[i]->GetName());
8826 126 : GUInt64 nIndex = anOtherDimCoord[j];
8827 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8828 : // subsetted dimensions as generated by GetView()
8829 126 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8830 : {
8831 : CPLStringList aosTokens(
8832 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8833 6 : if (aosTokens.size() == 5)
8834 : {
8835 6 : dimName = aosTokens[1];
8836 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8837 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8838 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8839 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8840 0 : : nStartDim - (nIndex * -nIncrDim);
8841 : }
8842 : }
8843 126 : if (nDimCount != 3 || dimName != "Band")
8844 : {
8845 70 : SetMetadataItem(
8846 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8847 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8848 : }
8849 :
8850 126 : auto indexingVar = dims[i]->GetIndexingVariable();
8851 :
8852 : // If the indexing variable is also listed in band parameter arrays,
8853 : // then don't use our default formatting
8854 126 : if (indexingVar)
8855 : {
8856 42 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8857 : {
8858 14 : if (oItem.poArray->GetFullName() ==
8859 14 : indexingVar->GetFullName())
8860 : {
8861 12 : indexingVar.reset();
8862 12 : break;
8863 : }
8864 : }
8865 : }
8866 :
8867 154 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8868 28 : indexingVar->GetDimensions()[0]->GetSize() ==
8869 28 : dims[i]->GetSize())
8870 : {
8871 28 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8872 : {
8873 0 : if (!bHasWarned)
8874 : {
8875 0 : CPLError(
8876 : CE_Warning, CPLE_AppDefined,
8877 : "Maximum delay to load band metadata from "
8878 : "dimension indexing variables has expired. "
8879 : "Increase the value of the "
8880 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8881 : "option of GDALMDArray::AsClassicDataset() "
8882 : "(also accessible as the "
8883 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8884 : "configuration option), "
8885 : "or set it to 'unlimited' for unlimited delay. ");
8886 0 : bHasWarned = true;
8887 : }
8888 : }
8889 : else
8890 : {
8891 28 : size_t nCount = 1;
8892 28 : const auto &dt(indexingVar->GetDataType());
8893 56 : std::vector<GByte> abyTmp(dt.GetSize());
8894 56 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8895 28 : nullptr, nullptr, dt, &abyTmp[0]))
8896 : {
8897 28 : char *pszTmp = nullptr;
8898 28 : GDALExtendedDataType::CopyValue(
8899 28 : &abyTmp[0], dt, &pszTmp,
8900 56 : GDALExtendedDataType::CreateString());
8901 28 : if (pszTmp)
8902 : {
8903 28 : SetMetadataItem(
8904 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8905 : pszTmp);
8906 28 : CPLFree(pszTmp);
8907 : }
8908 :
8909 28 : const auto &unit(indexingVar->GetUnit());
8910 28 : if (!unit.empty())
8911 : {
8912 12 : SetMetadataItem(
8913 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8914 : unit.c_str());
8915 : }
8916 : }
8917 : }
8918 : }
8919 :
8920 144 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8921 : {
8922 36 : CPLString osVal;
8923 :
8924 18 : size_t nCount = 1;
8925 18 : const auto &dt(oItem.poArray->GetDataType());
8926 18 : if (oItem.bDefinitionUsesPctForG)
8927 : {
8928 : // There is one and only one %[x][.y]f|g in osDefinition
8929 16 : std::vector<GByte> abyTmp(dt.GetSize());
8930 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8931 8 : nullptr, nullptr, dt, &abyTmp[0]))
8932 : {
8933 8 : double dfVal = 0;
8934 8 : GDALExtendedDataType::CopyValue(
8935 8 : &abyTmp[0], dt, &dfVal,
8936 16 : GDALExtendedDataType::Create(GDT_Float64));
8937 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8938 : }
8939 : }
8940 : else
8941 : {
8942 : // There should be zero or one %s in osDefinition
8943 10 : char *pszValue = nullptr;
8944 10 : if (dt.GetClass() == GEDTC_STRING)
8945 : {
8946 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8947 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8948 2 : dt, &pszValue));
8949 : }
8950 : else
8951 : {
8952 16 : std::vector<GByte> abyTmp(dt.GetSize());
8953 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8954 : nullptr, nullptr, dt,
8955 8 : &abyTmp[0]))
8956 : {
8957 8 : GDALExtendedDataType::CopyValue(
8958 8 : &abyTmp[0], dt, &pszValue,
8959 16 : GDALExtendedDataType::CreateString());
8960 : }
8961 : }
8962 :
8963 10 : if (pszValue)
8964 : {
8965 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8966 10 : CPLFree(pszValue);
8967 : }
8968 : }
8969 18 : if (!osVal.empty())
8970 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
8971 : }
8972 :
8973 126 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8974 : {
8975 : auto &poCentralWavelengthArray =
8976 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8977 4 : size_t nCount = 1;
8978 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
8979 8 : std::vector<GByte> abyTmp(dt.GetSize());
8980 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8981 : &nCount, nullptr, nullptr,
8982 4 : dt, &abyTmp[0]))
8983 : {
8984 4 : double dfVal = 0;
8985 4 : GDALExtendedDataType::CopyValue(
8986 4 : &abyTmp[0], dt, &dfVal,
8987 8 : GDALExtendedDataType::Create(GDT_Float64));
8988 4 : SetMetadataItem(
8989 : "CENTRAL_WAVELENGTH_UM",
8990 : CPLSPrintf(
8991 4 : "%g", dfVal * aoBandImageryMetadata[j]
8992 4 : .dfCentralWavelengthToMicrometer),
8993 : "IMAGERY");
8994 : }
8995 : }
8996 :
8997 126 : if (aoBandImageryMetadata[j].poFWHMArray)
8998 : {
8999 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9000 2 : size_t nCount = 1;
9001 2 : const auto &dt(poFWHMArray->GetDataType());
9002 4 : std::vector<GByte> abyTmp(dt.GetSize());
9003 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9004 2 : nullptr, dt, &abyTmp[0]))
9005 : {
9006 2 : double dfVal = 0;
9007 2 : GDALExtendedDataType::CopyValue(
9008 2 : &abyTmp[0], dt, &dfVal,
9009 4 : GDALExtendedDataType::Create(GDT_Float64));
9010 2 : SetMetadataItem(
9011 : "FWHM_UM",
9012 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9013 2 : .dfFWHMToMicrometer),
9014 : "IMAGERY");
9015 : }
9016 : }
9017 :
9018 126 : m_anOffset[i] = anOtherDimCoord[j];
9019 126 : j++;
9020 : }
9021 : }
9022 282 : }
9023 :
9024 : /************************************************************************/
9025 : /* GetNoDataValue() */
9026 : /************************************************************************/
9027 :
9028 111 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9029 : {
9030 111 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9031 111 : const auto &poArray(l_poDS->m_poArray);
9032 111 : bool bHasNodata = false;
9033 111 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9034 111 : if (pbHasNoData)
9035 99 : *pbHasNoData = bHasNodata;
9036 111 : return res;
9037 : }
9038 :
9039 : /************************************************************************/
9040 : /* GetNoDataValueAsInt64() */
9041 : /************************************************************************/
9042 :
9043 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9044 : {
9045 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9046 1 : const auto &poArray(l_poDS->m_poArray);
9047 1 : bool bHasNodata = false;
9048 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9049 1 : if (pbHasNoData)
9050 1 : *pbHasNoData = bHasNodata;
9051 1 : return nodata;
9052 : }
9053 :
9054 : /************************************************************************/
9055 : /* GetNoDataValueAsUInt64() */
9056 : /************************************************************************/
9057 :
9058 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9059 : {
9060 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9061 1 : const auto &poArray(l_poDS->m_poArray);
9062 1 : bool bHasNodata = false;
9063 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9064 1 : if (pbHasNoData)
9065 1 : *pbHasNoData = bHasNodata;
9066 1 : return nodata;
9067 : }
9068 :
9069 : /************************************************************************/
9070 : /* GetOffset() */
9071 : /************************************************************************/
9072 :
9073 38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9074 : {
9075 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9076 38 : const auto &poArray(l_poDS->m_poArray);
9077 38 : bool bHasValue = false;
9078 38 : double dfRes = poArray->GetOffset(&bHasValue);
9079 38 : if (pbHasOffset)
9080 19 : *pbHasOffset = bHasValue;
9081 38 : return dfRes;
9082 : }
9083 :
9084 : /************************************************************************/
9085 : /* GetUnitType() */
9086 : /************************************************************************/
9087 :
9088 44 : const char *GDALRasterBandFromArray::GetUnitType()
9089 : {
9090 44 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9091 44 : const auto &poArray(l_poDS->m_poArray);
9092 44 : return poArray->GetUnit().c_str();
9093 : }
9094 :
9095 : /************************************************************************/
9096 : /* GetScale() */
9097 : /************************************************************************/
9098 :
9099 36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9100 : {
9101 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9102 36 : const auto &poArray(l_poDS->m_poArray);
9103 36 : bool bHasValue = false;
9104 36 : double dfRes = poArray->GetScale(&bHasValue);
9105 36 : if (pbHasScale)
9106 17 : *pbHasScale = bHasValue;
9107 36 : return dfRes;
9108 : }
9109 :
9110 : /************************************************************************/
9111 : /* IReadBlock() */
9112 : /************************************************************************/
9113 :
9114 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9115 : void *pImage)
9116 : {
9117 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9118 68 : const int nXOff = nBlockXOff * nBlockXSize;
9119 68 : const int nYOff = nBlockYOff * nBlockYSize;
9120 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9121 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9122 : GDALRasterIOExtraArg sExtraArg;
9123 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9124 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9125 : nReqXSize, nReqYSize, eDataType, nDTSize,
9126 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9127 : }
9128 :
9129 : /************************************************************************/
9130 : /* IWriteBlock() */
9131 : /************************************************************************/
9132 :
9133 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9134 : void *pImage)
9135 : {
9136 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9137 1 : const int nXOff = nBlockXOff * nBlockXSize;
9138 1 : const int nYOff = nBlockYOff * nBlockYSize;
9139 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9140 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9141 : GDALRasterIOExtraArg sExtraArg;
9142 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9143 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9144 : nReqXSize, nReqYSize, eDataType, nDTSize,
9145 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9146 : }
9147 :
9148 : /************************************************************************/
9149 : /* IRasterIO() */
9150 : /************************************************************************/
9151 :
9152 333 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9153 : int nYOff, int nXSize, int nYSize,
9154 : void *pData, int nBufXSize,
9155 : int nBufYSize, GDALDataType eBufType,
9156 : GSpacing nPixelSpaceBuf,
9157 : GSpacing nLineSpaceBuf,
9158 : GDALRasterIOExtraArg *psExtraArg)
9159 : {
9160 333 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9161 333 : const auto &poArray(l_poDS->m_poArray);
9162 333 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9163 333 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9164 333 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9165 333 : (nLineSpaceBuf % nBufferDTSize) == 0)
9166 : {
9167 333 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9168 333 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9169 666 : m_anStride[l_poDS->m_iXDim] =
9170 333 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9171 333 : if (poArray->GetDimensionCount() >= 2)
9172 : {
9173 323 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9174 323 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9175 323 : m_anStride[l_poDS->m_iYDim] =
9176 323 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9177 : }
9178 333 : if (eRWFlag == GF_Read)
9179 : {
9180 654 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9181 327 : m_anStride.data(),
9182 654 : GDALExtendedDataType::Create(eBufType), pData)
9183 327 : ? CE_None
9184 327 : : CE_Failure;
9185 : }
9186 : else
9187 : {
9188 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9189 6 : m_anStride.data(),
9190 12 : GDALExtendedDataType::Create(eBufType), pData)
9191 6 : ? CE_None
9192 6 : : CE_Failure;
9193 : }
9194 : }
9195 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9196 : pData, nBufXSize, nBufYSize, eBufType,
9197 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9198 : }
9199 :
9200 : /************************************************************************/
9201 : /* GetColorInterpretation() */
9202 : /************************************************************************/
9203 :
9204 60 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9205 : {
9206 60 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9207 60 : const auto &poArray(l_poDS->m_poArray);
9208 180 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9209 60 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9210 : {
9211 6 : bool bOK = false;
9212 6 : GUInt64 nStartIndex = 0;
9213 6 : if (poArray->GetDimensionCount() == 2 &&
9214 0 : poAttr->GetDimensionCount() == 0)
9215 : {
9216 0 : bOK = true;
9217 : }
9218 6 : else if (poArray->GetDimensionCount() == 3)
9219 : {
9220 6 : uint64_t nExtraDimSamples = 1;
9221 6 : const auto &apoDims = poArray->GetDimensions();
9222 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9223 : {
9224 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9225 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9226 : }
9227 6 : if (poAttr->GetDimensionsSize() ==
9228 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9229 : {
9230 6 : bOK = true;
9231 : }
9232 6 : nStartIndex = nBand - 1;
9233 : }
9234 6 : if (bOK)
9235 : {
9236 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9237 6 : const size_t nCount = 1;
9238 6 : const GInt64 arrayStep = 1;
9239 6 : const GPtrDiff_t bufferStride = 1;
9240 6 : char *pszValue = nullptr;
9241 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9242 6 : oStringDT, &pszValue);
9243 6 : if (pszValue)
9244 : {
9245 : const auto eColorInterp =
9246 6 : GDALGetColorInterpretationByName(pszValue);
9247 6 : CPLFree(pszValue);
9248 6 : return eColorInterp;
9249 : }
9250 : }
9251 : }
9252 54 : return GCI_Undefined;
9253 : }
9254 :
9255 : /************************************************************************/
9256 : /* GDALDatasetFromArray::Create() */
9257 : /************************************************************************/
9258 :
9259 260 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9260 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9261 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9262 :
9263 : {
9264 260 : const auto nDimCount(array->GetDimensionCount());
9265 260 : if (nDimCount == 0)
9266 : {
9267 1 : CPLError(CE_Failure, CPLE_NotSupported,
9268 : "Unsupported number of dimensions");
9269 1 : return nullptr;
9270 : }
9271 517 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9272 258 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9273 : {
9274 1 : CPLError(CE_Failure, CPLE_NotSupported,
9275 : "Only arrays with numeric data types "
9276 : "can be exposed as classic GDALDataset");
9277 1 : return nullptr;
9278 : }
9279 258 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9280 238 : (nDimCount >= 2 && iXDim == iYDim))
9281 : {
9282 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9283 8 : return nullptr;
9284 : }
9285 250 : GUInt64 nTotalBands = 1;
9286 250 : const auto &dims(array->GetDimensions());
9287 825 : for (size_t i = 0; i < nDimCount; ++i)
9288 : {
9289 576 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9290 : {
9291 91 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9292 : {
9293 1 : CPLError(CE_Failure, CPLE_AppDefined,
9294 : "Too many bands. Operate on a sliced view");
9295 1 : return nullptr;
9296 : }
9297 90 : nTotalBands *= dims[i]->GetSize();
9298 : }
9299 : }
9300 :
9301 498 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9302 498 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9303 824 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9304 : {
9305 575 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9306 : {
9307 90 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9308 90 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9309 90 : ++j;
9310 : }
9311 : }
9312 :
9313 249 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9314 :
9315 : const char *pszBandMetadata =
9316 249 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9317 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9318 498 : nNewDimCount);
9319 249 : if (pszBandMetadata)
9320 : {
9321 32 : if (!poRootGroup)
9322 : {
9323 1 : CPLError(CE_Failure, CPLE_AppDefined,
9324 : "Root group should be provided when BAND_METADATA is set");
9325 24 : return nullptr;
9326 : }
9327 31 : CPLJSONDocument oDoc;
9328 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9329 : {
9330 1 : CPLError(CE_Failure, CPLE_AppDefined,
9331 : "Invalid JSON content for BAND_METADATA");
9332 1 : return nullptr;
9333 : }
9334 30 : auto oRoot = oDoc.GetRoot();
9335 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9336 : {
9337 1 : CPLError(CE_Failure, CPLE_AppDefined,
9338 : "Value of BAND_METADATA should be an array");
9339 1 : return nullptr;
9340 : }
9341 :
9342 29 : auto oArray = oRoot.ToArray();
9343 38 : for (int j = 0; j < oArray.Size(); ++j)
9344 : {
9345 30 : const auto oJsonItem = oArray[j];
9346 30 : MetadataItem oItem;
9347 30 : size_t iExtraDimIdx = 0;
9348 :
9349 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9350 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9351 0 : std::shared_ptr<GDALMDArray> poArray;
9352 0 : std::shared_ptr<GDALAttribute> poAttribute;
9353 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9354 : {
9355 1 : CPLError(CE_Failure, CPLE_AppDefined,
9356 : "BAND_METADATA[%d][\"array\"] or "
9357 : "BAND_METADATA[%d][\"attribute\"] is missing",
9358 : j, j);
9359 1 : return nullptr;
9360 : }
9361 48 : else if (!osBandArrayFullname.empty() &&
9362 19 : !osBandAttributeName.empty())
9363 : {
9364 1 : CPLError(
9365 : CE_Failure, CPLE_AppDefined,
9366 : "BAND_METADATA[%d][\"array\"] and "
9367 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9368 : j, j);
9369 1 : return nullptr;
9370 : }
9371 28 : else if (!osBandArrayFullname.empty())
9372 : {
9373 : poArray =
9374 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9375 18 : if (!poArray)
9376 : {
9377 1 : CPLError(CE_Failure, CPLE_AppDefined,
9378 : "Array %s cannot be found",
9379 : osBandArrayFullname.c_str());
9380 3 : return nullptr;
9381 : }
9382 17 : if (poArray->GetDimensionCount() != 1)
9383 : {
9384 1 : CPLError(CE_Failure, CPLE_AppDefined,
9385 : "Array %s is not a 1D array",
9386 : osBandArrayFullname.c_str());
9387 1 : return nullptr;
9388 : }
9389 : const auto &osAuxArrayDimName =
9390 16 : poArray->GetDimensions()[0]->GetName();
9391 : auto oIter =
9392 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9393 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9394 : {
9395 1 : CPLError(
9396 : CE_Failure, CPLE_AppDefined,
9397 : "Dimension %s of array %s is not a non-X/Y dimension "
9398 : "of array %s",
9399 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9400 1 : array->GetName().c_str());
9401 1 : return nullptr;
9402 : }
9403 15 : iExtraDimIdx = oIter->second;
9404 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9405 : }
9406 : else
9407 : {
9408 10 : CPLAssert(!osBandAttributeName.empty());
9409 10 : poAttribute = !osBandAttributeName.empty() &&
9410 10 : osBandAttributeName[0] == '/'
9411 24 : ? poRootGroup->OpenAttributeFromFullname(
9412 : osBandAttributeName)
9413 10 : : array->GetAttribute(osBandAttributeName);
9414 10 : if (!poAttribute)
9415 : {
9416 2 : CPLError(CE_Failure, CPLE_AppDefined,
9417 : "Attribute %s cannot be found",
9418 : osBandAttributeName.c_str());
9419 8 : return nullptr;
9420 : }
9421 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9422 8 : if (aoAttrDims.size() != 1)
9423 : {
9424 4 : CPLError(CE_Failure, CPLE_AppDefined,
9425 : "Attribute %s is not a 1D array",
9426 : osBandAttributeName.c_str());
9427 4 : return nullptr;
9428 : }
9429 4 : bool found = false;
9430 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9431 : {
9432 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9433 5 : ->GetSize() == aoAttrDims[0])
9434 : {
9435 4 : if (found)
9436 : {
9437 2 : CPLError(CE_Failure, CPLE_AppDefined,
9438 : "Several dimensions of %s have the same "
9439 : "size as attribute %s. Cannot infer which "
9440 : "one to bind to!",
9441 1 : array->GetName().c_str(),
9442 : osBandAttributeName.c_str());
9443 1 : return nullptr;
9444 : }
9445 3 : found = true;
9446 3 : iExtraDimIdx = iter.second;
9447 : }
9448 : }
9449 3 : if (!found)
9450 : {
9451 2 : CPLError(
9452 : CE_Failure, CPLE_AppDefined,
9453 : "No dimension of %s has the same size as attribute %s",
9454 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9455 1 : return nullptr;
9456 : }
9457 : }
9458 :
9459 17 : oItem.osName = oJsonItem.GetString("item_name");
9460 17 : if (oItem.osName.empty())
9461 : {
9462 1 : CPLError(CE_Failure, CPLE_AppDefined,
9463 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9464 1 : return nullptr;
9465 : }
9466 :
9467 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9468 :
9469 : // Check correctness of definition
9470 16 : bool bFirstNumericFormatter = true;
9471 16 : std::string osModDefinition;
9472 16 : bool bDefinitionUsesPctForG = false;
9473 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9474 : {
9475 70 : if (osDefinition[k] == '%')
9476 : {
9477 15 : osModDefinition += osDefinition[k];
9478 15 : if (k + 1 == osDefinition.size())
9479 : {
9480 1 : CPLError(CE_Failure, CPLE_AppDefined,
9481 : "Value of "
9482 : "BAND_METADATA[%d][\"item_value\"] = "
9483 : "%s is invalid at offset %d",
9484 : j, osDefinition.c_str(), int(k));
9485 1 : return nullptr;
9486 : }
9487 14 : ++k;
9488 14 : if (osDefinition[k] == '%')
9489 : {
9490 1 : osModDefinition += osDefinition[k];
9491 1 : continue;
9492 : }
9493 13 : if (!bFirstNumericFormatter)
9494 : {
9495 1 : CPLError(CE_Failure, CPLE_AppDefined,
9496 : "Value of "
9497 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9498 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9499 : "formatters should be specified at most once",
9500 : j, osDefinition.c_str(), int(k));
9501 1 : return nullptr;
9502 : }
9503 12 : bFirstNumericFormatter = false;
9504 19 : for (; k < osDefinition.size(); ++k)
9505 : {
9506 19 : osModDefinition += osDefinition[k];
9507 38 : if (!((osDefinition[k] >= '0' &&
9508 16 : osDefinition[k] <= '9') ||
9509 15 : osDefinition[k] == '.'))
9510 12 : break;
9511 : }
9512 24 : if (k == osDefinition.size() ||
9513 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9514 5 : osDefinition[k] != 's'))
9515 : {
9516 1 : CPLError(CE_Failure, CPLE_AppDefined,
9517 : "Value of "
9518 : "BAND_METADATA[%d][\"item_value\"] = "
9519 : "%s is invalid at offset %d: only "
9520 : "%%[x][.y]f|g or %%s formatters are accepted",
9521 : j, osDefinition.c_str(), int(k));
9522 1 : return nullptr;
9523 : }
9524 11 : bDefinitionUsesPctForG =
9525 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9526 11 : if (bDefinitionUsesPctForG)
9527 : {
9528 12 : if (poArray &&
9529 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9530 : {
9531 1 : CPLError(CE_Failure, CPLE_AppDefined,
9532 : "Data type of %s array is not numeric",
9533 1 : poArray->GetName().c_str());
9534 1 : return nullptr;
9535 : }
9536 8 : else if (poAttribute &&
9537 2 : poAttribute->GetDataType().GetClass() !=
9538 6 : GEDTC_NUMERIC)
9539 : {
9540 0 : CPLError(CE_Failure, CPLE_AppDefined,
9541 : "Data type of %s attribute is not numeric",
9542 0 : poAttribute->GetFullName().c_str());
9543 0 : return nullptr;
9544 : }
9545 : }
9546 : }
9547 62 : else if (osDefinition[k] == '$' &&
9548 62 : k + 1 < osDefinition.size() &&
9549 7 : osDefinition[k + 1] == '{')
9550 : {
9551 7 : const auto nPos = osDefinition.find('}', k);
9552 7 : if (nPos == std::string::npos)
9553 : {
9554 1 : CPLError(CE_Failure, CPLE_AppDefined,
9555 : "Value of "
9556 : "BAND_METADATA[%d][\"item_value\"] = "
9557 : "%s is invalid at offset %d",
9558 : j, osDefinition.c_str(), int(k));
9559 3 : return nullptr;
9560 : }
9561 : const auto osAttrName =
9562 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9563 0 : std::shared_ptr<GDALAttribute> poAttr;
9564 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9565 : {
9566 4 : poAttr = poArray->GetAttribute(osAttrName);
9567 4 : if (!poAttr)
9568 : {
9569 1 : CPLError(
9570 : CE_Failure, CPLE_AppDefined,
9571 : "Value of "
9572 : "BAND_METADATA[%d][\"item_value\"] = "
9573 : "%s is invalid: %s is not an attribute of %s",
9574 : j, osDefinition.c_str(), osAttrName.c_str(),
9575 1 : poArray->GetName().c_str());
9576 1 : return nullptr;
9577 : }
9578 : }
9579 : else
9580 : {
9581 : poAttr =
9582 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9583 2 : if (!poAttr)
9584 : {
9585 1 : CPLError(CE_Failure, CPLE_AppDefined,
9586 : "Value of "
9587 : "BAND_METADATA[%d][\"item_value\"] = "
9588 : "%s is invalid: %s is not an attribute",
9589 : j, osDefinition.c_str(),
9590 : osAttrName.c_str());
9591 1 : return nullptr;
9592 : }
9593 : }
9594 4 : k = nPos;
9595 4 : const char *pszValue = poAttr->ReadAsString();
9596 4 : if (!pszValue)
9597 : {
9598 0 : CPLError(CE_Failure, CPLE_AppDefined,
9599 : "Cannot get value of attribute %s as a "
9600 : "string",
9601 : osAttrName.c_str());
9602 0 : return nullptr;
9603 : }
9604 4 : osModDefinition += pszValue;
9605 : }
9606 : else
9607 : {
9608 48 : osModDefinition += osDefinition[k];
9609 : }
9610 : }
9611 :
9612 9 : if (poArray)
9613 8 : oItem.poArray = std::move(poArray);
9614 : else
9615 1 : oItem.poArray = std::move(poAttribute);
9616 9 : oItem.osDefinition = std::move(osModDefinition);
9617 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9618 :
9619 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9620 9 : std::move(oItem));
9621 : }
9622 : }
9623 :
9624 450 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9625 : const char *pszBandImageryMetadata =
9626 225 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9627 225 : if (pszBandImageryMetadata)
9628 : {
9629 20 : if (!poRootGroup)
9630 : {
9631 1 : CPLError(CE_Failure, CPLE_AppDefined,
9632 : "Root group should be provided when BAND_IMAGERY_METADATA "
9633 : "is set");
9634 17 : return nullptr;
9635 : }
9636 19 : CPLJSONDocument oDoc;
9637 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9638 : {
9639 1 : CPLError(CE_Failure, CPLE_AppDefined,
9640 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9641 1 : return nullptr;
9642 : }
9643 18 : auto oRoot = oDoc.GetRoot();
9644 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9645 : {
9646 1 : CPLError(CE_Failure, CPLE_AppDefined,
9647 : "Value of BAND_IMAGERY_METADATA should be an object");
9648 1 : return nullptr;
9649 : }
9650 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9651 : {
9652 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9653 20 : oJsonItem.GetName() == "FWHM_UM")
9654 : {
9655 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9656 : const auto osBandAttributeName =
9657 34 : oJsonItem.GetString("attribute");
9658 0 : std::shared_ptr<GDALMDArray> poArray;
9659 0 : std::shared_ptr<GDALAttribute> poAttribute;
9660 17 : size_t iExtraDimIdx = 0;
9661 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9662 : {
9663 2 : CPLError(CE_Failure, CPLE_AppDefined,
9664 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9665 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9666 : "missing",
9667 2 : oJsonItem.GetName().c_str(),
9668 2 : oJsonItem.GetName().c_str());
9669 1 : return nullptr;
9670 : }
9671 25 : else if (!osBandArrayFullname.empty() &&
9672 9 : !osBandAttributeName.empty())
9673 : {
9674 2 : CPLError(CE_Failure, CPLE_AppDefined,
9675 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9676 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9677 : "mutually exclusive",
9678 2 : oJsonItem.GetName().c_str(),
9679 2 : oJsonItem.GetName().c_str());
9680 1 : return nullptr;
9681 : }
9682 15 : else if (!osBandArrayFullname.empty())
9683 : {
9684 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9685 8 : osBandArrayFullname);
9686 8 : if (!poArray)
9687 : {
9688 1 : CPLError(CE_Failure, CPLE_AppDefined,
9689 : "Array %s cannot be found",
9690 : osBandArrayFullname.c_str());
9691 3 : return nullptr;
9692 : }
9693 7 : if (poArray->GetDimensionCount() != 1)
9694 : {
9695 1 : CPLError(CE_Failure, CPLE_AppDefined,
9696 : "Array %s is not a 1D array",
9697 : osBandArrayFullname.c_str());
9698 1 : return nullptr;
9699 : }
9700 : const auto &osAuxArrayDimName =
9701 6 : poArray->GetDimensions()[0]->GetName();
9702 : auto oIter =
9703 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9704 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9705 : {
9706 1 : CPLError(CE_Failure, CPLE_AppDefined,
9707 : "Dimension \"%s\" of array \"%s\" is not a "
9708 : "non-X/Y dimension of array \"%s\"",
9709 : osAuxArrayDimName.c_str(),
9710 : osBandArrayFullname.c_str(),
9711 1 : array->GetName().c_str());
9712 1 : return nullptr;
9713 : }
9714 5 : iExtraDimIdx = oIter->second;
9715 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9716 : }
9717 : else
9718 : {
9719 : poAttribute =
9720 7 : !osBandAttributeName.empty() &&
9721 7 : osBandAttributeName[0] == '/'
9722 16 : ? poRootGroup->OpenAttributeFromFullname(
9723 : osBandAttributeName)
9724 7 : : array->GetAttribute(osBandAttributeName);
9725 7 : if (!poAttribute)
9726 : {
9727 2 : CPLError(CE_Failure, CPLE_AppDefined,
9728 : "Attribute %s cannot be found",
9729 : osBandAttributeName.c_str());
9730 6 : return nullptr;
9731 : }
9732 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9733 5 : if (aoAttrDims.size() != 1)
9734 : {
9735 2 : CPLError(CE_Failure, CPLE_AppDefined,
9736 : "Attribute %s is not a 1D array",
9737 : osBandAttributeName.c_str());
9738 2 : return nullptr;
9739 : }
9740 3 : bool found = false;
9741 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9742 : {
9743 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9744 4 : ->GetSize() == aoAttrDims[0])
9745 : {
9746 3 : if (found)
9747 : {
9748 2 : CPLError(CE_Failure, CPLE_AppDefined,
9749 : "Several dimensions of %s have the "
9750 : "same size as attribute %s. Cannot "
9751 : "infer which one to bind to!",
9752 1 : array->GetName().c_str(),
9753 : osBandAttributeName.c_str());
9754 1 : return nullptr;
9755 : }
9756 2 : found = true;
9757 2 : iExtraDimIdx = iter.second;
9758 : }
9759 : }
9760 2 : if (!found)
9761 : {
9762 2 : CPLError(CE_Failure, CPLE_AppDefined,
9763 : "No dimension of %s has the same size as "
9764 : "attribute %s",
9765 1 : array->GetName().c_str(),
9766 : osBandAttributeName.c_str());
9767 1 : return nullptr;
9768 : }
9769 : }
9770 :
9771 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9772 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9773 : {
9774 4 : if (osUnit.back() != '}')
9775 : {
9776 2 : CPLError(CE_Failure, CPLE_AppDefined,
9777 : "Value of "
9778 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9779 : "%s is invalid",
9780 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9781 2 : return nullptr;
9782 : }
9783 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9784 0 : std::shared_ptr<GDALAttribute> poAttr;
9785 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9786 : {
9787 2 : poAttr = poArray->GetAttribute(osAttrName);
9788 2 : if (!poAttr)
9789 : {
9790 2 : CPLError(
9791 : CE_Failure, CPLE_AppDefined,
9792 : "Value of "
9793 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9794 : "%s is invalid: %s is not an attribute of %s",
9795 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9796 : osAttrName.c_str(),
9797 : osBandArrayFullname.c_str());
9798 1 : return nullptr;
9799 : }
9800 : }
9801 : else
9802 : {
9803 : poAttr =
9804 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9805 1 : if (!poAttr)
9806 : {
9807 0 : CPLError(
9808 : CE_Failure, CPLE_AppDefined,
9809 : "Value of "
9810 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9811 : "%s is invalid: %s is not an attribute",
9812 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9813 : osAttrName.c_str());
9814 0 : return nullptr;
9815 : }
9816 : }
9817 :
9818 2 : const char *pszValue = poAttr->ReadAsString();
9819 2 : if (!pszValue)
9820 : {
9821 0 : CPLError(CE_Failure, CPLE_AppDefined,
9822 : "Cannot get value of attribute %s of %s as a "
9823 : "string",
9824 : osAttrName.c_str(),
9825 : osBandArrayFullname.c_str());
9826 0 : return nullptr;
9827 : }
9828 2 : osUnit = pszValue;
9829 : }
9830 4 : double dfConvToUM = 1.0;
9831 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9832 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9833 3 : osUnit == "nanometers")
9834 : {
9835 1 : dfConvToUM = 1e-3;
9836 : }
9837 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9838 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9839 1 : osUnit == "micrometers"))
9840 : {
9841 2 : CPLError(CE_Failure, CPLE_AppDefined,
9842 : "Unhandled value for "
9843 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9844 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9845 1 : return nullptr;
9846 : }
9847 :
9848 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9849 :
9850 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9851 3 : if (poArray)
9852 2 : abstractArray = std::move(poArray);
9853 : else
9854 1 : abstractArray = std::move(poAttribute);
9855 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9856 : {
9857 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9858 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9859 : }
9860 : else
9861 : {
9862 1 : item.poFWHMArray = std::move(abstractArray);
9863 1 : item.dfFWHMToMicrometer = dfConvToUM;
9864 : }
9865 : }
9866 : else
9867 : {
9868 1 : CPLError(CE_Warning, CPLE_AppDefined,
9869 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9870 2 : oJsonItem.GetName().c_str());
9871 : }
9872 : }
9873 : }
9874 :
9875 416 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9876 :
9877 208 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9878 :
9879 208 : poDS->nRasterYSize =
9880 208 : nDimCount < 2 ? 1
9881 195 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9882 195 : dims[iYDim]->GetSize()));
9883 416 : poDS->nRasterXSize = static_cast<int>(
9884 208 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9885 :
9886 416 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9887 416 : std::vector<GUInt64> anStackIters(nDimCount);
9888 416 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9889 658 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9890 : {
9891 450 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9892 : {
9893 47 : anMapNewToOld[j] = i;
9894 47 : j++;
9895 : }
9896 : }
9897 :
9898 208 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9899 :
9900 416 : const auto attrs(array->GetAttributes());
9901 321 : for (const auto &attr : attrs)
9902 : {
9903 113 : if (attr->GetName() != "COLOR_INTERPRETATION")
9904 : {
9905 204 : auto stringArray = attr->ReadAsStringArray();
9906 204 : std::string val;
9907 102 : if (stringArray.size() > 1)
9908 : {
9909 44 : val += '{';
9910 : }
9911 452 : for (int i = 0; i < stringArray.size(); ++i)
9912 : {
9913 350 : if (i > 0)
9914 248 : val += ',';
9915 350 : val += stringArray[i];
9916 : }
9917 102 : if (stringArray.size() > 1)
9918 : {
9919 44 : val += '}';
9920 : }
9921 102 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9922 : }
9923 : }
9924 :
9925 208 : const char *pszDelay = CSLFetchNameValueDef(
9926 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9927 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9928 : const double dfDelay =
9929 208 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9930 208 : const auto nStartTime = time(nullptr);
9931 208 : bool bHasWarned = false;
9932 : // Instantiate bands by iterating over non-XY variables
9933 208 : size_t iDim = 0;
9934 208 : int nCurBand = 1;
9935 331 : lbl_next_depth:
9936 331 : if (iDim < nNewDimCount)
9937 : {
9938 49 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9939 49 : anOtherDimCoord[iDim] = 0;
9940 : while (true)
9941 : {
9942 123 : ++iDim;
9943 123 : goto lbl_next_depth;
9944 123 : lbl_return_to_caller:
9945 123 : --iDim;
9946 123 : --anStackIters[iDim];
9947 123 : if (anStackIters[iDim] == 0)
9948 49 : break;
9949 74 : ++anOtherDimCoord[iDim];
9950 : }
9951 : }
9952 : else
9953 : {
9954 564 : poDS->SetBand(nCurBand,
9955 : new GDALRasterBandFromArray(
9956 282 : poDS.get(), anOtherDimCoord,
9957 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9958 282 : dfDelay, nStartTime, bHasWarned));
9959 282 : ++nCurBand;
9960 : }
9961 331 : if (iDim > 0)
9962 123 : goto lbl_return_to_caller;
9963 :
9964 208 : if (!array->GetFilename().empty())
9965 : {
9966 183 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9967 : std::string osDerivedDatasetName(
9968 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9969 366 : int(iYDim), array->GetFullName().c_str()));
9970 183 : if (!array->GetContext().empty())
9971 : {
9972 2 : osDerivedDatasetName += " with context ";
9973 2 : osDerivedDatasetName += array->GetContext();
9974 : }
9975 183 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9976 183 : poDS->TryLoadXML();
9977 :
9978 2 : for (const auto &[pszKey, pszValue] :
9979 : cpl::IterateNameValue(static_cast<CSLConstList>(
9980 185 : poDS->GDALPamDataset::GetMetadata())))
9981 : {
9982 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9983 : }
9984 : }
9985 :
9986 208 : return poDS.release();
9987 : }
9988 :
9989 : /************************************************************************/
9990 : /* AsClassicDataset() */
9991 : /************************************************************************/
9992 :
9993 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9994 : *
9995 : * In the case of > 2D arrays, additional dimensions will be represented as
9996 : * raster bands.
9997 : *
9998 : * The "reverse" method is GDALRasterBand::AsMDArray().
9999 : *
10000 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10001 : *
10002 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10003 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10004 : * Ignored if the dimension count is 1.
10005 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10006 : * and BAND_IMAGERY_METADATA option.
10007 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10008 : * nullptr. Current supported options are:
10009 : * <ul>
10010 : * <li>BAND_METADATA: JSON serialized array defining which
10011 : * arrays of the poRootGroup, indexed by non-X and Y
10012 : * dimensions, should be mapped as band metadata items.
10013 : * Each array item should be an object with the
10014 : * following members:
10015 : * - "array": full name of a band parameter array.
10016 : * Such array must be a one
10017 : * dimensional array, and its dimension must be one of
10018 : * the dimensions of the array on which the method is
10019 : * called (excluding the X and Y dimensions).
10020 : * - "attribute": name relative to *this array or full
10021 : * name of a single dimension numeric array whose size
10022 : * must be one of the dimensions of *this array
10023 : * (excluding the X and Y dimensions).
10024 : * "array" and "attribute" are mutually exclusive.
10025 : * - "item_name": band metadata item name
10026 : * - "item_value": (optional) String, where "%[x][.y]f",
10027 : * "%[x][.y]g" or "%s" printf-like formatting can be
10028 : * used to format the corresponding value of the
10029 : * parameter array. The percentage character should be
10030 : * repeated: "%%"
10031 : * "${attribute_name}" can also be used to include the
10032 : * value of an attribute for "array" when set and if
10033 : * not starting with '/'. Otherwise if starting with
10034 : * '/', it is the full path to the attribute.
10035 : *
10036 : * If "item_value" is not provided, a default formatting
10037 : * of the value will be applied.
10038 : *
10039 : * Example:
10040 : * [
10041 : * {
10042 : * "array": "/sensor_band_parameters/wavelengths",
10043 : * "item_name": "WAVELENGTH",
10044 : * "item_value": "%.1f ${units}"
10045 : * },
10046 : * {
10047 : * "array": "/sensor_band_parameters/fwhm",
10048 : * "item_name": "FWHM"
10049 : * },
10050 : * {
10051 : * "array": "/sensor_band_parameters/fwhm",
10052 : * "item_name": "FWHM_UNIT",
10053 : * "item_value": "${units}"
10054 : * }
10055 : * ]
10056 : *
10057 : * Example for Planet Labs Tanager radiance products:
10058 : * [
10059 : * {
10060 : * "attribute": "center_wavelengths",
10061 : * "item_name": "WAVELENGTH",
10062 : * "item_value": "%.1f ${center_wavelengths_units}"
10063 : * }
10064 : * ]
10065 : *
10066 : * </li>
10067 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10068 : * JSON serialized object defining which arrays of the
10069 : * poRootGroup, indexed by non-X and Y dimensions,
10070 : * should be mapped as band metadata items in the
10071 : * band IMAGERY domain.
10072 : * The object currently accepts 2 members:
10073 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10074 : * micrometers.
10075 : * - "FWHM_UM": Full-width half-maximum
10076 : * in micrometers.
10077 : * The value of each member should be an object with the
10078 : * following members:
10079 : * - "array": full name of a band parameter array.
10080 : * Such array must be a one dimensional array, and its
10081 : * dimension must be one of the dimensions of the
10082 : * array on which the method is called
10083 : * (excluding the X and Y dimensions).
10084 : * - "attribute": name relative to *this array or full
10085 : * name of a single dimension numeric array whose size
10086 : * must be one of the dimensions of *this array
10087 : * (excluding the X and Y dimensions).
10088 : * "array" and "attribute" are mutually exclusive,
10089 : * and one of them is required.
10090 : * - "unit": (optional) unit of the values pointed in
10091 : * the above array.
10092 : * Can be a literal string or a string of the form
10093 : * "${attribute_name}" to point to an attribute for
10094 : * "array" when set and if no starting
10095 : * with '/'. Otherwise if starting with '/', it is
10096 : * the full path to the attribute.
10097 : * Accepted values are "um", "micrometer"
10098 : * (with UK vs US spelling, singular or plural), "nm",
10099 : * "nanometer" (with UK vs US spelling, singular or
10100 : * plural)
10101 : * If not provided, micrometer is assumed.
10102 : *
10103 : * Example for EMIT datasets:
10104 : * {
10105 : * "CENTRAL_WAVELENGTH_UM": {
10106 : * "array": "/sensor_band_parameters/wavelengths",
10107 : * "unit": "${units}"
10108 : * },
10109 : * "FWHM_UM": {
10110 : * "array": "/sensor_band_parameters/fwhm",
10111 : * "unit": "${units}"
10112 : * }
10113 : * }
10114 : *
10115 : * Example for Planet Labs Tanager radiance products:
10116 : * {
10117 : * "CENTRAL_WAVELENGTH_UM": {
10118 : * "attribute": "center_wavelengths",
10119 : * "unit": "${center_wavelengths_units}"
10120 : * },
10121 : * "FWHM_UM": {
10122 : * "attribute": "fwhm",
10123 : * "unit": "${fwhm_units}"
10124 : * }
10125 : * }
10126 : *
10127 : * </li>
10128 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10129 : * seconds allowed to set the DIM_{dimname}_VALUE band
10130 : * metadata items from the indexing variable of the
10131 : * dimensions.
10132 : * Default value is 5. 'unlimited' can be used to mean
10133 : * unlimited delay. Can also be defined globally with
10134 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10135 : * option.</li>
10136 : * </ul>
10137 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10138 : */
10139 : GDALDataset *
10140 260 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10141 : const std::shared_ptr<GDALGroup> &poRootGroup,
10142 : CSLConstList papszOptions) const
10143 : {
10144 520 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10145 260 : if (!self)
10146 : {
10147 0 : CPLError(CE_Failure, CPLE_AppDefined,
10148 : "Driver implementation issue: m_pSelf not set !");
10149 0 : return nullptr;
10150 : }
10151 260 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10152 260 : papszOptions);
10153 : }
10154 :
10155 : /************************************************************************/
10156 : /* GetStatistics() */
10157 : /************************************************************************/
10158 :
10159 : /**
10160 : * \brief Fetch statistics.
10161 : *
10162 : * Returns the minimum, maximum, mean and standard deviation of all
10163 : * pixel values in this array.
10164 : *
10165 : * If bForce is FALSE results will only be returned if it can be done
10166 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10167 : * results cannot be returned efficiently, the method will return CE_Warning
10168 : * but no warning will have been issued. This is a non-standard use of
10169 : * the CE_Warning return value to indicate "nothing done".
10170 : *
10171 : * When cached statistics are not available, and bForce is TRUE,
10172 : * ComputeStatistics() is called.
10173 : *
10174 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10175 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10176 : * after the first request.
10177 : *
10178 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10179 : *
10180 : * This method is the same as the C function GDALMDArrayGetStatistics().
10181 : *
10182 : * @param bApproxOK Currently ignored. In the future, should be set to true
10183 : * if statistics on the whole array are wished, or to false if a subset of it
10184 : * may be used.
10185 : *
10186 : * @param bForce If false statistics will only be returned if it can
10187 : * be done without rescanning the image.
10188 : *
10189 : * @param pdfMin Location into which to load image minimum (may be NULL).
10190 : *
10191 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10192 : *
10193 : * @param pdfMean Location into which to load image mean (may be NULL).
10194 : *
10195 : * @param pdfStdDev Location into which to load image standard deviation
10196 : * (may be NULL).
10197 : *
10198 : * @param pnValidCount Number of samples whose value is different from the
10199 : * nodata value. (may be NULL)
10200 : *
10201 : * @param pfnProgress a function to call to report progress, or NULL.
10202 : *
10203 : * @param pProgressData application data to pass to the progress function.
10204 : *
10205 : * @return CE_None on success, CE_Warning if no values returned,
10206 : * CE_Failure if an error occurs.
10207 : *
10208 : * @since GDAL 3.2
10209 : */
10210 :
10211 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10212 : double *pdfMax, double *pdfMean,
10213 : double *pdfStdDev, GUInt64 *pnValidCount,
10214 : GDALProgressFunc pfnProgress,
10215 : void *pProgressData)
10216 : {
10217 10 : if (!bForce)
10218 1 : return CE_Warning;
10219 :
10220 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10221 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10222 9 : ? CE_None
10223 9 : : CE_Failure;
10224 : }
10225 :
10226 : /************************************************************************/
10227 : /* ComputeStatistics() */
10228 : /************************************************************************/
10229 :
10230 : /**
10231 : * \brief Compute statistics.
10232 : *
10233 : * Returns the minimum, maximum, mean and standard deviation of all
10234 : * pixel values in this array.
10235 : *
10236 : * Pixels taken into account in statistics are those whose mask value
10237 : * (as determined by GetMask()) is non-zero.
10238 : *
10239 : * Once computed, the statistics will generally be "set" back on the
10240 : * owing dataset.
10241 : *
10242 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10243 : *
10244 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10245 : * and GDALMDArrayComputeStatisticsEx().
10246 : *
10247 : * @param bApproxOK Currently ignored. In the future, should be set to true
10248 : * if statistics on the whole array are wished, or to false if a subset of it
10249 : * may be used.
10250 : *
10251 : * @param pdfMin Location into which to load image minimum (may be NULL).
10252 : *
10253 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10254 : *
10255 : * @param pdfMean Location into which to load image mean (may be NULL).
10256 : *
10257 : * @param pdfStdDev Location into which to load image standard deviation
10258 : * (may be NULL).
10259 : *
10260 : * @param pnValidCount Number of samples whose value is different from the
10261 : * nodata value. (may be NULL)
10262 : *
10263 : * @param pfnProgress a function to call to report progress, or NULL.
10264 : *
10265 : * @param pProgressData application data to pass to the progress function.
10266 : *
10267 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10268 : * Options are driver specific. For now the netCDF and Zarr
10269 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10270 : * to add or update the actual_range attribute with the
10271 : * computed min/max, only if done on the full array, in non
10272 : * approximate mode, and the dataset is opened in update
10273 : * mode.
10274 : *
10275 : * @return true on success
10276 : *
10277 : * @since GDAL 3.2
10278 : */
10279 :
10280 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10281 : double *pdfMax, double *pdfMean,
10282 : double *pdfStdDev, GUInt64 *pnValidCount,
10283 : GDALProgressFunc pfnProgress,
10284 : void *pProgressData,
10285 : CSLConstList papszOptions)
10286 : {
10287 : struct StatsPerChunkType
10288 : {
10289 : const GDALMDArray *array = nullptr;
10290 : std::shared_ptr<GDALMDArray> poMask{};
10291 : double dfMin = cpl::NumericLimits<double>::max();
10292 : double dfMax = -cpl::NumericLimits<double>::max();
10293 : double dfMean = 0.0;
10294 : double dfM2 = 0.0;
10295 : GUInt64 nValidCount = 0;
10296 : std::vector<GByte> abyData{};
10297 : std::vector<double> adfData{};
10298 : std::vector<GByte> abyMaskData{};
10299 : GDALProgressFunc pfnProgress = nullptr;
10300 : void *pProgressData = nullptr;
10301 : };
10302 :
10303 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10304 : const GUInt64 *chunkArrayStartIdx,
10305 : const size_t *chunkCount, GUInt64 iCurChunk,
10306 : GUInt64 nChunkCount, void *pUserData)
10307 : {
10308 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10309 13 : const GDALMDArray *array = data->array;
10310 13 : const GDALMDArray *poMask = data->poMask.get();
10311 13 : const size_t nDims = array->GetDimensionCount();
10312 13 : size_t nVals = 1;
10313 34 : for (size_t i = 0; i < nDims; i++)
10314 21 : nVals *= chunkCount[i];
10315 :
10316 : // Get mask
10317 13 : data->abyMaskData.resize(nVals);
10318 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10319 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10320 : {
10321 0 : return false;
10322 : }
10323 :
10324 : // Get data
10325 13 : const auto &oType = array->GetDataType();
10326 13 : if (oType.GetNumericDataType() == GDT_Float64)
10327 : {
10328 6 : data->adfData.resize(nVals);
10329 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10330 6 : oType, &data->adfData[0]))
10331 : {
10332 0 : return false;
10333 : }
10334 : }
10335 : else
10336 : {
10337 7 : data->abyData.resize(nVals * oType.GetSize());
10338 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10339 7 : oType, &data->abyData[0]))
10340 : {
10341 0 : return false;
10342 : }
10343 7 : data->adfData.resize(nVals);
10344 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10345 7 : static_cast<int>(oType.GetSize()),
10346 7 : &data->adfData[0], GDT_Float64,
10347 : static_cast<int>(sizeof(double)),
10348 : static_cast<GPtrDiff_t>(nVals));
10349 : }
10350 912 : for (size_t i = 0; i < nVals; i++)
10351 : {
10352 899 : if (data->abyMaskData[i])
10353 : {
10354 894 : const double dfValue = data->adfData[i];
10355 894 : data->dfMin = std::min(data->dfMin, dfValue);
10356 894 : data->dfMax = std::max(data->dfMax, dfValue);
10357 894 : data->nValidCount++;
10358 894 : const double dfDelta = dfValue - data->dfMean;
10359 894 : data->dfMean += dfDelta / data->nValidCount;
10360 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10361 : }
10362 : }
10363 13 : if (data->pfnProgress &&
10364 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10365 : "", data->pProgressData))
10366 : {
10367 0 : return false;
10368 : }
10369 13 : return true;
10370 : };
10371 :
10372 13 : const auto &oType = GetDataType();
10373 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10374 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10375 : {
10376 0 : CPLError(
10377 : CE_Failure, CPLE_NotSupported,
10378 : "Statistics can only be computed on non-complex numeric data type");
10379 0 : return false;
10380 : }
10381 :
10382 13 : const size_t nDims = GetDimensionCount();
10383 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10384 26 : std::vector<GUInt64> count(nDims);
10385 13 : const auto &poDims = GetDimensions();
10386 34 : for (size_t i = 0; i < nDims; i++)
10387 : {
10388 21 : count[i] = poDims[i]->GetSize();
10389 : }
10390 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10391 : const size_t nMaxChunkSize =
10392 : pszSwathSize
10393 13 : ? static_cast<size_t>(
10394 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10395 0 : CPLAtoGIntBig(pszSwathSize)))
10396 : : static_cast<size_t>(
10397 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10398 13 : GDALGetCacheMax64() / 4));
10399 26 : StatsPerChunkType sData;
10400 13 : sData.array = this;
10401 13 : sData.poMask = GetMask(nullptr);
10402 13 : if (sData.poMask == nullptr)
10403 : {
10404 0 : return false;
10405 : }
10406 13 : sData.pfnProgress = pfnProgress;
10407 13 : sData.pProgressData = pProgressData;
10408 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10409 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10410 13 : PerChunkFunc, &sData))
10411 : {
10412 0 : return false;
10413 : }
10414 :
10415 13 : if (pdfMin)
10416 13 : *pdfMin = sData.dfMin;
10417 :
10418 13 : if (pdfMax)
10419 13 : *pdfMax = sData.dfMax;
10420 :
10421 13 : if (pdfMean)
10422 11 : *pdfMean = sData.dfMean;
10423 :
10424 : const double dfStdDev =
10425 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10426 13 : if (pdfStdDev)
10427 11 : *pdfStdDev = dfStdDev;
10428 :
10429 13 : if (pnValidCount)
10430 11 : *pnValidCount = sData.nValidCount;
10431 :
10432 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10433 13 : sData.nValidCount, papszOptions);
10434 :
10435 13 : return true;
10436 : }
10437 :
10438 : /************************************************************************/
10439 : /* SetStatistics() */
10440 : /************************************************************************/
10441 : //! @cond Doxygen_Suppress
10442 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10443 : double /* dfMax */, double /* dfMean */,
10444 : double /* dfStdDev */,
10445 : GUInt64 /* nValidCount */,
10446 : CSLConstList /* papszOptions */)
10447 : {
10448 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10449 5 : return false;
10450 : }
10451 :
10452 : //! @endcond
10453 :
10454 : /************************************************************************/
10455 : /* ClearStatistics() */
10456 : /************************************************************************/
10457 :
10458 : /**
10459 : * \brief Clear statistics.
10460 : *
10461 : * @since GDAL 3.4
10462 : */
10463 0 : void GDALMDArray::ClearStatistics()
10464 : {
10465 0 : }
10466 :
10467 : /************************************************************************/
10468 : /* GetCoordinateVariables() */
10469 : /************************************************************************/
10470 :
10471 : /**
10472 : * \brief Return coordinate variables.
10473 : *
10474 : * Coordinate variables are an alternate way of indexing an array that can
10475 : * be sometimes used. For example, an array collected through remote sensing
10476 : * might be indexed by (scanline, pixel). But there can be
10477 : * a longitude and latitude arrays alongside that are also both indexed by
10478 : * (scanline, pixel), and are referenced from operational arrays for
10479 : * reprojection purposes.
10480 : *
10481 : * For netCDF, this will return the arrays referenced by the "coordinates"
10482 : * attribute.
10483 : *
10484 : * This method is the same as the C function
10485 : * GDALMDArrayGetCoordinateVariables().
10486 : *
10487 : * @return a vector of arrays
10488 : *
10489 : * @since GDAL 3.4
10490 : */
10491 :
10492 : std::vector<std::shared_ptr<GDALMDArray>>
10493 13 : GDALMDArray::GetCoordinateVariables() const
10494 : {
10495 13 : return {};
10496 : }
10497 :
10498 : /************************************************************************/
10499 : /* ~GDALExtendedDataType() */
10500 : /************************************************************************/
10501 :
10502 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10503 :
10504 : /************************************************************************/
10505 : /* GDALExtendedDataType() */
10506 : /************************************************************************/
10507 :
10508 9766 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10509 9766 : GDALExtendedDataTypeSubType eSubType)
10510 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10511 9766 : m_nMaxStringLength(nMaxStringLength)
10512 : {
10513 9766 : }
10514 :
10515 : /************************************************************************/
10516 : /* GDALExtendedDataType() */
10517 : /************************************************************************/
10518 :
10519 37407 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10520 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10521 37407 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10522 : {
10523 37407 : }
10524 :
10525 : /************************************************************************/
10526 : /* GDALExtendedDataType() */
10527 : /************************************************************************/
10528 :
10529 63 : GDALExtendedDataType::GDALExtendedDataType(
10530 : const std::string &osName, GDALDataType eBaseType,
10531 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10532 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10533 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10534 : {
10535 63 : }
10536 :
10537 : /************************************************************************/
10538 : /* GDALExtendedDataType() */
10539 : /************************************************************************/
10540 :
10541 855 : GDALExtendedDataType::GDALExtendedDataType(
10542 : const std::string &osName, size_t nTotalSize,
10543 855 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10544 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10545 855 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10546 : {
10547 855 : }
10548 :
10549 : /************************************************************************/
10550 : /* GDALExtendedDataType() */
10551 : /************************************************************************/
10552 :
10553 : /** Move constructor. */
10554 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10555 :
10556 : /************************************************************************/
10557 : /* GDALExtendedDataType() */
10558 : /************************************************************************/
10559 :
10560 : /** Copy constructor. */
10561 16127 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10562 32254 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10563 16127 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10564 16127 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10565 16127 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10566 : {
10567 16127 : if (m_eClass == GEDTC_COMPOUND)
10568 : {
10569 472 : for (const auto &elt : other.m_aoComponents)
10570 : {
10571 312 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10572 : }
10573 : }
10574 16127 : }
10575 :
10576 : /************************************************************************/
10577 : /* operator= () */
10578 : /************************************************************************/
10579 :
10580 : /** Copy assignment. */
10581 : GDALExtendedDataType &
10582 1066 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10583 : {
10584 1066 : if (this != &other)
10585 : {
10586 1066 : m_osName = other.m_osName;
10587 1066 : m_eClass = other.m_eClass;
10588 1066 : m_eSubType = other.m_eSubType;
10589 1066 : m_eNumericDT = other.m_eNumericDT;
10590 1066 : m_nSize = other.m_nSize;
10591 1066 : m_nMaxStringLength = other.m_nMaxStringLength;
10592 1066 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10593 1066 : m_aoComponents.clear();
10594 1066 : if (m_eClass == GEDTC_COMPOUND)
10595 : {
10596 0 : for (const auto &elt : other.m_aoComponents)
10597 : {
10598 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10599 : }
10600 : }
10601 : }
10602 1066 : return *this;
10603 : }
10604 :
10605 : /************************************************************************/
10606 : /* operator= () */
10607 : /************************************************************************/
10608 :
10609 : /** Move assignment. */
10610 : GDALExtendedDataType &
10611 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10612 :
10613 : /************************************************************************/
10614 : /* Create() */
10615 : /************************************************************************/
10616 :
10617 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10618 : *
10619 : * This is the same as the C function GDALExtendedDataTypeCreate()
10620 : *
10621 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10622 : * GDT_TypeCount
10623 : */
10624 37400 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10625 : {
10626 37400 : return GDALExtendedDataType(eType);
10627 : }
10628 :
10629 : /************************************************************************/
10630 : /* Create() */
10631 : /************************************************************************/
10632 :
10633 : /** Return a new GDALExtendedDataType from a raster attribute table.
10634 : *
10635 : * @param osName Type name
10636 : * @param eBaseType Base integer data type.
10637 : * @param poRAT Raster attribute table. Must not be NULL.
10638 : * @since 3.12
10639 : */
10640 : GDALExtendedDataType
10641 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10642 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10643 : {
10644 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10645 : }
10646 :
10647 : /************************************************************************/
10648 : /* Create() */
10649 : /************************************************************************/
10650 :
10651 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10652 : *
10653 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10654 : *
10655 : * @param osName Type name.
10656 : * @param nTotalSize Total size of the type in bytes.
10657 : * Should be large enough to store all components.
10658 : * @param components Components of the compound type.
10659 : */
10660 862 : GDALExtendedDataType GDALExtendedDataType::Create(
10661 : const std::string &osName, size_t nTotalSize,
10662 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10663 : {
10664 862 : size_t nLastOffset = 0;
10665 : // Some arbitrary threshold to avoid potential integer overflows
10666 862 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10667 : {
10668 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10669 2 : return GDALExtendedDataType(GDT_Unknown);
10670 : }
10671 4088 : for (const auto &comp : components)
10672 : {
10673 : // Check alignment too ?
10674 3229 : if (comp->GetOffset() < nLastOffset)
10675 : {
10676 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10677 1 : return GDALExtendedDataType(GDT_Unknown);
10678 : }
10679 3228 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10680 : }
10681 859 : if (nTotalSize < nLastOffset)
10682 : {
10683 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10684 1 : return GDALExtendedDataType(GDT_Unknown);
10685 : }
10686 858 : if (nTotalSize == 0 || components.empty())
10687 : {
10688 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10689 3 : return GDALExtendedDataType(GDT_Unknown);
10690 : }
10691 855 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10692 : }
10693 :
10694 : /************************************************************************/
10695 : /* Create() */
10696 : /************************************************************************/
10697 :
10698 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10699 : *
10700 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10701 : *
10702 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10703 : * unknown/unlimited
10704 : * @param eSubType Subtype.
10705 : */
10706 : GDALExtendedDataType
10707 9766 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10708 : GDALExtendedDataTypeSubType eSubType)
10709 : {
10710 9766 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10711 : }
10712 :
10713 : /************************************************************************/
10714 : /* operator==() */
10715 : /************************************************************************/
10716 :
10717 : /** Equality operator.
10718 : *
10719 : * This is the same as the C function GDALExtendedDataTypeEquals().
10720 : */
10721 2759 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10722 : {
10723 2732 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10724 5491 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10725 : {
10726 258 : return false;
10727 : }
10728 2501 : if (m_eClass == GEDTC_NUMERIC)
10729 : {
10730 874 : return m_eNumericDT == other.m_eNumericDT;
10731 : }
10732 1627 : if (m_eClass == GEDTC_STRING)
10733 : {
10734 1409 : return true;
10735 : }
10736 218 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10737 218 : if (m_aoComponents.size() != other.m_aoComponents.size())
10738 : {
10739 2 : return false;
10740 : }
10741 1139 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10742 : {
10743 923 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10744 : {
10745 0 : return false;
10746 : }
10747 : }
10748 216 : return true;
10749 : }
10750 :
10751 : /************************************************************************/
10752 : /* CanConvertTo() */
10753 : /************************************************************************/
10754 :
10755 : /** Return whether this data type can be converted to the other one.
10756 : *
10757 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10758 : *
10759 : * @param other Target data type for the conversion being considered.
10760 : */
10761 8829 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10762 : {
10763 8829 : if (m_eClass == GEDTC_NUMERIC)
10764 : {
10765 6052 : if (m_eNumericDT == GDT_Unknown)
10766 0 : return false;
10767 6052 : if (other.m_eClass == GEDTC_NUMERIC &&
10768 5943 : other.m_eNumericDT == GDT_Unknown)
10769 0 : return false;
10770 6161 : return other.m_eClass == GEDTC_NUMERIC ||
10771 6161 : other.m_eClass == GEDTC_STRING;
10772 : }
10773 2777 : if (m_eClass == GEDTC_STRING)
10774 : {
10775 2591 : return other.m_eClass == m_eClass;
10776 : }
10777 186 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10778 186 : if (other.m_eClass != GEDTC_COMPOUND)
10779 0 : return false;
10780 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10781 372 : srcComponents;
10782 929 : for (const auto &srcComp : m_aoComponents)
10783 : {
10784 743 : srcComponents[srcComp->GetName()] = &srcComp;
10785 : }
10786 513 : for (const auto &dstComp : other.m_aoComponents)
10787 : {
10788 328 : auto oIter = srcComponents.find(dstComp->GetName());
10789 328 : if (oIter == srcComponents.end())
10790 1 : return false;
10791 327 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10792 0 : return false;
10793 : }
10794 185 : return true;
10795 : }
10796 :
10797 : /************************************************************************/
10798 : /* NeedsFreeDynamicMemory() */
10799 : /************************************************************************/
10800 :
10801 : /** Return whether the data type holds dynamically allocated memory, that
10802 : * needs to be freed with FreeDynamicMemory().
10803 : *
10804 : */
10805 3769 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10806 : {
10807 3769 : switch (m_eClass)
10808 : {
10809 959 : case GEDTC_STRING:
10810 959 : return true;
10811 :
10812 2700 : case GEDTC_NUMERIC:
10813 2700 : return false;
10814 :
10815 110 : case GEDTC_COMPOUND:
10816 : {
10817 223 : for (const auto &comp : m_aoComponents)
10818 : {
10819 209 : if (comp->GetType().NeedsFreeDynamicMemory())
10820 96 : return true;
10821 : }
10822 : }
10823 : }
10824 14 : return false;
10825 : }
10826 :
10827 : /************************************************************************/
10828 : /* FreeDynamicMemory() */
10829 : /************************************************************************/
10830 :
10831 : /** Release the dynamic memory (strings typically) from a raw value.
10832 : *
10833 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10834 : *
10835 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10836 : */
10837 3902 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10838 : {
10839 3902 : switch (m_eClass)
10840 : {
10841 2823 : case GEDTC_STRING:
10842 : {
10843 : char *pszStr;
10844 2823 : memcpy(&pszStr, pBuffer, sizeof(char *));
10845 2823 : if (pszStr)
10846 : {
10847 2265 : VSIFree(pszStr);
10848 : }
10849 2823 : break;
10850 : }
10851 :
10852 903 : case GEDTC_NUMERIC:
10853 : {
10854 903 : break;
10855 : }
10856 :
10857 176 : case GEDTC_COMPOUND:
10858 : {
10859 176 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10860 938 : for (const auto &comp : m_aoComponents)
10861 : {
10862 1524 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10863 762 : comp->GetOffset());
10864 : }
10865 176 : break;
10866 : }
10867 : }
10868 3902 : }
10869 :
10870 : /************************************************************************/
10871 : /* ~GDALEDTComponent() */
10872 : /************************************************************************/
10873 :
10874 : GDALEDTComponent::~GDALEDTComponent() = default;
10875 :
10876 : /************************************************************************/
10877 : /* GDALEDTComponent() */
10878 : /************************************************************************/
10879 :
10880 : /** constructor of a GDALEDTComponent
10881 : *
10882 : * This is the same as the C function GDALEDTComponendCreate()
10883 : *
10884 : * @param name Component name
10885 : * @param offset Offset in byte of the component in the compound data type.
10886 : * In case of nesting of compound data type, this should be
10887 : * the offset to the immediate belonging data type, not to the
10888 : * higher level one.
10889 : * @param type Component data type.
10890 : */
10891 3220 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10892 3220 : const GDALExtendedDataType &type)
10893 3220 : : m_osName(name), m_nOffset(offset), m_oType(type)
10894 : {
10895 3220 : }
10896 :
10897 : /************************************************************************/
10898 : /* GDALEDTComponent() */
10899 : /************************************************************************/
10900 :
10901 : /** Copy constructor. */
10902 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10903 :
10904 : /************************************************************************/
10905 : /* operator==() */
10906 : /************************************************************************/
10907 :
10908 : /** Equality operator.
10909 : */
10910 923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10911 : {
10912 1846 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10913 1846 : m_oType == other.m_oType;
10914 : }
10915 :
10916 : /************************************************************************/
10917 : /* ~GDALDimension() */
10918 : /************************************************************************/
10919 :
10920 : GDALDimension::~GDALDimension() = default;
10921 :
10922 : /************************************************************************/
10923 : /* GDALDimension() */
10924 : /************************************************************************/
10925 :
10926 : //! @cond Doxygen_Suppress
10927 : /** Constructor.
10928 : *
10929 : * @param osParentName Parent name
10930 : * @param osName name
10931 : * @param osType type. See GetType().
10932 : * @param osDirection direction. See GetDirection().
10933 : * @param nSize size.
10934 : */
10935 8530 : GDALDimension::GDALDimension(const std::string &osParentName,
10936 : const std::string &osName,
10937 : const std::string &osType,
10938 8530 : const std::string &osDirection, GUInt64 nSize)
10939 : : m_osName(osName),
10940 : m_osFullName(
10941 8530 : !osParentName.empty()
10942 12568 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10943 : : osName),
10944 29628 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10945 : {
10946 8530 : }
10947 :
10948 : //! @endcond
10949 :
10950 : /************************************************************************/
10951 : /* GetIndexingVariable() */
10952 : /************************************************************************/
10953 :
10954 : /** Return the variable that is used to index the dimension (if there is one).
10955 : *
10956 : * This is the array, typically one-dimensional, describing the values taken
10957 : * by the dimension.
10958 : */
10959 73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10960 : {
10961 73 : return nullptr;
10962 : }
10963 :
10964 : /************************************************************************/
10965 : /* SetIndexingVariable() */
10966 : /************************************************************************/
10967 :
10968 : /** Set the variable that is used to index the dimension.
10969 : *
10970 : * This is the array, typically one-dimensional, describing the values taken
10971 : * by the dimension.
10972 : *
10973 : * Optionally implemented by drivers.
10974 : *
10975 : * Drivers known to implement it: MEM.
10976 : *
10977 : * @param poArray Variable to use to index the dimension.
10978 : * @return true in case of success.
10979 : */
10980 3 : bool GDALDimension::SetIndexingVariable(
10981 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10982 : {
10983 3 : CPLError(CE_Failure, CPLE_NotSupported,
10984 : "SetIndexingVariable() not implemented");
10985 3 : return false;
10986 : }
10987 :
10988 : /************************************************************************/
10989 : /* Rename() */
10990 : /************************************************************************/
10991 :
10992 : /** Rename the dimension.
10993 : *
10994 : * This is not implemented by all drivers.
10995 : *
10996 : * Drivers known to implement it: MEM, netCDF, ZARR.
10997 : *
10998 : * This is the same as the C function GDALDimensionRename().
10999 : *
11000 : * @param osNewName New name.
11001 : *
11002 : * @return true in case of success
11003 : * @since GDAL 3.8
11004 : */
11005 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11006 : {
11007 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11008 0 : return false;
11009 : }
11010 :
11011 : /************************************************************************/
11012 : /* BaseRename() */
11013 : /************************************************************************/
11014 :
11015 : //! @cond Doxygen_Suppress
11016 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11017 : {
11018 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11019 8 : m_osFullName += osNewName;
11020 8 : m_osName = osNewName;
11021 8 : }
11022 :
11023 : //! @endcond
11024 :
11025 : //! @cond Doxygen_Suppress
11026 : /************************************************************************/
11027 : /* ParentRenamed() */
11028 : /************************************************************************/
11029 :
11030 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11031 : {
11032 8 : m_osFullName = osNewParentFullName;
11033 8 : m_osFullName += "/";
11034 8 : m_osFullName += m_osName;
11035 8 : }
11036 :
11037 : //! @endcond
11038 :
11039 : //! @cond Doxygen_Suppress
11040 : /************************************************************************/
11041 : /* ParentDeleted() */
11042 : /************************************************************************/
11043 :
11044 4 : void GDALDimension::ParentDeleted()
11045 : {
11046 4 : }
11047 :
11048 : //! @endcond
11049 :
11050 : /************************************************************************/
11051 : /************************************************************************/
11052 : /************************************************************************/
11053 : /* C API */
11054 : /************************************************************************/
11055 : /************************************************************************/
11056 : /************************************************************************/
11057 :
11058 : /************************************************************************/
11059 : /* GDALExtendedDataTypeCreate() */
11060 : /************************************************************************/
11061 :
11062 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11063 : *
11064 : * This is the same as the C++ method GDALExtendedDataType::Create()
11065 : *
11066 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11067 : *
11068 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11069 : * GDT_TypeCount
11070 : *
11071 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11072 : */
11073 2012 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11074 : {
11075 2012 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11076 : {
11077 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11078 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11079 0 : return nullptr;
11080 : }
11081 : return new GDALExtendedDataTypeHS(
11082 2012 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11083 : }
11084 :
11085 : /************************************************************************/
11086 : /* GDALExtendedDataTypeCreateString() */
11087 : /************************************************************************/
11088 :
11089 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11090 : *
11091 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11092 : *
11093 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11094 : *
11095 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11096 : */
11097 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11098 : {
11099 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11100 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11101 : }
11102 :
11103 : /************************************************************************/
11104 : /* GDALExtendedDataTypeCreateStringEx() */
11105 : /************************************************************************/
11106 :
11107 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11108 : *
11109 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11110 : *
11111 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11112 : *
11113 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11114 : * @since GDAL 3.4
11115 : */
11116 : GDALExtendedDataTypeH
11117 195 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11118 : GDALExtendedDataTypeSubType eSubType)
11119 : {
11120 195 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11121 195 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11122 : }
11123 :
11124 : /************************************************************************/
11125 : /* GDALExtendedDataTypeCreateCompound() */
11126 : /************************************************************************/
11127 :
11128 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11129 : *
11130 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11131 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11132 : *
11133 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11134 : *
11135 : * @param pszName Type name.
11136 : * @param nTotalSize Total size of the type in bytes.
11137 : * Should be large enough to store all components.
11138 : * @param nComponents Number of components in comps array.
11139 : * @param comps Components.
11140 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11141 : */
11142 : GDALExtendedDataTypeH
11143 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11144 : size_t nComponents,
11145 : const GDALEDTComponentH *comps)
11146 : {
11147 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11148 54 : for (size_t i = 0; i < nComponents; i++)
11149 : {
11150 : compsCpp.emplace_back(
11151 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11152 : }
11153 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11154 66 : std::move(compsCpp));
11155 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11156 6 : return nullptr;
11157 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11158 : }
11159 :
11160 : /************************************************************************/
11161 : /* GDALExtendedDataTypeRelease() */
11162 : /************************************************************************/
11163 :
11164 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11165 : *
11166 : * Note: when applied on a object coming from a driver, this does not
11167 : * destroy the object in the file, database, etc...
11168 : */
11169 6635 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11170 : {
11171 6635 : delete hEDT;
11172 6635 : }
11173 :
11174 : /************************************************************************/
11175 : /* GDALExtendedDataTypeGetName() */
11176 : /************************************************************************/
11177 :
11178 : /** Return type name.
11179 : *
11180 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11181 : */
11182 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11183 : {
11184 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11185 8 : return hEDT->m_poImpl->GetName().c_str();
11186 : }
11187 :
11188 : /************************************************************************/
11189 : /* GDALExtendedDataTypeGetClass() */
11190 : /************************************************************************/
11191 :
11192 : /** Return type class.
11193 : *
11194 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11195 : */
11196 : GDALExtendedDataTypeClass
11197 9183 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11198 : {
11199 9183 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11200 9183 : return hEDT->m_poImpl->GetClass();
11201 : }
11202 :
11203 : /************************************************************************/
11204 : /* GDALExtendedDataTypeGetNumericDataType() */
11205 : /************************************************************************/
11206 :
11207 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11208 : *
11209 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11210 : */
11211 2069 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11212 : {
11213 2069 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11214 2069 : return hEDT->m_poImpl->GetNumericDataType();
11215 : }
11216 :
11217 : /************************************************************************/
11218 : /* GDALExtendedDataTypeGetSize() */
11219 : /************************************************************************/
11220 :
11221 : /** Return data type size in bytes.
11222 : *
11223 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11224 : */
11225 2539 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11226 : {
11227 2539 : VALIDATE_POINTER1(hEDT, __func__, 0);
11228 2539 : return hEDT->m_poImpl->GetSize();
11229 : }
11230 :
11231 : /************************************************************************/
11232 : /* GDALExtendedDataTypeGetMaxStringLength() */
11233 : /************************************************************************/
11234 :
11235 : /** Return the maximum length of a string in bytes.
11236 : *
11237 : * 0 indicates unknown/unlimited string.
11238 : *
11239 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11240 : */
11241 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11242 : {
11243 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11244 3 : return hEDT->m_poImpl->GetMaxStringLength();
11245 : }
11246 :
11247 : /************************************************************************/
11248 : /* GDALExtendedDataTypeCanConvertTo() */
11249 : /************************************************************************/
11250 :
11251 : /** Return whether this data type can be converted to the other one.
11252 : *
11253 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11254 : *
11255 : * @param hSourceEDT Source data type for the conversion being considered.
11256 : * @param hTargetEDT Target data type for the conversion being considered.
11257 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11258 : */
11259 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11260 : GDALExtendedDataTypeH hTargetEDT)
11261 : {
11262 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11263 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11264 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11265 : }
11266 :
11267 : /************************************************************************/
11268 : /* GDALExtendedDataTypeEquals() */
11269 : /************************************************************************/
11270 :
11271 : /** Return whether this data type is equal to another one.
11272 : *
11273 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11274 : *
11275 : * @param hFirstEDT First data type.
11276 : * @param hSecondEDT Second data type.
11277 : * @return TRUE if they are equal. FALSE otherwise.
11278 : */
11279 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11280 : GDALExtendedDataTypeH hSecondEDT)
11281 : {
11282 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11283 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11284 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11285 : }
11286 :
11287 : /************************************************************************/
11288 : /* GDALExtendedDataTypeGetSubType() */
11289 : /************************************************************************/
11290 :
11291 : /** Return the subtype of a type.
11292 : *
11293 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11294 : *
11295 : * @param hEDT Data type.
11296 : * @return subtype.
11297 : * @since 3.4
11298 : */
11299 : GDALExtendedDataTypeSubType
11300 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11301 : {
11302 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11303 104 : return hEDT->m_poImpl->GetSubType();
11304 : }
11305 :
11306 : /************************************************************************/
11307 : /* GDALExtendedDataTypeGetRAT() */
11308 : /************************************************************************/
11309 :
11310 : /** Return associated raster attribute table, when there is one.
11311 : *
11312 : * * For the netCDF driver, the RAT will capture enumerated types, with
11313 : * a "value" column with an integer value and a "name" column with the
11314 : * associated name.
11315 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11316 : *
11317 : * @param hEDT Data type.
11318 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11319 : * @since 3.12
11320 : */
11321 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11322 : {
11323 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11324 1 : return GDALRasterAttributeTable::ToHandle(
11325 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11326 : }
11327 :
11328 : /************************************************************************/
11329 : /* GDALExtendedDataTypeGetComponents() */
11330 : /************************************************************************/
11331 :
11332 : /** Return the components of the data type (only valid when GetClass() ==
11333 : * GEDTC_COMPOUND)
11334 : *
11335 : * The returned array and its content must be freed with
11336 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11337 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11338 : * individual array members).
11339 : *
11340 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11341 : *
11342 : * @param hEDT Data type
11343 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11344 : * @return an array of *pnCount components.
11345 : */
11346 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11347 : size_t *pnCount)
11348 : {
11349 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11350 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11351 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11352 : auto ret = static_cast<GDALEDTComponentH *>(
11353 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11354 131 : for (size_t i = 0; i < components.size(); i++)
11355 : {
11356 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11357 : }
11358 44 : *pnCount = components.size();
11359 44 : return ret;
11360 : }
11361 :
11362 : /************************************************************************/
11363 : /* GDALExtendedDataTypeFreeComponents() */
11364 : /************************************************************************/
11365 :
11366 : /** Free the return of GDALExtendedDataTypeGetComponents().
11367 : *
11368 : * @param components return value of GDALExtendedDataTypeGetComponents()
11369 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11370 : */
11371 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11372 : size_t nCount)
11373 : {
11374 131 : for (size_t i = 0; i < nCount; i++)
11375 : {
11376 87 : delete components[i];
11377 : }
11378 44 : CPLFree(components);
11379 44 : }
11380 :
11381 : /************************************************************************/
11382 : /* GDALEDTComponentCreate() */
11383 : /************************************************************************/
11384 :
11385 : /** Create a new GDALEDTComponent.
11386 : *
11387 : * The returned value must be freed with GDALEDTComponentRelease().
11388 : *
11389 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11390 : */
11391 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11392 : GDALExtendedDataTypeH hType)
11393 : {
11394 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11395 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11396 : return new GDALEDTComponentHS(
11397 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11398 : }
11399 :
11400 : /************************************************************************/
11401 : /* GDALEDTComponentRelease() */
11402 : /************************************************************************/
11403 :
11404 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11405 : *
11406 : * Note: when applied on a object coming from a driver, this does not
11407 : * destroy the object in the file, database, etc...
11408 : */
11409 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11410 : {
11411 61 : delete hComp;
11412 61 : }
11413 :
11414 : /************************************************************************/
11415 : /* GDALEDTComponentGetName() */
11416 : /************************************************************************/
11417 :
11418 : /** Return the name.
11419 : *
11420 : * The returned pointer is valid until hComp is released.
11421 : *
11422 : * This is the same as the C++ method GDALEDTComponent::GetName().
11423 : */
11424 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11425 : {
11426 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11427 33 : return hComp->m_poImpl->GetName().c_str();
11428 : }
11429 :
11430 : /************************************************************************/
11431 : /* GDALEDTComponentGetOffset() */
11432 : /************************************************************************/
11433 :
11434 : /** Return the offset (in bytes) of the component in the compound data type.
11435 : *
11436 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11437 : */
11438 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11439 : {
11440 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11441 31 : return hComp->m_poImpl->GetOffset();
11442 : }
11443 :
11444 : /************************************************************************/
11445 : /* GDALEDTComponentGetType() */
11446 : /************************************************************************/
11447 :
11448 : /** Return the data type of the component.
11449 : *
11450 : * This is the same as the C++ method GDALEDTComponent::GetType().
11451 : */
11452 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11453 : {
11454 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11455 : return new GDALExtendedDataTypeHS(
11456 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11457 : }
11458 :
11459 : /************************************************************************/
11460 : /* GDALGroupRelease() */
11461 : /************************************************************************/
11462 :
11463 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11464 : *
11465 : * Note: when applied on a object coming from a driver, this does not
11466 : * destroy the object in the file, database, etc...
11467 : */
11468 1448 : void GDALGroupRelease(GDALGroupH hGroup)
11469 : {
11470 1448 : delete hGroup;
11471 1448 : }
11472 :
11473 : /************************************************************************/
11474 : /* GDALGroupGetName() */
11475 : /************************************************************************/
11476 :
11477 : /** Return the name of the group.
11478 : *
11479 : * The returned pointer is valid until hGroup is released.
11480 : *
11481 : * This is the same as the C++ method GDALGroup::GetName().
11482 : */
11483 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11484 : {
11485 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11486 95 : return hGroup->m_poImpl->GetName().c_str();
11487 : }
11488 :
11489 : /************************************************************************/
11490 : /* GDALGroupGetFullName() */
11491 : /************************************************************************/
11492 :
11493 : /** Return the full name of the group.
11494 : *
11495 : * The returned pointer is valid until hGroup is released.
11496 : *
11497 : * This is the same as the C++ method GDALGroup::GetFullName().
11498 : */
11499 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11500 : {
11501 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11502 47 : return hGroup->m_poImpl->GetFullName().c_str();
11503 : }
11504 :
11505 : /************************************************************************/
11506 : /* GDALGroupGetMDArrayNames() */
11507 : /************************************************************************/
11508 :
11509 : /** Return the list of multidimensional array names contained in this group.
11510 : *
11511 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11512 : *
11513 : * @return the array names, to be freed with CSLDestroy()
11514 : */
11515 329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11516 : {
11517 329 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11518 658 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11519 658 : CPLStringList res;
11520 829 : for (const auto &name : names)
11521 : {
11522 500 : res.AddString(name.c_str());
11523 : }
11524 329 : return res.StealList();
11525 : }
11526 :
11527 : /************************************************************************/
11528 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11529 : /************************************************************************/
11530 :
11531 : /** Return the list of multidimensional array full names contained in this
11532 : * group and its subgroups.
11533 : *
11534 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11535 : *
11536 : * @return the array names, to be freed with CSLDestroy()
11537 : *
11538 : * @since 3.11
11539 : */
11540 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11541 : CSLConstList papszGroupOptions,
11542 : CSLConstList papszArrayOptions)
11543 : {
11544 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11545 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11546 2 : papszGroupOptions, papszArrayOptions);
11547 2 : CPLStringList res;
11548 5 : for (const auto &name : names)
11549 : {
11550 4 : res.AddString(name.c_str());
11551 : }
11552 1 : return res.StealList();
11553 : }
11554 :
11555 : /************************************************************************/
11556 : /* GDALGroupOpenMDArray() */
11557 : /************************************************************************/
11558 :
11559 : /** Open and return a multidimensional array.
11560 : *
11561 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11562 : *
11563 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11564 : */
11565 806 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11566 : CSLConstList papszOptions)
11567 : {
11568 806 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11569 806 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11570 2418 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11571 2418 : papszOptions);
11572 806 : if (!array)
11573 30 : return nullptr;
11574 776 : return new GDALMDArrayHS(array);
11575 : }
11576 :
11577 : /************************************************************************/
11578 : /* GDALGroupOpenMDArrayFromFullname() */
11579 : /************************************************************************/
11580 :
11581 : /** Open and return a multidimensional array from its fully qualified name.
11582 : *
11583 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11584 : *
11585 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11586 : *
11587 : * @since GDAL 3.2
11588 : */
11589 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11590 : const char *pszFullname,
11591 : CSLConstList papszOptions)
11592 : {
11593 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11594 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11595 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11596 48 : std::string(pszFullname), papszOptions);
11597 16 : if (!array)
11598 2 : return nullptr;
11599 14 : return new GDALMDArrayHS(array);
11600 : }
11601 :
11602 : /************************************************************************/
11603 : /* GDALGroupResolveMDArray() */
11604 : /************************************************************************/
11605 :
11606 : /** Locate an array in a group and its subgroups by name.
11607 : *
11608 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11609 : * @since GDAL 3.2
11610 : */
11611 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11612 : const char *pszStartingPoint,
11613 : CSLConstList papszOptions)
11614 : {
11615 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11616 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11617 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11618 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11619 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11620 19 : if (!array)
11621 2 : return nullptr;
11622 17 : return new GDALMDArrayHS(array);
11623 : }
11624 :
11625 : /************************************************************************/
11626 : /* GDALGroupGetGroupNames() */
11627 : /************************************************************************/
11628 :
11629 : /** Return the list of sub-groups contained in this group.
11630 : *
11631 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11632 : *
11633 : * @return the group names, to be freed with CSLDestroy()
11634 : */
11635 97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11636 : {
11637 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11638 194 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11639 194 : CPLStringList res;
11640 219 : for (const auto &name : names)
11641 : {
11642 122 : res.AddString(name.c_str());
11643 : }
11644 97 : return res.StealList();
11645 : }
11646 :
11647 : /************************************************************************/
11648 : /* GDALGroupOpenGroup() */
11649 : /************************************************************************/
11650 :
11651 : /** Open and return a sub-group.
11652 : *
11653 : * This is the same as the C++ method GDALGroup::OpenGroup().
11654 : *
11655 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11656 : */
11657 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11658 : CSLConstList papszOptions)
11659 : {
11660 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11661 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11662 : auto subGroup =
11663 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11664 163 : if (!subGroup)
11665 30 : return nullptr;
11666 133 : return new GDALGroupHS(subGroup);
11667 : }
11668 :
11669 : /************************************************************************/
11670 : /* GDALGroupGetVectorLayerNames() */
11671 : /************************************************************************/
11672 :
11673 : /** Return the list of layer names contained in this group.
11674 : *
11675 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11676 : *
11677 : * @return the group names, to be freed with CSLDestroy()
11678 : * @since 3.4
11679 : */
11680 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11681 : CSLConstList papszOptions)
11682 : {
11683 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11684 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11685 16 : CPLStringList res;
11686 18 : for (const auto &name : names)
11687 : {
11688 10 : res.AddString(name.c_str());
11689 : }
11690 8 : return res.StealList();
11691 : }
11692 :
11693 : /************************************************************************/
11694 : /* GDALGroupOpenVectorLayer() */
11695 : /************************************************************************/
11696 :
11697 : /** Open and return a vector layer.
11698 : *
11699 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11700 : *
11701 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11702 : * the returned handled if only valid while the parent GDALDatasetH is kept
11703 : * opened.
11704 : *
11705 : * @return the vector layer, or nullptr.
11706 : * @since 3.4
11707 : */
11708 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11709 : const char *pszVectorLayerName,
11710 : CSLConstList papszOptions)
11711 : {
11712 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11713 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11714 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11715 24 : std::string(pszVectorLayerName), papszOptions));
11716 : }
11717 :
11718 : /************************************************************************/
11719 : /* GDALGroupOpenMDArrayFromFullname() */
11720 : /************************************************************************/
11721 :
11722 : /** Open and return a sub-group from its fully qualified name.
11723 : *
11724 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11725 : *
11726 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11727 : *
11728 : * @since GDAL 3.2
11729 : */
11730 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11731 : const char *pszFullname,
11732 : CSLConstList papszOptions)
11733 : {
11734 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11735 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11736 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11737 9 : std::string(pszFullname), papszOptions);
11738 3 : if (!subGroup)
11739 2 : return nullptr;
11740 1 : return new GDALGroupHS(subGroup);
11741 : }
11742 :
11743 : /************************************************************************/
11744 : /* GDALGroupGetDimensions() */
11745 : /************************************************************************/
11746 :
11747 : /** Return the list of dimensions contained in this group and used by its
11748 : * arrays.
11749 : *
11750 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11751 : * array itself needs to be freed, CPLFree() should be called (and
11752 : * GDALDimensionRelease() on individual array members).
11753 : *
11754 : * This is the same as the C++ method GDALGroup::GetDimensions().
11755 : *
11756 : * @param hGroup Group.
11757 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11758 : * @param papszOptions Driver specific options determining how dimensions
11759 : * should be retrieved. Pass nullptr for default behavior.
11760 : *
11761 : * @return an array of *pnCount dimensions.
11762 : */
11763 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11764 : CSLConstList papszOptions)
11765 : {
11766 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11767 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11768 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11769 : auto ret = static_cast<GDALDimensionH *>(
11770 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11771 230 : for (size_t i = 0; i < dims.size(); i++)
11772 : {
11773 157 : ret[i] = new GDALDimensionHS(dims[i]);
11774 : }
11775 73 : *pnCount = dims.size();
11776 73 : return ret;
11777 : }
11778 :
11779 : /************************************************************************/
11780 : /* GDALGroupGetAttribute() */
11781 : /************************************************************************/
11782 :
11783 : /** Return an attribute by its name.
11784 : *
11785 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11786 : *
11787 : * The returned attribute must be freed with GDALAttributeRelease().
11788 : */
11789 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11790 : {
11791 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11792 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11793 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11794 80 : if (attr)
11795 76 : return new GDALAttributeHS(attr);
11796 4 : return nullptr;
11797 : }
11798 :
11799 : /************************************************************************/
11800 : /* GDALGroupGetAttributes() */
11801 : /************************************************************************/
11802 :
11803 : /** Return the list of attributes contained in this group.
11804 : *
11805 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11806 : * array itself needs to be freed, CPLFree() should be called (and
11807 : * GDALAttributeRelease() on individual array members).
11808 : *
11809 : * This is the same as the C++ method GDALGroup::GetAttributes().
11810 : *
11811 : * @param hGroup Group.
11812 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11813 : * @param papszOptions Driver specific options determining how attributes
11814 : * should be retrieved. Pass nullptr for default behavior.
11815 : *
11816 : * @return an array of *pnCount attributes.
11817 : */
11818 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11819 : CSLConstList papszOptions)
11820 : {
11821 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11822 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11823 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11824 : auto ret = static_cast<GDALAttributeH *>(
11825 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11826 229 : for (size_t i = 0; i < attrs.size(); i++)
11827 : {
11828 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11829 : }
11830 71 : *pnCount = attrs.size();
11831 71 : return ret;
11832 : }
11833 :
11834 : /************************************************************************/
11835 : /* GDALGroupGetStructuralInfo() */
11836 : /************************************************************************/
11837 :
11838 : /** Return structural information on the group.
11839 : *
11840 : * This may be the compression, etc..
11841 : *
11842 : * The return value should not be freed and is valid until GDALGroup is
11843 : * released or this function called again.
11844 : *
11845 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11846 : */
11847 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11848 : {
11849 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11850 4 : return hGroup->m_poImpl->GetStructuralInfo();
11851 : }
11852 :
11853 : /************************************************************************/
11854 : /* GDALGroupGetDataTypeCount() */
11855 : /************************************************************************/
11856 :
11857 : /** Return the number of data types associated with the group
11858 : * (typically enumerations).
11859 : *
11860 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11861 : *
11862 : * @since 3.12
11863 : */
11864 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11865 : {
11866 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11867 4 : return hGroup->m_poImpl->GetDataTypes().size();
11868 : }
11869 :
11870 : /************************************************************************/
11871 : /* GDALGroupGetDataType() */
11872 : /************************************************************************/
11873 :
11874 : /** Return one of the data types associated with the group.
11875 : *
11876 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11877 : *
11878 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11879 : * or nullptr in case of error.
11880 : * @since 3.12
11881 : */
11882 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11883 : {
11884 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11885 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11886 0 : return nullptr;
11887 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11888 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11889 : }
11890 :
11891 : /************************************************************************/
11892 : /* GDALReleaseAttributes() */
11893 : /************************************************************************/
11894 :
11895 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11896 : *
11897 : * @param attributes return pointer of above methods
11898 : * @param nCount *pnCount value returned by above methods
11899 : */
11900 129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11901 : {
11902 416 : for (size_t i = 0; i < nCount; i++)
11903 : {
11904 287 : delete attributes[i];
11905 : }
11906 129 : CPLFree(attributes);
11907 129 : }
11908 :
11909 : /************************************************************************/
11910 : /* GDALGroupCreateGroup() */
11911 : /************************************************************************/
11912 :
11913 : /** Create a sub-group within a group.
11914 : *
11915 : * This is the same as the C++ method GDALGroup::CreateGroup().
11916 : *
11917 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11918 : */
11919 179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11920 : CSLConstList papszOptions)
11921 : {
11922 179 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11923 179 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11924 537 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11925 537 : papszOptions);
11926 179 : if (!ret)
11927 49 : return nullptr;
11928 130 : return new GDALGroupHS(ret);
11929 : }
11930 :
11931 : /************************************************************************/
11932 : /* GDALGroupDeleteGroup() */
11933 : /************************************************************************/
11934 :
11935 : /** Delete a sub-group from a group.
11936 : *
11937 : * After this call, if a previously obtained instance of the deleted object
11938 : * is still alive, no method other than for freeing it should be invoked.
11939 : *
11940 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11941 : *
11942 : * @return true in case of success.
11943 : * @since GDAL 3.8
11944 : */
11945 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11946 : CSLConstList papszOptions)
11947 : {
11948 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11949 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11950 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11951 20 : papszOptions);
11952 : }
11953 :
11954 : /************************************************************************/
11955 : /* GDALGroupCreateDimension() */
11956 : /************************************************************************/
11957 :
11958 : /** Create a dimension within a group.
11959 : *
11960 : * This is the same as the C++ method GDALGroup::CreateDimension().
11961 : *
11962 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11963 : */
11964 669 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11965 : const char *pszType,
11966 : const char *pszDirection, GUInt64 nSize,
11967 : CSLConstList papszOptions)
11968 : {
11969 669 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11970 669 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11971 669 : auto ret = hGroup->m_poImpl->CreateDimension(
11972 1338 : std::string(pszName), std::string(pszType ? pszType : ""),
11973 2676 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11974 669 : if (!ret)
11975 9 : return nullptr;
11976 660 : return new GDALDimensionHS(ret);
11977 : }
11978 :
11979 : /************************************************************************/
11980 : /* GDALGroupCreateMDArray() */
11981 : /************************************************************************/
11982 :
11983 : /** Create a multidimensional array within a group.
11984 : *
11985 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11986 : *
11987 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11988 : */
11989 612 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11990 : size_t nDimensions,
11991 : GDALDimensionH *pahDimensions,
11992 : GDALExtendedDataTypeH hEDT,
11993 : CSLConstList papszOptions)
11994 : {
11995 612 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11996 612 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11997 612 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11998 1224 : std::vector<std::shared_ptr<GDALDimension>> dims;
11999 612 : dims.reserve(nDimensions);
12000 1443 : for (size_t i = 0; i < nDimensions; i++)
12001 831 : dims.push_back(pahDimensions[i]->m_poImpl);
12002 1836 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12003 1836 : *(hEDT->m_poImpl), papszOptions);
12004 612 : if (!ret)
12005 65 : return nullptr;
12006 547 : return new GDALMDArrayHS(ret);
12007 : }
12008 :
12009 : /************************************************************************/
12010 : /* GDALGroupDeleteMDArray() */
12011 : /************************************************************************/
12012 :
12013 : /** Delete an array from a group.
12014 : *
12015 : * After this call, if a previously obtained instance of the deleted object
12016 : * is still alive, no method other than for freeing it should be invoked.
12017 : *
12018 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12019 : *
12020 : * @return true in case of success.
12021 : * @since GDAL 3.8
12022 : */
12023 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12024 : CSLConstList papszOptions)
12025 : {
12026 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12027 20 : VALIDATE_POINTER1(pszName, __func__, false);
12028 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12029 : }
12030 :
12031 : /************************************************************************/
12032 : /* GDALGroupCreateAttribute() */
12033 : /************************************************************************/
12034 :
12035 : /** Create a attribute within a group.
12036 : *
12037 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12038 : *
12039 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12040 : */
12041 125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12042 : size_t nDimensions,
12043 : const GUInt64 *panDimensions,
12044 : GDALExtendedDataTypeH hEDT,
12045 : CSLConstList papszOptions)
12046 : {
12047 125 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12048 125 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12049 250 : std::vector<GUInt64> dims;
12050 125 : dims.reserve(nDimensions);
12051 175 : for (size_t i = 0; i < nDimensions; i++)
12052 50 : dims.push_back(panDimensions[i]);
12053 125 : auto ret = hGroup->m_poImpl->CreateAttribute(
12054 375 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12055 125 : if (!ret)
12056 14 : return nullptr;
12057 111 : return new GDALAttributeHS(ret);
12058 : }
12059 :
12060 : /************************************************************************/
12061 : /* GDALGroupDeleteAttribute() */
12062 : /************************************************************************/
12063 :
12064 : /** Delete an attribute from a group.
12065 : *
12066 : * After this call, if a previously obtained instance of the deleted object
12067 : * is still alive, no method other than for freeing it should be invoked.
12068 : *
12069 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12070 : *
12071 : * @return true in case of success.
12072 : * @since GDAL 3.8
12073 : */
12074 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12075 : CSLConstList papszOptions)
12076 : {
12077 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12078 25 : VALIDATE_POINTER1(pszName, __func__, false);
12079 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12080 25 : papszOptions);
12081 : }
12082 :
12083 : /************************************************************************/
12084 : /* GDALGroupRename() */
12085 : /************************************************************************/
12086 :
12087 : /** Rename the group.
12088 : *
12089 : * This is not implemented by all drivers.
12090 : *
12091 : * Drivers known to implement it: MEM, netCDF.
12092 : *
12093 : * This is the same as the C++ method GDALGroup::Rename()
12094 : *
12095 : * @return true in case of success
12096 : * @since GDAL 3.8
12097 : */
12098 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12099 : {
12100 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12101 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12102 45 : return hGroup->m_poImpl->Rename(pszNewName);
12103 : }
12104 :
12105 : /************************************************************************/
12106 : /* GDALGroupSubsetDimensionFromSelection() */
12107 : /************************************************************************/
12108 :
12109 : /** Return a virtual group whose one dimension has been subset according to a
12110 : * selection.
12111 : *
12112 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12113 : *
12114 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12115 : */
12116 : GDALGroupH
12117 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12118 : const char *pszSelection,
12119 : CPL_UNUSED CSLConstList papszOptions)
12120 : {
12121 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12122 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12123 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12124 42 : std::string(pszSelection));
12125 14 : if (!hNewGroup)
12126 8 : return nullptr;
12127 6 : return new GDALGroupHS(hNewGroup);
12128 : }
12129 :
12130 : /************************************************************************/
12131 : /* GDALMDArrayRelease() */
12132 : /************************************************************************/
12133 :
12134 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12135 : *
12136 : * Note: when applied on a object coming from a driver, this does not
12137 : * destroy the object in the file, database, etc...
12138 : */
12139 2020 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12140 : {
12141 2020 : delete hMDArray;
12142 2020 : }
12143 :
12144 : /************************************************************************/
12145 : /* GDALMDArrayGetName() */
12146 : /************************************************************************/
12147 :
12148 : /** Return array name.
12149 : *
12150 : * This is the same as the C++ method GDALMDArray::GetName()
12151 : */
12152 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12153 : {
12154 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12155 83 : return hArray->m_poImpl->GetName().c_str();
12156 : }
12157 :
12158 : /************************************************************************/
12159 : /* GDALMDArrayGetFullName() */
12160 : /************************************************************************/
12161 :
12162 : /** Return array full name.
12163 : *
12164 : * This is the same as the C++ method GDALMDArray::GetFullName()
12165 : */
12166 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12167 : {
12168 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12169 50 : return hArray->m_poImpl->GetFullName().c_str();
12170 : }
12171 :
12172 : /************************************************************************/
12173 : /* GDALMDArrayGetName() */
12174 : /************************************************************************/
12175 :
12176 : /** Return the total number of values in the array.
12177 : *
12178 : * This is the same as the C++ method
12179 : * GDALAbstractMDArray::GetTotalElementsCount()
12180 : */
12181 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12182 : {
12183 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12184 6 : return hArray->m_poImpl->GetTotalElementsCount();
12185 : }
12186 :
12187 : /************************************************************************/
12188 : /* GDALMDArrayGetDimensionCount() */
12189 : /************************************************************************/
12190 :
12191 : /** Return the number of dimensions.
12192 : *
12193 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12194 : */
12195 10371 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12196 : {
12197 10371 : VALIDATE_POINTER1(hArray, __func__, 0);
12198 10371 : return hArray->m_poImpl->GetDimensionCount();
12199 : }
12200 :
12201 : /************************************************************************/
12202 : /* GDALMDArrayGetDimensions() */
12203 : /************************************************************************/
12204 :
12205 : /** Return the dimensions of the array
12206 : *
12207 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12208 : * array itself needs to be freed, CPLFree() should be called (and
12209 : * GDALDimensionRelease() on individual array members).
12210 : *
12211 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12212 : *
12213 : * @param hArray Array.
12214 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12215 : *
12216 : * @return an array of *pnCount dimensions.
12217 : */
12218 2300 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12219 : {
12220 2300 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12221 2300 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12222 2300 : const auto &dims(hArray->m_poImpl->GetDimensions());
12223 : auto ret = static_cast<GDALDimensionH *>(
12224 2300 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12225 6467 : for (size_t i = 0; i < dims.size(); i++)
12226 : {
12227 4167 : ret[i] = new GDALDimensionHS(dims[i]);
12228 : }
12229 2300 : *pnCount = dims.size();
12230 2300 : return ret;
12231 : }
12232 :
12233 : /************************************************************************/
12234 : /* GDALReleaseDimensions() */
12235 : /************************************************************************/
12236 :
12237 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12238 : *
12239 : * @param dims return pointer of above methods
12240 : * @param nCount *pnCount value returned by above methods
12241 : */
12242 2373 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12243 : {
12244 6697 : for (size_t i = 0; i < nCount; i++)
12245 : {
12246 4324 : delete dims[i];
12247 : }
12248 2373 : CPLFree(dims);
12249 2373 : }
12250 :
12251 : /************************************************************************/
12252 : /* GDALMDArrayGetDataType() */
12253 : /************************************************************************/
12254 :
12255 : /** Return the data type
12256 : *
12257 : * The return must be freed with GDALExtendedDataTypeRelease().
12258 : */
12259 3910 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12260 : {
12261 3910 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12262 : return new GDALExtendedDataTypeHS(
12263 3910 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12264 : }
12265 :
12266 : /************************************************************************/
12267 : /* GDALMDArrayRead() */
12268 : /************************************************************************/
12269 :
12270 : /** Read part or totality of a multidimensional array.
12271 : *
12272 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12273 : *
12274 : * @return TRUE in case of success.
12275 : */
12276 1956 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12277 : const size_t *count, const GInt64 *arrayStep,
12278 : const GPtrDiff_t *bufferStride,
12279 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12280 : const void *pDstBufferAllocStart,
12281 : size_t nDstBufferAllocSize)
12282 : {
12283 1956 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12284 1956 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12285 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12286 : {
12287 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12288 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12289 : }
12290 1956 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12291 1956 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12292 3912 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12293 1956 : *(bufferDataType->m_poImpl), pDstBuffer,
12294 1956 : pDstBufferAllocStart, nDstBufferAllocSize);
12295 : }
12296 :
12297 : /************************************************************************/
12298 : /* GDALMDArrayWrite() */
12299 : /************************************************************************/
12300 :
12301 : /** Write part or totality of a multidimensional array.
12302 : *
12303 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12304 : *
12305 : * @return TRUE in case of success.
12306 : */
12307 559 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12308 : const size_t *count, const GInt64 *arrayStep,
12309 : const GPtrDiff_t *bufferStride,
12310 : GDALExtendedDataTypeH bufferDataType,
12311 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12312 : size_t nSrcBufferAllocSize)
12313 : {
12314 559 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12315 559 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12316 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12317 : {
12318 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12319 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12320 : }
12321 559 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12322 559 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12323 1118 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12324 559 : bufferStride, *(bufferDataType->m_poImpl),
12325 : pSrcBuffer, pSrcBufferAllocStart,
12326 559 : nSrcBufferAllocSize);
12327 : }
12328 :
12329 : /************************************************************************/
12330 : /* GDALMDArrayAdviseRead() */
12331 : /************************************************************************/
12332 :
12333 : /** Advise driver of upcoming read requests.
12334 : *
12335 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12336 : *
12337 : * @return TRUE in case of success.
12338 : *
12339 : * @since GDAL 3.2
12340 : */
12341 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12342 : const size_t *count)
12343 : {
12344 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12345 : }
12346 :
12347 : /************************************************************************/
12348 : /* GDALMDArrayAdviseReadEx() */
12349 : /************************************************************************/
12350 :
12351 : /** Advise driver of upcoming read requests.
12352 : *
12353 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12354 : *
12355 : * @return TRUE in case of success.
12356 : *
12357 : * @since GDAL 3.4
12358 : */
12359 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12360 : const size_t *count, CSLConstList papszOptions)
12361 : {
12362 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12363 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12364 : }
12365 :
12366 : /************************************************************************/
12367 : /* GDALMDArrayGetAttribute() */
12368 : /************************************************************************/
12369 :
12370 : /** Return an attribute by its name.
12371 : *
12372 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12373 : *
12374 : * The returned attribute must be freed with GDALAttributeRelease().
12375 : */
12376 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12377 : {
12378 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12379 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12380 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12381 119 : if (attr)
12382 110 : return new GDALAttributeHS(attr);
12383 9 : return nullptr;
12384 : }
12385 :
12386 : /************************************************************************/
12387 : /* GDALMDArrayGetAttributes() */
12388 : /************************************************************************/
12389 :
12390 : /** Return the list of attributes contained in this array.
12391 : *
12392 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12393 : * array itself needs to be freed, CPLFree() should be called (and
12394 : * GDALAttributeRelease() on individual array members).
12395 : *
12396 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12397 : *
12398 : * @param hArray Array.
12399 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12400 : * @param papszOptions Driver specific options determining how attributes
12401 : * should be retrieved. Pass nullptr for default behavior.
12402 : *
12403 : * @return an array of *pnCount attributes.
12404 : */
12405 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12406 : CSLConstList papszOptions)
12407 : {
12408 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12409 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12410 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12411 : auto ret = static_cast<GDALAttributeH *>(
12412 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12413 187 : for (size_t i = 0; i < attrs.size(); i++)
12414 : {
12415 129 : ret[i] = new GDALAttributeHS(attrs[i]);
12416 : }
12417 58 : *pnCount = attrs.size();
12418 58 : return ret;
12419 : }
12420 :
12421 : /************************************************************************/
12422 : /* GDALMDArrayCreateAttribute() */
12423 : /************************************************************************/
12424 :
12425 : /** Create a attribute within an array.
12426 : *
12427 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12428 : *
12429 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12430 : */
12431 162 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12432 : const char *pszName,
12433 : size_t nDimensions,
12434 : const GUInt64 *panDimensions,
12435 : GDALExtendedDataTypeH hEDT,
12436 : CSLConstList papszOptions)
12437 : {
12438 162 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12439 162 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12440 162 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12441 324 : std::vector<GUInt64> dims;
12442 162 : dims.reserve(nDimensions);
12443 197 : for (size_t i = 0; i < nDimensions; i++)
12444 35 : dims.push_back(panDimensions[i]);
12445 162 : auto ret = hArray->m_poImpl->CreateAttribute(
12446 486 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12447 162 : if (!ret)
12448 9 : return nullptr;
12449 153 : return new GDALAttributeHS(ret);
12450 : }
12451 :
12452 : /************************************************************************/
12453 : /* GDALMDArrayDeleteAttribute() */
12454 : /************************************************************************/
12455 :
12456 : /** Delete an attribute from an array.
12457 : *
12458 : * After this call, if a previously obtained instance of the deleted object
12459 : * is still alive, no method other than for freeing it should be invoked.
12460 : *
12461 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12462 : *
12463 : * @return true in case of success.
12464 : * @since GDAL 3.8
12465 : */
12466 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12467 : CSLConstList papszOptions)
12468 : {
12469 24 : VALIDATE_POINTER1(hArray, __func__, false);
12470 24 : VALIDATE_POINTER1(pszName, __func__, false);
12471 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12472 24 : papszOptions);
12473 : }
12474 :
12475 : /************************************************************************/
12476 : /* GDALMDArrayGetRawNoDataValue() */
12477 : /************************************************************************/
12478 :
12479 : /** Return the nodata value as a "raw" value.
12480 : *
12481 : * The value returned might be nullptr in case of no nodata value. When
12482 : * a nodata value is registered, a non-nullptr will be returned whose size in
12483 : * bytes is GetDataType().GetSize().
12484 : *
12485 : * The returned value should not be modified or freed.
12486 : *
12487 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12488 : *
12489 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12490 : */
12491 76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12492 : {
12493 76 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12494 76 : return hArray->m_poImpl->GetRawNoDataValue();
12495 : }
12496 :
12497 : /************************************************************************/
12498 : /* GDALMDArrayGetNoDataValueAsDouble() */
12499 : /************************************************************************/
12500 :
12501 : /** Return the nodata value as a double.
12502 : *
12503 : * The value returned might be nullptr in case of no nodata value. When
12504 : * a nodata value is registered, a non-nullptr will be returned whose size in
12505 : * bytes is GetDataType().GetSize().
12506 : *
12507 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12508 : *
12509 : * @param hArray Array handle.
12510 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12511 : * if a nodata value exists and can be converted to double. Might be nullptr.
12512 : *
12513 : * @return the nodata value as a double. A 0.0 value might also indicate the
12514 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12515 : * will be set to false then).
12516 : */
12517 120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12518 : int *pbHasNoDataValue)
12519 : {
12520 120 : VALIDATE_POINTER1(hArray, __func__, 0);
12521 120 : bool bHasNodataValue = false;
12522 120 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12523 120 : if (pbHasNoDataValue)
12524 120 : *pbHasNoDataValue = bHasNodataValue;
12525 120 : return ret;
12526 : }
12527 :
12528 : /************************************************************************/
12529 : /* GDALMDArrayGetNoDataValueAsInt64() */
12530 : /************************************************************************/
12531 :
12532 : /** Return the nodata value as a Int64.
12533 : *
12534 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12535 : *
12536 : * @param hArray Array handle.
12537 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12538 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12539 : *
12540 : * @return the nodata value as a Int64.
12541 : * @since GDAL 3.5
12542 : */
12543 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12544 : int *pbHasNoDataValue)
12545 : {
12546 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12547 11 : bool bHasNodataValue = false;
12548 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12549 11 : if (pbHasNoDataValue)
12550 11 : *pbHasNoDataValue = bHasNodataValue;
12551 11 : return ret;
12552 : }
12553 :
12554 : /************************************************************************/
12555 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12556 : /************************************************************************/
12557 :
12558 : /** Return the nodata value as a UInt64.
12559 : *
12560 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12561 : *
12562 : * @param hArray Array handle.
12563 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12564 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12565 : *
12566 : * @return the nodata value as a UInt64.
12567 : * @since GDAL 3.5
12568 : */
12569 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12570 : int *pbHasNoDataValue)
12571 : {
12572 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12573 7 : bool bHasNodataValue = false;
12574 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12575 7 : if (pbHasNoDataValue)
12576 7 : *pbHasNoDataValue = bHasNodataValue;
12577 7 : return ret;
12578 : }
12579 :
12580 : /************************************************************************/
12581 : /* GDALMDArraySetRawNoDataValue() */
12582 : /************************************************************************/
12583 :
12584 : /** Set the nodata value as a "raw" value.
12585 : *
12586 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12587 : * void*).
12588 : *
12589 : * @return TRUE in case of success.
12590 : */
12591 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12592 : {
12593 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12594 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12595 : }
12596 :
12597 : /************************************************************************/
12598 : /* GDALMDArraySetNoDataValueAsDouble() */
12599 : /************************************************************************/
12600 :
12601 : /** Set the nodata value as a double.
12602 : *
12603 : * If the natural data type of the attribute/array is not double, type
12604 : * conversion will occur to the type returned by GetDataType().
12605 : *
12606 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12607 : *
12608 : * @return TRUE in case of success.
12609 : */
12610 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12611 : {
12612 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12613 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12614 : }
12615 :
12616 : /************************************************************************/
12617 : /* GDALMDArraySetNoDataValueAsInt64() */
12618 : /************************************************************************/
12619 :
12620 : /** Set the nodata value as a Int64.
12621 : *
12622 : * If the natural data type of the attribute/array is not Int64, type conversion
12623 : * will occur to the type returned by GetDataType().
12624 : *
12625 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12626 : *
12627 : * @return TRUE in case of success.
12628 : * @since GDAL 3.5
12629 : */
12630 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12631 : {
12632 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12633 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12634 : }
12635 :
12636 : /************************************************************************/
12637 : /* GDALMDArraySetNoDataValueAsUInt64() */
12638 : /************************************************************************/
12639 :
12640 : /** Set the nodata value as a UInt64.
12641 : *
12642 : * If the natural data type of the attribute/array is not UInt64, type
12643 : * conversion will occur to the type returned by GetDataType().
12644 : *
12645 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12646 : *
12647 : * @return TRUE in case of success.
12648 : * @since GDAL 3.5
12649 : */
12650 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12651 : uint64_t nNoDataValue)
12652 : {
12653 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12654 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12655 : }
12656 :
12657 : /************************************************************************/
12658 : /* GDALMDArrayResize() */
12659 : /************************************************************************/
12660 :
12661 : /** Resize an array to new dimensions.
12662 : *
12663 : * Not all drivers may allow this operation, and with restrictions (e.g.
12664 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12665 : *
12666 : * Resizing a dimension used in other arrays will cause those other arrays
12667 : * to be resized.
12668 : *
12669 : * This is the same as the C++ method GDALMDArray::Resize().
12670 : *
12671 : * @param hArray Array.
12672 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12673 : * new size of each indexing dimension.
12674 : * @param papszOptions Options. (Driver specific)
12675 : * @return true in case of success.
12676 : * @since GDAL 3.7
12677 : */
12678 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12679 : CSLConstList papszOptions)
12680 : {
12681 42 : VALIDATE_POINTER1(hArray, __func__, false);
12682 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12683 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12684 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12685 : {
12686 83 : anNewDimSizes[i] = panNewDimSizes[i];
12687 : }
12688 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12689 : }
12690 :
12691 : /************************************************************************/
12692 : /* GDALMDArraySetScale() */
12693 : /************************************************************************/
12694 :
12695 : /** Set the scale value to apply to raw values.
12696 : *
12697 : * unscaled_value = raw_value * GetScale() + GetOffset()
12698 : *
12699 : * This is the same as the C++ method GDALMDArray::SetScale().
12700 : *
12701 : * @return TRUE in case of success.
12702 : */
12703 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12704 : {
12705 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12706 0 : return hArray->m_poImpl->SetScale(dfScale);
12707 : }
12708 :
12709 : /************************************************************************/
12710 : /* GDALMDArraySetScaleEx() */
12711 : /************************************************************************/
12712 :
12713 : /** Set the scale value to apply to raw values.
12714 : *
12715 : * unscaled_value = raw_value * GetScale() + GetOffset()
12716 : *
12717 : * This is the same as the C++ method GDALMDArray::SetScale().
12718 : *
12719 : * @return TRUE in case of success.
12720 : * @since GDAL 3.3
12721 : */
12722 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12723 : GDALDataType eStorageType)
12724 : {
12725 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12726 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12727 : }
12728 :
12729 : /************************************************************************/
12730 : /* GDALMDArraySetOffset() */
12731 : /************************************************************************/
12732 :
12733 : /** Set the scale value to apply to raw values.
12734 : *
12735 : * unscaled_value = raw_value * GetScale() + GetOffset()
12736 : *
12737 : * This is the same as the C++ method GDALMDArray::SetOffset().
12738 : *
12739 : * @return TRUE in case of success.
12740 : */
12741 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12742 : {
12743 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12744 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12745 : }
12746 :
12747 : /************************************************************************/
12748 : /* GDALMDArraySetOffsetEx() */
12749 : /************************************************************************/
12750 :
12751 : /** Set the scale value to apply to raw values.
12752 : *
12753 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12754 : *
12755 : * This is the same as the C++ method GDALMDArray::SetOffset().
12756 : *
12757 : * @return TRUE in case of success.
12758 : * @since GDAL 3.3
12759 : */
12760 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12761 : GDALDataType eStorageType)
12762 : {
12763 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12764 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12765 : }
12766 :
12767 : /************************************************************************/
12768 : /* GDALMDArrayGetScale() */
12769 : /************************************************************************/
12770 :
12771 : /** Get the scale value to apply to raw values.
12772 : *
12773 : * unscaled_value = raw_value * GetScale() + GetOffset()
12774 : *
12775 : * This is the same as the C++ method GDALMDArray::GetScale().
12776 : *
12777 : * @return the scale value
12778 : */
12779 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12780 : {
12781 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12782 103 : bool bHasValue = false;
12783 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12784 103 : if (pbHasValue)
12785 103 : *pbHasValue = bHasValue;
12786 103 : return dfRet;
12787 : }
12788 :
12789 : /************************************************************************/
12790 : /* GDALMDArrayGetScaleEx() */
12791 : /************************************************************************/
12792 :
12793 : /** Get the scale value to apply to raw values.
12794 : *
12795 : * unscaled_value = raw_value * GetScale() + GetScale()
12796 : *
12797 : * This is the same as the C++ method GDALMDArray::GetScale().
12798 : *
12799 : * @return the scale value
12800 : * @since GDAL 3.3
12801 : */
12802 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12803 : GDALDataType *peStorageType)
12804 : {
12805 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12806 5 : bool bHasValue = false;
12807 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12808 5 : if (pbHasValue)
12809 5 : *pbHasValue = bHasValue;
12810 5 : return dfRet;
12811 : }
12812 :
12813 : /************************************************************************/
12814 : /* GDALMDArrayGetOffset() */
12815 : /************************************************************************/
12816 :
12817 : /** Get the scale value to apply to raw values.
12818 : *
12819 : * unscaled_value = raw_value * GetScale() + GetOffset()
12820 : *
12821 : * This is the same as the C++ method GDALMDArray::GetOffset().
12822 : *
12823 : * @return the scale value
12824 : */
12825 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12826 : {
12827 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12828 100 : bool bHasValue = false;
12829 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12830 100 : if (pbHasValue)
12831 100 : *pbHasValue = bHasValue;
12832 100 : return dfRet;
12833 : }
12834 :
12835 : /************************************************************************/
12836 : /* GDALMDArrayGetOffsetEx() */
12837 : /************************************************************************/
12838 :
12839 : /** Get the scale value to apply to raw values.
12840 : *
12841 : * unscaled_value = raw_value * GetScale() + GetOffset()
12842 : *
12843 : * This is the same as the C++ method GDALMDArray::GetOffset().
12844 : *
12845 : * @return the scale value
12846 : * @since GDAL 3.3
12847 : */
12848 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12849 : GDALDataType *peStorageType)
12850 : {
12851 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12852 5 : bool bHasValue = false;
12853 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12854 5 : if (pbHasValue)
12855 5 : *pbHasValue = bHasValue;
12856 5 : return dfRet;
12857 : }
12858 :
12859 : /************************************************************************/
12860 : /* GDALMDArrayGetBlockSize() */
12861 : /************************************************************************/
12862 :
12863 : /** Return the "natural" block size of the array along all dimensions.
12864 : *
12865 : * Some drivers might organize the array in tiles/blocks and reading/writing
12866 : * aligned on those tile/block boundaries will be more efficient.
12867 : *
12868 : * The returned number of elements in the vector is the same as
12869 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12870 : * the natural block size along the considered dimension.
12871 : * "Flat" arrays will typically return a vector of values set to 0.
12872 : *
12873 : * The default implementation will return a vector of values set to 0.
12874 : *
12875 : * This method is used by GetProcessingChunkSize().
12876 : *
12877 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12878 : * theoretical case of a 32-bit platform, this might exceed its size_t
12879 : * allocation capabilities.
12880 : *
12881 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12882 : *
12883 : * @return the block size, in number of elements along each dimension.
12884 : */
12885 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12886 : {
12887 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12888 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12889 93 : auto res = hArray->m_poImpl->GetBlockSize();
12890 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12891 285 : for (size_t i = 0; i < res.size(); i++)
12892 : {
12893 192 : ret[i] = res[i];
12894 : }
12895 93 : *pnCount = res.size();
12896 93 : return ret;
12897 : }
12898 :
12899 : /***********************************************************************/
12900 : /* GDALMDArrayGetProcessingChunkSize() */
12901 : /************************************************************************/
12902 :
12903 : /** \brief Return an optimal chunk size for read/write operations, given the
12904 : * natural block size and memory constraints specified.
12905 : *
12906 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12907 : * multiple of those returned by GetBlockSize() (unless the block define by
12908 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12909 : * returned by this method).
12910 : *
12911 : * This is the same as the C++ method
12912 : * GDALAbstractMDArray::GetProcessingChunkSize().
12913 : *
12914 : * @param hArray Array.
12915 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12916 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12917 : * chunk.
12918 : *
12919 : * @return the chunk size, in number of elements along each dimension.
12920 : */
12921 :
12922 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12923 : size_t nMaxChunkMemory)
12924 : {
12925 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12926 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12927 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12928 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12929 3 : for (size_t i = 0; i < res.size(); i++)
12930 : {
12931 2 : ret[i] = res[i];
12932 : }
12933 1 : *pnCount = res.size();
12934 1 : return ret;
12935 : }
12936 :
12937 : /************************************************************************/
12938 : /* GDALMDArrayGetStructuralInfo() */
12939 : /************************************************************************/
12940 :
12941 : /** Return structural information on the array.
12942 : *
12943 : * This may be the compression, etc..
12944 : *
12945 : * The return value should not be freed and is valid until GDALMDArray is
12946 : * released or this function called again.
12947 : *
12948 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12949 : */
12950 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12951 : {
12952 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12953 15 : return hArray->m_poImpl->GetStructuralInfo();
12954 : }
12955 :
12956 : /************************************************************************/
12957 : /* GDALMDArrayGetView() */
12958 : /************************************************************************/
12959 :
12960 : /** Return a view of the array using slicing or field access.
12961 : *
12962 : * The returned object should be released with GDALMDArrayRelease().
12963 : *
12964 : * This is the same as the C++ method GDALMDArray::GetView().
12965 : */
12966 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12967 : {
12968 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12969 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12970 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12971 430 : if (!sliced)
12972 22 : return nullptr;
12973 408 : return new GDALMDArrayHS(sliced);
12974 : }
12975 :
12976 : /************************************************************************/
12977 : /* GDALMDArrayTranspose() */
12978 : /************************************************************************/
12979 :
12980 : /** Return a view of the array whose axis have been reordered.
12981 : *
12982 : * The returned object should be released with GDALMDArrayRelease().
12983 : *
12984 : * This is the same as the C++ method GDALMDArray::Transpose().
12985 : */
12986 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12987 : const int *panMapNewAxisToOldAxis)
12988 : {
12989 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12990 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12991 44 : if (nNewAxisCount)
12992 : {
12993 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12994 : nNewAxisCount * sizeof(int));
12995 : }
12996 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12997 44 : if (!reordered)
12998 7 : return nullptr;
12999 37 : return new GDALMDArrayHS(reordered);
13000 : }
13001 :
13002 : /************************************************************************/
13003 : /* GDALMDArrayGetUnscaled() */
13004 : /************************************************************************/
13005 :
13006 : /** Return an array that is the unscaled version of the current one.
13007 : *
13008 : * That is each value of the unscaled array will be
13009 : * unscaled_value = raw_value * GetScale() + GetOffset()
13010 : *
13011 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13012 : * from unscaled values to raw values.
13013 : *
13014 : * The returned object should be released with GDALMDArrayRelease().
13015 : *
13016 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13017 : */
13018 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13019 : {
13020 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13021 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13022 13 : if (!unscaled)
13023 0 : return nullptr;
13024 13 : return new GDALMDArrayHS(unscaled);
13025 : }
13026 :
13027 : /************************************************************************/
13028 : /* GDALMDArrayGetMask() */
13029 : /************************************************************************/
13030 :
13031 : /** Return an array that is a mask for the current array
13032 : *
13033 : * This array will be of type Byte, with values set to 0 to indicate invalid
13034 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13035 : *
13036 : * The returned object should be released with GDALMDArrayRelease().
13037 : *
13038 : * This is the same as the C++ method GDALMDArray::GetMask().
13039 : */
13040 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13041 : {
13042 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13043 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13044 35 : if (!unscaled)
13045 7 : return nullptr;
13046 28 : return new GDALMDArrayHS(unscaled);
13047 : }
13048 :
13049 : /************************************************************************/
13050 : /* GDALMDArrayGetResampled() */
13051 : /************************************************************************/
13052 :
13053 : /** Return an array that is a resampled / reprojected view of the current array
13054 : *
13055 : * This is the same as the C++ method GDALMDArray::GetResampled().
13056 : *
13057 : * Currently this method can only resample along the last 2 dimensions, unless
13058 : * orthorectifying a NASA EMIT dataset.
13059 : *
13060 : * The returned object should be released with GDALMDArrayRelease().
13061 : *
13062 : * @since 3.4
13063 : */
13064 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13065 : const GDALDimensionH *pahNewDims,
13066 : GDALRIOResampleAlg resampleAlg,
13067 : OGRSpatialReferenceH hTargetSRS,
13068 : CSLConstList papszOptions)
13069 : {
13070 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13071 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13072 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13073 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13074 : {
13075 78 : if (pahNewDims[i])
13076 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13077 : }
13078 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13079 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13080 68 : papszOptions);
13081 34 : if (!poNewArray)
13082 8 : return nullptr;
13083 26 : return new GDALMDArrayHS(poNewArray);
13084 : }
13085 :
13086 : /************************************************************************/
13087 : /* GDALMDArraySetUnit() */
13088 : /************************************************************************/
13089 :
13090 : /** Set the variable unit.
13091 : *
13092 : * Values should conform as much as possible with those allowed by
13093 : * the NetCDF CF conventions:
13094 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13095 : * but others might be returned.
13096 : *
13097 : * Few examples are "meter", "degrees", "second", ...
13098 : * Empty value means unknown.
13099 : *
13100 : * This is the same as the C function GDALMDArraySetUnit()
13101 : *
13102 : * @param hArray array.
13103 : * @param pszUnit unit name.
13104 : * @return TRUE in case of success.
13105 : */
13106 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13107 : {
13108 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13109 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13110 : }
13111 :
13112 : /************************************************************************/
13113 : /* GDALMDArrayGetUnit() */
13114 : /************************************************************************/
13115 :
13116 : /** Return the array unit.
13117 : *
13118 : * Values should conform as much as possible with those allowed by
13119 : * the NetCDF CF conventions:
13120 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13121 : * but others might be returned.
13122 : *
13123 : * Few examples are "meter", "degrees", "second", ...
13124 : * Empty value means unknown.
13125 : *
13126 : * The return value should not be freed and is valid until GDALMDArray is
13127 : * released or this function called again.
13128 : *
13129 : * This is the same as the C++ method GDALMDArray::GetUnit().
13130 : */
13131 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13132 : {
13133 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13134 111 : return hArray->m_poImpl->GetUnit().c_str();
13135 : }
13136 :
13137 : /************************************************************************/
13138 : /* GDALMDArrayGetSpatialRef() */
13139 : /************************************************************************/
13140 :
13141 : /** Assign a spatial reference system object to the array.
13142 : *
13143 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13144 : * @return TRUE in case of success.
13145 : */
13146 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13147 : {
13148 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13149 60 : return hArray->m_poImpl->SetSpatialRef(
13150 60 : OGRSpatialReference::FromHandle(hSRS));
13151 : }
13152 :
13153 : /************************************************************************/
13154 : /* GDALMDArrayGetSpatialRef() */
13155 : /************************************************************************/
13156 :
13157 : /** Return the spatial reference system object associated with the array.
13158 : *
13159 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13160 : *
13161 : * The returned object must be freed with OSRDestroySpatialReference().
13162 : */
13163 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13164 : {
13165 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13166 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13167 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13168 : }
13169 :
13170 : /************************************************************************/
13171 : /* GDALMDArrayGetStatistics() */
13172 : /************************************************************************/
13173 :
13174 : /**
13175 : * \brief Fetch statistics.
13176 : *
13177 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13178 : *
13179 : * @since GDAL 3.2
13180 : */
13181 :
13182 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13183 : int bApproxOK, int bForce, double *pdfMin,
13184 : double *pdfMax, double *pdfMean,
13185 : double *pdfStdDev, GUInt64 *pnValidCount,
13186 : GDALProgressFunc pfnProgress,
13187 : void *pProgressData)
13188 : {
13189 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13190 30 : return hArray->m_poImpl->GetStatistics(
13191 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13192 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13193 : }
13194 :
13195 : /************************************************************************/
13196 : /* GDALMDArrayComputeStatistics() */
13197 : /************************************************************************/
13198 :
13199 : /**
13200 : * \brief Compute statistics.
13201 : *
13202 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13203 : *
13204 : * @since GDAL 3.2
13205 : * @see GDALMDArrayComputeStatisticsEx()
13206 : */
13207 :
13208 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13209 : int bApproxOK, double *pdfMin, double *pdfMax,
13210 : double *pdfMean, double *pdfStdDev,
13211 : GUInt64 *pnValidCount,
13212 : GDALProgressFunc pfnProgress,
13213 : void *pProgressData)
13214 : {
13215 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13216 0 : return hArray->m_poImpl->ComputeStatistics(
13217 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13218 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13219 : }
13220 :
13221 : /************************************************************************/
13222 : /* GDALMDArrayComputeStatisticsEx() */
13223 : /************************************************************************/
13224 :
13225 : /**
13226 : * \brief Compute statistics.
13227 : *
13228 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13229 : *
13230 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13231 : *
13232 : * @since GDAL 3.8
13233 : */
13234 :
13235 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13236 : int bApproxOK, double *pdfMin,
13237 : double *pdfMax, double *pdfMean,
13238 : double *pdfStdDev, GUInt64 *pnValidCount,
13239 : GDALProgressFunc pfnProgress,
13240 : void *pProgressData,
13241 : CSLConstList papszOptions)
13242 : {
13243 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13244 8 : return hArray->m_poImpl->ComputeStatistics(
13245 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13246 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13247 : }
13248 :
13249 : /************************************************************************/
13250 : /* GDALMDArrayGetCoordinateVariables() */
13251 : /************************************************************************/
13252 :
13253 : /** Return coordinate variables.
13254 : *
13255 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13256 : * itself needs to be freed, CPLFree() should be called (and
13257 : * GDALMDArrayRelease() on individual array members).
13258 : *
13259 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13260 : *
13261 : * @param hArray Array.
13262 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13263 : *
13264 : * @return an array of *pnCount arrays.
13265 : * @since 3.4
13266 : */
13267 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13268 : size_t *pnCount)
13269 : {
13270 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13271 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13272 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13273 : auto ret = static_cast<GDALMDArrayH *>(
13274 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13275 29 : for (size_t i = 0; i < coordinates.size(); i++)
13276 : {
13277 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13278 : }
13279 13 : *pnCount = coordinates.size();
13280 13 : return ret;
13281 : }
13282 :
13283 : /************************************************************************/
13284 : /* GDALMDArrayGetGridded() */
13285 : /************************************************************************/
13286 :
13287 : /** Return a gridded array from scattered point data, that is from an array
13288 : * whose last dimension is the indexing variable of X and Y arrays.
13289 : *
13290 : * The returned object should be released with GDALMDArrayRelease().
13291 : *
13292 : * This is the same as the C++ method GDALMDArray::GetGridded().
13293 : *
13294 : * @since GDAL 3.7
13295 : */
13296 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13297 : const char *pszGridOptions,
13298 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13299 : CSLConstList papszOptions)
13300 : {
13301 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13302 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13303 22 : auto gridded = hArray->m_poImpl->GetGridded(
13304 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13305 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13306 22 : if (!gridded)
13307 19 : return nullptr;
13308 3 : return new GDALMDArrayHS(gridded);
13309 : }
13310 :
13311 : /************************************************************************/
13312 : /* GDALMDArrayGetMeshGrid() */
13313 : /************************************************************************/
13314 :
13315 : /** Return a list of multidimensional arrays from a list of one-dimensional
13316 : * arrays.
13317 : *
13318 : * This is typically used to transform one-dimensional longitude, latitude
13319 : * arrays into 2D ones.
13320 : *
13321 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13322 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13323 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13324 : * repeated to fill the matrix along the first dimension for x1, the second
13325 : * for x2 and so on.
13326 : *
13327 : * For example, if x = [1, 2], and y = [3, 4, 5],
13328 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13329 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13330 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13331 : *
13332 : * and
13333 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13334 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13335 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13336 : *
13337 : * The currently supported options are:
13338 : * <ul>
13339 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13340 : * output.
13341 : * </li>
13342 : * </ul>
13343 : *
13344 : * This is the same as
13345 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13346 : * function.
13347 : *
13348 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13349 : * If only the array itself needs to be freed, CPLFree() should be called
13350 : * (and GDALMDArrayRelease() on individual array members).
13351 : *
13352 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13353 : *
13354 : * @param pahInputArrays Input arrays
13355 : * @param nCountInputArrays Number of input arrays
13356 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13357 : * @param papszOptions NULL, or NULL terminated list of options.
13358 : *
13359 : * @return an array of *pnCountOutputArrays arrays.
13360 : * @since 3.10
13361 : */
13362 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13363 : size_t nCountInputArrays,
13364 : size_t *pnCountOutputArrays,
13365 : CSLConstList papszOptions)
13366 : {
13367 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13368 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13369 :
13370 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13371 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13372 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13373 :
13374 : const auto apoOutputArrays =
13375 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13376 : auto ret = static_cast<GDALMDArrayH *>(
13377 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13378 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13379 : {
13380 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13381 : }
13382 7 : *pnCountOutputArrays = apoOutputArrays.size();
13383 7 : return ret;
13384 : }
13385 :
13386 : /************************************************************************/
13387 : /* GDALReleaseArrays() */
13388 : /************************************************************************/
13389 :
13390 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13391 : *
13392 : * @param arrays return pointer of above methods
13393 : * @param nCount *pnCount value returned by above methods
13394 : */
13395 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13396 : {
13397 46 : for (size_t i = 0; i < nCount; i++)
13398 : {
13399 26 : delete arrays[i];
13400 : }
13401 20 : CPLFree(arrays);
13402 20 : }
13403 :
13404 : /************************************************************************/
13405 : /* GDALMDArrayCache() */
13406 : /************************************************************************/
13407 :
13408 : /**
13409 : * \brief Cache the content of the array into an auxiliary filename.
13410 : *
13411 : * This is the same as the C++ method GDALMDArray::Cache().
13412 : *
13413 : * @since GDAL 3.4
13414 : */
13415 :
13416 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13417 : {
13418 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13419 7 : return hArray->m_poImpl->Cache(papszOptions);
13420 : }
13421 :
13422 : /************************************************************************/
13423 : /* GDALMDArrayRename() */
13424 : /************************************************************************/
13425 :
13426 : /** Rename the array.
13427 : *
13428 : * This is not implemented by all drivers.
13429 : *
13430 : * Drivers known to implement it: MEM, netCDF, Zarr.
13431 : *
13432 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13433 : *
13434 : * @return true in case of success
13435 : * @since GDAL 3.8
13436 : */
13437 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13438 : {
13439 28 : VALIDATE_POINTER1(hArray, __func__, false);
13440 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13441 28 : return hArray->m_poImpl->Rename(pszNewName);
13442 : }
13443 :
13444 : /************************************************************************/
13445 : /* GDALAttributeRelease() */
13446 : /************************************************************************/
13447 :
13448 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13449 : *
13450 : * Note: when applied on a object coming from a driver, this does not
13451 : * destroy the object in the file, database, etc...
13452 : */
13453 737 : void GDALAttributeRelease(GDALAttributeH hAttr)
13454 : {
13455 737 : delete hAttr;
13456 737 : }
13457 :
13458 : /************************************************************************/
13459 : /* GDALAttributeGetName() */
13460 : /************************************************************************/
13461 :
13462 : /** Return the name of the attribute.
13463 : *
13464 : * The returned pointer is valid until hAttr is released.
13465 : *
13466 : * This is the same as the C++ method GDALAttribute::GetName().
13467 : */
13468 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13469 : {
13470 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13471 361 : return hAttr->m_poImpl->GetName().c_str();
13472 : }
13473 :
13474 : /************************************************************************/
13475 : /* GDALAttributeGetFullName() */
13476 : /************************************************************************/
13477 :
13478 : /** Return the full name of the attribute.
13479 : *
13480 : * The returned pointer is valid until hAttr is released.
13481 : *
13482 : * This is the same as the C++ method GDALAttribute::GetFullName().
13483 : */
13484 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13485 : {
13486 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13487 49 : return hAttr->m_poImpl->GetFullName().c_str();
13488 : }
13489 :
13490 : /************************************************************************/
13491 : /* GDALAttributeGetTotalElementsCount() */
13492 : /************************************************************************/
13493 :
13494 : /** Return the total number of values in the attribute.
13495 : *
13496 : * This is the same as the C++ method
13497 : * GDALAbstractMDArray::GetTotalElementsCount()
13498 : */
13499 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13500 : {
13501 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13502 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13503 : }
13504 :
13505 : /************************************************************************/
13506 : /* GDALAttributeGetDimensionCount() */
13507 : /************************************************************************/
13508 :
13509 : /** Return the number of dimensions.
13510 : *
13511 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13512 : */
13513 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13514 : {
13515 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13516 12 : return hAttr->m_poImpl->GetDimensionCount();
13517 : }
13518 :
13519 : /************************************************************************/
13520 : /* GDALAttributeGetDimensionsSize() */
13521 : /************************************************************************/
13522 :
13523 : /** Return the dimension sizes of the attribute.
13524 : *
13525 : * The returned array must be freed with CPLFree()
13526 : *
13527 : * @param hAttr Attribute.
13528 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13529 : *
13530 : * @return an array of *pnCount values.
13531 : */
13532 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13533 : {
13534 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13535 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13536 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13537 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13538 22 : for (size_t i = 0; i < dims.size(); i++)
13539 : {
13540 11 : ret[i] = dims[i]->GetSize();
13541 : }
13542 11 : *pnCount = dims.size();
13543 11 : return ret;
13544 : }
13545 :
13546 : /************************************************************************/
13547 : /* GDALAttributeGetDataType() */
13548 : /************************************************************************/
13549 :
13550 : /** Return the data type
13551 : *
13552 : * The return must be freed with GDALExtendedDataTypeRelease().
13553 : */
13554 433 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13555 : {
13556 433 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13557 : return new GDALExtendedDataTypeHS(
13558 433 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13559 : }
13560 :
13561 : /************************************************************************/
13562 : /* GDALAttributeReadAsRaw() */
13563 : /************************************************************************/
13564 :
13565 : /** Return the raw value of an attribute.
13566 : *
13567 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13568 : *
13569 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13570 : *
13571 : * @param hAttr Attribute.
13572 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13573 : *
13574 : * @return a buffer of *pnSize bytes.
13575 : */
13576 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13577 : {
13578 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13579 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13580 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13581 6 : *pnSize = res.size();
13582 6 : auto ret = res.StealData();
13583 6 : if (!ret)
13584 : {
13585 0 : *pnSize = 0;
13586 0 : return nullptr;
13587 : }
13588 6 : return ret;
13589 : }
13590 :
13591 : /************************************************************************/
13592 : /* GDALAttributeFreeRawResult() */
13593 : /************************************************************************/
13594 :
13595 : /** Free the return of GDALAttributeAsRaw()
13596 : */
13597 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13598 : CPL_UNUSED size_t nSize)
13599 : {
13600 6 : VALIDATE_POINTER0(hAttr, __func__);
13601 6 : if (raw)
13602 : {
13603 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13604 6 : const auto nDTSize(dt.GetSize());
13605 6 : GByte *pabyPtr = raw;
13606 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13607 6 : CPLAssert(nSize == nDTSize * nEltCount);
13608 12 : for (size_t i = 0; i < nEltCount; ++i)
13609 : {
13610 6 : dt.FreeDynamicMemory(pabyPtr);
13611 6 : pabyPtr += nDTSize;
13612 : }
13613 6 : CPLFree(raw);
13614 : }
13615 : }
13616 :
13617 : /************************************************************************/
13618 : /* GDALAttributeReadAsString() */
13619 : /************************************************************************/
13620 :
13621 : /** Return the value of an attribute as a string.
13622 : *
13623 : * The returned string should not be freed, and its lifetime does not
13624 : * excess a next call to ReadAsString() on the same object, or the deletion
13625 : * of the object itself.
13626 : *
13627 : * This function will only return the first element if there are several.
13628 : *
13629 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13630 : *
13631 : * @return a string, or nullptr.
13632 : */
13633 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13634 : {
13635 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13636 107 : return hAttr->m_poImpl->ReadAsString();
13637 : }
13638 :
13639 : /************************************************************************/
13640 : /* GDALAttributeReadAsInt() */
13641 : /************************************************************************/
13642 :
13643 : /** Return the value of an attribute as a integer.
13644 : *
13645 : * This function will only return the first element if there are several.
13646 : *
13647 : * It can fail if its value can not be converted to integer.
13648 : *
13649 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13650 : *
13651 : * @return a integer, or INT_MIN in case of error.
13652 : */
13653 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13654 : {
13655 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13656 22 : return hAttr->m_poImpl->ReadAsInt();
13657 : }
13658 :
13659 : /************************************************************************/
13660 : /* GDALAttributeReadAsInt64() */
13661 : /************************************************************************/
13662 :
13663 : /** Return the value of an attribute as a int64_t.
13664 : *
13665 : * This function will only return the first element if there are several.
13666 : *
13667 : * It can fail if its value can not be converted to integer.
13668 : *
13669 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13670 : *
13671 : * @return an int64_t, or INT64_MIN in case of error.
13672 : */
13673 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13674 : {
13675 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13676 15 : return hAttr->m_poImpl->ReadAsInt64();
13677 : }
13678 :
13679 : /************************************************************************/
13680 : /* GDALAttributeReadAsDouble() */
13681 : /************************************************************************/
13682 :
13683 : /** Return the value of an attribute as a double.
13684 : *
13685 : * This function will only return the first element if there are several.
13686 : *
13687 : * It can fail if its value can not be converted to double.
13688 : *
13689 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13690 : *
13691 : * @return a double value.
13692 : */
13693 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13694 : {
13695 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13696 40 : return hAttr->m_poImpl->ReadAsDouble();
13697 : }
13698 :
13699 : /************************************************************************/
13700 : /* GDALAttributeReadAsStringArray() */
13701 : /************************************************************************/
13702 :
13703 : /** Return the value of an attribute as an array of strings.
13704 : *
13705 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13706 : *
13707 : * The return value must be freed with CSLDestroy().
13708 : */
13709 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13710 : {
13711 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13712 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13713 : }
13714 :
13715 : /************************************************************************/
13716 : /* GDALAttributeReadAsIntArray() */
13717 : /************************************************************************/
13718 :
13719 : /** Return the value of an attribute as an array of integers.
13720 : *
13721 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13722 : *
13723 : * @param hAttr Attribute
13724 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13725 : * @return array to be freed with CPLFree(), or nullptr.
13726 : */
13727 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13728 : {
13729 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13730 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13731 15 : *pnCount = 0;
13732 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13733 15 : if (tmp.empty())
13734 0 : return nullptr;
13735 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13736 15 : if (!ret)
13737 0 : return nullptr;
13738 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13739 15 : *pnCount = tmp.size();
13740 15 : return ret;
13741 : }
13742 :
13743 : /************************************************************************/
13744 : /* GDALAttributeReadAsInt64Array() */
13745 : /************************************************************************/
13746 :
13747 : /** Return the value of an attribute as an array of int64_t.
13748 : *
13749 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13750 : *
13751 : * @param hAttr Attribute
13752 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13753 : * @return array to be freed with CPLFree(), or nullptr.
13754 : */
13755 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13756 : {
13757 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13758 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13759 14 : *pnCount = 0;
13760 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13761 14 : if (tmp.empty())
13762 0 : return nullptr;
13763 : auto ret = static_cast<int64_t *>(
13764 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13765 14 : if (!ret)
13766 0 : return nullptr;
13767 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13768 14 : *pnCount = tmp.size();
13769 14 : return ret;
13770 : }
13771 :
13772 : /************************************************************************/
13773 : /* GDALAttributeReadAsDoubleArray() */
13774 : /************************************************************************/
13775 :
13776 : /** Return the value of an attribute as an array of doubles.
13777 : *
13778 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13779 : *
13780 : * @param hAttr Attribute
13781 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13782 : * @return array to be freed with CPLFree(), or nullptr.
13783 : */
13784 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13785 : {
13786 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13787 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13788 29 : *pnCount = 0;
13789 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13790 29 : if (tmp.empty())
13791 0 : return nullptr;
13792 : auto ret =
13793 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13794 29 : if (!ret)
13795 0 : return nullptr;
13796 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13797 29 : *pnCount = tmp.size();
13798 29 : return ret;
13799 : }
13800 :
13801 : /************************************************************************/
13802 : /* GDALAttributeWriteRaw() */
13803 : /************************************************************************/
13804 :
13805 : /** Write an attribute from raw values expressed in GetDataType()
13806 : *
13807 : * The values should be provided in the type of GetDataType() and there should
13808 : * be exactly GetTotalElementsCount() of them.
13809 : * If GetDataType() is a string, each value should be a char* pointer.
13810 : *
13811 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13812 : *
13813 : * @param hAttr Attribute
13814 : * @param pabyValue Buffer of nLen bytes.
13815 : * @param nLength Size of pabyValue in bytes. Should be equal to
13816 : * GetTotalElementsCount() * GetDataType().GetSize()
13817 : * @return TRUE in case of success.
13818 : */
13819 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13820 : size_t nLength)
13821 : {
13822 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13823 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13824 : }
13825 :
13826 : /************************************************************************/
13827 : /* GDALAttributeWriteString() */
13828 : /************************************************************************/
13829 :
13830 : /** Write an attribute from a string value.
13831 : *
13832 : * Type conversion will be performed if needed. If the attribute contains
13833 : * multiple values, only the first one will be updated.
13834 : *
13835 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13836 : *
13837 : * @param hAttr Attribute
13838 : * @param pszVal Pointer to a string.
13839 : * @return TRUE in case of success.
13840 : */
13841 181 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13842 : {
13843 181 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13844 181 : return hAttr->m_poImpl->Write(pszVal);
13845 : }
13846 :
13847 : /************************************************************************/
13848 : /* GDALAttributeWriteInt() */
13849 : /************************************************************************/
13850 :
13851 : /** Write an attribute from a integer value.
13852 : *
13853 : * Type conversion will be performed if needed. If the attribute contains
13854 : * multiple values, only the first one will be updated.
13855 : *
13856 : * This is the same as the C++ method GDALAttribute::WriteInt()
13857 : *
13858 : * @param hAttr Attribute
13859 : * @param nVal Value.
13860 : * @return TRUE in case of success.
13861 : */
13862 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13863 : {
13864 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13865 22 : return hAttr->m_poImpl->WriteInt(nVal);
13866 : }
13867 :
13868 : /************************************************************************/
13869 : /* GDALAttributeWriteInt64() */
13870 : /************************************************************************/
13871 :
13872 : /** Write an attribute from an int64_t value.
13873 : *
13874 : * Type conversion will be performed if needed. If the attribute contains
13875 : * multiple values, only the first one will be updated.
13876 : *
13877 : * This is the same as the C++ method GDALAttribute::WriteLong()
13878 : *
13879 : * @param hAttr Attribute
13880 : * @param nVal Value.
13881 : * @return TRUE in case of success.
13882 : */
13883 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13884 : {
13885 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13886 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13887 : }
13888 :
13889 : /************************************************************************/
13890 : /* GDALAttributeWriteDouble() */
13891 : /************************************************************************/
13892 :
13893 : /** Write an attribute from a double value.
13894 : *
13895 : * Type conversion will be performed if needed. If the attribute contains
13896 : * multiple values, only the first one will be updated.
13897 : *
13898 : * This is the same as the C++ method GDALAttribute::Write(double);
13899 : *
13900 : * @param hAttr Attribute
13901 : * @param dfVal Value.
13902 : *
13903 : * @return TRUE in case of success.
13904 : */
13905 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13906 : {
13907 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13908 11 : return hAttr->m_poImpl->Write(dfVal);
13909 : }
13910 :
13911 : /************************************************************************/
13912 : /* GDALAttributeWriteStringArray() */
13913 : /************************************************************************/
13914 :
13915 : /** Write an attribute from an array of strings.
13916 : *
13917 : * Type conversion will be performed if needed.
13918 : *
13919 : * Exactly GetTotalElementsCount() strings must be provided
13920 : *
13921 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13922 : *
13923 : * @param hAttr Attribute
13924 : * @param papszValues Array of strings.
13925 : * @return TRUE in case of success.
13926 : */
13927 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13928 : CSLConstList papszValues)
13929 : {
13930 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13931 8 : return hAttr->m_poImpl->Write(papszValues);
13932 : }
13933 :
13934 : /************************************************************************/
13935 : /* GDALAttributeWriteIntArray() */
13936 : /************************************************************************/
13937 :
13938 : /** Write an attribute from an array of int.
13939 : *
13940 : * Type conversion will be performed if needed.
13941 : *
13942 : * Exactly GetTotalElementsCount() strings must be provided
13943 : *
13944 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13945 : * size_t)
13946 : *
13947 : * @param hAttr Attribute
13948 : * @param panValues Array of int.
13949 : * @param nCount Should be equal to GetTotalElementsCount().
13950 : * @return TRUE in case of success.
13951 : */
13952 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13953 : size_t nCount)
13954 : {
13955 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13956 11 : return hAttr->m_poImpl->Write(panValues, nCount);
13957 : }
13958 :
13959 : /************************************************************************/
13960 : /* GDALAttributeWriteInt64Array() */
13961 : /************************************************************************/
13962 :
13963 : /** Write an attribute from an array of int64_t.
13964 : *
13965 : * Type conversion will be performed if needed.
13966 : *
13967 : * Exactly GetTotalElementsCount() strings must be provided
13968 : *
13969 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13970 : * size_t)
13971 : *
13972 : * @param hAttr Attribute
13973 : * @param panValues Array of int64_t.
13974 : * @param nCount Should be equal to GetTotalElementsCount().
13975 : * @return TRUE in case of success.
13976 : */
13977 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13978 : size_t nCount)
13979 : {
13980 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13981 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13982 : }
13983 :
13984 : /************************************************************************/
13985 : /* GDALAttributeWriteDoubleArray() */
13986 : /************************************************************************/
13987 :
13988 : /** Write an attribute from an array of double.
13989 : *
13990 : * Type conversion will be performed if needed.
13991 : *
13992 : * Exactly GetTotalElementsCount() strings must be provided
13993 : *
13994 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13995 : * size_t)
13996 : *
13997 : * @param hAttr Attribute
13998 : * @param padfValues Array of double.
13999 : * @param nCount Should be equal to GetTotalElementsCount().
14000 : * @return TRUE in case of success.
14001 : */
14002 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14003 : const double *padfValues, size_t nCount)
14004 : {
14005 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14006 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14007 : }
14008 :
14009 : /************************************************************************/
14010 : /* GDALAttributeRename() */
14011 : /************************************************************************/
14012 :
14013 : /** Rename the attribute.
14014 : *
14015 : * This is not implemented by all drivers.
14016 : *
14017 : * Drivers known to implement it: MEM, netCDF.
14018 : *
14019 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14020 : *
14021 : * @return true in case of success
14022 : * @since GDAL 3.8
14023 : */
14024 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14025 : {
14026 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14027 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14028 27 : return hAttr->m_poImpl->Rename(pszNewName);
14029 : }
14030 :
14031 : /************************************************************************/
14032 : /* GDALDimensionRelease() */
14033 : /************************************************************************/
14034 :
14035 : /** Release the GDAL in-memory object associated with a GDALDimension.
14036 : *
14037 : * Note: when applied on a object coming from a driver, this does not
14038 : * destroy the object in the file, database, etc...
14039 : */
14040 4914 : void GDALDimensionRelease(GDALDimensionH hDim)
14041 : {
14042 4914 : delete hDim;
14043 4914 : }
14044 :
14045 : /************************************************************************/
14046 : /* GDALDimensionGetName() */
14047 : /************************************************************************/
14048 :
14049 : /** Return dimension name.
14050 : *
14051 : * This is the same as the C++ method GDALDimension::GetName()
14052 : */
14053 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14054 : {
14055 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14056 284 : return hDim->m_poImpl->GetName().c_str();
14057 : }
14058 :
14059 : /************************************************************************/
14060 : /* GDALDimensionGetFullName() */
14061 : /************************************************************************/
14062 :
14063 : /** Return dimension full name.
14064 : *
14065 : * This is the same as the C++ method GDALDimension::GetFullName()
14066 : */
14067 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14068 : {
14069 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14070 80 : return hDim->m_poImpl->GetFullName().c_str();
14071 : }
14072 :
14073 : /************************************************************************/
14074 : /* GDALDimensionGetType() */
14075 : /************************************************************************/
14076 :
14077 : /** Return dimension type.
14078 : *
14079 : * This is the same as the C++ method GDALDimension::GetType()
14080 : */
14081 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14082 : {
14083 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14084 62 : return hDim->m_poImpl->GetType().c_str();
14085 : }
14086 :
14087 : /************************************************************************/
14088 : /* GDALDimensionGetDirection() */
14089 : /************************************************************************/
14090 :
14091 : /** Return dimension direction.
14092 : *
14093 : * This is the same as the C++ method GDALDimension::GetDirection()
14094 : */
14095 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14096 : {
14097 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14098 32 : return hDim->m_poImpl->GetDirection().c_str();
14099 : }
14100 :
14101 : /************************************************************************/
14102 : /* GDALDimensionGetSize() */
14103 : /************************************************************************/
14104 :
14105 : /** Return the size, that is the number of values along the dimension.
14106 : *
14107 : * This is the same as the C++ method GDALDimension::GetSize()
14108 : */
14109 3686 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14110 : {
14111 3686 : VALIDATE_POINTER1(hDim, __func__, 0);
14112 3686 : return hDim->m_poImpl->GetSize();
14113 : }
14114 :
14115 : /************************************************************************/
14116 : /* GDALDimensionGetIndexingVariable() */
14117 : /************************************************************************/
14118 :
14119 : /** Return the variable that is used to index the dimension (if there is one).
14120 : *
14121 : * This is the array, typically one-dimensional, describing the values taken
14122 : * by the dimension.
14123 : *
14124 : * The returned value should be freed with GDALMDArrayRelease().
14125 : *
14126 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14127 : */
14128 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14129 : {
14130 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14131 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
14132 118 : if (!var)
14133 10 : return nullptr;
14134 108 : return new GDALMDArrayHS(var);
14135 : }
14136 :
14137 : /************************************************************************/
14138 : /* GDALDimensionSetIndexingVariable() */
14139 : /************************************************************************/
14140 :
14141 : /** Set the variable that is used to index the dimension.
14142 : *
14143 : * This is the array, typically one-dimensional, describing the values taken
14144 : * by the dimension.
14145 : *
14146 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14147 : *
14148 : * @return TRUE in case of success.
14149 : */
14150 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14151 : {
14152 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14153 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14154 46 : : nullptr);
14155 : }
14156 :
14157 : /************************************************************************/
14158 : /* GDALDimensionRename() */
14159 : /************************************************************************/
14160 :
14161 : /** Rename the dimension.
14162 : *
14163 : * This is not implemented by all drivers.
14164 : *
14165 : * Drivers known to implement it: MEM, netCDF.
14166 : *
14167 : * This is the same as the C++ method GDALDimension::Rename()
14168 : *
14169 : * @return true in case of success
14170 : * @since GDAL 3.8
14171 : */
14172 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14173 : {
14174 31 : VALIDATE_POINTER1(hDim, __func__, false);
14175 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14176 31 : return hDim->m_poImpl->Rename(pszNewName);
14177 : }
14178 :
14179 : /************************************************************************/
14180 : /* GDALDatasetGetRootGroup() */
14181 : /************************************************************************/
14182 :
14183 : /** Return the root GDALGroup of this dataset.
14184 : *
14185 : * Only valid for multidimensional datasets.
14186 : *
14187 : * The returned value must be freed with GDALGroupRelease().
14188 : *
14189 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14190 : *
14191 : * @since GDAL 3.1
14192 : */
14193 1182 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14194 : {
14195 1182 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14196 1182 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14197 1182 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14198 : }
14199 :
14200 : /************************************************************************/
14201 : /* GDALRasterBandAsMDArray() */
14202 : /************************************************************************/
14203 :
14204 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14205 : *
14206 : * The band must be linked to a GDALDataset. If this dataset is not already
14207 : * marked as shared, it will be, so that the returned array holds a reference
14208 : * to it.
14209 : *
14210 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14211 : * returned array will have an associated indexing variable.
14212 : *
14213 : * The returned pointer must be released with GDALMDArrayRelease().
14214 : *
14215 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14216 : *
14217 : * @return a new array, or NULL.
14218 : *
14219 : * @since GDAL 3.1
14220 : */
14221 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14222 : {
14223 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14224 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14225 21 : if (!poArray)
14226 0 : return nullptr;
14227 21 : return new GDALMDArrayHS(poArray);
14228 : }
14229 :
14230 : /************************************************************************/
14231 : /* GDALMDArrayAsClassicDataset() */
14232 : /************************************************************************/
14233 :
14234 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14235 : *
14236 : * Only 2D or more arrays are supported.
14237 : *
14238 : * In the case of > 2D arrays, additional dimensions will be represented as
14239 : * raster bands.
14240 : *
14241 : * The "reverse" method is GDALRasterBand::AsMDArray().
14242 : *
14243 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14244 : *
14245 : * @param hArray Array.
14246 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14247 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14248 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14249 : */
14250 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14251 : size_t iYDim)
14252 : {
14253 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14254 0 : return GDALDataset::ToHandle(
14255 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14256 : }
14257 :
14258 : /************************************************************************/
14259 : /* GDALMDArrayAsClassicDatasetEx() */
14260 : /************************************************************************/
14261 :
14262 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14263 : *
14264 : * Only 2D or more arrays are supported.
14265 : *
14266 : * In the case of > 2D arrays, additional dimensions will be represented as
14267 : * raster bands.
14268 : *
14269 : * The "reverse" method is GDALRasterBand::AsMDArray().
14270 : *
14271 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14272 : * @param hArray Array.
14273 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14274 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14275 : * Ignored if the dimension count is 1.
14276 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14277 : * BAND_IMAGERY_METADATA option.
14278 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14279 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14280 : * @since GDAL 3.8
14281 : */
14282 93 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14283 : size_t iYDim, GDALGroupH hRootGroup,
14284 : CSLConstList papszOptions)
14285 : {
14286 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14287 186 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14288 186 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14289 186 : papszOptions));
14290 : }
14291 :
14292 : //! @cond Doxygen_Suppress
14293 :
14294 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14295 : const std::string &osName,
14296 : const std::string &osValue,
14297 180 : GDALExtendedDataTypeSubType eSubType)
14298 : : GDALAbstractMDArray(osParentName, osName),
14299 : GDALAttribute(osParentName, osName),
14300 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14301 : {
14302 180 : }
14303 :
14304 : const std::vector<std::shared_ptr<GDALDimension>> &
14305 30 : GDALAttributeString::GetDimensions() const
14306 : {
14307 30 : return m_dims;
14308 : }
14309 :
14310 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14311 : {
14312 21 : return m_dt;
14313 : }
14314 :
14315 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14316 : const GPtrDiff_t *,
14317 : const GDALExtendedDataType &bufferDataType,
14318 : void *pDstBuffer) const
14319 : {
14320 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14321 0 : return false;
14322 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14323 10 : if (!pszStr)
14324 0 : return false;
14325 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14326 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14327 10 : return true;
14328 : }
14329 :
14330 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14331 : const std::string &osName,
14332 66 : double dfValue)
14333 : : GDALAbstractMDArray(osParentName, osName),
14334 : GDALAttribute(osParentName, osName),
14335 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14336 : {
14337 66 : }
14338 :
14339 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14340 : const std::string &osName,
14341 27 : int nValue)
14342 : : GDALAbstractMDArray(osParentName, osName),
14343 : GDALAttribute(osParentName, osName),
14344 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14345 : {
14346 27 : }
14347 :
14348 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14349 : const std::string &osName,
14350 7 : const std::vector<GUInt32> &anValues)
14351 : : GDALAbstractMDArray(osParentName, osName),
14352 : GDALAttribute(osParentName, osName),
14353 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14354 : {
14355 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14356 14 : std::string(), "dim0", std::string(), std::string(),
14357 7 : m_anValuesUInt32.size()));
14358 7 : }
14359 :
14360 : const std::vector<std::shared_ptr<GDALDimension>> &
14361 14 : GDALAttributeNumeric::GetDimensions() const
14362 : {
14363 14 : return m_dims;
14364 : }
14365 :
14366 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14367 : {
14368 8 : return m_dt;
14369 : }
14370 :
14371 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14372 : const size_t *count, const GInt64 *arrayStep,
14373 : const GPtrDiff_t *bufferStride,
14374 : const GDALExtendedDataType &bufferDataType,
14375 : void *pDstBuffer) const
14376 : {
14377 4 : if (m_dims.empty())
14378 : {
14379 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14380 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14381 : bufferDataType);
14382 : else
14383 : {
14384 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14385 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14386 : bufferDataType);
14387 : }
14388 : }
14389 : else
14390 : {
14391 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14392 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14393 30 : for (size_t i = 0; i < count[0]; ++i)
14394 : {
14395 29 : GDALExtendedDataType::CopyValue(
14396 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14397 29 : i * arrayStep[0])],
14398 29 : m_dt, pabyDstBuffer, bufferDataType);
14399 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14400 : }
14401 : }
14402 4 : return true;
14403 : }
14404 :
14405 194 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14406 : const std::string &osParentName, const std::string &osName,
14407 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14408 194 : double dfIncrement, double dfOffsetInIncrement)
14409 : : GDALAbstractMDArray(osParentName, osName),
14410 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14411 : m_dfIncrement(dfIncrement),
14412 388 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14413 : {
14414 194 : }
14415 :
14416 194 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14417 : const std::string &osParentName, const std::string &osName,
14418 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14419 : double dfIncrement, double dfOffsetInIncrement)
14420 : {
14421 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14422 194 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14423 194 : poArray->SetSelf(poArray);
14424 194 : return poArray;
14425 : }
14426 :
14427 : const std::vector<std::shared_ptr<GDALDimension>> &
14428 788 : GDALMDArrayRegularlySpaced::GetDimensions() const
14429 : {
14430 788 : return m_dims;
14431 : }
14432 :
14433 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14434 : {
14435 316 : return m_dt;
14436 : }
14437 :
14438 : std::vector<std::shared_ptr<GDALAttribute>>
14439 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14440 : {
14441 4 : return m_attributes;
14442 : }
14443 :
14444 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14445 : const std::shared_ptr<GDALAttribute> &poAttr)
14446 : {
14447 0 : m_attributes.emplace_back(poAttr);
14448 0 : }
14449 :
14450 188 : bool GDALMDArrayRegularlySpaced::IRead(
14451 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14452 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14453 : void *pDstBuffer) const
14454 : {
14455 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14456 14719 : for (size_t i = 0; i < count[0]; i++)
14457 : {
14458 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
14459 14531 : m_dfOffsetInIncrement) *
14460 14531 : m_dfIncrement;
14461 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14462 : bufferDataType);
14463 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14464 : }
14465 188 : return true;
14466 : }
14467 :
14468 3045 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14469 : const std::string &osParentName, const std::string &osName,
14470 3045 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14471 3045 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14472 : {
14473 3045 : }
14474 :
14475 : std::shared_ptr<GDALMDArray>
14476 741 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14477 : {
14478 741 : return m_poIndexingVariable.lock();
14479 : }
14480 :
14481 : // cppcheck-suppress passedByValue
14482 508 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14483 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14484 : {
14485 508 : m_poIndexingVariable = poIndexingVariable;
14486 508 : return true;
14487 : }
14488 :
14489 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14490 : {
14491 33 : m_nSize = nNewSize;
14492 33 : }
14493 :
14494 : /************************************************************************/
14495 : /* GDALPamMultiDim::Private */
14496 : /************************************************************************/
14497 :
14498 : struct GDALPamMultiDim::Private
14499 : {
14500 : std::string m_osFilename{};
14501 : std::string m_osPamFilename{};
14502 :
14503 : struct Statistics
14504 : {
14505 : bool bHasStats = false;
14506 : bool bApproxStats = false;
14507 : double dfMin = 0;
14508 : double dfMax = 0;
14509 : double dfMean = 0;
14510 : double dfStdDev = 0;
14511 : GUInt64 nValidCount = 0;
14512 : };
14513 :
14514 : struct ArrayInfo
14515 : {
14516 : std::shared_ptr<OGRSpatialReference> poSRS{};
14517 : // cppcheck-suppress unusedStructMember
14518 : Statistics stats{};
14519 : };
14520 :
14521 : typedef std::pair<std::string, std::string> NameContext;
14522 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14523 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14524 : bool m_bDirty = false;
14525 : bool m_bLoaded = false;
14526 : };
14527 :
14528 : /************************************************************************/
14529 : /* GDALPamMultiDim */
14530 : /************************************************************************/
14531 :
14532 1461 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14533 1461 : : d(new Private())
14534 : {
14535 1461 : d->m_osFilename = osFilename;
14536 1461 : }
14537 :
14538 : /************************************************************************/
14539 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14540 : /************************************************************************/
14541 :
14542 1461 : GDALPamMultiDim::~GDALPamMultiDim()
14543 : {
14544 1461 : if (d->m_bDirty)
14545 30 : Save();
14546 1461 : }
14547 :
14548 : /************************************************************************/
14549 : /* GDALPamMultiDim::Load() */
14550 : /************************************************************************/
14551 :
14552 107 : void GDALPamMultiDim::Load()
14553 : {
14554 107 : if (d->m_bLoaded)
14555 96 : return;
14556 45 : d->m_bLoaded = true;
14557 :
14558 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14559 45 : d->m_osPamFilename =
14560 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14561 45 : CPLXMLTreeCloser oTree(nullptr);
14562 : {
14563 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14564 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14565 : }
14566 45 : if (!oTree)
14567 : {
14568 34 : return;
14569 : }
14570 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14571 11 : if (!poPAMMultiDim)
14572 0 : return;
14573 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14574 24 : psIter = psIter->psNext)
14575 : {
14576 24 : if (psIter->eType == CXT_Element &&
14577 24 : strcmp(psIter->pszValue, "Array") == 0)
14578 : {
14579 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14580 13 : if (!pszName)
14581 0 : continue;
14582 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14583 : const auto oKey =
14584 26 : std::pair<std::string, std::string>(pszName, pszContext);
14585 :
14586 : /* --------------------------------------------------------------------
14587 : */
14588 : /* Check for an SRS node. */
14589 : /* --------------------------------------------------------------------
14590 : */
14591 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14592 13 : if (psSRSNode)
14593 : {
14594 : std::shared_ptr<OGRSpatialReference> poSRS =
14595 6 : std::make_shared<OGRSpatialReference>();
14596 3 : poSRS->SetFromUserInput(
14597 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14598 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14599 3 : const char *pszMapping = CPLGetXMLValue(
14600 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14601 3 : if (pszMapping)
14602 : {
14603 : char **papszTokens =
14604 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14605 6 : std::vector<int> anMapping;
14606 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14607 : {
14608 6 : anMapping.push_back(atoi(papszTokens[i]));
14609 : }
14610 3 : CSLDestroy(papszTokens);
14611 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14612 : }
14613 : else
14614 : {
14615 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14616 : }
14617 :
14618 : const char *pszCoordinateEpoch =
14619 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14620 3 : if (pszCoordinateEpoch)
14621 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14622 :
14623 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14624 : }
14625 :
14626 : const CPLXMLNode *psStatistics =
14627 13 : CPLGetXMLNode(psIter, "Statistics");
14628 13 : if (psStatistics)
14629 : {
14630 7 : Private::Statistics sStats;
14631 7 : sStats.bHasStats = true;
14632 7 : sStats.bApproxStats = CPLTestBool(
14633 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14634 7 : sStats.dfMin =
14635 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14636 7 : sStats.dfMax =
14637 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14638 7 : sStats.dfMean =
14639 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14640 7 : sStats.dfStdDev =
14641 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14642 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14643 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14644 7 : d->m_oMapArray[oKey].stats = sStats;
14645 13 : }
14646 : }
14647 : else
14648 : {
14649 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14650 11 : psIter->psNext = nullptr;
14651 11 : d->m_apoOtherNodes.emplace_back(
14652 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14653 11 : psIter->psNext = psNextBackup;
14654 : }
14655 : }
14656 : }
14657 :
14658 : /************************************************************************/
14659 : /* GDALPamMultiDim::Save() */
14660 : /************************************************************************/
14661 :
14662 30 : void GDALPamMultiDim::Save()
14663 : {
14664 : CPLXMLTreeCloser oTree(
14665 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14666 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14667 : {
14668 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14669 : }
14670 112 : for (const auto &kv : d->m_oMapArray)
14671 : {
14672 : CPLXMLNode *psArrayNode =
14673 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14674 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14675 82 : if (!kv.first.second.empty())
14676 : {
14677 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14678 : kv.first.second.c_str());
14679 : }
14680 82 : if (kv.second.poSRS)
14681 : {
14682 71 : char *pszWKT = nullptr;
14683 : {
14684 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14685 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14686 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14687 : }
14688 : CPLXMLNode *psSRSNode =
14689 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14690 71 : CPLFree(pszWKT);
14691 : const auto &mapping =
14692 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14693 142 : CPLString osMapping;
14694 213 : for (size_t i = 0; i < mapping.size(); ++i)
14695 : {
14696 142 : if (!osMapping.empty())
14697 71 : osMapping += ",";
14698 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14699 : }
14700 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14701 : osMapping.c_str());
14702 :
14703 : const double dfCoordinateEpoch =
14704 71 : kv.second.poSRS->GetCoordinateEpoch();
14705 71 : if (dfCoordinateEpoch > 0)
14706 : {
14707 : std::string osCoordinateEpoch =
14708 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14709 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14710 : {
14711 6 : while (osCoordinateEpoch.back() == '0')
14712 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14713 : }
14714 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14715 : osCoordinateEpoch.c_str());
14716 : }
14717 : }
14718 :
14719 82 : if (kv.second.stats.bHasStats)
14720 : {
14721 : CPLXMLNode *psMDArray =
14722 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14723 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14724 8 : kv.second.stats.bApproxStats ? "1"
14725 : : "0");
14726 8 : CPLCreateXMLElementAndValue(
14727 : psMDArray, "Minimum",
14728 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14729 8 : CPLCreateXMLElementAndValue(
14730 : psMDArray, "Maximum",
14731 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14732 8 : CPLCreateXMLElementAndValue(
14733 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14734 8 : CPLCreateXMLElementAndValue(
14735 : psMDArray, "StdDev",
14736 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14737 8 : CPLCreateXMLElementAndValue(
14738 : psMDArray, "ValidSampleCount",
14739 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14740 : }
14741 : }
14742 :
14743 : int bSaved;
14744 60 : CPLErrorAccumulator oErrorAccumulator;
14745 : {
14746 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14747 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14748 : bSaved =
14749 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14750 : }
14751 :
14752 30 : const char *pszNewPam = nullptr;
14753 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14754 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14755 : {
14756 0 : CPLErrorReset();
14757 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14758 : }
14759 : else
14760 : {
14761 30 : oErrorAccumulator.ReplayErrors();
14762 : }
14763 30 : }
14764 :
14765 : /************************************************************************/
14766 : /* GDALPamMultiDim::GetSpatialRef() */
14767 : /************************************************************************/
14768 :
14769 : std::shared_ptr<OGRSpatialReference>
14770 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14771 : const std::string &osContext)
14772 : {
14773 10 : Load();
14774 : auto oIter =
14775 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14776 10 : if (oIter != d->m_oMapArray.end())
14777 2 : return oIter->second.poSRS;
14778 8 : return nullptr;
14779 : }
14780 :
14781 : /************************************************************************/
14782 : /* GDALPamMultiDim::SetSpatialRef() */
14783 : /************************************************************************/
14784 :
14785 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14786 : const std::string &osContext,
14787 : const OGRSpatialReference *poSRS)
14788 : {
14789 72 : Load();
14790 72 : d->m_bDirty = true;
14791 72 : if (poSRS && !poSRS->IsEmpty())
14792 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14793 : poSRS->Clone());
14794 : else
14795 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14796 1 : .poSRS.reset();
14797 72 : }
14798 :
14799 : /************************************************************************/
14800 : /* GetStatistics() */
14801 : /************************************************************************/
14802 :
14803 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14804 : const std::string &osContext,
14805 : bool bApproxOK, double *pdfMin,
14806 : double *pdfMax, double *pdfMean,
14807 : double *pdfStdDev, GUInt64 *pnValidCount)
14808 : {
14809 16 : Load();
14810 : auto oIter =
14811 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14812 16 : if (oIter == d->m_oMapArray.end())
14813 9 : return CE_Failure;
14814 7 : const auto &stats = oIter->second.stats;
14815 7 : if (!stats.bHasStats)
14816 1 : return CE_Failure;
14817 6 : if (!bApproxOK && stats.bApproxStats)
14818 0 : return CE_Failure;
14819 6 : if (pdfMin)
14820 6 : *pdfMin = stats.dfMin;
14821 6 : if (pdfMax)
14822 6 : *pdfMax = stats.dfMax;
14823 6 : if (pdfMean)
14824 6 : *pdfMean = stats.dfMean;
14825 6 : if (pdfStdDev)
14826 6 : *pdfStdDev = stats.dfStdDev;
14827 6 : if (pnValidCount)
14828 6 : *pnValidCount = stats.nValidCount;
14829 6 : return CE_None;
14830 : }
14831 :
14832 : /************************************************************************/
14833 : /* SetStatistics() */
14834 : /************************************************************************/
14835 :
14836 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14837 : const std::string &osContext,
14838 : bool bApproxStats, double dfMin,
14839 : double dfMax, double dfMean,
14840 : double dfStdDev, GUInt64 nValidCount)
14841 : {
14842 8 : Load();
14843 8 : d->m_bDirty = true;
14844 : auto &stats =
14845 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14846 8 : stats.bHasStats = true;
14847 8 : stats.bApproxStats = bApproxStats;
14848 8 : stats.dfMin = dfMin;
14849 8 : stats.dfMax = dfMax;
14850 8 : stats.dfMean = dfMean;
14851 8 : stats.dfStdDev = dfStdDev;
14852 8 : stats.nValidCount = nValidCount;
14853 8 : }
14854 :
14855 : /************************************************************************/
14856 : /* ClearStatistics() */
14857 : /************************************************************************/
14858 :
14859 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14860 : const std::string &osContext)
14861 : {
14862 0 : Load();
14863 0 : d->m_bDirty = true;
14864 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14865 : false;
14866 0 : }
14867 :
14868 : /************************************************************************/
14869 : /* ClearStatistics() */
14870 : /************************************************************************/
14871 :
14872 1 : void GDALPamMultiDim::ClearStatistics()
14873 : {
14874 1 : Load();
14875 1 : d->m_bDirty = true;
14876 3 : for (auto &kv : d->m_oMapArray)
14877 2 : kv.second.stats.bHasStats = false;
14878 1 : }
14879 :
14880 : /************************************************************************/
14881 : /* GetPAM() */
14882 : /************************************************************************/
14883 :
14884 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14885 815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14886 : {
14887 815 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14888 815 : if (poPamArray)
14889 567 : return poPamArray->GetPAM();
14890 248 : return nullptr;
14891 : }
14892 :
14893 : /************************************************************************/
14894 : /* GDALPamMDArray */
14895 : /************************************************************************/
14896 :
14897 3782 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14898 : const std::string &osName,
14899 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14900 0 : const std::string &osContext)
14901 : :
14902 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14903 : GDALAbstractMDArray(osParentName, osName),
14904 : #endif
14905 3782 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14906 : {
14907 3782 : }
14908 :
14909 : /************************************************************************/
14910 : /* GDALPamMDArray::SetSpatialRef() */
14911 : /************************************************************************/
14912 :
14913 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14914 : {
14915 72 : if (!m_poPam)
14916 0 : return false;
14917 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14918 72 : return true;
14919 : }
14920 :
14921 : /************************************************************************/
14922 : /* GDALPamMDArray::GetSpatialRef() */
14923 : /************************************************************************/
14924 :
14925 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14926 : {
14927 10 : if (!m_poPam)
14928 0 : return nullptr;
14929 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14930 : }
14931 :
14932 : /************************************************************************/
14933 : /* GetStatistics() */
14934 : /************************************************************************/
14935 :
14936 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14937 : double *pdfMin, double *pdfMax,
14938 : double *pdfMean, double *pdfStdDev,
14939 : GUInt64 *pnValidCount,
14940 : GDALProgressFunc pfnProgress,
14941 : void *pProgressData)
14942 : {
14943 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14944 : bApproxOK, pdfMin, pdfMax, pdfMean,
14945 16 : pdfStdDev, pnValidCount) == CE_None)
14946 : {
14947 6 : return CE_None;
14948 : }
14949 10 : if (!bForce)
14950 4 : return CE_Warning;
14951 :
14952 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14953 : pdfMean, pdfStdDev, pnValidCount,
14954 6 : pfnProgress, pProgressData);
14955 : }
14956 :
14957 : /************************************************************************/
14958 : /* SetStatistics() */
14959 : /************************************************************************/
14960 :
14961 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14962 : double dfMax, double dfMean, double dfStdDev,
14963 : GUInt64 nValidCount,
14964 : CSLConstList /* papszOptions */)
14965 : {
14966 8 : if (!m_poPam)
14967 0 : return false;
14968 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14969 : dfMax, dfMean, dfStdDev, nValidCount);
14970 8 : return true;
14971 : }
14972 :
14973 : /************************************************************************/
14974 : /* ClearStatistics() */
14975 : /************************************************************************/
14976 :
14977 0 : void GDALPamMDArray::ClearStatistics()
14978 : {
14979 0 : if (!m_poPam)
14980 0 : return;
14981 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14982 : }
14983 :
14984 : //! @endcond
|