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_pam_multidim.h"
31 : #include "gdal_rat.h"
32 : #include "gdal_utils.h"
33 : #include "cpl_safemaths.hpp"
34 : #include "memmultidim.h"
35 : #include "ogrsf_frmts.h"
36 : #include "gdalmultidim_priv.h"
37 :
38 : #if defined(__clang__) || defined(_MSC_VER)
39 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40 : #endif
41 :
42 : /************************************************************************/
43 : /* GDALMDArrayUnscaled */
44 : /************************************************************************/
45 :
46 : class GDALMDArrayUnscaled final : public GDALPamMDArray
47 : {
48 : private:
49 : std::shared_ptr<GDALMDArray> m_poParent{};
50 : const GDALExtendedDataType m_dt;
51 : bool m_bHasNoData;
52 : const double m_dfScale;
53 : const double m_dfOffset;
54 : std::vector<GByte> m_abyRawNoData{};
55 :
56 : protected:
57 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58 : double dfScale, double dfOffset,
59 : double dfOverriddenDstNodata, GDALDataType eDT)
60 26 : : GDALAbstractMDArray(std::string(),
61 26 : "Unscaled view of " + poParent->GetFullName()),
62 : GDALPamMDArray(
63 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
64 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65 13 : m_poParent(std::move(poParent)),
66 : m_dt(GDALExtendedDataType::Create(eDT)),
67 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
69 : {
70 13 : m_abyRawNoData.resize(m_dt.GetSize());
71 : const auto eNonComplexDT =
72 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73 26 : GDALCopyWords64(
74 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77 13 : }
78 :
79 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81 : const GDALExtendedDataType &bufferDataType,
82 : void *pDstBuffer) const override;
83 :
84 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86 : const GDALExtendedDataType &bufferDataType,
87 : const void *pSrcBuffer) override;
88 :
89 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90 : CSLConstList papszOptions) const override
91 : {
92 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93 : }
94 :
95 : public:
96 : static std::shared_ptr<GDALMDArrayUnscaled>
97 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98 : double dfOffset, double dfDstNodata, GDALDataType eDT)
99 : {
100 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102 13 : newAr->SetSelf(newAr);
103 13 : return newAr;
104 : }
105 :
106 1 : bool IsWritable() const override
107 : {
108 1 : return m_poParent->IsWritable();
109 : }
110 :
111 15 : const std::string &GetFilename() const override
112 : {
113 15 : return m_poParent->GetFilename();
114 : }
115 :
116 : const std::vector<std::shared_ptr<GDALDimension>> &
117 220 : GetDimensions() const override
118 : {
119 220 : return m_poParent->GetDimensions();
120 : }
121 :
122 103 : const GDALExtendedDataType &GetDataType() const override
123 : {
124 103 : return m_dt;
125 : }
126 :
127 1 : const std::string &GetUnit() const override
128 : {
129 1 : return m_poParent->GetUnit();
130 : }
131 :
132 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133 : {
134 1 : return m_poParent->GetSpatialRef();
135 : }
136 :
137 6 : const void *GetRawNoDataValue() const override
138 : {
139 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140 : }
141 :
142 1 : bool SetRawNoDataValue(const void *pRawNoData) override
143 : {
144 1 : m_bHasNoData = true;
145 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146 1 : return true;
147 : }
148 :
149 4 : std::vector<GUInt64> GetBlockSize() const override
150 : {
151 4 : return m_poParent->GetBlockSize();
152 : }
153 :
154 : std::shared_ptr<GDALAttribute>
155 0 : GetAttribute(const std::string &osName) const override
156 : {
157 0 : return m_poParent->GetAttribute(osName);
158 : }
159 :
160 : std::vector<std::shared_ptr<GDALAttribute>>
161 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
162 : {
163 1 : return m_poParent->GetAttributes(papszOptions);
164 : }
165 :
166 0 : bool SetUnit(const std::string &osUnit) override
167 : {
168 0 : return m_poParent->SetUnit(osUnit);
169 : }
170 :
171 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172 : {
173 0 : return m_poParent->SetSpatialRef(poSRS);
174 : }
175 :
176 : std::shared_ptr<GDALAttribute>
177 1 : CreateAttribute(const std::string &osName,
178 : const std::vector<GUInt64> &anDimensions,
179 : const GDALExtendedDataType &oDataType,
180 : CSLConstList papszOptions = nullptr) override
181 : {
182 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183 1 : papszOptions);
184 : }
185 : };
186 :
187 : /************************************************************************/
188 : /* ~GDALIHasAttribute() */
189 : /************************************************************************/
190 :
191 : GDALIHasAttribute::~GDALIHasAttribute() = default;
192 :
193 : /************************************************************************/
194 : /* GetAttribute() */
195 : /************************************************************************/
196 :
197 : /** Return an attribute by its name.
198 : *
199 : * If the attribute does not exist, nullptr should be silently returned.
200 : *
201 : * @note Driver implementation: this method will fallback to
202 : * GetAttributeFromAttributes() is not explicitly implemented
203 : *
204 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
205 : *
206 : * This is the same as the C function GDALGroupGetAttribute() or
207 : * GDALMDArrayGetAttribute().
208 : *
209 : * @param osName Attribute name
210 : * @return the attribute, or nullptr if it does not exist or an error occurred.
211 : */
212 : std::shared_ptr<GDALAttribute>
213 10737 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
214 : {
215 10737 : return GetAttributeFromAttributes(osName);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetAttributeFromAttributes() */
220 : /************************************************************************/
221 :
222 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
223 : */
224 : std::shared_ptr<GDALAttribute>
225 10737 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226 : {
227 21474 : auto attrs(GetAttributes());
228 85091 : for (const auto &attr : attrs)
229 : {
230 83301 : if (attr->GetName() == osName)
231 8947 : return attr;
232 : }
233 1790 : return nullptr;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetAttributes() */
238 : /************************************************************************/
239 :
240 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241 : *
242 : * If the attribute does not exist, nullptr should be silently returned.
243 : *
244 : * @note Driver implementation: optionally implemented. If implemented,
245 : * GetAttribute() should also be implemented.
246 : *
247 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
248 : *
249 : * This is the same as the C function GDALGroupGetAttributes() or
250 : * GDALMDArrayGetAttributes().
251 :
252 : * @param papszOptions Driver specific options determining how attributes
253 : * should be retrieved. Pass nullptr for default behavior.
254 : *
255 : * @return the attributes.
256 : */
257 : std::vector<std::shared_ptr<GDALAttribute>>
258 203 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259 : {
260 203 : return {};
261 : }
262 :
263 : /************************************************************************/
264 : /* CreateAttribute() */
265 : /************************************************************************/
266 :
267 : /** Create an attribute within a GDALMDArray or GDALGroup.
268 : *
269 : * The attribute might not be "physically" created until a value is written
270 : * into it.
271 : *
272 : * Optionally implemented.
273 : *
274 : * Drivers known to implement it: MEM, netCDF
275 : *
276 : * This is the same as the C function GDALGroupCreateAttribute() or
277 : * GDALMDArrayCreateAttribute()
278 : *
279 : * @param osName Attribute name.
280 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
281 : * dimension first to the fastest varying dimension last.
282 : * Empty for a scalar attribute (common case)
283 : * @param oDataType Attribute data type.
284 : * @param papszOptions Driver specific options determining how the attribute.
285 : * should be created.
286 : *
287 : * @return the new attribute, or nullptr if case of error
288 : */
289 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290 : CPL_UNUSED const std::string &osName,
291 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292 : CPL_UNUSED const GDALExtendedDataType &oDataType,
293 : CPL_UNUSED CSLConstList papszOptions)
294 : {
295 0 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "CreateAttribute() not implemented");
297 0 : return nullptr;
298 : }
299 :
300 : /************************************************************************/
301 : /* DeleteAttribute() */
302 : /************************************************************************/
303 :
304 : /** Delete an attribute from a GDALMDArray or GDALGroup.
305 : *
306 : * Optionally implemented.
307 : *
308 : * After this call, if a previously obtained instance of the deleted object
309 : * is still alive, no method other than for freeing it should be invoked.
310 : *
311 : * Drivers known to implement it: MEM, netCDF
312 : *
313 : * This is the same as the C function GDALGroupDeleteAttribute() or
314 : * GDALMDArrayDeleteAttribute()
315 : *
316 : * @param osName Attribute name.
317 : * @param papszOptions Driver specific options determining how the attribute.
318 : * should be deleted.
319 : *
320 : * @return true in case of success
321 : * @since GDAL 3.8
322 : */
323 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324 : CPL_UNUSED CSLConstList papszOptions)
325 : {
326 0 : CPLError(CE_Failure, CPLE_NotSupported,
327 : "DeleteAttribute() not implemented");
328 0 : return false;
329 : }
330 :
331 : /************************************************************************/
332 : /* GDALGroup() */
333 : /************************************************************************/
334 :
335 : //! @cond Doxygen_Suppress
336 13184 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 13184 : const std::string &osContext)
338 13184 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 26368 : !osParentName.empty()
341 20019 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 33203 : m_osContext(osContext)
344 : {
345 13184 : }
346 :
347 : //! @endcond
348 :
349 : /************************************************************************/
350 : /* ~GDALGroup() */
351 : /************************************************************************/
352 :
353 : GDALGroup::~GDALGroup() = default;
354 :
355 : /************************************************************************/
356 : /* GetMDArrayNames() */
357 : /************************************************************************/
358 :
359 : /** Return the list of multidimensional array names contained in this group.
360 : *
361 : * @note Driver implementation: optionally implemented. If implemented,
362 : * OpenMDArray() should also be implemented.
363 : *
364 : * Drivers known to implement it: MEM, netCDF.
365 : *
366 : * This is the same as the C function GDALGroupGetMDArrayNames().
367 : *
368 : * @param papszOptions Driver specific options determining how arrays
369 : * should be retrieved. Pass nullptr for default behavior.
370 : *
371 : * @return the array names.
372 : */
373 : std::vector<std::string>
374 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375 : {
376 0 : return {};
377 : }
378 :
379 : /************************************************************************/
380 : /* GetMDArrayFullNamesRecursive() */
381 : /************************************************************************/
382 :
383 : /** Return the list of multidimensional array full names contained in this
384 : * group and its subgroups.
385 : *
386 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387 : *
388 : * @param papszGroupOptions Driver specific options determining how groups
389 : * should be retrieved. Pass nullptr for default behavior.
390 : * @param papszArrayOptions Driver specific options determining how arrays
391 : * should be retrieved. Pass nullptr for default behavior.
392 : *
393 : * @return the array full names.
394 : *
395 : * @since 3.11
396 : */
397 : std::vector<std::string>
398 10 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399 : CSLConstList papszArrayOptions) const
400 : {
401 10 : std::vector<std::string> ret;
402 20 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
403 10 : stackGroups.push_back(nullptr); // nullptr means this
404 23 : while (!stackGroups.empty())
405 : {
406 26 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407 13 : stackGroups.erase(stackGroups.begin());
408 13 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409 31 : for (const std::string &arrayName :
410 75 : poCurGroup->GetMDArrayNames(papszArrayOptions))
411 : {
412 62 : std::string osFullName = poCurGroup->GetFullName();
413 31 : if (!osFullName.empty() && osFullName.back() != '/')
414 3 : osFullName += '/';
415 31 : osFullName += arrayName;
416 31 : ret.push_back(std::move(osFullName));
417 : }
418 13 : auto insertionPoint = stackGroups.begin();
419 3 : for (const auto &osSubGroup :
420 19 : poCurGroup->GetGroupNames(papszGroupOptions))
421 : {
422 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423 3 : if (poSubGroup)
424 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
425 : }
426 : }
427 :
428 20 : return ret;
429 : }
430 :
431 : /************************************************************************/
432 : /* OpenMDArray() */
433 : /************************************************************************/
434 :
435 : /** Open and return a multidimensional array.
436 : *
437 : * @note Driver implementation: optionally implemented. If implemented,
438 : * GetMDArrayNames() should also be implemented.
439 : *
440 : * Drivers known to implement it: MEM, netCDF.
441 : *
442 : * This is the same as the C function GDALGroupOpenMDArray().
443 : *
444 : * @param osName Array name.
445 : * @param papszOptions Driver specific options determining how the array should
446 : * be opened. Pass nullptr for default behavior.
447 : *
448 : * @return the array, or nullptr.
449 : */
450 : std::shared_ptr<GDALMDArray>
451 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452 : CPL_UNUSED CSLConstList papszOptions) const
453 : {
454 0 : return nullptr;
455 : }
456 :
457 : /************************************************************************/
458 : /* GetGroupNames() */
459 : /************************************************************************/
460 :
461 : /** Return the list of sub-groups contained in this group.
462 : *
463 : * @note Driver implementation: optionally implemented. If implemented,
464 : * OpenGroup() should also be implemented.
465 : *
466 : * Drivers known to implement it: MEM, netCDF.
467 : *
468 : * This is the same as the C function GDALGroupGetGroupNames().
469 : *
470 : * @param papszOptions Driver specific options determining how groups
471 : * should be retrieved. Pass nullptr for default behavior.
472 : *
473 : * @return the group names.
474 : */
475 : std::vector<std::string>
476 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477 : {
478 4 : return {};
479 : }
480 :
481 : /************************************************************************/
482 : /* OpenGroup() */
483 : /************************************************************************/
484 :
485 : /** Open and return a sub-group.
486 : *
487 : * @note Driver implementation: optionally implemented. If implemented,
488 : * GetGroupNames() should also be implemented.
489 : *
490 : * Drivers known to implement it: MEM, netCDF.
491 : *
492 : * This is the same as the C function GDALGroupOpenGroup().
493 : *
494 : * @param osName Sub-group name.
495 : * @param papszOptions Driver specific options determining how the sub-group
496 : * should be opened. Pass nullptr for default behavior.
497 : *
498 : * @return the group, or nullptr.
499 : */
500 : std::shared_ptr<GDALGroup>
501 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502 : CPL_UNUSED CSLConstList papszOptions) const
503 : {
504 4 : return nullptr;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetVectorLayerNames() */
509 : /************************************************************************/
510 :
511 : /** Return the list of layer names contained in this group.
512 : *
513 : * @note Driver implementation: optionally implemented. If implemented,
514 : * OpenVectorLayer() should also be implemented.
515 : *
516 : * Drivers known to implement it: OpenFileGDB, FileGDB
517 : *
518 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519 : * GDALDataset::GetLayer() should then be used.
520 : *
521 : * This is the same as the C function GDALGroupGetVectorLayerNames().
522 : *
523 : * @param papszOptions Driver specific options determining how layers
524 : * should be retrieved. Pass nullptr for default behavior.
525 : *
526 : * @return the vector layer names.
527 : * @since GDAL 3.4
528 : */
529 : std::vector<std::string>
530 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 1 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* OpenVectorLayer() */
537 : /************************************************************************/
538 :
539 : /** Open and return a vector layer.
540 : *
541 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542 : * lifetime of the returned OGRLayer* is linked to the one of the owner
543 : * dataset (contrary to the general design of this class where objects can be
544 : * used independently of the object that returned them)
545 : *
546 : * @note Driver implementation: optionally implemented. If implemented,
547 : * GetVectorLayerNames() should also be implemented.
548 : *
549 : * Drivers known to implement it: MEM, netCDF.
550 : *
551 : * This is the same as the C function GDALGroupOpenVectorLayer().
552 : *
553 : * @param osName Vector layer name.
554 : * @param papszOptions Driver specific options determining how the layer should
555 : * be opened. Pass nullptr for default behavior.
556 : *
557 : * @return the group, or nullptr.
558 : */
559 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560 : CPL_UNUSED CSLConstList papszOptions) const
561 : {
562 2 : return nullptr;
563 : }
564 :
565 : /************************************************************************/
566 : /* GetDimensions() */
567 : /************************************************************************/
568 :
569 : /** Return the list of dimensions contained in this group and used by its
570 : * arrays.
571 : *
572 : * This is for dimensions that can potentially be used by several arrays.
573 : * Not all drivers might implement this. To retrieve the dimensions used by
574 : * a specific array, use GDALMDArray::GetDimensions().
575 : *
576 : * Drivers known to implement it: MEM, netCDF
577 : *
578 : * This is the same as the C function GDALGroupGetDimensions().
579 : *
580 : * @param papszOptions Driver specific options determining how groups
581 : * should be retrieved. Pass nullptr for default behavior.
582 : *
583 : * @return the dimensions.
584 : */
585 : std::vector<std::shared_ptr<GDALDimension>>
586 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587 : {
588 11 : return {};
589 : }
590 :
591 : /************************************************************************/
592 : /* GetStructuralInfo() */
593 : /************************************************************************/
594 :
595 : /** Return structural information on the group.
596 : *
597 : * This may be the compression, etc..
598 : *
599 : * The return value should not be freed and is valid until GDALGroup is
600 : * released or this function called again.
601 : *
602 : * This is the same as the C function GDALGroupGetStructuralInfo().
603 : */
604 46 : CSLConstList GDALGroup::GetStructuralInfo() const
605 : {
606 46 : return nullptr;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateGroup() */
611 : /************************************************************************/
612 :
613 : /** Create a sub-group within a group.
614 : *
615 : * Optionally implemented by drivers.
616 : *
617 : * Drivers known to implement it: MEM, netCDF
618 : *
619 : * This is the same as the C function GDALGroupCreateGroup().
620 : *
621 : * @param osName Sub-group name.
622 : * @param papszOptions Driver specific options determining how the sub-group
623 : * should be created.
624 : *
625 : * @return the new sub-group, or nullptr in case of error.
626 : */
627 : std::shared_ptr<GDALGroup>
628 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629 : CPL_UNUSED CSLConstList papszOptions)
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632 0 : return nullptr;
633 : }
634 :
635 : /************************************************************************/
636 : /* DeleteGroup() */
637 : /************************************************************************/
638 :
639 : /** Delete a sub-group from a group.
640 : *
641 : * Optionally implemented.
642 : *
643 : * After this call, if a previously obtained instance of the deleted object
644 : * is still alive, no method other than for freeing it should be invoked.
645 : *
646 : * Drivers known to implement it: MEM, Zarr
647 : *
648 : * This is the same as the C function GDALGroupDeleteGroup().
649 : *
650 : * @param osName Sub-group name.
651 : * @param papszOptions Driver specific options determining how the group.
652 : * should be deleted.
653 : *
654 : * @return true in case of success
655 : * @since GDAL 3.8
656 : */
657 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658 : CPL_UNUSED CSLConstList papszOptions)
659 : {
660 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661 0 : return false;
662 : }
663 :
664 : /************************************************************************/
665 : /* CreateDimension() */
666 : /************************************************************************/
667 :
668 : /** Create a dimension within a group.
669 : *
670 : * @note Driver implementation: drivers supporting CreateDimension() should
671 : * implement this method, but do not have necessarily to implement
672 : * GDALGroup::GetDimensions().
673 : *
674 : * Drivers known to implement it: MEM, netCDF
675 : *
676 : * This is the same as the C function GDALGroupCreateDimension().
677 : *
678 : * @param osName Dimension name.
679 : * @param osType Dimension type (might be empty, and ignored by drivers)
680 : * @param osDirection Dimension direction (might be empty, and ignored by
681 : * drivers)
682 : * @param nSize Number of values indexed by this dimension. Should be > 0.
683 : * @param papszOptions Driver specific options determining how the dimension
684 : * should be created.
685 : *
686 : * @return the new dimension, or nullptr if case of error
687 : */
688 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691 : CPL_UNUSED CSLConstList papszOptions)
692 : {
693 0 : CPLError(CE_Failure, CPLE_NotSupported,
694 : "CreateDimension() not implemented");
695 0 : return nullptr;
696 : }
697 :
698 : /************************************************************************/
699 : /* CreateMDArray() */
700 : /************************************************************************/
701 :
702 : /** Create a multidimensional array within a group.
703 : *
704 : * It is recommended that the GDALDimension objects passed in aoDimensions
705 : * belong to this group, either by retrieving them with GetDimensions()
706 : * or creating a new one with CreateDimension().
707 : *
708 : * Optionally implemented.
709 : *
710 : * Drivers known to implement it: MEM, netCDF
711 : *
712 : * This is the same as the C function GDALGroupCreateMDArray().
713 : *
714 : * @note Driver implementation: drivers should take into account the possibility
715 : * that GDALDimension object passed in aoDimensions might belong to a different
716 : * group / dataset / driver and act accordingly.
717 : *
718 : * @param osName Array name.
719 : * @param aoDimensions List of dimensions, ordered from the slowest varying
720 : * dimension first to the fastest varying dimension last.
721 : * Might be empty for a scalar array (if supported by
722 : * driver)
723 : * @param oDataType Array data type.
724 : * @param papszOptions Driver specific options determining how the array
725 : * should be created.
726 : *
727 : * @return the new array, or nullptr in case of error
728 : */
729 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730 : CPL_UNUSED const std::string &osName,
731 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732 : CPL_UNUSED const GDALExtendedDataType &oDataType,
733 : CPL_UNUSED CSLConstList papszOptions)
734 : {
735 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736 0 : return nullptr;
737 : }
738 :
739 : /************************************************************************/
740 : /* DeleteMDArray() */
741 : /************************************************************************/
742 :
743 : /** Delete an array from a group.
744 : *
745 : * Optionally implemented.
746 : *
747 : * After this call, if a previously obtained instance of the deleted object
748 : * is still alive, no method other than for freeing it should be invoked.
749 : *
750 : * Drivers known to implement it: MEM, Zarr
751 : *
752 : * This is the same as the C function GDALGroupDeleteMDArray().
753 : *
754 : * @param osName Arrayname.
755 : * @param papszOptions Driver specific options determining how the array.
756 : * should be deleted.
757 : *
758 : * @return true in case of success
759 : * @since GDAL 3.8
760 : */
761 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762 : CPL_UNUSED CSLConstList papszOptions)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765 0 : return false;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetTotalCopyCost() */
770 : /************************************************************************/
771 :
772 : /** Return a total "cost" to copy the group.
773 : *
774 : * Used as a parameter for CopFrom()
775 : */
776 33 : GUInt64 GDALGroup::GetTotalCopyCost() const
777 : {
778 33 : GUInt64 nCost = COPY_COST;
779 33 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780 :
781 66 : auto groupNames = GetGroupNames();
782 39 : for (const auto &name : groupNames)
783 : {
784 12 : auto subGroup = OpenGroup(name);
785 6 : if (subGroup)
786 : {
787 6 : nCost += subGroup->GetTotalCopyCost();
788 : }
789 : }
790 :
791 33 : auto arrayNames = GetMDArrayNames();
792 102 : for (const auto &name : arrayNames)
793 : {
794 138 : auto array = OpenMDArray(name);
795 69 : if (array)
796 : {
797 69 : nCost += array->GetTotalCopyCost();
798 : }
799 : }
800 66 : return nCost;
801 : }
802 :
803 : /************************************************************************/
804 : /* CopyFrom() */
805 : /************************************************************************/
806 :
807 : /** Copy the content of a group into a new (generally empty) group.
808 : *
809 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
811 : * of some output drivers this is not recommended)
812 : * @param poSrcGroup Source group. Must NOT be nullptr.
813 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
814 : * stop the copy. In relaxed mode, the copy will be attempted to
815 : * be pursued.
816 : * @param nCurCost Should be provided as a variable initially set to 0.
817 : * @param nTotalCost Total cost from GetTotalCopyCost().
818 : * @param pfnProgress Progress callback, or nullptr.
819 : * @param pProgressData Progress user data, or nullptr.
820 : * @param papszOptions Creation options. Currently, only array creation
821 : * options are supported. They must be prefixed with
822 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
823 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
824 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
825 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
826 : * Restriction to arrays of a given name is done with adding
827 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
828 : * a full qualified name.
829 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
830 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
831 : * scaled to UInt16 with scale and offset values being computed from the minimum
832 : * and maximum of the source array. The integer data type used can be set with
833 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
834 : *
835 : * @return true in case of success (or partial success if bStrict == false).
836 : */
837 33 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
838 : GDALDataset *poSrcDS,
839 : const std::shared_ptr<GDALGroup> &poSrcGroup,
840 : bool bStrict, GUInt64 &nCurCost,
841 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
842 : void *pProgressData, CSLConstList papszOptions)
843 : {
844 33 : if (pfnProgress == nullptr)
845 0 : pfnProgress = GDALDummyProgress;
846 :
847 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
848 : if (!(x)) \
849 : { \
850 : if (bStrict) \
851 : return false; \
852 : continue; \
853 : } \
854 : (void)0
855 :
856 : try
857 : {
858 33 : nCurCost += GDALGroup::COPY_COST;
859 :
860 66 : const auto srcDims = poSrcGroup->GetDimensions();
861 : std::map<std::string, std::shared_ptr<GDALDimension>>
862 66 : mapExistingDstDims;
863 66 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
864 87 : for (const auto &dim : srcDims)
865 : {
866 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
867 54 : dim->GetDirection(), dim->GetSize());
868 54 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
869 54 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
870 108 : auto poIndexingVarSrc(dim->GetIndexingVariable());
871 54 : if (poIndexingVarSrc)
872 : {
873 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
874 35 : ->GetName()] =
875 70 : dim->GetName();
876 : }
877 : }
878 :
879 66 : auto attrs = poSrcGroup->GetAttributes();
880 51 : for (const auto &attr : attrs)
881 : {
882 : auto dstAttr =
883 18 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
884 36 : attr->GetDataType());
885 18 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
886 18 : auto raw(attr->ReadAsRaw());
887 18 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
888 0 : return false;
889 : }
890 33 : if (!attrs.empty())
891 : {
892 8 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
893 8 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
894 0 : return false;
895 : }
896 :
897 : const auto CopyArray =
898 69 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
899 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
900 : papszOptions, bStrict, &nCurCost,
901 616 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
902 : {
903 : // Map source dimensions to target dimensions
904 138 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
905 69 : const auto &srcArrayDims(srcArray->GetDimensions());
906 172 : for (const auto &dim : srcArrayDims)
907 : {
908 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
909 103 : dim->GetFullName());
910 103 : if (dstDim && dstDim->GetSize() == dim->GetSize())
911 : {
912 93 : dstArrayDims.emplace_back(dstDim);
913 : }
914 : else
915 : {
916 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
917 19 : if (oIter != mapExistingDstDims.end() &&
918 9 : oIter->second->GetSize() == dim->GetSize())
919 : {
920 8 : dstArrayDims.emplace_back(oIter->second);
921 : }
922 : else
923 : {
924 2 : std::string newDimName;
925 2 : if (oIter == mapExistingDstDims.end())
926 : {
927 1 : newDimName = dim->GetName();
928 : }
929 : else
930 : {
931 1 : std::string newDimNamePrefix(srcArray->GetName() +
932 3 : '_' + dim->GetName());
933 1 : newDimName = newDimNamePrefix;
934 1 : int nIterCount = 2;
935 0 : while (
936 1 : cpl::contains(mapExistingDstDims, newDimName))
937 : {
938 0 : newDimName = newDimNamePrefix +
939 0 : CPLSPrintf("_%d", nIterCount);
940 0 : nIterCount++;
941 : }
942 : }
943 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
944 : dim->GetDirection(),
945 4 : dim->GetSize());
946 2 : if (!dstDim)
947 0 : return false;
948 2 : mapExistingDstDims[newDimName] = dstDim;
949 2 : dstArrayDims.emplace_back(dstDim);
950 : }
951 : }
952 : }
953 :
954 138 : CPLStringList aosArrayCO;
955 69 : bool bAutoScale = false;
956 69 : GDALDataType eAutoScaleType = GDT_UInt16;
957 76 : for (const char *pszItem : cpl::Iterate(papszOptions))
958 : {
959 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
960 : {
961 7 : const char *pszOption = pszItem + strlen("ARRAY:");
962 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
963 : {
964 1 : const char *pszNext = strchr(pszOption, ':');
965 1 : if (pszNext != nullptr)
966 : {
967 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
968 1 : if (static_cast<size_t>(nDim) ==
969 1 : dstArrayDims.size())
970 : {
971 1 : pszOption = pszNext + 1;
972 : }
973 : else
974 : {
975 0 : pszOption = nullptr;
976 : }
977 : }
978 : }
979 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
980 : {
981 2 : const char *pszName = pszOption + strlen("IF(NAME=");
982 2 : const char *pszNext = strchr(pszName, ':');
983 2 : if (pszNext != nullptr && pszNext > pszName &&
984 2 : pszNext[-1] == ')')
985 : {
986 4 : CPLString osName;
987 2 : osName.assign(pszName, pszNext - pszName - 1);
988 3 : if (osName == srcArray->GetName() ||
989 1 : osName == srcArray->GetFullName())
990 : {
991 2 : pszOption = pszNext + 1;
992 : }
993 : else
994 : {
995 0 : pszOption = nullptr;
996 : }
997 : }
998 : }
999 7 : if (pszOption)
1000 : {
1001 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1002 : {
1003 : bAutoScale =
1004 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1005 : }
1006 5 : else if (STARTS_WITH_CI(pszOption,
1007 : "AUTOSCALE_DATA_TYPE="))
1008 : {
1009 1 : const char *pszDataType =
1010 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1011 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1012 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1013 1 : GDALDataTypeIsFloating(eAutoScaleType))
1014 : {
1015 0 : CPLError(CE_Failure, CPLE_NotSupported,
1016 : "Unsupported value for "
1017 : "AUTOSCALE_DATA_TYPE");
1018 0 : return false;
1019 : }
1020 : }
1021 : else
1022 : {
1023 4 : aosArrayCO.AddString(pszOption);
1024 : }
1025 : }
1026 : }
1027 : }
1028 :
1029 69 : if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
1030 : {
1031 136 : const auto anBlockSize = srcArray->GetBlockSize();
1032 136 : std::string osBlockSize;
1033 74 : for (auto v : anBlockSize)
1034 : {
1035 69 : if (v == 0)
1036 : {
1037 63 : osBlockSize.clear();
1038 63 : break;
1039 : }
1040 6 : if (!osBlockSize.empty())
1041 2 : osBlockSize += ',';
1042 6 : osBlockSize += std::to_string(v);
1043 : }
1044 68 : if (!osBlockSize.empty())
1045 3 : aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
1046 : }
1047 :
1048 : auto oIterDimName =
1049 69 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1050 69 : const auto &srcArrayType = srcArray->GetDataType();
1051 :
1052 69 : std::shared_ptr<GDALMDArray> dstArray;
1053 :
1054 : // Only autoscale non-indexing variables
1055 69 : bool bHasOffset = false;
1056 69 : bool bHasScale = false;
1057 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1058 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1059 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1060 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1061 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1062 73 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1063 71 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1064 : {
1065 2 : constexpr bool bApproxOK = false;
1066 2 : constexpr bool bForce = true;
1067 2 : double dfMin = 0.0;
1068 2 : double dfMax = 0.0;
1069 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1070 : nullptr, nullptr, nullptr, nullptr,
1071 2 : nullptr) != CE_None)
1072 : {
1073 0 : CPLError(CE_Failure, CPLE_AppDefined,
1074 : "Could not retrieve statistics for array %s",
1075 0 : srcArray->GetName().c_str());
1076 0 : return false;
1077 : }
1078 2 : double dfDTMin = 0;
1079 2 : double dfDTMax = 0;
1080 : #define setDTMinMax(ctype) \
1081 : do \
1082 : { \
1083 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1084 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1085 : } while (0)
1086 :
1087 2 : switch (eAutoScaleType)
1088 : {
1089 0 : case GDT_UInt8:
1090 0 : setDTMinMax(GByte);
1091 0 : break;
1092 0 : case GDT_Int8:
1093 0 : setDTMinMax(GInt8);
1094 0 : break;
1095 1 : case GDT_UInt16:
1096 1 : setDTMinMax(GUInt16);
1097 1 : break;
1098 1 : case GDT_Int16:
1099 1 : setDTMinMax(GInt16);
1100 1 : break;
1101 0 : case GDT_UInt32:
1102 0 : setDTMinMax(GUInt32);
1103 0 : break;
1104 0 : case GDT_Int32:
1105 0 : setDTMinMax(GInt32);
1106 0 : break;
1107 0 : case GDT_UInt64:
1108 0 : setDTMinMax(std::uint64_t);
1109 0 : break;
1110 0 : case GDT_Int64:
1111 0 : setDTMinMax(std::int64_t);
1112 0 : break;
1113 0 : case GDT_Float16:
1114 : case GDT_Float32:
1115 : case GDT_Float64:
1116 : case GDT_Unknown:
1117 : case GDT_CInt16:
1118 : case GDT_CInt32:
1119 : case GDT_CFloat16:
1120 : case GDT_CFloat32:
1121 : case GDT_CFloat64:
1122 : case GDT_TypeCount:
1123 0 : CPLAssert(false);
1124 : }
1125 :
1126 : dstArray =
1127 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1128 4 : GDALExtendedDataType::Create(eAutoScaleType),
1129 4 : aosArrayCO.List());
1130 2 : if (!dstArray)
1131 0 : return !bStrict;
1132 :
1133 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1134 : {
1135 : // If there's a nodata value in the source array, reserve
1136 : // DTMax for that purpose in the target scaled array
1137 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1138 : {
1139 0 : CPLError(CE_Failure, CPLE_AppDefined,
1140 : "Cannot set nodata value");
1141 0 : return false;
1142 : }
1143 1 : dfDTMax--;
1144 : }
1145 2 : const double dfScale =
1146 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1147 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1148 :
1149 4 : if (!dstArray->SetOffset(dfOffset) ||
1150 2 : !dstArray->SetScale(dfScale))
1151 : {
1152 0 : CPLError(CE_Failure, CPLE_AppDefined,
1153 : "Cannot set scale/offset");
1154 0 : return false;
1155 : }
1156 :
1157 2 : auto poUnscaled = dstArray->GetUnscaled();
1158 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1159 : {
1160 1 : poUnscaled->SetNoDataValue(
1161 : srcArray->GetNoDataValueAsDouble());
1162 : }
1163 :
1164 : // Copy source array into unscaled array
1165 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1166 : nCurCost, nTotalCost, pfnProgress,
1167 2 : pProgressData))
1168 : {
1169 0 : return false;
1170 : }
1171 : }
1172 : else
1173 : {
1174 134 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1175 134 : srcArrayType, aosArrayCO.List());
1176 67 : if (!dstArray)
1177 0 : return !bStrict;
1178 :
1179 134 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1180 : nCurCost, nTotalCost, pfnProgress,
1181 67 : pProgressData))
1182 : {
1183 0 : return false;
1184 : }
1185 : }
1186 :
1187 : // If this array is the indexing variable of a dimension, link them
1188 : // together.
1189 69 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1190 : {
1191 : auto oCorrespondingDimIter =
1192 35 : mapExistingDstDims.find(oIterDimName->second);
1193 35 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1194 : {
1195 : CPLErrorStateBackuper oErrorStateBackuper(
1196 35 : CPLQuietErrorHandler);
1197 70 : oCorrespondingDimIter->second->SetIndexingVariable(
1198 35 : std::move(dstArray));
1199 : }
1200 : }
1201 :
1202 69 : return true;
1203 33 : };
1204 :
1205 66 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1206 :
1207 : // Start by copying arrays that are indexing variables of dimensions
1208 102 : for (const auto &name : arrayNames)
1209 : {
1210 69 : auto srcArray = poSrcGroup->OpenMDArray(name);
1211 69 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1212 :
1213 69 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1214 69 : srcArray->GetName()))
1215 : {
1216 35 : if (!CopyArray(srcArray))
1217 0 : return false;
1218 : }
1219 : }
1220 :
1221 : // Then copy regular arrays
1222 102 : for (const auto &name : arrayNames)
1223 : {
1224 69 : auto srcArray = poSrcGroup->OpenMDArray(name);
1225 69 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1226 :
1227 69 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1228 69 : srcArray->GetName()))
1229 : {
1230 34 : if (!CopyArray(srcArray))
1231 0 : return false;
1232 : }
1233 : }
1234 :
1235 66 : const auto groupNames = poSrcGroup->GetGroupNames();
1236 39 : for (const auto &name : groupNames)
1237 : {
1238 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1239 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1240 6 : auto dstSubGroup = CreateGroup(name);
1241 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1242 12 : if (!dstSubGroup->CopyFrom(
1243 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1244 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1245 0 : return false;
1246 : }
1247 :
1248 33 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1249 0 : return false;
1250 :
1251 33 : return true;
1252 : }
1253 0 : catch (const std::exception &e)
1254 : {
1255 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1256 0 : return false;
1257 : }
1258 : }
1259 :
1260 : /************************************************************************/
1261 : /* GetInnerMostGroup() */
1262 : /************************************************************************/
1263 :
1264 : //! @cond Doxygen_Suppress
1265 : const GDALGroup *
1266 2285 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1267 : std::shared_ptr<GDALGroup> &curGroupHolder,
1268 : std::string &osLastPart) const
1269 : {
1270 2285 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1271 6 : return nullptr;
1272 2279 : const GDALGroup *poCurGroup = this;
1273 : CPLStringList aosTokens(
1274 4558 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1275 2279 : if (aosTokens.size() == 0)
1276 : {
1277 : // "/" case: the root group itself is the innermost group
1278 2 : osLastPart.clear();
1279 2 : return poCurGroup;
1280 : }
1281 :
1282 2798 : for (int i = 0; i < aosTokens.size() - 1; i++)
1283 : {
1284 533 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1285 533 : if (!curGroupHolder)
1286 : {
1287 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1288 : aosTokens[i]);
1289 12 : return nullptr;
1290 : }
1291 521 : poCurGroup = curGroupHolder.get();
1292 : }
1293 2265 : osLastPart = aosTokens[aosTokens.size() - 1];
1294 2265 : return poCurGroup;
1295 : }
1296 :
1297 : //! @endcond
1298 :
1299 : /************************************************************************/
1300 : /* OpenMDArrayFromFullname() */
1301 : /************************************************************************/
1302 :
1303 : /** Get an array from its fully qualified name */
1304 : std::shared_ptr<GDALMDArray>
1305 806 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1306 : CSLConstList papszOptions) const
1307 : {
1308 1612 : std::string osName;
1309 806 : std::shared_ptr<GDALGroup> curGroupHolder;
1310 806 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1311 806 : if (poGroup == nullptr)
1312 12 : return nullptr;
1313 794 : return poGroup->OpenMDArray(osName, papszOptions);
1314 : }
1315 :
1316 : /************************************************************************/
1317 : /* OpenAttributeFromFullname() */
1318 : /************************************************************************/
1319 :
1320 : /** Get an attribute from its fully qualified name */
1321 : std::shared_ptr<GDALAttribute>
1322 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1323 : CSLConstList papszOptions) const
1324 : {
1325 9 : const auto pos = osFullName.rfind('/');
1326 9 : if (pos == std::string::npos)
1327 0 : return nullptr;
1328 18 : const std::string attrName = osFullName.substr(pos + 1);
1329 9 : if (pos == 0)
1330 2 : return GetAttribute(attrName);
1331 14 : const std::string container = osFullName.substr(0, pos);
1332 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1333 7 : if (poArray)
1334 4 : return poArray->GetAttribute(attrName);
1335 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1336 3 : if (poGroup)
1337 1 : return poGroup->GetAttribute(attrName);
1338 2 : return nullptr;
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* ResolveMDArray() */
1343 : /************************************************************************/
1344 :
1345 : /** Locate an array in a group and its subgroups by name.
1346 : *
1347 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1348 : * used
1349 : * Otherwise the search will start from the group identified by osStartingPath,
1350 : * and an array whose name is osName will be looked for in this group (if
1351 : * osStartingPath is empty or "/", then the current group is used). If there
1352 : * is no match, then a recursive descendant search will be made in its
1353 : * subgroups. If there is no match in the subgroups, then the parent (if
1354 : * existing) of the group pointed by osStartingPath will be used as the new
1355 : * starting point for the search.
1356 : *
1357 : * @param osName name, qualified or not
1358 : * @param osStartingPath fully qualified name of the (sub-)group from which
1359 : * the search should be started. If this is a non-empty
1360 : * string, the group on which this method is called should
1361 : * nominally be the root group (otherwise the path will
1362 : * be interpreted as from the current group)
1363 : * @param papszOptions options to pass to OpenMDArray()
1364 : * @since GDAL 3.2
1365 : */
1366 : std::shared_ptr<GDALMDArray>
1367 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1368 : const std::string &osStartingPath,
1369 : CSLConstList papszOptions) const
1370 : {
1371 19 : if (!osName.empty() && osName[0] == '/')
1372 : {
1373 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1374 1 : if (poArray)
1375 1 : return poArray;
1376 : }
1377 36 : std::string osPath(osStartingPath);
1378 36 : std::set<std::string> oSetAlreadyVisited;
1379 :
1380 : while (true)
1381 : {
1382 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1383 0 : std::shared_ptr<GDALGroup> poGroup;
1384 :
1385 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1386 22 : bool goOn = false;
1387 22 : if (osPath.empty() || osPath == "/")
1388 : {
1389 11 : goOn = true;
1390 : }
1391 : else
1392 : {
1393 22 : std::string osLastPart;
1394 : const GDALGroup *poGroupPtr =
1395 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1396 11 : if (poGroupPtr)
1397 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1398 22 : if (poGroup &&
1399 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1400 : {
1401 11 : oQueue.push(poGroup);
1402 11 : goOn = true;
1403 : }
1404 : }
1405 :
1406 22 : if (goOn)
1407 : {
1408 17 : do
1409 : {
1410 : const GDALGroup *groupPtr;
1411 39 : if (!oQueue.empty())
1412 : {
1413 28 : poGroup = oQueue.front();
1414 28 : oQueue.pop();
1415 28 : groupPtr = poGroup.get();
1416 : }
1417 : else
1418 : {
1419 11 : groupPtr = this;
1420 : }
1421 :
1422 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1423 39 : if (poArray)
1424 16 : return poArray;
1425 :
1426 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1427 47 : for (const auto &osGroupName : aosGroupNames)
1428 : {
1429 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1430 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1431 48 : poSubGroup->GetFullName()))
1432 : {
1433 24 : oQueue.push(poSubGroup);
1434 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1435 : }
1436 : }
1437 23 : } while (!oQueue.empty());
1438 : }
1439 :
1440 6 : if (osPath.empty() || osPath == "/")
1441 2 : break;
1442 :
1443 4 : const auto nPos = osPath.rfind('/');
1444 4 : if (nPos == 0)
1445 1 : osPath = "/";
1446 : else
1447 : {
1448 3 : if (nPos == std::string::npos)
1449 0 : break;
1450 3 : osPath.resize(nPos);
1451 : }
1452 4 : }
1453 2 : return nullptr;
1454 : }
1455 :
1456 : /************************************************************************/
1457 : /* OpenGroupFromFullname() */
1458 : /************************************************************************/
1459 :
1460 : /** Get a group from its fully qualified name.
1461 : * @since GDAL 3.2
1462 : */
1463 : std::shared_ptr<GDALGroup>
1464 1263 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1465 : CSLConstList papszOptions) const
1466 : {
1467 2526 : std::string osName;
1468 1263 : std::shared_ptr<GDALGroup> curGroupHolder;
1469 1263 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1470 1263 : if (poGroup == nullptr)
1471 4 : return nullptr;
1472 1259 : if (osName.empty())
1473 2 : return m_pSelf.lock();
1474 1257 : return poGroup->OpenGroup(osName, papszOptions);
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* OpenDimensionFromFullname() */
1479 : /************************************************************************/
1480 :
1481 : /** Get a dimension from its fully qualified name */
1482 : std::shared_ptr<GDALDimension>
1483 205 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1484 : {
1485 410 : std::string osName;
1486 205 : std::shared_ptr<GDALGroup> curGroupHolder;
1487 205 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1488 205 : if (poGroup == nullptr)
1489 2 : return nullptr;
1490 406 : auto dims(poGroup->GetDimensions());
1491 348 : for (auto &dim : dims)
1492 : {
1493 296 : if (dim->GetName() == osName)
1494 151 : return dim;
1495 : }
1496 52 : return nullptr;
1497 : }
1498 :
1499 : /************************************************************************/
1500 : /* ClearStatistics() */
1501 : /************************************************************************/
1502 :
1503 : /**
1504 : * \brief Clear statistics.
1505 : *
1506 : * @since GDAL 3.4
1507 : */
1508 0 : void GDALGroup::ClearStatistics()
1509 : {
1510 0 : auto groupNames = GetGroupNames();
1511 0 : for (const auto &name : groupNames)
1512 : {
1513 0 : auto subGroup = OpenGroup(name);
1514 0 : if (subGroup)
1515 : {
1516 0 : subGroup->ClearStatistics();
1517 : }
1518 : }
1519 :
1520 0 : auto arrayNames = GetMDArrayNames();
1521 0 : for (const auto &name : arrayNames)
1522 : {
1523 0 : auto array = OpenMDArray(name);
1524 0 : if (array)
1525 : {
1526 0 : array->ClearStatistics();
1527 : }
1528 : }
1529 0 : }
1530 :
1531 : /************************************************************************/
1532 : /* Rename() */
1533 : /************************************************************************/
1534 :
1535 : /** Rename the group.
1536 : *
1537 : * This is not implemented by all drivers.
1538 : *
1539 : * Drivers known to implement it: MEM, netCDF, ZARR.
1540 : *
1541 : * This is the same as the C function GDALGroupRename().
1542 : *
1543 : * @param osNewName New name.
1544 : *
1545 : * @return true in case of success
1546 : * @since GDAL 3.8
1547 : */
1548 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1549 : {
1550 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1551 0 : return false;
1552 : }
1553 :
1554 : /************************************************************************/
1555 : /* BaseRename() */
1556 : /************************************************************************/
1557 :
1558 : //! @cond Doxygen_Suppress
1559 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1560 : {
1561 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1562 8 : m_osFullName += osNewName;
1563 8 : m_osName = osNewName;
1564 :
1565 8 : NotifyChildrenOfRenaming();
1566 8 : }
1567 :
1568 : //! @endcond
1569 :
1570 : /************************************************************************/
1571 : /* ParentRenamed() */
1572 : /************************************************************************/
1573 :
1574 : //! @cond Doxygen_Suppress
1575 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1576 : {
1577 7 : m_osFullName = osNewParentFullName;
1578 7 : m_osFullName += "/";
1579 7 : m_osFullName += m_osName;
1580 :
1581 7 : NotifyChildrenOfRenaming();
1582 7 : }
1583 :
1584 : //! @endcond
1585 :
1586 : /************************************************************************/
1587 : /* Deleted() */
1588 : /************************************************************************/
1589 :
1590 : //! @cond Doxygen_Suppress
1591 28 : void GDALGroup::Deleted()
1592 : {
1593 28 : m_bValid = false;
1594 :
1595 28 : NotifyChildrenOfDeletion();
1596 28 : }
1597 :
1598 : //! @endcond
1599 :
1600 : /************************************************************************/
1601 : /* ParentDeleted() */
1602 : /************************************************************************/
1603 :
1604 : //! @cond Doxygen_Suppress
1605 3 : void GDALGroup::ParentDeleted()
1606 : {
1607 3 : Deleted();
1608 3 : }
1609 :
1610 : //! @endcond
1611 :
1612 : /************************************************************************/
1613 : /* CheckValidAndErrorOutIfNot() */
1614 : /************************************************************************/
1615 :
1616 : //! @cond Doxygen_Suppress
1617 40261 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1618 : {
1619 40261 : if (!m_bValid)
1620 : {
1621 14 : CPLError(CE_Failure, CPLE_AppDefined,
1622 : "This object has been deleted. No action on it is possible");
1623 : }
1624 40261 : return m_bValid;
1625 : }
1626 :
1627 : //! @endcond
1628 :
1629 : /************************************************************************/
1630 : /* ~GDALAbstractMDArray() */
1631 : /************************************************************************/
1632 :
1633 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1634 :
1635 : /************************************************************************/
1636 : /* GDALAbstractMDArray() */
1637 : /************************************************************************/
1638 :
1639 : //! @cond Doxygen_Suppress
1640 171867 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1641 171867 : const std::string &osName)
1642 : : m_osName(osName),
1643 : m_osFullName(
1644 171867 : !osParentName.empty()
1645 340761 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1646 684495 : : osName)
1647 : {
1648 171867 : }
1649 :
1650 : //! @endcond
1651 :
1652 : /************************************************************************/
1653 : /* GetDimensions() */
1654 : /************************************************************************/
1655 :
1656 : /** \fn GDALAbstractMDArray::GetDimensions() const
1657 : * \brief Return the dimensions of an attribute/array.
1658 : *
1659 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1660 : * similar to GDALAttributeGetDimensionsSize().
1661 : */
1662 :
1663 : /************************************************************************/
1664 : /* GetDataType() */
1665 : /************************************************************************/
1666 :
1667 : /** \fn GDALAbstractMDArray::GetDataType() const
1668 : * \brief Return the data type of an attribute/array.
1669 : *
1670 : * This is the same as the C functions GDALMDArrayGetDataType() and
1671 : * GDALAttributeGetDataType()
1672 : */
1673 :
1674 : /************************************************************************/
1675 : /* GetDimensionCount() */
1676 : /************************************************************************/
1677 :
1678 : /** Return the number of dimensions.
1679 : *
1680 : * Default implementation is GetDimensions().size(), and may be overridden by
1681 : * drivers if they have a faster / less expensive implementations.
1682 : *
1683 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1684 : * GDALAttributeGetDimensionCount().
1685 : *
1686 : */
1687 57638 : size_t GDALAbstractMDArray::GetDimensionCount() const
1688 : {
1689 57638 : return GetDimensions().size();
1690 : }
1691 :
1692 : /************************************************************************/
1693 : /* Rename() */
1694 : /************************************************************************/
1695 :
1696 : /** Rename the attribute/array.
1697 : *
1698 : * This is not implemented by all drivers.
1699 : *
1700 : * Drivers known to implement it: MEM, netCDF, Zarr.
1701 : *
1702 : * This is the same as the C functions GDALMDArrayRename() or
1703 : * GDALAttributeRename().
1704 : *
1705 : * @param osNewName New name.
1706 : *
1707 : * @return true in case of success
1708 : * @since GDAL 3.8
1709 : */
1710 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1711 : {
1712 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1713 0 : return false;
1714 : }
1715 :
1716 : /************************************************************************/
1717 : /* CopyValue() */
1718 : /************************************************************************/
1719 :
1720 : /** Convert a value from a source type to a destination type.
1721 : *
1722 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1723 : * that must be freed with CPLFree().
1724 : */
1725 611925 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1726 : const GDALExtendedDataType &srcType,
1727 : void *pDst,
1728 : const GDALExtendedDataType &dstType)
1729 : {
1730 1215320 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1731 603397 : dstType.GetClass() == GEDTC_NUMERIC)
1732 : {
1733 601150 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1734 : dstType.GetNumericDataType(), 0, 1);
1735 601150 : return true;
1736 : }
1737 18911 : if (srcType.GetClass() == GEDTC_STRING &&
1738 8136 : dstType.GetClass() == GEDTC_STRING)
1739 : {
1740 : const char *srcStrPtr;
1741 6886 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1742 6886 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1743 6886 : *reinterpret_cast<void **>(pDst) = pszDup;
1744 6886 : return true;
1745 : }
1746 6136 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1747 2247 : dstType.GetClass() == GEDTC_STRING)
1748 : {
1749 2247 : const char *str = nullptr;
1750 2247 : switch (srcType.GetNumericDataType())
1751 : {
1752 0 : case GDT_Unknown:
1753 0 : break;
1754 531 : case GDT_UInt8:
1755 531 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1756 531 : break;
1757 3 : case GDT_Int8:
1758 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1759 3 : break;
1760 96 : case GDT_UInt16:
1761 96 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1762 96 : break;
1763 0 : case GDT_Int16:
1764 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1765 0 : break;
1766 153 : case GDT_UInt32:
1767 153 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1768 153 : break;
1769 68 : case GDT_Int32:
1770 68 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1771 68 : break;
1772 0 : case GDT_UInt64:
1773 : str =
1774 0 : CPLSPrintf(CPL_FRMT_GUIB,
1775 : static_cast<GUIntBig>(
1776 : *static_cast<const std::uint64_t *>(pSrc)));
1777 0 : break;
1778 53 : case GDT_Int64:
1779 53 : str = CPLSPrintf(CPL_FRMT_GIB,
1780 : static_cast<GIntBig>(
1781 : *static_cast<const std::int64_t *>(pSrc)));
1782 53 : break;
1783 0 : case GDT_Float16:
1784 0 : str = CPLSPrintf("%.5g",
1785 : double(*static_cast<const GFloat16 *>(pSrc)));
1786 0 : break;
1787 449 : case GDT_Float32:
1788 898 : str = CPLSPrintf(
1789 : "%.9g",
1790 449 : static_cast<double>(*static_cast<const float *>(pSrc)));
1791 449 : break;
1792 892 : case GDT_Float64:
1793 892 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1794 892 : break;
1795 2 : case GDT_CInt16:
1796 : {
1797 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1798 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1799 2 : break;
1800 : }
1801 0 : case GDT_CInt32:
1802 : {
1803 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1804 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1805 0 : break;
1806 : }
1807 0 : case GDT_CFloat16:
1808 : {
1809 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1810 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1811 0 : break;
1812 : }
1813 0 : case GDT_CFloat32:
1814 : {
1815 0 : const float *src = static_cast<const float *>(pSrc);
1816 0 : str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1817 0 : break;
1818 : }
1819 0 : case GDT_CFloat64:
1820 : {
1821 0 : const double *src = static_cast<const double *>(pSrc);
1822 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1823 0 : break;
1824 : }
1825 0 : case GDT_TypeCount:
1826 0 : CPLAssert(false);
1827 : break;
1828 : }
1829 2247 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1830 2247 : *reinterpret_cast<void **>(pDst) = pszDup;
1831 2247 : return true;
1832 : }
1833 2892 : if (srcType.GetClass() == GEDTC_STRING &&
1834 1250 : dstType.GetClass() == GEDTC_NUMERIC)
1835 : {
1836 : const char *srcStrPtr;
1837 1250 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1838 1250 : if (dstType.GetNumericDataType() == GDT_Int64)
1839 : {
1840 2 : *(static_cast<int64_t *>(pDst)) =
1841 2 : srcStrPtr == nullptr ? 0
1842 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1843 : }
1844 1248 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1845 : {
1846 2 : *(static_cast<uint64_t *>(pDst)) =
1847 2 : srcStrPtr == nullptr
1848 2 : ? 0
1849 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1850 : }
1851 : else
1852 : {
1853 1246 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1854 1246 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1855 : dstType.GetNumericDataType(), 0, 1);
1856 : }
1857 1250 : return true;
1858 : }
1859 784 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1860 392 : dstType.GetClass() == GEDTC_COMPOUND)
1861 : {
1862 392 : const auto &srcComponents = srcType.GetComponents();
1863 392 : const auto &dstComponents = dstType.GetComponents();
1864 392 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1865 392 : GByte *pabyDst = static_cast<GByte *>(pDst);
1866 :
1867 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1868 784 : srcComponentMap;
1869 2210 : for (const auto &srcComp : srcComponents)
1870 : {
1871 1818 : srcComponentMap[srcComp->GetName()] = &srcComp;
1872 : }
1873 954 : for (const auto &dstComp : dstComponents)
1874 : {
1875 562 : auto oIter = srcComponentMap.find(dstComp->GetName());
1876 562 : if (oIter == srcComponentMap.end())
1877 0 : return false;
1878 562 : const auto &srcComp = *(oIter->second);
1879 1686 : if (!GDALExtendedDataType::CopyValue(
1880 562 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1881 562 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1882 : {
1883 0 : return false;
1884 : }
1885 : }
1886 392 : return true;
1887 : }
1888 :
1889 0 : return false;
1890 : }
1891 :
1892 : /************************************************************************/
1893 : /* CopyValues() */
1894 : /************************************************************************/
1895 :
1896 : /** Convert several values from a source type to a destination type.
1897 : *
1898 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1899 : * that must be freed with CPLFree().
1900 : */
1901 507 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1902 : const GDALExtendedDataType &srcType,
1903 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1904 : const GDALExtendedDataType &dstType,
1905 : GPtrDiff_t nDstStrideInElts,
1906 : size_t nValues)
1907 : {
1908 : const auto nSrcStrideInBytes =
1909 507 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1910 : const auto nDstStrideInBytes =
1911 507 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1912 773 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1913 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1914 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1915 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1916 1039 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1917 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1918 : {
1919 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1920 : static_cast<int>(nSrcStrideInBytes), pDst,
1921 : dstType.GetNumericDataType(),
1922 : static_cast<int>(nDstStrideInBytes), nValues);
1923 : }
1924 : else
1925 : {
1926 241 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1927 241 : GByte *pabyDst = static_cast<GByte *>(pDst);
1928 482 : for (size_t i = 0; i < nValues; ++i)
1929 : {
1930 241 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1931 0 : return false;
1932 241 : pabySrc += nSrcStrideInBytes;
1933 241 : pabyDst += nDstStrideInBytes;
1934 : }
1935 : }
1936 507 : return true;
1937 : }
1938 :
1939 : /************************************************************************/
1940 : /* CheckReadWriteParams() */
1941 : /************************************************************************/
1942 : //! @cond Doxygen_Suppress
1943 22230 : bool GDALAbstractMDArray::CheckReadWriteParams(
1944 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1945 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1946 : const void *buffer, const void *buffer_alloc_start,
1947 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1948 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1949 : {
1950 0 : const auto lamda_error = []()
1951 : {
1952 0 : CPLError(CE_Failure, CPLE_AppDefined,
1953 : "Not all elements pointed by buffer will fit in "
1954 : "[buffer_alloc_start, "
1955 : "buffer_alloc_start + buffer_alloc_size]");
1956 0 : };
1957 :
1958 22230 : const auto &dims = GetDimensions();
1959 22230 : if (dims.empty())
1960 : {
1961 13644 : if (buffer_alloc_start)
1962 : {
1963 12772 : const size_t elementSize = bufferDataType.GetSize();
1964 12772 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1965 12772 : const GByte *paby_buffer_alloc_start =
1966 : static_cast<const GByte *>(buffer_alloc_start);
1967 12772 : const GByte *paby_buffer_alloc_end =
1968 : paby_buffer_alloc_start + buffer_alloc_size;
1969 :
1970 12772 : if (paby_buffer < paby_buffer_alloc_start ||
1971 12772 : paby_buffer + elementSize > paby_buffer_alloc_end)
1972 : {
1973 0 : lamda_error();
1974 0 : return false;
1975 : }
1976 : }
1977 13644 : return true;
1978 : }
1979 :
1980 8586 : if (arrayStep == nullptr)
1981 : {
1982 2047 : tmp_arrayStep.resize(dims.size(), 1);
1983 2047 : arrayStep = tmp_arrayStep.data();
1984 : }
1985 23291 : for (size_t i = 0; i < dims.size(); i++)
1986 : {
1987 14705 : assert(count);
1988 14705 : if (count[i] == 0)
1989 : {
1990 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1991 : static_cast<unsigned>(i));
1992 0 : return false;
1993 : }
1994 : }
1995 8586 : bool bufferStride_all_positive = true;
1996 8586 : if (bufferStride == nullptr)
1997 : {
1998 1558 : GPtrDiff_t stride = 1;
1999 1558 : assert(dims.empty() || count != nullptr);
2000 : // To compute strides we must proceed from the fastest varying dimension
2001 : // (the last one), and then reverse the result
2002 3528 : for (size_t i = dims.size(); i != 0;)
2003 : {
2004 1970 : --i;
2005 1970 : tmp_bufferStride.push_back(stride);
2006 1970 : GUInt64 newStride = 0;
2007 : bool bOK;
2008 : try
2009 : {
2010 1970 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2011 3940 : CPLSM(static_cast<uint64_t>(count[i])))
2012 1970 : .v();
2013 1970 : bOK = static_cast<size_t>(newStride) == newStride &&
2014 1970 : newStride < std::numeric_limits<size_t>::max() / 2;
2015 : }
2016 0 : catch (...)
2017 : {
2018 0 : bOK = false;
2019 : }
2020 1970 : if (!bOK)
2021 : {
2022 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2023 0 : return false;
2024 : }
2025 1970 : stride = static_cast<GPtrDiff_t>(newStride);
2026 : }
2027 1558 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2028 1558 : bufferStride = tmp_bufferStride.data();
2029 : }
2030 : else
2031 : {
2032 19641 : for (size_t i = 0; i < dims.size(); i++)
2033 : {
2034 12674 : if (bufferStride[i] < 0)
2035 : {
2036 61 : bufferStride_all_positive = false;
2037 61 : break;
2038 : }
2039 : }
2040 : }
2041 23260 : for (size_t i = 0; i < dims.size(); i++)
2042 : {
2043 14685 : assert(arrayStartIdx);
2044 14685 : assert(count);
2045 14685 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2046 : {
2047 2 : CPLError(CE_Failure, CPLE_AppDefined,
2048 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2049 : static_cast<unsigned>(i),
2050 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2051 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2052 2 : return false;
2053 : }
2054 : bool bOverflow;
2055 14683 : if (arrayStep[i] >= 0)
2056 : {
2057 : try
2058 : {
2059 13430 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2060 13432 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2061 53723 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2062 13430 : .v() >= dims[i]->GetSize();
2063 : }
2064 1 : catch (...)
2065 : {
2066 1 : bOverflow = true;
2067 : }
2068 13431 : if (bOverflow)
2069 : {
2070 6 : CPLError(CE_Failure, CPLE_AppDefined,
2071 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2072 : ">= " CPL_FRMT_GUIB,
2073 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2074 : static_cast<unsigned>(i),
2075 6 : static_cast<GUInt64>(dims[i]->GetSize()));
2076 6 : return false;
2077 : }
2078 : }
2079 : else
2080 : {
2081 : try
2082 : {
2083 1252 : bOverflow =
2084 1252 : arrayStartIdx[i] <
2085 1252 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2086 2504 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2087 : ? (static_cast<uint64_t>(1) << 63)
2088 2504 : : static_cast<uint64_t>(-arrayStep[i])))
2089 1252 : .v();
2090 : }
2091 0 : catch (...)
2092 : {
2093 0 : bOverflow = true;
2094 : }
2095 1252 : if (bOverflow)
2096 : {
2097 3 : CPLError(
2098 : CE_Failure, CPLE_AppDefined,
2099 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2100 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2101 : static_cast<unsigned>(i));
2102 3 : return false;
2103 : }
2104 : }
2105 : }
2106 :
2107 8575 : if (buffer_alloc_start)
2108 : {
2109 4180 : const size_t elementSize = bufferDataType.GetSize();
2110 4180 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2111 4180 : const GByte *paby_buffer_alloc_start =
2112 : static_cast<const GByte *>(buffer_alloc_start);
2113 4180 : const GByte *paby_buffer_alloc_end =
2114 : paby_buffer_alloc_start + buffer_alloc_size;
2115 4180 : if (bufferStride_all_positive)
2116 : {
2117 4180 : if (paby_buffer < paby_buffer_alloc_start)
2118 : {
2119 0 : lamda_error();
2120 0 : return false;
2121 : }
2122 4180 : GUInt64 nOffset = elementSize;
2123 11995 : for (size_t i = 0; i < dims.size(); i++)
2124 : {
2125 : try
2126 : {
2127 7815 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2128 7815 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2129 15630 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2130 31260 : CPLSM(static_cast<uint64_t>(elementSize)))
2131 7815 : .v();
2132 : }
2133 0 : catch (...)
2134 : {
2135 0 : lamda_error();
2136 0 : return false;
2137 : }
2138 : }
2139 : #if SIZEOF_VOIDP == 4
2140 : if (static_cast<size_t>(nOffset) != nOffset)
2141 : {
2142 : lamda_error();
2143 : return false;
2144 : }
2145 : #endif
2146 4180 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2147 : {
2148 0 : lamda_error();
2149 0 : return false;
2150 : }
2151 : }
2152 0 : else if (dims.size() < 31)
2153 : {
2154 : // Check all corners of the hypercube
2155 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2156 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2157 : {
2158 0 : const GByte *paby = paby_buffer;
2159 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2160 : i++)
2161 : {
2162 0 : if (iCornerCode & (1U << i))
2163 : {
2164 : // We should check for integer overflows
2165 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2166 : }
2167 : }
2168 0 : if (paby < paby_buffer_alloc_start ||
2169 0 : paby + elementSize > paby_buffer_alloc_end)
2170 : {
2171 0 : lamda_error();
2172 0 : return false;
2173 : }
2174 : }
2175 : }
2176 : }
2177 :
2178 8575 : return true;
2179 : }
2180 :
2181 : //! @endcond
2182 :
2183 : /************************************************************************/
2184 : /* Read() */
2185 : /************************************************************************/
2186 :
2187 : /** Read part or totality of a multidimensional array or attribute.
2188 : *
2189 : * This will extract the content of a hyper-rectangle from the array into
2190 : * a user supplied buffer.
2191 : *
2192 : * If bufferDataType is of type string, the values written in pDstBuffer
2193 : * will be char* pointers and the strings should be freed with CPLFree().
2194 : *
2195 : * This is the same as the C function GDALMDArrayRead().
2196 : *
2197 : * @param arrayStartIdx Values representing the starting index to read
2198 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2199 : * Array of GetDimensionCount() values. Must not be
2200 : * nullptr, unless for a zero-dimensional array.
2201 : *
2202 : * @param count Values representing the number of values to extract in
2203 : * each dimension.
2204 : * Array of GetDimensionCount() values. Must not be
2205 : * nullptr, unless for a zero-dimensional array.
2206 : *
2207 : * @param arrayStep Spacing between values to extract in each dimension.
2208 : * The spacing is in number of array elements, not bytes.
2209 : * If provided, must contain GetDimensionCount() values.
2210 : * If set to nullptr, [1, 1, ... 1] will be used as a
2211 : * default to indicate consecutive elements.
2212 : *
2213 : * @param bufferStride Spacing between values to store in pDstBuffer.
2214 : * The spacing is in number of array elements, not bytes.
2215 : * If provided, must contain GetDimensionCount() values.
2216 : * Negative values are possible (for example to reorder
2217 : * from bottom-to-top to top-to-bottom).
2218 : * If set to nullptr, will be set so that pDstBuffer is
2219 : * written in a compact way, with elements of the last /
2220 : * fastest varying dimension being consecutive.
2221 : *
2222 : * @param bufferDataType Data type of values in pDstBuffer.
2223 : *
2224 : * @param pDstBuffer User buffer to store the values read. Should be big
2225 : * enough to store the number of values indicated by
2226 : * count[] and with the spacing of bufferStride[].
2227 : *
2228 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2229 : * validity of pDstBuffer. pDstBufferAllocStart
2230 : * should be the pointer returned by the malloc() or equivalent call used to
2231 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2232 : * bufferStride[] values are all positive), but not necessarily. If specified,
2233 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2234 : * validation is needed, nullptr can be passed.
2235 : *
2236 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2237 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2238 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2239 : * set to the appropriate value.
2240 : * If no validation is needed, 0 can be passed.
2241 : *
2242 : * @return true in case of success.
2243 : */
2244 12446 : bool GDALAbstractMDArray::Read(
2245 : const GUInt64 *arrayStartIdx, const size_t *count,
2246 : const GInt64 *arrayStep, // step in elements
2247 : const GPtrDiff_t *bufferStride, // stride in elements
2248 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2249 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2250 : {
2251 12446 : if (!GetDataType().CanConvertTo(bufferDataType))
2252 : {
2253 0 : CPLError(CE_Failure, CPLE_AppDefined,
2254 : "Array data type is not convertible to buffer data type");
2255 0 : return false;
2256 : }
2257 :
2258 24892 : std::vector<GInt64> tmp_arrayStep;
2259 24892 : std::vector<GPtrDiff_t> tmp_bufferStride;
2260 12446 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2261 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2262 : nDstBufferAllocSize, tmp_arrayStep,
2263 : tmp_bufferStride))
2264 : {
2265 0 : return false;
2266 : }
2267 :
2268 12446 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2269 12446 : pDstBuffer);
2270 : }
2271 :
2272 : /************************************************************************/
2273 : /* IWrite() */
2274 : /************************************************************************/
2275 :
2276 : //! @cond Doxygen_Suppress
2277 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2278 : const GInt64 *, const GPtrDiff_t *,
2279 : const GDALExtendedDataType &, const void *)
2280 : {
2281 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2282 1 : return false;
2283 : }
2284 :
2285 : //! @endcond
2286 :
2287 : /************************************************************************/
2288 : /* Write() */
2289 : /************************************************************************/
2290 :
2291 : /** Write part or totality of a multidimensional array or attribute.
2292 : *
2293 : * This will set the content of a hyper-rectangle into the array from
2294 : * a user supplied buffer.
2295 : *
2296 : * If bufferDataType is of type string, the values read from pSrcBuffer
2297 : * will be char* pointers.
2298 : *
2299 : * This is the same as the C function GDALMDArrayWrite().
2300 : *
2301 : * @param arrayStartIdx Values representing the starting index to write
2302 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2303 : * Array of GetDimensionCount() values. Must not be
2304 : * nullptr, unless for a zero-dimensional array.
2305 : *
2306 : * @param count Values representing the number of values to write in
2307 : * each dimension.
2308 : * Array of GetDimensionCount() values. Must not be
2309 : * nullptr, unless for a zero-dimensional array.
2310 : *
2311 : * @param arrayStep Spacing between values to write in each dimension.
2312 : * The spacing is in number of array elements, not bytes.
2313 : * If provided, must contain GetDimensionCount() values.
2314 : * If set to nullptr, [1, 1, ... 1] will be used as a
2315 : * default to indicate consecutive elements.
2316 : *
2317 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2318 : * The spacing is in number of array elements, not bytes.
2319 : * If provided, must contain GetDimensionCount() values.
2320 : * Negative values are possible (for example to reorder
2321 : * from bottom-to-top to top-to-bottom).
2322 : * If set to nullptr, will be set so that pSrcBuffer is
2323 : * written in a compact way, with elements of the last /
2324 : * fastest varying dimension being consecutive.
2325 : *
2326 : * @param bufferDataType Data type of values in pSrcBuffer.
2327 : *
2328 : * @param pSrcBuffer User buffer to read the values from. Should be big
2329 : * enough to store the number of values indicated by
2330 : * count[] and with the spacing of bufferStride[].
2331 : *
2332 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2333 : * validity of pSrcBuffer. pSrcBufferAllocStart
2334 : * should be the pointer returned by the malloc() or equivalent call used to
2335 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2336 : * bufferStride[] values are all positive), but not necessarily. If specified,
2337 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2338 : * validation is needed, nullptr can be passed.
2339 : *
2340 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2341 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2342 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2343 : * set to the appropriate value.
2344 : * If no validation is needed, 0 can be passed.
2345 : *
2346 : * @return true in case of success.
2347 : */
2348 3280 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2349 : const size_t *count, const GInt64 *arrayStep,
2350 : const GPtrDiff_t *bufferStride,
2351 : const GDALExtendedDataType &bufferDataType,
2352 : const void *pSrcBuffer,
2353 : const void *pSrcBufferAllocStart,
2354 : size_t nSrcBufferAllocSize)
2355 : {
2356 3280 : if (!bufferDataType.CanConvertTo(GetDataType()))
2357 : {
2358 0 : CPLError(CE_Failure, CPLE_AppDefined,
2359 : "Buffer data type is not convertible to array data type");
2360 0 : return false;
2361 : }
2362 :
2363 6560 : std::vector<GInt64> tmp_arrayStep;
2364 6560 : std::vector<GPtrDiff_t> tmp_bufferStride;
2365 3280 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2366 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2367 : nSrcBufferAllocSize, tmp_arrayStep,
2368 : tmp_bufferStride))
2369 : {
2370 0 : return false;
2371 : }
2372 :
2373 3280 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2374 3280 : pSrcBuffer);
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* GetTotalElementsCount() */
2379 : /************************************************************************/
2380 :
2381 : /** Return the total number of values in the array.
2382 : *
2383 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2384 : * and GDALAttributeGetTotalElementsCount().
2385 : *
2386 : */
2387 1596 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2388 : {
2389 1596 : const auto &dims = GetDimensions();
2390 1596 : if (dims.empty())
2391 815 : return 1;
2392 781 : GUInt64 nElts = 1;
2393 1700 : for (const auto &dim : dims)
2394 : {
2395 : try
2396 : {
2397 919 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2398 2757 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2399 919 : .v();
2400 : }
2401 0 : catch (...)
2402 : {
2403 0 : return 0;
2404 : }
2405 : }
2406 781 : return nElts;
2407 : }
2408 :
2409 : /************************************************************************/
2410 : /* GetBlockSize() */
2411 : /************************************************************************/
2412 :
2413 : /** Return the "natural" block size of the array along all dimensions.
2414 : *
2415 : * Some drivers might organize the array in tiles/blocks and reading/writing
2416 : * aligned on those tile/block boundaries will be more efficient.
2417 : *
2418 : * The returned number of elements in the vector is the same as
2419 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2420 : * the natural block size along the considered dimension.
2421 : * "Flat" arrays will typically return a vector of values set to 0.
2422 : *
2423 : * The default implementation will return a vector of values set to 0.
2424 : *
2425 : * This method is used by GetProcessingChunkSize().
2426 : *
2427 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
2428 : * theoretical case of a 32-bit platform, this might exceed its size_t
2429 : * allocation capabilities.
2430 : *
2431 : * This is the same as the C function GDALMDArrayGetBlockSize().
2432 : *
2433 : * @return the block size, in number of elements along each dimension.
2434 : */
2435 312 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2436 : {
2437 312 : return std::vector<GUInt64>(GetDimensionCount());
2438 : }
2439 :
2440 : /************************************************************************/
2441 : /* GetProcessingChunkSize() */
2442 : /************************************************************************/
2443 :
2444 : /** \brief Return an optimal chunk size for read/write operations, given the
2445 : * natural block size and memory constraints specified.
2446 : *
2447 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2448 : * multiple of those returned by GetBlockSize() (unless the block define by
2449 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2450 : * returned by this method).
2451 : *
2452 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2453 : *
2454 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2455 : * chunk.
2456 : *
2457 : * @return the chunk size, in number of elements along each dimension.
2458 : */
2459 : std::vector<size_t>
2460 99 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2461 : {
2462 99 : const auto &dims = GetDimensions();
2463 99 : const auto &nDTSize = GetDataType().GetSize();
2464 99 : std::vector<size_t> anChunkSize;
2465 198 : auto blockSize = GetBlockSize();
2466 99 : CPLAssert(blockSize.size() == dims.size());
2467 99 : size_t nChunkSize = nDTSize;
2468 99 : bool bOverflow = false;
2469 99 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2470 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2471 : // [1, min(sizet_max, dim_size[i])]
2472 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2473 268 : for (size_t i = 0; i < dims.size(); i++)
2474 : {
2475 : const auto sizeDimI =
2476 338 : std::max(static_cast<size_t>(1),
2477 338 : static_cast<size_t>(
2478 338 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2479 169 : std::min(blockSize[i], dims[i]->GetSize()))));
2480 169 : anChunkSize.push_back(sizeDimI);
2481 169 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2482 : {
2483 4 : bOverflow = true;
2484 : }
2485 : else
2486 : {
2487 165 : nChunkSize *= sizeDimI;
2488 : }
2489 : }
2490 99 : if (nChunkSize == 0)
2491 0 : return anChunkSize;
2492 :
2493 : // If the product of all anChunkSize[i] does not fit on size_t, then
2494 : // set lowest anChunkSize[i] to 1.
2495 99 : if (bOverflow)
2496 : {
2497 2 : nChunkSize = nDTSize;
2498 2 : bOverflow = false;
2499 8 : for (size_t i = dims.size(); i > 0;)
2500 : {
2501 6 : --i;
2502 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2503 : {
2504 4 : bOverflow = true;
2505 4 : anChunkSize[i] = 1;
2506 : }
2507 : else
2508 : {
2509 2 : nChunkSize *= anChunkSize[i];
2510 : }
2511 : }
2512 : }
2513 :
2514 99 : nChunkSize = nDTSize;
2515 198 : std::vector<size_t> anAccBlockSizeFromStart;
2516 268 : for (size_t i = 0; i < dims.size(); i++)
2517 : {
2518 169 : nChunkSize *= anChunkSize[i];
2519 169 : anAccBlockSizeFromStart.push_back(nChunkSize);
2520 : }
2521 99 : if (nChunkSize <= nMaxChunkMemory / 2)
2522 : {
2523 95 : size_t nVoxelsFromEnd = 1;
2524 256 : for (size_t i = dims.size(); i > 0;)
2525 : {
2526 161 : --i;
2527 : const auto nCurBlockSize =
2528 161 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2529 161 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2530 161 : if (nMul >= 2)
2531 : {
2532 153 : const auto nSizeThisDim(dims[i]->GetSize());
2533 : const auto nBlocksThisDim =
2534 153 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2535 153 : anChunkSize[i] = static_cast<size_t>(std::min(
2536 153 : anChunkSize[i] *
2537 306 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2538 153 : nSizeThisDim));
2539 : }
2540 161 : nVoxelsFromEnd *= anChunkSize[i];
2541 : }
2542 : }
2543 99 : return anChunkSize;
2544 : }
2545 :
2546 : /************************************************************************/
2547 : /* BaseRename() */
2548 : /************************************************************************/
2549 :
2550 : //! @cond Doxygen_Suppress
2551 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2552 : {
2553 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2554 18 : m_osFullName += osNewName;
2555 18 : m_osName = osNewName;
2556 :
2557 18 : NotifyChildrenOfRenaming();
2558 18 : }
2559 :
2560 : //! @endcond
2561 :
2562 : //! @cond Doxygen_Suppress
2563 : /************************************************************************/
2564 : /* ParentRenamed() */
2565 : /************************************************************************/
2566 :
2567 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2568 : {
2569 50 : m_osFullName = osNewParentFullName;
2570 50 : m_osFullName += "/";
2571 50 : m_osFullName += m_osName;
2572 :
2573 50 : NotifyChildrenOfRenaming();
2574 50 : }
2575 :
2576 : //! @endcond
2577 :
2578 : /************************************************************************/
2579 : /* Deleted() */
2580 : /************************************************************************/
2581 :
2582 : //! @cond Doxygen_Suppress
2583 58 : void GDALAbstractMDArray::Deleted()
2584 : {
2585 58 : m_bValid = false;
2586 :
2587 58 : NotifyChildrenOfDeletion();
2588 58 : }
2589 :
2590 : //! @endcond
2591 :
2592 : /************************************************************************/
2593 : /* ParentDeleted() */
2594 : /************************************************************************/
2595 :
2596 : //! @cond Doxygen_Suppress
2597 30 : void GDALAbstractMDArray::ParentDeleted()
2598 : {
2599 30 : Deleted();
2600 30 : }
2601 :
2602 : //! @endcond
2603 :
2604 : /************************************************************************/
2605 : /* CheckValidAndErrorOutIfNot() */
2606 : /************************************************************************/
2607 :
2608 : //! @cond Doxygen_Suppress
2609 10196 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2610 : {
2611 10196 : if (!m_bValid)
2612 : {
2613 26 : CPLError(CE_Failure, CPLE_AppDefined,
2614 : "This object has been deleted. No action on it is possible");
2615 : }
2616 10196 : return m_bValid;
2617 : }
2618 :
2619 : //! @endcond
2620 :
2621 : /************************************************************************/
2622 : /* SetUnit() */
2623 : /************************************************************************/
2624 :
2625 : /** Set the variable unit.
2626 : *
2627 : * Values should conform as much as possible with those allowed by
2628 : * the NetCDF CF conventions:
2629 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2630 : * but others might be returned.
2631 : *
2632 : * Few examples are "meter", "degrees", "second", ...
2633 : * Empty value means unknown.
2634 : *
2635 : * This is the same as the C function GDALMDArraySetUnit()
2636 : *
2637 : * @note Driver implementation: optionally implemented.
2638 : *
2639 : * @param osUnit unit name.
2640 : * @return true in case of success.
2641 : */
2642 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2643 : {
2644 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2645 0 : return false;
2646 : }
2647 :
2648 : /************************************************************************/
2649 : /* GetUnit() */
2650 : /************************************************************************/
2651 :
2652 : /** Return the array unit.
2653 : *
2654 : * Values should conform as much as possible with those allowed by
2655 : * the NetCDF CF conventions:
2656 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2657 : * but others might be returned.
2658 : *
2659 : * Few examples are "meter", "degrees", "second", ...
2660 : * Empty value means unknown.
2661 : *
2662 : * This is the same as the C function GDALMDArrayGetUnit()
2663 : */
2664 5 : const std::string &GDALMDArray::GetUnit() const
2665 : {
2666 5 : static const std::string emptyString;
2667 5 : return emptyString;
2668 : }
2669 :
2670 : /************************************************************************/
2671 : /* SetSpatialRef() */
2672 : /************************************************************************/
2673 :
2674 : /** Assign a spatial reference system object to the array.
2675 : *
2676 : * This is the same as the C function GDALMDArraySetSpatialRef().
2677 : */
2678 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2679 : {
2680 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2681 0 : return false;
2682 : }
2683 :
2684 : /************************************************************************/
2685 : /* GetSpatialRef() */
2686 : /************************************************************************/
2687 :
2688 : /** Return the spatial reference system object associated with the array.
2689 : *
2690 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2691 : */
2692 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2693 : {
2694 4 : return nullptr;
2695 : }
2696 :
2697 : /************************************************************************/
2698 : /* GetRawNoDataValue() */
2699 : /************************************************************************/
2700 :
2701 : /** Return the nodata value as a "raw" value.
2702 : *
2703 : * The value returned might be nullptr in case of no nodata value. When
2704 : * a nodata value is registered, a non-nullptr will be returned whose size in
2705 : * bytes is GetDataType().GetSize().
2706 : *
2707 : * The returned value should not be modified or freed. It is valid until
2708 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2709 : * SetRawNoDataValue(), or any similar methods.
2710 : *
2711 : * @note Driver implementation: this method shall be implemented if nodata
2712 : * is supported.
2713 : *
2714 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2715 : *
2716 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2717 : */
2718 5 : const void *GDALMDArray::GetRawNoDataValue() const
2719 : {
2720 5 : return nullptr;
2721 : }
2722 :
2723 : /************************************************************************/
2724 : /* GetNoDataValueAsDouble() */
2725 : /************************************************************************/
2726 :
2727 : /** Return the nodata value as a double.
2728 : *
2729 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2730 : *
2731 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2732 : * a nodata value exists and can be converted to double. Might be nullptr.
2733 : *
2734 : * @return the nodata value as a double. A 0.0 value might also indicate the
2735 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2736 : * set to false then).
2737 : */
2738 23215 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2739 : {
2740 23215 : const void *pNoData = GetRawNoDataValue();
2741 23215 : double dfNoData = 0.0;
2742 23215 : const auto &eDT = GetDataType();
2743 23215 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2744 23215 : if (ok)
2745 : {
2746 22876 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2747 : GDT_Float64, 0, 1);
2748 : }
2749 23215 : if (pbHasNoData)
2750 521 : *pbHasNoData = ok;
2751 23215 : return dfNoData;
2752 : }
2753 :
2754 : /************************************************************************/
2755 : /* GetNoDataValueAsInt64() */
2756 : /************************************************************************/
2757 :
2758 : /** Return the nodata value as a Int64.
2759 : *
2760 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2761 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2762 : *
2763 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2764 : *
2765 : * @return the nodata value as a Int64
2766 : *
2767 : * @since GDAL 3.5
2768 : */
2769 14 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2770 : {
2771 14 : const void *pNoData = GetRawNoDataValue();
2772 14 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2773 14 : const auto &eDT = GetDataType();
2774 14 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2775 14 : if (ok)
2776 : {
2777 10 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2778 : GDT_Int64, 0, 1);
2779 : }
2780 14 : if (pbHasNoData)
2781 14 : *pbHasNoData = ok;
2782 14 : return nNoData;
2783 : }
2784 :
2785 : /************************************************************************/
2786 : /* GetNoDataValueAsUInt64() */
2787 : /************************************************************************/
2788 :
2789 : /** Return the nodata value as a UInt64.
2790 : *
2791 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2792 :
2793 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2794 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2795 : *
2796 : * @return the nodata value as a UInt64
2797 : *
2798 : * @since GDAL 3.5
2799 : */
2800 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2801 : {
2802 8 : const void *pNoData = GetRawNoDataValue();
2803 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2804 8 : const auto &eDT = GetDataType();
2805 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2806 8 : if (ok)
2807 : {
2808 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2809 : GDT_UInt64, 0, 1);
2810 : }
2811 8 : if (pbHasNoData)
2812 8 : *pbHasNoData = ok;
2813 8 : return nNoData;
2814 : }
2815 :
2816 : /************************************************************************/
2817 : /* SetRawNoDataValue() */
2818 : /************************************************************************/
2819 :
2820 : /** Set the nodata value as a "raw" value.
2821 : *
2822 : * The value passed might be nullptr in case of no nodata value. When
2823 : * a nodata value is registered, a non-nullptr whose size in
2824 : * bytes is GetDataType().GetSize() must be passed.
2825 : *
2826 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2827 : *
2828 : * @note Driver implementation: this method shall be implemented if setting
2829 : nodata
2830 : * is supported.
2831 :
2832 : * @return true in case of success.
2833 : */
2834 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2835 : {
2836 0 : CPLError(CE_Failure, CPLE_NotSupported,
2837 : "SetRawNoDataValue() not implemented");
2838 0 : return false;
2839 : }
2840 :
2841 : /************************************************************************/
2842 : /* SetNoDataValue() */
2843 : /************************************************************************/
2844 :
2845 : /** Set the nodata value as a double.
2846 : *
2847 : * If the natural data type of the attribute/array is not double, type
2848 : * conversion will occur to the type returned by GetDataType().
2849 : *
2850 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2851 : *
2852 : * @return true in case of success.
2853 : */
2854 64 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2855 : {
2856 64 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2857 64 : bool bRet = false;
2858 64 : if (GDALExtendedDataType::CopyValue(
2859 128 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2860 64 : GetDataType()))
2861 : {
2862 64 : bRet = SetRawNoDataValue(pRawNoData);
2863 : }
2864 64 : CPLFree(pRawNoData);
2865 64 : return bRet;
2866 : }
2867 :
2868 : /************************************************************************/
2869 : /* SetNoDataValue() */
2870 : /************************************************************************/
2871 :
2872 : /** Set the nodata value as a Int64.
2873 : *
2874 : * If the natural data type of the attribute/array is not Int64, type conversion
2875 : * will occur to the type returned by GetDataType().
2876 : *
2877 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2878 : *
2879 : * @return true in case of success.
2880 : *
2881 : * @since GDAL 3.5
2882 : */
2883 6 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2884 : {
2885 6 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2886 6 : bool bRet = false;
2887 6 : if (GDALExtendedDataType::CopyValue(&nNoData,
2888 12 : GDALExtendedDataType::Create(GDT_Int64),
2889 6 : pRawNoData, GetDataType()))
2890 : {
2891 6 : bRet = SetRawNoDataValue(pRawNoData);
2892 : }
2893 6 : CPLFree(pRawNoData);
2894 6 : return bRet;
2895 : }
2896 :
2897 : /************************************************************************/
2898 : /* SetNoDataValue() */
2899 : /************************************************************************/
2900 :
2901 : /** Set the nodata value as a Int64.
2902 : *
2903 : * If the natural data type of the attribute/array is not Int64, type conversion
2904 : * will occur to the type returned by GetDataType().
2905 : *
2906 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2907 : *
2908 : * @return true in case of success.
2909 : *
2910 : * @since GDAL 3.5
2911 : */
2912 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2913 : {
2914 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2915 1 : bool bRet = false;
2916 1 : if (GDALExtendedDataType::CopyValue(
2917 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2918 1 : GetDataType()))
2919 : {
2920 1 : bRet = SetRawNoDataValue(pRawNoData);
2921 : }
2922 1 : CPLFree(pRawNoData);
2923 1 : return bRet;
2924 : }
2925 :
2926 : /************************************************************************/
2927 : /* Resize() */
2928 : /************************************************************************/
2929 :
2930 : /** Resize an array to new dimensions.
2931 : *
2932 : * Not all drivers may allow this operation, and with restrictions (e.g.
2933 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2934 : *
2935 : * Resizing a dimension used in other arrays will cause those other arrays
2936 : * to be resized.
2937 : *
2938 : * This is the same as the C function GDALMDArrayResize().
2939 : *
2940 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2941 : * new size of each indexing dimension.
2942 : * @param papszOptions Options. (Driver specific)
2943 : * @return true in case of success.
2944 : * @since GDAL 3.7
2945 : */
2946 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2947 : CPL_UNUSED CSLConstList papszOptions)
2948 : {
2949 0 : CPLError(CE_Failure, CPLE_NotSupported,
2950 : "Resize() is not supported for this array");
2951 0 : return false;
2952 : }
2953 :
2954 : /************************************************************************/
2955 : /* SetScale() */
2956 : /************************************************************************/
2957 :
2958 : /** Set the scale value to apply to raw values.
2959 : *
2960 : * unscaled_value = raw_value * GetScale() + GetOffset()
2961 : *
2962 : * This is the same as the C function GDALMDArraySetScale() /
2963 : * GDALMDArraySetScaleEx().
2964 : *
2965 : * @note Driver implementation: this method shall be implemented if setting
2966 : * scale is supported.
2967 : *
2968 : * @param dfScale scale
2969 : * @param eStorageType Data type to which create the potential attribute that
2970 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2971 : * implementation will decide automatically the data type. Note that changing
2972 : * the data type after initial setting might not be supported.
2973 : * @return true in case of success.
2974 : */
2975 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2976 : CPL_UNUSED GDALDataType eStorageType)
2977 : {
2978 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2979 0 : return false;
2980 : }
2981 :
2982 : /************************************************************************/
2983 : /* SetOffset) */
2984 : /************************************************************************/
2985 :
2986 : /** Set the offset value to apply to raw values.
2987 : *
2988 : * unscaled_value = raw_value * GetScale() + GetOffset()
2989 : *
2990 : * This is the same as the C function GDALMDArraySetOffset() /
2991 : * GDALMDArraySetOffsetEx().
2992 : *
2993 : * @note Driver implementation: this method shall be implemented if setting
2994 : * offset is supported.
2995 : *
2996 : * @param dfOffset Offset
2997 : * @param eStorageType Data type to which create the potential attribute that
2998 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2999 : * implementation will decide automatically the data type. Note that changing
3000 : * the data type after initial setting might not be supported.
3001 : * @return true in case of success.
3002 : */
3003 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
3004 : CPL_UNUSED GDALDataType eStorageType)
3005 : {
3006 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
3007 0 : return false;
3008 : }
3009 :
3010 : /************************************************************************/
3011 : /* GetScale() */
3012 : /************************************************************************/
3013 :
3014 : /** Get the scale value to apply to raw values.
3015 : *
3016 : * unscaled_value = raw_value * GetScale() + GetOffset()
3017 : *
3018 : * This is the same as the C function GDALMDArrayGetScale().
3019 : *
3020 : * @note Driver implementation: this method shall be implemented if getting
3021 : * scale is supported.
3022 : *
3023 : * @param pbHasScale Pointer to a output boolean that will be set to true if
3024 : * a scale value exists. Might be nullptr.
3025 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3026 : * the storage type of the scale value, when known/relevant. Otherwise will be
3027 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3028 : *
3029 : * @return the scale value. A 1.0 value might also indicate the
3030 : * absence of a scale value.
3031 : */
3032 22 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3033 : CPL_UNUSED GDALDataType *peStorageType) const
3034 : {
3035 22 : if (pbHasScale)
3036 22 : *pbHasScale = false;
3037 22 : return 1.0;
3038 : }
3039 :
3040 : /************************************************************************/
3041 : /* GetOffset() */
3042 : /************************************************************************/
3043 :
3044 : /** Get the offset value to apply to raw values.
3045 : *
3046 : * unscaled_value = raw_value * GetScale() + GetOffset()
3047 : *
3048 : * This is the same as the C function GDALMDArrayGetOffset().
3049 : *
3050 : * @note Driver implementation: this method shall be implemented if getting
3051 : * offset is supported.
3052 : *
3053 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3054 : * a offset value exists. Might be nullptr.
3055 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3056 : * the storage type of the offset value, when known/relevant. Otherwise will be
3057 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3058 : *
3059 : * @return the offset value. A 0.0 value might also indicate the
3060 : * absence of a offset value.
3061 : */
3062 22 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3063 : CPL_UNUSED GDALDataType *peStorageType) const
3064 : {
3065 22 : if (pbHasOffset)
3066 22 : *pbHasOffset = false;
3067 22 : return 0.0;
3068 : }
3069 :
3070 : /************************************************************************/
3071 : /* ProcessPerChunk() */
3072 : /************************************************************************/
3073 :
3074 : namespace
3075 : {
3076 : enum class Caller
3077 : {
3078 : CALLER_END_OF_LOOP,
3079 : CALLER_IN_LOOP,
3080 : };
3081 : }
3082 :
3083 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3084 : *
3085 : * This method is to be used when doing operations on an array, or a subset of
3086 : * it, in a chunk by chunk way.
3087 : *
3088 : * @param arrayStartIdx Values representing the starting index to use
3089 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3090 : * Array of GetDimensionCount() values. Must not be
3091 : * nullptr, unless for a zero-dimensional array.
3092 : *
3093 : * @param count Values representing the number of values to use in
3094 : * each dimension.
3095 : * Array of GetDimensionCount() values. Must not be
3096 : * nullptr, unless for a zero-dimensional array.
3097 : *
3098 : * @param chunkSize Values representing the chunk size in each dimension.
3099 : * Might typically the output of GetProcessingChunkSize().
3100 : * Array of GetDimensionCount() values. Must not be
3101 : * nullptr, unless for a zero-dimensional array.
3102 : *
3103 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3104 : * Must NOT be nullptr.
3105 : *
3106 : * @param pUserData Pointer to pass as the value of the pUserData argument
3107 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3108 : *
3109 : * @return true in case of success.
3110 : */
3111 97 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3112 : const GUInt64 *count,
3113 : const size_t *chunkSize,
3114 : FuncProcessPerChunkType pfnFunc,
3115 : void *pUserData)
3116 : {
3117 97 : const auto &dims = GetDimensions();
3118 97 : if (dims.empty())
3119 : {
3120 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3121 : }
3122 :
3123 : // Sanity check
3124 95 : size_t nTotalChunkSize = 1;
3125 241 : for (size_t i = 0; i < dims.size(); i++)
3126 : {
3127 153 : const auto nSizeThisDim(dims[i]->GetSize());
3128 153 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3129 151 : arrayStartIdx[i] > nSizeThisDim - count[i])
3130 : {
3131 4 : CPLError(CE_Failure, CPLE_AppDefined,
3132 : "Inconsistent arrayStartIdx[] / count[] values "
3133 : "regarding array size");
3134 4 : return false;
3135 : }
3136 296 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3137 147 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3138 : {
3139 3 : CPLError(CE_Failure, CPLE_AppDefined,
3140 : "Inconsistent chunkSize[] values");
3141 3 : return false;
3142 : }
3143 146 : nTotalChunkSize *= chunkSize[i];
3144 : }
3145 :
3146 88 : size_t dimIdx = 0;
3147 176 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3148 176 : std::vector<size_t> chunkCount(dims.size());
3149 :
3150 : struct Stack
3151 : {
3152 : GUInt64 nBlockCounter = 0;
3153 : GUInt64 nBlocksMinusOne = 0;
3154 : size_t first_count = 0; // only used if nBlocks > 1
3155 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3156 : };
3157 :
3158 176 : std::vector<Stack> stack(dims.size());
3159 88 : GUInt64 iCurChunk = 0;
3160 88 : GUInt64 nChunkCount = 1;
3161 233 : for (size_t i = 0; i < dims.size(); i++)
3162 : {
3163 145 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3164 145 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3165 145 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3166 145 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3167 145 : if (stack[i].nBlocksMinusOne == 0)
3168 : {
3169 140 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3170 140 : chunkCount[i] = static_cast<size_t>(count[i]);
3171 : }
3172 : else
3173 : {
3174 5 : stack[i].first_count = static_cast<size_t>(
3175 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3176 : }
3177 : }
3178 :
3179 88 : lbl_next_depth:
3180 343 : if (dimIdx == dims.size())
3181 : {
3182 121 : ++iCurChunk;
3183 121 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3184 : iCurChunk, nChunkCount, pUserData))
3185 : {
3186 3 : return false;
3187 : }
3188 : }
3189 : else
3190 : {
3191 222 : if (stack[dimIdx].nBlocksMinusOne != 0)
3192 : {
3193 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3194 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3195 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3196 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3197 : while (true)
3198 : {
3199 33 : dimIdx++;
3200 33 : goto lbl_next_depth;
3201 33 : lbl_return_to_caller_in_loop:
3202 33 : --stack[dimIdx].nBlockCounter;
3203 33 : if (stack[dimIdx].nBlockCounter == 0)
3204 11 : break;
3205 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3206 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3207 : }
3208 :
3209 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3210 22 : chunkCount[dimIdx] =
3211 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3212 11 : chunkArrayStartIdx[dimIdx]);
3213 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3214 : }
3215 222 : dimIdx++;
3216 222 : goto lbl_next_depth;
3217 216 : lbl_return_to_caller_end_of_loop:
3218 216 : if (dimIdx == 0)
3219 85 : goto end;
3220 : }
3221 :
3222 249 : assert(dimIdx > 0);
3223 249 : dimIdx--;
3224 : // cppcheck-suppress negativeContainerIndex
3225 249 : switch (stack[dimIdx].return_point)
3226 : {
3227 216 : case Caller::CALLER_END_OF_LOOP:
3228 216 : goto lbl_return_to_caller_end_of_loop;
3229 33 : case Caller::CALLER_IN_LOOP:
3230 33 : goto lbl_return_to_caller_in_loop;
3231 : }
3232 85 : end:
3233 85 : return true;
3234 : }
3235 :
3236 : /************************************************************************/
3237 : /* GDALAttribute() */
3238 : /************************************************************************/
3239 :
3240 : //! @cond Doxygen_Suppress
3241 161798 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3242 0 : CPL_UNUSED const std::string &osName)
3243 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3244 161798 : : GDALAbstractMDArray(osParentName, osName)
3245 : #endif
3246 : {
3247 161798 : }
3248 :
3249 : GDALAttribute::~GDALAttribute() = default;
3250 :
3251 : //! @endcond
3252 :
3253 : /************************************************************************/
3254 : /* GetDimensionSize() */
3255 : /************************************************************************/
3256 :
3257 : /** Return the size of the dimensions of the attribute.
3258 : *
3259 : * This will be an empty array for a scalar (single value) attribute.
3260 : *
3261 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3262 : */
3263 791 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3264 : {
3265 791 : const auto &dims = GetDimensions();
3266 791 : std::vector<GUInt64> ret;
3267 791 : ret.reserve(dims.size());
3268 974 : for (const auto &dim : dims)
3269 183 : ret.push_back(dim->GetSize());
3270 791 : return ret;
3271 : }
3272 :
3273 : /************************************************************************/
3274 : /* GDALRawResult() */
3275 : /************************************************************************/
3276 :
3277 : //! @cond Doxygen_Suppress
3278 236 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3279 236 : size_t nEltCount)
3280 472 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3281 236 : m_raw(raw)
3282 : {
3283 236 : }
3284 :
3285 : //! @endcond
3286 :
3287 : /************************************************************************/
3288 : /* GDALRawResult() */
3289 : /************************************************************************/
3290 :
3291 : /** Move constructor. */
3292 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3293 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3294 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3295 : {
3296 0 : other.m_nEltCount = 0;
3297 0 : other.m_nSize = 0;
3298 0 : other.m_raw = nullptr;
3299 0 : }
3300 :
3301 : /************************************************************************/
3302 : /* FreeMe() */
3303 : /************************************************************************/
3304 :
3305 236 : void GDALRawResult::FreeMe()
3306 : {
3307 236 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3308 : {
3309 122 : GByte *pabyPtr = m_raw;
3310 122 : const auto nDTSize(m_dt.GetSize());
3311 244 : for (size_t i = 0; i < m_nEltCount; ++i)
3312 : {
3313 122 : m_dt.FreeDynamicMemory(pabyPtr);
3314 122 : pabyPtr += nDTSize;
3315 : }
3316 : }
3317 236 : VSIFree(m_raw);
3318 236 : }
3319 :
3320 : /************************************************************************/
3321 : /* operator=() */
3322 : /************************************************************************/
3323 :
3324 : /** Move assignment. */
3325 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3326 : {
3327 0 : FreeMe();
3328 0 : m_dt = std::move(other.m_dt);
3329 0 : m_nEltCount = other.m_nEltCount;
3330 0 : m_nSize = other.m_nSize;
3331 0 : m_raw = other.m_raw;
3332 0 : other.m_nEltCount = 0;
3333 0 : other.m_nSize = 0;
3334 0 : other.m_raw = nullptr;
3335 0 : return *this;
3336 : }
3337 :
3338 : /************************************************************************/
3339 : /* ~GDALRawResult() */
3340 : /************************************************************************/
3341 :
3342 : /** Destructor. */
3343 236 : GDALRawResult::~GDALRawResult()
3344 : {
3345 236 : FreeMe();
3346 236 : }
3347 :
3348 : /************************************************************************/
3349 : /* StealData() */
3350 : /************************************************************************/
3351 :
3352 : //! @cond Doxygen_Suppress
3353 : /** Return buffer to caller which becomes owner of it.
3354 : * Only to be used by GDALAttributeReadAsRaw().
3355 : */
3356 6 : GByte *GDALRawResult::StealData()
3357 : {
3358 6 : GByte *ret = m_raw;
3359 6 : m_raw = nullptr;
3360 6 : m_nEltCount = 0;
3361 6 : m_nSize = 0;
3362 6 : return ret;
3363 : }
3364 :
3365 : //! @endcond
3366 :
3367 : /************************************************************************/
3368 : /* ReadAsRaw() */
3369 : /************************************************************************/
3370 :
3371 : /** Return the raw value of an attribute.
3372 : *
3373 : *
3374 : * This is the same as the C function GDALAttributeReadAsRaw().
3375 : */
3376 236 : GDALRawResult GDALAttribute::ReadAsRaw() const
3377 : {
3378 236 : const auto nEltCount(GetTotalElementsCount());
3379 236 : const auto &dt(GetDataType());
3380 236 : const auto nDTSize(dt.GetSize());
3381 : GByte *res = static_cast<GByte *>(
3382 236 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3383 236 : if (!res)
3384 0 : return GDALRawResult(nullptr, dt, 0);
3385 236 : const auto &dims = GetDimensions();
3386 236 : const auto nDims = GetDimensionCount();
3387 472 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3388 472 : std::vector<size_t> count(1 + nDims);
3389 262 : for (size_t i = 0; i < nDims; i++)
3390 : {
3391 26 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3392 : }
3393 236 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3394 236 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3395 : {
3396 0 : VSIFree(res);
3397 0 : return GDALRawResult(nullptr, dt, 0);
3398 : }
3399 236 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3400 : }
3401 :
3402 : /************************************************************************/
3403 : /* ReadAsString() */
3404 : /************************************************************************/
3405 :
3406 : /** Return the value of an attribute as a string.
3407 : *
3408 : * The returned string should not be freed, and its lifetime does not
3409 : * excess a next call to ReadAsString() on the same object, or the deletion
3410 : * of the object itself.
3411 : *
3412 : * This function will only return the first element if there are several.
3413 : *
3414 : * This is the same as the C function GDALAttributeReadAsString()
3415 : *
3416 : * @return a string, or nullptr.
3417 : */
3418 4680 : const char *GDALAttribute::ReadAsString() const
3419 : {
3420 4680 : const auto nDims = GetDimensionCount();
3421 9360 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3422 9360 : std::vector<size_t> count(1 + nDims, 1);
3423 4680 : char *szRet = nullptr;
3424 4680 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3425 4680 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3426 14039 : sizeof(szRet)) ||
3427 4679 : szRet == nullptr)
3428 : {
3429 5 : return nullptr;
3430 : }
3431 4675 : m_osCachedVal = szRet;
3432 4675 : CPLFree(szRet);
3433 4675 : return m_osCachedVal.c_str();
3434 : }
3435 :
3436 : /************************************************************************/
3437 : /* ReadAsInt() */
3438 : /************************************************************************/
3439 :
3440 : /** Return the value of an attribute as a integer.
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 integer.
3445 : *
3446 : * This is the same as the C function GDALAttributeReadAsInt()
3447 : *
3448 : * @return a integer, or INT_MIN in case of error.
3449 : */
3450 2903 : int GDALAttribute::ReadAsInt() const
3451 : {
3452 2903 : const auto nDims = GetDimensionCount();
3453 5806 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3454 2903 : std::vector<size_t> count(1 + nDims, 1);
3455 2903 : int nRet = INT_MIN;
3456 2903 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3457 5806 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3458 5806 : return nRet;
3459 : }
3460 :
3461 : /************************************************************************/
3462 : /* ReadAsInt64() */
3463 : /************************************************************************/
3464 :
3465 : /** Return the value of an attribute as an int64_t.
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 long.
3470 : *
3471 : * This is the same as the C function GDALAttributeReadAsInt64()
3472 : *
3473 : * @return an int64_t, or INT64_MIN in case of error.
3474 : */
3475 131 : int64_t GDALAttribute::ReadAsInt64() const
3476 : {
3477 131 : const auto nDims = GetDimensionCount();
3478 262 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3479 131 : std::vector<size_t> count(1 + nDims, 1);
3480 131 : int64_t nRet = INT64_MIN;
3481 131 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3482 262 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3483 262 : return nRet;
3484 : }
3485 :
3486 : /************************************************************************/
3487 : /* ReadAsDouble() */
3488 : /************************************************************************/
3489 :
3490 : /** Return the value of an attribute as a double.
3491 : *
3492 : * This function will only return the first element if there are several.
3493 : *
3494 : * It can fail if its value can not be converted to double.
3495 : *
3496 : * This is the same as the C function GDALAttributeReadAsInt()
3497 : *
3498 : * @return a double value.
3499 : */
3500 4003 : double GDALAttribute::ReadAsDouble() const
3501 : {
3502 4003 : const auto nDims = GetDimensionCount();
3503 8006 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3504 4003 : std::vector<size_t> count(1 + nDims, 1);
3505 4003 : double dfRet = 0;
3506 4003 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3507 4003 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3508 4003 : sizeof(dfRet));
3509 8006 : return dfRet;
3510 : }
3511 :
3512 : /************************************************************************/
3513 : /* ReadAsStringArray() */
3514 : /************************************************************************/
3515 :
3516 : /** Return the value of an attribute as an array of strings.
3517 : *
3518 : * This is the same as the C function GDALAttributeReadAsStringArray()
3519 : */
3520 220 : CPLStringList GDALAttribute::ReadAsStringArray() const
3521 : {
3522 220 : const auto nElts = GetTotalElementsCount();
3523 220 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3524 0 : return CPLStringList();
3525 : char **papszList = static_cast<char **>(
3526 220 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3527 220 : const auto &dims = GetDimensions();
3528 220 : const auto nDims = GetDimensionCount();
3529 440 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3530 440 : std::vector<size_t> count(1 + nDims);
3531 350 : for (size_t i = 0; i < nDims; i++)
3532 : {
3533 130 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3534 : }
3535 220 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3536 220 : GDALExtendedDataType::CreateString(), papszList, papszList,
3537 220 : sizeof(char *) * static_cast<int>(nElts));
3538 807 : for (int i = 0; i < static_cast<int>(nElts); i++)
3539 : {
3540 587 : if (papszList[i] == nullptr)
3541 13 : papszList[i] = CPLStrdup("");
3542 : }
3543 220 : return CPLStringList(papszList);
3544 : }
3545 :
3546 : /************************************************************************/
3547 : /* ReadAsIntArray() */
3548 : /************************************************************************/
3549 :
3550 : /** Return the value of an attribute as an array of integers.
3551 : *
3552 : * This is the same as the C function GDALAttributeReadAsIntArray().
3553 : */
3554 18 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3555 : {
3556 18 : const auto nElts = GetTotalElementsCount();
3557 : #if SIZEOF_VOIDP == 4
3558 : if (nElts > static_cast<size_t>(nElts))
3559 : return {};
3560 : #endif
3561 18 : std::vector<int> res(static_cast<size_t>(nElts));
3562 18 : const auto &dims = GetDimensions();
3563 18 : const auto nDims = GetDimensionCount();
3564 36 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3565 36 : std::vector<size_t> count(1 + nDims);
3566 38 : for (size_t i = 0; i < nDims; i++)
3567 : {
3568 20 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3569 : }
3570 18 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3571 36 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3572 18 : res.size() * sizeof(res[0]));
3573 36 : return res;
3574 : }
3575 :
3576 : /************************************************************************/
3577 : /* ReadAsInt64Array() */
3578 : /************************************************************************/
3579 :
3580 : /** Return the value of an attribute as an array of int64_t.
3581 : *
3582 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3583 : */
3584 90 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3585 : {
3586 90 : const auto nElts = GetTotalElementsCount();
3587 : #if SIZEOF_VOIDP == 4
3588 : if (nElts > static_cast<size_t>(nElts))
3589 : return {};
3590 : #endif
3591 90 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3592 90 : const auto &dims = GetDimensions();
3593 90 : const auto nDims = GetDimensionCount();
3594 180 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3595 180 : std::vector<size_t> count(1 + nDims);
3596 180 : for (size_t i = 0; i < nDims; i++)
3597 : {
3598 90 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3599 : }
3600 90 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3601 180 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3602 90 : res.size() * sizeof(res[0]));
3603 180 : return res;
3604 : }
3605 :
3606 : /************************************************************************/
3607 : /* ReadAsDoubleArray() */
3608 : /************************************************************************/
3609 :
3610 : /** Return the value of an attribute as an array of double.
3611 : *
3612 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3613 : */
3614 123 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3615 : {
3616 123 : const auto nElts = GetTotalElementsCount();
3617 : #if SIZEOF_VOIDP == 4
3618 : if (nElts > static_cast<size_t>(nElts))
3619 : return {};
3620 : #endif
3621 123 : std::vector<double> res(static_cast<size_t>(nElts));
3622 123 : const auto &dims = GetDimensions();
3623 123 : const auto nDims = GetDimensionCount();
3624 246 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3625 246 : std::vector<size_t> count(1 + nDims);
3626 230 : for (size_t i = 0; i < nDims; i++)
3627 : {
3628 107 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3629 : }
3630 123 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3631 246 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3632 123 : res.size() * sizeof(res[0]));
3633 246 : return res;
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* Write() */
3638 : /************************************************************************/
3639 :
3640 : /** Write an attribute from raw values expressed in GetDataType()
3641 : *
3642 : * The values should be provided in the type of GetDataType() and there should
3643 : * be exactly GetTotalElementsCount() of them.
3644 : * If GetDataType() is a string, each value should be a char* pointer.
3645 : *
3646 : * This is the same as the C function GDALAttributeWriteRaw().
3647 : *
3648 : * @param pabyValue Buffer of nLen bytes.
3649 : * @param nLen Size of pabyValue in bytes. Should be equal to
3650 : * GetTotalElementsCount() * GetDataType().GetSize()
3651 : * @return true in case of success.
3652 : */
3653 173 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3654 : {
3655 173 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3656 : {
3657 0 : CPLError(CE_Failure, CPLE_AppDefined,
3658 : "Length is not of expected value");
3659 0 : return false;
3660 : }
3661 173 : const auto &dims = GetDimensions();
3662 173 : const auto nDims = GetDimensionCount();
3663 346 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3664 346 : std::vector<size_t> count(1 + nDims);
3665 199 : for (size_t i = 0; i < nDims; i++)
3666 : {
3667 26 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3668 : }
3669 173 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3670 173 : pabyValue, pabyValue, nLen);
3671 : }
3672 :
3673 : /************************************************************************/
3674 : /* Write() */
3675 : /************************************************************************/
3676 :
3677 : /** Write an attribute from a string value.
3678 : *
3679 : * Type conversion will be performed if needed. If the attribute contains
3680 : * multiple values, only the first one will be updated.
3681 : *
3682 : * This is the same as the C function GDALAttributeWriteString().
3683 : *
3684 : * @param pszValue Pointer to a string.
3685 : * @return true in case of success.
3686 : */
3687 418 : bool GDALAttribute::Write(const char *pszValue)
3688 : {
3689 418 : const auto nDims = GetDimensionCount();
3690 836 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3691 418 : std::vector<size_t> count(1 + nDims, 1);
3692 418 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3693 836 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3694 836 : sizeof(pszValue));
3695 : }
3696 :
3697 : /************************************************************************/
3698 : /* WriteInt() */
3699 : /************************************************************************/
3700 :
3701 : /** Write an attribute from a integer value.
3702 : *
3703 : * Type conversion will be performed if needed. If the attribute contains
3704 : * multiple values, only the first one will be updated.
3705 : *
3706 : * This is the same as the C function GDALAttributeWriteInt().
3707 : *
3708 : * @param nVal Value.
3709 : * @return true in case of success.
3710 : */
3711 23 : bool GDALAttribute::WriteInt(int nVal)
3712 : {
3713 23 : const auto nDims = GetDimensionCount();
3714 46 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3715 23 : std::vector<size_t> count(1 + nDims, 1);
3716 23 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3717 46 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3718 46 : sizeof(nVal));
3719 : }
3720 :
3721 : /************************************************************************/
3722 : /* WriteInt64() */
3723 : /************************************************************************/
3724 :
3725 : /** Write an attribute from an int64_t value.
3726 : *
3727 : * Type conversion will be performed if needed. If the attribute contains
3728 : * multiple values, only the first one will be updated.
3729 : *
3730 : * This is the same as the C function GDALAttributeWriteInt().
3731 : *
3732 : * @param nVal Value.
3733 : * @return true in case of success.
3734 : */
3735 14 : bool GDALAttribute::WriteInt64(int64_t nVal)
3736 : {
3737 14 : const auto nDims = GetDimensionCount();
3738 28 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3739 14 : std::vector<size_t> count(1 + nDims, 1);
3740 14 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3741 28 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3742 28 : sizeof(nVal));
3743 : }
3744 :
3745 : /************************************************************************/
3746 : /* Write() */
3747 : /************************************************************************/
3748 :
3749 : /** Write an attribute from a double value.
3750 : *
3751 : * Type conversion will be performed if needed. If the attribute contains
3752 : * multiple values, only the first one will be updated.
3753 : *
3754 : * This is the same as the C function GDALAttributeWriteDouble().
3755 : *
3756 : * @param dfVal Value.
3757 : * @return true in case of success.
3758 : */
3759 40 : bool GDALAttribute::Write(double dfVal)
3760 : {
3761 40 : const auto nDims = GetDimensionCount();
3762 80 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3763 40 : std::vector<size_t> count(1 + nDims, 1);
3764 40 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3765 80 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3766 80 : sizeof(dfVal));
3767 : }
3768 :
3769 : /************************************************************************/
3770 : /* Write() */
3771 : /************************************************************************/
3772 :
3773 : /** Write an attribute from an array of strings.
3774 : *
3775 : * Type conversion will be performed if needed.
3776 : *
3777 : * Exactly GetTotalElementsCount() strings must be provided
3778 : *
3779 : * This is the same as the C function GDALAttributeWriteStringArray().
3780 : *
3781 : * @param vals Array of strings.
3782 : * @return true in case of success.
3783 : */
3784 9 : bool GDALAttribute::Write(CSLConstList vals)
3785 : {
3786 9 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3787 : {
3788 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3789 1 : return false;
3790 : }
3791 8 : const auto nDims = GetDimensionCount();
3792 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3793 8 : std::vector<size_t> count(1 + nDims);
3794 8 : const auto &dims = GetDimensions();
3795 17 : for (size_t i = 0; i < nDims; i++)
3796 9 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3797 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3798 8 : GDALExtendedDataType::CreateString(), vals, vals,
3799 16 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3800 : }
3801 :
3802 : /************************************************************************/
3803 : /* Write() */
3804 : /************************************************************************/
3805 :
3806 : /** Write an attribute from an array of int.
3807 : *
3808 : * Type conversion will be performed if needed.
3809 : *
3810 : * Exactly GetTotalElementsCount() strings must be provided
3811 : *
3812 : * This is the same as the C function GDALAttributeWriteIntArray()
3813 : *
3814 : * @param vals Array of int.
3815 : * @param nVals Should be equal to GetTotalElementsCount().
3816 : * @return true in case of success.
3817 : */
3818 12 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3819 : {
3820 12 : if (nVals != GetTotalElementsCount())
3821 : {
3822 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3823 1 : return false;
3824 : }
3825 11 : const auto nDims = GetDimensionCount();
3826 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3827 11 : std::vector<size_t> count(1 + nDims);
3828 11 : const auto &dims = GetDimensions();
3829 22 : for (size_t i = 0; i < nDims; i++)
3830 11 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3831 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3832 11 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3833 22 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3834 : }
3835 :
3836 : /************************************************************************/
3837 : /* Write() */
3838 : /************************************************************************/
3839 :
3840 : /** Write an attribute from an array of int64_t.
3841 : *
3842 : * Type conversion will be performed if needed.
3843 : *
3844 : * Exactly GetTotalElementsCount() strings must be provided
3845 : *
3846 : * This is the same as the C function GDALAttributeWriteLongArray()
3847 : *
3848 : * @param vals Array of int64_t.
3849 : * @param nVals Should be equal to GetTotalElementsCount().
3850 : * @return true in case of success.
3851 : */
3852 13 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3853 : {
3854 13 : if (nVals != GetTotalElementsCount())
3855 : {
3856 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3857 0 : return false;
3858 : }
3859 13 : const auto nDims = GetDimensionCount();
3860 26 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3861 13 : std::vector<size_t> count(1 + nDims);
3862 13 : const auto &dims = GetDimensions();
3863 26 : for (size_t i = 0; i < nDims; i++)
3864 13 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3865 13 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3866 13 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3867 13 : static_cast<size_t>(GetTotalElementsCount()) *
3868 13 : sizeof(int64_t));
3869 : }
3870 :
3871 : /************************************************************************/
3872 : /* Write() */
3873 : /************************************************************************/
3874 :
3875 : /** Write an attribute from an array of double.
3876 : *
3877 : * Type conversion will be performed if needed.
3878 : *
3879 : * Exactly GetTotalElementsCount() strings must be provided
3880 : *
3881 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3882 : *
3883 : * @param vals Array of double.
3884 : * @param nVals Should be equal to GetTotalElementsCount().
3885 : * @return true in case of success.
3886 : */
3887 8 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3888 : {
3889 8 : if (nVals != GetTotalElementsCount())
3890 : {
3891 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3892 1 : return false;
3893 : }
3894 7 : const auto nDims = GetDimensionCount();
3895 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3896 7 : std::vector<size_t> count(1 + nDims);
3897 7 : const auto &dims = GetDimensions();
3898 15 : for (size_t i = 0; i < nDims; i++)
3899 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3900 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3901 7 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3902 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3903 : }
3904 :
3905 : /************************************************************************/
3906 : /* GDALMDArray() */
3907 : /************************************************************************/
3908 :
3909 : //! @cond Doxygen_Suppress
3910 10069 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3911 : CPL_UNUSED const std::string &osName,
3912 0 : const std::string &osContext)
3913 : :
3914 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3915 : GDALAbstractMDArray(osParentName, osName),
3916 : #endif
3917 10069 : m_osContext(osContext)
3918 : {
3919 10069 : }
3920 :
3921 : //! @endcond
3922 :
3923 : /************************************************************************/
3924 : /* GetTotalCopyCost() */
3925 : /************************************************************************/
3926 :
3927 : /** Return a total "cost" to copy the array.
3928 : *
3929 : * Used as a parameter for CopyFrom()
3930 : */
3931 73 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3932 : {
3933 146 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3934 146 : GetTotalElementsCount() * GetDataType().GetSize();
3935 : }
3936 :
3937 : /************************************************************************/
3938 : /* CopyFromAllExceptValues() */
3939 : /************************************************************************/
3940 :
3941 : //! @cond Doxygen_Suppress
3942 :
3943 214 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3944 : bool bStrict, GUInt64 &nCurCost,
3945 : const GUInt64 nTotalCost,
3946 : GDALProgressFunc pfnProgress,
3947 : void *pProgressData)
3948 : {
3949 : // Nodata setting must be one of the first things done for TileDB
3950 214 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3951 214 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3952 : {
3953 13 : SetRawNoDataValue(pNoData);
3954 : }
3955 :
3956 214 : const bool bThisIsUnscaledArray =
3957 214 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3958 428 : auto attrs = poSrcArray->GetAttributes();
3959 297 : for (const auto &attr : attrs)
3960 : {
3961 83 : const auto &osAttrName = attr->GetName();
3962 83 : if (bThisIsUnscaledArray)
3963 : {
3964 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3965 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3966 1 : osAttrName == "valid_range")
3967 : {
3968 1 : continue;
3969 : }
3970 : }
3971 :
3972 82 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3973 164 : attr->GetDataType());
3974 82 : if (!dstAttr)
3975 : {
3976 0 : if (bStrict)
3977 0 : return false;
3978 0 : continue;
3979 : }
3980 82 : auto raw = attr->ReadAsRaw();
3981 82 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3982 0 : return false;
3983 : }
3984 214 : if (!attrs.empty())
3985 : {
3986 47 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3987 81 : if (pfnProgress &&
3988 34 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3989 0 : return false;
3990 : }
3991 :
3992 214 : auto srcSRS = poSrcArray->GetSpatialRef();
3993 214 : if (srcSRS)
3994 : {
3995 22 : SetSpatialRef(srcSRS.get());
3996 : }
3997 :
3998 214 : const std::string &osUnit(poSrcArray->GetUnit());
3999 214 : if (!osUnit.empty())
4000 : {
4001 24 : SetUnit(osUnit);
4002 : }
4003 :
4004 214 : bool bGotValue = false;
4005 214 : GDALDataType eOffsetStorageType = GDT_Unknown;
4006 : const double dfOffset =
4007 214 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
4008 214 : if (bGotValue)
4009 : {
4010 3 : SetOffset(dfOffset, eOffsetStorageType);
4011 : }
4012 :
4013 214 : bGotValue = false;
4014 214 : GDALDataType eScaleStorageType = GDT_Unknown;
4015 214 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
4016 214 : if (bGotValue)
4017 : {
4018 3 : SetScale(dfScale, eScaleStorageType);
4019 : }
4020 :
4021 214 : return true;
4022 : }
4023 :
4024 : //! @endcond
4025 :
4026 : /************************************************************************/
4027 : /* CopyFrom() */
4028 : /************************************************************************/
4029 :
4030 : /** Copy the content of an array into a new (generally empty) array.
4031 : *
4032 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4033 : * of some output drivers this is not recommended)
4034 : * @param poSrcArray Source array. Should NOT be nullptr.
4035 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4036 : * stop the copy. In relaxed mode, the copy will be attempted to
4037 : * be pursued.
4038 : * @param nCurCost Should be provided as a variable initially set to 0.
4039 : * @param nTotalCost Total cost from GetTotalCopyCost().
4040 : * @param pfnProgress Progress callback, or nullptr.
4041 : * @param pProgressData Progress user data, or nullptr.
4042 : *
4043 : * @return true in case of success (or partial success if bStrict == false).
4044 : */
4045 68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4046 : const GDALMDArray *poSrcArray, bool bStrict,
4047 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4048 : GDALProgressFunc pfnProgress, void *pProgressData)
4049 : {
4050 68 : if (pfnProgress == nullptr)
4051 4 : pfnProgress = GDALDummyProgress;
4052 :
4053 68 : nCurCost += GDALMDArray::COPY_COST;
4054 :
4055 68 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4056 : pfnProgress, pProgressData))
4057 : {
4058 0 : return false;
4059 : }
4060 :
4061 68 : const auto &dims = poSrcArray->GetDimensions();
4062 68 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4063 68 : if (dims.empty())
4064 : {
4065 2 : std::vector<GByte> abyTmp(nDTSize);
4066 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4067 2 : GetDataType(), &abyTmp[0]) &&
4068 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4069 4 : &abyTmp[0])) &&
4070 : bStrict)
4071 : {
4072 0 : return false;
4073 : }
4074 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4075 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4076 0 : return false;
4077 : }
4078 : else
4079 : {
4080 66 : std::vector<GUInt64> arrayStartIdx(dims.size());
4081 66 : std::vector<GUInt64> count(dims.size());
4082 172 : for (size_t i = 0; i < dims.size(); i++)
4083 : {
4084 106 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4085 : }
4086 :
4087 : struct CopyFunc
4088 : {
4089 : GDALMDArray *poDstArray = nullptr;
4090 : std::vector<GByte> abyTmp{};
4091 : GDALProgressFunc pfnProgress = nullptr;
4092 : void *pProgressData = nullptr;
4093 : GUInt64 nCurCost = 0;
4094 : GUInt64 nTotalCost = 0;
4095 : GUInt64 nTotalBytesThisArray = 0;
4096 : bool bStop = false;
4097 :
4098 84 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4099 : const GUInt64 *chunkArrayStartIdx,
4100 : const size_t *chunkCount, GUInt64 iCurChunk,
4101 : GUInt64 nChunkCount, void *pUserData)
4102 : {
4103 84 : const auto &dt(l_poSrcArray->GetDataType());
4104 84 : auto data = static_cast<CopyFunc *>(pUserData);
4105 84 : auto poDstArray = data->poDstArray;
4106 84 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4107 84 : nullptr, dt, &data->abyTmp[0]))
4108 : {
4109 1 : return false;
4110 : }
4111 : bool bRet =
4112 83 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4113 83 : nullptr, dt, &data->abyTmp[0]);
4114 83 : if (dt.NeedsFreeDynamicMemory())
4115 : {
4116 5 : const auto l_nDTSize = dt.GetSize();
4117 5 : GByte *ptr = &data->abyTmp[0];
4118 5 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4119 5 : size_t nEltCount = 1;
4120 10 : for (size_t i = 0; i < l_nDims; ++i)
4121 : {
4122 5 : nEltCount *= chunkCount[i];
4123 : }
4124 22 : for (size_t i = 0; i < nEltCount; i++)
4125 : {
4126 17 : dt.FreeDynamicMemory(ptr);
4127 17 : ptr += l_nDTSize;
4128 : }
4129 : }
4130 83 : if (!bRet)
4131 : {
4132 0 : return false;
4133 : }
4134 :
4135 83 : double dfCurCost =
4136 83 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4137 83 : data->nTotalBytesThisArray;
4138 83 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4139 : data->pProgressData))
4140 : {
4141 0 : data->bStop = true;
4142 0 : return false;
4143 : }
4144 :
4145 83 : return true;
4146 : }
4147 : };
4148 :
4149 66 : CopyFunc copyFunc;
4150 66 : copyFunc.poDstArray = this;
4151 66 : copyFunc.nCurCost = nCurCost;
4152 66 : copyFunc.nTotalCost = nTotalCost;
4153 66 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4154 66 : copyFunc.pfnProgress = pfnProgress;
4155 66 : copyFunc.pProgressData = pProgressData;
4156 : const char *pszSwathSize =
4157 66 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4158 : const size_t nMaxChunkSize =
4159 : pszSwathSize
4160 66 : ? static_cast<size_t>(
4161 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4162 1 : CPLAtoGIntBig(pszSwathSize)))
4163 : : static_cast<size_t>(
4164 65 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4165 65 : GDALGetCacheMax64() / 4));
4166 66 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4167 66 : size_t nRealChunkSize = nDTSize;
4168 172 : for (const auto &nChunkSize : anChunkSizes)
4169 : {
4170 106 : nRealChunkSize *= nChunkSize;
4171 : }
4172 : try
4173 : {
4174 66 : copyFunc.abyTmp.resize(nRealChunkSize);
4175 : }
4176 0 : catch (const std::exception &)
4177 : {
4178 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4179 : "Cannot allocate temporary buffer");
4180 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4181 0 : return false;
4182 : }
4183 197 : if (copyFunc.nTotalBytesThisArray != 0 &&
4184 65 : !const_cast<GDALMDArray *>(poSrcArray)
4185 65 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4186 : anChunkSizes.data(), CopyFunc::f,
4187 132 : ©Func) &&
4188 1 : (bStrict || copyFunc.bStop))
4189 : {
4190 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4191 0 : return false;
4192 : }
4193 66 : nCurCost += copyFunc.nTotalBytesThisArray;
4194 : }
4195 :
4196 68 : return true;
4197 : }
4198 :
4199 : /************************************************************************/
4200 : /* GetStructuralInfo() */
4201 : /************************************************************************/
4202 :
4203 : /** Return structural information on the array.
4204 : *
4205 : * This may be the compression, etc..
4206 : *
4207 : * The return value should not be freed and is valid until GDALMDArray is
4208 : * released or this function called again.
4209 : *
4210 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4211 : */
4212 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4213 : {
4214 95 : return nullptr;
4215 : }
4216 :
4217 : /************************************************************************/
4218 : /* AdviseRead() */
4219 : /************************************************************************/
4220 :
4221 : /** Advise driver of upcoming read requests.
4222 : *
4223 : * Some GDAL drivers operate more efficiently if they know in advance what
4224 : * set of upcoming read requests will be made. The AdviseRead() method allows
4225 : * an application to notify the driver of the region of interest.
4226 : *
4227 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4228 : * accelerate access via some drivers. One such case is when reading through
4229 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4230 : * with the region of interest defined by AdviseRead())
4231 : *
4232 : * This is the same as the C function GDALMDArrayAdviseRead().
4233 : *
4234 : * @param arrayStartIdx Values representing the starting index to read
4235 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4236 : * Array of GetDimensionCount() values.
4237 : * Can be nullptr as a synonymous for [0 for i in
4238 : * range(GetDimensionCount() ]
4239 : *
4240 : * @param count Values representing the number of values to extract in
4241 : * each dimension.
4242 : * Array of GetDimensionCount() values.
4243 : * Can be nullptr as a synonymous for
4244 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4245 : * range(GetDimensionCount() ]
4246 : *
4247 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4248 : * documentation.
4249 : *
4250 : * @return true in case of success (ignoring the advice is a success)
4251 : *
4252 : * @since GDAL 3.2
4253 : */
4254 68 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4255 : CSLConstList papszOptions) const
4256 : {
4257 68 : const auto nDimCount = GetDimensionCount();
4258 68 : if (nDimCount == 0)
4259 2 : return true;
4260 :
4261 132 : std::vector<GUInt64> tmp_arrayStartIdx;
4262 66 : if (arrayStartIdx == nullptr)
4263 : {
4264 0 : tmp_arrayStartIdx.resize(nDimCount);
4265 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4266 : }
4267 :
4268 132 : std::vector<size_t> tmp_count;
4269 66 : if (count == nullptr)
4270 : {
4271 0 : tmp_count.resize(nDimCount);
4272 0 : const auto &dims = GetDimensions();
4273 0 : for (size_t i = 0; i < nDimCount; i++)
4274 : {
4275 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4276 : #if SIZEOF_VOIDP < 8
4277 : if (nSize != static_cast<size_t>(nSize))
4278 : {
4279 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4280 : return false;
4281 : }
4282 : #endif
4283 0 : tmp_count[i] = static_cast<size_t>(nSize);
4284 : }
4285 0 : count = tmp_count.data();
4286 : }
4287 :
4288 132 : std::vector<GInt64> tmp_arrayStep;
4289 132 : std::vector<GPtrDiff_t> tmp_bufferStride;
4290 66 : const GInt64 *arrayStep = nullptr;
4291 66 : const GPtrDiff_t *bufferStride = nullptr;
4292 66 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4293 132 : GDALExtendedDataType::Create(GDT_Unknown),
4294 : nullptr, nullptr, 0, tmp_arrayStep,
4295 : tmp_bufferStride))
4296 : {
4297 2 : return false;
4298 : }
4299 :
4300 64 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4301 : }
4302 :
4303 : /************************************************************************/
4304 : /* IAdviseRead() */
4305 : /************************************************************************/
4306 :
4307 : //! @cond Doxygen_Suppress
4308 19 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4309 : CSLConstList /* papszOptions*/) const
4310 : {
4311 19 : return true;
4312 : }
4313 :
4314 : //! @endcond
4315 :
4316 : /************************************************************************/
4317 : /* MassageName() */
4318 : /************************************************************************/
4319 :
4320 : //! @cond Doxygen_Suppress
4321 49 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4322 : {
4323 49 : std::string ret;
4324 717 : for (const char ch : inputName)
4325 : {
4326 668 : if (!isalnum(static_cast<unsigned char>(ch)))
4327 156 : ret += '_';
4328 : else
4329 512 : ret += ch;
4330 : }
4331 49 : return ret;
4332 : }
4333 :
4334 : //! @endcond
4335 :
4336 : /************************************************************************/
4337 : /* GetCacheRootGroup() */
4338 : /************************************************************************/
4339 :
4340 : //! @cond Doxygen_Suppress
4341 : std::shared_ptr<GDALGroup>
4342 3554 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4343 : std::string &osCacheFilenameOut) const
4344 : {
4345 3554 : const auto &osFilename = GetFilename();
4346 3554 : if (osFilename.empty())
4347 : {
4348 1 : CPLError(CE_Failure, CPLE_AppDefined,
4349 : "Cannot cache an array with an empty filename");
4350 1 : return nullptr;
4351 : }
4352 :
4353 3553 : osCacheFilenameOut = osFilename + ".gmac";
4354 3553 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4355 : {
4356 2 : const auto nPosQuestionMark = osFilename.find('?');
4357 2 : if (nPosQuestionMark != std::string::npos)
4358 : {
4359 : osCacheFilenameOut =
4360 0 : osFilename.substr(0, nPosQuestionMark)
4361 0 : .append(".gmac")
4362 0 : .append(osFilename.substr(nPosQuestionMark));
4363 : }
4364 : }
4365 3553 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4366 3553 : if (pszProxy != nullptr)
4367 7 : osCacheFilenameOut = pszProxy;
4368 :
4369 : // .gmac sidecars are local-only; skip stat for non-local filesystems.
4370 7086 : if (!bCanCreate && pszProxy == nullptr &&
4371 3533 : !VSIIsLocal(osCacheFilenameOut.c_str()))
4372 : {
4373 2 : return nullptr;
4374 : }
4375 :
4376 3551 : std::unique_ptr<GDALDataset> poDS;
4377 : VSIStatBufL sStat;
4378 3551 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4379 : {
4380 42 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4381 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4382 : nullptr, nullptr, nullptr));
4383 : }
4384 3551 : if (poDS)
4385 : {
4386 42 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4387 42 : return poDS->GetRootGroup();
4388 : }
4389 :
4390 3509 : if (bCanCreate)
4391 : {
4392 7 : const char *pszDrvName = "netCDF";
4393 7 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4394 7 : if (poDrv == nullptr)
4395 : {
4396 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4397 : pszDrvName);
4398 0 : return nullptr;
4399 : }
4400 : {
4401 14 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4402 14 : CPLErrorStateBackuper oErrorStateBackuper;
4403 7 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4404 : nullptr, nullptr));
4405 : }
4406 7 : if (!poDS)
4407 : {
4408 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4409 1 : if (pszProxy)
4410 : {
4411 1 : osCacheFilenameOut = pszProxy;
4412 1 : poDS.reset(poDrv->CreateMultiDimensional(
4413 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4414 : }
4415 : }
4416 7 : if (poDS)
4417 : {
4418 7 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4419 7 : return poDS->GetRootGroup();
4420 : }
4421 : else
4422 : {
4423 0 : CPLError(CE_Failure, CPLE_AppDefined,
4424 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4425 : "configuration option to write the cache in "
4426 : "another directory",
4427 : osCacheFilenameOut.c_str());
4428 : }
4429 : }
4430 :
4431 3502 : return nullptr;
4432 : }
4433 :
4434 : //! @endcond
4435 :
4436 : /************************************************************************/
4437 : /* Cache() */
4438 : /************************************************************************/
4439 :
4440 : /** Cache the content of the array into an auxiliary filename.
4441 : *
4442 : * The main purpose of this method is to be able to cache views that are
4443 : * expensive to compute, such as transposed arrays.
4444 : *
4445 : * The array will be stored in a file whose name is the one of
4446 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4447 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4448 : *
4449 : * If the .gmac file cannot be written next to the dataset, the
4450 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4451 : * directory.
4452 : *
4453 : * The GDALMDArray::Read() method will automatically use the cache when it
4454 : * exists. There is no timestamp checks between the source array and the cached
4455 : * array. If the source arrays changes, the cache must be manually deleted.
4456 : *
4457 : * This is the same as the C function GDALMDArrayCache()
4458 : *
4459 : * @note Driver implementation: optionally implemented.
4460 : *
4461 : * @param papszOptions List of options, null terminated, or NULL. Currently
4462 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4463 : * to specify the block size of the cached array.
4464 : * @return true in case of success.
4465 : */
4466 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4467 : {
4468 14 : std::string osCacheFilename;
4469 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4470 7 : if (!poRG)
4471 1 : return false;
4472 :
4473 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4474 6 : if (poRG->OpenMDArray(osCachedArrayName))
4475 : {
4476 2 : CPLError(CE_Failure, CPLE_NotSupported,
4477 : "An array with same name %s already exists in %s",
4478 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4479 2 : return false;
4480 : }
4481 :
4482 8 : CPLStringList aosOptions;
4483 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4484 4 : const auto &aoDims = GetDimensions();
4485 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4486 4 : if (!aoDims.empty())
4487 : {
4488 : std::string osBlockSize(
4489 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4490 4 : if (osBlockSize.empty())
4491 : {
4492 6 : const auto anBlockSize = GetBlockSize();
4493 3 : int idxDim = 0;
4494 10 : for (auto nBlockSize : anBlockSize)
4495 : {
4496 7 : if (idxDim > 0)
4497 4 : osBlockSize += ',';
4498 7 : if (nBlockSize == 0)
4499 7 : nBlockSize = 256;
4500 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4501 : osBlockSize +=
4502 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4503 7 : idxDim++;
4504 : }
4505 : }
4506 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4507 :
4508 4 : int idxDim = 0;
4509 13 : for (const auto &poDim : aoDims)
4510 : {
4511 9 : auto poNewDim = poRG->CreateDimension(
4512 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4513 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4514 9 : if (!poNewDim)
4515 0 : return false;
4516 9 : aoNewDims.emplace_back(poNewDim);
4517 9 : idxDim++;
4518 : }
4519 : }
4520 :
4521 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4522 8 : GetDataType(), aosOptions.List());
4523 4 : if (!poCachedArray)
4524 : {
4525 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4526 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4527 0 : return false;
4528 : }
4529 :
4530 4 : GUInt64 nCost = 0;
4531 8 : return poCachedArray->CopyFrom(nullptr, this,
4532 : false, // strict
4533 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4534 : }
4535 :
4536 : /************************************************************************/
4537 : /* Read() */
4538 : /************************************************************************/
4539 :
4540 6438 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4541 : const GInt64 *arrayStep, // step in elements
4542 : const GPtrDiff_t *bufferStride, // stride in elements
4543 : const GDALExtendedDataType &bufferDataType,
4544 : void *pDstBuffer, const void *pDstBufferAllocStart,
4545 : size_t nDstBufferAllocSize) const
4546 : {
4547 6438 : if (!m_bHasTriedCachedArray)
4548 : {
4549 3469 : m_bHasTriedCachedArray = true;
4550 3469 : if (IsCacheable())
4551 : {
4552 3469 : const auto &osFilename = GetFilename();
4553 6216 : if (!osFilename.empty() &&
4554 6216 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4555 : {
4556 5458 : std::string osCacheFilename;
4557 5458 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4558 2729 : if (poRG)
4559 : {
4560 : const std::string osCachedArrayName(
4561 44 : MassageName(GetFullName()));
4562 22 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4563 22 : if (m_poCachedArray)
4564 : {
4565 6 : const auto &dims = GetDimensions();
4566 : const auto &cachedDims =
4567 6 : m_poCachedArray->GetDimensions();
4568 6 : const size_t nDims = dims.size();
4569 : bool ok =
4570 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4571 6 : cachedDims.size() == nDims;
4572 19 : for (size_t i = 0; ok && i < nDims; ++i)
4573 : {
4574 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4575 : }
4576 6 : if (ok)
4577 : {
4578 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4579 : osCachedArrayName.c_str(),
4580 : osCacheFilename.c_str());
4581 : }
4582 : else
4583 : {
4584 0 : CPLError(CE_Warning, CPLE_AppDefined,
4585 : "Cached array %s in %s has incompatible "
4586 : "characteristics with current array.",
4587 : osCachedArrayName.c_str(),
4588 : osCacheFilename.c_str());
4589 0 : m_poCachedArray.reset();
4590 : }
4591 : }
4592 : }
4593 : }
4594 : }
4595 : }
4596 :
4597 6438 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4598 6438 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4599 : {
4600 0 : CPLError(CE_Failure, CPLE_AppDefined,
4601 : "Array data type is not convertible to buffer data type");
4602 0 : return false;
4603 : }
4604 :
4605 12876 : std::vector<GInt64> tmp_arrayStep;
4606 12876 : std::vector<GPtrDiff_t> tmp_bufferStride;
4607 6438 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4608 : bufferStride, bufferDataType, pDstBuffer,
4609 : pDstBufferAllocStart, nDstBufferAllocSize,
4610 : tmp_arrayStep, tmp_bufferStride))
4611 : {
4612 9 : return false;
4613 : }
4614 :
4615 6429 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4616 6429 : bufferDataType, pDstBuffer);
4617 : }
4618 :
4619 : /************************************************************************/
4620 : /* GetRootGroup() */
4621 : /************************************************************************/
4622 :
4623 : /** Return the root group to which this arrays belongs too.
4624 : *
4625 : * Note that arrays may be free standing and some drivers may not implement
4626 : * this method, hence nullptr may be returned.
4627 : *
4628 : * It is used internally by the GetResampled() method to detect if GLT
4629 : * orthorectification is available.
4630 : *
4631 : * @return the root group, or nullptr.
4632 : * @since GDAL 3.8
4633 : */
4634 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4635 : {
4636 0 : return nullptr;
4637 : }
4638 :
4639 : //! @cond Doxygen_Suppress
4640 :
4641 : /************************************************************************/
4642 : /* IsTransposedRequest() */
4643 : /************************************************************************/
4644 :
4645 1541 : bool GDALMDArray::IsTransposedRequest(
4646 : const size_t *count,
4647 : const GPtrDiff_t *bufferStride) const // stride in elements
4648 : {
4649 : /*
4650 : For example:
4651 : count = [2,3,4]
4652 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4653 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4654 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4655 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4656 : */
4657 1541 : const size_t nDims(GetDimensionCount());
4658 1541 : size_t nCurStrideForRowMajorStrides = 1;
4659 1541 : bool bRowMajorStrides = true;
4660 1541 : size_t nElts = 1;
4661 1541 : size_t nLastIdx = 0;
4662 3885 : for (size_t i = nDims; i > 0;)
4663 : {
4664 2344 : --i;
4665 2344 : if (bufferStride[i] < 0)
4666 0 : return false;
4667 2344 : if (static_cast<size_t>(bufferStride[i]) !=
4668 : nCurStrideForRowMajorStrides)
4669 : {
4670 482 : bRowMajorStrides = false;
4671 : }
4672 : // Integer overflows have already been checked in CheckReadWriteParams()
4673 2344 : nCurStrideForRowMajorStrides *= count[i];
4674 2344 : nElts *= count[i];
4675 2344 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4676 : }
4677 1541 : if (bRowMajorStrides)
4678 1169 : return false;
4679 372 : return nLastIdx == nElts - 1;
4680 : }
4681 :
4682 : /************************************************************************/
4683 : /* CopyToFinalBufferSameDataType() */
4684 : /************************************************************************/
4685 :
4686 : template <size_t N>
4687 104 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4688 : size_t nDims, const size_t *count,
4689 : const GPtrDiff_t *bufferStride)
4690 : {
4691 208 : std::vector<size_t> anStackCount(nDims);
4692 208 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4693 104 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4694 : #if defined(__GNUC__)
4695 : #pragma GCC diagnostic push
4696 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4697 : #endif
4698 104 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4699 : #if defined(__GNUC__)
4700 : #pragma GCC diagnostic pop
4701 : #endif
4702 104 : size_t iDim = 0;
4703 :
4704 647 : lbl_next_depth:
4705 647 : if (iDim == nDims - 1)
4706 : {
4707 531 : size_t n = count[iDim];
4708 531 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4709 531 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4710 18246 : while (n > 0)
4711 : {
4712 17715 : --n;
4713 17715 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4714 17715 : pabyDstBuffer += bufferStrideLastDim;
4715 17715 : pabySrcBuffer += N;
4716 : }
4717 : }
4718 : else
4719 : {
4720 116 : anStackCount[iDim] = count[iDim];
4721 : while (true)
4722 : {
4723 543 : ++iDim;
4724 543 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4725 543 : goto lbl_next_depth;
4726 543 : lbl_return_to_caller_in_loop:
4727 543 : --iDim;
4728 543 : --anStackCount[iDim];
4729 543 : if (anStackCount[iDim] == 0)
4730 116 : break;
4731 427 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4732 : }
4733 : }
4734 647 : if (iDim > 0)
4735 543 : goto lbl_return_to_caller_in_loop;
4736 104 : }
4737 :
4738 : /************************************************************************/
4739 : /* CopyToFinalBuffer() */
4740 : /************************************************************************/
4741 :
4742 352 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4743 : const GDALExtendedDataType &eSrcDataType,
4744 : void *pDstBuffer,
4745 : const GDALExtendedDataType &eDstDataType,
4746 : size_t nDims, const size_t *count,
4747 : const GPtrDiff_t *bufferStride)
4748 : {
4749 352 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4750 : // Use specialized implementation for well-known data types when no
4751 : // type conversion is needed
4752 352 : if (eSrcDataType == eDstDataType)
4753 : {
4754 157 : if (nSrcDataTypeSize == 1)
4755 : {
4756 77 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4757 : count, bufferStride);
4758 104 : return;
4759 : }
4760 80 : else if (nSrcDataTypeSize == 2)
4761 : {
4762 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4763 : count, bufferStride);
4764 1 : return;
4765 : }
4766 79 : else if (nSrcDataTypeSize == 4)
4767 : {
4768 18 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4769 : count, bufferStride);
4770 18 : return;
4771 : }
4772 61 : else if (nSrcDataTypeSize == 8)
4773 : {
4774 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4775 : count, bufferStride);
4776 8 : return;
4777 : }
4778 : }
4779 :
4780 248 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4781 496 : std::vector<size_t> anStackCount(nDims);
4782 496 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4783 248 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4784 248 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4785 248 : size_t iDim = 0;
4786 :
4787 517 : lbl_next_depth:
4788 517 : if (iDim == nDims - 1)
4789 : {
4790 507 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4791 507 : pabyDstBufferStack[iDim], eDstDataType,
4792 507 : bufferStride[iDim], count[iDim]);
4793 507 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4794 : }
4795 : else
4796 : {
4797 10 : anStackCount[iDim] = count[iDim];
4798 : while (true)
4799 : {
4800 269 : ++iDim;
4801 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4802 269 : goto lbl_next_depth;
4803 269 : lbl_return_to_caller_in_loop:
4804 269 : --iDim;
4805 269 : --anStackCount[iDim];
4806 269 : if (anStackCount[iDim] == 0)
4807 10 : break;
4808 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4809 : }
4810 : }
4811 517 : if (iDim > 0)
4812 269 : goto lbl_return_to_caller_in_loop;
4813 : }
4814 :
4815 : /************************************************************************/
4816 : /* TransposeLast2Dims() */
4817 : /************************************************************************/
4818 :
4819 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4820 : const GDALExtendedDataType &eDT,
4821 : const size_t nDims, const size_t *count,
4822 : const size_t nEltsNonLast2Dims)
4823 : {
4824 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4825 19 : const auto nDTSize = eDT.GetSize();
4826 : void *pTempBufferForLast2DimsTranspose =
4827 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4828 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4829 0 : return false;
4830 :
4831 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4832 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4833 : {
4834 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4835 : pTempBufferForLast2DimsTranspose,
4836 39 : eDT.GetNumericDataType(), count[nDims - 1],
4837 39 : count[nDims - 2]);
4838 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4839 : nDTSize * nEltsLast2Dims);
4840 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4841 : }
4842 :
4843 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4844 :
4845 19 : return true;
4846 : }
4847 :
4848 : /************************************************************************/
4849 : /* ReadForTransposedRequest() */
4850 : /************************************************************************/
4851 :
4852 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4853 : // transposed view yield to extremely poor/unusable performance. This fixes
4854 : // this by using temporary memory to read in a contiguous buffer in a
4855 : // row-major order, and then do the transposition to the final buffer.
4856 :
4857 371 : bool GDALMDArray::ReadForTransposedRequest(
4858 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4859 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4860 : void *pDstBuffer) const
4861 : {
4862 371 : const size_t nDims(GetDimensionCount());
4863 371 : if (nDims == 0)
4864 : {
4865 0 : CPLAssert(false);
4866 : return false; // shouldn't happen
4867 : }
4868 371 : size_t nElts = 1;
4869 892 : for (size_t i = 0; i < nDims; ++i)
4870 521 : nElts *= count[i];
4871 :
4872 742 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4873 371 : tmpBufferStrides.back() = 1;
4874 521 : for (size_t i = nDims - 1; i > 0;)
4875 : {
4876 150 : --i;
4877 150 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4878 : }
4879 :
4880 371 : const auto &eDT = GetDataType();
4881 371 : const auto nDTSize = eDT.GetSize();
4882 547 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4883 563 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4884 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4885 : {
4886 : // Optimization of the optimization if only the last 2 dims are
4887 : // transposed that saves on temporary buffer allocation
4888 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4889 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4890 23 : bool bRowMajorStridesForNonLast2Dims = true;
4891 23 : size_t nEltsNonLast2Dims = 1;
4892 40 : for (size_t i = nDims - 2; i > 0;)
4893 : {
4894 17 : --i;
4895 17 : if (static_cast<size_t>(bufferStride[i]) !=
4896 : nCurStrideForRowMajorStrides)
4897 : {
4898 4 : bRowMajorStridesForNonLast2Dims = false;
4899 : }
4900 : // Integer overflows have already been checked in
4901 : // CheckReadWriteParams()
4902 17 : nCurStrideForRowMajorStrides *= count[i];
4903 17 : nEltsNonLast2Dims *= count[i];
4904 : }
4905 23 : if (bRowMajorStridesForNonLast2Dims)
4906 : {
4907 : // We read in the final buffer!
4908 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4909 19 : eDT, pDstBuffer))
4910 : {
4911 0 : return false;
4912 : }
4913 :
4914 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4915 19 : nEltsNonLast2Dims);
4916 : }
4917 : }
4918 :
4919 352 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4920 352 : if (pTempBuffer == nullptr)
4921 0 : return false;
4922 :
4923 352 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4924 352 : pTempBuffer))
4925 : {
4926 0 : VSIFree(pTempBuffer);
4927 0 : return false;
4928 : }
4929 352 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4930 : count, bufferStride);
4931 :
4932 352 : if (eDT.NeedsFreeDynamicMemory())
4933 : {
4934 237 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4935 474 : for (size_t i = 0; i < nElts; ++i)
4936 : {
4937 237 : eDT.FreeDynamicMemory(pabyPtr);
4938 237 : pabyPtr += nDTSize;
4939 : }
4940 : }
4941 :
4942 352 : VSIFree(pTempBuffer);
4943 352 : return true;
4944 : }
4945 :
4946 : /************************************************************************/
4947 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4948 : /************************************************************************/
4949 :
4950 : // Returns true if at all following conditions are met:
4951 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4952 : // defines a row-major ordered contiguous buffer.
4953 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4954 : const size_t *count, const GInt64 *arrayStep,
4955 : const GPtrDiff_t *bufferStride,
4956 : const GDALExtendedDataType &bufferDataType) const
4957 : {
4958 78 : if (bufferDataType != GetDataType())
4959 5 : return false;
4960 73 : size_t nExpectedStride = 1;
4961 166 : for (size_t i = GetDimensionCount(); i > 0;)
4962 : {
4963 96 : --i;
4964 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4965 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4966 : {
4967 3 : return false;
4968 : }
4969 93 : nExpectedStride *= count[i];
4970 : }
4971 70 : return true;
4972 : }
4973 :
4974 : /************************************************************************/
4975 : /* ReadUsingContiguousIRead() */
4976 : /************************************************************************/
4977 :
4978 : // Used for example by the TileDB driver when requesting it with
4979 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4980 : // not defining a row-major ordered contiguous buffer.
4981 : // Should only be called when at least one of the above conditions are true,
4982 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4983 : // returning none.
4984 : // This method will call IRead() again with arrayStep[] == 1,
4985 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4986 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4987 : // content of that temporary buffer onto pDstBuffer.
4988 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4989 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4990 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4991 : void *pDstBuffer) const
4992 : {
4993 7 : const size_t nDims(GetDimensionCount());
4994 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4995 14 : std::vector<size_t> anTmpCount(nDims);
4996 7 : const auto &oType = GetDataType();
4997 7 : size_t nMemArraySize = oType.GetSize();
4998 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4999 7 : GPtrDiff_t nStride = 1;
5000 18 : for (size_t i = nDims; i > 0;)
5001 : {
5002 11 : --i;
5003 11 : if (arrayStep[i] > 0)
5004 9 : anTmpStartIdx[i] = arrayStartIdx[i];
5005 : else
5006 2 : anTmpStartIdx[i] =
5007 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
5008 : const uint64_t nCount =
5009 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
5010 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
5011 : {
5012 0 : CPLError(CE_Failure, CPLE_AppDefined,
5013 : "Read() failed due to too large memory requirement");
5014 0 : return false;
5015 : }
5016 11 : anTmpCount[i] = static_cast<size_t>(nCount);
5017 11 : nMemArraySize *= anTmpCount[i];
5018 11 : anTmpStride[i] = nStride;
5019 11 : nStride *= anTmpCount[i];
5020 : }
5021 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
5022 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
5023 7 : if (!pTmpBuffer)
5024 0 : return false;
5025 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
5026 14 : std::vector<GInt64>(nDims, 1).data(), // steps
5027 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
5028 : {
5029 0 : return false;
5030 : }
5031 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5032 18 : for (size_t i = 0; i < nDims; ++i)
5033 : {
5034 11 : if (arrayStep[i] > 0)
5035 9 : anTmpStartIdx[i] = 0;
5036 : else
5037 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5038 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5039 22 : std::string(), std::string(), std::string(), std::string(),
5040 22 : anTmpCount[i]);
5041 : }
5042 : auto poMEMArray =
5043 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5044 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5045 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5046 7 : bufferStride, bufferDataType, pDstBuffer);
5047 : }
5048 :
5049 : //! @endcond
5050 :
5051 : /************************************************************************/
5052 : /* GDALSlicedMDArray */
5053 : /************************************************************************/
5054 :
5055 : class GDALSlicedMDArray final : public GDALPamMDArray
5056 : {
5057 : private:
5058 : std::shared_ptr<GDALMDArray> m_poParent{};
5059 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5060 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5061 : std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5062 : std::vector<Range>
5063 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5064 :
5065 : mutable std::vector<GUInt64> m_parentStart;
5066 : mutable std::vector<size_t> m_parentCount;
5067 : mutable std::vector<GInt64> m_parentStep;
5068 : mutable std::vector<GPtrDiff_t> m_parentStride;
5069 :
5070 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5071 : const GInt64 *arrayStep,
5072 : const GPtrDiff_t *bufferStride) const;
5073 :
5074 : protected:
5075 862 : explicit GDALSlicedMDArray(
5076 : const std::shared_ptr<GDALMDArray> &poParent,
5077 : const std::string &viewExpr,
5078 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5079 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5080 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5081 : std::vector<Range> &&parentRanges)
5082 2586 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5083 2586 : poParent->GetFullName() +
5084 1724 : " (" + viewExpr + ")"),
5085 1724 : GDALPamMDArray(std::string(),
5086 1724 : "Sliced view of " + poParent->GetFullName() + " (" +
5087 1724 : viewExpr + ")",
5088 1724 : GDALPamMultiDim::GetPAM(poParent),
5089 : poParent->GetContext()),
5090 1724 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5091 862 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5092 862 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5093 862 : m_parentRanges(std::move(parentRanges)),
5094 862 : m_parentStart(m_poParent->GetDimensionCount()),
5095 862 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5096 862 : m_parentStep(m_poParent->GetDimensionCount()),
5097 6896 : m_parentStride(m_poParent->GetDimensionCount())
5098 : {
5099 862 : }
5100 :
5101 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5102 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5103 : const GDALExtendedDataType &bufferDataType,
5104 : void *pDstBuffer) const override;
5105 :
5106 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5107 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5108 : const GDALExtendedDataType &bufferDataType,
5109 : const void *pSrcBuffer) override;
5110 :
5111 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5112 : CSLConstList papszOptions) const override;
5113 :
5114 : public:
5115 : static std::shared_ptr<GDALSlicedMDArray>
5116 862 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5117 : const std::string &viewExpr,
5118 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5119 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5120 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5121 : std::vector<Range> &&parentRanges)
5122 : {
5123 862 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5124 862 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5125 :
5126 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5127 862 : poParent, viewExpr, std::move(dims),
5128 862 : std::move(mapDimIdxToParentDimIdx),
5129 862 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5130 862 : newAr->SetSelf(newAr);
5131 862 : return newAr;
5132 : }
5133 :
5134 228 : bool IsWritable() const override
5135 : {
5136 228 : return m_poParent->IsWritable();
5137 : }
5138 :
5139 1834 : const std::string &GetFilename() const override
5140 : {
5141 1834 : return m_poParent->GetFilename();
5142 : }
5143 :
5144 : const std::vector<std::shared_ptr<GDALDimension>> &
5145 5991 : GetDimensions() const override
5146 : {
5147 5991 : return m_dims;
5148 : }
5149 :
5150 2445 : const GDALExtendedDataType &GetDataType() const override
5151 : {
5152 2445 : return m_poParent->GetDataType();
5153 : }
5154 :
5155 4 : const std::string &GetUnit() const override
5156 : {
5157 4 : return m_poParent->GetUnit();
5158 : }
5159 :
5160 : // bool SetUnit(const std::string& osUnit) override { return
5161 : // m_poParent->SetUnit(osUnit); }
5162 :
5163 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5164 : {
5165 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5166 2 : if (!poSrcSRS)
5167 1 : return nullptr;
5168 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5169 2 : std::vector<int> dstMapping;
5170 3 : for (int srcAxis : srcMapping)
5171 : {
5172 2 : bool bFound = false;
5173 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5174 : {
5175 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5176 3 : srcAxis - 1)
5177 : {
5178 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5179 2 : bFound = true;
5180 2 : break;
5181 : }
5182 : }
5183 2 : if (!bFound)
5184 : {
5185 0 : dstMapping.push_back(0);
5186 : }
5187 : }
5188 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5189 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5190 1 : return poClone;
5191 : }
5192 :
5193 104 : const void *GetRawNoDataValue() const override
5194 : {
5195 104 : return m_poParent->GetRawNoDataValue();
5196 : }
5197 :
5198 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5199 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5200 :
5201 4 : double GetOffset(bool *pbHasOffset,
5202 : GDALDataType *peStorageType) const override
5203 : {
5204 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5205 : }
5206 :
5207 4 : double GetScale(bool *pbHasScale,
5208 : GDALDataType *peStorageType) const override
5209 : {
5210 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
5211 : }
5212 :
5213 : // bool SetOffset(double dfOffset) override { return
5214 : // m_poParent->SetOffset(dfOffset); }
5215 :
5216 : // bool SetScale(double dfScale) override { return
5217 : // m_poParent->SetScale(dfScale); }
5218 :
5219 580 : std::vector<GUInt64> GetBlockSize() const override
5220 : {
5221 580 : std::vector<GUInt64> ret(GetDimensionCount());
5222 1160 : const auto parentBlockSize(m_poParent->GetBlockSize());
5223 1564 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5224 : {
5225 984 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5226 984 : if (iOldAxis != static_cast<size_t>(-1))
5227 : {
5228 984 : ret[i] = parentBlockSize[iOldAxis];
5229 : }
5230 : }
5231 1160 : return ret;
5232 : }
5233 :
5234 : std::shared_ptr<GDALAttribute>
5235 14 : GetAttribute(const std::string &osName) const override
5236 : {
5237 14 : return m_poParent->GetAttribute(osName);
5238 : }
5239 :
5240 : std::vector<std::shared_ptr<GDALAttribute>>
5241 37 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5242 : {
5243 37 : return m_poParent->GetAttributes(papszOptions);
5244 : }
5245 : };
5246 :
5247 : /************************************************************************/
5248 : /* PrepareParentArrays() */
5249 : /************************************************************************/
5250 :
5251 763 : void GDALSlicedMDArray::PrepareParentArrays(
5252 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5253 : const GPtrDiff_t *bufferStride) const
5254 : {
5255 763 : const size_t nParentDimCount = m_parentRanges.size();
5256 2148 : for (size_t i = 0; i < nParentDimCount; i++)
5257 : {
5258 : // For dimensions in parent that have no existence in sliced array
5259 1385 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5260 : }
5261 :
5262 1919 : for (size_t i = 0; i < m_dims.size(); i++)
5263 : {
5264 1156 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5265 1156 : if (iParent != static_cast<size_t>(-1))
5266 : {
5267 1154 : m_parentStart[iParent] =
5268 1154 : m_parentRanges[iParent].m_nIncr >= 0
5269 1154 : ? m_parentRanges[iParent].m_nStartIdx +
5270 852 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5271 302 : : m_parentRanges[iParent].m_nStartIdx -
5272 604 : arrayStartIdx[i] *
5273 302 : static_cast<GUInt64>(
5274 302 : -m_parentRanges[iParent].m_nIncr);
5275 1154 : m_parentCount[iParent] = count[i];
5276 1154 : if (arrayStep)
5277 : {
5278 1151 : m_parentStep[iParent] =
5279 1151 : count[i] == 1 ? 1 :
5280 : // other checks should have ensured this does
5281 : // not overflow
5282 947 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5283 : }
5284 1154 : if (bufferStride)
5285 : {
5286 1151 : m_parentStride[iParent] = bufferStride[i];
5287 : }
5288 : }
5289 : }
5290 763 : }
5291 :
5292 : /************************************************************************/
5293 : /* IRead() */
5294 : /************************************************************************/
5295 :
5296 732 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5297 : const GInt64 *arrayStep,
5298 : const GPtrDiff_t *bufferStride,
5299 : const GDALExtendedDataType &bufferDataType,
5300 : void *pDstBuffer) const
5301 : {
5302 732 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5303 1464 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5304 732 : m_parentStep.data(), m_parentStride.data(),
5305 732 : bufferDataType, pDstBuffer);
5306 : }
5307 :
5308 : /************************************************************************/
5309 : /* IWrite() */
5310 : /************************************************************************/
5311 :
5312 29 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5313 : const size_t *count, const GInt64 *arrayStep,
5314 : const GPtrDiff_t *bufferStride,
5315 : const GDALExtendedDataType &bufferDataType,
5316 : const void *pSrcBuffer)
5317 : {
5318 29 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5319 58 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5320 29 : m_parentStep.data(), m_parentStride.data(),
5321 29 : bufferDataType, pSrcBuffer);
5322 : }
5323 :
5324 : /************************************************************************/
5325 : /* IAdviseRead() */
5326 : /************************************************************************/
5327 :
5328 2 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5329 : const size_t *count,
5330 : CSLConstList papszOptions) const
5331 : {
5332 2 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5333 2 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5334 2 : papszOptions);
5335 : }
5336 :
5337 : /************************************************************************/
5338 : /* CreateSlicedArray() */
5339 : /************************************************************************/
5340 :
5341 : static std::shared_ptr<GDALMDArray>
5342 724 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5343 : const std::string &viewExpr, const std::string &activeSlice,
5344 : bool bRenameDimensions,
5345 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5346 : {
5347 724 : const auto &srcDims(self->GetDimensions());
5348 724 : if (srcDims.empty())
5349 : {
5350 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5351 2 : return nullptr;
5352 : }
5353 :
5354 1444 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5355 722 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5356 :
5357 1444 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5358 1444 : std::vector<size_t> mapDimIdxToParentDimIdx;
5359 1444 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5360 722 : newDims.reserve(nTokens);
5361 722 : mapDimIdxToParentDimIdx.reserve(nTokens);
5362 722 : parentRanges.reserve(nTokens);
5363 :
5364 722 : bool bGotEllipsis = false;
5365 722 : size_t nCurSrcDim = 0;
5366 1444 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5367 2151 : for (size_t i = 0; i < nTokens; i++)
5368 : {
5369 1446 : const char *pszIdxSpec = aosTokens[i];
5370 1446 : if (EQUAL(pszIdxSpec, "..."))
5371 : {
5372 129 : if (bGotEllipsis)
5373 : {
5374 2 : CPLError(CE_Failure, CPLE_AppDefined,
5375 : "Only one single ellipsis is supported");
5376 2 : return nullptr;
5377 : }
5378 127 : bGotEllipsis = true;
5379 127 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5380 263 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5381 : {
5382 136 : parentRanges.emplace_back(0, 1);
5383 136 : newDims.push_back(srcDims[nCurSrcDim]);
5384 136 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5385 : }
5386 127 : continue;
5387 : }
5388 1317 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5389 1314 : EQUAL(pszIdxSpec, "np.newaxis"))
5390 : {
5391 3 : newDims.push_back(std::make_shared<GDALDimension>(
5392 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5393 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5394 3 : continue;
5395 : }
5396 1314 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5397 : {
5398 342 : if (nCurSrcDim >= srcDims.size())
5399 : {
5400 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5401 : activeSlice.c_str());
5402 7 : return nullptr;
5403 : }
5404 :
5405 340 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5406 340 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5407 340 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5408 336 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5409 : {
5410 5 : CPLError(CE_Failure, CPLE_AppDefined,
5411 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5412 5 : return nullptr;
5413 : }
5414 335 : if (nVal < 0)
5415 0 : nVal += nDimSize;
5416 335 : parentRanges.emplace_back(nVal, 0);
5417 : }
5418 : else
5419 : {
5420 972 : if (nCurSrcDim >= srcDims.size())
5421 : {
5422 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5423 : activeSlice.c_str());
5424 8 : return nullptr;
5425 : }
5426 :
5427 : CPLStringList aosRangeTokens(
5428 971 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5429 971 : int nRangeTokens = aosRangeTokens.size();
5430 971 : if (nRangeTokens > 3)
5431 : {
5432 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5433 : pszIdxSpec);
5434 1 : return nullptr;
5435 : }
5436 970 : if (nRangeTokens <= 1)
5437 : {
5438 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5439 : pszIdxSpec);
5440 1 : return nullptr;
5441 : }
5442 969 : const char *pszStart = aosRangeTokens[0];
5443 969 : const char *pszEnd = aosRangeTokens[1];
5444 969 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5445 969 : GDALSlicedMDArray::Range range;
5446 969 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5447 969 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5448 1937 : if (range.m_nIncr == 0 ||
5449 968 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5450 : {
5451 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5452 1 : return nullptr;
5453 : }
5454 968 : auto startIdx(CPLAtoGIntBig(pszStart));
5455 968 : if (startIdx < 0)
5456 : {
5457 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5458 0 : startIdx = 0;
5459 : else
5460 0 : startIdx = nDimSize + startIdx;
5461 : }
5462 968 : const bool bPosIncr = range.m_nIncr > 0;
5463 968 : range.m_nStartIdx = startIdx;
5464 1936 : range.m_nStartIdx = EQUAL(pszStart, "")
5465 968 : ? (bPosIncr ? 0 : nDimSize - 1)
5466 : : range.m_nStartIdx;
5467 968 : if (range.m_nStartIdx >= nDimSize - 1)
5468 286 : range.m_nStartIdx = nDimSize - 1;
5469 968 : auto endIdx(CPLAtoGIntBig(pszEnd));
5470 968 : if (endIdx < 0)
5471 : {
5472 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5473 1 : if (nDimSize < positiveEndIdx)
5474 0 : endIdx = 0;
5475 : else
5476 1 : endIdx = nDimSize - positiveEndIdx;
5477 : }
5478 968 : GUInt64 nEndIdx = endIdx;
5479 968 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5480 968 : if (pszStart[0] || pszEnd[0])
5481 : {
5482 636 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5483 633 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5484 : {
5485 4 : CPLError(CE_Failure, CPLE_AppDefined,
5486 : "Output dimension of size 0 is not allowed");
5487 4 : return nullptr;
5488 : }
5489 : }
5490 964 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5491 964 : const auto nAbsIncr = std::abs(range.m_nIncr);
5492 964 : const GUInt64 newSize =
5493 332 : (pszStart[0] == 0 && pszEnd[0] == 0 &&
5494 332 : range.m_nStartIdx == nEndIdx)
5495 1928 : ? 1
5496 : : bPosIncr
5497 1091 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5498 128 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5499 964 : const auto &poSrcDim = srcDims[nCurSrcDim];
5500 1544 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5501 580 : newSize == poSrcDim->GetSize())
5502 : {
5503 211 : newDims.push_back(poSrcDim);
5504 : }
5505 : else
5506 : {
5507 1506 : std::string osNewDimName(poSrcDim->GetName());
5508 753 : if (bRenameDimensions)
5509 : {
5510 : osNewDimName =
5511 1410 : "subset_" + poSrcDim->GetName() +
5512 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5513 : "_" CPL_FRMT_GUIB,
5514 705 : static_cast<GUIntBig>(range.m_nStartIdx),
5515 705 : static_cast<GIntBig>(range.m_nIncr),
5516 705 : static_cast<GUIntBig>(newSize));
5517 : }
5518 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5519 1506 : std::string(), osNewDimName, poSrcDim->GetType(),
5520 753 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5521 : : std::string(),
5522 1506 : newSize);
5523 753 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5524 910 : if (poSrcIndexingVar &&
5525 910 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5526 157 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5527 : {
5528 : std::vector<std::shared_ptr<GDALDimension>>
5529 628 : indexingVarNewDims{poNewDim};
5530 314 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5531 : std::vector<std::shared_ptr<GDALMDArray>>
5532 314 : indexingVarNewIndexingVar;
5533 : std::vector<GDALSlicedMDArray::Range>
5534 314 : indexingVarParentRanges{range};
5535 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5536 : poSrcIndexingVar, pszIdxSpec,
5537 157 : std::move(indexingVarNewDims),
5538 157 : std::move(indexingVarMapDimIdxToParentDimIdx),
5539 157 : std::move(indexingVarNewIndexingVar),
5540 471 : std::move(indexingVarParentRanges));
5541 157 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5542 157 : apoNewIndexingVariables.push_back(
5543 157 : std::move(poNewIndexingVar));
5544 : }
5545 753 : newDims.push_back(std::move(poNewDim));
5546 : }
5547 964 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5548 964 : parentRanges.emplace_back(range);
5549 : }
5550 :
5551 1299 : nCurSrcDim++;
5552 : }
5553 778 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5554 : {
5555 73 : parentRanges.emplace_back(0, 1);
5556 73 : newDims.push_back(srcDims[nCurSrcDim]);
5557 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5558 : }
5559 :
5560 705 : GDALMDArray::ViewSpec viewSpec;
5561 705 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5562 705 : viewSpec.m_parentRanges = parentRanges;
5563 705 : viewSpecs.emplace_back(std::move(viewSpec));
5564 :
5565 1410 : return GDALSlicedMDArray::Create(
5566 705 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5567 1410 : std::move(apoNewIndexingVariables), std::move(parentRanges));
5568 : }
5569 :
5570 : /************************************************************************/
5571 : /* GDALExtractFieldMDArray */
5572 : /************************************************************************/
5573 :
5574 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5575 : {
5576 : private:
5577 : std::shared_ptr<GDALMDArray> m_poParent{};
5578 : GDALExtendedDataType m_dt;
5579 : std::string m_srcCompName;
5580 : mutable std::vector<GByte> m_pabyNoData{};
5581 :
5582 : protected:
5583 465 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5584 : const std::string &fieldName,
5585 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5586 1860 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5587 930 : " of " +
5588 465 : poParent->GetFullName()),
5589 : GDALPamMDArray(
5590 930 : std::string(),
5591 930 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5592 930 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5593 : m_poParent(poParent), m_dt(srcComp->GetType()),
5594 2325 : m_srcCompName(srcComp->GetName())
5595 : {
5596 465 : m_pabyNoData.resize(m_dt.GetSize());
5597 465 : }
5598 :
5599 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5600 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5601 : const GDALExtendedDataType &bufferDataType,
5602 : void *pDstBuffer) const override;
5603 :
5604 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5605 : CSLConstList papszOptions) const override
5606 : {
5607 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5608 : }
5609 :
5610 : public:
5611 : static std::shared_ptr<GDALExtractFieldMDArray>
5612 465 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5613 : const std::string &fieldName,
5614 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5615 : {
5616 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5617 465 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5618 465 : newAr->SetSelf(newAr);
5619 465 : return newAr;
5620 : }
5621 :
5622 930 : ~GDALExtractFieldMDArray() override
5623 465 : {
5624 465 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5625 930 : }
5626 :
5627 200 : bool IsWritable() const override
5628 : {
5629 200 : return m_poParent->IsWritable();
5630 : }
5631 :
5632 1314 : const std::string &GetFilename() const override
5633 : {
5634 1314 : return m_poParent->GetFilename();
5635 : }
5636 :
5637 : const std::vector<std::shared_ptr<GDALDimension>> &
5638 1384 : GetDimensions() const override
5639 : {
5640 1384 : return m_poParent->GetDimensions();
5641 : }
5642 :
5643 1137 : const GDALExtendedDataType &GetDataType() const override
5644 : {
5645 1137 : return m_dt;
5646 : }
5647 :
5648 2 : const std::string &GetUnit() const override
5649 : {
5650 2 : return m_poParent->GetUnit();
5651 : }
5652 :
5653 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5654 : {
5655 2 : return m_poParent->GetSpatialRef();
5656 : }
5657 :
5658 99 : const void *GetRawNoDataValue() const override
5659 : {
5660 99 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5661 99 : if (parentNoData == nullptr)
5662 6 : return nullptr;
5663 :
5664 93 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5665 93 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5666 :
5667 186 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5668 186 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5669 186 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5670 93 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5671 279 : std::move(comps)));
5672 :
5673 93 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5674 93 : &m_pabyNoData[0], tmpDT);
5675 :
5676 93 : return &m_pabyNoData[0];
5677 : }
5678 :
5679 2 : double GetOffset(bool *pbHasOffset,
5680 : GDALDataType *peStorageType) const override
5681 : {
5682 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5683 : }
5684 :
5685 2 : double GetScale(bool *pbHasScale,
5686 : GDALDataType *peStorageType) const override
5687 : {
5688 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5689 : }
5690 :
5691 201 : std::vector<GUInt64> GetBlockSize() const override
5692 : {
5693 201 : return m_poParent->GetBlockSize();
5694 : }
5695 : };
5696 :
5697 : /************************************************************************/
5698 : /* IRead() */
5699 : /************************************************************************/
5700 :
5701 414 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5702 : const size_t *count,
5703 : const GInt64 *arrayStep,
5704 : const GPtrDiff_t *bufferStride,
5705 : const GDALExtendedDataType &bufferDataType,
5706 : void *pDstBuffer) const
5707 : {
5708 828 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5709 828 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5710 828 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5711 : auto tmpDT(GDALExtendedDataType::Create(
5712 828 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5713 :
5714 414 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5715 828 : tmpDT, pDstBuffer);
5716 : }
5717 :
5718 : /************************************************************************/
5719 : /* CreateFieldNameExtractArray() */
5720 : /************************************************************************/
5721 :
5722 : static std::shared_ptr<GDALMDArray>
5723 466 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5724 : const std::string &fieldName)
5725 : {
5726 466 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5727 466 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5728 983 : for (const auto &comp : self->GetDataType().GetComponents())
5729 : {
5730 982 : if (comp->GetName() == fieldName)
5731 : {
5732 465 : srcComp = ∁
5733 465 : break;
5734 : }
5735 : }
5736 466 : if (srcComp == nullptr)
5737 : {
5738 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5739 : fieldName.c_str());
5740 1 : return nullptr;
5741 : }
5742 465 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5743 : }
5744 :
5745 : /************************************************************************/
5746 : /* GetView() */
5747 : /************************************************************************/
5748 :
5749 : // clang-format off
5750 : /** Return a view of the array using slicing or field access.
5751 : *
5752 : * The slice expression uses the same syntax as NumPy basic slicing and
5753 : * indexing. See
5754 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5755 : * Or it can use field access by name. See
5756 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5757 : *
5758 : * Multiple [] bracket elements can be concatenated, with a slice expression
5759 : * or field name inside each.
5760 : *
5761 : * For basic slicing and indexing, inside each [] bracket element, a list of
5762 : * indexes that apply to successive source dimensions, can be specified, using
5763 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5764 : * or newaxis, using a comma separator.
5765 : *
5766 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5767 : * <ul>
5768 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5769 : * at index 1 in the first dimension, and index 2 in the second dimension
5770 : * from the source array. That is 5</li>
5771 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5772 : * implemented internally doing this intermediate slicing approach.</li>
5773 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5774 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5775 : * first dimension. That is [4,5,6,7].</li>
5776 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5777 : * second dimension. That is [2,6].</li>
5778 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5779 : * the second dimension. That is [[2],[6]].</li>
5780 : * <li>GetView("[::,2]"): Same as
5781 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5782 : * ellipsis only expands to one dimension here.</li>
5783 : * <li>GetView("[:,::2]"):
5784 : * returns a 2-dimensional array, with even-indexed elements of the second
5785 : * dimension. That is [[0,2],[4,6]].</li>
5786 : * <li>GetView("[:,1::2]"): returns a
5787 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5788 : * is [[1,3],[5,7]].</li>
5789 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5790 : * array, with elements of the second dimension with index in the range [1,3[.
5791 : * That is [[1,2],[5,6]].</li>
5792 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5793 : * array, with the values in first dimension reversed. That is
5794 : * [[4,5,6,7],[0,1,2,3]].</li>
5795 : * <li>GetView("[newaxis,...]"): returns a
5796 : * 3-dimensional array, with an additional dimension of size 1 put at the
5797 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5798 : * </ul>
5799 : *
5800 : * One difference with NumPy behavior is that ranges that would result in
5801 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5802 : * GDAL multidimensional model).
5803 : *
5804 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5805 : * Multiple field specification is not supported currently.
5806 : *
5807 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5808 : *
5809 : * \note When using the GDAL Python bindings, natural Python syntax can be
5810 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5811 : * ar.GetView("[0,::,1]['foo']")
5812 : * \note When using the C++ API and integer indexing only, you may use the
5813 : * at(idx0, idx1, ...) method.
5814 : *
5815 : * The returned array holds a reference to the original one, and thus is
5816 : * a view of it (not a copy). If the content of the original array changes,
5817 : * the content of the view array too. When using basic slicing and indexing,
5818 : * the view can be written if the underlying array is writable.
5819 : *
5820 : * This is the same as the C function GDALMDArrayGetView()
5821 : *
5822 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5823 : * access.
5824 : * @return a new array, that holds a reference to the original one, and thus is
5825 : * a view of it (not a copy), or nullptr in case of error.
5826 : */
5827 : // clang-format on
5828 :
5829 : std::shared_ptr<GDALMDArray>
5830 1124 : GDALMDArray::GetView(const std::string &viewExpr) const
5831 : {
5832 2248 : std::vector<ViewSpec> viewSpecs;
5833 2248 : return GetView(viewExpr, true, viewSpecs);
5834 : }
5835 :
5836 : //! @cond Doxygen_Suppress
5837 : std::shared_ptr<GDALMDArray>
5838 1196 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5839 : std::vector<ViewSpec> &viewSpecs) const
5840 : {
5841 2392 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5842 1196 : if (!self)
5843 : {
5844 1 : CPLError(CE_Failure, CPLE_AppDefined,
5845 : "Driver implementation issue: m_pSelf not set !");
5846 1 : return nullptr;
5847 : }
5848 1195 : std::string curExpr(viewExpr);
5849 : while (true)
5850 : {
5851 1198 : if (curExpr.empty() || curExpr[0] != '[')
5852 : {
5853 2 : CPLError(CE_Failure, CPLE_AppDefined,
5854 : "Slice string should start with ['");
5855 1195 : return nullptr;
5856 : }
5857 :
5858 1196 : std::string fieldName;
5859 : size_t endExpr;
5860 1196 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5861 : {
5862 470 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5863 : {
5864 2 : CPLError(CE_Failure, CPLE_AppDefined,
5865 : "Field access not allowed on non-compound data type");
5866 2 : return nullptr;
5867 : }
5868 468 : size_t idx = 2;
5869 5119 : for (; idx < curExpr.size(); idx++)
5870 : {
5871 5118 : const char ch = curExpr[idx];
5872 5118 : if (ch == curExpr[1])
5873 467 : break;
5874 4651 : if (ch == '\\' && idx + 1 < curExpr.size())
5875 : {
5876 1 : fieldName += curExpr[idx + 1];
5877 1 : idx++;
5878 : }
5879 : else
5880 : {
5881 4650 : fieldName += ch;
5882 : }
5883 : }
5884 468 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5885 : {
5886 2 : CPLError(CE_Failure, CPLE_AppDefined,
5887 : "Invalid field access specification");
5888 2 : return nullptr;
5889 : }
5890 466 : endExpr = idx + 1;
5891 : }
5892 : else
5893 : {
5894 726 : endExpr = curExpr.find(']');
5895 : }
5896 1192 : if (endExpr == std::string::npos)
5897 : {
5898 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5899 1 : return nullptr;
5900 : }
5901 1191 : if (endExpr == 1)
5902 : {
5903 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5904 1 : return nullptr;
5905 : }
5906 1190 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5907 :
5908 1190 : if (!fieldName.empty())
5909 : {
5910 932 : ViewSpec viewSpec;
5911 466 : viewSpec.m_osFieldName = fieldName;
5912 466 : viewSpecs.emplace_back(std::move(viewSpec));
5913 : }
5914 :
5915 1190 : auto newArray = !fieldName.empty()
5916 : ? CreateFieldNameExtractArray(self, fieldName)
5917 : : CreateSlicedArray(self, viewExpr, activeSlice,
5918 1190 : bRenameDimensions, viewSpecs);
5919 :
5920 1190 : if (endExpr == curExpr.size() - 1)
5921 : {
5922 1187 : return newArray;
5923 : }
5924 3 : self = std::move(newArray);
5925 3 : curExpr = curExpr.substr(endExpr + 1);
5926 3 : }
5927 : }
5928 :
5929 : //! @endcond
5930 :
5931 : std::shared_ptr<GDALMDArray>
5932 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5933 : {
5934 19 : std::string osExpr("[");
5935 19 : bool bFirst = true;
5936 45 : for (const auto &idx : indices)
5937 : {
5938 26 : if (!bFirst)
5939 7 : osExpr += ',';
5940 26 : bFirst = false;
5941 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5942 : }
5943 57 : return GetView(osExpr + ']');
5944 : }
5945 :
5946 : /************************************************************************/
5947 : /* operator[] */
5948 : /************************************************************************/
5949 :
5950 : /** Return a view of the array using field access
5951 : *
5952 : * Equivalent of GetView("['fieldName']")
5953 : *
5954 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5955 : */
5956 : std::shared_ptr<GDALMDArray>
5957 2 : GDALMDArray::operator[](const std::string &fieldName) const
5958 : {
5959 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5960 4 : .replaceAll('\\', "\\\\")
5961 4 : .replaceAll('\'', "\\\'")
5962 6 : .c_str()));
5963 : }
5964 :
5965 : /************************************************************************/
5966 : /* GDALMDArrayTransposed */
5967 : /************************************************************************/
5968 :
5969 : class GDALMDArrayTransposed final : public GDALPamMDArray
5970 : {
5971 : private:
5972 : std::shared_ptr<GDALMDArray> m_poParent{};
5973 : std::vector<int> m_anMapNewAxisToOldAxis{};
5974 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5975 :
5976 : mutable std::vector<GUInt64> m_parentStart;
5977 : mutable std::vector<size_t> m_parentCount;
5978 : mutable std::vector<GInt64> m_parentStep;
5979 : mutable std::vector<GPtrDiff_t> m_parentStride;
5980 :
5981 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5982 : const GInt64 *arrayStep,
5983 : const GPtrDiff_t *bufferStride) const;
5984 :
5985 : static std::string
5986 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5987 : {
5988 84 : std::string ret;
5989 84 : ret += '[';
5990 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5991 : {
5992 228 : if (i > 0)
5993 144 : ret += ',';
5994 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5995 : }
5996 84 : ret += ']';
5997 84 : return ret;
5998 : }
5999 :
6000 : protected:
6001 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
6002 : const std::vector<int> &anMapNewAxisToOldAxis,
6003 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
6004 84 : : GDALAbstractMDArray(std::string(),
6005 84 : "Transposed view of " + poParent->GetFullName() +
6006 84 : " along " +
6007 42 : MappingToStr(anMapNewAxisToOldAxis)),
6008 84 : GDALPamMDArray(std::string(),
6009 84 : "Transposed view of " + poParent->GetFullName() +
6010 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
6011 84 : GDALPamMultiDim::GetPAM(poParent),
6012 : poParent->GetContext()),
6013 42 : m_poParent(std::move(poParent)),
6014 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
6015 42 : m_dims(std::move(dims)),
6016 42 : m_parentStart(m_poParent->GetDimensionCount()),
6017 42 : m_parentCount(m_poParent->GetDimensionCount()),
6018 42 : m_parentStep(m_poParent->GetDimensionCount()),
6019 336 : m_parentStride(m_poParent->GetDimensionCount())
6020 : {
6021 42 : }
6022 :
6023 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6024 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6025 : const GDALExtendedDataType &bufferDataType,
6026 : void *pDstBuffer) const override;
6027 :
6028 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
6029 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6030 : const GDALExtendedDataType &bufferDataType,
6031 : const void *pSrcBuffer) override;
6032 :
6033 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6034 : CSLConstList papszOptions) const override;
6035 :
6036 : public:
6037 : static std::shared_ptr<GDALMDArrayTransposed>
6038 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6039 : const std::vector<int> &anMapNewAxisToOldAxis)
6040 : {
6041 42 : const auto &parentDims(poParent->GetDimensions());
6042 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
6043 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6044 : {
6045 114 : if (iOldAxis < 0)
6046 : {
6047 1 : dims.push_back(std::make_shared<GDALDimension>(
6048 2 : std::string(), "newaxis", std::string(), std::string(), 1));
6049 : }
6050 : else
6051 : {
6052 113 : dims.emplace_back(parentDims[iOldAxis]);
6053 : }
6054 : }
6055 :
6056 : auto newAr(
6057 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6058 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
6059 42 : newAr->SetSelf(newAr);
6060 84 : return newAr;
6061 : }
6062 :
6063 1 : bool IsWritable() const override
6064 : {
6065 1 : return m_poParent->IsWritable();
6066 : }
6067 :
6068 84 : const std::string &GetFilename() const override
6069 : {
6070 84 : return m_poParent->GetFilename();
6071 : }
6072 :
6073 : const std::vector<std::shared_ptr<GDALDimension>> &
6074 358 : GetDimensions() const override
6075 : {
6076 358 : return m_dims;
6077 : }
6078 :
6079 141 : const GDALExtendedDataType &GetDataType() const override
6080 : {
6081 141 : return m_poParent->GetDataType();
6082 : }
6083 :
6084 4 : const std::string &GetUnit() const override
6085 : {
6086 4 : return m_poParent->GetUnit();
6087 : }
6088 :
6089 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6090 : {
6091 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6092 5 : if (!poSrcSRS)
6093 2 : return nullptr;
6094 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6095 6 : std::vector<int> dstMapping;
6096 9 : for (int srcAxis : srcMapping)
6097 : {
6098 6 : bool bFound = false;
6099 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6100 : {
6101 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6102 : {
6103 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6104 6 : bFound = true;
6105 6 : break;
6106 : }
6107 : }
6108 6 : if (!bFound)
6109 : {
6110 0 : dstMapping.push_back(0);
6111 : }
6112 : }
6113 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6114 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6115 3 : return poClone;
6116 : }
6117 :
6118 4 : const void *GetRawNoDataValue() const override
6119 : {
6120 4 : return m_poParent->GetRawNoDataValue();
6121 : }
6122 :
6123 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6124 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6125 :
6126 4 : double GetOffset(bool *pbHasOffset,
6127 : GDALDataType *peStorageType) const override
6128 : {
6129 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6130 : }
6131 :
6132 4 : double GetScale(bool *pbHasScale,
6133 : GDALDataType *peStorageType) const override
6134 : {
6135 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6136 : }
6137 :
6138 : // bool SetOffset(double dfOffset) override { return
6139 : // m_poParent->SetOffset(dfOffset); }
6140 :
6141 : // bool SetScale(double dfScale) override { return
6142 : // m_poParent->SetScale(dfScale); }
6143 :
6144 3 : std::vector<GUInt64> GetBlockSize() const override
6145 : {
6146 3 : std::vector<GUInt64> ret(GetDimensionCount());
6147 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6148 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6149 : {
6150 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6151 8 : if (iOldAxis >= 0)
6152 : {
6153 7 : ret[i] = parentBlockSize[iOldAxis];
6154 : }
6155 : }
6156 6 : return ret;
6157 : }
6158 :
6159 : std::shared_ptr<GDALAttribute>
6160 1 : GetAttribute(const std::string &osName) const override
6161 : {
6162 1 : return m_poParent->GetAttribute(osName);
6163 : }
6164 :
6165 : std::vector<std::shared_ptr<GDALAttribute>>
6166 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6167 : {
6168 6 : return m_poParent->GetAttributes(papszOptions);
6169 : }
6170 : };
6171 :
6172 : /************************************************************************/
6173 : /* PrepareParentArrays() */
6174 : /************************************************************************/
6175 :
6176 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6177 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6178 : const GPtrDiff_t *bufferStride) const
6179 : {
6180 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6181 : {
6182 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6183 129 : if (iOldAxis >= 0)
6184 : {
6185 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6186 128 : m_parentCount[iOldAxis] = count[i];
6187 128 : if (arrayStep) // only null when called from IAdviseRead()
6188 : {
6189 126 : m_parentStep[iOldAxis] = arrayStep[i];
6190 : }
6191 128 : if (bufferStride) // only null when called from IAdviseRead()
6192 : {
6193 126 : m_parentStride[iOldAxis] = bufferStride[i];
6194 : }
6195 : }
6196 : }
6197 47 : }
6198 :
6199 : /************************************************************************/
6200 : /* IRead() */
6201 : /************************************************************************/
6202 :
6203 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6204 : const size_t *count, const GInt64 *arrayStep,
6205 : const GPtrDiff_t *bufferStride,
6206 : const GDALExtendedDataType &bufferDataType,
6207 : void *pDstBuffer) const
6208 : {
6209 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6210 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6211 44 : m_parentStep.data(), m_parentStride.data(),
6212 44 : bufferDataType, pDstBuffer);
6213 : }
6214 :
6215 : /************************************************************************/
6216 : /* IWrite() */
6217 : /************************************************************************/
6218 :
6219 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6220 : const size_t *count, const GInt64 *arrayStep,
6221 : const GPtrDiff_t *bufferStride,
6222 : const GDALExtendedDataType &bufferDataType,
6223 : const void *pSrcBuffer)
6224 : {
6225 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6226 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6227 2 : m_parentStep.data(), m_parentStride.data(),
6228 2 : bufferDataType, pSrcBuffer);
6229 : }
6230 :
6231 : /************************************************************************/
6232 : /* IAdviseRead() */
6233 : /************************************************************************/
6234 :
6235 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6236 : const size_t *count,
6237 : CSLConstList papszOptions) const
6238 : {
6239 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6240 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6241 1 : papszOptions);
6242 : }
6243 :
6244 : /************************************************************************/
6245 : /* Transpose() */
6246 : /************************************************************************/
6247 :
6248 : /** Return a view of the array whose axis have been reordered.
6249 : *
6250 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6251 : * and GetDimensionCount() - 1, and each only once.
6252 : * -1 can be used as a special index value to ask for the insertion of a new
6253 : * axis of size 1.
6254 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6255 : * index of one of its dimension, it corresponds to the axis of index
6256 : * anMapNewAxisToOldAxis[i] from the current array.
6257 : *
6258 : * This is similar to the numpy.transpose() method
6259 : *
6260 : * The returned array holds a reference to the original one, and thus is
6261 : * a view of it (not a copy). If the content of the original array changes,
6262 : * the content of the view array too. The view can be written if the underlying
6263 : * array is writable.
6264 : *
6265 : * Note that I/O performance in such a transposed view might be poor.
6266 : *
6267 : * This is the same as the C function GDALMDArrayTranspose().
6268 : *
6269 : * @return a new array, that holds a reference to the original one, and thus is
6270 : * a view of it (not a copy), or nullptr in case of error.
6271 : */
6272 : std::shared_ptr<GDALMDArray>
6273 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6274 : {
6275 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6276 50 : if (!self)
6277 : {
6278 0 : CPLError(CE_Failure, CPLE_AppDefined,
6279 : "Driver implementation issue: m_pSelf not set !");
6280 0 : return nullptr;
6281 : }
6282 50 : const int nDims = static_cast<int>(GetDimensionCount());
6283 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6284 50 : int nCountOldAxis = 0;
6285 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6286 : {
6287 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6288 : {
6289 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6290 4 : return nullptr;
6291 : }
6292 130 : if (iOldAxis >= 0)
6293 : {
6294 128 : if (alreadyUsedOldAxis[iOldAxis])
6295 : {
6296 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6297 : iOldAxis);
6298 1 : return nullptr;
6299 : }
6300 127 : alreadyUsedOldAxis[iOldAxis] = true;
6301 127 : nCountOldAxis++;
6302 : }
6303 : }
6304 46 : if (nCountOldAxis != nDims)
6305 : {
6306 4 : CPLError(CE_Failure, CPLE_AppDefined,
6307 : "One or several original axis missing");
6308 4 : return nullptr;
6309 : }
6310 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6311 : }
6312 :
6313 : /************************************************************************/
6314 : /* IRead() */
6315 : /************************************************************************/
6316 :
6317 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6318 : const size_t *count, const GInt64 *arrayStep,
6319 : const GPtrDiff_t *bufferStride,
6320 : const GDALExtendedDataType &bufferDataType,
6321 : void *pDstBuffer) const
6322 : {
6323 16 : const double dfScale = m_dfScale;
6324 16 : const double dfOffset = m_dfOffset;
6325 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6326 : const auto dtDouble =
6327 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6328 16 : const size_t nDTSize = dtDouble.GetSize();
6329 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6330 :
6331 16 : double adfSrcNoData[2] = {0, 0};
6332 16 : if (m_bHasNoData)
6333 : {
6334 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6335 9 : m_poParent->GetDataType(),
6336 : &adfSrcNoData[0], dtDouble);
6337 : }
6338 :
6339 16 : const auto nDims = GetDimensions().size();
6340 16 : if (nDims == 0)
6341 : {
6342 : double adfVal[2];
6343 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6344 : dtDouble, &adfVal[0]))
6345 : {
6346 0 : return false;
6347 : }
6348 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6349 : {
6350 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6351 6 : if (bDTIsComplex)
6352 : {
6353 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6354 : }
6355 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6356 : bufferDataType);
6357 : }
6358 : else
6359 : {
6360 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6361 : pDstBuffer, bufferDataType);
6362 : }
6363 9 : return true;
6364 : }
6365 :
6366 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6367 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6368 7 : void *pTempBuffer = pDstBuffer;
6369 7 : if (bTempBufferNeeded)
6370 : {
6371 2 : size_t nElts = 1;
6372 2 : actualBufferStrideVector.resize(nDims);
6373 7 : for (size_t i = 0; i < nDims; i++)
6374 5 : nElts *= count[i];
6375 2 : actualBufferStrideVector.back() = 1;
6376 5 : for (size_t i = nDims - 1; i > 0;)
6377 : {
6378 3 : --i;
6379 3 : actualBufferStrideVector[i] =
6380 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6381 : }
6382 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6383 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6384 2 : if (!pTempBuffer)
6385 0 : return false;
6386 : }
6387 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6388 : actualBufferStridePtr, dtDouble, pTempBuffer))
6389 : {
6390 0 : if (bTempBufferNeeded)
6391 0 : VSIFree(pTempBuffer);
6392 0 : return false;
6393 : }
6394 :
6395 : struct Stack
6396 : {
6397 : size_t nIters = 0;
6398 : double *src_ptr = nullptr;
6399 : GByte *dst_ptr = nullptr;
6400 : GPtrDiff_t src_inc_offset = 0;
6401 : GPtrDiff_t dst_inc_offset = 0;
6402 : };
6403 :
6404 7 : std::vector<Stack> stack(nDims);
6405 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6406 23 : for (size_t i = 0; i < nDims; i++)
6407 : {
6408 32 : stack[i].src_inc_offset =
6409 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6410 16 : stack[i].dst_inc_offset =
6411 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6412 : }
6413 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6414 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6415 :
6416 7 : size_t dimIdx = 0;
6417 7 : const size_t nDimsMinus1 = nDims - 1;
6418 : GByte abyDstNoData[16];
6419 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6420 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6421 : bufferDataType);
6422 :
6423 37 : lbl_next_depth:
6424 37 : if (dimIdx == nDimsMinus1)
6425 : {
6426 25 : auto nIters = count[dimIdx];
6427 25 : double *padfVal = stack[dimIdx].src_ptr;
6428 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6429 : while (true)
6430 : {
6431 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6432 : {
6433 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6434 88 : if (bDTIsComplex)
6435 : {
6436 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6437 : }
6438 88 : if (bTempBufferNeeded)
6439 : {
6440 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6441 : dst_ptr, bufferDataType);
6442 : }
6443 : }
6444 : else
6445 : {
6446 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6447 : }
6448 :
6449 92 : if ((--nIters) == 0)
6450 25 : break;
6451 67 : padfVal += stack[dimIdx].src_inc_offset;
6452 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6453 : }
6454 : }
6455 : else
6456 : {
6457 12 : stack[dimIdx].nIters = count[dimIdx];
6458 : while (true)
6459 : {
6460 30 : dimIdx++;
6461 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6462 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6463 30 : goto lbl_next_depth;
6464 30 : lbl_return_to_caller:
6465 30 : dimIdx--;
6466 30 : if ((--stack[dimIdx].nIters) == 0)
6467 12 : break;
6468 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6469 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6470 : }
6471 : }
6472 37 : if (dimIdx > 0)
6473 30 : goto lbl_return_to_caller;
6474 :
6475 7 : if (bTempBufferNeeded)
6476 2 : VSIFree(pTempBuffer);
6477 7 : return true;
6478 : }
6479 :
6480 : /************************************************************************/
6481 : /* IWrite() */
6482 : /************************************************************************/
6483 :
6484 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6485 : const size_t *count, const GInt64 *arrayStep,
6486 : const GPtrDiff_t *bufferStride,
6487 : const GDALExtendedDataType &bufferDataType,
6488 : const void *pSrcBuffer)
6489 : {
6490 16 : const double dfScale = m_dfScale;
6491 16 : const double dfOffset = m_dfOffset;
6492 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6493 : const auto dtDouble =
6494 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6495 16 : const size_t nDTSize = dtDouble.GetSize();
6496 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6497 : const bool bSelfAndParentHaveNoData =
6498 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6499 16 : double dfNoData = 0;
6500 16 : if (m_bHasNoData)
6501 : {
6502 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6503 : &dfNoData, GDT_Float64, 0, 1);
6504 : }
6505 :
6506 16 : double adfSrcNoData[2] = {0, 0};
6507 16 : if (bSelfAndParentHaveNoData)
6508 : {
6509 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6510 7 : m_poParent->GetDataType(),
6511 : &adfSrcNoData[0], dtDouble);
6512 : }
6513 :
6514 16 : const auto nDims = GetDimensions().size();
6515 16 : if (nDims == 0)
6516 : {
6517 : double adfVal[2];
6518 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6519 : dtDouble);
6520 16 : if (bSelfAndParentHaveNoData &&
6521 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6522 : {
6523 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6524 2 : bufferStride, m_poParent->GetDataType(),
6525 4 : m_poParent->GetRawNoDataValue());
6526 : }
6527 : else
6528 : {
6529 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6530 8 : if (bDTIsComplex)
6531 : {
6532 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6533 : }
6534 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6535 8 : bufferStride, dtDouble, &adfVal[0]);
6536 : }
6537 : }
6538 :
6539 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6540 6 : size_t nElts = 1;
6541 6 : tmpBufferStrideVector.resize(nDims);
6542 20 : for (size_t i = 0; i < nDims; i++)
6543 14 : nElts *= count[i];
6544 6 : tmpBufferStrideVector.back() = 1;
6545 14 : for (size_t i = nDims - 1; i > 0;)
6546 : {
6547 8 : --i;
6548 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6549 : }
6550 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6551 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6552 6 : if (!pTempBuffer)
6553 0 : return false;
6554 :
6555 : struct Stack
6556 : {
6557 : size_t nIters = 0;
6558 : double *dst_ptr = nullptr;
6559 : const GByte *src_ptr = nullptr;
6560 : GPtrDiff_t src_inc_offset = 0;
6561 : GPtrDiff_t dst_inc_offset = 0;
6562 : };
6563 :
6564 6 : std::vector<Stack> stack(nDims);
6565 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6566 20 : for (size_t i = 0; i < nDims; i++)
6567 : {
6568 28 : stack[i].dst_inc_offset =
6569 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6570 14 : stack[i].src_inc_offset =
6571 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6572 : }
6573 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6574 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6575 :
6576 6 : size_t dimIdx = 0;
6577 6 : const size_t nDimsMinus1 = nDims - 1;
6578 :
6579 34 : lbl_next_depth:
6580 34 : if (dimIdx == nDimsMinus1)
6581 : {
6582 23 : auto nIters = count[dimIdx];
6583 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6584 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6585 : while (true)
6586 : {
6587 : double adfVal[2];
6588 : const double *padfSrcVal;
6589 86 : if (bIsBufferDataTypeNativeDataType)
6590 : {
6591 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6592 : }
6593 : else
6594 : {
6595 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6596 : &adfVal[0], dtDouble);
6597 36 : padfSrcVal = adfVal;
6598 : }
6599 :
6600 148 : if (bSelfAndParentHaveNoData &&
6601 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6602 : {
6603 3 : dst_ptr[0] = adfSrcNoData[0];
6604 3 : if (bDTIsComplex)
6605 : {
6606 1 : dst_ptr[1] = adfSrcNoData[1];
6607 : }
6608 : }
6609 : else
6610 : {
6611 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6612 83 : if (bDTIsComplex)
6613 : {
6614 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6615 : }
6616 : }
6617 :
6618 86 : if ((--nIters) == 0)
6619 23 : break;
6620 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6621 63 : src_ptr += stack[dimIdx].src_inc_offset;
6622 63 : }
6623 : }
6624 : else
6625 : {
6626 11 : stack[dimIdx].nIters = count[dimIdx];
6627 : while (true)
6628 : {
6629 28 : dimIdx++;
6630 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6631 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6632 28 : goto lbl_next_depth;
6633 28 : lbl_return_to_caller:
6634 28 : dimIdx--;
6635 28 : if ((--stack[dimIdx].nIters) == 0)
6636 11 : break;
6637 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6638 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6639 : }
6640 : }
6641 34 : if (dimIdx > 0)
6642 28 : goto lbl_return_to_caller;
6643 :
6644 : // If the parent array is not double/complex-double, then convert the
6645 : // values to it, before calling Write(), as some implementations can be
6646 : // very slow when doing the type conversion.
6647 6 : const auto &eParentDT = m_poParent->GetDataType();
6648 6 : const size_t nParentDTSize = eParentDT.GetSize();
6649 6 : if (nParentDTSize <= nDTSize / 2)
6650 : {
6651 : // Copy in-place by making sure that source and target do not overlap
6652 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6653 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6654 :
6655 : // Copy first element
6656 : {
6657 6 : std::vector<GByte> abyTemp(nParentDTSize);
6658 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6659 6 : static_cast<int>(nDTSize), &abyTemp[0],
6660 : eParentNumericDT, static_cast<int>(nParentDTSize),
6661 : 1);
6662 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6663 : }
6664 : // Remaining elements
6665 86 : for (size_t i = 1; i < nElts; ++i)
6666 : {
6667 80 : GDALCopyWords64(
6668 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6669 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6670 : eParentNumericDT, 0, 1);
6671 : }
6672 : }
6673 :
6674 : const bool ret =
6675 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6676 : eParentDT, pTempBuffer);
6677 :
6678 6 : VSIFree(pTempBuffer);
6679 6 : return ret;
6680 : }
6681 :
6682 : /************************************************************************/
6683 : /* GetUnscaled() */
6684 : /************************************************************************/
6685 :
6686 : /** Return an array that is the unscaled version of the current one.
6687 : *
6688 : * That is each value of the unscaled array will be
6689 : * unscaled_value = raw_value * GetScale() + GetOffset()
6690 : *
6691 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6692 : * from unscaled values to raw values.
6693 : *
6694 : * This is the same as the C function GDALMDArrayGetUnscaled().
6695 : *
6696 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6697 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6698 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6699 : * @return a new array, that holds a reference to the original one, and thus is
6700 : * a view of it (not a copy), or nullptr in case of error.
6701 : */
6702 : std::shared_ptr<GDALMDArray>
6703 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6704 : double dfOverriddenDstNodata) const
6705 : {
6706 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6707 17 : if (!self)
6708 : {
6709 0 : CPLError(CE_Failure, CPLE_AppDefined,
6710 : "Driver implementation issue: m_pSelf not set !");
6711 0 : return nullptr;
6712 : }
6713 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6714 : {
6715 0 : CPLError(CE_Failure, CPLE_AppDefined,
6716 : "GetUnscaled() only supports numeric data type");
6717 0 : return nullptr;
6718 : }
6719 : const double dfScale =
6720 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6721 : const double dfOffset =
6722 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6723 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6724 4 : return self;
6725 :
6726 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6727 13 : ? GDT_CFloat64
6728 13 : : GDT_Float64;
6729 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6730 : {
6731 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6732 0 : eDT = GDT_Float16;
6733 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6734 1 : eDT = GDT_Float32;
6735 : }
6736 :
6737 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6738 13 : dfOverriddenDstNodata, eDT);
6739 : }
6740 :
6741 : /************************************************************************/
6742 : /* GDALMDArrayMask */
6743 : /************************************************************************/
6744 :
6745 : class GDALMDArrayMask final : public GDALPamMDArray
6746 : {
6747 : private:
6748 : std::shared_ptr<GDALMDArray> m_poParent{};
6749 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
6750 : double m_dfMissingValue = 0.0;
6751 : bool m_bHasMissingValue = false;
6752 : double m_dfFillValue = 0.0;
6753 : bool m_bHasFillValue = false;
6754 : double m_dfValidMin = 0.0;
6755 : bool m_bHasValidMin = false;
6756 : double m_dfValidMax = 0.0;
6757 : bool m_bHasValidMax = false;
6758 : std::vector<uint32_t> m_anValidFlagMasks{};
6759 : std::vector<uint32_t> m_anValidFlagValues{};
6760 :
6761 : bool Init(CSLConstList papszOptions);
6762 :
6763 : template <typename Type>
6764 : void
6765 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6766 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6767 : const void *pTempBuffer,
6768 : const GDALExtendedDataType &oTmpBufferDT,
6769 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6770 :
6771 : protected:
6772 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6773 96 : : GDALAbstractMDArray(std::string(),
6774 96 : "Mask of " + poParent->GetFullName()),
6775 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6776 96 : GDALPamMultiDim::GetPAM(poParent),
6777 : poParent->GetContext()),
6778 240 : m_poParent(std::move(poParent))
6779 : {
6780 48 : }
6781 :
6782 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6783 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6784 : const GDALExtendedDataType &bufferDataType,
6785 : void *pDstBuffer) const override;
6786 :
6787 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6788 : CSLConstList papszOptions) const override
6789 : {
6790 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6791 : }
6792 :
6793 : public:
6794 : static std::shared_ptr<GDALMDArrayMask>
6795 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6796 : CSLConstList papszOptions);
6797 :
6798 1 : bool IsWritable() const override
6799 : {
6800 1 : return false;
6801 : }
6802 :
6803 54 : const std::string &GetFilename() const override
6804 : {
6805 54 : return m_poParent->GetFilename();
6806 : }
6807 :
6808 : const std::vector<std::shared_ptr<GDALDimension>> &
6809 382 : GetDimensions() const override
6810 : {
6811 382 : return m_poParent->GetDimensions();
6812 : }
6813 :
6814 138 : const GDALExtendedDataType &GetDataType() const override
6815 : {
6816 138 : return m_dt;
6817 : }
6818 :
6819 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6820 : {
6821 1 : return m_poParent->GetSpatialRef();
6822 : }
6823 :
6824 2 : std::vector<GUInt64> GetBlockSize() const override
6825 : {
6826 2 : return m_poParent->GetBlockSize();
6827 : }
6828 : };
6829 :
6830 : /************************************************************************/
6831 : /* GDALMDArrayMask::Create() */
6832 : /************************************************************************/
6833 :
6834 : /* static */ std::shared_ptr<GDALMDArrayMask>
6835 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6836 : CSLConstList papszOptions)
6837 : {
6838 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6839 48 : newAr->SetSelf(newAr);
6840 48 : if (!newAr->Init(papszOptions))
6841 6 : return nullptr;
6842 42 : return newAr;
6843 : }
6844 :
6845 : /************************************************************************/
6846 : /* GDALMDArrayMask::Init() */
6847 : /************************************************************************/
6848 :
6849 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6850 : {
6851 : const auto GetSingleValNumericAttr =
6852 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6853 : {
6854 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6855 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6856 : {
6857 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6858 21 : if (anDimSizes.empty() ||
6859 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6860 : {
6861 11 : bHasVal = true;
6862 11 : dfVal = poAttr->ReadAsDouble();
6863 : }
6864 : }
6865 192 : };
6866 :
6867 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6868 48 : m_dfMissingValue);
6869 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6870 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6871 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6872 :
6873 : {
6874 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6875 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6876 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6877 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6878 : {
6879 6 : m_bHasValidMin = true;
6880 6 : m_bHasValidMax = true;
6881 6 : auto vals = poValidRange->ReadAsDoubleArray();
6882 6 : CPLAssert(vals.size() == 2);
6883 6 : m_dfValidMin = vals[0];
6884 6 : m_dfValidMax = vals[1];
6885 : }
6886 : }
6887 :
6888 : // Take into account
6889 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6890 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6891 : const char *pszUnmaskFlags =
6892 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6893 48 : if (pszUnmaskFlags)
6894 : {
6895 : const auto IsScalarStringAttr =
6896 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6897 : {
6898 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6899 26 : (poAttr->GetDimensionsSize().empty() ||
6900 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6901 26 : poAttr->GetDimensionsSize()[0] == 1));
6902 : };
6903 :
6904 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6905 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6906 : {
6907 1 : CPLError(CE_Failure, CPLE_AppDefined,
6908 : "UNMASK_FLAGS option specified but array has no "
6909 : "flag_meanings attribute");
6910 1 : return false;
6911 : }
6912 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6913 13 : if (!pszFlagMeanings)
6914 : {
6915 1 : CPLError(CE_Failure, CPLE_AppDefined,
6916 : "Cannot read flag_meanings attribute");
6917 1 : return false;
6918 : }
6919 :
6920 : const auto IsSingleDimNumericAttr =
6921 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6922 : {
6923 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6924 26 : poAttr->GetDimensionsSize().size() == 1;
6925 : };
6926 :
6927 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6928 : const bool bHasFlagValues =
6929 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6930 :
6931 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6932 : const bool bHasFlagMasks =
6933 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6934 :
6935 12 : if (!bHasFlagValues && !bHasFlagMasks)
6936 : {
6937 1 : CPLError(CE_Failure, CPLE_AppDefined,
6938 : "Cannot find flag_values and/or flag_masks attribute");
6939 1 : return false;
6940 : }
6941 :
6942 : const CPLStringList aosUnmaskFlags(
6943 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6944 : const CPLStringList aosFlagMeanings(
6945 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6946 :
6947 11 : if (bHasFlagValues)
6948 : {
6949 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6950 : // We could support Int64 or UInt64, but more work...
6951 7 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6952 7 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6953 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6954 : {
6955 0 : CPLError(CE_Failure, CPLE_NotSupported,
6956 : "Unsupported data type for flag_values attribute: %s",
6957 : GDALGetDataTypeName(eType));
6958 0 : return false;
6959 : }
6960 : }
6961 :
6962 11 : if (bHasFlagMasks)
6963 : {
6964 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6965 : // We could support Int64 or UInt64, but more work...
6966 6 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6967 6 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6968 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6969 : {
6970 0 : CPLError(CE_Failure, CPLE_NotSupported,
6971 : "Unsupported data type for flag_masks attribute: %s",
6972 : GDALGetDataTypeName(eType));
6973 0 : return false;
6974 : }
6975 : }
6976 :
6977 : const std::vector<double> adfValues(
6978 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6979 11 : : std::vector<double>());
6980 : const std::vector<double> adfMasks(
6981 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6982 11 : : std::vector<double>());
6983 :
6984 18 : if (bHasFlagValues &&
6985 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6986 : {
6987 1 : CPLError(CE_Failure, CPLE_AppDefined,
6988 : "Number of values in flag_values attribute is different "
6989 : "from the one in flag_meanings");
6990 1 : return false;
6991 : }
6992 :
6993 16 : if (bHasFlagMasks &&
6994 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6995 : {
6996 1 : CPLError(CE_Failure, CPLE_AppDefined,
6997 : "Number of values in flag_masks attribute is different "
6998 : "from the one in flag_meanings");
6999 1 : return false;
7000 : }
7001 :
7002 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
7003 : {
7004 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
7005 11 : if (nIdxFlag < 0)
7006 : {
7007 1 : CPLError(
7008 : CE_Failure, CPLE_AppDefined,
7009 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
7010 : aosUnmaskFlags[i], pszFlagMeanings);
7011 1 : return false;
7012 : }
7013 :
7014 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
7015 : {
7016 0 : CPLError(CE_Failure, CPLE_AppDefined,
7017 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
7018 0 : adfValues[nIdxFlag]);
7019 0 : return false;
7020 : }
7021 :
7022 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
7023 : {
7024 0 : CPLError(CE_Failure, CPLE_AppDefined,
7025 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
7026 0 : adfMasks[nIdxFlag]);
7027 0 : return false;
7028 : }
7029 :
7030 10 : if (bHasFlagValues)
7031 : {
7032 12 : m_anValidFlagValues.push_back(
7033 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
7034 : }
7035 :
7036 10 : if (bHasFlagMasks)
7037 : {
7038 12 : m_anValidFlagMasks.push_back(
7039 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
7040 : }
7041 : }
7042 : }
7043 :
7044 42 : return true;
7045 : }
7046 :
7047 : /************************************************************************/
7048 : /* IRead() */
7049 : /************************************************************************/
7050 :
7051 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7052 : const GInt64 *arrayStep,
7053 : const GPtrDiff_t *bufferStride,
7054 : const GDALExtendedDataType &bufferDataType,
7055 : void *pDstBuffer) const
7056 : {
7057 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7058 : {
7059 0 : CPLError(CE_Failure, CPLE_AppDefined,
7060 : "%s: only reading to a numeric data type is supported",
7061 : __func__);
7062 0 : return false;
7063 : }
7064 51 : size_t nElts = 1;
7065 51 : const size_t nDims = GetDimensionCount();
7066 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7067 139 : for (size_t i = 0; i < nDims; i++)
7068 88 : nElts *= count[i];
7069 51 : if (nDims > 0)
7070 : {
7071 46 : tmpBufferStrideVector.back() = 1;
7072 88 : for (size_t i = nDims - 1; i > 0;)
7073 : {
7074 42 : --i;
7075 42 : tmpBufferStrideVector[i] =
7076 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7077 : }
7078 : }
7079 :
7080 : /* Optimized case: if we are an integer data type and that there is no */
7081 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7082 : /* directly */
7083 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7084 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7085 34 : m_anValidFlagMasks.empty() &&
7086 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7087 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7088 : {
7089 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7090 7 : if (bBufferDataTypeIsByte) // Byte case
7091 : {
7092 4 : bool bContiguous = true;
7093 10 : for (size_t i = 0; i < nDims; i++)
7094 : {
7095 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7096 : {
7097 1 : bContiguous = false;
7098 1 : break;
7099 : }
7100 : }
7101 4 : if (bContiguous)
7102 : {
7103 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7104 3 : memset(pDstBuffer, 1, nElts);
7105 3 : return true;
7106 : }
7107 : }
7108 :
7109 : struct Stack
7110 : {
7111 : size_t nIters = 0;
7112 : GByte *dst_ptr = nullptr;
7113 : GPtrDiff_t dst_inc_offset = 0;
7114 : };
7115 :
7116 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7117 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7118 13 : for (size_t i = 0; i < nDims; i++)
7119 : {
7120 9 : stack[i].dst_inc_offset =
7121 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7122 : }
7123 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7124 :
7125 4 : size_t dimIdx = 0;
7126 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7127 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7128 4 : CPLAssert(nBufferDTSize <= 16);
7129 4 : const GByte flag = 1;
7130 4 : GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
7131 : bufferDataType.GetNumericDataType(), 0, 1);
7132 :
7133 28 : lbl_next_depth:
7134 28 : if (dimIdx == nDimsMinus1)
7135 : {
7136 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7137 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7138 :
7139 : while (true)
7140 : {
7141 : // cppcheck-suppress knownConditionTrueFalse
7142 73 : if (bBufferDataTypeIsByte)
7143 : {
7144 24 : *dst_ptr = flag;
7145 : }
7146 : else
7147 : {
7148 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7149 : }
7150 :
7151 73 : if ((--nIters) == 0)
7152 19 : break;
7153 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7154 : }
7155 : }
7156 : else
7157 : {
7158 9 : stack[dimIdx].nIters = count[dimIdx];
7159 : while (true)
7160 : {
7161 24 : dimIdx++;
7162 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7163 24 : goto lbl_next_depth;
7164 24 : lbl_return_to_caller:
7165 24 : dimIdx--;
7166 24 : if ((--stack[dimIdx].nIters) == 0)
7167 9 : break;
7168 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7169 : }
7170 : }
7171 28 : if (dimIdx > 0)
7172 24 : goto lbl_return_to_caller;
7173 :
7174 4 : return true;
7175 : }
7176 :
7177 : const auto oTmpBufferDT =
7178 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7179 : ? GDALExtendedDataType::Create(GDT_Float64)
7180 88 : : m_poParent->GetDataType();
7181 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7182 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7183 44 : if (!pTempBuffer)
7184 0 : return false;
7185 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7186 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7187 : pTempBuffer))
7188 : {
7189 0 : VSIFree(pTempBuffer);
7190 0 : return false;
7191 : }
7192 :
7193 44 : switch (oTmpBufferDT.GetNumericDataType())
7194 : {
7195 7 : case GDT_UInt8:
7196 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7197 : pTempBuffer, oTmpBufferDT,
7198 : tmpBufferStrideVector);
7199 7 : break;
7200 :
7201 0 : case GDT_Int8:
7202 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7203 : pTempBuffer, oTmpBufferDT,
7204 : tmpBufferStrideVector);
7205 0 : break;
7206 :
7207 1 : case GDT_UInt16:
7208 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7209 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7210 : tmpBufferStrideVector);
7211 1 : break;
7212 :
7213 14 : case GDT_Int16:
7214 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7215 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7216 : tmpBufferStrideVector);
7217 14 : break;
7218 :
7219 1 : case GDT_UInt32:
7220 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7221 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7222 : tmpBufferStrideVector);
7223 1 : break;
7224 :
7225 5 : case GDT_Int32:
7226 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7227 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7228 : tmpBufferStrideVector);
7229 5 : break;
7230 :
7231 0 : case GDT_UInt64:
7232 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7233 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7234 : tmpBufferStrideVector);
7235 0 : break;
7236 :
7237 0 : case GDT_Int64:
7238 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7239 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7240 : tmpBufferStrideVector);
7241 0 : break;
7242 :
7243 0 : case GDT_Float16:
7244 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7245 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7246 : tmpBufferStrideVector);
7247 0 : break;
7248 :
7249 7 : case GDT_Float32:
7250 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7251 : pTempBuffer, oTmpBufferDT,
7252 : tmpBufferStrideVector);
7253 7 : break;
7254 :
7255 9 : case GDT_Float64:
7256 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7257 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7258 : tmpBufferStrideVector);
7259 9 : break;
7260 0 : case GDT_Unknown:
7261 : case GDT_CInt16:
7262 : case GDT_CInt32:
7263 : case GDT_CFloat16:
7264 : case GDT_CFloat32:
7265 : case GDT_CFloat64:
7266 : case GDT_TypeCount:
7267 0 : CPLAssert(false);
7268 : break;
7269 : }
7270 :
7271 44 : VSIFree(pTempBuffer);
7272 :
7273 44 : return true;
7274 : }
7275 :
7276 : /************************************************************************/
7277 : /* IsValidForDT() */
7278 : /************************************************************************/
7279 :
7280 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7281 : {
7282 40 : if (std::isnan(dfVal))
7283 0 : return false;
7284 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7285 0 : return false;
7286 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7287 0 : return false;
7288 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7289 : }
7290 :
7291 9 : template <> bool IsValidForDT<double>(double)
7292 : {
7293 9 : return true;
7294 : }
7295 :
7296 : /************************************************************************/
7297 : /* IsNan() */
7298 : /************************************************************************/
7299 :
7300 1438 : template <typename Type> inline bool IsNan(Type)
7301 : {
7302 1438 : return false;
7303 : }
7304 :
7305 65 : template <> bool IsNan<double>(double val)
7306 : {
7307 65 : return std::isnan(val);
7308 : }
7309 :
7310 26 : template <> bool IsNan<float>(float val)
7311 : {
7312 26 : return std::isnan(val);
7313 : }
7314 :
7315 : /************************************************************************/
7316 : /* ReadInternal() */
7317 : /************************************************************************/
7318 :
7319 : template <typename Type>
7320 44 : void GDALMDArrayMask::ReadInternal(
7321 : const size_t *count, const GPtrDiff_t *bufferStride,
7322 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7323 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7324 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7325 : {
7326 44 : const size_t nDims = GetDimensionCount();
7327 :
7328 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7329 : {
7330 220 : if (bHasVal)
7331 : {
7332 49 : if (IsValidForDT<Type>(dfVal))
7333 : {
7334 49 : return static_cast<Type>(dfVal);
7335 : }
7336 : else
7337 : {
7338 0 : bHasVal = false;
7339 : }
7340 : }
7341 171 : return 0;
7342 : };
7343 :
7344 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7345 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7346 : const Type nNoDataValue =
7347 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7348 44 : bool bHasMissingValue = m_bHasMissingValue;
7349 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7350 44 : bool bHasFillValue = m_bHasFillValue;
7351 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7352 44 : bool bHasValidMin = m_bHasValidMin;
7353 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7354 44 : bool bHasValidMax = m_bHasValidMax;
7355 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7356 44 : const bool bHasValidFlags =
7357 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7358 :
7359 351 : const auto IsValidFlag = [this](Type v)
7360 : {
7361 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7362 : {
7363 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7364 : {
7365 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7366 : m_anValidFlagValues[i])
7367 : {
7368 4 : return true;
7369 : }
7370 : }
7371 : }
7372 42 : else if (!m_anValidFlagValues.empty())
7373 : {
7374 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7375 : {
7376 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7377 : {
7378 4 : return true;
7379 : }
7380 : }
7381 : }
7382 : else /* if( !m_anValidFlagMasks.empty() ) */
7383 : {
7384 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7385 : {
7386 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7387 : {
7388 9 : return true;
7389 : }
7390 : }
7391 : }
7392 37 : return false;
7393 : };
7394 :
7395 : #define GET_MASK_FOR_SAMPLE(v) \
7396 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7397 : !(bHasMissingValue && v == nMissingValue) && \
7398 : !(bHasFillValue && v == nFillValue) && \
7399 : !(bHasValidMin && v < nValidMin) && \
7400 : !(bHasValidMax && v > nValidMax) && \
7401 : (!bHasValidFlags || IsValidFlag(v)));
7402 :
7403 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7404 : /* Optimized case: Byte output and output buffer is contiguous */
7405 44 : if (bBufferDataTypeIsByte)
7406 : {
7407 40 : bool bContiguous = true;
7408 103 : for (size_t i = 0; i < nDims; i++)
7409 : {
7410 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7411 : {
7412 1 : bContiguous = false;
7413 1 : break;
7414 : }
7415 : }
7416 40 : if (bContiguous)
7417 : {
7418 39 : size_t nElts = 1;
7419 102 : for (size_t i = 0; i < nDims; i++)
7420 63 : nElts *= count[i];
7421 :
7422 1113 : for (size_t i = 0; i < nElts; i++)
7423 : {
7424 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7425 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7426 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7427 : }
7428 39 : return;
7429 : }
7430 : }
7431 :
7432 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7433 :
7434 : struct Stack
7435 : {
7436 : size_t nIters = 0;
7437 : const GByte *src_ptr = nullptr;
7438 : GByte *dst_ptr = nullptr;
7439 : GPtrDiff_t src_inc_offset = 0;
7440 : GPtrDiff_t dst_inc_offset = 0;
7441 : };
7442 :
7443 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7444 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7445 15 : for (size_t i = 0; i < nDims; i++)
7446 : {
7447 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7448 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7449 10 : stack[i].dst_inc_offset =
7450 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7451 : }
7452 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7453 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7454 :
7455 5 : size_t dimIdx = 0;
7456 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7457 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7458 5 : CPLAssert(nBufferDTSize <= 16);
7459 15 : for (GByte flag = 0; flag <= 1; flag++)
7460 : {
7461 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7462 : bufferDataType.GetNumericDataType(), 0, 1);
7463 : }
7464 :
7465 43 : lbl_next_depth:
7466 43 : if (dimIdx == nDimsMinus1)
7467 : {
7468 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7469 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7470 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7471 :
7472 420 : while (true)
7473 : {
7474 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7475 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7476 :
7477 455 : if (bBufferDataTypeIsByte)
7478 : {
7479 24 : *dst_ptr = flag;
7480 : }
7481 : else
7482 : {
7483 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7484 : }
7485 :
7486 455 : if ((--nIters) == 0)
7487 35 : break;
7488 420 : src_ptr += stack[dimIdx].src_inc_offset;
7489 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7490 : }
7491 : }
7492 : else
7493 : {
7494 8 : stack[dimIdx].nIters = count[dimIdx];
7495 : while (true)
7496 : {
7497 38 : dimIdx++;
7498 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7499 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7500 38 : goto lbl_next_depth;
7501 38 : lbl_return_to_caller:
7502 38 : dimIdx--;
7503 38 : if ((--stack[dimIdx].nIters) == 0)
7504 8 : break;
7505 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7506 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7507 : }
7508 : }
7509 43 : if (dimIdx > 0)
7510 38 : goto lbl_return_to_caller;
7511 : }
7512 :
7513 : /************************************************************************/
7514 : /* GetMask() */
7515 : /************************************************************************/
7516 :
7517 : /** Return an array that is a mask for the current array
7518 :
7519 : This array will be of type Byte, with values set to 0 to indicate invalid
7520 : pixels of the current array, and values set to 1 to indicate valid pixels.
7521 :
7522 : The generic implementation honours the NoDataValue, as well as various
7523 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7524 : and valid_range.
7525 :
7526 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7527 : can be used to specify strings of the "flag_meanings" attribute
7528 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7529 : for which pixels matching any of those flags will be set at 1 in the mask array,
7530 : and pixels matching none of those flags will be set at 0.
7531 : For example, let's consider the following netCDF variable defined with:
7532 : \verbatim
7533 : l2p_flags:valid_min = 0s ;
7534 : l2p_flags:valid_max = 256s ;
7535 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7536 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7537 : \endverbatim
7538 :
7539 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7540 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7541 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7542 : will be 1.
7543 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7544 : will be 0.
7545 :
7546 : This is the same as the C function GDALMDArrayGetMask().
7547 :
7548 : @param papszOptions NULL-terminated list of options, or NULL.
7549 :
7550 : @return a new array, that holds a reference to the original one, and thus is
7551 : a view of it (not a copy), or nullptr in case of error.
7552 : */
7553 : std::shared_ptr<GDALMDArray>
7554 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7555 : {
7556 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7557 49 : if (!self)
7558 : {
7559 0 : CPLError(CE_Failure, CPLE_AppDefined,
7560 : "Driver implementation issue: m_pSelf not set !");
7561 0 : return nullptr;
7562 : }
7563 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7564 : {
7565 1 : CPLError(CE_Failure, CPLE_AppDefined,
7566 : "GetMask() only supports numeric data type");
7567 1 : return nullptr;
7568 : }
7569 48 : return GDALMDArrayMask::Create(self, papszOptions);
7570 : }
7571 :
7572 : /************************************************************************/
7573 : /* IsRegularlySpaced() */
7574 : /************************************************************************/
7575 :
7576 : /** Returns whether an array is a 1D regularly spaced array.
7577 : *
7578 : * @param[out] dfStart First value in the array
7579 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7580 : * @return true if the array is regularly spaced.
7581 : */
7582 355 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7583 : {
7584 355 : dfStart = 0;
7585 355 : dfIncrement = 0;
7586 355 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7587 0 : return false;
7588 355 : const auto nSize = GetDimensions()[0]->GetSize();
7589 355 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7590 2 : return false;
7591 :
7592 353 : size_t nCount = static_cast<size_t>(nSize);
7593 706 : std::vector<double> adfTmp;
7594 : try
7595 : {
7596 353 : adfTmp.resize(nCount);
7597 : }
7598 0 : catch (const std::exception &)
7599 : {
7600 0 : return false;
7601 : }
7602 :
7603 353 : GUInt64 anStart[1] = {0};
7604 353 : size_t anCount[1] = {nCount};
7605 :
7606 : const auto IsRegularlySpacedInternal =
7607 45445 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7608 : {
7609 406 : dfStart = adfTmp[0];
7610 406 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7611 406 : if (dfIncrement == 0)
7612 : {
7613 3 : return false;
7614 : }
7615 11247 : for (size_t i = 1; i < anCount[0]; i++)
7616 : {
7617 10858 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7618 10858 : 1e-3 * fabs(dfIncrement))
7619 : {
7620 14 : return false;
7621 : }
7622 : }
7623 389 : return true;
7624 353 : };
7625 :
7626 : // First try with the first block(s). This can avoid excessive processing
7627 : // time, for example with Zarr datasets.
7628 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7629 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7630 353 : const auto nBlockSize = GetBlockSize()[0];
7631 353 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7632 : {
7633 : size_t nReducedCount =
7634 56 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7635 176 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7636 120 : nReducedCount *= 2;
7637 56 : anCount[0] = nReducedCount;
7638 56 : if (!Read(anStart, anCount, nullptr, nullptr,
7639 112 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7640 : {
7641 0 : return false;
7642 : }
7643 56 : if (!IsRegularlySpacedInternal())
7644 : {
7645 3 : return false;
7646 : }
7647 :
7648 : // Get next values
7649 53 : anStart[0] = nReducedCount;
7650 53 : anCount[0] = nCount - nReducedCount;
7651 : }
7652 :
7653 350 : if (!Read(anStart, anCount, nullptr, nullptr,
7654 700 : GDALExtendedDataType::Create(GDT_Float64),
7655 350 : &adfTmp[static_cast<size_t>(anStart[0])]))
7656 : {
7657 0 : return false;
7658 : }
7659 :
7660 350 : return IsRegularlySpacedInternal();
7661 : }
7662 :
7663 : /************************************************************************/
7664 : /* GuessGeoTransform() */
7665 : /************************************************************************/
7666 :
7667 : /** Returns whether 2 specified dimensions form a geotransform
7668 : *
7669 : * @param nDimX Index of the X axis.
7670 : * @param nDimY Index of the Y axis.
7671 : * @param bPixelIsPoint Whether the geotransform should be returned
7672 : * with the pixel-is-point (pixel-center) convention
7673 : * (bPixelIsPoint = true), or with the pixel-is-area
7674 : * (top left corner convention)
7675 : * (bPixelIsPoint = false)
7676 : * @param[out] gt Computed geotransform
7677 : * @return true if a geotransform could be computed.
7678 : */
7679 498 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7680 : bool bPixelIsPoint,
7681 : GDALGeoTransform >) const
7682 : {
7683 498 : const auto &dims(GetDimensions());
7684 996 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7685 996 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7686 498 : double dfXStart = 0.0;
7687 498 : double dfXSpacing = 0.0;
7688 498 : double dfYStart = 0.0;
7689 498 : double dfYSpacing = 0.0;
7690 1112 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7691 614 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7692 897 : poVarY && poVarY->GetDimensionCount() == 1 &&
7693 295 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7694 1095 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7695 290 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7696 : {
7697 288 : gt.xorig = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7698 288 : gt.xscale = dfXSpacing;
7699 288 : gt.xrot = 0;
7700 288 : gt.yorig = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7701 288 : gt.yrot = 0;
7702 288 : gt.yscale = dfYSpacing;
7703 288 : return true;
7704 : }
7705 210 : return false;
7706 : }
7707 :
7708 : /** Returns whether 2 specified dimensions form a geotransform
7709 : *
7710 : * @param nDimX Index of the X axis.
7711 : * @param nDimY Index of the Y axis.
7712 : * @param bPixelIsPoint Whether the geotransform should be returned
7713 : * with the pixel-is-point (pixel-center) convention
7714 : * (bPixelIsPoint = true), or with the pixel-is-area
7715 : * (top left corner convention)
7716 : * (bPixelIsPoint = false)
7717 : * @param[out] adfGeoTransform Computed geotransform
7718 : * @return true if a geotransform could be computed.
7719 : */
7720 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7721 : bool bPixelIsPoint,
7722 : double adfGeoTransform[6]) const
7723 : {
7724 0 : GDALGeoTransform *gt =
7725 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7726 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7727 : }
7728 :
7729 : /************************************************************************/
7730 : /* GDALMDArrayResampled */
7731 : /************************************************************************/
7732 :
7733 : class GDALMDArrayResampledDataset;
7734 :
7735 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7736 : {
7737 : protected:
7738 : CPLErr IReadBlock(int, int, void *) override;
7739 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7740 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7741 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7742 : GSpacing nLineSpaceBuf,
7743 : GDALRasterIOExtraArg *psExtraArg) override;
7744 :
7745 : public:
7746 : explicit GDALMDArrayResampledDatasetRasterBand(
7747 : GDALMDArrayResampledDataset *poDSIn);
7748 :
7749 : double GetNoDataValue(int *pbHasNoData) override;
7750 : };
7751 :
7752 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7753 : {
7754 : friend class GDALMDArrayResampled;
7755 : friend class GDALMDArrayResampledDatasetRasterBand;
7756 :
7757 : std::shared_ptr<GDALMDArray> m_poArray;
7758 : const size_t m_iXDim;
7759 : const size_t m_iYDim;
7760 : GDALGeoTransform m_gt{};
7761 : bool m_bHasGT = false;
7762 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7763 :
7764 : std::vector<GUInt64> m_anOffset{};
7765 : std::vector<size_t> m_anCount{};
7766 : std::vector<GPtrDiff_t> m_anStride{};
7767 :
7768 : std::string m_osFilenameLong{};
7769 : std::string m_osFilenameLat{};
7770 :
7771 : public:
7772 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7773 : size_t iXDim, size_t iYDim)
7774 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7775 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7776 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7777 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7778 : {
7779 24 : const auto &dims(m_poArray->GetDimensions());
7780 :
7781 24 : nRasterYSize = static_cast<int>(
7782 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7783 24 : nRasterXSize = static_cast<int>(
7784 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7785 :
7786 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7787 :
7788 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7789 24 : }
7790 :
7791 : ~GDALMDArrayResampledDataset() override;
7792 :
7793 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7794 : {
7795 43 : gt = m_gt;
7796 43 : return m_bHasGT ? CE_None : CE_Failure;
7797 : }
7798 :
7799 105 : const OGRSpatialReference *GetSpatialRef() const override
7800 : {
7801 105 : m_poSRS = m_poArray->GetSpatialRef();
7802 105 : if (m_poSRS)
7803 : {
7804 79 : m_poSRS.reset(m_poSRS->Clone());
7805 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7806 237 : for (auto &m : axisMapping)
7807 : {
7808 158 : if (m == static_cast<int>(m_iXDim) + 1)
7809 79 : m = 1;
7810 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7811 79 : m = 2;
7812 : }
7813 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7814 : }
7815 105 : return m_poSRS.get();
7816 : }
7817 :
7818 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7819 : const std::string &osFilenameLat)
7820 : {
7821 5 : m_osFilenameLong = osFilenameLong;
7822 5 : m_osFilenameLat = osFilenameLat;
7823 10 : CPLStringList aosGeoLoc;
7824 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7825 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7826 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7827 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7828 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7829 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7830 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7831 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7832 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7833 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7834 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7835 5 : }
7836 : };
7837 :
7838 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7839 : {
7840 24 : if (!m_osFilenameLong.empty())
7841 5 : VSIUnlink(m_osFilenameLong.c_str());
7842 24 : if (!m_osFilenameLat.empty())
7843 5 : VSIUnlink(m_osFilenameLat.c_str());
7844 48 : }
7845 :
7846 : /************************************************************************/
7847 : /* GDALMDArrayResampledDatasetRasterBand() */
7848 : /************************************************************************/
7849 :
7850 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7851 24 : GDALMDArrayResampledDataset *poDSIn)
7852 : {
7853 24 : const auto &poArray(poDSIn->m_poArray);
7854 24 : const auto blockSize(poArray->GetBlockSize());
7855 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7856 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7857 13 : blockSize[poDSIn->m_iYDim]))
7858 24 : : 1;
7859 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7860 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7861 13 : blockSize[poDSIn->m_iXDim]))
7862 24 : : poDSIn->GetRasterXSize();
7863 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7864 24 : eAccess = poDSIn->eAccess;
7865 24 : }
7866 :
7867 : /************************************************************************/
7868 : /* GetNoDataValue() */
7869 : /************************************************************************/
7870 :
7871 54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7872 : {
7873 54 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7874 54 : const auto &poArray(l_poDS->m_poArray);
7875 54 : bool bHasNodata = false;
7876 54 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7877 54 : if (pbHasNoData)
7878 48 : *pbHasNoData = bHasNodata;
7879 54 : return dfRes;
7880 : }
7881 :
7882 : /************************************************************************/
7883 : /* IReadBlock() */
7884 : /************************************************************************/
7885 :
7886 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7887 : int nBlockYOff,
7888 : void *pImage)
7889 : {
7890 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7891 0 : const int nXOff = nBlockXOff * nBlockXSize;
7892 0 : const int nYOff = nBlockYOff * nBlockYSize;
7893 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7894 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7895 : GDALRasterIOExtraArg sExtraArg;
7896 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7897 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7898 : nReqXSize, nReqYSize, eDataType, nDTSize,
7899 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7900 : }
7901 :
7902 : /************************************************************************/
7903 : /* IRasterIO() */
7904 : /************************************************************************/
7905 :
7906 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7907 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7908 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7909 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7910 : GDALRasterIOExtraArg *psExtraArg)
7911 : {
7912 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7913 32 : const auto &poArray(l_poDS->m_poArray);
7914 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7915 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7916 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7917 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7918 : {
7919 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7920 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7921 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7922 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7923 :
7924 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7925 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7926 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7927 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7928 :
7929 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7930 32 : l_poDS->m_anCount.data(), nullptr,
7931 32 : l_poDS->m_anStride.data(),
7932 64 : GDALExtendedDataType::Create(eBufType), pData)
7933 32 : ? CE_None
7934 32 : : CE_Failure;
7935 : }
7936 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7937 : pData, nBufXSize, nBufYSize, eBufType,
7938 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7939 : }
7940 :
7941 : class GDALMDArrayResampled final : public GDALPamMDArray
7942 : {
7943 : private:
7944 : std::shared_ptr<GDALMDArray> m_poParent{};
7945 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7946 : std::vector<GUInt64> m_anBlockSize;
7947 : GDALExtendedDataType m_dt;
7948 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7949 : std::shared_ptr<GDALMDArray> m_poVarX{};
7950 : std::shared_ptr<GDALMDArray> m_poVarY{};
7951 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7952 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7953 :
7954 : protected:
7955 21 : GDALMDArrayResampled(
7956 : const std::shared_ptr<GDALMDArray> &poParent,
7957 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7958 : const std::vector<GUInt64> &anBlockSize)
7959 42 : : GDALAbstractMDArray(std::string(),
7960 42 : "Resampled view of " + poParent->GetFullName()),
7961 : GDALPamMDArray(
7962 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7963 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7964 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7965 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7966 : {
7967 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7968 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7969 21 : }
7970 :
7971 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7972 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7973 : const GDALExtendedDataType &bufferDataType,
7974 : void *pDstBuffer) const override;
7975 :
7976 : public:
7977 : static std::shared_ptr<GDALMDArray>
7978 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7979 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7980 : GDALRIOResampleAlg resampleAlg,
7981 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7982 :
7983 42 : ~GDALMDArrayResampled() override
7984 21 : {
7985 : // First close the warped VRT
7986 21 : m_poReprojectedDS.reset();
7987 21 : m_poParentDS.reset();
7988 42 : }
7989 :
7990 11 : bool IsWritable() const override
7991 : {
7992 11 : return false;
7993 : }
7994 :
7995 74 : const std::string &GetFilename() const override
7996 : {
7997 74 : return m_poParent->GetFilename();
7998 : }
7999 :
8000 : const std::vector<std::shared_ptr<GDALDimension>> &
8001 257 : GetDimensions() const override
8002 : {
8003 257 : return m_apoDims;
8004 : }
8005 :
8006 109 : const GDALExtendedDataType &GetDataType() const override
8007 : {
8008 109 : return m_dt;
8009 : }
8010 :
8011 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
8012 : {
8013 21 : return m_poSRS;
8014 : }
8015 :
8016 12 : std::vector<GUInt64> GetBlockSize() const override
8017 : {
8018 12 : return m_anBlockSize;
8019 : }
8020 :
8021 : std::shared_ptr<GDALAttribute>
8022 1 : GetAttribute(const std::string &osName) const override
8023 : {
8024 1 : return m_poParent->GetAttribute(osName);
8025 : }
8026 :
8027 : std::vector<std::shared_ptr<GDALAttribute>>
8028 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
8029 : {
8030 12 : return m_poParent->GetAttributes(papszOptions);
8031 : }
8032 :
8033 1 : const std::string &GetUnit() const override
8034 : {
8035 1 : return m_poParent->GetUnit();
8036 : }
8037 :
8038 1 : const void *GetRawNoDataValue() const override
8039 : {
8040 1 : return m_poParent->GetRawNoDataValue();
8041 : }
8042 :
8043 1 : double GetOffset(bool *pbHasOffset,
8044 : GDALDataType *peStorageType) const override
8045 : {
8046 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
8047 : }
8048 :
8049 1 : double GetScale(bool *pbHasScale,
8050 : GDALDataType *peStorageType) const override
8051 : {
8052 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
8053 : }
8054 : };
8055 :
8056 : /************************************************************************/
8057 : /* GDALMDArrayResampled::Create() */
8058 : /************************************************************************/
8059 :
8060 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8061 : const std::shared_ptr<GDALMDArray> &poParent,
8062 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8063 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8064 : CSLConstList /* papszOptions */)
8065 : {
8066 29 : const char *pszResampleAlg = "nearest";
8067 29 : bool unsupported = false;
8068 29 : switch (resampleAlg)
8069 : {
8070 16 : case GRIORA_NearestNeighbour:
8071 16 : pszResampleAlg = "nearest";
8072 16 : break;
8073 2 : case GRIORA_Bilinear:
8074 2 : pszResampleAlg = "bilinear";
8075 2 : break;
8076 5 : case GRIORA_Cubic:
8077 5 : pszResampleAlg = "cubic";
8078 5 : break;
8079 1 : case GRIORA_CubicSpline:
8080 1 : pszResampleAlg = "cubicspline";
8081 1 : break;
8082 1 : case GRIORA_Lanczos:
8083 1 : pszResampleAlg = "lanczos";
8084 1 : break;
8085 1 : case GRIORA_Average:
8086 1 : pszResampleAlg = "average";
8087 1 : break;
8088 1 : case GRIORA_Mode:
8089 1 : pszResampleAlg = "mode";
8090 1 : break;
8091 1 : case GRIORA_Gauss:
8092 1 : unsupported = true;
8093 1 : break;
8094 0 : case GRIORA_RESERVED_START:
8095 0 : unsupported = true;
8096 0 : break;
8097 0 : case GRIORA_RESERVED_END:
8098 0 : unsupported = true;
8099 0 : break;
8100 1 : case GRIORA_RMS:
8101 1 : pszResampleAlg = "rms";
8102 1 : break;
8103 : }
8104 29 : if (unsupported)
8105 : {
8106 1 : CPLError(CE_Failure, CPLE_NotSupported,
8107 : "Unsupported resample method for GetResampled()");
8108 1 : return nullptr;
8109 : }
8110 :
8111 28 : if (poParent->GetDimensionCount() < 2)
8112 : {
8113 1 : CPLError(CE_Failure, CPLE_NotSupported,
8114 : "GetResampled() only supports 2 dimensions or more");
8115 1 : return nullptr;
8116 : }
8117 :
8118 27 : const auto &aoParentDims = poParent->GetDimensions();
8119 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8120 : {
8121 2 : CPLError(CE_Failure, CPLE_AppDefined,
8122 : "GetResampled(): apoNewDims size should be the same as "
8123 : "GetDimensionCount()");
8124 2 : return nullptr;
8125 : }
8126 :
8127 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8128 25 : apoNewDims.reserve(apoNewDimsIn.size());
8129 :
8130 50 : std::vector<GUInt64> anBlockSize;
8131 25 : anBlockSize.reserve(apoNewDimsIn.size());
8132 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8133 :
8134 50 : auto apoParentDims = poParent->GetDimensions();
8135 : // Special case for NASA EMIT datasets
8136 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8137 7 : apoParentDims[0]->GetName() == "downtrack" &&
8138 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8139 2 : apoParentDims[2]->GetName() == "bands");
8140 :
8141 : const size_t iYDimParent =
8142 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8143 : const size_t iXDimParent =
8144 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8145 :
8146 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8147 : {
8148 53 : if (i == iYDimParent || i == iXDimParent)
8149 48 : continue;
8150 5 : if (apoNewDimsIn[i] == nullptr)
8151 : {
8152 3 : apoNewDims.emplace_back(aoParentDims[i]);
8153 : }
8154 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8155 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8156 : {
8157 1 : CPLError(CE_Failure, CPLE_AppDefined,
8158 : "GetResampled(): apoNewDims[%u] should be the same "
8159 : "as its parent",
8160 : i);
8161 1 : return nullptr;
8162 : }
8163 : else
8164 : {
8165 1 : apoNewDims.emplace_back(aoParentDims[i]);
8166 : }
8167 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8168 : }
8169 :
8170 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8171 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8172 :
8173 24 : double dfXStart = 0.0;
8174 24 : double dfXSpacing = 0.0;
8175 24 : bool gotXSpacing = false;
8176 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8177 24 : if (poNewDimX)
8178 : {
8179 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8180 : {
8181 0 : CPLError(CE_Failure, CPLE_NotSupported,
8182 : "Too big size for X dimension");
8183 0 : return nullptr;
8184 : }
8185 4 : auto var = poNewDimX->GetIndexingVariable();
8186 4 : if (var)
8187 : {
8188 2 : if (var->GetDimensionCount() != 1 ||
8189 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8190 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8191 : {
8192 0 : CPLError(CE_Failure, CPLE_NotSupported,
8193 : "New X dimension should be indexed by a regularly "
8194 : "spaced variable");
8195 0 : return nullptr;
8196 : }
8197 1 : gotXSpacing = true;
8198 : }
8199 : }
8200 :
8201 24 : double dfYStart = 0.0;
8202 24 : double dfYSpacing = 0.0;
8203 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8204 24 : bool gotYSpacing = false;
8205 24 : if (poNewDimY)
8206 : {
8207 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8208 : {
8209 0 : CPLError(CE_Failure, CPLE_NotSupported,
8210 : "Too big size for Y dimension");
8211 0 : return nullptr;
8212 : }
8213 4 : auto var = poNewDimY->GetIndexingVariable();
8214 4 : if (var)
8215 : {
8216 2 : if (var->GetDimensionCount() != 1 ||
8217 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8218 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8219 : {
8220 0 : CPLError(CE_Failure, CPLE_NotSupported,
8221 : "New Y dimension should be indexed by a regularly "
8222 : "spaced variable");
8223 0 : return nullptr;
8224 : }
8225 1 : gotYSpacing = true;
8226 : }
8227 : }
8228 :
8229 : // This limitation could probably be removed
8230 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8231 : {
8232 0 : CPLError(CE_Failure, CPLE_NotSupported,
8233 : "Either none of new X or Y dimension should have an indexing "
8234 : "variable, or both should both should have one.");
8235 0 : return nullptr;
8236 : }
8237 :
8238 48 : std::string osDstWKT;
8239 24 : if (poTargetSRS)
8240 : {
8241 2 : char *pszDstWKT = nullptr;
8242 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8243 : {
8244 0 : CPLFree(pszDstWKT);
8245 0 : return nullptr;
8246 : }
8247 2 : osDstWKT = pszDstWKT;
8248 2 : CPLFree(pszDstWKT);
8249 : }
8250 :
8251 : // Use coordinate variables for geolocation array
8252 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8253 24 : bool useGeolocationArray = false;
8254 24 : if (apoCoordinateVars.size() >= 2)
8255 : {
8256 0 : std::shared_ptr<GDALMDArray> poLongVar;
8257 0 : std::shared_ptr<GDALMDArray> poLatVar;
8258 15 : for (const auto &poCoordVar : apoCoordinateVars)
8259 : {
8260 10 : const auto &osName = poCoordVar->GetName();
8261 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8262 20 : std::string osStandardName;
8263 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8264 2 : poAttr->GetDimensionCount() == 0)
8265 : {
8266 2 : const char *pszStandardName = poAttr->ReadAsString();
8267 2 : if (pszStandardName)
8268 2 : osStandardName = pszStandardName;
8269 : }
8270 21 : if (osName == "lon" || osName == "longitude" ||
8271 21 : osName == "Longitude" || osStandardName == "longitude")
8272 : {
8273 5 : poLongVar = poCoordVar;
8274 : }
8275 6 : else if (osName == "lat" || osName == "latitude" ||
8276 6 : osName == "Latitude" || osStandardName == "latitude")
8277 : {
8278 5 : poLatVar = poCoordVar;
8279 : }
8280 : }
8281 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8282 : {
8283 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8284 5 : const auto &longDims = poLongVar->GetDimensions();
8285 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8286 5 : const auto &latDims = poLatVar->GetDimensions();
8287 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8288 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8289 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8290 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8291 : {
8292 : // Geolocation arrays are 1D, and of consistent size with
8293 : // the variable
8294 0 : useGeolocationArray = true;
8295 : }
8296 1 : else if ((longDimCount == 2 ||
8297 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8298 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8299 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8300 1 : (latDimCount == 2 ||
8301 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8302 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8303 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8304 :
8305 : {
8306 : // Geolocation arrays are 2D (or 3D with first dimension of
8307 : // size 1, as found in Sentinel 5P products), and of consistent
8308 : // size with the variable
8309 5 : useGeolocationArray = true;
8310 : }
8311 : else
8312 : {
8313 0 : CPLDebug(
8314 : "GDAL",
8315 : "Longitude and latitude coordinate variables found, "
8316 : "but their characteristics are not compatible of using "
8317 : "them as geolocation arrays");
8318 : }
8319 5 : if (useGeolocationArray)
8320 : {
8321 10 : CPLDebug("GDAL",
8322 : "Setting geolocation array from variables %s and %s",
8323 5 : poLongVar->GetName().c_str(),
8324 5 : poLatVar->GetName().c_str());
8325 : const std::string osFilenameLong =
8326 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8327 : const std::string osFilenameLat =
8328 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8329 : std::unique_ptr<GDALDataset> poTmpLongDS(
8330 : longDimCount == 1
8331 0 : ? poLongVar->AsClassicDataset(0, 0)
8332 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8333 15 : longDimCount - 2));
8334 5 : auto hTIFFLongDS = GDALTranslate(
8335 : osFilenameLong.c_str(),
8336 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8337 : std::unique_ptr<GDALDataset> poTmpLatDS(
8338 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8339 20 : : poLatVar->AsClassicDataset(
8340 15 : latDimCount - 1, latDimCount - 2));
8341 5 : auto hTIFFLatDS = GDALTranslate(
8342 : osFilenameLat.c_str(),
8343 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8344 5 : const bool bError =
8345 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8346 5 : GDALClose(hTIFFLongDS);
8347 5 : GDALClose(hTIFFLatDS);
8348 5 : if (bError)
8349 : {
8350 0 : VSIUnlink(osFilenameLong.c_str());
8351 0 : VSIUnlink(osFilenameLat.c_str());
8352 0 : return nullptr;
8353 : }
8354 :
8355 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8356 : }
8357 : }
8358 : else
8359 : {
8360 0 : CPLDebug("GDAL",
8361 : "Coordinate variables available for %s, but "
8362 : "longitude and/or latitude variables were not identified",
8363 0 : poParent->GetName().c_str());
8364 : }
8365 : }
8366 :
8367 : // Build gdalwarp arguments
8368 48 : CPLStringList aosArgv;
8369 :
8370 24 : aosArgv.AddString("-of");
8371 24 : aosArgv.AddString("VRT");
8372 :
8373 24 : aosArgv.AddString("-r");
8374 24 : aosArgv.AddString(pszResampleAlg);
8375 :
8376 24 : if (!osDstWKT.empty())
8377 : {
8378 2 : aosArgv.AddString("-t_srs");
8379 2 : aosArgv.AddString(osDstWKT.c_str());
8380 : }
8381 :
8382 24 : if (useGeolocationArray)
8383 5 : aosArgv.AddString("-geoloc");
8384 :
8385 24 : if (gotXSpacing && gotYSpacing)
8386 : {
8387 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8388 : const double dfXMax =
8389 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8390 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8391 : const double dfYMin =
8392 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8393 1 : aosArgv.AddString("-te");
8394 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8395 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8396 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8397 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8398 : }
8399 :
8400 24 : if (poNewDimX && poNewDimY)
8401 : {
8402 3 : aosArgv.AddString("-ts");
8403 : aosArgv.AddString(
8404 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8405 : aosArgv.AddString(
8406 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8407 : }
8408 21 : else if (poNewDimX)
8409 : {
8410 1 : aosArgv.AddString("-ts");
8411 : aosArgv.AddString(
8412 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8413 1 : aosArgv.AddString("0");
8414 : }
8415 20 : else if (poNewDimY)
8416 : {
8417 1 : aosArgv.AddString("-ts");
8418 1 : aosArgv.AddString("0");
8419 : aosArgv.AddString(
8420 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8421 : }
8422 :
8423 : // Create a warped VRT dataset
8424 : GDALWarpAppOptions *psOptions =
8425 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8426 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8427 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8428 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8429 24 : GDALWarpAppOptionsFree(psOptions);
8430 24 : if (poReprojectedDS == nullptr)
8431 3 : return nullptr;
8432 :
8433 : int nBlockXSize;
8434 : int nBlockYSize;
8435 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8436 21 : anBlockSize.emplace_back(nBlockYSize);
8437 21 : anBlockSize.emplace_back(nBlockXSize);
8438 :
8439 21 : GDALGeoTransform gt;
8440 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8441 21 : CPLAssert(eErr == CE_None);
8442 21 : CPL_IGNORE_RET_VAL(eErr);
8443 :
8444 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8445 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8446 42 : poReprojectedDS->GetRasterYSize());
8447 : auto varY = GDALMDArrayRegularlySpaced::Create(
8448 63 : std::string(), poDimY->GetName(), poDimY, gt.yorig + gt.yscale / 2,
8449 84 : gt.yscale, 0);
8450 21 : poDimY->SetIndexingVariable(varY);
8451 :
8452 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8453 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8454 42 : poReprojectedDS->GetRasterXSize());
8455 : auto varX = GDALMDArrayRegularlySpaced::Create(
8456 63 : std::string(), poDimX->GetName(), poDimX, gt.xorig + gt.xscale / 2,
8457 84 : gt.xscale, 0);
8458 21 : poDimX->SetIndexingVariable(varX);
8459 :
8460 21 : apoNewDims.emplace_back(poDimY);
8461 21 : apoNewDims.emplace_back(poDimX);
8462 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8463 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8464 21 : newAr->SetSelf(newAr);
8465 21 : if (poTargetSRS)
8466 : {
8467 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8468 : }
8469 : else
8470 : {
8471 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8472 : }
8473 21 : newAr->m_poVarX = varX;
8474 21 : newAr->m_poVarY = varY;
8475 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8476 21 : newAr->m_poParentDS = std::move(poParentDS);
8477 :
8478 : // If the input array is y,x,band ordered, the above newAr is
8479 : // actually band,y,x ordered as it is more convenient for
8480 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8481 : // array to the order of the input array
8482 21 : if (bYXBandOrder)
8483 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8484 :
8485 19 : return newAr;
8486 : }
8487 :
8488 : /************************************************************************/
8489 : /* GDALMDArrayResampled::IRead() */
8490 : /************************************************************************/
8491 :
8492 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8493 : const size_t *count, const GInt64 *arrayStep,
8494 : const GPtrDiff_t *bufferStride,
8495 : const GDALExtendedDataType &bufferDataType,
8496 : void *pDstBuffer) const
8497 : {
8498 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8499 0 : return false;
8500 :
8501 : struct Stack
8502 : {
8503 : size_t nIters = 0;
8504 : GByte *dst_ptr = nullptr;
8505 : GPtrDiff_t dst_inc_offset = 0;
8506 : };
8507 :
8508 29 : const auto nDims = GetDimensionCount();
8509 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8510 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8511 92 : for (size_t i = 0; i < nDims; i++)
8512 : {
8513 63 : stack[i].dst_inc_offset =
8514 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8515 : }
8516 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8517 :
8518 29 : size_t dimIdx = 0;
8519 29 : const size_t iDimY = nDims - 2;
8520 29 : const size_t iDimX = nDims - 1;
8521 : // Use an array to avoid a false positive warning from CLang Static
8522 : // Analyzer about flushCaches being never read
8523 29 : bool flushCaches[] = {false};
8524 : const bool bYXBandOrder =
8525 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8526 :
8527 38 : lbl_next_depth:
8528 38 : if (dimIdx == iDimY)
8529 : {
8530 33 : if (flushCaches[0])
8531 : {
8532 5 : flushCaches[0] = false;
8533 : // When changing of 2D slice, flush GDAL 2D buffers
8534 5 : m_poParentDS->FlushCache(false);
8535 5 : m_poReprojectedDS->FlushCache(false);
8536 : }
8537 :
8538 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8539 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8540 : arrayStep, bufferStride, bufferDataType,
8541 33 : stack[dimIdx].dst_ptr))
8542 : {
8543 0 : return false;
8544 : }
8545 : }
8546 : else
8547 : {
8548 5 : stack[dimIdx].nIters = count[dimIdx];
8549 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8550 5 : arrayStartIdx[dimIdx])
8551 : {
8552 1 : flushCaches[0] = true;
8553 : }
8554 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8555 5 : arrayStartIdx[dimIdx];
8556 : while (true)
8557 : {
8558 9 : dimIdx++;
8559 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8560 9 : goto lbl_next_depth;
8561 9 : lbl_return_to_caller:
8562 9 : dimIdx--;
8563 9 : if ((--stack[dimIdx].nIters) == 0)
8564 5 : break;
8565 4 : flushCaches[0] = true;
8566 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8567 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8568 : }
8569 : }
8570 38 : if (dimIdx > 0)
8571 9 : goto lbl_return_to_caller;
8572 :
8573 29 : return true;
8574 : }
8575 :
8576 : /************************************************************************/
8577 : /* GetResampled() */
8578 : /************************************************************************/
8579 :
8580 : /** Return an array that is a resampled / reprojected view of the current array
8581 : *
8582 : * This is the same as the C function GDALMDArrayGetResampled().
8583 : *
8584 : * Currently this method can only resample along the last 2 dimensions, unless
8585 : * orthorectifying a NASA EMIT dataset.
8586 : *
8587 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8588 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8589 : *
8590 : * Options available are:
8591 : * <ul>
8592 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8593 : * Can be set to NO to use generic reprojection method.
8594 : * </li>
8595 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8596 : * orthorectification to take into account the value of the
8597 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8598 : * current array along the band dimension are valid.</li>
8599 : * </ul>
8600 : *
8601 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8602 : * apoNewDims[i] can be NULL to let the method automatically
8603 : * determine it.
8604 : * @param resampleAlg Resampling algorithm
8605 : * @param poTargetSRS Target SRS, or nullptr
8606 : * @param papszOptions NULL-terminated list of options, or NULL.
8607 : *
8608 : * @return a new array, that holds a reference to the original one, and thus is
8609 : * a view of it (not a copy), or nullptr in case of error.
8610 : *
8611 : * @since 3.4
8612 : */
8613 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8614 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8615 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8616 : CSLConstList papszOptions) const
8617 : {
8618 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8619 38 : if (!self)
8620 : {
8621 0 : CPLError(CE_Failure, CPLE_AppDefined,
8622 : "Driver implementation issue: m_pSelf not set !");
8623 0 : return nullptr;
8624 : }
8625 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8626 : {
8627 0 : CPLError(CE_Failure, CPLE_AppDefined,
8628 : "GetResampled() only supports numeric data type");
8629 0 : return nullptr;
8630 : }
8631 :
8632 : // Special case for NASA EMIT datasets
8633 76 : auto apoDims = GetDimensions();
8634 36 : if (poTargetSRS == nullptr &&
8635 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8636 20 : apoDims[1]->GetName() == "crosstrack" &&
8637 10 : apoDims[2]->GetName() == "bands" &&
8638 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8639 1 : apoNewDims ==
8640 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8641 30 : apoDims[2]})) ||
8642 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8643 3 : apoDims[1]->GetName() == "crosstrack" &&
8644 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8645 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8646 : "EMIT_ORTHORECTIFICATION", "YES")))
8647 : {
8648 9 : auto poRootGroup = GetRootGroup();
8649 9 : if (poRootGroup)
8650 : {
8651 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8652 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8653 9 : if (poAttrGeotransform &&
8654 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8655 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8656 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8657 9 : poLocationGroup)
8658 : {
8659 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8660 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8661 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8662 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8663 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8664 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8665 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8666 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8667 : {
8668 : return CreateGLTOrthorectified(
8669 : self, poRootGroup, poGLT_X, poGLT_Y,
8670 : /* nGLTIndexOffset = */ -1,
8671 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8672 : }
8673 : }
8674 : }
8675 : }
8676 :
8677 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8678 : "EMIT_ORTHORECTIFICATION", "NO")))
8679 : {
8680 0 : CPLError(CE_Failure, CPLE_AppDefined,
8681 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8682 : "parameters are not compatible with it");
8683 0 : return nullptr;
8684 : }
8685 :
8686 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8687 29 : poTargetSRS, papszOptions);
8688 : }
8689 :
8690 : /************************************************************************/
8691 : /* GDALDatasetFromArray() */
8692 : /************************************************************************/
8693 :
8694 : class GDALDatasetFromArray;
8695 :
8696 : namespace
8697 : {
8698 : struct MetadataItem
8699 : {
8700 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8701 : std::string osName{};
8702 : std::string osDefinition{};
8703 : bool bDefinitionUsesPctForG = false;
8704 : };
8705 :
8706 : struct BandImageryMetadata
8707 : {
8708 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8709 : double dfCentralWavelengthToMicrometer = 1.0;
8710 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8711 : double dfFWHMToMicrometer = 1.0;
8712 : };
8713 :
8714 : } // namespace
8715 :
8716 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8717 : {
8718 : std::vector<GUInt64> m_anOffset{};
8719 : std::vector<size_t> m_anCount{};
8720 : std::vector<GPtrDiff_t> m_anStride{};
8721 :
8722 : protected:
8723 : CPLErr IReadBlock(int, int, void *) override;
8724 : CPLErr IWriteBlock(int, int, void *) override;
8725 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8726 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8727 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8728 : GSpacing nLineSpaceBuf,
8729 : GDALRasterIOExtraArg *psExtraArg) override;
8730 :
8731 : public:
8732 : explicit GDALRasterBandFromArray(
8733 : GDALDatasetFromArray *poDSIn,
8734 : const std::vector<GUInt64> &anOtherDimCoord,
8735 : const std::vector<std::vector<MetadataItem>>
8736 : &aoBandParameterMetadataItems,
8737 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8738 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8739 :
8740 : double GetNoDataValue(int *pbHasNoData) override;
8741 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8742 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8743 : double GetOffset(int *pbHasOffset) override;
8744 : double GetScale(int *pbHasScale) override;
8745 : const char *GetUnitType() override;
8746 : GDALColorInterp GetColorInterpretation() override;
8747 : int GetOverviewCount() override;
8748 : GDALRasterBand *GetOverview(int idx) override;
8749 : CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
8750 : int nBufXSize, int nBufYSize, GDALDataType eBufType,
8751 : CSLConstList papszOptions) override;
8752 : };
8753 :
8754 : class GDALDatasetFromArray final : public GDALPamDataset
8755 : {
8756 : friend class GDALRasterBandFromArray;
8757 :
8758 : std::shared_ptr<GDALMDArray> m_poArray;
8759 : const size_t m_iXDim;
8760 : const size_t m_iYDim;
8761 : const CPLStringList m_aosOptions;
8762 : GDALGeoTransform m_gt{};
8763 : bool m_bHasGT = false;
8764 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8765 : GDALMultiDomainMetadata m_oMDD{};
8766 : std::string m_osOvrFilename{};
8767 : bool m_bOverviewsDiscovered = false;
8768 : std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
8769 : m_apoOverviews{};
8770 :
8771 : public:
8772 474 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8773 : size_t iXDim, size_t iYDim,
8774 : const CPLStringList &aosOptions)
8775 474 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
8776 474 : m_aosOptions(aosOptions)
8777 : {
8778 : // Initialize an overview filename from the filename of the array
8779 : // and its name.
8780 474 : const std::string &osFilename = m_poArray->GetFilename();
8781 474 : if (!osFilename.empty())
8782 : {
8783 432 : m_osOvrFilename = osFilename;
8784 432 : m_osOvrFilename += '.';
8785 25213 : for (char ch : m_poArray->GetName())
8786 : {
8787 24781 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8788 21657 : (ch >= 'a' && ch <= 'z') || ch == '_')
8789 : {
8790 19594 : m_osOvrFilename += ch;
8791 : }
8792 : else
8793 : {
8794 5187 : m_osOvrFilename += '_';
8795 : }
8796 : }
8797 432 : m_osOvrFilename += ".ovr";
8798 432 : oOvManager.Initialize(this);
8799 : }
8800 474 : }
8801 :
8802 : static std::unique_ptr<GDALDatasetFromArray>
8803 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8804 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8805 : CSLConstList papszOptions);
8806 :
8807 : ~GDALDatasetFromArray() override;
8808 :
8809 669 : CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override
8810 : {
8811 669 : CPLErr eErr = CE_None;
8812 669 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8813 : {
8814 669 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8815 : CE_None)
8816 0 : eErr = CE_Failure;
8817 669 : m_poArray.reset();
8818 : }
8819 669 : return eErr;
8820 : }
8821 :
8822 83 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8823 : {
8824 83 : gt = m_gt;
8825 83 : return m_bHasGT ? CE_None : CE_Failure;
8826 : }
8827 :
8828 93 : const OGRSpatialReference *GetSpatialRef() const override
8829 : {
8830 93 : if (m_poArray->GetDimensionCount() < 2)
8831 3 : return nullptr;
8832 90 : m_poSRS = m_poArray->GetSpatialRef();
8833 90 : if (m_poSRS)
8834 : {
8835 46 : m_poSRS.reset(m_poSRS->Clone());
8836 92 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8837 138 : for (auto &m : axisMapping)
8838 : {
8839 92 : if (m == static_cast<int>(m_iXDim) + 1)
8840 46 : m = 1;
8841 46 : else if (m == static_cast<int>(m_iYDim) + 1)
8842 46 : m = 2;
8843 : }
8844 46 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8845 : }
8846 90 : return m_poSRS.get();
8847 : }
8848 :
8849 9 : CPLErr SetMetadata(CSLConstList papszMetadata,
8850 : const char *pszDomain) override
8851 : {
8852 9 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8853 : }
8854 :
8855 184 : CSLConstList GetMetadata(const char *pszDomain) override
8856 : {
8857 184 : return m_oMDD.GetMetadata(pszDomain);
8858 : }
8859 :
8860 258 : const char *GetMetadataItem(const char *pszName,
8861 : const char *pszDomain) override
8862 : {
8863 469 : if (!m_osOvrFilename.empty() && pszName &&
8864 492 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8865 23 : EQUAL(pszDomain, "OVERVIEWS"))
8866 : {
8867 23 : return m_osOvrFilename.c_str();
8868 : }
8869 235 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8870 : }
8871 :
8872 2 : CPLErr IBuildOverviews(const char *pszResampling, int nOverviews,
8873 : const int *panOverviewList, int nListBands,
8874 : const int *panBandList, GDALProgressFunc pfnProgress,
8875 : void *pProgressData,
8876 : CSLConstList papszOptions) override
8877 : {
8878 : // Try the multidimensional array path. Use quiet handler to
8879 : // suppress the "not supported" error from the base class stub.
8880 2 : bool bNotSupported = false;
8881 4 : std::string osErrMsg;
8882 2 : CPLErr eSavedClass = CE_None;
8883 2 : int nSavedNo = CPLE_None;
8884 : {
8885 2 : CPLErrorHandlerPusher oQuiet(CPLQuietErrorHandler);
8886 2 : CPLErr eErr = m_poArray->BuildOverviews(
8887 : pszResampling, nOverviews, panOverviewList, pfnProgress,
8888 2 : pProgressData, papszOptions);
8889 2 : if (eErr == CE_None)
8890 : {
8891 1 : m_bOverviewsDiscovered = false;
8892 1 : m_apoOverviews.clear();
8893 1 : return CE_None;
8894 : }
8895 1 : nSavedNo = CPLGetLastErrorNo();
8896 1 : eSavedClass = CPLGetLastErrorType();
8897 1 : osErrMsg = CPLGetLastErrorMsg();
8898 1 : bNotSupported = (nSavedNo == CPLE_NotSupported);
8899 : }
8900 1 : if (!bNotSupported)
8901 : {
8902 : // Re-emit the error that was suppressed by the quiet handler.
8903 0 : CPLError(eSavedClass, nSavedNo, "%s", osErrMsg.c_str());
8904 0 : return CE_Failure;
8905 : }
8906 : // Driver doesn't implement BuildOverviews - fall back to
8907 : // default path (e.g. external .ovr file).
8908 1 : CPLErrorReset();
8909 1 : return GDALDataset::IBuildOverviews(
8910 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
8911 1 : pfnProgress, pProgressData, papszOptions);
8912 : }
8913 :
8914 68 : void DiscoverOverviews()
8915 : {
8916 68 : if (!m_bOverviewsDiscovered)
8917 : {
8918 23 : m_bOverviewsDiscovered = true;
8919 23 : if (const int nOverviews = m_poArray->GetOverviewCount())
8920 : {
8921 14 : if (auto poRootGroup = m_poArray->GetRootGroup())
8922 : {
8923 7 : const size_t nDims = m_poArray->GetDimensionCount();
8924 14 : CPLStringList aosOptions(m_aosOptions);
8925 7 : aosOptions.SetNameValue("LOAD_PAM", "NO");
8926 18 : for (int iOvr = 0; iOvr < nOverviews; ++iOvr)
8927 : {
8928 22 : if (auto poOvrArray = m_poArray->GetOverview(iOvr))
8929 : {
8930 22 : if (poOvrArray->GetDimensionCount() == nDims &&
8931 11 : poOvrArray->GetDataType() ==
8932 11 : m_poArray->GetDataType())
8933 : {
8934 : auto poOvrDS =
8935 11 : Create(poOvrArray, m_iXDim, m_iYDim,
8936 22 : poRootGroup, aosOptions);
8937 11 : if (poOvrDS)
8938 : {
8939 11 : m_apoOverviews.push_back(
8940 22 : std::unique_ptr<
8941 : GDALDataset,
8942 : GDALDatasetUniquePtrReleaser>(
8943 11 : poOvrDS.release()));
8944 : }
8945 : }
8946 : }
8947 : }
8948 : }
8949 : }
8950 : }
8951 68 : }
8952 : };
8953 :
8954 948 : GDALDatasetFromArray::~GDALDatasetFromArray()
8955 : {
8956 474 : GDALDatasetFromArray::Close();
8957 948 : }
8958 :
8959 : /************************************************************************/
8960 : /* GDALRasterBandFromArray() */
8961 : /************************************************************************/
8962 :
8963 563 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8964 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8965 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8966 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8967 563 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8968 : {
8969 563 : const auto &poArray(poDSIn->m_poArray);
8970 563 : const auto &dims(poArray->GetDimensions());
8971 563 : const auto nDimCount(dims.size());
8972 1126 : const auto blockSize(poArray->GetBlockSize());
8973 :
8974 544 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8975 1107 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8976 387 : blockSize[poDSIn->m_iYDim]))
8977 : : 1;
8978 563 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8979 404 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8980 404 : blockSize[poDSIn->m_iXDim]))
8981 563 : : poDSIn->GetRasterXSize();
8982 :
8983 563 : eDataType = poArray->GetDataType().GetNumericDataType();
8984 563 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
8985 :
8986 563 : if (nDTSize > 0)
8987 : {
8988 : // If the above computed block size exceeds INT_MAX or 1/100th of the
8989 : // maximum allowed size for the block cache, divide its shape by two,
8990 : // along the largest dimension. Only do that while there are at least
8991 : // one dimension with 2 pixels.
8992 12 : while (
8993 1105 : (nBlockXSize >= 2 || nBlockYSize >= 2) &&
8994 530 : (nBlockXSize > INT_MAX / nBlockYSize / nDTSize ||
8995 527 : (nBlockXSize > GDALGetCacheMax64() / 100 / nBlockYSize / nDTSize)))
8996 : {
8997 12 : if (nBlockXSize > nBlockYSize)
8998 12 : nBlockXSize /= 2;
8999 : else
9000 0 : nBlockYSize /= 2;
9001 : }
9002 : }
9003 :
9004 563 : eAccess = poDSIn->eAccess;
9005 563 : m_anOffset.resize(nDimCount);
9006 563 : m_anCount.resize(nDimCount, 1);
9007 563 : m_anStride.resize(nDimCount);
9008 1821 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9009 : {
9010 1258 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
9011 : {
9012 302 : std::string dimName(dims[i]->GetName());
9013 151 : GUInt64 nIndex = anOtherDimCoord[j];
9014 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
9015 : // subsetted dimensions as generated by GetView()
9016 151 : if (STARTS_WITH(dimName.c_str(), "subset_"))
9017 : {
9018 : CPLStringList aosTokens(
9019 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
9020 6 : if (aosTokens.size() == 5)
9021 : {
9022 6 : dimName = aosTokens[1];
9023 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
9024 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
9025 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
9026 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
9027 0 : : nStartDim - (nIndex * -nIncrDim);
9028 : }
9029 : }
9030 151 : if (nDimCount != 3 || dimName != "Band")
9031 : {
9032 90 : SetMetadataItem(
9033 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
9034 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
9035 : }
9036 :
9037 151 : auto indexingVar = dims[i]->GetIndexingVariable();
9038 :
9039 : // If the indexing variable is also listed in band parameter arrays,
9040 : // then don't use our default formatting
9041 151 : if (indexingVar)
9042 : {
9043 49 : for (const auto &oItem : aoBandParameterMetadataItems[j])
9044 : {
9045 14 : if (oItem.poArray->GetFullName() ==
9046 14 : indexingVar->GetFullName())
9047 : {
9048 12 : indexingVar.reset();
9049 12 : break;
9050 : }
9051 : }
9052 : }
9053 :
9054 186 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
9055 35 : indexingVar->GetDimensions()[0]->GetSize() ==
9056 35 : dims[i]->GetSize())
9057 : {
9058 35 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
9059 : {
9060 0 : if (!bHasWarned)
9061 : {
9062 0 : CPLError(
9063 : CE_Warning, CPLE_AppDefined,
9064 : "Maximum delay to load band metadata from "
9065 : "dimension indexing variables has expired. "
9066 : "Increase the value of the "
9067 : "LOAD_EXTRA_DIM_METADATA_DELAY "
9068 : "option of GDALMDArray::AsClassicDataset() "
9069 : "(also accessible as the "
9070 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
9071 : "configuration option), "
9072 : "or set it to 'unlimited' for unlimited delay. ");
9073 0 : bHasWarned = true;
9074 : }
9075 : }
9076 : else
9077 : {
9078 35 : size_t nCount = 1;
9079 35 : const auto &dt(indexingVar->GetDataType());
9080 70 : std::vector<GByte> abyTmp(dt.GetSize());
9081 70 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
9082 35 : nullptr, nullptr, dt, &abyTmp[0]))
9083 : {
9084 35 : char *pszTmp = nullptr;
9085 35 : GDALExtendedDataType::CopyValue(
9086 35 : &abyTmp[0], dt, &pszTmp,
9087 70 : GDALExtendedDataType::CreateString());
9088 35 : dt.FreeDynamicMemory(abyTmp.data());
9089 35 : if (pszTmp)
9090 : {
9091 35 : SetMetadataItem(
9092 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
9093 : pszTmp);
9094 35 : CPLFree(pszTmp);
9095 : }
9096 :
9097 35 : const auto &unit(indexingVar->GetUnit());
9098 35 : if (!unit.empty())
9099 : {
9100 12 : SetMetadataItem(
9101 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
9102 : unit.c_str());
9103 : }
9104 : }
9105 : }
9106 : }
9107 :
9108 169 : for (const auto &oItem : aoBandParameterMetadataItems[j])
9109 : {
9110 36 : CPLString osVal;
9111 :
9112 18 : size_t nCount = 1;
9113 18 : const auto &dt(oItem.poArray->GetDataType());
9114 18 : if (oItem.bDefinitionUsesPctForG)
9115 : {
9116 : // There is one and only one %[x][.y]f|g in osDefinition
9117 16 : std::vector<GByte> abyTmp(dt.GetSize());
9118 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9119 8 : nullptr, nullptr, dt, &abyTmp[0]))
9120 : {
9121 8 : double dfVal = 0;
9122 8 : GDALExtendedDataType::CopyValue(
9123 8 : &abyTmp[0], dt, &dfVal,
9124 16 : GDALExtendedDataType::Create(GDT_Float64));
9125 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
9126 8 : dt.FreeDynamicMemory(abyTmp.data());
9127 : }
9128 : }
9129 : else
9130 : {
9131 : // There should be zero or one %s in osDefinition
9132 10 : char *pszValue = nullptr;
9133 10 : if (dt.GetClass() == GEDTC_STRING)
9134 : {
9135 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
9136 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
9137 2 : dt, &pszValue));
9138 : }
9139 : else
9140 : {
9141 16 : std::vector<GByte> abyTmp(dt.GetSize());
9142 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9143 : nullptr, nullptr, dt,
9144 8 : &abyTmp[0]))
9145 : {
9146 8 : GDALExtendedDataType::CopyValue(
9147 8 : &abyTmp[0], dt, &pszValue,
9148 16 : GDALExtendedDataType::CreateString());
9149 : }
9150 : }
9151 :
9152 10 : if (pszValue)
9153 : {
9154 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9155 10 : CPLFree(pszValue);
9156 : }
9157 : }
9158 18 : if (!osVal.empty())
9159 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9160 : }
9161 :
9162 151 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9163 : {
9164 : auto &poCentralWavelengthArray =
9165 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9166 4 : size_t nCount = 1;
9167 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9168 8 : std::vector<GByte> abyTmp(dt.GetSize());
9169 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9170 : &nCount, nullptr, nullptr,
9171 4 : dt, &abyTmp[0]))
9172 : {
9173 4 : double dfVal = 0;
9174 4 : GDALExtendedDataType::CopyValue(
9175 4 : &abyTmp[0], dt, &dfVal,
9176 8 : GDALExtendedDataType::Create(GDT_Float64));
9177 4 : dt.FreeDynamicMemory(abyTmp.data());
9178 4 : SetMetadataItem(
9179 : "CENTRAL_WAVELENGTH_UM",
9180 : CPLSPrintf(
9181 4 : "%g", dfVal * aoBandImageryMetadata[j]
9182 4 : .dfCentralWavelengthToMicrometer),
9183 : "IMAGERY");
9184 : }
9185 : }
9186 :
9187 151 : if (aoBandImageryMetadata[j].poFWHMArray)
9188 : {
9189 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9190 2 : size_t nCount = 1;
9191 2 : const auto &dt(poFWHMArray->GetDataType());
9192 4 : std::vector<GByte> abyTmp(dt.GetSize());
9193 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9194 2 : nullptr, dt, &abyTmp[0]))
9195 : {
9196 2 : double dfVal = 0;
9197 2 : GDALExtendedDataType::CopyValue(
9198 2 : &abyTmp[0], dt, &dfVal,
9199 4 : GDALExtendedDataType::Create(GDT_Float64));
9200 2 : dt.FreeDynamicMemory(abyTmp.data());
9201 2 : SetMetadataItem(
9202 : "FWHM_UM",
9203 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9204 2 : .dfFWHMToMicrometer),
9205 : "IMAGERY");
9206 : }
9207 : }
9208 :
9209 151 : m_anOffset[i] = anOtherDimCoord[j];
9210 151 : j++;
9211 : }
9212 : }
9213 563 : }
9214 :
9215 : /************************************************************************/
9216 : /* GetNoDataValue() */
9217 : /************************************************************************/
9218 :
9219 158 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9220 : {
9221 158 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9222 158 : const auto &poArray(l_poDS->m_poArray);
9223 158 : bool bHasNodata = false;
9224 158 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9225 158 : if (pbHasNoData)
9226 142 : *pbHasNoData = bHasNodata;
9227 158 : return res;
9228 : }
9229 :
9230 : /************************************************************************/
9231 : /* GetNoDataValueAsInt64() */
9232 : /************************************************************************/
9233 :
9234 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9235 : {
9236 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9237 1 : const auto &poArray(l_poDS->m_poArray);
9238 1 : bool bHasNodata = false;
9239 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9240 1 : if (pbHasNoData)
9241 1 : *pbHasNoData = bHasNodata;
9242 1 : return nodata;
9243 : }
9244 :
9245 : /************************************************************************/
9246 : /* GetNoDataValueAsUInt64() */
9247 : /************************************************************************/
9248 :
9249 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9250 : {
9251 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9252 1 : const auto &poArray(l_poDS->m_poArray);
9253 1 : bool bHasNodata = false;
9254 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9255 1 : if (pbHasNoData)
9256 1 : *pbHasNoData = bHasNodata;
9257 1 : return nodata;
9258 : }
9259 :
9260 : /************************************************************************/
9261 : /* GetOffset() */
9262 : /************************************************************************/
9263 :
9264 42 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9265 : {
9266 42 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9267 42 : const auto &poArray(l_poDS->m_poArray);
9268 42 : bool bHasValue = false;
9269 42 : double dfRes = poArray->GetOffset(&bHasValue);
9270 42 : if (pbHasOffset)
9271 23 : *pbHasOffset = bHasValue;
9272 42 : return dfRes;
9273 : }
9274 :
9275 : /************************************************************************/
9276 : /* GetUnitType() */
9277 : /************************************************************************/
9278 :
9279 52 : const char *GDALRasterBandFromArray::GetUnitType()
9280 : {
9281 52 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9282 52 : const auto &poArray(l_poDS->m_poArray);
9283 52 : return poArray->GetUnit().c_str();
9284 : }
9285 :
9286 : /************************************************************************/
9287 : /* GetScale() */
9288 : /************************************************************************/
9289 :
9290 40 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9291 : {
9292 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9293 40 : const auto &poArray(l_poDS->m_poArray);
9294 40 : bool bHasValue = false;
9295 40 : double dfRes = poArray->GetScale(&bHasValue);
9296 40 : if (pbHasScale)
9297 21 : *pbHasScale = bHasValue;
9298 40 : return dfRes;
9299 : }
9300 :
9301 : /************************************************************************/
9302 : /* IReadBlock() */
9303 : /************************************************************************/
9304 :
9305 102 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9306 : void *pImage)
9307 : {
9308 102 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9309 102 : const int nXOff = nBlockXOff * nBlockXSize;
9310 102 : const int nYOff = nBlockYOff * nBlockYSize;
9311 102 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9312 102 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9313 : GDALRasterIOExtraArg sExtraArg;
9314 102 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9315 204 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9316 : nReqXSize, nReqYSize, eDataType, nDTSize,
9317 204 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9318 : }
9319 :
9320 : /************************************************************************/
9321 : /* IWriteBlock() */
9322 : /************************************************************************/
9323 :
9324 2 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9325 : void *pImage)
9326 : {
9327 2 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9328 2 : const int nXOff = nBlockXOff * nBlockXSize;
9329 2 : const int nYOff = nBlockYOff * nBlockYSize;
9330 2 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9331 2 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9332 : GDALRasterIOExtraArg sExtraArg;
9333 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9334 4 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9335 : nReqXSize, nReqYSize, eDataType, nDTSize,
9336 4 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9337 : }
9338 :
9339 : /************************************************************************/
9340 : /* AdviseRead() */
9341 : /************************************************************************/
9342 :
9343 45 : CPLErr GDALRasterBandFromArray::AdviseRead(int nXOff, int nYOff, int nXSize,
9344 : int nYSize, int nBufXSize,
9345 : int nBufYSize,
9346 : GDALDataType /*eBufType*/,
9347 : CSLConstList papszOptions)
9348 : {
9349 45 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9350 45 : int bStopProcessing = FALSE;
9351 90 : const CPLErr eErr = l_poDS->ValidateRasterIOOrAdviseReadParameters(
9352 : "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
9353 45 : nBufXSize, nBufYSize, 1, &nBand);
9354 45 : if (eErr != CE_None || bStopProcessing)
9355 0 : return eErr;
9356 :
9357 45 : const auto &poArray(l_poDS->m_poArray);
9358 90 : std::vector<GUInt64> anArrayStartIdx = m_anOffset;
9359 45 : std::vector<size_t> anCount = m_anCount;
9360 45 : anArrayStartIdx[l_poDS->m_iXDim] = nXOff;
9361 45 : anCount[l_poDS->m_iXDim] = nXSize;
9362 45 : if (poArray->GetDimensionCount() >= 2)
9363 : {
9364 42 : anArrayStartIdx[l_poDS->m_iYDim] = nYOff;
9365 42 : anCount[l_poDS->m_iYDim] = nYSize;
9366 : }
9367 45 : return poArray->AdviseRead(anArrayStartIdx.data(), anCount.data(),
9368 : papszOptions)
9369 45 : ? CE_None
9370 45 : : CE_Failure;
9371 : }
9372 :
9373 : /************************************************************************/
9374 : /* IRasterIO() */
9375 : /************************************************************************/
9376 :
9377 547 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9378 : int nYOff, int nXSize, int nYSize,
9379 : void *pData, int nBufXSize,
9380 : int nBufYSize, GDALDataType eBufType,
9381 : GSpacing nPixelSpaceBuf,
9382 : GSpacing nLineSpaceBuf,
9383 : GDALRasterIOExtraArg *psExtraArg)
9384 : {
9385 547 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9386 547 : const auto &poArray(l_poDS->m_poArray);
9387 547 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9388 : // If reading/writing at full resolution and with proper stride, go
9389 : // directly to the array, but, for performance reasons,
9390 : // only if exactly on chunk boundaries, otherwise go through the block cache.
9391 547 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9392 547 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9393 547 : (nLineSpaceBuf % nBufferDTSize) == 0 && (nXOff % nBlockXSize) == 0 &&
9394 545 : (nYOff % nBlockYSize) == 0 &&
9395 545 : ((nXSize % nBlockXSize) == 0 || nXOff + nXSize == nRasterXSize) &&
9396 542 : ((nYSize % nBlockYSize) == 0 || nYOff + nYSize == nRasterYSize))
9397 : {
9398 542 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9399 542 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9400 1084 : m_anStride[l_poDS->m_iXDim] =
9401 542 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9402 542 : if (poArray->GetDimensionCount() >= 2)
9403 : {
9404 529 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9405 529 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9406 529 : m_anStride[l_poDS->m_iYDim] =
9407 529 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9408 : }
9409 542 : if (eRWFlag == GF_Read)
9410 : {
9411 1046 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9412 523 : m_anStride.data(),
9413 1046 : GDALExtendedDataType::Create(eBufType), pData)
9414 523 : ? CE_None
9415 523 : : CE_Failure;
9416 : }
9417 : else
9418 : {
9419 38 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9420 19 : m_anStride.data(),
9421 38 : GDALExtendedDataType::Create(eBufType), pData)
9422 19 : ? CE_None
9423 19 : : CE_Failure;
9424 : }
9425 : }
9426 : // For unaligned reads, give the array a chance to pre-populate its
9427 : // internal chunk cache (e.g. Zarr v3 sharded batches I/O via
9428 : // PreloadShardedBlocks). The block cache loop below then hits the
9429 : // already-decompressed chunks instead of issuing individual reads.
9430 : // Backends that don't override AdviseRead() return true (no-op).
9431 5 : if (eRWFlag == GF_Read)
9432 : {
9433 4 : AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eBufType,
9434 : nullptr);
9435 : }
9436 5 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9437 : pData, nBufXSize, nBufYSize, eBufType,
9438 5 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9439 : }
9440 :
9441 : /************************************************************************/
9442 : /* GetColorInterpretation() */
9443 : /************************************************************************/
9444 :
9445 80 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9446 : {
9447 80 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9448 80 : const auto &poArray(l_poDS->m_poArray);
9449 240 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9450 80 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9451 : {
9452 6 : bool bOK = false;
9453 6 : GUInt64 nStartIndex = 0;
9454 6 : if (poArray->GetDimensionCount() == 2 &&
9455 0 : poAttr->GetDimensionCount() == 0)
9456 : {
9457 0 : bOK = true;
9458 : }
9459 6 : else if (poArray->GetDimensionCount() == 3)
9460 : {
9461 6 : uint64_t nExtraDimSamples = 1;
9462 6 : const auto &apoDims = poArray->GetDimensions();
9463 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9464 : {
9465 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9466 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9467 : }
9468 6 : if (poAttr->GetDimensionsSize() ==
9469 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9470 : {
9471 6 : bOK = true;
9472 : }
9473 6 : nStartIndex = nBand - 1;
9474 : }
9475 6 : if (bOK)
9476 : {
9477 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9478 6 : const size_t nCount = 1;
9479 6 : const GInt64 arrayStep = 1;
9480 6 : const GPtrDiff_t bufferStride = 1;
9481 6 : char *pszValue = nullptr;
9482 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9483 6 : oStringDT, &pszValue);
9484 6 : if (pszValue)
9485 : {
9486 : const auto eColorInterp =
9487 6 : GDALGetColorInterpretationByName(pszValue);
9488 6 : CPLFree(pszValue);
9489 6 : return eColorInterp;
9490 : }
9491 : }
9492 : }
9493 74 : return GCI_Undefined;
9494 : }
9495 :
9496 : /************************************************************************/
9497 : /* GDALRasterBandFromArray::GetOverviewCount() */
9498 : /************************************************************************/
9499 :
9500 37 : int GDALRasterBandFromArray::GetOverviewCount()
9501 : {
9502 37 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9503 37 : if (nPAMCount)
9504 2 : return nPAMCount;
9505 35 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9506 35 : l_poDS->DiscoverOverviews();
9507 35 : return static_cast<int>(l_poDS->m_apoOverviews.size());
9508 : }
9509 :
9510 : /************************************************************************/
9511 : /* GDALRasterBandFromArray::GetOverview() */
9512 : /************************************************************************/
9513 :
9514 34 : GDALRasterBand *GDALRasterBandFromArray::GetOverview(int idx)
9515 : {
9516 34 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9517 34 : if (nPAMCount)
9518 1 : return GDALPamRasterBand::GetOverview(idx);
9519 33 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9520 33 : l_poDS->DiscoverOverviews();
9521 33 : if (idx < 0 || static_cast<size_t>(idx) >= l_poDS->m_apoOverviews.size())
9522 : {
9523 8 : return nullptr;
9524 : }
9525 25 : return l_poDS->m_apoOverviews[idx]->GetRasterBand(nBand);
9526 : }
9527 :
9528 : /************************************************************************/
9529 : /* GDALDatasetFromArray::Create() */
9530 : /************************************************************************/
9531 :
9532 527 : std::unique_ptr<GDALDatasetFromArray> GDALDatasetFromArray::Create(
9533 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9534 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9535 :
9536 : {
9537 527 : const auto nDimCount(array->GetDimensionCount());
9538 527 : if (nDimCount == 0)
9539 : {
9540 1 : CPLError(CE_Failure, CPLE_NotSupported,
9541 : "Unsupported number of dimensions");
9542 1 : return nullptr;
9543 : }
9544 1051 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9545 525 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9546 : {
9547 1 : CPLError(CE_Failure, CPLE_NotSupported,
9548 : "Only arrays with numeric data types "
9549 : "can be exposed as classic GDALDataset");
9550 1 : return nullptr;
9551 : }
9552 525 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9553 499 : (nDimCount >= 2 && iXDim == iYDim))
9554 : {
9555 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9556 8 : return nullptr;
9557 : }
9558 517 : GUInt64 nTotalBands = 1;
9559 517 : const auto &dims(array->GetDimensions());
9560 1630 : for (size_t i = 0; i < nDimCount; ++i)
9561 : {
9562 1114 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9563 : {
9564 101 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9565 : {
9566 1 : CPLError(CE_Failure, CPLE_AppDefined,
9567 : "Too many bands. Operate on a sliced view");
9568 1 : return nullptr;
9569 : }
9570 100 : nTotalBands *= dims[i]->GetSize();
9571 : }
9572 : }
9573 :
9574 1032 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9575 1032 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9576 1629 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9577 : {
9578 1113 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9579 : {
9580 100 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9581 100 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9582 100 : ++j;
9583 : }
9584 : }
9585 :
9586 516 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9587 :
9588 : const char *pszBandMetadata =
9589 516 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9590 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9591 1032 : nNewDimCount);
9592 516 : if (pszBandMetadata)
9593 : {
9594 32 : if (!poRootGroup)
9595 : {
9596 1 : CPLError(CE_Failure, CPLE_AppDefined,
9597 : "Root group should be provided when BAND_METADATA is set");
9598 24 : return nullptr;
9599 : }
9600 31 : CPLJSONDocument oDoc;
9601 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9602 : {
9603 1 : CPLError(CE_Failure, CPLE_AppDefined,
9604 : "Invalid JSON content for BAND_METADATA");
9605 1 : return nullptr;
9606 : }
9607 30 : auto oRoot = oDoc.GetRoot();
9608 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9609 : {
9610 1 : CPLError(CE_Failure, CPLE_AppDefined,
9611 : "Value of BAND_METADATA should be an array");
9612 1 : return nullptr;
9613 : }
9614 :
9615 29 : auto oArray = oRoot.ToArray();
9616 38 : for (int j = 0; j < oArray.Size(); ++j)
9617 : {
9618 30 : const auto oJsonItem = oArray[j];
9619 30 : MetadataItem oItem;
9620 30 : size_t iExtraDimIdx = 0;
9621 :
9622 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9623 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9624 0 : std::shared_ptr<GDALMDArray> poArray;
9625 0 : std::shared_ptr<GDALAttribute> poAttribute;
9626 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9627 : {
9628 1 : CPLError(CE_Failure, CPLE_AppDefined,
9629 : "BAND_METADATA[%d][\"array\"] or "
9630 : "BAND_METADATA[%d][\"attribute\"] is missing",
9631 : j, j);
9632 1 : return nullptr;
9633 : }
9634 48 : else if (!osBandArrayFullname.empty() &&
9635 19 : !osBandAttributeName.empty())
9636 : {
9637 1 : CPLError(
9638 : CE_Failure, CPLE_AppDefined,
9639 : "BAND_METADATA[%d][\"array\"] and "
9640 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9641 : j, j);
9642 1 : return nullptr;
9643 : }
9644 28 : else if (!osBandArrayFullname.empty())
9645 : {
9646 : poArray =
9647 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9648 18 : if (!poArray)
9649 : {
9650 1 : CPLError(CE_Failure, CPLE_AppDefined,
9651 : "Array %s cannot be found",
9652 : osBandArrayFullname.c_str());
9653 3 : return nullptr;
9654 : }
9655 17 : if (poArray->GetDimensionCount() != 1)
9656 : {
9657 1 : CPLError(CE_Failure, CPLE_AppDefined,
9658 : "Array %s is not a 1D array",
9659 : osBandArrayFullname.c_str());
9660 1 : return nullptr;
9661 : }
9662 : const auto &osAuxArrayDimName =
9663 16 : poArray->GetDimensions()[0]->GetName();
9664 : auto oIter =
9665 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9666 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9667 : {
9668 1 : CPLError(
9669 : CE_Failure, CPLE_AppDefined,
9670 : "Dimension %s of array %s is not a non-X/Y dimension "
9671 : "of array %s",
9672 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9673 1 : array->GetName().c_str());
9674 1 : return nullptr;
9675 : }
9676 15 : iExtraDimIdx = oIter->second;
9677 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9678 : }
9679 : else
9680 : {
9681 10 : CPLAssert(!osBandAttributeName.empty());
9682 10 : poAttribute = !osBandAttributeName.empty() &&
9683 10 : osBandAttributeName[0] == '/'
9684 24 : ? poRootGroup->OpenAttributeFromFullname(
9685 : osBandAttributeName)
9686 10 : : array->GetAttribute(osBandAttributeName);
9687 10 : if (!poAttribute)
9688 : {
9689 2 : CPLError(CE_Failure, CPLE_AppDefined,
9690 : "Attribute %s cannot be found",
9691 : osBandAttributeName.c_str());
9692 8 : return nullptr;
9693 : }
9694 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9695 8 : if (aoAttrDims.size() != 1)
9696 : {
9697 4 : CPLError(CE_Failure, CPLE_AppDefined,
9698 : "Attribute %s is not a 1D array",
9699 : osBandAttributeName.c_str());
9700 4 : return nullptr;
9701 : }
9702 4 : bool found = false;
9703 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9704 : {
9705 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9706 5 : ->GetSize() == aoAttrDims[0])
9707 : {
9708 4 : if (found)
9709 : {
9710 2 : CPLError(CE_Failure, CPLE_AppDefined,
9711 : "Several dimensions of %s have the same "
9712 : "size as attribute %s. Cannot infer which "
9713 : "one to bind to!",
9714 1 : array->GetName().c_str(),
9715 : osBandAttributeName.c_str());
9716 1 : return nullptr;
9717 : }
9718 3 : found = true;
9719 3 : iExtraDimIdx = iter.second;
9720 : }
9721 : }
9722 3 : if (!found)
9723 : {
9724 2 : CPLError(
9725 : CE_Failure, CPLE_AppDefined,
9726 : "No dimension of %s has the same size as attribute %s",
9727 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9728 1 : return nullptr;
9729 : }
9730 : }
9731 :
9732 17 : oItem.osName = oJsonItem.GetString("item_name");
9733 17 : if (oItem.osName.empty())
9734 : {
9735 1 : CPLError(CE_Failure, CPLE_AppDefined,
9736 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9737 1 : return nullptr;
9738 : }
9739 :
9740 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9741 :
9742 : // Check correctness of definition
9743 16 : bool bFirstNumericFormatter = true;
9744 16 : std::string osModDefinition;
9745 16 : bool bDefinitionUsesPctForG = false;
9746 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9747 : {
9748 70 : if (osDefinition[k] == '%')
9749 : {
9750 15 : osModDefinition += osDefinition[k];
9751 15 : if (k + 1 == osDefinition.size())
9752 : {
9753 1 : CPLError(CE_Failure, CPLE_AppDefined,
9754 : "Value of "
9755 : "BAND_METADATA[%d][\"item_value\"] = "
9756 : "%s is invalid at offset %d",
9757 : j, osDefinition.c_str(), int(k));
9758 1 : return nullptr;
9759 : }
9760 14 : ++k;
9761 14 : if (osDefinition[k] == '%')
9762 : {
9763 1 : osModDefinition += osDefinition[k];
9764 1 : continue;
9765 : }
9766 13 : if (!bFirstNumericFormatter)
9767 : {
9768 1 : CPLError(CE_Failure, CPLE_AppDefined,
9769 : "Value of "
9770 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9771 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9772 : "formatters should be specified at most once",
9773 : j, osDefinition.c_str(), int(k));
9774 1 : return nullptr;
9775 : }
9776 12 : bFirstNumericFormatter = false;
9777 19 : for (; k < osDefinition.size(); ++k)
9778 : {
9779 19 : osModDefinition += osDefinition[k];
9780 38 : if (!((osDefinition[k] >= '0' &&
9781 16 : osDefinition[k] <= '9') ||
9782 15 : osDefinition[k] == '.'))
9783 12 : break;
9784 : }
9785 24 : if (k == osDefinition.size() ||
9786 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9787 5 : osDefinition[k] != 's'))
9788 : {
9789 1 : CPLError(CE_Failure, CPLE_AppDefined,
9790 : "Value of "
9791 : "BAND_METADATA[%d][\"item_value\"] = "
9792 : "%s is invalid at offset %d: only "
9793 : "%%[x][.y]f|g or %%s formatters are accepted",
9794 : j, osDefinition.c_str(), int(k));
9795 1 : return nullptr;
9796 : }
9797 11 : bDefinitionUsesPctForG =
9798 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9799 11 : if (bDefinitionUsesPctForG)
9800 : {
9801 12 : if (poArray &&
9802 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9803 : {
9804 1 : CPLError(CE_Failure, CPLE_AppDefined,
9805 : "Data type of %s array is not numeric",
9806 1 : poArray->GetName().c_str());
9807 1 : return nullptr;
9808 : }
9809 8 : else if (poAttribute &&
9810 2 : poAttribute->GetDataType().GetClass() !=
9811 6 : GEDTC_NUMERIC)
9812 : {
9813 0 : CPLError(CE_Failure, CPLE_AppDefined,
9814 : "Data type of %s attribute is not numeric",
9815 0 : poAttribute->GetFullName().c_str());
9816 0 : return nullptr;
9817 : }
9818 : }
9819 : }
9820 62 : else if (osDefinition[k] == '$' &&
9821 62 : k + 1 < osDefinition.size() &&
9822 7 : osDefinition[k + 1] == '{')
9823 : {
9824 7 : const auto nPos = osDefinition.find('}', k);
9825 7 : if (nPos == std::string::npos)
9826 : {
9827 1 : CPLError(CE_Failure, CPLE_AppDefined,
9828 : "Value of "
9829 : "BAND_METADATA[%d][\"item_value\"] = "
9830 : "%s is invalid at offset %d",
9831 : j, osDefinition.c_str(), int(k));
9832 3 : return nullptr;
9833 : }
9834 : const auto osAttrName =
9835 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9836 0 : std::shared_ptr<GDALAttribute> poAttr;
9837 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9838 : {
9839 4 : poAttr = poArray->GetAttribute(osAttrName);
9840 4 : if (!poAttr)
9841 : {
9842 1 : CPLError(
9843 : CE_Failure, CPLE_AppDefined,
9844 : "Value of "
9845 : "BAND_METADATA[%d][\"item_value\"] = "
9846 : "%s is invalid: %s is not an attribute of %s",
9847 : j, osDefinition.c_str(), osAttrName.c_str(),
9848 1 : poArray->GetName().c_str());
9849 1 : return nullptr;
9850 : }
9851 : }
9852 : else
9853 : {
9854 : poAttr =
9855 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9856 2 : if (!poAttr)
9857 : {
9858 1 : CPLError(CE_Failure, CPLE_AppDefined,
9859 : "Value of "
9860 : "BAND_METADATA[%d][\"item_value\"] = "
9861 : "%s is invalid: %s is not an attribute",
9862 : j, osDefinition.c_str(),
9863 : osAttrName.c_str());
9864 1 : return nullptr;
9865 : }
9866 : }
9867 4 : k = nPos;
9868 4 : const char *pszValue = poAttr->ReadAsString();
9869 4 : if (!pszValue)
9870 : {
9871 0 : CPLError(CE_Failure, CPLE_AppDefined,
9872 : "Cannot get value of attribute %s as a "
9873 : "string",
9874 : osAttrName.c_str());
9875 0 : return nullptr;
9876 : }
9877 4 : osModDefinition += pszValue;
9878 : }
9879 : else
9880 : {
9881 48 : osModDefinition += osDefinition[k];
9882 : }
9883 : }
9884 :
9885 9 : if (poArray)
9886 8 : oItem.poArray = std::move(poArray);
9887 : else
9888 1 : oItem.poArray = std::move(poAttribute);
9889 9 : oItem.osDefinition = std::move(osModDefinition);
9890 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9891 :
9892 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9893 9 : std::move(oItem));
9894 : }
9895 : }
9896 :
9897 984 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9898 : const char *pszBandImageryMetadata =
9899 492 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9900 492 : if (pszBandImageryMetadata)
9901 : {
9902 20 : if (!poRootGroup)
9903 : {
9904 1 : CPLError(CE_Failure, CPLE_AppDefined,
9905 : "Root group should be provided when BAND_IMAGERY_METADATA "
9906 : "is set");
9907 17 : return nullptr;
9908 : }
9909 19 : CPLJSONDocument oDoc;
9910 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9911 : {
9912 1 : CPLError(CE_Failure, CPLE_AppDefined,
9913 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9914 1 : return nullptr;
9915 : }
9916 18 : auto oRoot = oDoc.GetRoot();
9917 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9918 : {
9919 1 : CPLError(CE_Failure, CPLE_AppDefined,
9920 : "Value of BAND_IMAGERY_METADATA should be an object");
9921 1 : return nullptr;
9922 : }
9923 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9924 : {
9925 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9926 20 : oJsonItem.GetName() == "FWHM_UM")
9927 : {
9928 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9929 : const auto osBandAttributeName =
9930 34 : oJsonItem.GetString("attribute");
9931 0 : std::shared_ptr<GDALMDArray> poArray;
9932 0 : std::shared_ptr<GDALAttribute> poAttribute;
9933 17 : size_t iExtraDimIdx = 0;
9934 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9935 : {
9936 2 : CPLError(CE_Failure, CPLE_AppDefined,
9937 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9938 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9939 : "missing",
9940 2 : oJsonItem.GetName().c_str(),
9941 2 : oJsonItem.GetName().c_str());
9942 1 : return nullptr;
9943 : }
9944 25 : else if (!osBandArrayFullname.empty() &&
9945 9 : !osBandAttributeName.empty())
9946 : {
9947 2 : CPLError(CE_Failure, CPLE_AppDefined,
9948 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9949 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9950 : "mutually exclusive",
9951 2 : oJsonItem.GetName().c_str(),
9952 2 : oJsonItem.GetName().c_str());
9953 1 : return nullptr;
9954 : }
9955 15 : else if (!osBandArrayFullname.empty())
9956 : {
9957 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9958 8 : osBandArrayFullname);
9959 8 : if (!poArray)
9960 : {
9961 1 : CPLError(CE_Failure, CPLE_AppDefined,
9962 : "Array %s cannot be found",
9963 : osBandArrayFullname.c_str());
9964 3 : return nullptr;
9965 : }
9966 7 : if (poArray->GetDimensionCount() != 1)
9967 : {
9968 1 : CPLError(CE_Failure, CPLE_AppDefined,
9969 : "Array %s is not a 1D array",
9970 : osBandArrayFullname.c_str());
9971 1 : return nullptr;
9972 : }
9973 : const auto &osAuxArrayDimName =
9974 6 : poArray->GetDimensions()[0]->GetName();
9975 : auto oIter =
9976 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9977 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9978 : {
9979 1 : CPLError(CE_Failure, CPLE_AppDefined,
9980 : "Dimension \"%s\" of array \"%s\" is not a "
9981 : "non-X/Y dimension of array \"%s\"",
9982 : osAuxArrayDimName.c_str(),
9983 : osBandArrayFullname.c_str(),
9984 1 : array->GetName().c_str());
9985 1 : return nullptr;
9986 : }
9987 5 : iExtraDimIdx = oIter->second;
9988 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9989 : }
9990 : else
9991 : {
9992 : poAttribute =
9993 7 : !osBandAttributeName.empty() &&
9994 7 : osBandAttributeName[0] == '/'
9995 16 : ? poRootGroup->OpenAttributeFromFullname(
9996 : osBandAttributeName)
9997 7 : : array->GetAttribute(osBandAttributeName);
9998 7 : if (!poAttribute)
9999 : {
10000 2 : CPLError(CE_Failure, CPLE_AppDefined,
10001 : "Attribute %s cannot be found",
10002 : osBandAttributeName.c_str());
10003 6 : return nullptr;
10004 : }
10005 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
10006 5 : if (aoAttrDims.size() != 1)
10007 : {
10008 2 : CPLError(CE_Failure, CPLE_AppDefined,
10009 : "Attribute %s is not a 1D array",
10010 : osBandAttributeName.c_str());
10011 2 : return nullptr;
10012 : }
10013 3 : bool found = false;
10014 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
10015 : {
10016 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
10017 4 : ->GetSize() == aoAttrDims[0])
10018 : {
10019 3 : if (found)
10020 : {
10021 2 : CPLError(CE_Failure, CPLE_AppDefined,
10022 : "Several dimensions of %s have the "
10023 : "same size as attribute %s. Cannot "
10024 : "infer which one to bind to!",
10025 1 : array->GetName().c_str(),
10026 : osBandAttributeName.c_str());
10027 1 : return nullptr;
10028 : }
10029 2 : found = true;
10030 2 : iExtraDimIdx = iter.second;
10031 : }
10032 : }
10033 2 : if (!found)
10034 : {
10035 2 : CPLError(CE_Failure, CPLE_AppDefined,
10036 : "No dimension of %s has the same size as "
10037 : "attribute %s",
10038 1 : array->GetName().c_str(),
10039 : osBandAttributeName.c_str());
10040 1 : return nullptr;
10041 : }
10042 : }
10043 :
10044 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
10045 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
10046 : {
10047 4 : if (osUnit.back() != '}')
10048 : {
10049 2 : CPLError(CE_Failure, CPLE_AppDefined,
10050 : "Value of "
10051 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
10052 : "%s is invalid",
10053 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
10054 2 : return nullptr;
10055 : }
10056 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
10057 0 : std::shared_ptr<GDALAttribute> poAttr;
10058 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
10059 : {
10060 2 : poAttr = poArray->GetAttribute(osAttrName);
10061 2 : if (!poAttr)
10062 : {
10063 2 : CPLError(
10064 : CE_Failure, CPLE_AppDefined,
10065 : "Value of "
10066 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
10067 : "%s is invalid: %s is not an attribute of %s",
10068 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
10069 : osAttrName.c_str(),
10070 : osBandArrayFullname.c_str());
10071 1 : return nullptr;
10072 : }
10073 : }
10074 : else
10075 : {
10076 : poAttr =
10077 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
10078 1 : if (!poAttr)
10079 : {
10080 0 : CPLError(
10081 : CE_Failure, CPLE_AppDefined,
10082 : "Value of "
10083 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
10084 : "%s is invalid: %s is not an attribute",
10085 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
10086 : osAttrName.c_str());
10087 0 : return nullptr;
10088 : }
10089 : }
10090 :
10091 2 : const char *pszValue = poAttr->ReadAsString();
10092 2 : if (!pszValue)
10093 : {
10094 0 : CPLError(CE_Failure, CPLE_AppDefined,
10095 : "Cannot get value of attribute %s of %s as a "
10096 : "string",
10097 : osAttrName.c_str(),
10098 : osBandArrayFullname.c_str());
10099 0 : return nullptr;
10100 : }
10101 2 : osUnit = pszValue;
10102 : }
10103 4 : double dfConvToUM = 1.0;
10104 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
10105 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
10106 3 : osUnit == "nanometers")
10107 : {
10108 1 : dfConvToUM = 1e-3;
10109 : }
10110 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
10111 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
10112 1 : osUnit == "micrometers"))
10113 : {
10114 2 : CPLError(CE_Failure, CPLE_AppDefined,
10115 : "Unhandled value for "
10116 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
10117 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
10118 1 : return nullptr;
10119 : }
10120 :
10121 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
10122 :
10123 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
10124 3 : if (poArray)
10125 2 : abstractArray = std::move(poArray);
10126 : else
10127 1 : abstractArray = std::move(poAttribute);
10128 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
10129 : {
10130 2 : item.poCentralWavelengthArray = std::move(abstractArray);
10131 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
10132 : }
10133 : else
10134 : {
10135 1 : item.poFWHMArray = std::move(abstractArray);
10136 1 : item.dfFWHMToMicrometer = dfConvToUM;
10137 : }
10138 : }
10139 : else
10140 : {
10141 1 : CPLError(CE_Warning, CPLE_AppDefined,
10142 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
10143 2 : oJsonItem.GetName().c_str());
10144 : }
10145 : }
10146 : }
10147 :
10148 456 : if ((nDimCount >= 2 &&
10149 950 : dims[iYDim]->GetSize() > static_cast<uint64_t>(INT_MAX)) ||
10150 475 : dims[iXDim]->GetSize() > static_cast<uint64_t>(INT_MAX))
10151 : {
10152 1 : CPLError(CE_Failure, CPLE_AppDefined,
10153 : "Array is too large to be exposed as a GDAL dataset");
10154 1 : return nullptr;
10155 : }
10156 :
10157 : auto poDS = std::make_unique<GDALDatasetFromArray>(
10158 948 : array, iXDim, iYDim, CPLStringList(papszOptions));
10159 :
10160 474 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
10161 :
10162 474 : poDS->nRasterYSize =
10163 474 : nDimCount < 2 ? 1 : static_cast<int>(dims[iYDim]->GetSize());
10164 :
10165 474 : poDS->nRasterXSize = static_cast<int>(dims[iXDim]->GetSize());
10166 :
10167 948 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
10168 948 : std::vector<GUInt64> anStackIters(nDimCount);
10169 948 : std::vector<size_t> anMapNewToOld(nNewDimCount);
10170 1460 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
10171 : {
10172 986 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
10173 : {
10174 57 : anMapNewToOld[j] = i;
10175 57 : j++;
10176 : }
10177 : }
10178 :
10179 474 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
10180 :
10181 948 : const auto attrs(array->GetAttributes());
10182 644 : for (const auto &attr : attrs)
10183 : {
10184 170 : if (attr->GetName() == "spatial:registration")
10185 : {
10186 : // From https://github.com/zarr-conventions/spatial
10187 6 : const char *pszValue = attr->ReadAsString();
10188 6 : if (pszValue && strcmp(pszValue, "pixel") == 0)
10189 5 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10190 : GDALMD_AOP_AREA);
10191 1 : else if (pszValue && strcmp(pszValue, "node") == 0)
10192 1 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10193 : GDALMD_AOP_POINT);
10194 0 : else if (pszValue)
10195 0 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), pszValue);
10196 : }
10197 164 : else if (attr->GetName() == "gdal:geotransform")
10198 : {
10199 : // From Zarr driver
10200 4 : const auto doubleArray = attr->ReadAsDoubleArray();
10201 2 : if (doubleArray.size() == 6)
10202 : {
10203 2 : poDS->m_bHasGT = true;
10204 2 : poDS->m_gt = GDALGeoTransform(doubleArray.data());
10205 : }
10206 : }
10207 162 : else if (attr->GetName() != "COLOR_INTERPRETATION")
10208 : {
10209 300 : auto stringArray = attr->ReadAsStringArray();
10210 300 : std::string val;
10211 150 : if (stringArray.size() > 1)
10212 : {
10213 59 : val += '{';
10214 : }
10215 585 : for (int i = 0; i < stringArray.size(); ++i)
10216 : {
10217 435 : if (i > 0)
10218 285 : val += ',';
10219 435 : val += stringArray[i];
10220 : }
10221 150 : if (stringArray.size() > 1)
10222 : {
10223 59 : val += '}';
10224 : }
10225 150 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
10226 : }
10227 : }
10228 :
10229 474 : const char *pszDelay = CSLFetchNameValueDef(
10230 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
10231 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
10232 : const double dfDelay =
10233 474 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
10234 474 : const auto nStartTime = time(nullptr);
10235 474 : bool bHasWarned = false;
10236 : // Instantiate bands by iterating over non-XY variables
10237 474 : size_t iDim = 0;
10238 474 : int nCurBand = 1;
10239 622 : lbl_next_depth:
10240 622 : if (iDim < nNewDimCount)
10241 : {
10242 59 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
10243 59 : anOtherDimCoord[iDim] = 0;
10244 : while (true)
10245 : {
10246 148 : ++iDim;
10247 148 : goto lbl_next_depth;
10248 148 : lbl_return_to_caller:
10249 148 : --iDim;
10250 148 : --anStackIters[iDim];
10251 148 : if (anStackIters[iDim] == 0)
10252 59 : break;
10253 89 : ++anOtherDimCoord[iDim];
10254 : }
10255 : }
10256 : else
10257 : {
10258 1126 : poDS->SetBand(nCurBand,
10259 563 : std::make_unique<GDALRasterBandFromArray>(
10260 563 : poDS.get(), anOtherDimCoord,
10261 : aoBandParameterMetadataItems, aoBandImageryMetadata,
10262 : dfDelay, nStartTime, bHasWarned));
10263 563 : ++nCurBand;
10264 : }
10265 622 : if (iDim > 0)
10266 148 : goto lbl_return_to_caller;
10267 :
10268 906 : if (!array->GetFilename().empty() &&
10269 432 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "LOAD_PAM", "YES")))
10270 : {
10271 422 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10272 : std::string osDerivedDatasetName(
10273 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10274 844 : int(iYDim), array->GetFullName().c_str()));
10275 422 : if (!array->GetContext().empty())
10276 : {
10277 2 : osDerivedDatasetName += " with context ";
10278 2 : osDerivedDatasetName += array->GetContext();
10279 : }
10280 422 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10281 422 : poDS->TryLoadXML();
10282 :
10283 2 : for (const auto &[pszKey, pszValue] :
10284 : cpl::IterateNameValue(static_cast<CSLConstList>(
10285 424 : poDS->GDALPamDataset::GetMetadata())))
10286 : {
10287 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10288 : }
10289 : }
10290 :
10291 474 : return poDS;
10292 : }
10293 :
10294 : /************************************************************************/
10295 : /* AsClassicDataset() */
10296 : /************************************************************************/
10297 :
10298 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10299 : *
10300 : * In the case of > 2D arrays, additional dimensions will be represented as
10301 : * raster bands.
10302 : *
10303 : * The "reverse" method is GDALRasterBand::AsMDArray().
10304 : *
10305 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10306 : *
10307 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10308 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10309 : * Ignored if the dimension count is 1.
10310 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10311 : * and BAND_IMAGERY_METADATA option.
10312 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10313 : * nullptr. Current supported options are:
10314 : * <ul>
10315 : * <li>BAND_METADATA: JSON serialized array defining which
10316 : * arrays of the poRootGroup, indexed by non-X and Y
10317 : * dimensions, should be mapped as band metadata items.
10318 : * Each array item should be an object with the
10319 : * following members:
10320 : * - "array": full name of a band parameter array.
10321 : * Such array must be a one
10322 : * dimensional array, and its dimension must be one of
10323 : * the dimensions of the array on which the method is
10324 : * called (excluding the X and Y dimensions).
10325 : * - "attribute": name relative to *this array or full
10326 : * name of a single dimension numeric array whose size
10327 : * must be one of the dimensions of *this array
10328 : * (excluding the X and Y dimensions).
10329 : * "array" and "attribute" are mutually exclusive.
10330 : * - "item_name": band metadata item name
10331 : * - "item_value": (optional) String, where "%[x][.y]f",
10332 : * "%[x][.y]g" or "%s" printf-like formatting can be
10333 : * used to format the corresponding value of the
10334 : * parameter array. The percentage character should be
10335 : * repeated: "%%"
10336 : * "${attribute_name}" can also be used to include the
10337 : * value of an attribute for "array" when set and if
10338 : * not starting with '/'. Otherwise if starting with
10339 : * '/', it is the full path to the attribute.
10340 : *
10341 : * If "item_value" is not provided, a default formatting
10342 : * of the value will be applied.
10343 : *
10344 : * Example:
10345 : * [
10346 : * {
10347 : * "array": "/sensor_band_parameters/wavelengths",
10348 : * "item_name": "WAVELENGTH",
10349 : * "item_value": "%.1f ${units}"
10350 : * },
10351 : * {
10352 : * "array": "/sensor_band_parameters/fwhm",
10353 : * "item_name": "FWHM"
10354 : * },
10355 : * {
10356 : * "array": "/sensor_band_parameters/fwhm",
10357 : * "item_name": "FWHM_UNIT",
10358 : * "item_value": "${units}"
10359 : * }
10360 : * ]
10361 : *
10362 : * Example for Planet Labs Tanager radiance products:
10363 : * [
10364 : * {
10365 : * "attribute": "center_wavelengths",
10366 : * "item_name": "WAVELENGTH",
10367 : * "item_value": "%.1f ${center_wavelengths_units}"
10368 : * }
10369 : * ]
10370 : *
10371 : * </li>
10372 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10373 : * JSON serialized object defining which arrays of the
10374 : * poRootGroup, indexed by non-X and Y dimensions,
10375 : * should be mapped as band metadata items in the
10376 : * band IMAGERY domain.
10377 : * The object currently accepts 2 members:
10378 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10379 : * micrometers.
10380 : * - "FWHM_UM": Full-width half-maximum
10381 : * in micrometers.
10382 : * The value of each member should be an object with the
10383 : * following members:
10384 : * - "array": full name of a band parameter array.
10385 : * Such array must be a one dimensional array, and its
10386 : * dimension must be one of the dimensions of the
10387 : * array on which the method is called
10388 : * (excluding the X and Y dimensions).
10389 : * - "attribute": name relative to *this array or full
10390 : * name of a single dimension numeric array whose size
10391 : * must be one of the dimensions of *this array
10392 : * (excluding the X and Y dimensions).
10393 : * "array" and "attribute" are mutually exclusive,
10394 : * and one of them is required.
10395 : * - "unit": (optional) unit of the values pointed in
10396 : * the above array.
10397 : * Can be a literal string or a string of the form
10398 : * "${attribute_name}" to point to an attribute for
10399 : * "array" when set and if no starting
10400 : * with '/'. Otherwise if starting with '/', it is
10401 : * the full path to the attribute.
10402 : * Accepted values are "um", "micrometer"
10403 : * (with UK vs US spelling, singular or plural), "nm",
10404 : * "nanometer" (with UK vs US spelling, singular or
10405 : * plural)
10406 : * If not provided, micrometer is assumed.
10407 : *
10408 : * Example for EMIT datasets:
10409 : * {
10410 : * "CENTRAL_WAVELENGTH_UM": {
10411 : * "array": "/sensor_band_parameters/wavelengths",
10412 : * "unit": "${units}"
10413 : * },
10414 : * "FWHM_UM": {
10415 : * "array": "/sensor_band_parameters/fwhm",
10416 : * "unit": "${units}"
10417 : * }
10418 : * }
10419 : *
10420 : * Example for Planet Labs Tanager radiance products:
10421 : * {
10422 : * "CENTRAL_WAVELENGTH_UM": {
10423 : * "attribute": "center_wavelengths",
10424 : * "unit": "${center_wavelengths_units}"
10425 : * },
10426 : * "FWHM_UM": {
10427 : * "attribute": "fwhm",
10428 : * "unit": "${fwhm_units}"
10429 : * }
10430 : * }
10431 : *
10432 : * </li>
10433 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10434 : * seconds allowed to set the DIM_{dimname}_VALUE band
10435 : * metadata items from the indexing variable of the
10436 : * dimensions.
10437 : * Default value is 5. 'unlimited' can be used to mean
10438 : * unlimited delay. Can also be defined globally with
10439 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10440 : * option.</li>
10441 : * </ul>
10442 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10443 : */
10444 : GDALDataset *
10445 516 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10446 : const std::shared_ptr<GDALGroup> &poRootGroup,
10447 : CSLConstList papszOptions) const
10448 : {
10449 1032 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10450 516 : if (!self)
10451 : {
10452 0 : CPLError(CE_Failure, CPLE_AppDefined,
10453 : "Driver implementation issue: m_pSelf not set !");
10454 0 : return nullptr;
10455 : }
10456 1032 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10457 : papszOptions)
10458 516 : .release();
10459 : }
10460 :
10461 : /************************************************************************/
10462 : /* GetStatistics() */
10463 : /************************************************************************/
10464 :
10465 : /**
10466 : * \brief Fetch statistics.
10467 : *
10468 : * Returns the minimum, maximum, mean and standard deviation of all
10469 : * pixel values in this array.
10470 : *
10471 : * If bForce is FALSE results will only be returned if it can be done
10472 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10473 : * results cannot be returned efficiently, the method will return CE_Warning
10474 : * but no warning will have been issued. This is a non-standard use of
10475 : * the CE_Warning return value to indicate "nothing done".
10476 : *
10477 : * When cached statistics are not available, and bForce is TRUE,
10478 : * ComputeStatistics() is called.
10479 : *
10480 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10481 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10482 : * after the first request.
10483 : *
10484 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10485 : *
10486 : * This method is the same as the C function GDALMDArrayGetStatistics().
10487 : *
10488 : * @param bApproxOK Currently ignored. In the future, should be set to true
10489 : * if statistics on the whole array are wished, or to false if a subset of it
10490 : * may be used.
10491 : *
10492 : * @param bForce If false statistics will only be returned if it can
10493 : * be done without rescanning the image.
10494 : *
10495 : * @param pdfMin Location into which to load image minimum (may be NULL).
10496 : *
10497 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10498 : *
10499 : * @param pdfMean Location into which to load image mean (may be NULL).
10500 : *
10501 : * @param pdfStdDev Location into which to load image standard deviation
10502 : * (may be NULL).
10503 : *
10504 : * @param pnValidCount Number of samples whose value is different from the
10505 : * nodata value. (may be NULL)
10506 : *
10507 : * @param pfnProgress a function to call to report progress, or NULL.
10508 : *
10509 : * @param pProgressData application data to pass to the progress function.
10510 : *
10511 : * @return CE_None on success, CE_Warning if no values returned,
10512 : * CE_Failure if an error occurs.
10513 : *
10514 : * @since GDAL 3.2
10515 : */
10516 :
10517 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10518 : double *pdfMax, double *pdfMean,
10519 : double *pdfStdDev, GUInt64 *pnValidCount,
10520 : GDALProgressFunc pfnProgress,
10521 : void *pProgressData)
10522 : {
10523 10 : if (!bForce)
10524 1 : return CE_Warning;
10525 :
10526 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10527 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10528 9 : ? CE_None
10529 9 : : CE_Failure;
10530 : }
10531 :
10532 : /************************************************************************/
10533 : /* ComputeStatistics() */
10534 : /************************************************************************/
10535 :
10536 : /**
10537 : * \brief Compute statistics.
10538 : *
10539 : * Returns the minimum, maximum, mean and standard deviation of all
10540 : * pixel values in this array.
10541 : *
10542 : * Pixels taken into account in statistics are those whose mask value
10543 : * (as determined by GetMask()) is non-zero.
10544 : *
10545 : * Once computed, the statistics will generally be "set" back on the
10546 : * owing dataset.
10547 : *
10548 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10549 : *
10550 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10551 : * and GDALMDArrayComputeStatisticsEx().
10552 : *
10553 : * @param bApproxOK Currently ignored. In the future, should be set to true
10554 : * if statistics on the whole array are wished, or to false if a subset of it
10555 : * may be used.
10556 : *
10557 : * @param pdfMin Location into which to load image minimum (may be NULL).
10558 : *
10559 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10560 : *
10561 : * @param pdfMean Location into which to load image mean (may be NULL).
10562 : *
10563 : * @param pdfStdDev Location into which to load image standard deviation
10564 : * (may be NULL).
10565 : *
10566 : * @param pnValidCount Number of samples whose value is different from the
10567 : * nodata value. (may be NULL)
10568 : *
10569 : * @param pfnProgress a function to call to report progress, or NULL.
10570 : *
10571 : * @param pProgressData application data to pass to the progress function.
10572 : *
10573 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10574 : * Options are driver specific. For now the netCDF and Zarr
10575 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10576 : * to add or update the actual_range attribute with the
10577 : * computed min/max, only if done on the full array, in non
10578 : * approximate mode, and the dataset is opened in update
10579 : * mode.
10580 : *
10581 : * @return true on success
10582 : *
10583 : * @since GDAL 3.2
10584 : */
10585 :
10586 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10587 : double *pdfMax, double *pdfMean,
10588 : double *pdfStdDev, GUInt64 *pnValidCount,
10589 : GDALProgressFunc pfnProgress,
10590 : void *pProgressData,
10591 : CSLConstList papszOptions)
10592 : {
10593 : struct StatsPerChunkType
10594 : {
10595 : const GDALMDArray *array = nullptr;
10596 : std::shared_ptr<GDALMDArray> poMask{};
10597 : double dfMin = cpl::NumericLimits<double>::max();
10598 : double dfMax = -cpl::NumericLimits<double>::max();
10599 : double dfMean = 0.0;
10600 : double dfM2 = 0.0;
10601 : GUInt64 nValidCount = 0;
10602 : std::vector<GByte> abyData{};
10603 : std::vector<double> adfData{};
10604 : std::vector<GByte> abyMaskData{};
10605 : GDALProgressFunc pfnProgress = nullptr;
10606 : void *pProgressData = nullptr;
10607 : };
10608 :
10609 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10610 : const GUInt64 *chunkArrayStartIdx,
10611 : const size_t *chunkCount, GUInt64 iCurChunk,
10612 : GUInt64 nChunkCount, void *pUserData)
10613 : {
10614 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10615 13 : const GDALMDArray *array = data->array;
10616 13 : const GDALMDArray *poMask = data->poMask.get();
10617 13 : const size_t nDims = array->GetDimensionCount();
10618 13 : size_t nVals = 1;
10619 34 : for (size_t i = 0; i < nDims; i++)
10620 21 : nVals *= chunkCount[i];
10621 :
10622 : // Get mask
10623 13 : data->abyMaskData.resize(nVals);
10624 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10625 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10626 : {
10627 0 : return false;
10628 : }
10629 :
10630 : // Get data
10631 13 : const auto &oType = array->GetDataType();
10632 13 : if (oType.GetNumericDataType() == GDT_Float64)
10633 : {
10634 6 : data->adfData.resize(nVals);
10635 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10636 6 : oType, &data->adfData[0]))
10637 : {
10638 0 : return false;
10639 : }
10640 : }
10641 : else
10642 : {
10643 7 : data->abyData.resize(nVals * oType.GetSize());
10644 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10645 7 : oType, &data->abyData[0]))
10646 : {
10647 0 : return false;
10648 : }
10649 7 : data->adfData.resize(nVals);
10650 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10651 7 : static_cast<int>(oType.GetSize()),
10652 7 : &data->adfData[0], GDT_Float64,
10653 : static_cast<int>(sizeof(double)),
10654 : static_cast<GPtrDiff_t>(nVals));
10655 : }
10656 912 : for (size_t i = 0; i < nVals; i++)
10657 : {
10658 899 : if (data->abyMaskData[i])
10659 : {
10660 894 : const double dfValue = data->adfData[i];
10661 894 : data->dfMin = std::min(data->dfMin, dfValue);
10662 894 : data->dfMax = std::max(data->dfMax, dfValue);
10663 894 : data->nValidCount++;
10664 894 : const double dfDelta = dfValue - data->dfMean;
10665 894 : data->dfMean += dfDelta / data->nValidCount;
10666 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10667 : }
10668 : }
10669 13 : if (data->pfnProgress &&
10670 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10671 : "", data->pProgressData))
10672 : {
10673 0 : return false;
10674 : }
10675 13 : return true;
10676 : };
10677 :
10678 13 : const auto &oType = GetDataType();
10679 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10680 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10681 : {
10682 0 : CPLError(
10683 : CE_Failure, CPLE_NotSupported,
10684 : "Statistics can only be computed on non-complex numeric data type");
10685 0 : return false;
10686 : }
10687 :
10688 13 : const size_t nDims = GetDimensionCount();
10689 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10690 26 : std::vector<GUInt64> count(nDims);
10691 13 : const auto &poDims = GetDimensions();
10692 34 : for (size_t i = 0; i < nDims; i++)
10693 : {
10694 21 : count[i] = poDims[i]->GetSize();
10695 : }
10696 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10697 : const size_t nMaxChunkSize =
10698 : pszSwathSize
10699 13 : ? static_cast<size_t>(
10700 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10701 0 : CPLAtoGIntBig(pszSwathSize)))
10702 : : static_cast<size_t>(
10703 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10704 13 : GDALGetCacheMax64() / 4));
10705 26 : StatsPerChunkType sData;
10706 13 : sData.array = this;
10707 13 : sData.poMask = GetMask(nullptr);
10708 13 : if (sData.poMask == nullptr)
10709 : {
10710 0 : return false;
10711 : }
10712 13 : sData.pfnProgress = pfnProgress;
10713 13 : sData.pProgressData = pProgressData;
10714 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10715 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10716 13 : PerChunkFunc, &sData))
10717 : {
10718 0 : return false;
10719 : }
10720 :
10721 13 : if (pdfMin)
10722 13 : *pdfMin = sData.dfMin;
10723 :
10724 13 : if (pdfMax)
10725 13 : *pdfMax = sData.dfMax;
10726 :
10727 13 : if (pdfMean)
10728 11 : *pdfMean = sData.dfMean;
10729 :
10730 : const double dfStdDev =
10731 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10732 13 : if (pdfStdDev)
10733 11 : *pdfStdDev = dfStdDev;
10734 :
10735 13 : if (pnValidCount)
10736 11 : *pnValidCount = sData.nValidCount;
10737 :
10738 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10739 13 : sData.nValidCount, papszOptions);
10740 :
10741 13 : return true;
10742 : }
10743 :
10744 : /************************************************************************/
10745 : /* SetStatistics() */
10746 : /************************************************************************/
10747 : //! @cond Doxygen_Suppress
10748 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10749 : double /* dfMax */, double /* dfMean */,
10750 : double /* dfStdDev */,
10751 : GUInt64 /* nValidCount */,
10752 : CSLConstList /* papszOptions */)
10753 : {
10754 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10755 5 : return false;
10756 : }
10757 :
10758 : //! @endcond
10759 :
10760 : /************************************************************************/
10761 : /* ClearStatistics() */
10762 : /************************************************************************/
10763 :
10764 : /**
10765 : * \brief Clear statistics.
10766 : *
10767 : * @since GDAL 3.4
10768 : */
10769 0 : void GDALMDArray::ClearStatistics()
10770 : {
10771 0 : }
10772 :
10773 : /************************************************************************/
10774 : /* GetCoordinateVariables() */
10775 : /************************************************************************/
10776 :
10777 : /**
10778 : * \brief Return coordinate variables.
10779 : *
10780 : * Coordinate variables are an alternate way of indexing an array that can
10781 : * be sometimes used. For example, an array collected through remote sensing
10782 : * might be indexed by (scanline, pixel). But there can be
10783 : * a longitude and latitude arrays alongside that are also both indexed by
10784 : * (scanline, pixel), and are referenced from operational arrays for
10785 : * reprojection purposes.
10786 : *
10787 : * For netCDF, this will return the arrays referenced by the "coordinates"
10788 : * attribute.
10789 : *
10790 : * This method is the same as the C function
10791 : * GDALMDArrayGetCoordinateVariables().
10792 : *
10793 : * @return a vector of arrays
10794 : *
10795 : * @since GDAL 3.4
10796 : */
10797 :
10798 : std::vector<std::shared_ptr<GDALMDArray>>
10799 13 : GDALMDArray::GetCoordinateVariables() const
10800 : {
10801 13 : return {};
10802 : }
10803 :
10804 : /************************************************************************/
10805 : /* ~GDALExtendedDataType() */
10806 : /************************************************************************/
10807 :
10808 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10809 :
10810 : /************************************************************************/
10811 : /* GDALExtendedDataType() */
10812 : /************************************************************************/
10813 :
10814 44531 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10815 44531 : GDALExtendedDataTypeSubType eSubType)
10816 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10817 44531 : m_nMaxStringLength(nMaxStringLength)
10818 : {
10819 44531 : }
10820 :
10821 : /************************************************************************/
10822 : /* GDALExtendedDataType() */
10823 : /************************************************************************/
10824 :
10825 336740 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10826 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10827 336740 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10828 : {
10829 336740 : }
10830 :
10831 : /************************************************************************/
10832 : /* GDALExtendedDataType() */
10833 : /************************************************************************/
10834 :
10835 63 : GDALExtendedDataType::GDALExtendedDataType(
10836 : const std::string &osName, GDALDataType eBaseType,
10837 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10838 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10839 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10840 : {
10841 63 : }
10842 :
10843 : /************************************************************************/
10844 : /* GDALExtendedDataType() */
10845 : /************************************************************************/
10846 :
10847 1765 : GDALExtendedDataType::GDALExtendedDataType(
10848 : const std::string &osName, size_t nTotalSize,
10849 1765 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10850 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10851 1765 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10852 : {
10853 1765 : }
10854 :
10855 : /************************************************************************/
10856 : /* GDALExtendedDataType() */
10857 : /************************************************************************/
10858 :
10859 : /** Move constructor. */
10860 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10861 :
10862 : /************************************************************************/
10863 : /* GDALExtendedDataType() */
10864 : /************************************************************************/
10865 :
10866 : /** Copy constructor. */
10867 31527 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10868 63054 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10869 31527 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10870 31527 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10871 31527 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10872 : {
10873 31527 : if (m_eClass == GEDTC_COMPOUND)
10874 : {
10875 481 : for (const auto &elt : other.m_aoComponents)
10876 : {
10877 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10878 : }
10879 : }
10880 31527 : }
10881 :
10882 : /************************************************************************/
10883 : /* operator= () */
10884 : /************************************************************************/
10885 :
10886 : /** Copy assignment. */
10887 : GDALExtendedDataType &
10888 9068 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10889 : {
10890 9068 : if (this != &other)
10891 : {
10892 9068 : m_osName = other.m_osName;
10893 9068 : m_eClass = other.m_eClass;
10894 9068 : m_eSubType = other.m_eSubType;
10895 9068 : m_eNumericDT = other.m_eNumericDT;
10896 9068 : m_nSize = other.m_nSize;
10897 9068 : m_nMaxStringLength = other.m_nMaxStringLength;
10898 9068 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10899 9068 : m_aoComponents.clear();
10900 9068 : if (m_eClass == GEDTC_COMPOUND)
10901 : {
10902 0 : for (const auto &elt : other.m_aoComponents)
10903 : {
10904 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10905 : }
10906 : }
10907 : }
10908 9068 : return *this;
10909 : }
10910 :
10911 : /************************************************************************/
10912 : /* operator= () */
10913 : /************************************************************************/
10914 :
10915 : /** Move assignment. */
10916 : GDALExtendedDataType &
10917 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10918 :
10919 : /************************************************************************/
10920 : /* Create() */
10921 : /************************************************************************/
10922 :
10923 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10924 : *
10925 : * This is the same as the C function GDALExtendedDataTypeCreate()
10926 : *
10927 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10928 : * GDT_TypeCount
10929 : */
10930 336733 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10931 : {
10932 336733 : return GDALExtendedDataType(eType);
10933 : }
10934 :
10935 : /************************************************************************/
10936 : /* Create() */
10937 : /************************************************************************/
10938 :
10939 : /** Return a new GDALExtendedDataType from a raster attribute table.
10940 : *
10941 : * @param osName Type name
10942 : * @param eBaseType Base integer data type.
10943 : * @param poRAT Raster attribute table. Must not be NULL.
10944 : * @since 3.12
10945 : */
10946 : GDALExtendedDataType
10947 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10948 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10949 : {
10950 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10951 : }
10952 :
10953 : /************************************************************************/
10954 : /* Create() */
10955 : /************************************************************************/
10956 :
10957 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10958 : *
10959 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10960 : *
10961 : * @param osName Type name.
10962 : * @param nTotalSize Total size of the type in bytes.
10963 : * Should be large enough to store all components.
10964 : * @param components Components of the compound type.
10965 : */
10966 1772 : GDALExtendedDataType GDALExtendedDataType::Create(
10967 : const std::string &osName, size_t nTotalSize,
10968 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10969 : {
10970 1772 : size_t nLastOffset = 0;
10971 : // Some arbitrary threshold to avoid potential integer overflows
10972 1772 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10973 : {
10974 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10975 2 : return GDALExtendedDataType(GDT_Unknown);
10976 : }
10977 7512 : for (const auto &comp : components)
10978 : {
10979 : // Check alignment too ?
10980 5743 : if (comp->GetOffset() < nLastOffset)
10981 : {
10982 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10983 1 : return GDALExtendedDataType(GDT_Unknown);
10984 : }
10985 5742 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10986 : }
10987 1769 : if (nTotalSize < nLastOffset)
10988 : {
10989 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10990 1 : return GDALExtendedDataType(GDT_Unknown);
10991 : }
10992 1768 : if (nTotalSize == 0 || components.empty())
10993 : {
10994 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10995 3 : return GDALExtendedDataType(GDT_Unknown);
10996 : }
10997 1765 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10998 : }
10999 :
11000 : /************************************************************************/
11001 : /* Create() */
11002 : /************************************************************************/
11003 :
11004 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11005 : *
11006 : * This is the same as the C function GDALExtendedDataTypeCreateString().
11007 : *
11008 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
11009 : * unknown/unlimited
11010 : * @param eSubType Subtype.
11011 : */
11012 : GDALExtendedDataType
11013 44531 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
11014 : GDALExtendedDataTypeSubType eSubType)
11015 : {
11016 44531 : return GDALExtendedDataType(nMaxStringLength, eSubType);
11017 : }
11018 :
11019 : /************************************************************************/
11020 : /* operator==() */
11021 : /************************************************************************/
11022 :
11023 : /** Equality operator.
11024 : *
11025 : * This is the same as the C function GDALExtendedDataTypeEquals().
11026 : */
11027 5325 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
11028 : {
11029 5298 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
11030 10623 : m_nSize != other.m_nSize || m_osName != other.m_osName)
11031 : {
11032 767 : return false;
11033 : }
11034 4558 : if (m_eClass == GEDTC_NUMERIC)
11035 : {
11036 1406 : return m_eNumericDT == other.m_eNumericDT;
11037 : }
11038 3152 : if (m_eClass == GEDTC_STRING)
11039 : {
11040 2762 : return true;
11041 : }
11042 390 : CPLAssert(m_eClass == GEDTC_COMPOUND);
11043 390 : if (m_aoComponents.size() != other.m_aoComponents.size())
11044 : {
11045 2 : return false;
11046 : }
11047 2324 : for (size_t i = 0; i < m_aoComponents.size(); i++)
11048 : {
11049 1936 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
11050 : {
11051 0 : return false;
11052 : }
11053 : }
11054 388 : return true;
11055 : }
11056 :
11057 : /************************************************************************/
11058 : /* CanConvertTo() */
11059 : /************************************************************************/
11060 :
11061 : /** Return whether this data type can be converted to the other one.
11062 : *
11063 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
11064 : *
11065 : * @param other Target data type for the conversion being considered.
11066 : */
11067 22922 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
11068 : {
11069 22922 : if (m_eClass == GEDTC_NUMERIC)
11070 : {
11071 16723 : if (m_eNumericDT == GDT_Unknown)
11072 0 : return false;
11073 16723 : if (other.m_eClass == GEDTC_NUMERIC &&
11074 15287 : other.m_eNumericDT == GDT_Unknown)
11075 0 : return false;
11076 18159 : return other.m_eClass == GEDTC_NUMERIC ||
11077 18159 : other.m_eClass == GEDTC_STRING;
11078 : }
11079 6199 : if (m_eClass == GEDTC_STRING)
11080 : {
11081 5605 : return other.m_eClass == m_eClass;
11082 : }
11083 594 : CPLAssert(m_eClass == GEDTC_COMPOUND);
11084 594 : if (other.m_eClass != GEDTC_COMPOUND)
11085 0 : return false;
11086 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
11087 1188 : srcComponents;
11088 2822 : for (const auto &srcComp : m_aoComponents)
11089 : {
11090 2228 : srcComponents[srcComp->GetName()] = &srcComp;
11091 : }
11092 1345 : for (const auto &dstComp : other.m_aoComponents)
11093 : {
11094 752 : auto oIter = srcComponents.find(dstComp->GetName());
11095 752 : if (oIter == srcComponents.end())
11096 1 : return false;
11097 751 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
11098 0 : return false;
11099 : }
11100 593 : return true;
11101 : }
11102 :
11103 : /************************************************************************/
11104 : /* NeedsFreeDynamicMemory() */
11105 : /************************************************************************/
11106 :
11107 : /** Return whether the data type holds dynamically allocated memory, that
11108 : * needs to be freed with FreeDynamicMemory().
11109 : *
11110 : */
11111 6435 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
11112 : {
11113 6435 : switch (m_eClass)
11114 : {
11115 1751 : case GEDTC_STRING:
11116 1751 : return true;
11117 :
11118 4424 : case GEDTC_NUMERIC:
11119 4424 : return false;
11120 :
11121 260 : case GEDTC_COMPOUND:
11122 : {
11123 388 : for (const auto &comp : m_aoComponents)
11124 : {
11125 366 : if (comp->GetType().NeedsFreeDynamicMemory())
11126 238 : return true;
11127 : }
11128 : }
11129 : }
11130 22 : return false;
11131 : }
11132 :
11133 : /************************************************************************/
11134 : /* FreeDynamicMemory() */
11135 : /************************************************************************/
11136 :
11137 : /** Release the dynamic memory (strings typically) from a raw value.
11138 : *
11139 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
11140 : *
11141 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
11142 : */
11143 8780 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
11144 : {
11145 8780 : switch (m_eClass)
11146 : {
11147 5052 : case GEDTC_STRING:
11148 : {
11149 : char *pszStr;
11150 5052 : memcpy(&pszStr, pBuffer, sizeof(char *));
11151 5052 : if (pszStr)
11152 : {
11153 3813 : VSIFree(pszStr);
11154 : }
11155 5052 : break;
11156 : }
11157 :
11158 3399 : case GEDTC_NUMERIC:
11159 : {
11160 3399 : break;
11161 : }
11162 :
11163 329 : case GEDTC_COMPOUND:
11164 : {
11165 329 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
11166 2069 : for (const auto &comp : m_aoComponents)
11167 : {
11168 3480 : comp->GetType().FreeDynamicMemory(pabyBuffer +
11169 1740 : comp->GetOffset());
11170 : }
11171 329 : break;
11172 : }
11173 : }
11174 8780 : }
11175 :
11176 : /************************************************************************/
11177 : /* ~GDALEDTComponent() */
11178 : /************************************************************************/
11179 :
11180 : GDALEDTComponent::~GDALEDTComponent() = default;
11181 :
11182 : /************************************************************************/
11183 : /* GDALEDTComponent() */
11184 : /************************************************************************/
11185 :
11186 : /** constructor of a GDALEDTComponent
11187 : *
11188 : * This is the same as the C function GDALEDTComponendCreate()
11189 : *
11190 : * @param name Component name
11191 : * @param offset Offset in byte of the component in the compound data type.
11192 : * In case of nesting of compound data type, this should be
11193 : * the offset to the immediate belonging data type, not to the
11194 : * higher level one.
11195 : * @param type Component data type.
11196 : */
11197 5734 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
11198 5734 : const GDALExtendedDataType &type)
11199 5734 : : m_osName(name), m_nOffset(offset), m_oType(type)
11200 : {
11201 5734 : }
11202 :
11203 : /************************************************************************/
11204 : /* GDALEDTComponent() */
11205 : /************************************************************************/
11206 :
11207 : /** Copy constructor. */
11208 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
11209 :
11210 : /************************************************************************/
11211 : /* operator==() */
11212 : /************************************************************************/
11213 :
11214 : /** Equality operator.
11215 : */
11216 1936 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
11217 : {
11218 3872 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
11219 3872 : m_oType == other.m_oType;
11220 : }
11221 :
11222 : /************************************************************************/
11223 : /* ~GDALDimension() */
11224 : /************************************************************************/
11225 :
11226 : GDALDimension::~GDALDimension() = default;
11227 :
11228 : /************************************************************************/
11229 : /* GDALDimension() */
11230 : /************************************************************************/
11231 :
11232 : //! @cond Doxygen_Suppress
11233 : /** Constructor.
11234 : *
11235 : * @param osParentName Parent name
11236 : * @param osName name
11237 : * @param osType type. See GetType().
11238 : * @param osDirection direction. See GetDirection().
11239 : * @param nSize size.
11240 : */
11241 13035 : GDALDimension::GDALDimension(const std::string &osParentName,
11242 : const std::string &osName,
11243 : const std::string &osType,
11244 13035 : const std::string &osDirection, GUInt64 nSize)
11245 : : m_osName(osName),
11246 : m_osFullName(
11247 13035 : !osParentName.empty()
11248 18195 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
11249 : : osName),
11250 44265 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
11251 : {
11252 13035 : }
11253 :
11254 : //! @endcond
11255 :
11256 : /************************************************************************/
11257 : /* GetIndexingVariable() */
11258 : /************************************************************************/
11259 :
11260 : /** Return the variable that is used to index the dimension (if there is one).
11261 : *
11262 : * This is the array, typically one-dimensional, describing the values taken
11263 : * by the dimension.
11264 : */
11265 67 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11266 : {
11267 67 : return nullptr;
11268 : }
11269 :
11270 : /************************************************************************/
11271 : /* SetIndexingVariable() */
11272 : /************************************************************************/
11273 :
11274 : /** Set the variable that is used to index the dimension.
11275 : *
11276 : * This is the array, typically one-dimensional, describing the values taken
11277 : * by the dimension.
11278 : *
11279 : * Optionally implemented by drivers.
11280 : *
11281 : * Drivers known to implement it: MEM.
11282 : *
11283 : * @param poArray Variable to use to index the dimension.
11284 : * @return true in case of success.
11285 : */
11286 11 : bool GDALDimension::SetIndexingVariable(
11287 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11288 : {
11289 11 : CPLError(CE_Failure, CPLE_NotSupported,
11290 : "SetIndexingVariable() not implemented");
11291 11 : return false;
11292 : }
11293 :
11294 : /************************************************************************/
11295 : /* Rename() */
11296 : /************************************************************************/
11297 :
11298 : /** Rename the dimension.
11299 : *
11300 : * This is not implemented by all drivers.
11301 : *
11302 : * Drivers known to implement it: MEM, netCDF, ZARR.
11303 : *
11304 : * This is the same as the C function GDALDimensionRename().
11305 : *
11306 : * @param osNewName New name.
11307 : *
11308 : * @return true in case of success
11309 : * @since GDAL 3.8
11310 : */
11311 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11312 : {
11313 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11314 0 : return false;
11315 : }
11316 :
11317 : /************************************************************************/
11318 : /* BaseRename() */
11319 : /************************************************************************/
11320 :
11321 : //! @cond Doxygen_Suppress
11322 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11323 : {
11324 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11325 8 : m_osFullName += osNewName;
11326 8 : m_osName = osNewName;
11327 8 : }
11328 :
11329 : //! @endcond
11330 :
11331 : //! @cond Doxygen_Suppress
11332 : /************************************************************************/
11333 : /* ParentRenamed() */
11334 : /************************************************************************/
11335 :
11336 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11337 : {
11338 8 : m_osFullName = osNewParentFullName;
11339 8 : m_osFullName += "/";
11340 8 : m_osFullName += m_osName;
11341 8 : }
11342 :
11343 : //! @endcond
11344 :
11345 : //! @cond Doxygen_Suppress
11346 : /************************************************************************/
11347 : /* ParentDeleted() */
11348 : /************************************************************************/
11349 :
11350 8 : void GDALDimension::ParentDeleted()
11351 : {
11352 8 : }
11353 :
11354 : //! @endcond
11355 :
11356 : /************************************************************************/
11357 : /************************************************************************/
11358 : /************************************************************************/
11359 : /* C API */
11360 : /************************************************************************/
11361 : /************************************************************************/
11362 : /************************************************************************/
11363 :
11364 : /************************************************************************/
11365 : /* GDALExtendedDataTypeCreate() */
11366 : /************************************************************************/
11367 :
11368 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11369 : *
11370 : * This is the same as the C++ method GDALExtendedDataType::Create()
11371 : *
11372 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11373 : *
11374 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11375 : * GDT_TypeCount
11376 : *
11377 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11378 : */
11379 2454 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11380 : {
11381 2454 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11382 : {
11383 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11384 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11385 0 : return nullptr;
11386 : }
11387 : return new GDALExtendedDataTypeHS(
11388 2454 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11389 : }
11390 :
11391 : /************************************************************************/
11392 : /* GDALExtendedDataTypeCreateString() */
11393 : /************************************************************************/
11394 :
11395 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11396 : *
11397 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11398 : *
11399 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11400 : *
11401 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11402 : */
11403 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11404 : {
11405 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11406 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11407 : }
11408 :
11409 : /************************************************************************/
11410 : /* GDALExtendedDataTypeCreateStringEx() */
11411 : /************************************************************************/
11412 :
11413 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11414 : *
11415 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11416 : *
11417 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11418 : *
11419 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11420 : * @since GDAL 3.4
11421 : */
11422 : GDALExtendedDataTypeH
11423 233 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11424 : GDALExtendedDataTypeSubType eSubType)
11425 : {
11426 233 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11427 233 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11428 : }
11429 :
11430 : /************************************************************************/
11431 : /* GDALExtendedDataTypeCreateCompound() */
11432 : /************************************************************************/
11433 :
11434 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11435 : *
11436 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11437 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11438 : *
11439 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11440 : *
11441 : * @param pszName Type name.
11442 : * @param nTotalSize Total size of the type in bytes.
11443 : * Should be large enough to store all components.
11444 : * @param nComponents Number of components in comps array.
11445 : * @param comps Components.
11446 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11447 : */
11448 : GDALExtendedDataTypeH
11449 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11450 : size_t nComponents,
11451 : const GDALEDTComponentH *comps)
11452 : {
11453 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11454 54 : for (size_t i = 0; i < nComponents; i++)
11455 : {
11456 : compsCpp.emplace_back(
11457 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11458 : }
11459 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11460 66 : std::move(compsCpp));
11461 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11462 6 : return nullptr;
11463 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11464 : }
11465 :
11466 : /************************************************************************/
11467 : /* GDALExtendedDataTypeRelease() */
11468 : /************************************************************************/
11469 :
11470 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11471 : *
11472 : * Note: when applied on a object coming from a driver, this does not
11473 : * destroy the object in the file, database, etc...
11474 : */
11475 9479 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11476 : {
11477 9479 : delete hEDT;
11478 9479 : }
11479 :
11480 : /************************************************************************/
11481 : /* GDALExtendedDataTypeGetName() */
11482 : /************************************************************************/
11483 :
11484 : /** Return type name.
11485 : *
11486 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11487 : */
11488 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11489 : {
11490 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11491 8 : return hEDT->m_poImpl->GetName().c_str();
11492 : }
11493 :
11494 : /************************************************************************/
11495 : /* GDALExtendedDataTypeGetClass() */
11496 : /************************************************************************/
11497 :
11498 : /** Return type class.
11499 : *
11500 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11501 : */
11502 : GDALExtendedDataTypeClass
11503 14625 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11504 : {
11505 14625 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11506 14625 : return hEDT->m_poImpl->GetClass();
11507 : }
11508 :
11509 : /************************************************************************/
11510 : /* GDALExtendedDataTypeGetNumericDataType() */
11511 : /************************************************************************/
11512 :
11513 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11514 : *
11515 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11516 : */
11517 3574 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11518 : {
11519 3574 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11520 3574 : return hEDT->m_poImpl->GetNumericDataType();
11521 : }
11522 :
11523 : /************************************************************************/
11524 : /* GDALExtendedDataTypeGetSize() */
11525 : /************************************************************************/
11526 :
11527 : /** Return data type size in bytes.
11528 : *
11529 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11530 : */
11531 3926 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11532 : {
11533 3926 : VALIDATE_POINTER1(hEDT, __func__, 0);
11534 3926 : return hEDT->m_poImpl->GetSize();
11535 : }
11536 :
11537 : /************************************************************************/
11538 : /* GDALExtendedDataTypeGetMaxStringLength() */
11539 : /************************************************************************/
11540 :
11541 : /** Return the maximum length of a string in bytes.
11542 : *
11543 : * 0 indicates unknown/unlimited string.
11544 : *
11545 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11546 : */
11547 7 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11548 : {
11549 7 : VALIDATE_POINTER1(hEDT, __func__, 0);
11550 7 : return hEDT->m_poImpl->GetMaxStringLength();
11551 : }
11552 :
11553 : /************************************************************************/
11554 : /* GDALExtendedDataTypeCanConvertTo() */
11555 : /************************************************************************/
11556 :
11557 : /** Return whether this data type can be converted to the other one.
11558 : *
11559 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11560 : *
11561 : * @param hSourceEDT Source data type for the conversion being considered.
11562 : * @param hTargetEDT Target data type for the conversion being considered.
11563 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11564 : */
11565 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11566 : GDALExtendedDataTypeH hTargetEDT)
11567 : {
11568 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11569 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11570 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11571 : }
11572 :
11573 : /************************************************************************/
11574 : /* GDALExtendedDataTypeEquals() */
11575 : /************************************************************************/
11576 :
11577 : /** Return whether this data type is equal to another one.
11578 : *
11579 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11580 : *
11581 : * @param hFirstEDT First data type.
11582 : * @param hSecondEDT Second data type.
11583 : * @return TRUE if they are equal. FALSE otherwise.
11584 : */
11585 102 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11586 : GDALExtendedDataTypeH hSecondEDT)
11587 : {
11588 102 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11589 102 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11590 102 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11591 : }
11592 :
11593 : /************************************************************************/
11594 : /* GDALExtendedDataTypeGetSubType() */
11595 : /************************************************************************/
11596 :
11597 : /** Return the subtype of a type.
11598 : *
11599 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11600 : *
11601 : * @param hEDT Data type.
11602 : * @return subtype.
11603 : * @since 3.4
11604 : */
11605 : GDALExtendedDataTypeSubType
11606 116 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11607 : {
11608 116 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11609 116 : return hEDT->m_poImpl->GetSubType();
11610 : }
11611 :
11612 : /************************************************************************/
11613 : /* GDALExtendedDataTypeGetRAT() */
11614 : /************************************************************************/
11615 :
11616 : /** Return associated raster attribute table, when there is one.
11617 : *
11618 : * * For the netCDF driver, the RAT will capture enumerated types, with
11619 : * a "value" column with an integer value and a "name" column with the
11620 : * associated name.
11621 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11622 : *
11623 : * @param hEDT Data type.
11624 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11625 : * @since 3.12
11626 : */
11627 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11628 : {
11629 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11630 1 : return GDALRasterAttributeTable::ToHandle(
11631 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11632 : }
11633 :
11634 : /************************************************************************/
11635 : /* GDALExtendedDataTypeGetComponents() */
11636 : /************************************************************************/
11637 :
11638 : /** Return the components of the data type (only valid when GetClass() ==
11639 : * GEDTC_COMPOUND)
11640 : *
11641 : * The returned array and its content must be freed with
11642 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11643 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11644 : * individual array members).
11645 : *
11646 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11647 : *
11648 : * @param hEDT Data type
11649 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11650 : * @return an array of *pnCount components.
11651 : */
11652 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11653 : size_t *pnCount)
11654 : {
11655 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11656 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11657 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11658 : auto ret = static_cast<GDALEDTComponentH *>(
11659 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11660 131 : for (size_t i = 0; i < components.size(); i++)
11661 : {
11662 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11663 : }
11664 44 : *pnCount = components.size();
11665 44 : return ret;
11666 : }
11667 :
11668 : /************************************************************************/
11669 : /* GDALExtendedDataTypeFreeComponents() */
11670 : /************************************************************************/
11671 :
11672 : /** Free the return of GDALExtendedDataTypeGetComponents().
11673 : *
11674 : * @param components return value of GDALExtendedDataTypeGetComponents()
11675 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11676 : */
11677 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11678 : size_t nCount)
11679 : {
11680 131 : for (size_t i = 0; i < nCount; i++)
11681 : {
11682 87 : delete components[i];
11683 : }
11684 44 : CPLFree(components);
11685 44 : }
11686 :
11687 : /************************************************************************/
11688 : /* GDALEDTComponentCreate() */
11689 : /************************************************************************/
11690 :
11691 : /** Create a new GDALEDTComponent.
11692 : *
11693 : * The returned value must be freed with GDALEDTComponentRelease().
11694 : *
11695 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11696 : */
11697 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11698 : GDALExtendedDataTypeH hType)
11699 : {
11700 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11701 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11702 : return new GDALEDTComponentHS(
11703 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11704 : }
11705 :
11706 : /************************************************************************/
11707 : /* GDALEDTComponentRelease() */
11708 : /************************************************************************/
11709 :
11710 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11711 : *
11712 : * Note: when applied on a object coming from a driver, this does not
11713 : * destroy the object in the file, database, etc...
11714 : */
11715 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11716 : {
11717 61 : delete hComp;
11718 61 : }
11719 :
11720 : /************************************************************************/
11721 : /* GDALEDTComponentGetName() */
11722 : /************************************************************************/
11723 :
11724 : /** Return the name.
11725 : *
11726 : * The returned pointer is valid until hComp is released.
11727 : *
11728 : * This is the same as the C++ method GDALEDTComponent::GetName().
11729 : */
11730 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11731 : {
11732 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11733 33 : return hComp->m_poImpl->GetName().c_str();
11734 : }
11735 :
11736 : /************************************************************************/
11737 : /* GDALEDTComponentGetOffset() */
11738 : /************************************************************************/
11739 :
11740 : /** Return the offset (in bytes) of the component in the compound data type.
11741 : *
11742 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11743 : */
11744 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11745 : {
11746 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11747 31 : return hComp->m_poImpl->GetOffset();
11748 : }
11749 :
11750 : /************************************************************************/
11751 : /* GDALEDTComponentGetType() */
11752 : /************************************************************************/
11753 :
11754 : /** Return the data type of the component.
11755 : *
11756 : * This is the same as the C++ method GDALEDTComponent::GetType().
11757 : */
11758 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11759 : {
11760 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11761 : return new GDALExtendedDataTypeHS(
11762 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11763 : }
11764 :
11765 : /************************************************************************/
11766 : /* GDALGroupRelease() */
11767 : /************************************************************************/
11768 :
11769 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11770 : *
11771 : * Note: when applied on a object coming from a driver, this does not
11772 : * destroy the object in the file, database, etc...
11773 : */
11774 2401 : void GDALGroupRelease(GDALGroupH hGroup)
11775 : {
11776 2401 : delete hGroup;
11777 2401 : }
11778 :
11779 : /************************************************************************/
11780 : /* GDALGroupGetName() */
11781 : /************************************************************************/
11782 :
11783 : /** Return the name of the group.
11784 : *
11785 : * The returned pointer is valid until hGroup is released.
11786 : *
11787 : * This is the same as the C++ method GDALGroup::GetName().
11788 : */
11789 97 : const char *GDALGroupGetName(GDALGroupH hGroup)
11790 : {
11791 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11792 97 : return hGroup->m_poImpl->GetName().c_str();
11793 : }
11794 :
11795 : /************************************************************************/
11796 : /* GDALGroupGetFullName() */
11797 : /************************************************************************/
11798 :
11799 : /** Return the full name of the group.
11800 : *
11801 : * The returned pointer is valid until hGroup is released.
11802 : *
11803 : * This is the same as the C++ method GDALGroup::GetFullName().
11804 : */
11805 49 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11806 : {
11807 49 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11808 49 : return hGroup->m_poImpl->GetFullName().c_str();
11809 : }
11810 :
11811 : /************************************************************************/
11812 : /* GDALGroupGetMDArrayNames() */
11813 : /************************************************************************/
11814 :
11815 : /** Return the list of multidimensional array names contained in this group.
11816 : *
11817 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11818 : *
11819 : * @return the array names, to be freed with CSLDestroy()
11820 : */
11821 408 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11822 : {
11823 408 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11824 816 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11825 816 : CPLStringList res;
11826 993 : for (const auto &name : names)
11827 : {
11828 585 : res.AddString(name.c_str());
11829 : }
11830 408 : return res.StealList();
11831 : }
11832 :
11833 : /************************************************************************/
11834 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11835 : /************************************************************************/
11836 :
11837 : /** Return the list of multidimensional array full names contained in this
11838 : * group and its subgroups.
11839 : *
11840 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11841 : *
11842 : * @return the array names, to be freed with CSLDestroy()
11843 : *
11844 : * @since 3.11
11845 : */
11846 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11847 : CSLConstList papszGroupOptions,
11848 : CSLConstList papszArrayOptions)
11849 : {
11850 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11851 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11852 2 : papszGroupOptions, papszArrayOptions);
11853 2 : CPLStringList res;
11854 5 : for (const auto &name : names)
11855 : {
11856 4 : res.AddString(name.c_str());
11857 : }
11858 1 : return res.StealList();
11859 : }
11860 :
11861 : /************************************************************************/
11862 : /* GDALGroupOpenMDArray() */
11863 : /************************************************************************/
11864 :
11865 : /** Open and return a multidimensional array.
11866 : *
11867 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11868 : *
11869 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11870 : */
11871 1637 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11872 : CSLConstList papszOptions)
11873 : {
11874 1637 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11875 1637 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11876 4911 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11877 4911 : papszOptions);
11878 1637 : if (!array)
11879 30 : return nullptr;
11880 1607 : return new GDALMDArrayHS(array);
11881 : }
11882 :
11883 : /************************************************************************/
11884 : /* GDALGroupOpenMDArrayFromFullname() */
11885 : /************************************************************************/
11886 :
11887 : /** Open and return a multidimensional array from its fully qualified name.
11888 : *
11889 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11890 : *
11891 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11892 : *
11893 : * @since GDAL 3.2
11894 : */
11895 18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11896 : const char *pszFullname,
11897 : CSLConstList papszOptions)
11898 : {
11899 18 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11900 18 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11901 18 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11902 54 : std::string(pszFullname), papszOptions);
11903 18 : if (!array)
11904 2 : return nullptr;
11905 16 : return new GDALMDArrayHS(array);
11906 : }
11907 :
11908 : /************************************************************************/
11909 : /* GDALGroupResolveMDArray() */
11910 : /************************************************************************/
11911 :
11912 : /** Locate an array in a group and its subgroups by name.
11913 : *
11914 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11915 : * @since GDAL 3.2
11916 : */
11917 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11918 : const char *pszStartingPoint,
11919 : CSLConstList papszOptions)
11920 : {
11921 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11922 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11923 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11924 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11925 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11926 19 : if (!array)
11927 2 : return nullptr;
11928 17 : return new GDALMDArrayHS(array);
11929 : }
11930 :
11931 : /************************************************************************/
11932 : /* GDALGroupGetGroupNames() */
11933 : /************************************************************************/
11934 :
11935 : /** Return the list of sub-groups contained in this group.
11936 : *
11937 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11938 : *
11939 : * @return the group names, to be freed with CSLDestroy()
11940 : */
11941 106 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11942 : {
11943 106 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11944 212 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11945 212 : CPLStringList res;
11946 241 : for (const auto &name : names)
11947 : {
11948 135 : res.AddString(name.c_str());
11949 : }
11950 106 : return res.StealList();
11951 : }
11952 :
11953 : /************************************************************************/
11954 : /* GDALGroupOpenGroup() */
11955 : /************************************************************************/
11956 :
11957 : /** Open and return a sub-group.
11958 : *
11959 : * This is the same as the C++ method GDALGroup::OpenGroup().
11960 : *
11961 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11962 : */
11963 201 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11964 : CSLConstList papszOptions)
11965 : {
11966 201 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11967 201 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11968 : auto subGroup =
11969 603 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11970 201 : if (!subGroup)
11971 30 : return nullptr;
11972 171 : return new GDALGroupHS(subGroup);
11973 : }
11974 :
11975 : /************************************************************************/
11976 : /* GDALGroupGetVectorLayerNames() */
11977 : /************************************************************************/
11978 :
11979 : /** Return the list of layer names contained in this group.
11980 : *
11981 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11982 : *
11983 : * @return the group names, to be freed with CSLDestroy()
11984 : * @since 3.4
11985 : */
11986 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11987 : CSLConstList papszOptions)
11988 : {
11989 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11990 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11991 16 : CPLStringList res;
11992 18 : for (const auto &name : names)
11993 : {
11994 10 : res.AddString(name.c_str());
11995 : }
11996 8 : return res.StealList();
11997 : }
11998 :
11999 : /************************************************************************/
12000 : /* GDALGroupOpenVectorLayer() */
12001 : /************************************************************************/
12002 :
12003 : /** Open and return a vector layer.
12004 : *
12005 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
12006 : *
12007 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
12008 : * the returned handled if only valid while the parent GDALDatasetH is kept
12009 : * opened.
12010 : *
12011 : * @return the vector layer, or nullptr.
12012 : * @since 3.4
12013 : */
12014 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
12015 : const char *pszVectorLayerName,
12016 : CSLConstList papszOptions)
12017 : {
12018 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12019 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
12020 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
12021 24 : std::string(pszVectorLayerName), papszOptions));
12022 : }
12023 :
12024 : /************************************************************************/
12025 : /* GDALGroupOpenMDArrayFromFullname() */
12026 : /************************************************************************/
12027 :
12028 : /** Open and return a sub-group from its fully qualified name.
12029 : *
12030 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
12031 : *
12032 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
12033 : *
12034 : * @since GDAL 3.2
12035 : */
12036 4 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
12037 : const char *pszFullname,
12038 : CSLConstList papszOptions)
12039 : {
12040 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12041 4 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
12042 4 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
12043 12 : std::string(pszFullname), papszOptions);
12044 4 : if (!subGroup)
12045 2 : return nullptr;
12046 2 : return new GDALGroupHS(subGroup);
12047 : }
12048 :
12049 : /************************************************************************/
12050 : /* GDALGroupGetDimensions() */
12051 : /************************************************************************/
12052 :
12053 : /** Return the list of dimensions contained in this group and used by its
12054 : * arrays.
12055 : *
12056 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12057 : * array itself needs to be freed, CPLFree() should be called (and
12058 : * GDALDimensionRelease() on individual array members).
12059 : *
12060 : * This is the same as the C++ method GDALGroup::GetDimensions().
12061 : *
12062 : * @param hGroup Group.
12063 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12064 : * @param papszOptions Driver specific options determining how dimensions
12065 : * should be retrieved. Pass nullptr for default behavior.
12066 : *
12067 : * @return an array of *pnCount dimensions.
12068 : */
12069 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
12070 : CSLConstList papszOptions)
12071 : {
12072 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12073 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12074 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
12075 : auto ret = static_cast<GDALDimensionH *>(
12076 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12077 230 : for (size_t i = 0; i < dims.size(); i++)
12078 : {
12079 157 : ret[i] = new GDALDimensionHS(dims[i]);
12080 : }
12081 73 : *pnCount = dims.size();
12082 73 : return ret;
12083 : }
12084 :
12085 : /************************************************************************/
12086 : /* GDALGroupGetAttribute() */
12087 : /************************************************************************/
12088 :
12089 : /** Return an attribute by its name.
12090 : *
12091 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12092 : *
12093 : * The returned attribute must be freed with GDALAttributeRelease().
12094 : */
12095 121 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
12096 : {
12097 121 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12098 121 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12099 363 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
12100 121 : if (attr)
12101 116 : return new GDALAttributeHS(attr);
12102 5 : return nullptr;
12103 : }
12104 :
12105 : /************************************************************************/
12106 : /* GDALGroupGetAttributes() */
12107 : /************************************************************************/
12108 :
12109 : /** Return the list of attributes contained in this group.
12110 : *
12111 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12112 : * array itself needs to be freed, CPLFree() should be called (and
12113 : * GDALAttributeRelease() on individual array members).
12114 : *
12115 : * This is the same as the C++ method GDALGroup::GetAttributes().
12116 : *
12117 : * @param hGroup Group.
12118 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12119 : * @param papszOptions Driver specific options determining how attributes
12120 : * should be retrieved. Pass nullptr for default behavior.
12121 : *
12122 : * @return an array of *pnCount attributes.
12123 : */
12124 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
12125 : CSLConstList papszOptions)
12126 : {
12127 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12128 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12129 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
12130 : auto ret = static_cast<GDALAttributeH *>(
12131 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12132 229 : for (size_t i = 0; i < attrs.size(); i++)
12133 : {
12134 158 : ret[i] = new GDALAttributeHS(attrs[i]);
12135 : }
12136 71 : *pnCount = attrs.size();
12137 71 : return ret;
12138 : }
12139 :
12140 : /************************************************************************/
12141 : /* GDALGroupGetStructuralInfo() */
12142 : /************************************************************************/
12143 :
12144 : /** Return structural information on the group.
12145 : *
12146 : * This may be the compression, etc..
12147 : *
12148 : * The return value should not be freed and is valid until GDALGroup is
12149 : * released or this function called again.
12150 : *
12151 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
12152 : */
12153 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
12154 : {
12155 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12156 4 : return hGroup->m_poImpl->GetStructuralInfo();
12157 : }
12158 :
12159 : /************************************************************************/
12160 : /* GDALGroupGetDataTypeCount() */
12161 : /************************************************************************/
12162 :
12163 : /** Return the number of data types associated with the group
12164 : * (typically enumerations).
12165 : *
12166 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
12167 : *
12168 : * @since 3.12
12169 : */
12170 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
12171 : {
12172 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
12173 4 : return hGroup->m_poImpl->GetDataTypes().size();
12174 : }
12175 :
12176 : /************************************************************************/
12177 : /* GDALGroupGetDataType() */
12178 : /************************************************************************/
12179 :
12180 : /** Return one of the data types associated with the group.
12181 : *
12182 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
12183 : *
12184 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
12185 : * or nullptr in case of error.
12186 : * @since 3.12
12187 : */
12188 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
12189 : {
12190 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12191 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
12192 0 : return nullptr;
12193 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
12194 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
12195 : }
12196 :
12197 : /************************************************************************/
12198 : /* GDALReleaseAttributes() */
12199 : /************************************************************************/
12200 :
12201 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
12202 : *
12203 : * @param attributes return pointer of above methods
12204 : * @param nCount *pnCount value returned by above methods
12205 : */
12206 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
12207 : {
12208 418 : for (size_t i = 0; i < nCount; i++)
12209 : {
12210 288 : delete attributes[i];
12211 : }
12212 130 : CPLFree(attributes);
12213 130 : }
12214 :
12215 : /************************************************************************/
12216 : /* GDALGroupCreateGroup() */
12217 : /************************************************************************/
12218 :
12219 : /** Create a sub-group within a group.
12220 : *
12221 : * This is the same as the C++ method GDALGroup::CreateGroup().
12222 : *
12223 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
12224 : */
12225 185 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12226 : CSLConstList papszOptions)
12227 : {
12228 185 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12229 185 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
12230 555 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
12231 555 : papszOptions);
12232 185 : if (!ret)
12233 52 : return nullptr;
12234 133 : return new GDALGroupHS(ret);
12235 : }
12236 :
12237 : /************************************************************************/
12238 : /* GDALGroupDeleteGroup() */
12239 : /************************************************************************/
12240 :
12241 : /** Delete a sub-group from a group.
12242 : *
12243 : * After this call, if a previously obtained instance of the deleted object
12244 : * is still alive, no method other than for freeing it should be invoked.
12245 : *
12246 : * This is the same as the C++ method GDALGroup::DeleteGroup().
12247 : *
12248 : * @return true in case of success.
12249 : * @since GDAL 3.8
12250 : */
12251 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12252 : CSLConstList papszOptions)
12253 : {
12254 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12255 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12256 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12257 20 : papszOptions);
12258 : }
12259 :
12260 : /************************************************************************/
12261 : /* GDALGroupCreateDimension() */
12262 : /************************************************************************/
12263 :
12264 : /** Create a dimension within a group.
12265 : *
12266 : * This is the same as the C++ method GDALGroup::CreateDimension().
12267 : *
12268 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12269 : */
12270 836 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12271 : const char *pszType,
12272 : const char *pszDirection, GUInt64 nSize,
12273 : CSLConstList papszOptions)
12274 : {
12275 836 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12276 836 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12277 836 : auto ret = hGroup->m_poImpl->CreateDimension(
12278 1672 : std::string(pszName), std::string(pszType ? pszType : ""),
12279 3344 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12280 836 : if (!ret)
12281 9 : return nullptr;
12282 827 : return new GDALDimensionHS(ret);
12283 : }
12284 :
12285 : /************************************************************************/
12286 : /* GDALGroupCreateMDArray() */
12287 : /************************************************************************/
12288 :
12289 : /** Create a multidimensional array within a group.
12290 : *
12291 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12292 : *
12293 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12294 : */
12295 750 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12296 : size_t nDimensions,
12297 : GDALDimensionH *pahDimensions,
12298 : GDALExtendedDataTypeH hEDT,
12299 : CSLConstList papszOptions)
12300 : {
12301 750 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12302 750 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12303 750 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12304 1500 : std::vector<std::shared_ptr<GDALDimension>> dims;
12305 750 : dims.reserve(nDimensions);
12306 1808 : for (size_t i = 0; i < nDimensions; i++)
12307 1058 : dims.push_back(pahDimensions[i]->m_poImpl);
12308 2250 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12309 2250 : *(hEDT->m_poImpl), papszOptions);
12310 750 : if (!ret)
12311 64 : return nullptr;
12312 686 : return new GDALMDArrayHS(ret);
12313 : }
12314 :
12315 : /************************************************************************/
12316 : /* GDALGroupDeleteMDArray() */
12317 : /************************************************************************/
12318 :
12319 : /** Delete an array from a group.
12320 : *
12321 : * After this call, if a previously obtained instance of the deleted object
12322 : * is still alive, no method other than for freeing it should be invoked.
12323 : *
12324 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12325 : *
12326 : * @return true in case of success.
12327 : * @since GDAL 3.8
12328 : */
12329 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12330 : CSLConstList papszOptions)
12331 : {
12332 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12333 20 : VALIDATE_POINTER1(pszName, __func__, false);
12334 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12335 : }
12336 :
12337 : /************************************************************************/
12338 : /* GDALGroupCreateAttribute() */
12339 : /************************************************************************/
12340 :
12341 : /** Create a attribute within a group.
12342 : *
12343 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12344 : *
12345 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12346 : */
12347 142 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12348 : size_t nDimensions,
12349 : const GUInt64 *panDimensions,
12350 : GDALExtendedDataTypeH hEDT,
12351 : CSLConstList papszOptions)
12352 : {
12353 142 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12354 142 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12355 284 : std::vector<GUInt64> dims;
12356 142 : dims.reserve(nDimensions);
12357 200 : for (size_t i = 0; i < nDimensions; i++)
12358 58 : dims.push_back(panDimensions[i]);
12359 142 : auto ret = hGroup->m_poImpl->CreateAttribute(
12360 426 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12361 142 : if (!ret)
12362 18 : return nullptr;
12363 124 : return new GDALAttributeHS(ret);
12364 : }
12365 :
12366 : /************************************************************************/
12367 : /* GDALGroupDeleteAttribute() */
12368 : /************************************************************************/
12369 :
12370 : /** Delete an attribute from a group.
12371 : *
12372 : * After this call, if a previously obtained instance of the deleted object
12373 : * is still alive, no method other than for freeing it should be invoked.
12374 : *
12375 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12376 : *
12377 : * @return true in case of success.
12378 : * @since GDAL 3.8
12379 : */
12380 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12381 : CSLConstList papszOptions)
12382 : {
12383 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12384 25 : VALIDATE_POINTER1(pszName, __func__, false);
12385 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12386 25 : papszOptions);
12387 : }
12388 :
12389 : /************************************************************************/
12390 : /* GDALGroupRename() */
12391 : /************************************************************************/
12392 :
12393 : /** Rename the group.
12394 : *
12395 : * This is not implemented by all drivers.
12396 : *
12397 : * Drivers known to implement it: MEM, netCDF.
12398 : *
12399 : * This is the same as the C++ method GDALGroup::Rename()
12400 : *
12401 : * @return true in case of success
12402 : * @since GDAL 3.8
12403 : */
12404 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12405 : {
12406 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12407 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12408 45 : return hGroup->m_poImpl->Rename(pszNewName);
12409 : }
12410 :
12411 : /************************************************************************/
12412 : /* GDALGroupSubsetDimensionFromSelection() */
12413 : /************************************************************************/
12414 :
12415 : /** Return a virtual group whose one dimension has been subset according to a
12416 : * selection.
12417 : *
12418 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12419 : *
12420 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12421 : */
12422 : GDALGroupH
12423 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12424 : const char *pszSelection,
12425 : CPL_UNUSED CSLConstList papszOptions)
12426 : {
12427 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12428 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12429 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12430 42 : std::string(pszSelection));
12431 14 : if (!hNewGroup)
12432 8 : return nullptr;
12433 6 : return new GDALGroupHS(hNewGroup);
12434 : }
12435 :
12436 : /************************************************************************/
12437 : /* GDALMDArrayRelease() */
12438 : /************************************************************************/
12439 :
12440 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12441 : *
12442 : * Note: when applied on a object coming from a driver, this does not
12443 : * destroy the object in the file, database, etc...
12444 : */
12445 3071 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12446 : {
12447 3071 : delete hMDArray;
12448 3071 : }
12449 :
12450 : /************************************************************************/
12451 : /* GDALMDArrayGetName() */
12452 : /************************************************************************/
12453 :
12454 : /** Return array name.
12455 : *
12456 : * This is the same as the C++ method GDALMDArray::GetName()
12457 : */
12458 84 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12459 : {
12460 84 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12461 84 : return hArray->m_poImpl->GetName().c_str();
12462 : }
12463 :
12464 : /************************************************************************/
12465 : /* GDALMDArrayGetFullName() */
12466 : /************************************************************************/
12467 :
12468 : /** Return array full name.
12469 : *
12470 : * This is the same as the C++ method GDALMDArray::GetFullName()
12471 : */
12472 71 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12473 : {
12474 71 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12475 71 : return hArray->m_poImpl->GetFullName().c_str();
12476 : }
12477 :
12478 : /************************************************************************/
12479 : /* GDALMDArrayGetName() */
12480 : /************************************************************************/
12481 :
12482 : /** Return the total number of values in the array.
12483 : *
12484 : * This is the same as the C++ method
12485 : * GDALAbstractMDArray::GetTotalElementsCount()
12486 : */
12487 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12488 : {
12489 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12490 6 : return hArray->m_poImpl->GetTotalElementsCount();
12491 : }
12492 :
12493 : /************************************************************************/
12494 : /* GDALMDArrayGetDimensionCount() */
12495 : /************************************************************************/
12496 :
12497 : /** Return the number of dimensions.
12498 : *
12499 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12500 : */
12501 16438 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12502 : {
12503 16438 : VALIDATE_POINTER1(hArray, __func__, 0);
12504 16438 : return hArray->m_poImpl->GetDimensionCount();
12505 : }
12506 :
12507 : /************************************************************************/
12508 : /* GDALMDArrayGetDimensions() */
12509 : /************************************************************************/
12510 :
12511 : /** Return the dimensions of the array
12512 : *
12513 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12514 : * array itself needs to be freed, CPLFree() should be called (and
12515 : * GDALDimensionRelease() on individual array members).
12516 : *
12517 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12518 : *
12519 : * @param hArray Array.
12520 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12521 : *
12522 : * @return an array of *pnCount dimensions.
12523 : */
12524 3505 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12525 : {
12526 3505 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12527 3505 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12528 3505 : const auto &dims(hArray->m_poImpl->GetDimensions());
12529 : auto ret = static_cast<GDALDimensionH *>(
12530 3505 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12531 10064 : for (size_t i = 0; i < dims.size(); i++)
12532 : {
12533 6559 : ret[i] = new GDALDimensionHS(dims[i]);
12534 : }
12535 3505 : *pnCount = dims.size();
12536 3505 : return ret;
12537 : }
12538 :
12539 : /************************************************************************/
12540 : /* GDALReleaseDimensions() */
12541 : /************************************************************************/
12542 :
12543 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12544 : *
12545 : * @param dims return pointer of above methods
12546 : * @param nCount *pnCount value returned by above methods
12547 : */
12548 3578 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12549 : {
12550 10294 : for (size_t i = 0; i < nCount; i++)
12551 : {
12552 6716 : delete dims[i];
12553 : }
12554 3578 : CPLFree(dims);
12555 3578 : }
12556 :
12557 : /************************************************************************/
12558 : /* GDALMDArrayGetDataType() */
12559 : /************************************************************************/
12560 :
12561 : /** Return the data type
12562 : *
12563 : * The return must be freed with GDALExtendedDataTypeRelease().
12564 : */
12565 6224 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12566 : {
12567 6224 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12568 : return new GDALExtendedDataTypeHS(
12569 6224 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12570 : }
12571 :
12572 : /************************************************************************/
12573 : /* GDALMDArrayRead() */
12574 : /************************************************************************/
12575 :
12576 : /** Read part or totality of a multidimensional array.
12577 : *
12578 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12579 : *
12580 : * @return TRUE in case of success.
12581 : */
12582 3029 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12583 : const size_t *count, const GInt64 *arrayStep,
12584 : const GPtrDiff_t *bufferStride,
12585 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12586 : const void *pDstBufferAllocStart,
12587 : size_t nDstBufferAllocSize)
12588 : {
12589 3029 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12590 3029 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12591 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12592 : {
12593 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12594 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12595 : }
12596 3029 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12597 3029 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12598 6058 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12599 3029 : *(bufferDataType->m_poImpl), pDstBuffer,
12600 3029 : pDstBufferAllocStart, nDstBufferAllocSize);
12601 : }
12602 :
12603 : /************************************************************************/
12604 : /* GDALMDArrayWrite() */
12605 : /************************************************************************/
12606 :
12607 : /** Write part or totality of a multidimensional array.
12608 : *
12609 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12610 : *
12611 : * @return TRUE in case of success.
12612 : */
12613 892 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12614 : const size_t *count, const GInt64 *arrayStep,
12615 : const GPtrDiff_t *bufferStride,
12616 : GDALExtendedDataTypeH bufferDataType,
12617 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12618 : size_t nSrcBufferAllocSize)
12619 : {
12620 892 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12621 892 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12622 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12623 : {
12624 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12625 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12626 : }
12627 892 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12628 892 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12629 1784 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12630 892 : bufferStride, *(bufferDataType->m_poImpl),
12631 : pSrcBuffer, pSrcBufferAllocStart,
12632 892 : nSrcBufferAllocSize);
12633 : }
12634 :
12635 : /************************************************************************/
12636 : /* GDALMDArrayAdviseRead() */
12637 : /************************************************************************/
12638 :
12639 : /** Advise driver of upcoming read requests.
12640 : *
12641 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12642 : *
12643 : * @return TRUE in case of success.
12644 : *
12645 : * @since GDAL 3.2
12646 : */
12647 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12648 : const size_t *count)
12649 : {
12650 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12651 : }
12652 :
12653 : /************************************************************************/
12654 : /* GDALMDArrayAdviseReadEx() */
12655 : /************************************************************************/
12656 :
12657 : /** Advise driver of upcoming read requests.
12658 : *
12659 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12660 : *
12661 : * @return TRUE in case of success.
12662 : *
12663 : * @since GDAL 3.4
12664 : */
12665 19 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12666 : const size_t *count, CSLConstList papszOptions)
12667 : {
12668 19 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12669 19 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12670 : }
12671 :
12672 : /************************************************************************/
12673 : /* GDALMDArrayGetAttribute() */
12674 : /************************************************************************/
12675 :
12676 : /** Return an attribute by its name.
12677 : *
12678 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12679 : *
12680 : * The returned attribute must be freed with GDALAttributeRelease().
12681 : */
12682 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12683 : {
12684 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12685 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12686 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12687 120 : if (attr)
12688 111 : return new GDALAttributeHS(attr);
12689 9 : return nullptr;
12690 : }
12691 :
12692 : /************************************************************************/
12693 : /* GDALMDArrayGetAttributes() */
12694 : /************************************************************************/
12695 :
12696 : /** Return the list of attributes contained in this array.
12697 : *
12698 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12699 : * array itself needs to be freed, CPLFree() should be called (and
12700 : * GDALAttributeRelease() on individual array members).
12701 : *
12702 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12703 : *
12704 : * @param hArray Array.
12705 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12706 : * @param papszOptions Driver specific options determining how attributes
12707 : * should be retrieved. Pass nullptr for default behavior.
12708 : *
12709 : * @return an array of *pnCount attributes.
12710 : */
12711 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12712 : CSLConstList papszOptions)
12713 : {
12714 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12715 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12716 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12717 : auto ret = static_cast<GDALAttributeH *>(
12718 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12719 189 : for (size_t i = 0; i < attrs.size(); i++)
12720 : {
12721 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12722 : }
12723 59 : *pnCount = attrs.size();
12724 59 : return ret;
12725 : }
12726 :
12727 : /************************************************************************/
12728 : /* GDALMDArrayCreateAttribute() */
12729 : /************************************************************************/
12730 :
12731 : /** Create a attribute within an array.
12732 : *
12733 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12734 : *
12735 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12736 : */
12737 188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12738 : const char *pszName,
12739 : size_t nDimensions,
12740 : const GUInt64 *panDimensions,
12741 : GDALExtendedDataTypeH hEDT,
12742 : CSLConstList papszOptions)
12743 : {
12744 188 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12745 188 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12746 188 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12747 376 : std::vector<GUInt64> dims;
12748 188 : dims.reserve(nDimensions);
12749 249 : for (size_t i = 0; i < nDimensions; i++)
12750 61 : dims.push_back(panDimensions[i]);
12751 188 : auto ret = hArray->m_poImpl->CreateAttribute(
12752 564 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12753 188 : if (!ret)
12754 9 : return nullptr;
12755 179 : return new GDALAttributeHS(ret);
12756 : }
12757 :
12758 : /************************************************************************/
12759 : /* GDALMDArrayDeleteAttribute() */
12760 : /************************************************************************/
12761 :
12762 : /** Delete an attribute from an array.
12763 : *
12764 : * After this call, if a previously obtained instance of the deleted object
12765 : * is still alive, no method other than for freeing it should be invoked.
12766 : *
12767 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12768 : *
12769 : * @return true in case of success.
12770 : * @since GDAL 3.8
12771 : */
12772 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12773 : CSLConstList papszOptions)
12774 : {
12775 24 : VALIDATE_POINTER1(hArray, __func__, false);
12776 24 : VALIDATE_POINTER1(pszName, __func__, false);
12777 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12778 24 : papszOptions);
12779 : }
12780 :
12781 : /************************************************************************/
12782 : /* GDALMDArrayGetRawNoDataValue() */
12783 : /************************************************************************/
12784 :
12785 : /** Return the nodata value as a "raw" value.
12786 : *
12787 : * The value returned might be nullptr in case of no nodata value. When
12788 : * a nodata value is registered, a non-nullptr will be returned whose size in
12789 : * bytes is GetDataType().GetSize().
12790 : *
12791 : * The returned value should not be modified or freed.
12792 : *
12793 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12794 : *
12795 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12796 : */
12797 79 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12798 : {
12799 79 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12800 79 : return hArray->m_poImpl->GetRawNoDataValue();
12801 : }
12802 :
12803 : /************************************************************************/
12804 : /* GDALMDArrayGetNoDataValueAsDouble() */
12805 : /************************************************************************/
12806 :
12807 : /** Return the nodata value as a double.
12808 : *
12809 : * The value returned might be nullptr in case of no nodata value. When
12810 : * a nodata value is registered, a non-nullptr will be returned whose size in
12811 : * bytes is GetDataType().GetSize().
12812 : *
12813 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12814 : *
12815 : * @param hArray Array handle.
12816 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12817 : * if a nodata value exists and can be converted to double. Might be nullptr.
12818 : *
12819 : * @return the nodata value as a double. A 0.0 value might also indicate the
12820 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12821 : * will be set to false then).
12822 : */
12823 124 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12824 : int *pbHasNoDataValue)
12825 : {
12826 124 : VALIDATE_POINTER1(hArray, __func__, 0);
12827 124 : bool bHasNodataValue = false;
12828 124 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12829 124 : if (pbHasNoDataValue)
12830 124 : *pbHasNoDataValue = bHasNodataValue;
12831 124 : return ret;
12832 : }
12833 :
12834 : /************************************************************************/
12835 : /* GDALMDArrayGetNoDataValueAsInt64() */
12836 : /************************************************************************/
12837 :
12838 : /** Return the nodata value as a Int64.
12839 : *
12840 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12841 : *
12842 : * @param hArray Array handle.
12843 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12844 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12845 : *
12846 : * @return the nodata value as a Int64.
12847 : * @since GDAL 3.5
12848 : */
12849 13 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12850 : int *pbHasNoDataValue)
12851 : {
12852 13 : VALIDATE_POINTER1(hArray, __func__, 0);
12853 13 : bool bHasNodataValue = false;
12854 13 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12855 13 : if (pbHasNoDataValue)
12856 13 : *pbHasNoDataValue = bHasNodataValue;
12857 13 : return ret;
12858 : }
12859 :
12860 : /************************************************************************/
12861 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12862 : /************************************************************************/
12863 :
12864 : /** Return the nodata value as a UInt64.
12865 : *
12866 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12867 : *
12868 : * @param hArray Array handle.
12869 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12870 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12871 : *
12872 : * @return the nodata value as a UInt64.
12873 : * @since GDAL 3.5
12874 : */
12875 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12876 : int *pbHasNoDataValue)
12877 : {
12878 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12879 7 : bool bHasNodataValue = false;
12880 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12881 7 : if (pbHasNoDataValue)
12882 7 : *pbHasNoDataValue = bHasNodataValue;
12883 7 : return ret;
12884 : }
12885 :
12886 : /************************************************************************/
12887 : /* GDALMDArraySetRawNoDataValue() */
12888 : /************************************************************************/
12889 :
12890 : /** Set the nodata value as a "raw" value.
12891 : *
12892 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12893 : * void*).
12894 : *
12895 : * @return TRUE in case of success.
12896 : */
12897 15 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12898 : {
12899 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12900 15 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12901 : }
12902 :
12903 : /************************************************************************/
12904 : /* GDALMDArraySetNoDataValueAsDouble() */
12905 : /************************************************************************/
12906 :
12907 : /** Set the nodata value as a double.
12908 : *
12909 : * If the natural data type of the attribute/array is not double, type
12910 : * conversion will occur to the type returned by GetDataType().
12911 : *
12912 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12913 : *
12914 : * @return TRUE in case of success.
12915 : */
12916 58 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12917 : {
12918 58 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12919 58 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12920 : }
12921 :
12922 : /************************************************************************/
12923 : /* GDALMDArraySetNoDataValueAsInt64() */
12924 : /************************************************************************/
12925 :
12926 : /** Set the nodata value as a Int64.
12927 : *
12928 : * If the natural data type of the attribute/array is not Int64, type conversion
12929 : * will occur to the type returned by GetDataType().
12930 : *
12931 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12932 : *
12933 : * @return TRUE in case of success.
12934 : * @since GDAL 3.5
12935 : */
12936 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12937 : {
12938 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12939 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12940 : }
12941 :
12942 : /************************************************************************/
12943 : /* GDALMDArraySetNoDataValueAsUInt64() */
12944 : /************************************************************************/
12945 :
12946 : /** Set the nodata value as a UInt64.
12947 : *
12948 : * If the natural data type of the attribute/array is not UInt64, type
12949 : * conversion will occur to the type returned by GetDataType().
12950 : *
12951 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12952 : *
12953 : * @return TRUE in case of success.
12954 : * @since GDAL 3.5
12955 : */
12956 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12957 : uint64_t nNoDataValue)
12958 : {
12959 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12960 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12961 : }
12962 :
12963 : /************************************************************************/
12964 : /* GDALMDArrayResize() */
12965 : /************************************************************************/
12966 :
12967 : /** Resize an array to new dimensions.
12968 : *
12969 : * Not all drivers may allow this operation, and with restrictions (e.g.
12970 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12971 : *
12972 : * Resizing a dimension used in other arrays will cause those other arrays
12973 : * to be resized.
12974 : *
12975 : * This is the same as the C++ method GDALMDArray::Resize().
12976 : *
12977 : * @param hArray Array.
12978 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12979 : * new size of each indexing dimension.
12980 : * @param papszOptions Options. (Driver specific)
12981 : * @return true in case of success.
12982 : * @since GDAL 3.7
12983 : */
12984 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12985 : CSLConstList papszOptions)
12986 : {
12987 42 : VALIDATE_POINTER1(hArray, __func__, false);
12988 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12989 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12990 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12991 : {
12992 83 : anNewDimSizes[i] = panNewDimSizes[i];
12993 : }
12994 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12995 : }
12996 :
12997 : /************************************************************************/
12998 : /* GDALMDArraySetScale() */
12999 : /************************************************************************/
13000 :
13001 : /** Set the scale value to apply to raw values.
13002 : *
13003 : * unscaled_value = raw_value * GetScale() + GetOffset()
13004 : *
13005 : * This is the same as the C++ method GDALMDArray::SetScale().
13006 : *
13007 : * @return TRUE in case of success.
13008 : */
13009 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
13010 : {
13011 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13012 0 : return hArray->m_poImpl->SetScale(dfScale);
13013 : }
13014 :
13015 : /************************************************************************/
13016 : /* GDALMDArraySetScaleEx() */
13017 : /************************************************************************/
13018 :
13019 : /** Set the scale value to apply to raw values.
13020 : *
13021 : * unscaled_value = raw_value * GetScale() + GetOffset()
13022 : *
13023 : * This is the same as the C++ method GDALMDArray::SetScale().
13024 : *
13025 : * @return TRUE in case of success.
13026 : * @since GDAL 3.3
13027 : */
13028 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
13029 : GDALDataType eStorageType)
13030 : {
13031 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13032 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
13033 : }
13034 :
13035 : /************************************************************************/
13036 : /* GDALMDArraySetOffset() */
13037 : /************************************************************************/
13038 :
13039 : /** Set the scale value to apply to raw values.
13040 : *
13041 : * unscaled_value = raw_value * GetScale() + GetOffset()
13042 : *
13043 : * This is the same as the C++ method GDALMDArray::SetOffset().
13044 : *
13045 : * @return TRUE in case of success.
13046 : */
13047 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
13048 : {
13049 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13050 0 : return hArray->m_poImpl->SetOffset(dfOffset);
13051 : }
13052 :
13053 : /************************************************************************/
13054 : /* GDALMDArraySetOffsetEx() */
13055 : /************************************************************************/
13056 :
13057 : /** Set the scale value to apply to raw values.
13058 : *
13059 : * unscaled_value = raw_value * GetOffset() + GetOffset()
13060 : *
13061 : * This is the same as the C++ method GDALMDArray::SetOffset().
13062 : *
13063 : * @return TRUE in case of success.
13064 : * @since GDAL 3.3
13065 : */
13066 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
13067 : GDALDataType eStorageType)
13068 : {
13069 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13070 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
13071 : }
13072 :
13073 : /************************************************************************/
13074 : /* GDALMDArrayGetScale() */
13075 : /************************************************************************/
13076 :
13077 : /** Get the scale value to apply to raw values.
13078 : *
13079 : * unscaled_value = raw_value * GetScale() + GetOffset()
13080 : *
13081 : * This is the same as the C++ method GDALMDArray::GetScale().
13082 : *
13083 : * @return the scale value
13084 : */
13085 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
13086 : {
13087 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13088 105 : bool bHasValue = false;
13089 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
13090 105 : if (pbHasValue)
13091 105 : *pbHasValue = bHasValue;
13092 105 : return dfRet;
13093 : }
13094 :
13095 : /************************************************************************/
13096 : /* GDALMDArrayGetScaleEx() */
13097 : /************************************************************************/
13098 :
13099 : /** Get the scale value to apply to raw values.
13100 : *
13101 : * unscaled_value = raw_value * GetScale() + GetScale()
13102 : *
13103 : * This is the same as the C++ method GDALMDArray::GetScale().
13104 : *
13105 : * @return the scale value
13106 : * @since GDAL 3.3
13107 : */
13108 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
13109 : GDALDataType *peStorageType)
13110 : {
13111 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13112 5 : bool bHasValue = false;
13113 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
13114 5 : if (pbHasValue)
13115 5 : *pbHasValue = bHasValue;
13116 5 : return dfRet;
13117 : }
13118 :
13119 : /************************************************************************/
13120 : /* GDALMDArrayGetOffset() */
13121 : /************************************************************************/
13122 :
13123 : /** Get the scale value to apply to raw values.
13124 : *
13125 : * unscaled_value = raw_value * GetScale() + GetOffset()
13126 : *
13127 : * This is the same as the C++ method GDALMDArray::GetOffset().
13128 : *
13129 : * @return the scale value
13130 : */
13131 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
13132 : {
13133 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13134 102 : bool bHasValue = false;
13135 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
13136 102 : if (pbHasValue)
13137 102 : *pbHasValue = bHasValue;
13138 102 : return dfRet;
13139 : }
13140 :
13141 : /************************************************************************/
13142 : /* GDALMDArrayGetOffsetEx() */
13143 : /************************************************************************/
13144 :
13145 : /** Get the scale value to apply to raw values.
13146 : *
13147 : * unscaled_value = raw_value * GetScale() + GetOffset()
13148 : *
13149 : * This is the same as the C++ method GDALMDArray::GetOffset().
13150 : *
13151 : * @return the scale value
13152 : * @since GDAL 3.3
13153 : */
13154 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
13155 : GDALDataType *peStorageType)
13156 : {
13157 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13158 5 : bool bHasValue = false;
13159 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
13160 5 : if (pbHasValue)
13161 5 : *pbHasValue = bHasValue;
13162 5 : return dfRet;
13163 : }
13164 :
13165 : /************************************************************************/
13166 : /* GDALMDArrayGetBlockSize() */
13167 : /************************************************************************/
13168 :
13169 : /** Return the "natural" block size of the array along all dimensions.
13170 : *
13171 : * Some drivers might organize the array in tiles/blocks and reading/writing
13172 : * aligned on those tile/block boundaries will be more efficient.
13173 : *
13174 : * The returned number of elements in the vector is the same as
13175 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
13176 : * the natural block size along the considered dimension.
13177 : * "Flat" arrays will typically return a vector of values set to 0.
13178 : *
13179 : * The default implementation will return a vector of values set to 0.
13180 : *
13181 : * This method is used by GetProcessingChunkSize().
13182 : *
13183 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
13184 : * theoretical case of a 32-bit platform, this might exceed its size_t
13185 : * allocation capabilities.
13186 : *
13187 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
13188 : *
13189 : * @return the block size, in number of elements along each dimension.
13190 : */
13191 110 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
13192 : {
13193 110 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13194 110 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13195 110 : auto res = hArray->m_poImpl->GetBlockSize();
13196 110 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
13197 339 : for (size_t i = 0; i < res.size(); i++)
13198 : {
13199 229 : ret[i] = res[i];
13200 : }
13201 110 : *pnCount = res.size();
13202 110 : return ret;
13203 : }
13204 :
13205 : /************************************************************************/
13206 : /* GDALMDArrayGetProcessingChunkSize() */
13207 : /************************************************************************/
13208 :
13209 : /** \brief Return an optimal chunk size for read/write operations, given the
13210 : * natural block size and memory constraints specified.
13211 : *
13212 : * This method will use GetBlockSize() to define a chunk whose dimensions are
13213 : * multiple of those returned by GetBlockSize() (unless the block define by
13214 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
13215 : * returned by this method).
13216 : *
13217 : * This is the same as the C++ method
13218 : * GDALAbstractMDArray::GetProcessingChunkSize().
13219 : *
13220 : * @param hArray Array.
13221 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13222 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
13223 : * chunk.
13224 : *
13225 : * @return the chunk size, in number of elements along each dimension.
13226 : */
13227 :
13228 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
13229 : size_t nMaxChunkMemory)
13230 : {
13231 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13232 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13233 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
13234 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
13235 3 : for (size_t i = 0; i < res.size(); i++)
13236 : {
13237 2 : ret[i] = res[i];
13238 : }
13239 1 : *pnCount = res.size();
13240 1 : return ret;
13241 : }
13242 :
13243 : /************************************************************************/
13244 : /* GDALMDArrayGetStructuralInfo() */
13245 : /************************************************************************/
13246 :
13247 : /** Return structural information on the array.
13248 : *
13249 : * This may be the compression, etc..
13250 : *
13251 : * The return value should not be freed and is valid until GDALMDArray is
13252 : * released or this function called again.
13253 : *
13254 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
13255 : */
13256 17 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13257 : {
13258 17 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13259 17 : return hArray->m_poImpl->GetStructuralInfo();
13260 : }
13261 :
13262 : /************************************************************************/
13263 : /* GDALMDArrayGetView() */
13264 : /************************************************************************/
13265 :
13266 : /** Return a view of the array using slicing or field access.
13267 : *
13268 : * The returned object should be released with GDALMDArrayRelease().
13269 : *
13270 : * This is the same as the C++ method GDALMDArray::GetView().
13271 : */
13272 438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13273 : {
13274 438 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13275 438 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13276 1314 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13277 438 : if (!sliced)
13278 23 : return nullptr;
13279 415 : return new GDALMDArrayHS(sliced);
13280 : }
13281 :
13282 : /************************************************************************/
13283 : /* GDALMDArrayTranspose() */
13284 : /************************************************************************/
13285 :
13286 : /** Return a view of the array whose axis have been reordered.
13287 : *
13288 : * The returned object should be released with GDALMDArrayRelease().
13289 : *
13290 : * This is the same as the C++ method GDALMDArray::Transpose().
13291 : */
13292 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13293 : const int *panMapNewAxisToOldAxis)
13294 : {
13295 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13296 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13297 44 : if (nNewAxisCount)
13298 : {
13299 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13300 : nNewAxisCount * sizeof(int));
13301 : }
13302 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13303 44 : if (!reordered)
13304 7 : return nullptr;
13305 37 : return new GDALMDArrayHS(reordered);
13306 : }
13307 :
13308 : /************************************************************************/
13309 : /* GDALMDArrayGetUnscaled() */
13310 : /************************************************************************/
13311 :
13312 : /** Return an array that is the unscaled version of the current one.
13313 : *
13314 : * That is each value of the unscaled array will be
13315 : * unscaled_value = raw_value * GetScale() + GetOffset()
13316 : *
13317 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13318 : * from unscaled values to raw values.
13319 : *
13320 : * The returned object should be released with GDALMDArrayRelease().
13321 : *
13322 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13323 : */
13324 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13325 : {
13326 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13327 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13328 13 : if (!unscaled)
13329 0 : return nullptr;
13330 13 : return new GDALMDArrayHS(unscaled);
13331 : }
13332 :
13333 : /************************************************************************/
13334 : /* GDALMDArrayGetMask() */
13335 : /************************************************************************/
13336 :
13337 : /** Return an array that is a mask for the current array
13338 : *
13339 : * This array will be of type Byte, with values set to 0 to indicate invalid
13340 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13341 : *
13342 : * The returned object should be released with GDALMDArrayRelease().
13343 : *
13344 : * This is the same as the C++ method GDALMDArray::GetMask().
13345 : */
13346 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13347 : {
13348 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13349 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13350 35 : if (!unscaled)
13351 7 : return nullptr;
13352 28 : return new GDALMDArrayHS(unscaled);
13353 : }
13354 :
13355 : /************************************************************************/
13356 : /* GDALMDArrayGetResampled() */
13357 : /************************************************************************/
13358 :
13359 : /** Return an array that is a resampled / reprojected view of the current array
13360 : *
13361 : * This is the same as the C++ method GDALMDArray::GetResampled().
13362 : *
13363 : * Currently this method can only resample along the last 2 dimensions, unless
13364 : * orthorectifying a NASA EMIT dataset.
13365 : *
13366 : * The returned object should be released with GDALMDArrayRelease().
13367 : *
13368 : * @since 3.4
13369 : */
13370 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13371 : const GDALDimensionH *pahNewDims,
13372 : GDALRIOResampleAlg resampleAlg,
13373 : OGRSpatialReferenceH hTargetSRS,
13374 : CSLConstList papszOptions)
13375 : {
13376 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13377 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13378 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13379 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13380 : {
13381 78 : if (pahNewDims[i])
13382 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13383 : }
13384 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13385 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13386 68 : papszOptions);
13387 34 : if (!poNewArray)
13388 8 : return nullptr;
13389 26 : return new GDALMDArrayHS(poNewArray);
13390 : }
13391 :
13392 : /************************************************************************/
13393 : /* GDALMDArraySetUnit() */
13394 : /************************************************************************/
13395 :
13396 : /** Set the variable unit.
13397 : *
13398 : * Values should conform as much as possible with those allowed by
13399 : * the NetCDF CF conventions:
13400 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13401 : * but others might be returned.
13402 : *
13403 : * Few examples are "meter", "degrees", "second", ...
13404 : * Empty value means unknown.
13405 : *
13406 : * This is the same as the C function GDALMDArraySetUnit()
13407 : *
13408 : * @param hArray array.
13409 : * @param pszUnit unit name.
13410 : * @return TRUE in case of success.
13411 : */
13412 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13413 : {
13414 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13415 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13416 : }
13417 :
13418 : /************************************************************************/
13419 : /* GDALMDArrayGetUnit() */
13420 : /************************************************************************/
13421 :
13422 : /** Return the array unit.
13423 : *
13424 : * Values should conform as much as possible with those allowed by
13425 : * the NetCDF CF conventions:
13426 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13427 : * but others might be returned.
13428 : *
13429 : * Few examples are "meter", "degrees", "second", ...
13430 : * Empty value means unknown.
13431 : *
13432 : * The return value should not be freed and is valid until GDALMDArray is
13433 : * released or this function called again.
13434 : *
13435 : * This is the same as the C++ method GDALMDArray::GetUnit().
13436 : */
13437 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13438 : {
13439 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13440 113 : return hArray->m_poImpl->GetUnit().c_str();
13441 : }
13442 :
13443 : /************************************************************************/
13444 : /* GDALMDArrayGetSpatialRef() */
13445 : /************************************************************************/
13446 :
13447 : /** Assign a spatial reference system object to the array.
13448 : *
13449 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13450 : * @return TRUE in case of success.
13451 : */
13452 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13453 : {
13454 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13455 60 : return hArray->m_poImpl->SetSpatialRef(
13456 60 : OGRSpatialReference::FromHandle(hSRS));
13457 : }
13458 :
13459 : /************************************************************************/
13460 : /* GDALMDArrayGetSpatialRef() */
13461 : /************************************************************************/
13462 :
13463 : /** Return the spatial reference system object associated with the array.
13464 : *
13465 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13466 : *
13467 : * The returned object must be freed with OSRDestroySpatialReference().
13468 : */
13469 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13470 : {
13471 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13472 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13473 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13474 : }
13475 :
13476 : /************************************************************************/
13477 : /* GDALMDArrayGetStatistics() */
13478 : /************************************************************************/
13479 :
13480 : /**
13481 : * \brief Fetch statistics.
13482 : *
13483 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13484 : *
13485 : * @since GDAL 3.2
13486 : */
13487 :
13488 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13489 : int bApproxOK, int bForce, double *pdfMin,
13490 : double *pdfMax, double *pdfMean,
13491 : double *pdfStdDev, GUInt64 *pnValidCount,
13492 : GDALProgressFunc pfnProgress,
13493 : void *pProgressData)
13494 : {
13495 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13496 30 : return hArray->m_poImpl->GetStatistics(
13497 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13498 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13499 : }
13500 :
13501 : /************************************************************************/
13502 : /* GDALMDArrayComputeStatistics() */
13503 : /************************************************************************/
13504 :
13505 : /**
13506 : * \brief Compute statistics.
13507 : *
13508 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13509 : *
13510 : * @since GDAL 3.2
13511 : * @see GDALMDArrayComputeStatisticsEx()
13512 : */
13513 :
13514 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13515 : int bApproxOK, double *pdfMin, double *pdfMax,
13516 : double *pdfMean, double *pdfStdDev,
13517 : GUInt64 *pnValidCount,
13518 : GDALProgressFunc pfnProgress,
13519 : void *pProgressData)
13520 : {
13521 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13522 0 : return hArray->m_poImpl->ComputeStatistics(
13523 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13524 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13525 : }
13526 :
13527 : /************************************************************************/
13528 : /* GDALMDArrayComputeStatisticsEx() */
13529 : /************************************************************************/
13530 :
13531 : /**
13532 : * \brief Compute statistics.
13533 : *
13534 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13535 : *
13536 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13537 : *
13538 : * @since GDAL 3.8
13539 : */
13540 :
13541 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13542 : int bApproxOK, double *pdfMin,
13543 : double *pdfMax, double *pdfMean,
13544 : double *pdfStdDev, GUInt64 *pnValidCount,
13545 : GDALProgressFunc pfnProgress,
13546 : void *pProgressData,
13547 : CSLConstList papszOptions)
13548 : {
13549 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13550 8 : return hArray->m_poImpl->ComputeStatistics(
13551 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13552 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13553 : }
13554 :
13555 : /************************************************************************/
13556 : /* GDALMDArrayGetCoordinateVariables() */
13557 : /************************************************************************/
13558 :
13559 : /** Return coordinate variables.
13560 : *
13561 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13562 : * itself needs to be freed, CPLFree() should be called (and
13563 : * GDALMDArrayRelease() on individual array members).
13564 : *
13565 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13566 : *
13567 : * @param hArray Array.
13568 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13569 : *
13570 : * @return an array of *pnCount arrays.
13571 : * @since 3.4
13572 : */
13573 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13574 : size_t *pnCount)
13575 : {
13576 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13577 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13578 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13579 : auto ret = static_cast<GDALMDArrayH *>(
13580 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13581 29 : for (size_t i = 0; i < coordinates.size(); i++)
13582 : {
13583 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13584 : }
13585 13 : *pnCount = coordinates.size();
13586 13 : return ret;
13587 : }
13588 :
13589 : /************************************************************************/
13590 : /* GDALMDArrayGetGridded() */
13591 : /************************************************************************/
13592 :
13593 : /** Return a gridded array from scattered point data, that is from an array
13594 : * whose last dimension is the indexing variable of X and Y arrays.
13595 : *
13596 : * The returned object should be released with GDALMDArrayRelease().
13597 : *
13598 : * This is the same as the C++ method GDALMDArray::GetGridded().
13599 : *
13600 : * @since GDAL 3.7
13601 : */
13602 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13603 : const char *pszGridOptions,
13604 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13605 : CSLConstList papszOptions)
13606 : {
13607 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13608 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13609 22 : auto gridded = hArray->m_poImpl->GetGridded(
13610 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13611 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13612 22 : if (!gridded)
13613 19 : return nullptr;
13614 3 : return new GDALMDArrayHS(gridded);
13615 : }
13616 :
13617 : /************************************************************************/
13618 : /* GDALMDArrayGetMeshGrid() */
13619 : /************************************************************************/
13620 :
13621 : /** Return a list of multidimensional arrays from a list of one-dimensional
13622 : * arrays.
13623 : *
13624 : * This is typically used to transform one-dimensional longitude, latitude
13625 : * arrays into 2D ones.
13626 : *
13627 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13628 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13629 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13630 : * repeated to fill the matrix along the first dimension for x1, the second
13631 : * for x2 and so on.
13632 : *
13633 : * For example, if x = [1, 2], and y = [3, 4, 5],
13634 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13635 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13636 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13637 : *
13638 : * and
13639 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13640 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13641 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13642 : *
13643 : * The currently supported options are:
13644 : * <ul>
13645 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13646 : * output.
13647 : * </li>
13648 : * </ul>
13649 : *
13650 : * This is the same as
13651 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13652 : * function.
13653 : *
13654 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13655 : * If only the array itself needs to be freed, CPLFree() should be called
13656 : * (and GDALMDArrayRelease() on individual array members).
13657 : *
13658 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13659 : *
13660 : * @param pahInputArrays Input arrays
13661 : * @param nCountInputArrays Number of input arrays
13662 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13663 : * @param papszOptions NULL, or NULL terminated list of options.
13664 : *
13665 : * @return an array of *pnCountOutputArrays arrays.
13666 : * @since 3.10
13667 : */
13668 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13669 : size_t nCountInputArrays,
13670 : size_t *pnCountOutputArrays,
13671 : CSLConstList papszOptions)
13672 : {
13673 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13674 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13675 :
13676 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13677 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13678 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13679 :
13680 : const auto apoOutputArrays =
13681 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13682 : auto ret = static_cast<GDALMDArrayH *>(
13683 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13684 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13685 : {
13686 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13687 : }
13688 7 : *pnCountOutputArrays = apoOutputArrays.size();
13689 7 : return ret;
13690 : }
13691 :
13692 : /************************************************************************/
13693 : /* GDALReleaseArrays() */
13694 : /************************************************************************/
13695 :
13696 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13697 : *
13698 : * @param arrays return pointer of above methods
13699 : * @param nCount *pnCount value returned by above methods
13700 : */
13701 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13702 : {
13703 46 : for (size_t i = 0; i < nCount; i++)
13704 : {
13705 26 : delete arrays[i];
13706 : }
13707 20 : CPLFree(arrays);
13708 20 : }
13709 :
13710 : /************************************************************************/
13711 : /* GDALMDArrayCache() */
13712 : /************************************************************************/
13713 :
13714 : /**
13715 : * \brief Cache the content of the array into an auxiliary filename.
13716 : *
13717 : * This is the same as the C++ method GDALMDArray::Cache().
13718 : *
13719 : * @since GDAL 3.4
13720 : */
13721 :
13722 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13723 : {
13724 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13725 7 : return hArray->m_poImpl->Cache(papszOptions);
13726 : }
13727 :
13728 : /************************************************************************/
13729 : /* GDALMDArrayRename() */
13730 : /************************************************************************/
13731 :
13732 : /** Rename the array.
13733 : *
13734 : * This is not implemented by all drivers.
13735 : *
13736 : * Drivers known to implement it: MEM, netCDF, Zarr.
13737 : *
13738 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13739 : *
13740 : * @return true in case of success
13741 : * @since GDAL 3.8
13742 : */
13743 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13744 : {
13745 28 : VALIDATE_POINTER1(hArray, __func__, false);
13746 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13747 28 : return hArray->m_poImpl->Rename(pszNewName);
13748 : }
13749 :
13750 : /************************************************************************/
13751 : /* GDALAttributeRelease() */
13752 : /************************************************************************/
13753 :
13754 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13755 : *
13756 : * Note: when applied on a object coming from a driver, this does not
13757 : * destroy the object in the file, database, etc...
13758 : */
13759 818 : void GDALAttributeRelease(GDALAttributeH hAttr)
13760 : {
13761 818 : delete hAttr;
13762 818 : }
13763 :
13764 : /************************************************************************/
13765 : /* GDALAttributeGetName() */
13766 : /************************************************************************/
13767 :
13768 : /** Return the name of the attribute.
13769 : *
13770 : * The returned pointer is valid until hAttr is released.
13771 : *
13772 : * This is the same as the C++ method GDALAttribute::GetName().
13773 : */
13774 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13775 : {
13776 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13777 361 : return hAttr->m_poImpl->GetName().c_str();
13778 : }
13779 :
13780 : /************************************************************************/
13781 : /* GDALAttributeGetFullName() */
13782 : /************************************************************************/
13783 :
13784 : /** Return the full name of the attribute.
13785 : *
13786 : * The returned pointer is valid until hAttr is released.
13787 : *
13788 : * This is the same as the C++ method GDALAttribute::GetFullName().
13789 : */
13790 50 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13791 : {
13792 50 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13793 50 : return hAttr->m_poImpl->GetFullName().c_str();
13794 : }
13795 :
13796 : /************************************************************************/
13797 : /* GDALAttributeGetTotalElementsCount() */
13798 : /************************************************************************/
13799 :
13800 : /** Return the total number of values in the attribute.
13801 : *
13802 : * This is the same as the C++ method
13803 : * GDALAbstractMDArray::GetTotalElementsCount()
13804 : */
13805 190 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13806 : {
13807 190 : VALIDATE_POINTER1(hAttr, __func__, 0);
13808 190 : return hAttr->m_poImpl->GetTotalElementsCount();
13809 : }
13810 :
13811 : /************************************************************************/
13812 : /* GDALAttributeGetDimensionCount() */
13813 : /************************************************************************/
13814 :
13815 : /** Return the number of dimensions.
13816 : *
13817 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13818 : */
13819 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13820 : {
13821 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13822 12 : return hAttr->m_poImpl->GetDimensionCount();
13823 : }
13824 :
13825 : /************************************************************************/
13826 : /* GDALAttributeGetDimensionsSize() */
13827 : /************************************************************************/
13828 :
13829 : /** Return the dimension sizes of the attribute.
13830 : *
13831 : * The returned array must be freed with CPLFree()
13832 : *
13833 : * @param hAttr Attribute.
13834 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13835 : *
13836 : * @return an array of *pnCount values.
13837 : */
13838 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13839 : {
13840 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13841 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13842 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13843 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13844 22 : for (size_t i = 0; i < dims.size(); i++)
13845 : {
13846 11 : ret[i] = dims[i]->GetSize();
13847 : }
13848 11 : *pnCount = dims.size();
13849 11 : return ret;
13850 : }
13851 :
13852 : /************************************************************************/
13853 : /* GDALAttributeGetDataType() */
13854 : /************************************************************************/
13855 :
13856 : /** Return the data type
13857 : *
13858 : * The return must be freed with GDALExtendedDataTypeRelease().
13859 : */
13860 483 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13861 : {
13862 483 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13863 : return new GDALExtendedDataTypeHS(
13864 483 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13865 : }
13866 :
13867 : /************************************************************************/
13868 : /* GDALAttributeReadAsRaw() */
13869 : /************************************************************************/
13870 :
13871 : /** Return the raw value of an attribute.
13872 : *
13873 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13874 : *
13875 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13876 : *
13877 : * @param hAttr Attribute.
13878 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13879 : *
13880 : * @return a buffer of *pnSize bytes.
13881 : */
13882 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13883 : {
13884 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13885 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13886 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13887 6 : *pnSize = res.size();
13888 6 : auto ret = res.StealData();
13889 6 : if (!ret)
13890 : {
13891 0 : *pnSize = 0;
13892 0 : return nullptr;
13893 : }
13894 6 : return ret;
13895 : }
13896 :
13897 : /************************************************************************/
13898 : /* GDALAttributeFreeRawResult() */
13899 : /************************************************************************/
13900 :
13901 : /** Free the return of GDALAttributeAsRaw()
13902 : */
13903 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13904 : CPL_UNUSED size_t nSize)
13905 : {
13906 6 : VALIDATE_POINTER0(hAttr, __func__);
13907 6 : if (raw)
13908 : {
13909 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13910 6 : const auto nDTSize(dt.GetSize());
13911 6 : GByte *pabyPtr = raw;
13912 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13913 6 : CPLAssert(nSize == nDTSize * nEltCount);
13914 12 : for (size_t i = 0; i < nEltCount; ++i)
13915 : {
13916 6 : dt.FreeDynamicMemory(pabyPtr);
13917 6 : pabyPtr += nDTSize;
13918 : }
13919 6 : CPLFree(raw);
13920 : }
13921 : }
13922 :
13923 : /************************************************************************/
13924 : /* GDALAttributeReadAsString() */
13925 : /************************************************************************/
13926 :
13927 : /** Return the value of an attribute as a string.
13928 : *
13929 : * The returned string should not be freed, and its lifetime does not
13930 : * excess a next call to ReadAsString() on the same object, or the deletion
13931 : * of the object itself.
13932 : *
13933 : * This function will only return the first element if there are several.
13934 : *
13935 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13936 : *
13937 : * @return a string, or nullptr.
13938 : */
13939 115 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13940 : {
13941 115 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13942 115 : return hAttr->m_poImpl->ReadAsString();
13943 : }
13944 :
13945 : /************************************************************************/
13946 : /* GDALAttributeReadAsInt() */
13947 : /************************************************************************/
13948 :
13949 : /** Return the value of an attribute as a integer.
13950 : *
13951 : * This function will only return the first element if there are several.
13952 : *
13953 : * It can fail if its value can not be converted to integer.
13954 : *
13955 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13956 : *
13957 : * @return a integer, or INT_MIN in case of error.
13958 : */
13959 25 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13960 : {
13961 25 : VALIDATE_POINTER1(hAttr, __func__, 0);
13962 25 : return hAttr->m_poImpl->ReadAsInt();
13963 : }
13964 :
13965 : /************************************************************************/
13966 : /* GDALAttributeReadAsInt64() */
13967 : /************************************************************************/
13968 :
13969 : /** Return the value of an attribute as a int64_t.
13970 : *
13971 : * This function will only return the first element if there are several.
13972 : *
13973 : * It can fail if its value can not be converted to integer.
13974 : *
13975 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13976 : *
13977 : * @return an int64_t, or INT64_MIN in case of error.
13978 : */
13979 27 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13980 : {
13981 27 : VALIDATE_POINTER1(hAttr, __func__, 0);
13982 27 : return hAttr->m_poImpl->ReadAsInt64();
13983 : }
13984 :
13985 : /************************************************************************/
13986 : /* GDALAttributeReadAsDouble() */
13987 : /************************************************************************/
13988 :
13989 : /** Return the value of an attribute as a double.
13990 : *
13991 : * This function will only return the first element if there are several.
13992 : *
13993 : * It can fail if its value can not be converted to double.
13994 : *
13995 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13996 : *
13997 : * @return a double value.
13998 : */
13999 55 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
14000 : {
14001 55 : VALIDATE_POINTER1(hAttr, __func__, 0);
14002 55 : return hAttr->m_poImpl->ReadAsDouble();
14003 : }
14004 :
14005 : /************************************************************************/
14006 : /* GDALAttributeReadAsStringArray() */
14007 : /************************************************************************/
14008 :
14009 : /** Return the value of an attribute as an array of strings.
14010 : *
14011 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
14012 : *
14013 : * The return value must be freed with CSLDestroy().
14014 : */
14015 22 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
14016 : {
14017 22 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14018 22 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
14019 : }
14020 :
14021 : /************************************************************************/
14022 : /* GDALAttributeReadAsIntArray() */
14023 : /************************************************************************/
14024 :
14025 : /** Return the value of an attribute as an array of integers.
14026 : *
14027 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
14028 : *
14029 : * @param hAttr Attribute
14030 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
14031 : * @return array to be freed with CPLFree(), or nullptr.
14032 : */
14033 18 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
14034 : {
14035 18 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14036 18 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
14037 18 : *pnCount = 0;
14038 36 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
14039 18 : if (tmp.empty())
14040 0 : return nullptr;
14041 18 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
14042 18 : if (!ret)
14043 0 : return nullptr;
14044 18 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
14045 18 : *pnCount = tmp.size();
14046 18 : return ret;
14047 : }
14048 :
14049 : /************************************************************************/
14050 : /* GDALAttributeReadAsInt64Array() */
14051 : /************************************************************************/
14052 :
14053 : /** Return the value of an attribute as an array of int64_t.
14054 : *
14055 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
14056 : *
14057 : * @param hAttr Attribute
14058 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
14059 : * @return array to be freed with CPLFree(), or nullptr.
14060 : */
14061 26 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
14062 : {
14063 26 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14064 26 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
14065 26 : *pnCount = 0;
14066 52 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
14067 26 : if (tmp.empty())
14068 0 : return nullptr;
14069 : auto ret = static_cast<int64_t *>(
14070 26 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
14071 26 : if (!ret)
14072 0 : return nullptr;
14073 26 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
14074 26 : *pnCount = tmp.size();
14075 26 : return ret;
14076 : }
14077 :
14078 : /************************************************************************/
14079 : /* GDALAttributeReadAsDoubleArray() */
14080 : /************************************************************************/
14081 :
14082 : /** Return the value of an attribute as an array of doubles.
14083 : *
14084 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
14085 : *
14086 : * @param hAttr Attribute
14087 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
14088 : * @return array to be freed with CPLFree(), or nullptr.
14089 : */
14090 44 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
14091 : {
14092 44 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14093 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
14094 44 : *pnCount = 0;
14095 88 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
14096 44 : if (tmp.empty())
14097 0 : return nullptr;
14098 : auto ret =
14099 44 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
14100 44 : if (!ret)
14101 0 : return nullptr;
14102 44 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
14103 44 : *pnCount = tmp.size();
14104 44 : return ret;
14105 : }
14106 :
14107 : /************************************************************************/
14108 : /* GDALAttributeWriteRaw() */
14109 : /************************************************************************/
14110 :
14111 : /** Write an attribute from raw values expressed in GetDataType()
14112 : *
14113 : * The values should be provided in the type of GetDataType() and there should
14114 : * be exactly GetTotalElementsCount() of them.
14115 : * If GetDataType() is a string, each value should be a char* pointer.
14116 : *
14117 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
14118 : *
14119 : * @param hAttr Attribute
14120 : * @param pabyValue Buffer of nLen bytes.
14121 : * @param nLength Size of pabyValue in bytes. Should be equal to
14122 : * GetTotalElementsCount() * GetDataType().GetSize()
14123 : * @return TRUE in case of success.
14124 : */
14125 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
14126 : size_t nLength)
14127 : {
14128 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14129 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
14130 : }
14131 :
14132 : /************************************************************************/
14133 : /* GDALAttributeWriteString() */
14134 : /************************************************************************/
14135 :
14136 : /** Write an attribute from a string value.
14137 : *
14138 : * Type conversion will be performed if needed. If the attribute contains
14139 : * multiple values, only the first one will be updated.
14140 : *
14141 : * This is the same as the C++ method GDALAttribute::Write(const char*)
14142 : *
14143 : * @param hAttr Attribute
14144 : * @param pszVal Pointer to a string.
14145 : * @return TRUE in case of success.
14146 : */
14147 210 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
14148 : {
14149 210 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14150 210 : return hAttr->m_poImpl->Write(pszVal);
14151 : }
14152 :
14153 : /************************************************************************/
14154 : /* GDALAttributeWriteInt() */
14155 : /************************************************************************/
14156 :
14157 : /** Write an attribute from a integer value.
14158 : *
14159 : * Type conversion will be performed if needed. If the attribute contains
14160 : * multiple values, only the first one will be updated.
14161 : *
14162 : * This is the same as the C++ method GDALAttribute::WriteInt()
14163 : *
14164 : * @param hAttr Attribute
14165 : * @param nVal Value.
14166 : * @return TRUE in case of success.
14167 : */
14168 23 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
14169 : {
14170 23 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14171 23 : return hAttr->m_poImpl->WriteInt(nVal);
14172 : }
14173 :
14174 : /************************************************************************/
14175 : /* GDALAttributeWriteInt64() */
14176 : /************************************************************************/
14177 :
14178 : /** Write an attribute from an int64_t value.
14179 : *
14180 : * Type conversion will be performed if needed. If the attribute contains
14181 : * multiple values, only the first one will be updated.
14182 : *
14183 : * This is the same as the C++ method GDALAttribute::WriteLong()
14184 : *
14185 : * @param hAttr Attribute
14186 : * @param nVal Value.
14187 : * @return TRUE in case of success.
14188 : */
14189 14 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
14190 : {
14191 14 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14192 14 : return hAttr->m_poImpl->WriteInt64(nVal);
14193 : }
14194 :
14195 : /************************************************************************/
14196 : /* GDALAttributeWriteDouble() */
14197 : /************************************************************************/
14198 :
14199 : /** Write an attribute from a double value.
14200 : *
14201 : * Type conversion will be performed if needed. If the attribute contains
14202 : * multiple values, only the first one will be updated.
14203 : *
14204 : * This is the same as the C++ method GDALAttribute::Write(double);
14205 : *
14206 : * @param hAttr Attribute
14207 : * @param dfVal Value.
14208 : *
14209 : * @return TRUE in case of success.
14210 : */
14211 12 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
14212 : {
14213 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14214 12 : return hAttr->m_poImpl->Write(dfVal);
14215 : }
14216 :
14217 : /************************************************************************/
14218 : /* GDALAttributeWriteStringArray() */
14219 : /************************************************************************/
14220 :
14221 : /** Write an attribute from an array of strings.
14222 : *
14223 : * Type conversion will be performed if needed.
14224 : *
14225 : * Exactly GetTotalElementsCount() strings must be provided
14226 : *
14227 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
14228 : *
14229 : * @param hAttr Attribute
14230 : * @param papszValues Array of strings.
14231 : * @return TRUE in case of success.
14232 : */
14233 9 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
14234 : CSLConstList papszValues)
14235 : {
14236 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14237 9 : return hAttr->m_poImpl->Write(papszValues);
14238 : }
14239 :
14240 : /************************************************************************/
14241 : /* GDALAttributeWriteIntArray() */
14242 : /************************************************************************/
14243 :
14244 : /** Write an attribute from an array of int.
14245 : *
14246 : * Type conversion will be performed if needed.
14247 : *
14248 : * Exactly GetTotalElementsCount() strings must be provided
14249 : *
14250 : * This is the same as the C++ method GDALAttribute::Write(const int *,
14251 : * size_t)
14252 : *
14253 : * @param hAttr Attribute
14254 : * @param panValues Array of int.
14255 : * @param nCount Should be equal to GetTotalElementsCount().
14256 : * @return TRUE in case of success.
14257 : */
14258 12 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14259 : size_t nCount)
14260 : {
14261 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14262 12 : return hAttr->m_poImpl->Write(panValues, nCount);
14263 : }
14264 :
14265 : /************************************************************************/
14266 : /* GDALAttributeWriteInt64Array() */
14267 : /************************************************************************/
14268 :
14269 : /** Write an attribute from an array of int64_t.
14270 : *
14271 : * Type conversion will be performed if needed.
14272 : *
14273 : * Exactly GetTotalElementsCount() strings must be provided
14274 : *
14275 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14276 : * size_t)
14277 : *
14278 : * @param hAttr Attribute
14279 : * @param panValues Array of int64_t.
14280 : * @param nCount Should be equal to GetTotalElementsCount().
14281 : * @return TRUE in case of success.
14282 : */
14283 13 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14284 : size_t nCount)
14285 : {
14286 13 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14287 13 : return hAttr->m_poImpl->Write(panValues, nCount);
14288 : }
14289 :
14290 : /************************************************************************/
14291 : /* GDALAttributeWriteDoubleArray() */
14292 : /************************************************************************/
14293 :
14294 : /** Write an attribute from an array of double.
14295 : *
14296 : * Type conversion will be performed if needed.
14297 : *
14298 : * Exactly GetTotalElementsCount() strings must be provided
14299 : *
14300 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14301 : * size_t)
14302 : *
14303 : * @param hAttr Attribute
14304 : * @param padfValues Array of double.
14305 : * @param nCount Should be equal to GetTotalElementsCount().
14306 : * @return TRUE in case of success.
14307 : */
14308 8 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14309 : const double *padfValues, size_t nCount)
14310 : {
14311 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14312 8 : return hAttr->m_poImpl->Write(padfValues, nCount);
14313 : }
14314 :
14315 : /************************************************************************/
14316 : /* GDALAttributeRename() */
14317 : /************************************************************************/
14318 :
14319 : /** Rename the attribute.
14320 : *
14321 : * This is not implemented by all drivers.
14322 : *
14323 : * Drivers known to implement it: MEM, netCDF.
14324 : *
14325 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14326 : *
14327 : * @return true in case of success
14328 : * @since GDAL 3.8
14329 : */
14330 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14331 : {
14332 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14333 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14334 27 : return hAttr->m_poImpl->Rename(pszNewName);
14335 : }
14336 :
14337 : /************************************************************************/
14338 : /* GDALDimensionRelease() */
14339 : /************************************************************************/
14340 :
14341 : /** Release the GDAL in-memory object associated with a GDALDimension.
14342 : *
14343 : * Note: when applied on a object coming from a driver, this does not
14344 : * destroy the object in the file, database, etc...
14345 : */
14346 7473 : void GDALDimensionRelease(GDALDimensionH hDim)
14347 : {
14348 7473 : delete hDim;
14349 7473 : }
14350 :
14351 : /************************************************************************/
14352 : /* GDALDimensionGetName() */
14353 : /************************************************************************/
14354 :
14355 : /** Return dimension name.
14356 : *
14357 : * This is the same as the C++ method GDALDimension::GetName()
14358 : */
14359 296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14360 : {
14361 296 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14362 296 : return hDim->m_poImpl->GetName().c_str();
14363 : }
14364 :
14365 : /************************************************************************/
14366 : /* GDALDimensionGetFullName() */
14367 : /************************************************************************/
14368 :
14369 : /** Return dimension full name.
14370 : *
14371 : * This is the same as the C++ method GDALDimension::GetFullName()
14372 : */
14373 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14374 : {
14375 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14376 82 : return hDim->m_poImpl->GetFullName().c_str();
14377 : }
14378 :
14379 : /************************************************************************/
14380 : /* GDALDimensionGetType() */
14381 : /************************************************************************/
14382 :
14383 : /** Return dimension type.
14384 : *
14385 : * This is the same as the C++ method GDALDimension::GetType()
14386 : */
14387 70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14388 : {
14389 70 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14390 70 : return hDim->m_poImpl->GetType().c_str();
14391 : }
14392 :
14393 : /************************************************************************/
14394 : /* GDALDimensionGetDirection() */
14395 : /************************************************************************/
14396 :
14397 : /** Return dimension direction.
14398 : *
14399 : * This is the same as the C++ method GDALDimension::GetDirection()
14400 : */
14401 38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14402 : {
14403 38 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14404 38 : return hDim->m_poImpl->GetDirection().c_str();
14405 : }
14406 :
14407 : /************************************************************************/
14408 : /* GDALDimensionGetSize() */
14409 : /************************************************************************/
14410 :
14411 : /** Return the size, that is the number of values along the dimension.
14412 : *
14413 : * This is the same as the C++ method GDALDimension::GetSize()
14414 : */
14415 6028 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14416 : {
14417 6028 : VALIDATE_POINTER1(hDim, __func__, 0);
14418 6028 : return hDim->m_poImpl->GetSize();
14419 : }
14420 :
14421 : /************************************************************************/
14422 : /* GDALDimensionGetIndexingVariable() */
14423 : /************************************************************************/
14424 :
14425 : /** Return the variable that is used to index the dimension (if there is one).
14426 : *
14427 : * This is the array, typically one-dimensional, describing the values taken
14428 : * by the dimension.
14429 : *
14430 : * The returned value should be freed with GDALMDArrayRelease().
14431 : *
14432 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14433 : */
14434 140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14435 : {
14436 140 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14437 280 : auto var(hDim->m_poImpl->GetIndexingVariable());
14438 140 : if (!var)
14439 11 : return nullptr;
14440 129 : return new GDALMDArrayHS(var);
14441 : }
14442 :
14443 : /************************************************************************/
14444 : /* GDALDimensionSetIndexingVariable() */
14445 : /************************************************************************/
14446 :
14447 : /** Set the variable that is used to index the dimension.
14448 : *
14449 : * This is the array, typically one-dimensional, describing the values taken
14450 : * by the dimension.
14451 : *
14452 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14453 : *
14454 : * @return TRUE in case of success.
14455 : */
14456 27 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14457 : {
14458 27 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14459 81 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14460 54 : : nullptr);
14461 : }
14462 :
14463 : /************************************************************************/
14464 : /* GDALDimensionRename() */
14465 : /************************************************************************/
14466 :
14467 : /** Rename the dimension.
14468 : *
14469 : * This is not implemented by all drivers.
14470 : *
14471 : * Drivers known to implement it: MEM, netCDF.
14472 : *
14473 : * This is the same as the C++ method GDALDimension::Rename()
14474 : *
14475 : * @return true in case of success
14476 : * @since GDAL 3.8
14477 : */
14478 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14479 : {
14480 31 : VALIDATE_POINTER1(hDim, __func__, false);
14481 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14482 31 : return hDim->m_poImpl->Rename(pszNewName);
14483 : }
14484 :
14485 : /************************************************************************/
14486 : /* GDALDatasetGetRootGroup() */
14487 : /************************************************************************/
14488 :
14489 : /** Return the root GDALGroup of this dataset.
14490 : *
14491 : * Only valid for multidimensional datasets.
14492 : *
14493 : * The returned value must be freed with GDALGroupRelease().
14494 : *
14495 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14496 : *
14497 : * @since GDAL 3.1
14498 : */
14499 2093 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14500 : {
14501 2093 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14502 2093 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14503 2093 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14504 : }
14505 :
14506 : /************************************************************************/
14507 : /* GDALRasterBandAsMDArray() */
14508 : /************************************************************************/
14509 :
14510 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14511 : *
14512 : * The band must be linked to a GDALDataset. If this dataset is not already
14513 : * marked as shared, it will be, so that the returned array holds a reference
14514 : * to it.
14515 : *
14516 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14517 : * returned array will have an associated indexing variable.
14518 : *
14519 : * The returned pointer must be released with GDALMDArrayRelease().
14520 : *
14521 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14522 : *
14523 : * @return a new array, or NULL.
14524 : *
14525 : * @since GDAL 3.1
14526 : */
14527 25 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14528 : {
14529 25 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14530 50 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14531 25 : if (!poArray)
14532 0 : return nullptr;
14533 25 : return new GDALMDArrayHS(poArray);
14534 : }
14535 :
14536 : /************************************************************************/
14537 : /* GDALDatasetAsMDArray() */
14538 : /************************************************************************/
14539 :
14540 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14541 : *
14542 : * If this dataset is not already marked as shared, it will be, so that the
14543 : * returned array holds a reference to it.
14544 : *
14545 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14546 : * returned array will have an associated indexing variable.
14547 : *
14548 : * The currently supported list of options is:
14549 : * <ul>
14550 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14551 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14552 : * and the last (fastest changing direction) is X
14553 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14554 : * and the last (fastest changing direction) is Band.
14555 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14556 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14557 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14558 : * "Y,X,Band" is use.
14559 : * </li>
14560 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14561 : * item from which to build the band indexing variable.
14562 : * <ul>
14563 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14564 : * <li>"{None}" means that no band indexing variable must be created.</li>
14565 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14566 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14567 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14568 : * </ul>
14569 : * </li>
14570 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14571 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14572 : * Defaults to String.
14573 : * </li>
14574 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14575 : * Defaults to "Band".
14576 : * </li>
14577 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14578 : * </li>
14579 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14580 : * </li>
14581 : * </ul>
14582 : *
14583 : * The returned pointer must be released with GDALMDArrayRelease().
14584 : *
14585 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14586 : * GDALDataset::AsMDArray()
14587 : *
14588 : * This is the same as the C++ method GDALDataset::AsMDArray().
14589 : *
14590 : * @param hDS Dataset handle.
14591 : * @param papszOptions Null-terminated list of strings, or nullptr.
14592 : * @return a new array, or NULL.
14593 : *
14594 : * @since GDAL 3.12
14595 : */
14596 15 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14597 : {
14598 15 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14599 30 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14600 15 : if (!poArray)
14601 3 : return nullptr;
14602 12 : return new GDALMDArrayHS(poArray);
14603 : }
14604 :
14605 : /************************************************************************/
14606 : /* GDALMDArrayAsClassicDataset() */
14607 : /************************************************************************/
14608 :
14609 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14610 : *
14611 : * Only 2D or more arrays are supported.
14612 : *
14613 : * In the case of > 2D arrays, additional dimensions will be represented as
14614 : * raster bands.
14615 : *
14616 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14617 : * GDALDataset::AsMDArray()
14618 : *
14619 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14620 : *
14621 : * @param hArray Array.
14622 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14623 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14624 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14625 : */
14626 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14627 : size_t iYDim)
14628 : {
14629 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14630 0 : return GDALDataset::ToHandle(
14631 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14632 : }
14633 :
14634 : /************************************************************************/
14635 : /* GDALMDArrayAsClassicDatasetEx() */
14636 : /************************************************************************/
14637 :
14638 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14639 : *
14640 : * Only 2D or more arrays are supported.
14641 : *
14642 : * In the case of > 2D arrays, additional dimensions will be represented as
14643 : * raster bands.
14644 : *
14645 : * The "reverse" method is GDALRasterBand::AsMDArray().
14646 : *
14647 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14648 : * @param hArray Array.
14649 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14650 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14651 : * Ignored if the dimension count is 1.
14652 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14653 : * BAND_IMAGERY_METADATA option.
14654 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14655 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14656 : * @since GDAL 3.8
14657 : */
14658 110 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14659 : size_t iYDim, GDALGroupH hRootGroup,
14660 : CSLConstList papszOptions)
14661 : {
14662 110 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14663 220 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14664 220 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14665 220 : papszOptions));
14666 : }
14667 :
14668 : //! @cond Doxygen_Suppress
14669 :
14670 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14671 : const std::string &osName,
14672 : const std::string &osValue,
14673 180 : GDALExtendedDataTypeSubType eSubType)
14674 : : GDALAbstractMDArray(osParentName, osName),
14675 : GDALAttribute(osParentName, osName),
14676 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14677 : {
14678 180 : }
14679 :
14680 : const std::vector<std::shared_ptr<GDALDimension>> &
14681 30 : GDALAttributeString::GetDimensions() const
14682 : {
14683 30 : return m_dims;
14684 : }
14685 :
14686 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14687 : {
14688 21 : return m_dt;
14689 : }
14690 :
14691 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14692 : const GPtrDiff_t *,
14693 : const GDALExtendedDataType &bufferDataType,
14694 : void *pDstBuffer) const
14695 : {
14696 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14697 0 : return false;
14698 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14699 10 : if (!pszStr)
14700 0 : return false;
14701 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14702 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14703 10 : return true;
14704 : }
14705 :
14706 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14707 : const std::string &osName,
14708 66 : double dfValue)
14709 : : GDALAbstractMDArray(osParentName, osName),
14710 : GDALAttribute(osParentName, osName),
14711 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14712 : {
14713 66 : }
14714 :
14715 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14716 : const std::string &osName,
14717 27 : int nValue)
14718 : : GDALAbstractMDArray(osParentName, osName),
14719 : GDALAttribute(osParentName, osName),
14720 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14721 : {
14722 27 : }
14723 :
14724 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14725 : const std::string &osName,
14726 7 : const std::vector<GUInt32> &anValues)
14727 : : GDALAbstractMDArray(osParentName, osName),
14728 : GDALAttribute(osParentName, osName),
14729 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14730 : {
14731 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14732 14 : std::string(), "dim0", std::string(), std::string(),
14733 7 : m_anValuesUInt32.size()));
14734 7 : }
14735 :
14736 : const std::vector<std::shared_ptr<GDALDimension>> &
14737 14 : GDALAttributeNumeric::GetDimensions() const
14738 : {
14739 14 : return m_dims;
14740 : }
14741 :
14742 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14743 : {
14744 8 : return m_dt;
14745 : }
14746 :
14747 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14748 : const size_t *count, const GInt64 *arrayStep,
14749 : const GPtrDiff_t *bufferStride,
14750 : const GDALExtendedDataType &bufferDataType,
14751 : void *pDstBuffer) const
14752 : {
14753 4 : if (m_dims.empty())
14754 : {
14755 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14756 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14757 : bufferDataType);
14758 : else
14759 : {
14760 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14761 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14762 : bufferDataType);
14763 : }
14764 : }
14765 : else
14766 : {
14767 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14768 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14769 30 : for (size_t i = 0; i < count[0]; ++i)
14770 : {
14771 29 : GDALExtendedDataType::CopyValue(
14772 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14773 29 : i * arrayStep[0])],
14774 29 : m_dt, pabyDstBuffer, bufferDataType);
14775 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14776 : }
14777 : }
14778 4 : return true;
14779 : }
14780 :
14781 380 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14782 : const std::string &osParentName, const std::string &osName,
14783 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14784 380 : double dfIncrement, double dfOffsetInIncrement)
14785 : : GDALAbstractMDArray(osParentName, osName),
14786 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14787 : m_dfIncrement(dfIncrement), m_dfOffsetInIncrement(dfOffsetInIncrement),
14788 760 : m_dims{poDim}
14789 : {
14790 380 : }
14791 :
14792 380 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14793 : const std::string &osParentName, const std::string &osName,
14794 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14795 : double dfIncrement, double dfOffsetInIncrement)
14796 : {
14797 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14798 380 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14799 380 : poArray->SetSelf(poArray);
14800 380 : return poArray;
14801 : }
14802 :
14803 : const std::vector<std::shared_ptr<GDALDimension>> &
14804 1884 : GDALMDArrayRegularlySpaced::GetDimensions() const
14805 : {
14806 1884 : return m_dims;
14807 : }
14808 :
14809 679 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14810 : {
14811 679 : return m_dt;
14812 : }
14813 :
14814 : std::vector<std::shared_ptr<GDALAttribute>>
14815 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14816 : {
14817 4 : return m_attributes;
14818 : }
14819 :
14820 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14821 : const std::shared_ptr<GDALAttribute> &poAttr)
14822 : {
14823 0 : m_attributes.emplace_back(poAttr);
14824 0 : }
14825 :
14826 248 : bool GDALMDArrayRegularlySpaced::IRead(
14827 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14828 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14829 : void *pDstBuffer) const
14830 : {
14831 248 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14832 4690 : for (size_t i = 0; i < count[0]; i++)
14833 : {
14834 4442 : const double dfVal =
14835 4442 : m_dfStart +
14836 4442 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14837 4442 : m_dfOffsetInIncrement) *
14838 4442 : m_dfIncrement;
14839 4442 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14840 : bufferDataType);
14841 4442 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14842 : }
14843 248 : return true;
14844 : }
14845 :
14846 268 : bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
14847 : double &dfIncrement) const
14848 : {
14849 268 : dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
14850 268 : dfIncrement = m_dfIncrement;
14851 268 : return true;
14852 : }
14853 :
14854 6888 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14855 : const std::string &osParentName, const std::string &osName,
14856 6888 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14857 6888 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14858 : {
14859 6888 : }
14860 :
14861 : std::shared_ptr<GDALMDArray>
14862 2278 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14863 : {
14864 2278 : return m_poIndexingVariable.lock();
14865 : }
14866 :
14867 : // cppcheck-suppress passedByValue
14868 974 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14869 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14870 : {
14871 974 : m_poIndexingVariable = poIndexingVariable;
14872 974 : return true;
14873 : }
14874 :
14875 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14876 : {
14877 33 : m_nSize = nNewSize;
14878 33 : }
14879 :
14880 : /************************************************************************/
14881 : /* GDALPamMultiDim::Private */
14882 : /************************************************************************/
14883 :
14884 : struct GDALPamMultiDim::Private
14885 : {
14886 : std::string m_osFilename{};
14887 : std::string m_osPamFilename{};
14888 :
14889 : struct Statistics
14890 : {
14891 : bool bHasStats = false;
14892 : bool bApproxStats = false;
14893 : double dfMin = 0;
14894 : double dfMax = 0;
14895 : double dfMean = 0;
14896 : double dfStdDev = 0;
14897 : GUInt64 nValidCount = 0;
14898 : };
14899 :
14900 : struct ArrayInfo
14901 : {
14902 : std::shared_ptr<OGRSpatialReference> poSRS{};
14903 : // cppcheck-suppress unusedStructMember
14904 : Statistics stats{};
14905 : };
14906 :
14907 : typedef std::pair<std::string, std::string> NameContext;
14908 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14909 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14910 : bool m_bDirty = false;
14911 : bool m_bLoaded = false;
14912 : };
14913 :
14914 : /************************************************************************/
14915 : /* GDALPamMultiDim */
14916 : /************************************************************************/
14917 :
14918 2720 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14919 2720 : : d(new Private())
14920 : {
14921 2720 : d->m_osFilename = osFilename;
14922 2720 : }
14923 :
14924 : /************************************************************************/
14925 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14926 : /************************************************************************/
14927 :
14928 2720 : GDALPamMultiDim::~GDALPamMultiDim()
14929 : {
14930 2720 : if (d->m_bDirty)
14931 35 : Save();
14932 2720 : }
14933 :
14934 : /************************************************************************/
14935 : /* GDALPamMultiDim::Load() */
14936 : /************************************************************************/
14937 :
14938 131 : void GDALPamMultiDim::Load()
14939 : {
14940 131 : if (d->m_bLoaded)
14941 120 : return;
14942 52 : d->m_bLoaded = true;
14943 :
14944 52 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14945 52 : d->m_osPamFilename =
14946 104 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14947 52 : CPLXMLTreeCloser oTree(nullptr);
14948 : {
14949 104 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14950 52 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14951 : }
14952 52 : if (!oTree)
14953 : {
14954 41 : return;
14955 : }
14956 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14957 11 : if (!poPAMMultiDim)
14958 0 : return;
14959 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14960 24 : psIter = psIter->psNext)
14961 : {
14962 24 : if (psIter->eType == CXT_Element &&
14963 24 : strcmp(psIter->pszValue, "Array") == 0)
14964 : {
14965 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14966 13 : if (!pszName)
14967 0 : continue;
14968 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14969 : const auto oKey =
14970 26 : std::pair<std::string, std::string>(pszName, pszContext);
14971 :
14972 : /* --------------------------------------------------------------------
14973 : */
14974 : /* Check for an SRS node. */
14975 : /* --------------------------------------------------------------------
14976 : */
14977 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14978 13 : if (psSRSNode)
14979 : {
14980 : std::shared_ptr<OGRSpatialReference> poSRS =
14981 6 : std::make_shared<OGRSpatialReference>();
14982 3 : poSRS->SetFromUserInput(
14983 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14984 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14985 3 : const char *pszMapping = CPLGetXMLValue(
14986 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14987 3 : if (pszMapping)
14988 : {
14989 : char **papszTokens =
14990 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14991 6 : std::vector<int> anMapping;
14992 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14993 : {
14994 6 : anMapping.push_back(atoi(papszTokens[i]));
14995 : }
14996 3 : CSLDestroy(papszTokens);
14997 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14998 : }
14999 : else
15000 : {
15001 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
15002 : }
15003 :
15004 : const char *pszCoordinateEpoch =
15005 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
15006 3 : if (pszCoordinateEpoch)
15007 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
15008 :
15009 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
15010 : }
15011 :
15012 : const CPLXMLNode *psStatistics =
15013 13 : CPLGetXMLNode(psIter, "Statistics");
15014 13 : if (psStatistics)
15015 : {
15016 7 : Private::Statistics sStats;
15017 7 : sStats.bHasStats = true;
15018 7 : sStats.bApproxStats = CPLTestBool(
15019 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
15020 7 : sStats.dfMin =
15021 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
15022 7 : sStats.dfMax =
15023 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
15024 7 : sStats.dfMean =
15025 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
15026 7 : sStats.dfStdDev =
15027 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
15028 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
15029 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
15030 7 : d->m_oMapArray[oKey].stats = sStats;
15031 13 : }
15032 : }
15033 : else
15034 : {
15035 11 : CPLXMLNode *psNextBackup = psIter->psNext;
15036 11 : psIter->psNext = nullptr;
15037 11 : d->m_apoOtherNodes.emplace_back(
15038 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
15039 11 : psIter->psNext = psNextBackup;
15040 : }
15041 : }
15042 : }
15043 :
15044 : /************************************************************************/
15045 : /* GDALPamMultiDim::Save() */
15046 : /************************************************************************/
15047 :
15048 35 : void GDALPamMultiDim::Save()
15049 : {
15050 : CPLXMLTreeCloser oTree(
15051 70 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
15052 39 : for (const auto &poOtherNode : d->m_apoOtherNodes)
15053 : {
15054 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
15055 : }
15056 132 : for (const auto &kv : d->m_oMapArray)
15057 : {
15058 : CPLXMLNode *psArrayNode =
15059 97 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
15060 97 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
15061 97 : if (!kv.first.second.empty())
15062 : {
15063 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
15064 : kv.first.second.c_str());
15065 : }
15066 97 : if (kv.second.poSRS)
15067 : {
15068 86 : char *pszWKT = nullptr;
15069 : {
15070 172 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
15071 86 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
15072 86 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
15073 : }
15074 : CPLXMLNode *psSRSNode =
15075 86 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
15076 86 : CPLFree(pszWKT);
15077 : const auto &mapping =
15078 86 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
15079 172 : CPLString osMapping;
15080 258 : for (size_t i = 0; i < mapping.size(); ++i)
15081 : {
15082 172 : if (!osMapping.empty())
15083 86 : osMapping += ",";
15084 172 : osMapping += CPLSPrintf("%d", mapping[i]);
15085 : }
15086 86 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
15087 : osMapping.c_str());
15088 :
15089 : const double dfCoordinateEpoch =
15090 86 : kv.second.poSRS->GetCoordinateEpoch();
15091 86 : if (dfCoordinateEpoch > 0)
15092 : {
15093 : std::string osCoordinateEpoch =
15094 2 : CPLSPrintf("%f", dfCoordinateEpoch);
15095 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
15096 : {
15097 6 : while (osCoordinateEpoch.back() == '0')
15098 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
15099 : }
15100 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
15101 : osCoordinateEpoch.c_str());
15102 : }
15103 : }
15104 :
15105 97 : if (kv.second.stats.bHasStats)
15106 : {
15107 : CPLXMLNode *psMDArray =
15108 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
15109 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
15110 8 : kv.second.stats.bApproxStats ? "1"
15111 : : "0");
15112 8 : CPLCreateXMLElementAndValue(
15113 : psMDArray, "Minimum",
15114 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
15115 8 : CPLCreateXMLElementAndValue(
15116 : psMDArray, "Maximum",
15117 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
15118 8 : CPLCreateXMLElementAndValue(
15119 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
15120 8 : CPLCreateXMLElementAndValue(
15121 : psMDArray, "StdDev",
15122 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
15123 8 : CPLCreateXMLElementAndValue(
15124 : psMDArray, "ValidSampleCount",
15125 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
15126 : }
15127 : }
15128 :
15129 : int bSaved;
15130 70 : CPLErrorAccumulator oErrorAccumulator;
15131 : {
15132 35 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
15133 35 : CPL_IGNORE_RET_VAL(oAccumulator);
15134 : bSaved =
15135 35 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
15136 : }
15137 :
15138 35 : const char *pszNewPam = nullptr;
15139 35 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
15140 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
15141 : {
15142 0 : CPLErrorReset();
15143 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
15144 : }
15145 : else
15146 : {
15147 35 : oErrorAccumulator.ReplayErrors();
15148 : }
15149 35 : }
15150 :
15151 : /************************************************************************/
15152 : /* GDALPamMultiDim::GetSpatialRef() */
15153 : /************************************************************************/
15154 :
15155 : std::shared_ptr<OGRSpatialReference>
15156 19 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
15157 : const std::string &osContext)
15158 : {
15159 19 : Load();
15160 : auto oIter =
15161 19 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15162 19 : if (oIter != d->m_oMapArray.end())
15163 2 : return oIter->second.poSRS;
15164 17 : return nullptr;
15165 : }
15166 :
15167 : /************************************************************************/
15168 : /* GDALPamMultiDim::SetSpatialRef() */
15169 : /************************************************************************/
15170 :
15171 87 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
15172 : const std::string &osContext,
15173 : const OGRSpatialReference *poSRS)
15174 : {
15175 87 : Load();
15176 87 : d->m_bDirty = true;
15177 87 : if (poSRS && !poSRS->IsEmpty())
15178 86 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
15179 : poSRS->Clone());
15180 : else
15181 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
15182 1 : .poSRS.reset();
15183 87 : }
15184 :
15185 : /************************************************************************/
15186 : /* GetStatistics() */
15187 : /************************************************************************/
15188 :
15189 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
15190 : const std::string &osContext,
15191 : bool bApproxOK, double *pdfMin,
15192 : double *pdfMax, double *pdfMean,
15193 : double *pdfStdDev, GUInt64 *pnValidCount)
15194 : {
15195 16 : Load();
15196 : auto oIter =
15197 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15198 16 : if (oIter == d->m_oMapArray.end())
15199 9 : return CE_Failure;
15200 7 : const auto &stats = oIter->second.stats;
15201 7 : if (!stats.bHasStats)
15202 1 : return CE_Failure;
15203 6 : if (!bApproxOK && stats.bApproxStats)
15204 0 : return CE_Failure;
15205 6 : if (pdfMin)
15206 6 : *pdfMin = stats.dfMin;
15207 6 : if (pdfMax)
15208 6 : *pdfMax = stats.dfMax;
15209 6 : if (pdfMean)
15210 6 : *pdfMean = stats.dfMean;
15211 6 : if (pdfStdDev)
15212 6 : *pdfStdDev = stats.dfStdDev;
15213 6 : if (pnValidCount)
15214 6 : *pnValidCount = stats.nValidCount;
15215 6 : return CE_None;
15216 : }
15217 :
15218 : /************************************************************************/
15219 : /* SetStatistics() */
15220 : /************************************************************************/
15221 :
15222 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
15223 : const std::string &osContext,
15224 : bool bApproxStats, double dfMin,
15225 : double dfMax, double dfMean,
15226 : double dfStdDev, GUInt64 nValidCount)
15227 : {
15228 8 : Load();
15229 8 : d->m_bDirty = true;
15230 : auto &stats =
15231 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
15232 8 : stats.bHasStats = true;
15233 8 : stats.bApproxStats = bApproxStats;
15234 8 : stats.dfMin = dfMin;
15235 8 : stats.dfMax = dfMax;
15236 8 : stats.dfMean = dfMean;
15237 8 : stats.dfStdDev = dfStdDev;
15238 8 : stats.nValidCount = nValidCount;
15239 8 : }
15240 :
15241 : /************************************************************************/
15242 : /* ClearStatistics() */
15243 : /************************************************************************/
15244 :
15245 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
15246 : const std::string &osContext)
15247 : {
15248 0 : Load();
15249 0 : d->m_bDirty = true;
15250 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
15251 : false;
15252 0 : }
15253 :
15254 : /************************************************************************/
15255 : /* ClearStatistics() */
15256 : /************************************************************************/
15257 :
15258 1 : void GDALPamMultiDim::ClearStatistics()
15259 : {
15260 1 : Load();
15261 1 : d->m_bDirty = true;
15262 3 : for (auto &kv : d->m_oMapArray)
15263 2 : kv.second.stats.bHasStats = false;
15264 1 : }
15265 :
15266 : /************************************************************************/
15267 : /* GetPAM() */
15268 : /************************************************************************/
15269 :
15270 : /*static*/ std::shared_ptr<GDALPamMultiDim>
15271 1478 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15272 : {
15273 1478 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15274 1478 : if (poPamArray)
15275 745 : return poPamArray->GetPAM();
15276 733 : return nullptr;
15277 : }
15278 :
15279 : /************************************************************************/
15280 : /* GDALPamMDArray */
15281 : /************************************************************************/
15282 :
15283 6199 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15284 : const std::string &osName,
15285 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15286 0 : const std::string &osContext)
15287 : :
15288 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15289 : GDALAbstractMDArray(osParentName, osName),
15290 : #endif
15291 6199 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15292 : {
15293 6199 : }
15294 :
15295 : /************************************************************************/
15296 : /* GDALPamMDArray::SetSpatialRef() */
15297 : /************************************************************************/
15298 :
15299 87 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15300 : {
15301 87 : if (!m_poPam)
15302 0 : return false;
15303 87 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15304 87 : return true;
15305 : }
15306 :
15307 : /************************************************************************/
15308 : /* GDALPamMDArray::GetSpatialRef() */
15309 : /************************************************************************/
15310 :
15311 19 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15312 : {
15313 19 : if (!m_poPam)
15314 0 : return nullptr;
15315 19 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15316 : }
15317 :
15318 : /************************************************************************/
15319 : /* GetStatistics() */
15320 : /************************************************************************/
15321 :
15322 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15323 : double *pdfMin, double *pdfMax,
15324 : double *pdfMean, double *pdfStdDev,
15325 : GUInt64 *pnValidCount,
15326 : GDALProgressFunc pfnProgress,
15327 : void *pProgressData)
15328 : {
15329 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15330 : bApproxOK, pdfMin, pdfMax, pdfMean,
15331 16 : pdfStdDev, pnValidCount) == CE_None)
15332 : {
15333 6 : return CE_None;
15334 : }
15335 10 : if (!bForce)
15336 4 : return CE_Warning;
15337 :
15338 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15339 : pdfMean, pdfStdDev, pnValidCount,
15340 6 : pfnProgress, pProgressData);
15341 : }
15342 :
15343 : /************************************************************************/
15344 : /* SetStatistics() */
15345 : /************************************************************************/
15346 :
15347 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15348 : double dfMax, double dfMean, double dfStdDev,
15349 : GUInt64 nValidCount,
15350 : CSLConstList /* papszOptions */)
15351 : {
15352 8 : if (!m_poPam)
15353 0 : return false;
15354 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15355 : dfMax, dfMean, dfStdDev, nValidCount);
15356 8 : return true;
15357 : }
15358 :
15359 : /************************************************************************/
15360 : /* ClearStatistics() */
15361 : /************************************************************************/
15362 :
15363 0 : void GDALPamMDArray::ClearStatistics()
15364 : {
15365 0 : if (!m_poPam)
15366 0 : return;
15367 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15368 : }
15369 :
15370 : /************************************************************************/
15371 : /* GDALMDIAsAttribute::GetDimensions() */
15372 : /************************************************************************/
15373 :
15374 : const std::vector<std::shared_ptr<GDALDimension>> &
15375 29 : GDALMDIAsAttribute::GetDimensions() const
15376 : {
15377 29 : return m_dims;
15378 : }
15379 :
15380 : /************************************************************************/
15381 : /* GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo() */
15382 : /************************************************************************/
15383 :
15384 64 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
15385 : {
15386 32 : clear();
15387 32 : }
15388 :
15389 : /************************************************************************/
15390 : /* GDALMDArrayRawBlockInfo::clear() */
15391 : /************************************************************************/
15392 :
15393 53 : void GDALMDArrayRawBlockInfo::clear()
15394 : {
15395 53 : CPLFree(pszFilename);
15396 53 : pszFilename = nullptr;
15397 53 : CSLDestroy(papszInfo);
15398 53 : papszInfo = nullptr;
15399 53 : nOffset = 0;
15400 53 : nSize = 0;
15401 53 : CPLFree(pabyInlineData);
15402 53 : pabyInlineData = nullptr;
15403 53 : }
15404 :
15405 : /************************************************************************/
15406 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15407 : /************************************************************************/
15408 :
15409 4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15410 4 : const GDALMDArrayRawBlockInfo &other)
15411 4 : : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
15412 4 : nOffset(other.nOffset), nSize(other.nSize),
15413 4 : papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
15414 : {
15415 4 : if (other.pabyInlineData)
15416 : {
15417 3 : pabyInlineData = static_cast<GByte *>(
15418 3 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15419 3 : if (pabyInlineData)
15420 3 : memcpy(pabyInlineData, other.pabyInlineData,
15421 3 : static_cast<size_t>(other.nSize));
15422 : }
15423 4 : }
15424 :
15425 : /************************************************************************/
15426 : /* GDALMDArrayRawBlockInfo::operator=() */
15427 : /************************************************************************/
15428 :
15429 : GDALMDArrayRawBlockInfo &
15430 7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
15431 : {
15432 7 : if (this != &other)
15433 : {
15434 5 : CPLFree(pszFilename);
15435 5 : pszFilename =
15436 5 : other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
15437 5 : nOffset = other.nOffset;
15438 5 : nSize = other.nSize;
15439 5 : CSLDestroy(papszInfo);
15440 5 : papszInfo = CSLDuplicate(other.papszInfo);
15441 5 : CPLFree(pabyInlineData);
15442 5 : pabyInlineData = nullptr;
15443 5 : if (other.pabyInlineData)
15444 : {
15445 4 : pabyInlineData = static_cast<GByte *>(
15446 4 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15447 4 : if (pabyInlineData)
15448 4 : memcpy(pabyInlineData, other.pabyInlineData,
15449 4 : static_cast<size_t>(other.nSize));
15450 : }
15451 : }
15452 7 : return *this;
15453 : }
15454 :
15455 : /************************************************************************/
15456 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15457 : /************************************************************************/
15458 :
15459 2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15460 2 : GDALMDArrayRawBlockInfo &&other)
15461 2 : : pszFilename(other.pszFilename), nOffset(other.nOffset),
15462 2 : nSize(other.nSize), papszInfo(other.papszInfo),
15463 2 : pabyInlineData(other.pabyInlineData)
15464 : {
15465 2 : other.pszFilename = nullptr;
15466 2 : other.papszInfo = nullptr;
15467 2 : other.pabyInlineData = nullptr;
15468 2 : }
15469 :
15470 : /************************************************************************/
15471 : /* GDALMDArrayRawBlockInfo::operator=() */
15472 : /************************************************************************/
15473 :
15474 : GDALMDArrayRawBlockInfo &
15475 2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
15476 : {
15477 2 : if (this != &other)
15478 : {
15479 2 : std::swap(pszFilename, other.pszFilename);
15480 2 : nOffset = other.nOffset;
15481 2 : nSize = other.nSize;
15482 2 : std::swap(papszInfo, other.papszInfo);
15483 2 : std::swap(pabyInlineData, other.pabyInlineData);
15484 : }
15485 2 : return *this;
15486 : }
15487 :
15488 : //! @endcond
15489 :
15490 : /************************************************************************/
15491 : /* GDALMDArray::GetRawBlockInfo() */
15492 : /************************************************************************/
15493 :
15494 : /** Return information on a raw block.
15495 : *
15496 : * The block coordinates must be between 0 and
15497 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15498 : * 0 and GetDimensionCount()-1.
15499 : *
15500 : * If the queried block has valid coordinates but is missing in the dataset,
15501 : * all fields of info will be set to 0/nullptr, but the function will return
15502 : * true.
15503 : *
15504 : * This method is only implemented by a subset of drivers. The base
15505 : * implementation just returns false and empty info.
15506 : *
15507 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15508 : *
15509 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15510 : * value is "LITTLE" or "BIG".
15511 : *
15512 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15513 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15514 : * comma-separated)
15515 : *
15516 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15517 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15518 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15519 : * "[idx0,...,idxN]" with the permutation).
15520 : *
15521 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15522 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15523 : * that for each queried VRT block, there is one and only one source that
15524 : * is used to fill the VRT block and that the block size of this source is
15525 : * exactly the one of the VRT block.
15526 : *
15527 : * This is the same as C function GDALMDArrayGetRawBlockInfo().
15528 : *
15529 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15530 : * coordinates.
15531 : * @param[out] info structure to fill with block information.
15532 : * @return true in case of success, or false if an error occurs.
15533 : * @since 3.12
15534 : */
15535 0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
15536 : GDALMDArrayRawBlockInfo &info) const
15537 : {
15538 : (void)panBlockCoordinates;
15539 0 : info.clear();
15540 0 : return false;
15541 : }
15542 :
15543 : /************************************************************************/
15544 : /* GDALMDArrayGetRawBlockInfo() */
15545 : /************************************************************************/
15546 :
15547 : /** Return information on a raw block.
15548 : *
15549 : * The block coordinates must be between 0 and
15550 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15551 : * 0 and GetDimensionCount()-1.
15552 : *
15553 : * If the queried block has valid coordinates but is missing in the dataset,
15554 : * all fields of info will be set to 0/nullptr, but the function will return
15555 : * true.
15556 : *
15557 : * This method is only implemented by a subset of drivers. The base
15558 : * implementation just returns false and empty info.
15559 : *
15560 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15561 : *
15562 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15563 : * value is "LITTLE" or "BIG".
15564 : *
15565 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15566 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15567 : * comma-separated)
15568 : *
15569 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15570 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15571 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15572 : * "[idx0,...,idxN]" with the permutation).
15573 : *
15574 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15575 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15576 : * that for each queried VRT block, there is one and only one source that
15577 : * is used to fill the VRT block and that the block size of this source is
15578 : * exactly the one of the VRT block.
15579 : *
15580 : * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
15581 : *
15582 : * @param hArray handle to array.
15583 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15584 : * coordinates.
15585 : * @param[out] psBlockInfo structure to fill with block information.
15586 : * Must be allocated with GDALMDArrayRawBlockInfoCreate(),
15587 : * and freed with GDALMDArrayRawBlockInfoRelease().
15588 : * @return true in case of success, or false if an error occurs.
15589 : * @since 3.12
15590 : */
15591 21 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
15592 : const uint64_t *panBlockCoordinates,
15593 : GDALMDArrayRawBlockInfo *psBlockInfo)
15594 : {
15595 21 : VALIDATE_POINTER1(hArray, __func__, false);
15596 21 : VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
15597 21 : VALIDATE_POINTER1(psBlockInfo, __func__, false);
15598 21 : return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
15599 : }
15600 :
15601 : /************************************************************************/
15602 : /* GDALMDArrayRawBlockInfoCreate() */
15603 : /************************************************************************/
15604 :
15605 : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
15606 : *
15607 : * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
15608 : *
15609 : * @since 3.12
15610 : */
15611 21 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
15612 : {
15613 21 : return new GDALMDArrayRawBlockInfo();
15614 : }
15615 :
15616 : /************************************************************************/
15617 : /* GDALMDArrayRawBlockInfoRelease() */
15618 : /************************************************************************/
15619 :
15620 : /** Free an instance of GDALMDArrayRawBlockInfo.
15621 : *
15622 : * @since 3.12
15623 : */
15624 21 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
15625 : {
15626 21 : delete psBlockInfo;
15627 21 : }
15628 :
15629 : /************************************************************************/
15630 : /* GDALMDArray::GetOverviewCount() */
15631 : /************************************************************************/
15632 :
15633 : /**
15634 : * \brief Return the number of overview arrays available.
15635 : *
15636 : * This method is the same as the C function GDALMDArrayGetOverviewCount().
15637 : *
15638 : * @return overview count, zero if none.
15639 : *
15640 : * @since 3.13
15641 : */
15642 :
15643 115 : int GDALMDArray::GetOverviewCount() const
15644 : {
15645 115 : return 0;
15646 : }
15647 :
15648 : /************************************************************************/
15649 : /* GDALMDArrayGetOverviewCount() */
15650 : /************************************************************************/
15651 : /**
15652 : * \brief Return the number of overview arrays available.
15653 : *
15654 : * This method is the same as the C++ method GDALMDArray::GetOverviewCount().
15655 : *
15656 : * @param hArray Array.
15657 : * @return overview count, zero if none.
15658 : *
15659 : * @since 3.13
15660 : */
15661 :
15662 113 : int GDALMDArrayGetOverviewCount(GDALMDArrayH hArray)
15663 : {
15664 113 : VALIDATE_POINTER1(hArray, __func__, 0);
15665 113 : return hArray->m_poImpl->GetOverviewCount();
15666 : }
15667 :
15668 : /************************************************************************/
15669 : /* GDALMDArray::GetOverview() */
15670 : /************************************************************************/
15671 :
15672 : /**
15673 : * \brief Get overview array object.
15674 : *
15675 : * This method is the same as the C function GDALMDArrayGetOverview().
15676 : *
15677 : * @param nIdx overview index between 0 and GetOverviewCount()-1.
15678 : *
15679 : * @return overview GDALMDArray, or nullptr
15680 : *
15681 : * @since 3.13
15682 : */
15683 :
15684 70 : std::shared_ptr<GDALMDArray> GDALMDArray::GetOverview(int nIdx) const
15685 : {
15686 : (void)nIdx;
15687 70 : return nullptr;
15688 : }
15689 :
15690 : /************************************************************************/
15691 : /* GDALMDArrayGetOverview() */
15692 : /************************************************************************/
15693 :
15694 : /**
15695 : * \brief Get overview array object.
15696 : *
15697 : * This method is the same as the C++ method GDALMDArray::GetOverview().
15698 : *
15699 : * @param hArray Array.
15700 : * @param nIdx overview index between 0 and GDALMDArrayGetOverviewCount()-1.
15701 : *
15702 : * @return overview GDALMDArray, or nullptr.
15703 : * Must be released with GDALMDArrayRelease()
15704 : *
15705 : * @since 3.13
15706 : */
15707 :
15708 124 : GDALMDArrayH GDALMDArrayGetOverview(GDALMDArrayH hArray, int nIdx)
15709 : {
15710 124 : VALIDATE_POINTER1(hArray, __func__, nullptr);
15711 248 : auto poOverview = hArray->m_poImpl->GetOverview(nIdx);
15712 124 : if (!poOverview)
15713 89 : return nullptr;
15714 35 : return new GDALMDArrayHS(poOverview);
15715 : }
15716 :
15717 : /************************************************************************/
15718 : /* GDALMDArray::BuildOverviews() */
15719 : /************************************************************************/
15720 :
15721 : /** Build overviews for this array.
15722 : *
15723 : * Creates reduced resolution copies of this array using the specified
15724 : * resampling method. The driver is responsible for storing the overview
15725 : * arrays and any associated metadata (e.g., multiscales convention for Zarr).
15726 : *
15727 : * For arrays with more than 2 dimensions, only the spatial dimensions
15728 : * (last two by default, or as specified by the spatial:dimensions
15729 : * attribute) are downsampled. Non-spatial dimensions are preserved.
15730 : *
15731 : * Overview factors need not be sorted; the implementation will sort and
15732 : * deduplicate them. Each level is resampled sequentially from the
15733 : * previous level (e.g., 4x is built from 2x, not from the base).
15734 : *
15735 : * This method can also be invoked via GDALDataset::BuildOverviews()
15736 : * when the dataset was obtained through GDALMDArray::AsClassicDataset().
15737 : *
15738 : * @note The Zarr v3 implementation replaces all existing overviews on each
15739 : * call, unlike GDALDataset::BuildOverviews() which may add new levels.
15740 : *
15741 : * @note Currently only implemented by the Zarr v3 driver.
15742 : *
15743 : * @param pszResampling Resampling method name (e.g., "NEAREST", "AVERAGE").
15744 : * If nullptr or empty, defaults to "NEAREST".
15745 : * @param nOverviews Number of overview levels to build. Pass 0 to remove
15746 : * all existing overviews.
15747 : * @param panOverviewList Array of overview decimation factors (e.g., 2, 4, 8).
15748 : * Each factor must be >= 2. May be nullptr when
15749 : * nOverviews is 0.
15750 : * @param pfnProgress Progress callback, or nullptr.
15751 : * @param pProgressData Progress callback user data.
15752 : * @param papszOptions Driver-specific options, or nullptr.
15753 : * @return CE_None on success, CE_Failure otherwise.
15754 : * @since GDAL 3.13
15755 : */
15756 1 : CPLErr GDALMDArray::BuildOverviews(CPL_UNUSED const char *pszResampling,
15757 : CPL_UNUSED int nOverviews,
15758 : CPL_UNUSED const int *panOverviewList,
15759 : CPL_UNUSED GDALProgressFunc pfnProgress,
15760 : CPL_UNUSED void *pProgressData,
15761 : CPL_UNUSED CSLConstList papszOptions)
15762 : {
15763 1 : CPLError(CE_Failure, CPLE_NotSupported,
15764 : "BuildOverviews() not supported by this driver");
15765 1 : return CE_Failure;
15766 : }
15767 :
15768 : /************************************************************************/
15769 : /* GDALMDArrayBuildOverviews() */
15770 : /************************************************************************/
15771 :
15772 : /** \brief Build overviews for a multidimensional array.
15773 : *
15774 : * This is the same as the C++ method GDALMDArray::BuildOverviews().
15775 : *
15776 : * @since GDAL 3.13
15777 : */
15778 13 : CPLErr GDALMDArrayBuildOverviews(GDALMDArrayH hArray, const char *pszResampling,
15779 : int nOverviews, const int *panOverviewList,
15780 : GDALProgressFunc pfnProgress,
15781 : void *pProgressData, CSLConstList papszOptions)
15782 : {
15783 13 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
15784 26 : return hArray->m_poImpl->BuildOverviews(pszResampling, nOverviews,
15785 : panOverviewList, pfnProgress,
15786 13 : pProgressData, papszOptions);
15787 : }
|