Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_rat.h"
31 : #include "gdal_utils.h"
32 : #include "cpl_safemaths.hpp"
33 : #include "memmultidim.h"
34 : #include "ogrsf_frmts.h"
35 : #include "gdalmultidim_priv.h"
36 :
37 : #if defined(__clang__) || defined(_MSC_VER)
38 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
39 : #endif
40 :
41 : /************************************************************************/
42 : /* GDALMDArrayUnscaled */
43 : /************************************************************************/
44 :
45 : class GDALMDArrayUnscaled final : public GDALPamMDArray
46 : {
47 : private:
48 : std::shared_ptr<GDALMDArray> m_poParent{};
49 : const GDALExtendedDataType m_dt;
50 : bool m_bHasNoData;
51 : const double m_dfScale;
52 : const double m_dfOffset;
53 : std::vector<GByte> m_abyRawNoData{};
54 :
55 : protected:
56 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
57 : double dfScale, double dfOffset,
58 : double dfOverriddenDstNodata, GDALDataType eDT)
59 26 : : GDALAbstractMDArray(std::string(),
60 26 : "Unscaled view of " + poParent->GetFullName()),
61 : GDALPamMDArray(
62 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
63 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
64 13 : m_poParent(std::move(poParent)),
65 : m_dt(GDALExtendedDataType::Create(eDT)),
66 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
67 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
68 : {
69 13 : m_abyRawNoData.resize(m_dt.GetSize());
70 : const auto eNonComplexDT =
71 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
72 26 : GDALCopyWords64(
73 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
74 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
75 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
76 13 : }
77 :
78 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
79 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
80 : const GDALExtendedDataType &bufferDataType,
81 : void *pDstBuffer) const override;
82 :
83 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
84 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
85 : const GDALExtendedDataType &bufferDataType,
86 : const void *pSrcBuffer) override;
87 :
88 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
89 : CSLConstList papszOptions) const override
90 : {
91 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
92 : }
93 :
94 : public:
95 : static std::shared_ptr<GDALMDArrayUnscaled>
96 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
97 : double dfOffset, double dfDstNodata, GDALDataType eDT)
98 : {
99 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
100 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
101 13 : newAr->SetSelf(newAr);
102 13 : return newAr;
103 : }
104 :
105 1 : bool IsWritable() const override
106 : {
107 1 : return m_poParent->IsWritable();
108 : }
109 :
110 15 : const std::string &GetFilename() const override
111 : {
112 15 : return m_poParent->GetFilename();
113 : }
114 :
115 : const std::vector<std::shared_ptr<GDALDimension>> &
116 220 : GetDimensions() const override
117 : {
118 220 : return m_poParent->GetDimensions();
119 : }
120 :
121 103 : const GDALExtendedDataType &GetDataType() const override
122 : {
123 103 : return m_dt;
124 : }
125 :
126 1 : const std::string &GetUnit() const override
127 : {
128 1 : return m_poParent->GetUnit();
129 : }
130 :
131 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
132 : {
133 1 : return m_poParent->GetSpatialRef();
134 : }
135 :
136 6 : const void *GetRawNoDataValue() const override
137 : {
138 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
139 : }
140 :
141 1 : bool SetRawNoDataValue(const void *pRawNoData) override
142 : {
143 1 : m_bHasNoData = true;
144 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
145 1 : return true;
146 : }
147 :
148 4 : std::vector<GUInt64> GetBlockSize() const override
149 : {
150 4 : return m_poParent->GetBlockSize();
151 : }
152 :
153 : std::shared_ptr<GDALAttribute>
154 0 : GetAttribute(const std::string &osName) const override
155 : {
156 0 : return m_poParent->GetAttribute(osName);
157 : }
158 :
159 : std::vector<std::shared_ptr<GDALAttribute>>
160 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
161 : {
162 1 : return m_poParent->GetAttributes(papszOptions);
163 : }
164 :
165 0 : bool SetUnit(const std::string &osUnit) override
166 : {
167 0 : return m_poParent->SetUnit(osUnit);
168 : }
169 :
170 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
171 : {
172 0 : return m_poParent->SetSpatialRef(poSRS);
173 : }
174 :
175 : std::shared_ptr<GDALAttribute>
176 1 : CreateAttribute(const std::string &osName,
177 : const std::vector<GUInt64> &anDimensions,
178 : const GDALExtendedDataType &oDataType,
179 : CSLConstList papszOptions = nullptr) override
180 : {
181 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
182 1 : papszOptions);
183 : }
184 : };
185 :
186 : /************************************************************************/
187 : /* ~GDALIHasAttribute() */
188 : /************************************************************************/
189 :
190 : GDALIHasAttribute::~GDALIHasAttribute() = default;
191 :
192 : /************************************************************************/
193 : /* GetAttribute() */
194 : /************************************************************************/
195 :
196 : /** Return an attribute by its name.
197 : *
198 : * If the attribute does not exist, nullptr should be silently returned.
199 : *
200 : * @note Driver implementation: this method will fallback to
201 : * GetAttributeFromAttributes() is not explicitly implemented
202 : *
203 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
204 : *
205 : * This is the same as the C function GDALGroupGetAttribute() or
206 : * GDALMDArrayGetAttribute().
207 : *
208 : * @param osName Attribute name
209 : * @return the attribute, or nullptr if it does not exist or an error occurred.
210 : */
211 : std::shared_ptr<GDALAttribute>
212 1057 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
213 : {
214 1057 : return GetAttributeFromAttributes(osName);
215 : }
216 :
217 : /************************************************************************/
218 : /* GetAttributeFromAttributes() */
219 : /************************************************************************/
220 :
221 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
222 : */
223 : std::shared_ptr<GDALAttribute>
224 1057 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
225 : {
226 2114 : auto attrs(GetAttributes());
227 5493 : for (const auto &attr : attrs)
228 : {
229 5174 : if (attr->GetName() == osName)
230 738 : return attr;
231 : }
232 319 : return nullptr;
233 : }
234 :
235 : /************************************************************************/
236 : /* GetAttributes() */
237 : /************************************************************************/
238 :
239 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
240 : *
241 : * If the attribute does not exist, nullptr should be silently returned.
242 : *
243 : * @note Driver implementation: optionally implemented. If implemented,
244 : * GetAttribute() should also be implemented.
245 : *
246 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
247 : *
248 : * This is the same as the C function GDALGroupGetAttributes() or
249 : * GDALMDArrayGetAttributes().
250 :
251 : * @param papszOptions Driver specific options determining how attributes
252 : * should be retrieved. Pass nullptr for default behavior.
253 : *
254 : * @return the attributes.
255 : */
256 : std::vector<std::shared_ptr<GDALAttribute>>
257 44 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
258 : {
259 44 : return {};
260 : }
261 :
262 : /************************************************************************/
263 : /* CreateAttribute() */
264 : /************************************************************************/
265 :
266 : /** Create an attribute within a GDALMDArray or GDALGroup.
267 : *
268 : * The attribute might not be "physically" created until a value is written
269 : * into it.
270 : *
271 : * Optionally implemented.
272 : *
273 : * Drivers known to implement it: MEM, netCDF
274 : *
275 : * This is the same as the C function GDALGroupCreateAttribute() or
276 : * GDALMDArrayCreateAttribute()
277 : *
278 : * @param osName Attribute name.
279 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
280 : * dimension first to the fastest varying dimension last.
281 : * Empty for a scalar attribute (common case)
282 : * @param oDataType Attribute data type.
283 : * @param papszOptions Driver specific options determining how the attribute.
284 : * should be created.
285 : *
286 : * @return the new attribute, or nullptr if case of error
287 : */
288 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
289 : CPL_UNUSED const std::string &osName,
290 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
291 : CPL_UNUSED const GDALExtendedDataType &oDataType,
292 : CPL_UNUSED CSLConstList papszOptions)
293 : {
294 0 : CPLError(CE_Failure, CPLE_NotSupported,
295 : "CreateAttribute() not implemented");
296 0 : return nullptr;
297 : }
298 :
299 : /************************************************************************/
300 : /* DeleteAttribute() */
301 : /************************************************************************/
302 :
303 : /** Delete an attribute from a GDALMDArray or GDALGroup.
304 : *
305 : * Optionally implemented.
306 : *
307 : * After this call, if a previously obtained instance of the deleted object
308 : * is still alive, no method other than for freeing it should be invoked.
309 : *
310 : * Drivers known to implement it: MEM, netCDF
311 : *
312 : * This is the same as the C function GDALGroupDeleteAttribute() or
313 : * GDALMDArrayDeleteAttribute()
314 : *
315 : * @param osName Attribute name.
316 : * @param papszOptions Driver specific options determining how the attribute.
317 : * should be deleted.
318 : *
319 : * @return true in case of success
320 : * @since GDAL 3.8
321 : */
322 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
323 : CPL_UNUSED CSLConstList papszOptions)
324 : {
325 0 : CPLError(CE_Failure, CPLE_NotSupported,
326 : "DeleteAttribute() not implemented");
327 0 : return false;
328 : }
329 :
330 : /************************************************************************/
331 : /* GDALGroup() */
332 : /************************************************************************/
333 :
334 : //! @cond Doxygen_Suppress
335 6969 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
336 6969 : const std::string &osContext)
337 6969 : : m_osName(osParentName.empty() ? "/" : osName),
338 : m_osFullName(
339 13938 : !osParentName.empty()
340 10794 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
341 : : "/"),
342 17763 : m_osContext(osContext)
343 : {
344 6969 : }
345 :
346 : //! @endcond
347 :
348 : /************************************************************************/
349 : /* ~GDALGroup() */
350 : /************************************************************************/
351 :
352 : GDALGroup::~GDALGroup() = default;
353 :
354 : /************************************************************************/
355 : /* GetMDArrayNames() */
356 : /************************************************************************/
357 :
358 : /** Return the list of multidimensional array names contained in this group.
359 : *
360 : * @note Driver implementation: optionally implemented. If implemented,
361 : * OpenMDArray() should also be implemented.
362 : *
363 : * Drivers known to implement it: MEM, netCDF.
364 : *
365 : * This is the same as the C function GDALGroupGetMDArrayNames().
366 : *
367 : * @param papszOptions Driver specific options determining how arrays
368 : * should be retrieved. Pass nullptr for default behavior.
369 : *
370 : * @return the array names.
371 : */
372 : std::vector<std::string>
373 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
374 : {
375 0 : return {};
376 : }
377 :
378 : /************************************************************************/
379 : /* GetMDArrayFullNamesRecursive() */
380 : /************************************************************************/
381 :
382 : /** Return the list of multidimensional array full names contained in this
383 : * group and its subgroups.
384 : *
385 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
386 : *
387 : * @param papszGroupOptions Driver specific options determining how groups
388 : * should be retrieved. Pass nullptr for default behavior.
389 : * @param papszArrayOptions Driver specific options determining how arrays
390 : * should be retrieved. Pass nullptr for default behavior.
391 : *
392 : * @return the array full names.
393 : *
394 : * @since 3.11
395 : */
396 : std::vector<std::string>
397 3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
398 : CSLConstList papszArrayOptions) const
399 : {
400 3 : std::vector<std::string> ret;
401 6 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
402 3 : stackGroups.push_back(nullptr); // nullptr means this
403 9 : while (!stackGroups.empty())
404 : {
405 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
406 6 : stackGroups.erase(stackGroups.begin());
407 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
408 10 : for (const std::string &arrayName :
409 26 : poCurGroup->GetMDArrayNames(papszArrayOptions))
410 : {
411 20 : std::string osFullName = poCurGroup->GetFullName();
412 10 : if (!osFullName.empty() && osFullName.back() != '/')
413 3 : osFullName += '/';
414 10 : osFullName += arrayName;
415 10 : ret.push_back(std::move(osFullName));
416 : }
417 6 : auto insertionPoint = stackGroups.begin();
418 3 : for (const auto &osSubGroup :
419 12 : poCurGroup->GetGroupNames(papszGroupOptions))
420 : {
421 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
422 3 : if (poSubGroup)
423 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
424 : }
425 : }
426 :
427 6 : return ret;
428 : }
429 :
430 : /************************************************************************/
431 : /* OpenMDArray() */
432 : /************************************************************************/
433 :
434 : /** Open and return a multidimensional array.
435 : *
436 : * @note Driver implementation: optionally implemented. If implemented,
437 : * GetMDArrayNames() should also be implemented.
438 : *
439 : * Drivers known to implement it: MEM, netCDF.
440 : *
441 : * This is the same as the C function GDALGroupOpenMDArray().
442 : *
443 : * @param osName Array name.
444 : * @param papszOptions Driver specific options determining how the array should
445 : * be opened. Pass nullptr for default behavior.
446 : *
447 : * @return the array, or nullptr.
448 : */
449 : std::shared_ptr<GDALMDArray>
450 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
451 : CPL_UNUSED CSLConstList papszOptions) const
452 : {
453 0 : return nullptr;
454 : }
455 :
456 : /************************************************************************/
457 : /* GetGroupNames() */
458 : /************************************************************************/
459 :
460 : /** Return the list of sub-groups contained in this group.
461 : *
462 : * @note Driver implementation: optionally implemented. If implemented,
463 : * OpenGroup() should also be implemented.
464 : *
465 : * Drivers known to implement it: MEM, netCDF.
466 : *
467 : * This is the same as the C function GDALGroupGetGroupNames().
468 : *
469 : * @param papszOptions Driver specific options determining how groups
470 : * should be retrieved. Pass nullptr for default behavior.
471 : *
472 : * @return the group names.
473 : */
474 : std::vector<std::string>
475 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
476 : {
477 4 : return {};
478 : }
479 :
480 : /************************************************************************/
481 : /* OpenGroup() */
482 : /************************************************************************/
483 :
484 : /** Open and return a sub-group.
485 : *
486 : * @note Driver implementation: optionally implemented. If implemented,
487 : * GetGroupNames() should also be implemented.
488 : *
489 : * Drivers known to implement it: MEM, netCDF.
490 : *
491 : * This is the same as the C function GDALGroupOpenGroup().
492 : *
493 : * @param osName Sub-group name.
494 : * @param papszOptions Driver specific options determining how the sub-group
495 : * should be opened. Pass nullptr for default behavior.
496 : *
497 : * @return the group, or nullptr.
498 : */
499 : std::shared_ptr<GDALGroup>
500 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
501 : CPL_UNUSED CSLConstList papszOptions) const
502 : {
503 4 : return nullptr;
504 : }
505 :
506 : /************************************************************************/
507 : /* GetVectorLayerNames() */
508 : /************************************************************************/
509 :
510 : /** Return the list of layer names contained in this group.
511 : *
512 : * @note Driver implementation: optionally implemented. If implemented,
513 : * OpenVectorLayer() should also be implemented.
514 : *
515 : * Drivers known to implement it: OpenFileGDB, FileGDB
516 : *
517 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
518 : * GDALDataset::GetLayer() should then be used.
519 : *
520 : * This is the same as the C function GDALGroupGetVectorLayerNames().
521 : *
522 : * @param papszOptions Driver specific options determining how layers
523 : * should be retrieved. Pass nullptr for default behavior.
524 : *
525 : * @return the vector layer names.
526 : * @since GDAL 3.4
527 : */
528 : std::vector<std::string>
529 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
530 : {
531 1 : return {};
532 : }
533 :
534 : /************************************************************************/
535 : /* OpenVectorLayer() */
536 : /************************************************************************/
537 :
538 : /** Open and return a vector layer.
539 : *
540 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
541 : * lifetime of the returned OGRLayer* is linked to the one of the owner
542 : * dataset (contrary to the general design of this class where objects can be
543 : * used independently of the object that returned them)
544 : *
545 : * @note Driver implementation: optionally implemented. If implemented,
546 : * GetVectorLayerNames() should also be implemented.
547 : *
548 : * Drivers known to implement it: MEM, netCDF.
549 : *
550 : * This is the same as the C function GDALGroupOpenVectorLayer().
551 : *
552 : * @param osName Vector layer name.
553 : * @param papszOptions Driver specific options determining how the layer should
554 : * be opened. Pass nullptr for default behavior.
555 : *
556 : * @return the group, or nullptr.
557 : */
558 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
559 : CPL_UNUSED CSLConstList papszOptions) const
560 : {
561 2 : return nullptr;
562 : }
563 :
564 : /************************************************************************/
565 : /* GetDimensions() */
566 : /************************************************************************/
567 :
568 : /** Return the list of dimensions contained in this group and used by its
569 : * arrays.
570 : *
571 : * This is for dimensions that can potentially be used by several arrays.
572 : * Not all drivers might implement this. To retrieve the dimensions used by
573 : * a specific array, use GDALMDArray::GetDimensions().
574 : *
575 : * Drivers known to implement it: MEM, netCDF
576 : *
577 : * This is the same as the C function GDALGroupGetDimensions().
578 : *
579 : * @param papszOptions Driver specific options determining how groups
580 : * should be retrieved. Pass nullptr for default behavior.
581 : *
582 : * @return the dimensions.
583 : */
584 : std::vector<std::shared_ptr<GDALDimension>>
585 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
586 : {
587 11 : return {};
588 : }
589 :
590 : /************************************************************************/
591 : /* GetStructuralInfo() */
592 : /************************************************************************/
593 :
594 : /** Return structural information on the group.
595 : *
596 : * This may be the compression, etc..
597 : *
598 : * The return value should not be freed and is valid until GDALGroup is
599 : * released or this function called again.
600 : *
601 : * This is the same as the C function GDALGroupGetStructuralInfo().
602 : */
603 42 : CSLConstList GDALGroup::GetStructuralInfo() const
604 : {
605 42 : return nullptr;
606 : }
607 :
608 : /************************************************************************/
609 : /* CreateGroup() */
610 : /************************************************************************/
611 :
612 : /** Create a sub-group within a group.
613 : *
614 : * Optionally implemented by drivers.
615 : *
616 : * Drivers known to implement it: MEM, netCDF
617 : *
618 : * This is the same as the C function GDALGroupCreateGroup().
619 : *
620 : * @param osName Sub-group name.
621 : * @param papszOptions Driver specific options determining how the sub-group
622 : * should be created.
623 : *
624 : * @return the new sub-group, or nullptr in case of error.
625 : */
626 : std::shared_ptr<GDALGroup>
627 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
628 : CPL_UNUSED CSLConstList papszOptions)
629 : {
630 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
631 0 : return nullptr;
632 : }
633 :
634 : /************************************************************************/
635 : /* DeleteGroup() */
636 : /************************************************************************/
637 :
638 : /** Delete a sub-group from a group.
639 : *
640 : * Optionally implemented.
641 : *
642 : * After this call, if a previously obtained instance of the deleted object
643 : * is still alive, no method other than for freeing it should be invoked.
644 : *
645 : * Drivers known to implement it: MEM, Zarr
646 : *
647 : * This is the same as the C function GDALGroupDeleteGroup().
648 : *
649 : * @param osName Sub-group name.
650 : * @param papszOptions Driver specific options determining how the group.
651 : * should be deleted.
652 : *
653 : * @return true in case of success
654 : * @since GDAL 3.8
655 : */
656 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
657 : CPL_UNUSED CSLConstList papszOptions)
658 : {
659 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
660 0 : return false;
661 : }
662 :
663 : /************************************************************************/
664 : /* CreateDimension() */
665 : /************************************************************************/
666 :
667 : /** Create a dimension within a group.
668 : *
669 : * @note Driver implementation: drivers supporting CreateDimension() should
670 : * implement this method, but do not have necessarily to implement
671 : * GDALGroup::GetDimensions().
672 : *
673 : * Drivers known to implement it: MEM, netCDF
674 : *
675 : * This is the same as the C function GDALGroupCreateDimension().
676 : *
677 : * @param osName Dimension name.
678 : * @param osType Dimension type (might be empty, and ignored by drivers)
679 : * @param osDirection Dimension direction (might be empty, and ignored by
680 : * drivers)
681 : * @param nSize Number of values indexed by this dimension. Should be > 0.
682 : * @param papszOptions Driver specific options determining how the dimension
683 : * should be created.
684 : *
685 : * @return the new dimension, or nullptr if case of error
686 : */
687 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
688 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
689 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
690 : CPL_UNUSED CSLConstList papszOptions)
691 : {
692 0 : CPLError(CE_Failure, CPLE_NotSupported,
693 : "CreateDimension() not implemented");
694 0 : return nullptr;
695 : }
696 :
697 : /************************************************************************/
698 : /* CreateMDArray() */
699 : /************************************************************************/
700 :
701 : /** Create a multidimensional array within a group.
702 : *
703 : * It is recommended that the GDALDimension objects passed in aoDimensions
704 : * belong to this group, either by retrieving them with GetDimensions()
705 : * or creating a new one with CreateDimension().
706 : *
707 : * Optionally implemented.
708 : *
709 : * Drivers known to implement it: MEM, netCDF
710 : *
711 : * This is the same as the C function GDALGroupCreateMDArray().
712 : *
713 : * @note Driver implementation: drivers should take into account the possibility
714 : * that GDALDimension object passed in aoDimensions might belong to a different
715 : * group / dataset / driver and act accordingly.
716 : *
717 : * @param osName Array name.
718 : * @param aoDimensions List of dimensions, ordered from the slowest varying
719 : * dimension first to the fastest varying dimension last.
720 : * Might be empty for a scalar array (if supported by
721 : * driver)
722 : * @param oDataType Array data type.
723 : * @param papszOptions Driver specific options determining how the array
724 : * should be created.
725 : *
726 : * @return the new array, or nullptr in case of error
727 : */
728 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
729 : CPL_UNUSED const std::string &osName,
730 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
731 : CPL_UNUSED const GDALExtendedDataType &oDataType,
732 : CPL_UNUSED CSLConstList papszOptions)
733 : {
734 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
735 0 : return nullptr;
736 : }
737 :
738 : /************************************************************************/
739 : /* DeleteMDArray() */
740 : /************************************************************************/
741 :
742 : /** Delete an array from a group.
743 : *
744 : * Optionally implemented.
745 : *
746 : * After this call, if a previously obtained instance of the deleted object
747 : * is still alive, no method other than for freeing it should be invoked.
748 : *
749 : * Drivers known to implement it: MEM, Zarr
750 : *
751 : * This is the same as the C function GDALGroupDeleteMDArray().
752 : *
753 : * @param osName Arrayname.
754 : * @param papszOptions Driver specific options determining how the array.
755 : * should be deleted.
756 : *
757 : * @return true in case of success
758 : * @since GDAL 3.8
759 : */
760 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
761 : CPL_UNUSED CSLConstList papszOptions)
762 : {
763 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
764 0 : return false;
765 : }
766 :
767 : /************************************************************************/
768 : /* GetTotalCopyCost() */
769 : /************************************************************************/
770 :
771 : /** Return a total "cost" to copy the group.
772 : *
773 : * Used as a parameter for CopFrom()
774 : */
775 26 : GUInt64 GDALGroup::GetTotalCopyCost() const
776 : {
777 26 : GUInt64 nCost = COPY_COST;
778 26 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
779 :
780 52 : auto groupNames = GetGroupNames();
781 32 : for (const auto &name : groupNames)
782 : {
783 12 : auto subGroup = OpenGroup(name);
784 6 : if (subGroup)
785 : {
786 6 : nCost += subGroup->GetTotalCopyCost();
787 : }
788 : }
789 :
790 26 : auto arrayNames = GetMDArrayNames();
791 73 : for (const auto &name : arrayNames)
792 : {
793 94 : auto array = OpenMDArray(name);
794 47 : if (array)
795 : {
796 47 : nCost += array->GetTotalCopyCost();
797 : }
798 : }
799 52 : return nCost;
800 : }
801 :
802 : /************************************************************************/
803 : /* CopyFrom() */
804 : /************************************************************************/
805 :
806 : /** Copy the content of a group into a new (generally empty) group.
807 : *
808 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
809 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
810 : * of some output drivers this is not recommended)
811 : * @param poSrcGroup Source group. Must NOT be nullptr.
812 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
813 : * stop the copy. In relaxed mode, the copy will be attempted to
814 : * be pursued.
815 : * @param nCurCost Should be provided as a variable initially set to 0.
816 : * @param nTotalCost Total cost from GetTotalCopyCost().
817 : * @param pfnProgress Progress callback, or nullptr.
818 : * @param pProgressData Progress user data, or nulptr.
819 : * @param papszOptions Creation options. Currently, only array creation
820 : * options are supported. They must be prefixed with
821 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
822 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
823 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
824 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
825 : * Restriction to arrays of a given name is done with adding
826 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
827 : * a full qualified name.
828 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
829 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
830 : * scaled to UInt16 with scale and offset values being computed from the minimum
831 : * and maximum of the source array. The integer data type used can be set with
832 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
833 : *
834 : * @return true in case of success (or partial success if bStrict == false).
835 : */
836 26 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
837 : GDALDataset *poSrcDS,
838 : const std::shared_ptr<GDALGroup> &poSrcGroup,
839 : bool bStrict, GUInt64 &nCurCost,
840 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
841 : void *pProgressData, CSLConstList papszOptions)
842 : {
843 26 : if (pfnProgress == nullptr)
844 0 : pfnProgress = GDALDummyProgress;
845 :
846 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
847 : if (!(x)) \
848 : { \
849 : if (bStrict) \
850 : return false; \
851 : continue; \
852 : } \
853 : (void)0
854 :
855 : try
856 : {
857 26 : nCurCost += GDALGroup::COPY_COST;
858 :
859 52 : const auto srcDims = poSrcGroup->GetDimensions();
860 : std::map<std::string, std::shared_ptr<GDALDimension>>
861 52 : mapExistingDstDims;
862 52 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
863 65 : for (const auto &dim : srcDims)
864 : {
865 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
866 39 : dim->GetDirection(), dim->GetSize());
867 39 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
868 39 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
869 78 : auto poIndexingVarSrc(dim->GetIndexingVariable());
870 39 : if (poIndexingVarSrc)
871 : {
872 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
873 20 : ->GetName()] =
874 40 : dim->GetName();
875 : }
876 : }
877 :
878 52 : auto attrs = poSrcGroup->GetAttributes();
879 36 : for (const auto &attr : attrs)
880 : {
881 : auto dstAttr =
882 10 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
883 20 : attr->GetDataType());
884 10 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
885 10 : auto raw(attr->ReadAsRaw());
886 10 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
887 0 : return false;
888 : }
889 26 : if (!attrs.empty())
890 : {
891 6 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
892 6 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
893 0 : return false;
894 : }
895 :
896 : const auto CopyArray =
897 47 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
898 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
899 : papszOptions, bStrict, &nCurCost,
900 424 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
901 : {
902 : // Map source dimensions to target dimensions
903 94 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
904 47 : const auto &srcArrayDims(srcArray->GetDimensions());
905 120 : for (const auto &dim : srcArrayDims)
906 : {
907 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
908 73 : dim->GetFullName());
909 73 : if (dstDim && dstDim->GetSize() == dim->GetSize())
910 : {
911 63 : dstArrayDims.emplace_back(dstDim);
912 : }
913 : else
914 : {
915 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
916 19 : if (oIter != mapExistingDstDims.end() &&
917 9 : oIter->second->GetSize() == dim->GetSize())
918 : {
919 8 : dstArrayDims.emplace_back(oIter->second);
920 : }
921 : else
922 : {
923 2 : std::string newDimName;
924 2 : if (oIter == mapExistingDstDims.end())
925 : {
926 1 : newDimName = dim->GetName();
927 : }
928 : else
929 : {
930 1 : std::string newDimNamePrefix(srcArray->GetName() +
931 3 : '_' + dim->GetName());
932 1 : newDimName = newDimNamePrefix;
933 1 : int nIterCount = 2;
934 0 : while (
935 1 : cpl::contains(mapExistingDstDims, newDimName))
936 : {
937 0 : newDimName = newDimNamePrefix +
938 0 : CPLSPrintf("_%d", nIterCount);
939 0 : nIterCount++;
940 : }
941 : }
942 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
943 : dim->GetDirection(),
944 4 : dim->GetSize());
945 2 : if (!dstDim)
946 0 : return false;
947 2 : mapExistingDstDims[newDimName] = dstDim;
948 2 : dstArrayDims.emplace_back(dstDim);
949 : }
950 : }
951 : }
952 :
953 94 : CPLStringList aosArrayCO;
954 47 : bool bAutoScale = false;
955 47 : GDALDataType eAutoScaleType = GDT_UInt16;
956 54 : for (const char *pszItem : cpl::Iterate(papszOptions))
957 : {
958 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
959 : {
960 7 : const char *pszOption = pszItem + strlen("ARRAY:");
961 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
962 : {
963 1 : const char *pszNext = strchr(pszOption, ':');
964 1 : if (pszNext != nullptr)
965 : {
966 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
967 1 : if (static_cast<size_t>(nDim) ==
968 1 : dstArrayDims.size())
969 : {
970 1 : pszOption = pszNext + 1;
971 : }
972 : else
973 : {
974 0 : pszOption = nullptr;
975 : }
976 : }
977 : }
978 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
979 : {
980 2 : const char *pszName = pszOption + strlen("IF(NAME=");
981 2 : const char *pszNext = strchr(pszName, ':');
982 2 : if (pszNext != nullptr && pszNext > pszName &&
983 2 : pszNext[-1] == ')')
984 : {
985 4 : CPLString osName;
986 2 : osName.assign(pszName, pszNext - pszName - 1);
987 3 : if (osName == srcArray->GetName() ||
988 1 : osName == srcArray->GetFullName())
989 : {
990 2 : pszOption = pszNext + 1;
991 : }
992 : else
993 : {
994 0 : pszOption = nullptr;
995 : }
996 : }
997 : }
998 7 : if (pszOption)
999 : {
1000 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1001 : {
1002 : bAutoScale =
1003 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1004 : }
1005 5 : else if (STARTS_WITH_CI(pszOption,
1006 : "AUTOSCALE_DATA_TYPE="))
1007 : {
1008 1 : const char *pszDataType =
1009 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1010 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1011 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1012 1 : GDALDataTypeIsFloating(eAutoScaleType))
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_NotSupported,
1015 : "Unsupported value for "
1016 : "AUTOSCALE_DATA_TYPE");
1017 0 : return false;
1018 : }
1019 : }
1020 : else
1021 : {
1022 4 : aosArrayCO.AddString(pszOption);
1023 : }
1024 : }
1025 : }
1026 : }
1027 :
1028 : auto oIterDimName =
1029 47 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1030 47 : const auto &srcArrayType = srcArray->GetDataType();
1031 :
1032 47 : std::shared_ptr<GDALMDArray> dstArray;
1033 :
1034 : // Only autoscale non-indexing variables
1035 47 : bool bHasOffset = false;
1036 47 : bool bHasScale = false;
1037 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1038 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1039 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1040 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1041 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1042 51 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1043 49 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1044 : {
1045 2 : constexpr bool bApproxOK = false;
1046 2 : constexpr bool bForce = true;
1047 2 : double dfMin = 0.0;
1048 2 : double dfMax = 0.0;
1049 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1050 : nullptr, nullptr, nullptr, nullptr,
1051 2 : nullptr) != CE_None)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "Could not retrieve statistics for array %s",
1055 0 : srcArray->GetName().c_str());
1056 0 : return false;
1057 : }
1058 2 : double dfDTMin = 0;
1059 2 : double dfDTMax = 0;
1060 : #define setDTMinMax(ctype) \
1061 : do \
1062 : { \
1063 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1064 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1065 : } while (0)
1066 :
1067 2 : switch (eAutoScaleType)
1068 : {
1069 0 : case GDT_Byte:
1070 0 : setDTMinMax(GByte);
1071 0 : break;
1072 0 : case GDT_Int8:
1073 0 : setDTMinMax(GInt8);
1074 0 : break;
1075 1 : case GDT_UInt16:
1076 1 : setDTMinMax(GUInt16);
1077 1 : break;
1078 1 : case GDT_Int16:
1079 1 : setDTMinMax(GInt16);
1080 1 : break;
1081 0 : case GDT_UInt32:
1082 0 : setDTMinMax(GUInt32);
1083 0 : break;
1084 0 : case GDT_Int32:
1085 0 : setDTMinMax(GInt32);
1086 0 : break;
1087 0 : case GDT_UInt64:
1088 0 : setDTMinMax(std::uint64_t);
1089 0 : break;
1090 0 : case GDT_Int64:
1091 0 : setDTMinMax(std::int64_t);
1092 0 : break;
1093 0 : case GDT_Float16:
1094 : case GDT_Float32:
1095 : case GDT_Float64:
1096 : case GDT_Unknown:
1097 : case GDT_CInt16:
1098 : case GDT_CInt32:
1099 : case GDT_CFloat16:
1100 : case GDT_CFloat32:
1101 : case GDT_CFloat64:
1102 : case GDT_TypeCount:
1103 0 : CPLAssert(false);
1104 : }
1105 :
1106 : dstArray =
1107 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1108 4 : GDALExtendedDataType::Create(eAutoScaleType),
1109 4 : aosArrayCO.List());
1110 2 : if (!dstArray)
1111 0 : return !bStrict;
1112 :
1113 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1114 : {
1115 : // If there's a nodata value in the source array, reserve
1116 : // DTMax for that purpose in the target scaled array
1117 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AppDefined,
1120 : "Cannot set nodata value");
1121 0 : return false;
1122 : }
1123 1 : dfDTMax--;
1124 : }
1125 2 : const double dfScale =
1126 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1127 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1128 :
1129 4 : if (!dstArray->SetOffset(dfOffset) ||
1130 2 : !dstArray->SetScale(dfScale))
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Cannot set scale/offset");
1134 0 : return false;
1135 : }
1136 :
1137 2 : auto poUnscaled = dstArray->GetUnscaled();
1138 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1139 : {
1140 1 : poUnscaled->SetNoDataValue(
1141 : srcArray->GetNoDataValueAsDouble());
1142 : }
1143 :
1144 : // Copy source array into unscaled array
1145 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1146 : nCurCost, nTotalCost, pfnProgress,
1147 2 : pProgressData))
1148 : {
1149 0 : return false;
1150 : }
1151 : }
1152 : else
1153 : {
1154 90 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1155 90 : srcArrayType, aosArrayCO.List());
1156 45 : if (!dstArray)
1157 0 : return !bStrict;
1158 :
1159 90 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1160 : nCurCost, nTotalCost, pfnProgress,
1161 45 : pProgressData))
1162 : {
1163 0 : return false;
1164 : }
1165 : }
1166 :
1167 : // If this array is the indexing variable of a dimension, link them
1168 : // together.
1169 47 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1170 : {
1171 : auto oCorrespondingDimIter =
1172 20 : mapExistingDstDims.find(oIterDimName->second);
1173 20 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1174 : {
1175 : CPLErrorStateBackuper oErrorStateBackuper(
1176 20 : CPLQuietErrorHandler);
1177 40 : oCorrespondingDimIter->second->SetIndexingVariable(
1178 20 : std::move(dstArray));
1179 : }
1180 : }
1181 :
1182 47 : return true;
1183 26 : };
1184 :
1185 52 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1186 :
1187 : // Start by copying arrays that are indexing variables of dimensions
1188 73 : for (const auto &name : arrayNames)
1189 : {
1190 47 : auto srcArray = poSrcGroup->OpenMDArray(name);
1191 47 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1192 :
1193 47 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1194 47 : srcArray->GetName()))
1195 : {
1196 20 : if (!CopyArray(srcArray))
1197 0 : return false;
1198 : }
1199 : }
1200 :
1201 : // Then copy regular arrays
1202 73 : for (const auto &name : arrayNames)
1203 : {
1204 47 : auto srcArray = poSrcGroup->OpenMDArray(name);
1205 47 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1206 :
1207 47 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1208 47 : srcArray->GetName()))
1209 : {
1210 27 : if (!CopyArray(srcArray))
1211 0 : return false;
1212 : }
1213 : }
1214 :
1215 52 : const auto groupNames = poSrcGroup->GetGroupNames();
1216 32 : for (const auto &name : groupNames)
1217 : {
1218 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1219 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1220 6 : auto dstSubGroup = CreateGroup(name);
1221 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1222 12 : if (!dstSubGroup->CopyFrom(
1223 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1224 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1225 0 : return false;
1226 : }
1227 :
1228 26 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1229 0 : return false;
1230 :
1231 26 : return true;
1232 : }
1233 0 : catch (const std::exception &e)
1234 : {
1235 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1236 0 : return false;
1237 : }
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* GetInnerMostGroup() */
1242 : /************************************************************************/
1243 :
1244 : //! @cond Doxygen_Suppress
1245 : const GDALGroup *
1246 1304 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1247 : std::shared_ptr<GDALGroup> &curGroupHolder,
1248 : std::string &osLastPart) const
1249 : {
1250 1304 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1251 6 : return nullptr;
1252 1298 : const GDALGroup *poCurGroup = this;
1253 : CPLStringList aosTokens(
1254 2596 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1255 1298 : if (aosTokens.size() == 0)
1256 : {
1257 0 : return nullptr;
1258 : }
1259 :
1260 1643 : for (int i = 0; i < aosTokens.size() - 1; i++)
1261 : {
1262 357 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1263 357 : if (!curGroupHolder)
1264 : {
1265 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1266 : aosTokens[i]);
1267 12 : return nullptr;
1268 : }
1269 345 : poCurGroup = curGroupHolder.get();
1270 : }
1271 1286 : osLastPart = aosTokens[aosTokens.size() - 1];
1272 1286 : return poCurGroup;
1273 : }
1274 :
1275 : //! @endcond
1276 :
1277 : /************************************************************************/
1278 : /* OpenMDArrayFromFullname() */
1279 : /************************************************************************/
1280 :
1281 : /** Get an array from its fully qualified name */
1282 : std::shared_ptr<GDALMDArray>
1283 555 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1284 : CSLConstList papszOptions) const
1285 : {
1286 1110 : std::string osName;
1287 555 : std::shared_ptr<GDALGroup> curGroupHolder;
1288 555 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1289 555 : if (poGroup == nullptr)
1290 12 : return nullptr;
1291 543 : return poGroup->OpenMDArray(osName, papszOptions);
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* OpenAttributeFromFullname() */
1296 : /************************************************************************/
1297 :
1298 : /** Get an attribute from its fully qualified name */
1299 : std::shared_ptr<GDALAttribute>
1300 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1301 : CSLConstList papszOptions) const
1302 : {
1303 9 : const auto pos = osFullName.rfind('/');
1304 9 : if (pos == std::string::npos)
1305 0 : return nullptr;
1306 18 : const std::string attrName = osFullName.substr(pos + 1);
1307 9 : if (pos == 0)
1308 2 : return GetAttribute(attrName);
1309 14 : const std::string container = osFullName.substr(0, pos);
1310 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1311 7 : if (poArray)
1312 4 : return poArray->GetAttribute(attrName);
1313 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1314 3 : if (poGroup)
1315 1 : return poGroup->GetAttribute(attrName);
1316 2 : return nullptr;
1317 : }
1318 :
1319 : /************************************************************************/
1320 : /* ResolveMDArray() */
1321 : /************************************************************************/
1322 :
1323 : /** Locate an array in a group and its subgroups by name.
1324 : *
1325 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1326 : * used
1327 : * Otherwise the search will start from the group identified by osStartingPath,
1328 : * and an array whose name is osName will be looked for in this group (if
1329 : * osStartingPath is empty or "/", then the current group is used). If there
1330 : * is no match, then a recursive descendent search will be made in its
1331 : * subgroups. If there is no match in the subgroups, then the parent (if
1332 : * existing) of the group pointed by osStartingPath will be used as the new
1333 : * starting point for the search.
1334 : *
1335 : * @param osName name, qualified or not
1336 : * @param osStartingPath fully qualified name of the (sub-)group from which
1337 : * the search should be started. If this is a non-empty
1338 : * string, the group on which this method is called should
1339 : * nominally be the root group (otherwise the path will
1340 : * be interpreted as from the current group)
1341 : * @param papszOptions options to pass to OpenMDArray()
1342 : * @since GDAL 3.2
1343 : */
1344 : std::shared_ptr<GDALMDArray>
1345 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1346 : const std::string &osStartingPath,
1347 : CSLConstList papszOptions) const
1348 : {
1349 19 : if (!osName.empty() && osName[0] == '/')
1350 : {
1351 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1352 1 : if (poArray)
1353 1 : return poArray;
1354 : }
1355 36 : std::string osPath(osStartingPath);
1356 36 : std::set<std::string> oSetAlreadyVisited;
1357 :
1358 : while (true)
1359 : {
1360 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1361 0 : std::shared_ptr<GDALGroup> poGroup;
1362 :
1363 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1364 22 : bool goOn = false;
1365 22 : if (osPath.empty() || osPath == "/")
1366 : {
1367 11 : goOn = true;
1368 : }
1369 : else
1370 : {
1371 22 : std::string osLastPart;
1372 : const GDALGroup *poGroupPtr =
1373 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1374 11 : if (poGroupPtr)
1375 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1376 22 : if (poGroup &&
1377 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1378 : {
1379 11 : oQueue.push(poGroup);
1380 11 : goOn = true;
1381 : }
1382 : }
1383 :
1384 22 : if (goOn)
1385 : {
1386 17 : do
1387 : {
1388 : const GDALGroup *groupPtr;
1389 39 : if (!oQueue.empty())
1390 : {
1391 28 : poGroup = oQueue.front();
1392 28 : oQueue.pop();
1393 28 : groupPtr = poGroup.get();
1394 : }
1395 : else
1396 : {
1397 11 : groupPtr = this;
1398 : }
1399 :
1400 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1401 39 : if (poArray)
1402 16 : return poArray;
1403 :
1404 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1405 47 : for (const auto &osGroupName : aosGroupNames)
1406 : {
1407 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1408 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1409 48 : poSubGroup->GetFullName()))
1410 : {
1411 24 : oQueue.push(poSubGroup);
1412 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1413 : }
1414 : }
1415 23 : } while (!oQueue.empty());
1416 : }
1417 :
1418 6 : if (osPath.empty() || osPath == "/")
1419 2 : break;
1420 :
1421 4 : const auto nPos = osPath.rfind('/');
1422 4 : if (nPos == 0)
1423 1 : osPath = "/";
1424 : else
1425 : {
1426 3 : if (nPos == std::string::npos)
1427 0 : break;
1428 3 : osPath.resize(nPos);
1429 : }
1430 4 : }
1431 2 : return nullptr;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* OpenGroupFromFullname() */
1436 : /************************************************************************/
1437 :
1438 : /** Get a group from its fully qualified name.
1439 : * @since GDAL 3.2
1440 : */
1441 : std::shared_ptr<GDALGroup>
1442 569 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1443 : CSLConstList papszOptions) const
1444 : {
1445 1138 : std::string osName;
1446 569 : std::shared_ptr<GDALGroup> curGroupHolder;
1447 569 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1448 569 : if (poGroup == nullptr)
1449 4 : return nullptr;
1450 565 : return poGroup->OpenGroup(osName, papszOptions);
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* OpenDimensionFromFullname() */
1455 : /************************************************************************/
1456 :
1457 : /** Get a dimension from its fully qualified name */
1458 : std::shared_ptr<GDALDimension>
1459 169 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1460 : {
1461 338 : std::string osName;
1462 169 : std::shared_ptr<GDALGroup> curGroupHolder;
1463 169 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1464 169 : if (poGroup == nullptr)
1465 2 : return nullptr;
1466 334 : auto dims(poGroup->GetDimensions());
1467 288 : for (auto &dim : dims)
1468 : {
1469 242 : if (dim->GetName() == osName)
1470 121 : return dim;
1471 : }
1472 46 : return nullptr;
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* ClearStatistics() */
1477 : /************************************************************************/
1478 :
1479 : /**
1480 : * \brief Clear statistics.
1481 : *
1482 : * @since GDAL 3.4
1483 : */
1484 0 : void GDALGroup::ClearStatistics()
1485 : {
1486 0 : auto groupNames = GetGroupNames();
1487 0 : for (const auto &name : groupNames)
1488 : {
1489 0 : auto subGroup = OpenGroup(name);
1490 0 : if (subGroup)
1491 : {
1492 0 : subGroup->ClearStatistics();
1493 : }
1494 : }
1495 :
1496 0 : auto arrayNames = GetMDArrayNames();
1497 0 : for (const auto &name : arrayNames)
1498 : {
1499 0 : auto array = OpenMDArray(name);
1500 0 : if (array)
1501 : {
1502 0 : array->ClearStatistics();
1503 : }
1504 : }
1505 0 : }
1506 :
1507 : /************************************************************************/
1508 : /* Rename() */
1509 : /************************************************************************/
1510 :
1511 : /** Rename the group.
1512 : *
1513 : * This is not implemented by all drivers.
1514 : *
1515 : * Drivers known to implement it: MEM, netCDF, ZARR.
1516 : *
1517 : * This is the same as the C function GDALGroupRename().
1518 : *
1519 : * @param osNewName New name.
1520 : *
1521 : * @return true in case of success
1522 : * @since GDAL 3.8
1523 : */
1524 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1525 : {
1526 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1527 0 : return false;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* BaseRename() */
1532 : /************************************************************************/
1533 :
1534 : //! @cond Doxygen_Suppress
1535 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1536 : {
1537 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1538 8 : m_osFullName += osNewName;
1539 8 : m_osName = osNewName;
1540 :
1541 8 : NotifyChildrenOfRenaming();
1542 8 : }
1543 :
1544 : //! @endcond
1545 :
1546 : /************************************************************************/
1547 : /* ParentRenamed() */
1548 : /************************************************************************/
1549 :
1550 : //! @cond Doxygen_Suppress
1551 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1552 : {
1553 7 : m_osFullName = osNewParentFullName;
1554 7 : m_osFullName += "/";
1555 7 : m_osFullName += m_osName;
1556 :
1557 7 : NotifyChildrenOfRenaming();
1558 7 : }
1559 :
1560 : //! @endcond
1561 :
1562 : /************************************************************************/
1563 : /* Deleted() */
1564 : /************************************************************************/
1565 :
1566 : //! @cond Doxygen_Suppress
1567 22 : void GDALGroup::Deleted()
1568 : {
1569 22 : m_bValid = false;
1570 :
1571 22 : NotifyChildrenOfDeletion();
1572 22 : }
1573 :
1574 : //! @endcond
1575 :
1576 : /************************************************************************/
1577 : /* ParentDeleted() */
1578 : /************************************************************************/
1579 :
1580 : //! @cond Doxygen_Suppress
1581 3 : void GDALGroup::ParentDeleted()
1582 : {
1583 3 : Deleted();
1584 3 : }
1585 :
1586 : //! @endcond
1587 :
1588 : /************************************************************************/
1589 : /* CheckValidAndErrorOutIfNot() */
1590 : /************************************************************************/
1591 :
1592 : //! @cond Doxygen_Suppress
1593 16833 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1594 : {
1595 16833 : if (!m_bValid)
1596 : {
1597 14 : CPLError(CE_Failure, CPLE_AppDefined,
1598 : "This object has been deleted. No action on it is possible");
1599 : }
1600 16833 : return m_bValid;
1601 : }
1602 :
1603 : //! @endcond
1604 :
1605 : /************************************************************************/
1606 : /* ~GDALAbstractMDArray() */
1607 : /************************************************************************/
1608 :
1609 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1610 :
1611 : /************************************************************************/
1612 : /* GDALAbstractMDArray() */
1613 : /************************************************************************/
1614 :
1615 : //! @cond Doxygen_Suppress
1616 21339 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1617 21339 : const std::string &osName)
1618 : : m_osName(osName),
1619 : m_osFullName(
1620 21339 : !osParentName.empty()
1621 40943 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1622 83621 : : osName)
1623 : {
1624 21339 : }
1625 :
1626 : //! @endcond
1627 :
1628 : /************************************************************************/
1629 : /* GetDimensions() */
1630 : /************************************************************************/
1631 :
1632 : /** \fn GDALAbstractMDArray::GetDimensions() const
1633 : * \brief Return the dimensions of an attribute/array.
1634 : *
1635 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1636 : * similar to GDALAttributeGetDimensionsSize().
1637 : */
1638 :
1639 : /************************************************************************/
1640 : /* GetDataType() */
1641 : /************************************************************************/
1642 :
1643 : /** \fn GDALAbstractMDArray::GetDataType() const
1644 : * \brief Return the data type of an attribute/array.
1645 : *
1646 : * This is the same as the C functions GDALMDArrayGetDataType() and
1647 : * GDALAttributeGetDataType()
1648 : */
1649 :
1650 : /************************************************************************/
1651 : /* GetDimensionCount() */
1652 : /************************************************************************/
1653 :
1654 : /** Return the number of dimensions.
1655 : *
1656 : * Default implementation is GetDimensions().size(), and may be overridden by
1657 : * drivers if they have a faster / less expensive implementations.
1658 : *
1659 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1660 : * GDALAttributeGetDimensionCount().
1661 : *
1662 : */
1663 24011 : size_t GDALAbstractMDArray::GetDimensionCount() const
1664 : {
1665 24011 : return GetDimensions().size();
1666 : }
1667 :
1668 : /************************************************************************/
1669 : /* Rename() */
1670 : /************************************************************************/
1671 :
1672 : /** Rename the attribute/array.
1673 : *
1674 : * This is not implemented by all drivers.
1675 : *
1676 : * Drivers known to implement it: MEM, netCDF, Zarr.
1677 : *
1678 : * This is the same as the C functions GDALMDArrayRename() or
1679 : * GDALAttributeRename().
1680 : *
1681 : * @param osNewName New name.
1682 : *
1683 : * @return true in case of success
1684 : * @since GDAL 3.8
1685 : */
1686 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1689 0 : return false;
1690 : }
1691 :
1692 : /************************************************************************/
1693 : /* CopyValue() */
1694 : /************************************************************************/
1695 :
1696 : /** Convert a value from a source type to a destination type.
1697 : *
1698 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1699 : * that must be freed with CPLFree().
1700 : */
1701 81107 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1702 : const GDALExtendedDataType &srcType,
1703 : void *pDst,
1704 : const GDALExtendedDataType &dstType)
1705 : {
1706 158108 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1707 77001 : dstType.GetClass() == GEDTC_NUMERIC)
1708 : {
1709 76536 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1710 : dstType.GetNumericDataType(), 0, 1);
1711 76536 : return true;
1712 : }
1713 8461 : if (srcType.GetClass() == GEDTC_STRING &&
1714 3890 : dstType.GetClass() == GEDTC_STRING)
1715 : {
1716 : const char *srcStrPtr;
1717 3457 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1718 3457 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1719 3457 : *reinterpret_cast<void **>(pDst) = pszDup;
1720 3457 : return true;
1721 : }
1722 1579 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1723 465 : dstType.GetClass() == GEDTC_STRING)
1724 : {
1725 465 : const char *str = nullptr;
1726 465 : switch (srcType.GetNumericDataType())
1727 : {
1728 0 : case GDT_Unknown:
1729 0 : break;
1730 0 : case GDT_Byte:
1731 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1732 0 : break;
1733 3 : case GDT_Int8:
1734 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1735 3 : break;
1736 48 : case GDT_UInt16:
1737 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1738 48 : break;
1739 0 : case GDT_Int16:
1740 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1741 0 : break;
1742 8 : case GDT_UInt32:
1743 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1744 8 : break;
1745 60 : case GDT_Int32:
1746 60 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1747 60 : break;
1748 0 : case GDT_UInt64:
1749 : str =
1750 0 : CPLSPrintf(CPL_FRMT_GUIB,
1751 : static_cast<GUIntBig>(
1752 : *static_cast<const std::uint64_t *>(pSrc)));
1753 0 : break;
1754 24 : case GDT_Int64:
1755 24 : str = CPLSPrintf(CPL_FRMT_GIB,
1756 : static_cast<GIntBig>(
1757 : *static_cast<const std::int64_t *>(pSrc)));
1758 24 : break;
1759 0 : case GDT_Float16:
1760 0 : str = CPLSPrintf("%.5g",
1761 : double(*static_cast<const GFloat16 *>(pSrc)));
1762 0 : break;
1763 17 : case GDT_Float32:
1764 34 : str = CPLSPrintf(
1765 : "%.9g",
1766 17 : static_cast<double>(*static_cast<const float *>(pSrc)));
1767 17 : break;
1768 303 : case GDT_Float64:
1769 303 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1770 303 : break;
1771 2 : case GDT_CInt16:
1772 : {
1773 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1774 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1775 2 : break;
1776 : }
1777 0 : case GDT_CInt32:
1778 : {
1779 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1780 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1781 0 : break;
1782 : }
1783 0 : case GDT_CFloat16:
1784 : {
1785 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1786 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1787 0 : break;
1788 : }
1789 0 : case GDT_CFloat32:
1790 : {
1791 0 : const float *src = static_cast<const float *>(pSrc);
1792 0 : str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1793 0 : break;
1794 : }
1795 0 : case GDT_CFloat64:
1796 : {
1797 0 : const double *src = static_cast<const double *>(pSrc);
1798 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1799 0 : break;
1800 : }
1801 0 : case GDT_TypeCount:
1802 0 : CPLAssert(false);
1803 : break;
1804 : }
1805 465 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1806 465 : *reinterpret_cast<void **>(pDst) = pszDup;
1807 465 : return true;
1808 : }
1809 1082 : if (srcType.GetClass() == GEDTC_STRING &&
1810 433 : dstType.GetClass() == GEDTC_NUMERIC)
1811 : {
1812 : const char *srcStrPtr;
1813 433 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1814 433 : if (dstType.GetNumericDataType() == GDT_Int64)
1815 : {
1816 2 : *(static_cast<int64_t *>(pDst)) =
1817 2 : srcStrPtr == nullptr ? 0
1818 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1819 : }
1820 431 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1821 : {
1822 2 : *(static_cast<uint64_t *>(pDst)) =
1823 2 : srcStrPtr == nullptr
1824 2 : ? 0
1825 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1826 : }
1827 : else
1828 : {
1829 : // FIXME GDT_UInt64
1830 429 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1831 429 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1832 : dstType.GetNumericDataType(), 0, 1);
1833 : }
1834 433 : return true;
1835 : }
1836 432 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1837 216 : dstType.GetClass() == GEDTC_COMPOUND)
1838 : {
1839 216 : const auto &srcComponents = srcType.GetComponents();
1840 216 : const auto &dstComponents = dstType.GetComponents();
1841 216 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1842 216 : GByte *pabyDst = static_cast<GByte *>(pDst);
1843 :
1844 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1845 432 : srcComponentMap;
1846 1031 : for (const auto &srcComp : srcComponents)
1847 : {
1848 815 : srcComponentMap[srcComp->GetName()] = &srcComp;
1849 : }
1850 586 : for (const auto &dstComp : dstComponents)
1851 : {
1852 370 : auto oIter = srcComponentMap.find(dstComp->GetName());
1853 370 : if (oIter == srcComponentMap.end())
1854 0 : return false;
1855 370 : const auto &srcComp = *(oIter->second);
1856 1110 : if (!GDALExtendedDataType::CopyValue(
1857 370 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1858 370 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1859 : {
1860 0 : return false;
1861 : }
1862 : }
1863 216 : return true;
1864 : }
1865 :
1866 0 : return false;
1867 : }
1868 :
1869 : /************************************************************************/
1870 : /* CopyValues() */
1871 : /************************************************************************/
1872 :
1873 : /** Convert severals value from a source type to a destination type.
1874 : *
1875 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1876 : * that must be freed with CPLFree().
1877 : */
1878 365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1879 : const GDALExtendedDataType &srcType,
1880 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1881 : const GDALExtendedDataType &dstType,
1882 : GPtrDiff_t nDstStrideInElts,
1883 : size_t nValues)
1884 : {
1885 : const auto nSrcStrideInBytes =
1886 365 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1887 : const auto nDstStrideInBytes =
1888 365 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1889 631 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1890 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1891 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1892 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1893 897 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1894 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1895 : {
1896 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1897 : static_cast<int>(nSrcStrideInBytes), pDst,
1898 : dstType.GetNumericDataType(),
1899 : static_cast<int>(nDstStrideInBytes), nValues);
1900 : }
1901 : else
1902 : {
1903 99 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1904 99 : GByte *pabyDst = static_cast<GByte *>(pDst);
1905 198 : for (size_t i = 0; i < nValues; ++i)
1906 : {
1907 99 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1908 0 : return false;
1909 99 : pabySrc += nSrcStrideInBytes;
1910 99 : pabyDst += nDstStrideInBytes;
1911 : }
1912 : }
1913 365 : return true;
1914 : }
1915 :
1916 : /************************************************************************/
1917 : /* CheckReadWriteParams() */
1918 : /************************************************************************/
1919 : //! @cond Doxygen_Suppress
1920 8548 : bool GDALAbstractMDArray::CheckReadWriteParams(
1921 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1922 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1923 : const void *buffer, const void *buffer_alloc_start,
1924 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1925 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1926 : {
1927 0 : const auto lamda_error = []()
1928 : {
1929 0 : CPLError(CE_Failure, CPLE_AppDefined,
1930 : "Not all elements pointed by buffer will fit in "
1931 : "[buffer_alloc_start, "
1932 : "buffer_alloc_start + buffer_alloc_size]");
1933 0 : };
1934 :
1935 8548 : const auto &dims = GetDimensions();
1936 8548 : if (dims.empty())
1937 : {
1938 3422 : if (buffer_alloc_start)
1939 : {
1940 3037 : const size_t elementSize = bufferDataType.GetSize();
1941 3037 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1942 3037 : const GByte *paby_buffer_alloc_start =
1943 : static_cast<const GByte *>(buffer_alloc_start);
1944 3037 : const GByte *paby_buffer_alloc_end =
1945 : paby_buffer_alloc_start + buffer_alloc_size;
1946 :
1947 3037 : if (paby_buffer < paby_buffer_alloc_start ||
1948 3037 : paby_buffer + elementSize > paby_buffer_alloc_end)
1949 : {
1950 0 : lamda_error();
1951 0 : return false;
1952 : }
1953 : }
1954 3422 : return true;
1955 : }
1956 :
1957 5126 : if (arrayStep == nullptr)
1958 : {
1959 1409 : tmp_arrayStep.resize(dims.size(), 1);
1960 1409 : arrayStep = tmp_arrayStep.data();
1961 : }
1962 14335 : for (size_t i = 0; i < dims.size(); i++)
1963 : {
1964 9209 : assert(count);
1965 9209 : if (count[i] == 0)
1966 : {
1967 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1968 : static_cast<unsigned>(i));
1969 0 : return false;
1970 : }
1971 : }
1972 5126 : bool bufferStride_all_positive = true;
1973 5126 : if (bufferStride == nullptr)
1974 : {
1975 1110 : GPtrDiff_t stride = 1;
1976 1110 : assert(dims.empty() || count != nullptr);
1977 : // To compute strides we must proceed from the fastest varying dimension
1978 : // (the last one), and then reverse the result
1979 2515 : for (size_t i = dims.size(); i != 0;)
1980 : {
1981 1405 : --i;
1982 1405 : tmp_bufferStride.push_back(stride);
1983 1405 : GUInt64 newStride = 0;
1984 : bool bOK;
1985 : try
1986 : {
1987 1405 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1988 2810 : CPLSM(static_cast<uint64_t>(count[i])))
1989 1405 : .v();
1990 1405 : bOK = static_cast<size_t>(newStride) == newStride &&
1991 1405 : newStride < std::numeric_limits<size_t>::max() / 2;
1992 : }
1993 0 : catch (...)
1994 : {
1995 0 : bOK = false;
1996 : }
1997 1405 : if (!bOK)
1998 : {
1999 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2000 0 : return false;
2001 : }
2002 1405 : stride = static_cast<GPtrDiff_t>(newStride);
2003 : }
2004 1110 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2005 1110 : bufferStride = tmp_bufferStride.data();
2006 : }
2007 : else
2008 : {
2009 11818 : for (size_t i = 0; i < dims.size(); i++)
2010 : {
2011 7803 : if (bufferStride[i] < 0)
2012 : {
2013 1 : bufferStride_all_positive = false;
2014 1 : break;
2015 : }
2016 : }
2017 : }
2018 14306 : for (size_t i = 0; i < dims.size(); i++)
2019 : {
2020 9190 : assert(arrayStartIdx);
2021 9190 : assert(count);
2022 9190 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2023 : {
2024 2 : CPLError(CE_Failure, CPLE_AppDefined,
2025 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2026 : static_cast<unsigned>(i),
2027 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2028 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2029 2 : return false;
2030 : }
2031 : bool bOverflow;
2032 9188 : if (arrayStep[i] >= 0)
2033 : {
2034 : try
2035 : {
2036 8593 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2037 8595 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2038 34375 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2039 8593 : .v() >= dims[i]->GetSize();
2040 : }
2041 1 : catch (...)
2042 : {
2043 1 : bOverflow = true;
2044 : }
2045 8594 : if (bOverflow)
2046 : {
2047 5 : CPLError(CE_Failure, CPLE_AppDefined,
2048 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2049 : ">= " CPL_FRMT_GUIB,
2050 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2051 : static_cast<unsigned>(i),
2052 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2053 5 : return false;
2054 : }
2055 : }
2056 : else
2057 : {
2058 : try
2059 : {
2060 594 : bOverflow =
2061 594 : arrayStartIdx[i] <
2062 594 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2063 1188 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2064 : ? (static_cast<uint64_t>(1) << 63)
2065 1188 : : static_cast<uint64_t>(-arrayStep[i])))
2066 594 : .v();
2067 : }
2068 0 : catch (...)
2069 : {
2070 0 : bOverflow = true;
2071 : }
2072 594 : if (bOverflow)
2073 : {
2074 3 : CPLError(
2075 : CE_Failure, CPLE_AppDefined,
2076 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2077 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2078 : static_cast<unsigned>(i));
2079 3 : return false;
2080 : }
2081 : }
2082 : }
2083 :
2084 5116 : if (buffer_alloc_start)
2085 : {
2086 2636 : const size_t elementSize = bufferDataType.GetSize();
2087 2636 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2088 2636 : const GByte *paby_buffer_alloc_start =
2089 : static_cast<const GByte *>(buffer_alloc_start);
2090 2636 : const GByte *paby_buffer_alloc_end =
2091 : paby_buffer_alloc_start + buffer_alloc_size;
2092 2636 : if (bufferStride_all_positive)
2093 : {
2094 2636 : if (paby_buffer < paby_buffer_alloc_start)
2095 : {
2096 0 : lamda_error();
2097 0 : return false;
2098 : }
2099 2636 : GUInt64 nOffset = elementSize;
2100 7526 : for (size_t i = 0; i < dims.size(); i++)
2101 : {
2102 : try
2103 : {
2104 4890 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2105 4890 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2106 9780 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2107 19560 : CPLSM(static_cast<uint64_t>(elementSize)))
2108 4890 : .v();
2109 : }
2110 0 : catch (...)
2111 : {
2112 0 : lamda_error();
2113 0 : return false;
2114 : }
2115 : }
2116 : #if SIZEOF_VOIDP == 4
2117 : if (static_cast<size_t>(nOffset) != nOffset)
2118 : {
2119 : lamda_error();
2120 : return false;
2121 : }
2122 : #endif
2123 2636 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2124 : {
2125 0 : lamda_error();
2126 0 : return false;
2127 : }
2128 : }
2129 0 : else if (dims.size() < 31)
2130 : {
2131 : // Check all corners of the hypercube
2132 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2133 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2134 : {
2135 0 : const GByte *paby = paby_buffer;
2136 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2137 : i++)
2138 : {
2139 0 : if (iCornerCode & (1U << i))
2140 : {
2141 : // We should check for integer overflows
2142 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2143 : }
2144 : }
2145 0 : if (paby < paby_buffer_alloc_start ||
2146 0 : paby + elementSize > paby_buffer_alloc_end)
2147 : {
2148 0 : lamda_error();
2149 0 : return false;
2150 : }
2151 : }
2152 : }
2153 : }
2154 :
2155 5116 : return true;
2156 : }
2157 :
2158 : //! @endcond
2159 :
2160 : /************************************************************************/
2161 : /* Read() */
2162 : /************************************************************************/
2163 :
2164 : /** Read part or totality of a multidimensional array or attribute.
2165 : *
2166 : * This will extract the content of a hyper-rectangle from the array into
2167 : * a user supplied buffer.
2168 : *
2169 : * If bufferDataType is of type string, the values written in pDstBuffer
2170 : * will be char* pointers and the strings should be freed with CPLFree().
2171 : *
2172 : * This is the same as the C function GDALMDArrayRead().
2173 : *
2174 : * @param arrayStartIdx Values representing the starting index to read
2175 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2176 : * Array of GetDimensionCount() values. Must not be
2177 : * nullptr, unless for a zero-dimensional array.
2178 : *
2179 : * @param count Values representing the number of values to extract in
2180 : * each dimension.
2181 : * Array of GetDimensionCount() values. Must not be
2182 : * nullptr, unless for a zero-dimensional array.
2183 : *
2184 : * @param arrayStep Spacing between values to extract in each dimension.
2185 : * The spacing is in number of array elements, not bytes.
2186 : * If provided, must contain GetDimensionCount() values.
2187 : * If set to nullptr, [1, 1, ... 1] will be used as a
2188 : * default to indicate consecutive elements.
2189 : *
2190 : * @param bufferStride Spacing between values to store in pDstBuffer.
2191 : * The spacing is in number of array elements, not bytes.
2192 : * If provided, must contain GetDimensionCount() values.
2193 : * Negative values are possible (for example to reorder
2194 : * from bottom-to-top to top-to-bottom).
2195 : * If set to nullptr, will be set so that pDstBuffer is
2196 : * written in a compact way, with elements of the last /
2197 : * fastest varying dimension being consecutive.
2198 : *
2199 : * @param bufferDataType Data type of values in pDstBuffer.
2200 : *
2201 : * @param pDstBuffer User buffer to store the values read. Should be big
2202 : * enough to store the number of values indicated by
2203 : * count[] and with the spacing of bufferStride[].
2204 : *
2205 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2206 : * validity of pDstBuffer. pDstBufferAllocStart
2207 : * should be the pointer returned by the malloc() or equivalent call used to
2208 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2209 : * bufferStride[] values are all positive), but not necessarily. If specified,
2210 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2211 : * validation is needed, nullptr can be passed.
2212 : *
2213 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2214 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2215 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2216 : * set to the appropriate value.
2217 : * If no validation is needed, 0 can be passed.
2218 : *
2219 : * @return true in case of success.
2220 : */
2221 2754 : bool GDALAbstractMDArray::Read(
2222 : const GUInt64 *arrayStartIdx, const size_t *count,
2223 : const GInt64 *arrayStep, // step in elements
2224 : const GPtrDiff_t *bufferStride, // stride in elements
2225 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2226 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2227 : {
2228 2754 : if (!GetDataType().CanConvertTo(bufferDataType))
2229 : {
2230 0 : CPLError(CE_Failure, CPLE_AppDefined,
2231 : "Array data type is not convertible to buffer data type");
2232 0 : return false;
2233 : }
2234 :
2235 5508 : std::vector<GInt64> tmp_arrayStep;
2236 5508 : std::vector<GPtrDiff_t> tmp_bufferStride;
2237 2754 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2238 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2239 : nDstBufferAllocSize, tmp_arrayStep,
2240 : tmp_bufferStride))
2241 : {
2242 0 : return false;
2243 : }
2244 :
2245 2754 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2246 2754 : pDstBuffer);
2247 : }
2248 :
2249 : /************************************************************************/
2250 : /* IWrite() */
2251 : /************************************************************************/
2252 :
2253 : //! @cond Doxygen_Suppress
2254 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2255 : const GInt64 *, const GPtrDiff_t *,
2256 : const GDALExtendedDataType &, const void *)
2257 : {
2258 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2259 1 : return false;
2260 : }
2261 :
2262 : //! @endcond
2263 :
2264 : /************************************************************************/
2265 : /* Write() */
2266 : /************************************************************************/
2267 :
2268 : /** Write part or totality of a multidimensional array or attribute.
2269 : *
2270 : * This will set the content of a hyper-rectangle into the array from
2271 : * a user supplied buffer.
2272 : *
2273 : * If bufferDataType is of type string, the values read from pSrcBuffer
2274 : * will be char* pointers.
2275 : *
2276 : * This is the same as the C function GDALMDArrayWrite().
2277 : *
2278 : * @param arrayStartIdx Values representing the starting index to write
2279 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2280 : * Array of GetDimensionCount() values. Must not be
2281 : * nullptr, unless for a zero-dimensional array.
2282 : *
2283 : * @param count Values representing the number of values to write in
2284 : * each dimension.
2285 : * Array of GetDimensionCount() values. Must not be
2286 : * nullptr, unless for a zero-dimensional array.
2287 : *
2288 : * @param arrayStep Spacing between values to write in each dimension.
2289 : * The spacing is in number of array elements, not bytes.
2290 : * If provided, must contain GetDimensionCount() values.
2291 : * If set to nullptr, [1, 1, ... 1] will be used as a
2292 : * default to indicate consecutive elements.
2293 : *
2294 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2295 : * The spacing is in number of array elements, not bytes.
2296 : * If provided, must contain GetDimensionCount() values.
2297 : * Negative values are possible (for example to reorder
2298 : * from bottom-to-top to top-to-bottom).
2299 : * If set to nullptr, will be set so that pSrcBuffer is
2300 : * written in a compact way, with elements of the last /
2301 : * fastest varying dimension being consecutive.
2302 : *
2303 : * @param bufferDataType Data type of values in pSrcBuffer.
2304 : *
2305 : * @param pSrcBuffer User buffer to read the values from. Should be big
2306 : * enough to store the number of values indicated by
2307 : * count[] and with the spacing of bufferStride[].
2308 : *
2309 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2310 : * validity of pSrcBuffer. pSrcBufferAllocStart
2311 : * should be the pointer returned by the malloc() or equivalent call used to
2312 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2313 : * bufferStride[] values are all positive), but not necessarily. If specified,
2314 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2315 : * validation is needed, nullptr can be passed.
2316 : *
2317 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2318 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2319 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2320 : * set to the appropriate value.
2321 : * If no validation is needed, 0 can be passed.
2322 : *
2323 : * @return true in case of success.
2324 : */
2325 1843 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2326 : const size_t *count, const GInt64 *arrayStep,
2327 : const GPtrDiff_t *bufferStride,
2328 : const GDALExtendedDataType &bufferDataType,
2329 : const void *pSrcBuffer,
2330 : const void *pSrcBufferAllocStart,
2331 : size_t nSrcBufferAllocSize)
2332 : {
2333 1843 : if (!bufferDataType.CanConvertTo(GetDataType()))
2334 : {
2335 0 : CPLError(CE_Failure, CPLE_AppDefined,
2336 : "Buffer data type is not convertible to array data type");
2337 0 : return false;
2338 : }
2339 :
2340 3686 : std::vector<GInt64> tmp_arrayStep;
2341 3686 : std::vector<GPtrDiff_t> tmp_bufferStride;
2342 1843 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2343 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2344 : nSrcBufferAllocSize, tmp_arrayStep,
2345 : tmp_bufferStride))
2346 : {
2347 0 : return false;
2348 : }
2349 :
2350 1843 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2351 1843 : pSrcBuffer);
2352 : }
2353 :
2354 : /************************************************************************/
2355 : /* GetTotalElementsCount() */
2356 : /************************************************************************/
2357 :
2358 : /** Return the total number of values in the array.
2359 : *
2360 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2361 : * and GDALAttributeGetTotalElementsCount().
2362 : *
2363 : */
2364 1201 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2365 : {
2366 1201 : const auto &dims = GetDimensions();
2367 1201 : if (dims.empty())
2368 594 : return 1;
2369 607 : GUInt64 nElts = 1;
2370 1336 : for (const auto &dim : dims)
2371 : {
2372 : try
2373 : {
2374 729 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2375 2187 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2376 729 : .v();
2377 : }
2378 0 : catch (...)
2379 : {
2380 0 : return 0;
2381 : }
2382 : }
2383 607 : return nElts;
2384 : }
2385 :
2386 : /************************************************************************/
2387 : /* GetBlockSize() */
2388 : /************************************************************************/
2389 :
2390 : /** Return the "natural" block size of the array along all dimensions.
2391 : *
2392 : * Some drivers might organize the array in tiles/blocks and reading/writing
2393 : * aligned on those tile/block boundaries will be more efficient.
2394 : *
2395 : * The returned number of elements in the vector is the same as
2396 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2397 : * the natural block size along the considered dimension.
2398 : * "Flat" arrays will typically return a vector of values set to 0.
2399 : *
2400 : * The default implementation will return a vector of values set to 0.
2401 : *
2402 : * This method is used by GetProcessingChunkSize().
2403 : *
2404 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2405 : * theoretical case of a 32-bit platform, this might exceed its size_t
2406 : * allocation capabilities.
2407 : *
2408 : * This is the same as the C function GDALMDArrayGetBlockSize().
2409 : *
2410 : * @return the block size, in number of elements along each dimension.
2411 : */
2412 280 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2413 : {
2414 280 : return std::vector<GUInt64>(GetDimensionCount());
2415 : }
2416 :
2417 : /************************************************************************/
2418 : /* GetProcessingChunkSize() */
2419 : /************************************************************************/
2420 :
2421 : /** \brief Return an optimal chunk size for read/write operations, given the
2422 : * natural block size and memory constraints specified.
2423 : *
2424 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2425 : * multiple of those returned by GetBlockSize() (unless the block define by
2426 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2427 : * returned by this method).
2428 : *
2429 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2430 : *
2431 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2432 : * chunk.
2433 : *
2434 : * @return the chunk size, in number of elements along each dimension.
2435 : */
2436 : std::vector<size_t>
2437 71 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2438 : {
2439 71 : const auto &dims = GetDimensions();
2440 71 : const auto &nDTSize = GetDataType().GetSize();
2441 71 : std::vector<size_t> anChunkSize;
2442 142 : auto blockSize = GetBlockSize();
2443 71 : CPLAssert(blockSize.size() == dims.size());
2444 71 : size_t nChunkSize = nDTSize;
2445 71 : bool bOverflow = false;
2446 71 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2447 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2448 : // [1, min(sizet_max, dim_size[i])]
2449 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2450 201 : for (size_t i = 0; i < dims.size(); i++)
2451 : {
2452 : const auto sizeDimI =
2453 260 : std::max(static_cast<size_t>(1),
2454 260 : static_cast<size_t>(
2455 260 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2456 130 : std::min(blockSize[i], dims[i]->GetSize()))));
2457 130 : anChunkSize.push_back(sizeDimI);
2458 130 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2459 : {
2460 4 : bOverflow = true;
2461 : }
2462 : else
2463 : {
2464 126 : nChunkSize *= sizeDimI;
2465 : }
2466 : }
2467 71 : if (nChunkSize == 0)
2468 0 : return anChunkSize;
2469 :
2470 : // If the product of all anChunkSize[i] does not fit on size_t, then
2471 : // set lowest anChunkSize[i] to 1.
2472 71 : if (bOverflow)
2473 : {
2474 2 : nChunkSize = nDTSize;
2475 2 : bOverflow = false;
2476 8 : for (size_t i = dims.size(); i > 0;)
2477 : {
2478 6 : --i;
2479 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2480 : {
2481 4 : bOverflow = true;
2482 4 : anChunkSize[i] = 1;
2483 : }
2484 : else
2485 : {
2486 2 : nChunkSize *= anChunkSize[i];
2487 : }
2488 : }
2489 : }
2490 :
2491 71 : nChunkSize = nDTSize;
2492 142 : std::vector<size_t> anAccBlockSizeFromStart;
2493 201 : for (size_t i = 0; i < dims.size(); i++)
2494 : {
2495 130 : nChunkSize *= anChunkSize[i];
2496 130 : anAccBlockSizeFromStart.push_back(nChunkSize);
2497 : }
2498 71 : if (nChunkSize <= nMaxChunkMemory / 2)
2499 : {
2500 67 : size_t nVoxelsFromEnd = 1;
2501 189 : for (size_t i = dims.size(); i > 0;)
2502 : {
2503 122 : --i;
2504 : const auto nCurBlockSize =
2505 122 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2506 122 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2507 122 : if (nMul >= 2)
2508 : {
2509 114 : const auto nSizeThisDim(dims[i]->GetSize());
2510 : const auto nBlocksThisDim =
2511 114 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2512 114 : anChunkSize[i] = static_cast<size_t>(std::min(
2513 114 : anChunkSize[i] *
2514 228 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2515 114 : nSizeThisDim));
2516 : }
2517 122 : nVoxelsFromEnd *= anChunkSize[i];
2518 : }
2519 : }
2520 71 : return anChunkSize;
2521 : }
2522 :
2523 : /************************************************************************/
2524 : /* BaseRename() */
2525 : /************************************************************************/
2526 :
2527 : //! @cond Doxygen_Suppress
2528 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2529 : {
2530 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2531 18 : m_osFullName += osNewName;
2532 18 : m_osName = osNewName;
2533 :
2534 18 : NotifyChildrenOfRenaming();
2535 18 : }
2536 :
2537 : //! @endcond
2538 :
2539 : //! @cond Doxygen_Suppress
2540 : /************************************************************************/
2541 : /* ParentRenamed() */
2542 : /************************************************************************/
2543 :
2544 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2545 : {
2546 50 : m_osFullName = osNewParentFullName;
2547 50 : m_osFullName += "/";
2548 50 : m_osFullName += m_osName;
2549 :
2550 50 : NotifyChildrenOfRenaming();
2551 50 : }
2552 :
2553 : //! @endcond
2554 :
2555 : /************************************************************************/
2556 : /* Deleted() */
2557 : /************************************************************************/
2558 :
2559 : //! @cond Doxygen_Suppress
2560 52 : void GDALAbstractMDArray::Deleted()
2561 : {
2562 52 : m_bValid = false;
2563 :
2564 52 : NotifyChildrenOfDeletion();
2565 52 : }
2566 :
2567 : //! @endcond
2568 :
2569 : /************************************************************************/
2570 : /* ParentDeleted() */
2571 : /************************************************************************/
2572 :
2573 : //! @cond Doxygen_Suppress
2574 28 : void GDALAbstractMDArray::ParentDeleted()
2575 : {
2576 28 : Deleted();
2577 28 : }
2578 :
2579 : //! @endcond
2580 :
2581 : /************************************************************************/
2582 : /* CheckValidAndErrorOutIfNot() */
2583 : /************************************************************************/
2584 :
2585 : //! @cond Doxygen_Suppress
2586 6158 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2587 : {
2588 6158 : if (!m_bValid)
2589 : {
2590 26 : CPLError(CE_Failure, CPLE_AppDefined,
2591 : "This object has been deleted. No action on it is possible");
2592 : }
2593 6158 : return m_bValid;
2594 : }
2595 :
2596 : //! @endcond
2597 :
2598 : /************************************************************************/
2599 : /* SetUnit() */
2600 : /************************************************************************/
2601 :
2602 : /** Set the variable unit.
2603 : *
2604 : * Values should conform as much as possible with those allowed by
2605 : * the NetCDF CF conventions:
2606 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2607 : * but others might be returned.
2608 : *
2609 : * Few examples are "meter", "degrees", "second", ...
2610 : * Empty value means unknown.
2611 : *
2612 : * This is the same as the C function GDALMDArraySetUnit()
2613 : *
2614 : * @note Driver implementation: optionally implemented.
2615 : *
2616 : * @param osUnit unit name.
2617 : * @return true in case of success.
2618 : */
2619 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2620 : {
2621 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2622 0 : return false;
2623 : }
2624 :
2625 : /************************************************************************/
2626 : /* GetUnit() */
2627 : /************************************************************************/
2628 :
2629 : /** Return the array unit.
2630 : *
2631 : * Values should conform as much as possible with those allowed by
2632 : * the NetCDF CF conventions:
2633 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2634 : * but others might be returned.
2635 : *
2636 : * Few examples are "meter", "degrees", "second", ...
2637 : * Empty value means unknown.
2638 : *
2639 : * This is the same as the C function GDALMDArrayGetUnit()
2640 : */
2641 5 : const std::string &GDALMDArray::GetUnit() const
2642 : {
2643 5 : static const std::string emptyString;
2644 5 : return emptyString;
2645 : }
2646 :
2647 : /************************************************************************/
2648 : /* SetSpatialRef() */
2649 : /************************************************************************/
2650 :
2651 : /** Assign a spatial reference system object to the array.
2652 : *
2653 : * This is the same as the C function GDALMDArraySetSpatialRef().
2654 : */
2655 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2656 : {
2657 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2658 0 : return false;
2659 : }
2660 :
2661 : /************************************************************************/
2662 : /* GetSpatialRef() */
2663 : /************************************************************************/
2664 :
2665 : /** Return the spatial reference system object associated with the array.
2666 : *
2667 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2668 : */
2669 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2670 : {
2671 4 : return nullptr;
2672 : }
2673 :
2674 : /************************************************************************/
2675 : /* GetRawNoDataValue() */
2676 : /************************************************************************/
2677 :
2678 : /** Return the nodata value as a "raw" value.
2679 : *
2680 : * The value returned might be nullptr in case of no nodata value. When
2681 : * a nodata value is registered, a non-nullptr will be returned whose size in
2682 : * bytes is GetDataType().GetSize().
2683 : *
2684 : * The returned value should not be modified or freed. It is valid until
2685 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2686 : * SetRawNoDataValue(), or any similar methods.
2687 : *
2688 : * @note Driver implementation: this method shall be implemented if nodata
2689 : * is supported.
2690 : *
2691 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2692 : *
2693 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2694 : */
2695 5 : const void *GDALMDArray::GetRawNoDataValue() const
2696 : {
2697 5 : return nullptr;
2698 : }
2699 :
2700 : /************************************************************************/
2701 : /* GetNoDataValueAsDouble() */
2702 : /************************************************************************/
2703 :
2704 : /** Return the nodata value as a double.
2705 : *
2706 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2707 : *
2708 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2709 : * a nodata value exists and can be converted to double. Might be nullptr.
2710 : *
2711 : * @return the nodata value as a double. A 0.0 value might also indicate the
2712 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2713 : * set to false then).
2714 : */
2715 22478 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2716 : {
2717 22478 : const void *pNoData = GetRawNoDataValue();
2718 22478 : double dfNoData = 0.0;
2719 22478 : const auto &eDT = GetDataType();
2720 22478 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2721 22478 : if (ok)
2722 : {
2723 22188 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2724 : GDT_Float64, 0, 1);
2725 : }
2726 22478 : if (pbHasNoData)
2727 439 : *pbHasNoData = ok;
2728 22478 : return dfNoData;
2729 : }
2730 :
2731 : /************************************************************************/
2732 : /* GetNoDataValueAsInt64() */
2733 : /************************************************************************/
2734 :
2735 : /** Return the nodata value as a Int64.
2736 : *
2737 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2738 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2739 : *
2740 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2741 : *
2742 : * @return the nodata value as a Int64
2743 : *
2744 : * @since GDAL 3.5
2745 : */
2746 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2747 : {
2748 12 : const void *pNoData = GetRawNoDataValue();
2749 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2750 12 : const auto &eDT = GetDataType();
2751 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2752 12 : if (ok)
2753 : {
2754 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2755 : GDT_Int64, 0, 1);
2756 : }
2757 12 : if (pbHasNoData)
2758 12 : *pbHasNoData = ok;
2759 12 : return nNoData;
2760 : }
2761 :
2762 : /************************************************************************/
2763 : /* GetNoDataValueAsUInt64() */
2764 : /************************************************************************/
2765 :
2766 : /** Return the nodata value as a UInt64.
2767 : *
2768 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2769 :
2770 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2771 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2772 : *
2773 : * @return the nodata value as a UInt64
2774 : *
2775 : * @since GDAL 3.5
2776 : */
2777 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2778 : {
2779 8 : const void *pNoData = GetRawNoDataValue();
2780 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2781 8 : const auto &eDT = GetDataType();
2782 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2783 8 : if (ok)
2784 : {
2785 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2786 : GDT_UInt64, 0, 1);
2787 : }
2788 8 : if (pbHasNoData)
2789 8 : *pbHasNoData = ok;
2790 8 : return nNoData;
2791 : }
2792 :
2793 : /************************************************************************/
2794 : /* SetRawNoDataValue() */
2795 : /************************************************************************/
2796 :
2797 : /** Set the nodata value as a "raw" value.
2798 : *
2799 : * The value passed might be nullptr in case of no nodata value. When
2800 : * a nodata value is registered, a non-nullptr whose size in
2801 : * bytes is GetDataType().GetSize() must be passed.
2802 : *
2803 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2804 : *
2805 : * @note Driver implementation: this method shall be implemented if setting
2806 : nodata
2807 : * is supported.
2808 :
2809 : * @return true in case of success.
2810 : */
2811 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2812 : {
2813 0 : CPLError(CE_Failure, CPLE_NotSupported,
2814 : "SetRawNoDataValue() not implemented");
2815 0 : return false;
2816 : }
2817 :
2818 : /************************************************************************/
2819 : /* SetNoDataValue() */
2820 : /************************************************************************/
2821 :
2822 : /** Set the nodata value as a double.
2823 : *
2824 : * If the natural data type of the attribute/array is not double, type
2825 : * conversion will occur to the type returned by GetDataType().
2826 : *
2827 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2828 : *
2829 : * @return true in case of success.
2830 : */
2831 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2832 : {
2833 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2834 57 : bool bRet = false;
2835 57 : if (GDALExtendedDataType::CopyValue(
2836 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2837 57 : GetDataType()))
2838 : {
2839 57 : bRet = SetRawNoDataValue(pRawNoData);
2840 : }
2841 57 : CPLFree(pRawNoData);
2842 57 : return bRet;
2843 : }
2844 :
2845 : /************************************************************************/
2846 : /* SetNoDataValue() */
2847 : /************************************************************************/
2848 :
2849 : /** Set the nodata value as a Int64.
2850 : *
2851 : * If the natural data type of the attribute/array is not Int64, type conversion
2852 : * will occur to the type returned by GetDataType().
2853 : *
2854 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2855 : *
2856 : * @return true in case of success.
2857 : *
2858 : * @since GDAL 3.5
2859 : */
2860 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2861 : {
2862 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2863 3 : bool bRet = false;
2864 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2865 6 : GDALExtendedDataType::Create(GDT_Int64),
2866 3 : pRawNoData, GetDataType()))
2867 : {
2868 3 : bRet = SetRawNoDataValue(pRawNoData);
2869 : }
2870 3 : CPLFree(pRawNoData);
2871 3 : return bRet;
2872 : }
2873 :
2874 : /************************************************************************/
2875 : /* SetNoDataValue() */
2876 : /************************************************************************/
2877 :
2878 : /** Set the nodata value as a Int64.
2879 : *
2880 : * If the natural data type of the attribute/array is not Int64, type conversion
2881 : * will occur to the type returned by GetDataType().
2882 : *
2883 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2884 : *
2885 : * @return true in case of success.
2886 : *
2887 : * @since GDAL 3.5
2888 : */
2889 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2890 : {
2891 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2892 1 : bool bRet = false;
2893 1 : if (GDALExtendedDataType::CopyValue(
2894 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2895 1 : GetDataType()))
2896 : {
2897 1 : bRet = SetRawNoDataValue(pRawNoData);
2898 : }
2899 1 : CPLFree(pRawNoData);
2900 1 : return bRet;
2901 : }
2902 :
2903 : /************************************************************************/
2904 : /* Resize() */
2905 : /************************************************************************/
2906 :
2907 : /** Resize an array to new dimensions.
2908 : *
2909 : * Not all drivers may allow this operation, and with restrictions (e.g.
2910 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2911 : *
2912 : * Resizing a dimension used in other arrays will cause those other arrays
2913 : * to be resized.
2914 : *
2915 : * This is the same as the C function GDALMDArrayResize().
2916 : *
2917 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2918 : * new size of each indexing dimension.
2919 : * @param papszOptions Options. (Driver specific)
2920 : * @return true in case of success.
2921 : * @since GDAL 3.7
2922 : */
2923 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2924 : CPL_UNUSED CSLConstList papszOptions)
2925 : {
2926 0 : CPLError(CE_Failure, CPLE_NotSupported,
2927 : "Resize() is not supported for this array");
2928 0 : return false;
2929 : }
2930 :
2931 : /************************************************************************/
2932 : /* SetScale() */
2933 : /************************************************************************/
2934 :
2935 : /** Set the scale value to apply to raw values.
2936 : *
2937 : * unscaled_value = raw_value * GetScale() + GetOffset()
2938 : *
2939 : * This is the same as the C function GDALMDArraySetScale() /
2940 : * GDALMDArraySetScaleEx().
2941 : *
2942 : * @note Driver implementation: this method shall be implemented if setting
2943 : * scale is supported.
2944 : *
2945 : * @param dfScale scale
2946 : * @param eStorageType Data type to which create the potential attribute that
2947 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2948 : * implementation will decide automatically the data type. Note that changing
2949 : * the data type after initial setting might not be supported.
2950 : * @return true in case of success.
2951 : */
2952 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2953 : CPL_UNUSED GDALDataType eStorageType)
2954 : {
2955 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2956 0 : return false;
2957 : }
2958 :
2959 : /************************************************************************/
2960 : /* SetOffset) */
2961 : /************************************************************************/
2962 :
2963 : /** Set the offset value to apply to raw values.
2964 : *
2965 : * unscaled_value = raw_value * GetScale() + GetOffset()
2966 : *
2967 : * This is the same as the C function GDALMDArraySetOffset() /
2968 : * GDALMDArraySetOffsetEx().
2969 : *
2970 : * @note Driver implementation: this method shall be implemented if setting
2971 : * offset is supported.
2972 : *
2973 : * @param dfOffset Offset
2974 : * @param eStorageType Data type to which create the potential attribute that
2975 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2976 : * implementation will decide automatically the data type. Note that changing
2977 : * the data type after initial setting might not be supported.
2978 : * @return true in case of success.
2979 : */
2980 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2981 : CPL_UNUSED GDALDataType eStorageType)
2982 : {
2983 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2984 0 : return false;
2985 : }
2986 :
2987 : /************************************************************************/
2988 : /* GetScale() */
2989 : /************************************************************************/
2990 :
2991 : /** Get the scale value to apply to raw values.
2992 : *
2993 : * unscaled_value = raw_value * GetScale() + GetOffset()
2994 : *
2995 : * This is the same as the C function GDALMDArrayGetScale().
2996 : *
2997 : * @note Driver implementation: this method shall be implemented if getting
2998 : * scale is supported.
2999 : *
3000 : * @param pbHasScale Pointer to a output boolean that will be set to true if
3001 : * a scale value exists. Might be nullptr.
3002 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3003 : * the storage type of the scale value, when known/relevant. Otherwise will be
3004 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3005 : *
3006 : * @return the scale value. A 1.0 value might also indicate the
3007 : * absence of a scale value.
3008 : */
3009 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3010 : CPL_UNUSED GDALDataType *peStorageType) const
3011 : {
3012 20 : if (pbHasScale)
3013 20 : *pbHasScale = false;
3014 20 : return 1.0;
3015 : }
3016 :
3017 : /************************************************************************/
3018 : /* GetOffset() */
3019 : /************************************************************************/
3020 :
3021 : /** Get the offset value to apply to raw values.
3022 : *
3023 : * unscaled_value = raw_value * GetScale() + GetOffset()
3024 : *
3025 : * This is the same as the C function GDALMDArrayGetOffset().
3026 : *
3027 : * @note Driver implementation: this method shall be implemented if getting
3028 : * offset is supported.
3029 : *
3030 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3031 : * a offset value exists. Might be nullptr.
3032 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3033 : * the storage type of the offset value, when known/relevant. Otherwise will be
3034 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3035 : *
3036 : * @return the offset value. A 0.0 value might also indicate the
3037 : * absence of a offset value.
3038 : */
3039 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3040 : CPL_UNUSED GDALDataType *peStorageType) const
3041 : {
3042 20 : if (pbHasOffset)
3043 20 : *pbHasOffset = false;
3044 20 : return 0.0;
3045 : }
3046 :
3047 : /************************************************************************/
3048 : /* ProcessPerChunk() */
3049 : /************************************************************************/
3050 :
3051 : namespace
3052 : {
3053 : enum class Caller
3054 : {
3055 : CALLER_END_OF_LOOP,
3056 : CALLER_IN_LOOP,
3057 : };
3058 : }
3059 :
3060 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3061 : *
3062 : * This method is to be used when doing operations on an array, or a subset of
3063 : * it, in a chunk by chunk way.
3064 : *
3065 : * @param arrayStartIdx Values representing the starting index to use
3066 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3067 : * Array of GetDimensionCount() values. Must not be
3068 : * nullptr, unless for a zero-dimensional array.
3069 : *
3070 : * @param count Values representing the number of values to use in
3071 : * each dimension.
3072 : * Array of GetDimensionCount() values. Must not be
3073 : * nullptr, unless for a zero-dimensional array.
3074 : *
3075 : * @param chunkSize Values representing the chunk size in each dimension.
3076 : * Might typically the output of GetProcessingChunkSize().
3077 : * Array of GetDimensionCount() values. Must not be
3078 : * nullptr, unless for a zero-dimensional array.
3079 : *
3080 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3081 : * Must NOT be nullptr.
3082 : *
3083 : * @param pUserData Pointer to pass as the value of the pUserData argument
3084 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3085 : *
3086 : * @return true in case of success.
3087 : */
3088 69 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3089 : const GUInt64 *count,
3090 : const size_t *chunkSize,
3091 : FuncProcessPerChunkType pfnFunc,
3092 : void *pUserData)
3093 : {
3094 69 : const auto &dims = GetDimensions();
3095 69 : if (dims.empty())
3096 : {
3097 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3098 : }
3099 :
3100 : // Sanity check
3101 67 : size_t nTotalChunkSize = 1;
3102 174 : for (size_t i = 0; i < dims.size(); i++)
3103 : {
3104 114 : const auto nSizeThisDim(dims[i]->GetSize());
3105 114 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3106 112 : arrayStartIdx[i] > nSizeThisDim - count[i])
3107 : {
3108 4 : CPLError(CE_Failure, CPLE_AppDefined,
3109 : "Inconsistent arrayStartIdx[] / count[] values "
3110 : "regarding array size");
3111 4 : return false;
3112 : }
3113 218 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3114 108 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3115 : {
3116 3 : CPLError(CE_Failure, CPLE_AppDefined,
3117 : "Inconsistent chunkSize[] values");
3118 3 : return false;
3119 : }
3120 107 : nTotalChunkSize *= chunkSize[i];
3121 : }
3122 :
3123 60 : size_t dimIdx = 0;
3124 120 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3125 120 : std::vector<size_t> chunkCount(dims.size());
3126 :
3127 : struct Stack
3128 : {
3129 : GUInt64 nBlockCounter = 0;
3130 : GUInt64 nBlocksMinusOne = 0;
3131 : size_t first_count = 0; // only used if nBlocks > 1
3132 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3133 : };
3134 :
3135 120 : std::vector<Stack> stack(dims.size());
3136 60 : GUInt64 iCurChunk = 0;
3137 60 : GUInt64 nChunkCount = 1;
3138 166 : for (size_t i = 0; i < dims.size(); i++)
3139 : {
3140 106 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3141 106 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3142 106 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3143 106 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3144 106 : if (stack[i].nBlocksMinusOne == 0)
3145 : {
3146 101 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3147 101 : chunkCount[i] = static_cast<size_t>(count[i]);
3148 : }
3149 : else
3150 : {
3151 5 : stack[i].first_count = static_cast<size_t>(
3152 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3153 : }
3154 : }
3155 :
3156 60 : lbl_next_depth:
3157 276 : if (dimIdx == dims.size())
3158 : {
3159 93 : ++iCurChunk;
3160 93 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3161 : iCurChunk, nChunkCount, pUserData))
3162 : {
3163 0 : return false;
3164 : }
3165 : }
3166 : else
3167 : {
3168 183 : if (stack[dimIdx].nBlocksMinusOne != 0)
3169 : {
3170 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3171 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3172 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3173 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3174 : while (true)
3175 : {
3176 33 : dimIdx++;
3177 33 : goto lbl_next_depth;
3178 33 : lbl_return_to_caller_in_loop:
3179 33 : --stack[dimIdx].nBlockCounter;
3180 33 : if (stack[dimIdx].nBlockCounter == 0)
3181 11 : break;
3182 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3183 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3184 : }
3185 :
3186 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3187 22 : chunkCount[dimIdx] =
3188 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3189 11 : chunkArrayStartIdx[dimIdx]);
3190 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3191 : }
3192 183 : dimIdx++;
3193 183 : goto lbl_next_depth;
3194 183 : lbl_return_to_caller_end_of_loop:
3195 183 : if (dimIdx == 0)
3196 60 : goto end;
3197 : }
3198 :
3199 216 : assert(dimIdx > 0);
3200 216 : dimIdx--;
3201 : // cppcheck-suppress negativeContainerIndex
3202 216 : switch (stack[dimIdx].return_point)
3203 : {
3204 183 : case Caller::CALLER_END_OF_LOOP:
3205 183 : goto lbl_return_to_caller_end_of_loop;
3206 33 : case Caller::CALLER_IN_LOOP:
3207 33 : goto lbl_return_to_caller_in_loop;
3208 : }
3209 60 : end:
3210 60 : return true;
3211 : }
3212 :
3213 : /************************************************************************/
3214 : /* GDALAttribute() */
3215 : /************************************************************************/
3216 :
3217 : //! @cond Doxygen_Suppress
3218 14669 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3219 0 : CPL_UNUSED const std::string &osName)
3220 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3221 14669 : : GDALAbstractMDArray(osParentName, osName)
3222 : #endif
3223 : {
3224 14669 : }
3225 :
3226 : GDALAttribute::~GDALAttribute() = default;
3227 :
3228 : //! @endcond
3229 :
3230 : /************************************************************************/
3231 : /* GetDimensionSize() */
3232 : /************************************************************************/
3233 :
3234 : /** Return the size of the dimensions of the attribute.
3235 : *
3236 : * This will be an empty array for a scalar (single value) attribute.
3237 : *
3238 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3239 : */
3240 611 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3241 : {
3242 611 : const auto &dims = GetDimensions();
3243 611 : std::vector<GUInt64> ret;
3244 611 : ret.reserve(dims.size());
3245 761 : for (const auto &dim : dims)
3246 150 : ret.push_back(dim->GetSize());
3247 611 : return ret;
3248 : }
3249 :
3250 : /************************************************************************/
3251 : /* GDALRawResult() */
3252 : /************************************************************************/
3253 :
3254 : //! @cond Doxygen_Suppress
3255 166 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3256 166 : size_t nEltCount)
3257 332 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3258 166 : m_raw(raw)
3259 : {
3260 166 : }
3261 :
3262 : //! @endcond
3263 :
3264 : /************************************************************************/
3265 : /* GDALRawResult() */
3266 : /************************************************************************/
3267 :
3268 : /** Move constructor. */
3269 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3270 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3271 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3272 : {
3273 0 : other.m_nEltCount = 0;
3274 0 : other.m_nSize = 0;
3275 0 : other.m_raw = nullptr;
3276 0 : }
3277 :
3278 : /************************************************************************/
3279 : /* FreeMe() */
3280 : /************************************************************************/
3281 :
3282 166 : void GDALRawResult::FreeMe()
3283 : {
3284 166 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3285 : {
3286 60 : GByte *pabyPtr = m_raw;
3287 60 : const auto nDTSize(m_dt.GetSize());
3288 120 : for (size_t i = 0; i < m_nEltCount; ++i)
3289 : {
3290 60 : m_dt.FreeDynamicMemory(pabyPtr);
3291 60 : pabyPtr += nDTSize;
3292 : }
3293 : }
3294 166 : VSIFree(m_raw);
3295 166 : }
3296 :
3297 : /************************************************************************/
3298 : /* operator=() */
3299 : /************************************************************************/
3300 :
3301 : /** Move assignment. */
3302 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3303 : {
3304 0 : FreeMe();
3305 0 : m_dt = std::move(other.m_dt);
3306 0 : m_nEltCount = other.m_nEltCount;
3307 0 : m_nSize = other.m_nSize;
3308 0 : m_raw = other.m_raw;
3309 0 : other.m_nEltCount = 0;
3310 0 : other.m_nSize = 0;
3311 0 : other.m_raw = nullptr;
3312 0 : return *this;
3313 : }
3314 :
3315 : /************************************************************************/
3316 : /* ~GDALRawResult() */
3317 : /************************************************************************/
3318 :
3319 : /** Destructor. */
3320 166 : GDALRawResult::~GDALRawResult()
3321 : {
3322 166 : FreeMe();
3323 166 : }
3324 :
3325 : /************************************************************************/
3326 : /* StealData() */
3327 : /************************************************************************/
3328 :
3329 : //! @cond Doxygen_Suppress
3330 : /** Return buffer to caller which becomes owner of it.
3331 : * Only to be used by GDALAttributeReadAsRaw().
3332 : */
3333 6 : GByte *GDALRawResult::StealData()
3334 : {
3335 6 : GByte *ret = m_raw;
3336 6 : m_raw = nullptr;
3337 6 : m_nEltCount = 0;
3338 6 : m_nSize = 0;
3339 6 : return ret;
3340 : }
3341 :
3342 : //! @endcond
3343 :
3344 : /************************************************************************/
3345 : /* ReadAsRaw() */
3346 : /************************************************************************/
3347 :
3348 : /** Return the raw value of an attribute.
3349 : *
3350 : *
3351 : * This is the same as the C function GDALAttributeReadAsRaw().
3352 : */
3353 166 : GDALRawResult GDALAttribute::ReadAsRaw() const
3354 : {
3355 166 : const auto nEltCount(GetTotalElementsCount());
3356 166 : const auto &dt(GetDataType());
3357 166 : const auto nDTSize(dt.GetSize());
3358 : GByte *res = static_cast<GByte *>(
3359 166 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3360 166 : if (!res)
3361 0 : return GDALRawResult(nullptr, dt, 0);
3362 166 : const auto &dims = GetDimensions();
3363 166 : const auto nDims = GetDimensionCount();
3364 332 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3365 332 : std::vector<size_t> count(1 + nDims);
3366 189 : for (size_t i = 0; i < nDims; i++)
3367 : {
3368 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3369 : }
3370 166 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3371 166 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3372 : {
3373 0 : VSIFree(res);
3374 0 : return GDALRawResult(nullptr, dt, 0);
3375 : }
3376 166 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3377 : }
3378 :
3379 : /************************************************************************/
3380 : /* ReadAsString() */
3381 : /************************************************************************/
3382 :
3383 : /** Return the value of an attribute as a string.
3384 : *
3385 : * The returned string should not be freed, and its lifetime does not
3386 : * excess a next call to ReadAsString() on the same object, or the deletion
3387 : * of the object itself.
3388 : *
3389 : * This function will only return the first element if there are several.
3390 : *
3391 : * This is the same as the C function GDALAttributeReadAsString()
3392 : *
3393 : * @return a string, or nullptr.
3394 : */
3395 1551 : const char *GDALAttribute::ReadAsString() const
3396 : {
3397 1551 : const auto nDims = GetDimensionCount();
3398 3102 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3399 3102 : std::vector<size_t> count(1 + nDims, 1);
3400 1551 : char *szRet = nullptr;
3401 1551 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3402 1551 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3403 4652 : sizeof(szRet)) ||
3404 1550 : szRet == nullptr)
3405 : {
3406 5 : return nullptr;
3407 : }
3408 1546 : m_osCachedVal = szRet;
3409 1546 : CPLFree(szRet);
3410 1546 : return m_osCachedVal.c_str();
3411 : }
3412 :
3413 : /************************************************************************/
3414 : /* ReadAsInt() */
3415 : /************************************************************************/
3416 :
3417 : /** Return the value of an attribute as a integer.
3418 : *
3419 : * This function will only return the first element if there are several.
3420 : *
3421 : * It can fail if its value can not be converted to integer.
3422 : *
3423 : * This is the same as the C function GDALAttributeReadAsInt()
3424 : *
3425 : * @return a integer, or INT_MIN in case of error.
3426 : */
3427 226 : int GDALAttribute::ReadAsInt() const
3428 : {
3429 226 : const auto nDims = GetDimensionCount();
3430 452 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3431 226 : std::vector<size_t> count(1 + nDims, 1);
3432 226 : int nRet = INT_MIN;
3433 226 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3434 452 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3435 452 : return nRet;
3436 : }
3437 :
3438 : /************************************************************************/
3439 : /* ReadAsInt64() */
3440 : /************************************************************************/
3441 :
3442 : /** Return the value of an attribute as an int64_t.
3443 : *
3444 : * This function will only return the first element if there are several.
3445 : *
3446 : * It can fail if its value can not be converted to long.
3447 : *
3448 : * This is the same as the C function GDALAttributeReadAsInt64()
3449 : *
3450 : * @return an int64_t, or INT64_MIN in case of error.
3451 : */
3452 102 : int64_t GDALAttribute::ReadAsInt64() const
3453 : {
3454 102 : const auto nDims = GetDimensionCount();
3455 204 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3456 102 : std::vector<size_t> count(1 + nDims, 1);
3457 102 : int64_t nRet = INT64_MIN;
3458 102 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3459 204 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3460 204 : return nRet;
3461 : }
3462 :
3463 : /************************************************************************/
3464 : /* ReadAsDouble() */
3465 : /************************************************************************/
3466 :
3467 : /** Return the value of an attribute as a double.
3468 : *
3469 : * This function will only return the first element if there are several.
3470 : *
3471 : * It can fail if its value can not be converted to double.
3472 : *
3473 : * This is the same as the C function GDALAttributeReadAsInt()
3474 : *
3475 : * @return a double value.
3476 : */
3477 355 : double GDALAttribute::ReadAsDouble() const
3478 : {
3479 355 : const auto nDims = GetDimensionCount();
3480 710 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3481 355 : std::vector<size_t> count(1 + nDims, 1);
3482 355 : double dfRet = 0;
3483 355 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3484 355 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3485 355 : sizeof(dfRet));
3486 710 : return dfRet;
3487 : }
3488 :
3489 : /************************************************************************/
3490 : /* ReadAsStringArray() */
3491 : /************************************************************************/
3492 :
3493 : /** Return the value of an attribute as an array of strings.
3494 : *
3495 : * This is the same as the C function GDALAttributeReadAsStringArray()
3496 : */
3497 141 : CPLStringList GDALAttribute::ReadAsStringArray() const
3498 : {
3499 141 : const auto nElts = GetTotalElementsCount();
3500 141 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3501 0 : return CPLStringList();
3502 : char **papszList = static_cast<char **>(
3503 141 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3504 141 : const auto &dims = GetDimensions();
3505 141 : const auto nDims = GetDimensionCount();
3506 282 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3507 282 : std::vector<size_t> count(1 + nDims);
3508 225 : for (size_t i = 0; i < nDims; i++)
3509 : {
3510 84 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3511 : }
3512 141 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3513 141 : GDALExtendedDataType::CreateString(), papszList, papszList,
3514 141 : sizeof(char *) * static_cast<int>(nElts));
3515 577 : for (int i = 0; i < static_cast<int>(nElts); i++)
3516 : {
3517 436 : if (papszList[i] == nullptr)
3518 13 : papszList[i] = CPLStrdup("");
3519 : }
3520 141 : return CPLStringList(papszList);
3521 : }
3522 :
3523 : /************************************************************************/
3524 : /* ReadAsIntArray() */
3525 : /************************************************************************/
3526 :
3527 : /** Return the value of an attribute as an array of integers.
3528 : *
3529 : * This is the same as the C function GDALAttributeReadAsIntArray().
3530 : */
3531 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3532 : {
3533 15 : const auto nElts = GetTotalElementsCount();
3534 : #if SIZEOF_VOIDP == 4
3535 : if (nElts > static_cast<size_t>(nElts))
3536 : return {};
3537 : #endif
3538 15 : std::vector<int> res(static_cast<size_t>(nElts));
3539 15 : const auto &dims = GetDimensions();
3540 15 : const auto nDims = GetDimensionCount();
3541 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3542 30 : std::vector<size_t> count(1 + nDims);
3543 32 : for (size_t i = 0; i < nDims; i++)
3544 : {
3545 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3546 : }
3547 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3548 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3549 15 : res.size() * sizeof(res[0]));
3550 30 : return res;
3551 : }
3552 :
3553 : /************************************************************************/
3554 : /* ReadAsInt64Array() */
3555 : /************************************************************************/
3556 :
3557 : /** Return the value of an attribute as an array of int64_t.
3558 : *
3559 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3560 : */
3561 62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3562 : {
3563 62 : const auto nElts = GetTotalElementsCount();
3564 : #if SIZEOF_VOIDP == 4
3565 : if (nElts > static_cast<size_t>(nElts))
3566 : return {};
3567 : #endif
3568 62 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3569 62 : const auto &dims = GetDimensions();
3570 62 : const auto nDims = GetDimensionCount();
3571 124 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3572 124 : std::vector<size_t> count(1 + nDims);
3573 124 : for (size_t i = 0; i < nDims; i++)
3574 : {
3575 62 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3576 : }
3577 62 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3578 124 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3579 62 : res.size() * sizeof(res[0]));
3580 124 : return res;
3581 : }
3582 :
3583 : /************************************************************************/
3584 : /* ReadAsDoubleArray() */
3585 : /************************************************************************/
3586 :
3587 : /** Return the value of an attribute as an array of double.
3588 : *
3589 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3590 : */
3591 94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3592 : {
3593 94 : const auto nElts = GetTotalElementsCount();
3594 : #if SIZEOF_VOIDP == 4
3595 : if (nElts > static_cast<size_t>(nElts))
3596 : return {};
3597 : #endif
3598 94 : std::vector<double> res(static_cast<size_t>(nElts));
3599 94 : const auto &dims = GetDimensions();
3600 94 : const auto nDims = GetDimensionCount();
3601 188 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3602 188 : std::vector<size_t> count(1 + nDims);
3603 172 : for (size_t i = 0; i < nDims; i++)
3604 : {
3605 78 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3606 : }
3607 94 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3608 188 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3609 94 : res.size() * sizeof(res[0]));
3610 188 : return res;
3611 : }
3612 :
3613 : /************************************************************************/
3614 : /* Write() */
3615 : /************************************************************************/
3616 :
3617 : /** Write an attribute from raw values expressed in GetDataType()
3618 : *
3619 : * The values should be provided in the type of GetDataType() and there should
3620 : * be exactly GetTotalElementsCount() of them.
3621 : * If GetDataType() is a string, each value should be a char* pointer.
3622 : *
3623 : * This is the same as the C function GDALAttributeWriteRaw().
3624 : *
3625 : * @param pabyValue Buffer of nLen bytes.
3626 : * @param nLen Size of pabyValue in bytes. Should be equal to
3627 : * GetTotalElementsCount() * GetDataType().GetSize()
3628 : * @return true in case of success.
3629 : */
3630 106 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3631 : {
3632 106 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3633 : {
3634 0 : CPLError(CE_Failure, CPLE_AppDefined,
3635 : "Length is not of expected value");
3636 0 : return false;
3637 : }
3638 106 : const auto &dims = GetDimensions();
3639 106 : const auto nDims = GetDimensionCount();
3640 212 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3641 212 : std::vector<size_t> count(1 + nDims);
3642 129 : for (size_t i = 0; i < nDims; i++)
3643 : {
3644 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3645 : }
3646 106 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3647 106 : pabyValue, pabyValue, nLen);
3648 : }
3649 :
3650 : /************************************************************************/
3651 : /* Write() */
3652 : /************************************************************************/
3653 :
3654 : /** Write an attribute from a string value.
3655 : *
3656 : * Type conversion will be performed if needed. If the attribute contains
3657 : * multiple values, only the first one will be updated.
3658 : *
3659 : * This is the same as the C function GDALAttributeWriteString().
3660 : *
3661 : * @param pszValue Pointer to a string.
3662 : * @return true in case of success.
3663 : */
3664 312 : bool GDALAttribute::Write(const char *pszValue)
3665 : {
3666 312 : const auto nDims = GetDimensionCount();
3667 624 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3668 312 : std::vector<size_t> count(1 + nDims, 1);
3669 312 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3670 624 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3671 624 : sizeof(pszValue));
3672 : }
3673 :
3674 : /************************************************************************/
3675 : /* WriteInt() */
3676 : /************************************************************************/
3677 :
3678 : /** Write an attribute from a integer value.
3679 : *
3680 : * Type conversion will be performed if needed. If the attribute contains
3681 : * multiple values, only the first one will be updated.
3682 : *
3683 : * This is the same as the C function GDALAttributeWriteInt().
3684 : *
3685 : * @param nVal Value.
3686 : * @return true in case of success.
3687 : */
3688 22 : bool GDALAttribute::WriteInt(int nVal)
3689 : {
3690 22 : const auto nDims = GetDimensionCount();
3691 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3692 22 : std::vector<size_t> count(1 + nDims, 1);
3693 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3694 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3695 44 : sizeof(nVal));
3696 : }
3697 :
3698 : /************************************************************************/
3699 : /* WriteInt64() */
3700 : /************************************************************************/
3701 :
3702 : /** Write an attribute from an int64_t value.
3703 : *
3704 : * Type conversion will be performed if needed. If the attribute contains
3705 : * multiple values, only the first one will be updated.
3706 : *
3707 : * This is the same as the C function GDALAttributeWriteInt().
3708 : *
3709 : * @param nVal Value.
3710 : * @return true in case of success.
3711 : */
3712 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3713 : {
3714 11 : const auto nDims = GetDimensionCount();
3715 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3716 11 : std::vector<size_t> count(1 + nDims, 1);
3717 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3718 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3719 22 : sizeof(nVal));
3720 : }
3721 :
3722 : /************************************************************************/
3723 : /* Write() */
3724 : /************************************************************************/
3725 :
3726 : /** Write an attribute from a double value.
3727 : *
3728 : * Type conversion will be performed if needed. If the attribute contains
3729 : * multiple values, only the first one will be updated.
3730 : *
3731 : * This is the same as the C function GDALAttributeWriteDouble().
3732 : *
3733 : * @param dfVal Value.
3734 : * @return true in case of success.
3735 : */
3736 38 : bool GDALAttribute::Write(double dfVal)
3737 : {
3738 38 : const auto nDims = GetDimensionCount();
3739 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3740 38 : std::vector<size_t> count(1 + nDims, 1);
3741 38 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3742 76 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3743 76 : sizeof(dfVal));
3744 : }
3745 :
3746 : /************************************************************************/
3747 : /* Write() */
3748 : /************************************************************************/
3749 :
3750 : /** Write an attribute from an array of strings.
3751 : *
3752 : * Type conversion will be performed if needed.
3753 : *
3754 : * Exactly GetTotalElementsCount() strings must be provided
3755 : *
3756 : * This is the same as the C function GDALAttributeWriteStringArray().
3757 : *
3758 : * @param vals Array of strings.
3759 : * @return true in case of success.
3760 : */
3761 8 : bool GDALAttribute::Write(CSLConstList vals)
3762 : {
3763 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3764 : {
3765 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3766 1 : return false;
3767 : }
3768 7 : const auto nDims = GetDimensionCount();
3769 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3770 7 : std::vector<size_t> count(1 + nDims);
3771 7 : const auto &dims = GetDimensions();
3772 15 : for (size_t i = 0; i < nDims; i++)
3773 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3774 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3775 7 : GDALExtendedDataType::CreateString(), vals, vals,
3776 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3777 : }
3778 :
3779 : /************************************************************************/
3780 : /* Write() */
3781 : /************************************************************************/
3782 :
3783 : /** Write an attribute from an array of int.
3784 : *
3785 : * Type conversion will be performed if needed.
3786 : *
3787 : * Exactly GetTotalElementsCount() strings must be provided
3788 : *
3789 : * This is the same as the C function GDALAttributeWriteIntArray()
3790 : *
3791 : * @param vals Array of int.
3792 : * @param nVals Should be equal to GetTotalElementsCount().
3793 : * @return true in case of success.
3794 : */
3795 11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3796 : {
3797 11 : if (nVals != GetTotalElementsCount())
3798 : {
3799 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3800 1 : return false;
3801 : }
3802 10 : const auto nDims = GetDimensionCount();
3803 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3804 10 : std::vector<size_t> count(1 + nDims);
3805 10 : const auto &dims = GetDimensions();
3806 20 : for (size_t i = 0; i < nDims; i++)
3807 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3808 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3809 10 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3810 20 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3811 : }
3812 :
3813 : /************************************************************************/
3814 : /* Write() */
3815 : /************************************************************************/
3816 :
3817 : /** Write an attribute from an array of int64_t.
3818 : *
3819 : * Type conversion will be performed if needed.
3820 : *
3821 : * Exactly GetTotalElementsCount() strings must be provided
3822 : *
3823 : * This is the same as the C function GDALAttributeWriteLongArray()
3824 : *
3825 : * @param vals Array of int64_t.
3826 : * @param nVals Should be equal to GetTotalElementsCount().
3827 : * @return true in case of success.
3828 : */
3829 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3830 : {
3831 10 : if (nVals != GetTotalElementsCount())
3832 : {
3833 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3834 0 : return false;
3835 : }
3836 10 : const auto nDims = GetDimensionCount();
3837 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3838 10 : std::vector<size_t> count(1 + nDims);
3839 10 : const auto &dims = GetDimensions();
3840 20 : for (size_t i = 0; i < nDims; i++)
3841 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3842 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3843 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3844 10 : static_cast<size_t>(GetTotalElementsCount()) *
3845 10 : sizeof(int64_t));
3846 : }
3847 :
3848 : /************************************************************************/
3849 : /* Write() */
3850 : /************************************************************************/
3851 :
3852 : /** Write an attribute from an array of double.
3853 : *
3854 : * Type conversion will be performed if needed.
3855 : *
3856 : * Exactly GetTotalElementsCount() strings must be provided
3857 : *
3858 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3859 : *
3860 : * @param vals Array of double.
3861 : * @param nVals Should be equal to GetTotalElementsCount().
3862 : * @return true in case of success.
3863 : */
3864 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3865 : {
3866 7 : if (nVals != GetTotalElementsCount())
3867 : {
3868 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3869 1 : return false;
3870 : }
3871 6 : const auto nDims = GetDimensionCount();
3872 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3873 6 : std::vector<size_t> count(1 + nDims);
3874 6 : const auto &dims = GetDimensions();
3875 13 : for (size_t i = 0; i < nDims; i++)
3876 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3877 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3878 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3879 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3880 : }
3881 :
3882 : /************************************************************************/
3883 : /* GDALMDArray() */
3884 : /************************************************************************/
3885 :
3886 : //! @cond Doxygen_Suppress
3887 6670 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3888 : CPL_UNUSED const std::string &osName,
3889 0 : const std::string &osContext)
3890 : :
3891 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3892 : GDALAbstractMDArray(osParentName, osName),
3893 : #endif
3894 6670 : m_osContext(osContext)
3895 : {
3896 6670 : }
3897 :
3898 : //! @endcond
3899 :
3900 : /************************************************************************/
3901 : /* GetTotalCopyCost() */
3902 : /************************************************************************/
3903 :
3904 : /** Return a total "cost" to copy the array.
3905 : *
3906 : * Used as a parameter for CopyFrom()
3907 : */
3908 51 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3909 : {
3910 102 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3911 102 : GetTotalElementsCount() * GetDataType().GetSize();
3912 : }
3913 :
3914 : /************************************************************************/
3915 : /* CopyFromAllExceptValues() */
3916 : /************************************************************************/
3917 :
3918 : //! @cond Doxygen_Suppress
3919 :
3920 177 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3921 : bool bStrict, GUInt64 &nCurCost,
3922 : const GUInt64 nTotalCost,
3923 : GDALProgressFunc pfnProgress,
3924 : void *pProgressData)
3925 : {
3926 : // Nodata setting must be one of the first things done for TileDB
3927 177 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3928 177 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3929 : {
3930 13 : SetRawNoDataValue(pNoData);
3931 : }
3932 :
3933 177 : const bool bThisIsUnscaledArray =
3934 177 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3935 354 : auto attrs = poSrcArray->GetAttributes();
3936 228 : for (const auto &attr : attrs)
3937 : {
3938 51 : const auto &osAttrName = attr->GetName();
3939 51 : if (bThisIsUnscaledArray)
3940 : {
3941 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3942 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3943 1 : osAttrName == "valid_range")
3944 : {
3945 1 : continue;
3946 : }
3947 : }
3948 :
3949 50 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3950 100 : attr->GetDataType());
3951 50 : if (!dstAttr)
3952 : {
3953 0 : if (bStrict)
3954 0 : return false;
3955 0 : continue;
3956 : }
3957 50 : auto raw = attr->ReadAsRaw();
3958 50 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3959 0 : return false;
3960 : }
3961 177 : if (!attrs.empty())
3962 : {
3963 28 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3964 49 : if (pfnProgress &&
3965 21 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3966 0 : return false;
3967 : }
3968 :
3969 177 : auto srcSRS = poSrcArray->GetSpatialRef();
3970 177 : if (srcSRS)
3971 : {
3972 11 : SetSpatialRef(srcSRS.get());
3973 : }
3974 :
3975 177 : const std::string &osUnit(poSrcArray->GetUnit());
3976 177 : if (!osUnit.empty())
3977 : {
3978 20 : SetUnit(osUnit);
3979 : }
3980 :
3981 177 : bool bGotValue = false;
3982 177 : GDALDataType eOffsetStorageType = GDT_Unknown;
3983 : const double dfOffset =
3984 177 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3985 177 : if (bGotValue)
3986 : {
3987 3 : SetOffset(dfOffset, eOffsetStorageType);
3988 : }
3989 :
3990 177 : bGotValue = false;
3991 177 : GDALDataType eScaleStorageType = GDT_Unknown;
3992 177 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3993 177 : if (bGotValue)
3994 : {
3995 3 : SetScale(dfScale, eScaleStorageType);
3996 : }
3997 :
3998 177 : return true;
3999 : }
4000 :
4001 : //! @endcond
4002 :
4003 : /************************************************************************/
4004 : /* CopyFrom() */
4005 : /************************************************************************/
4006 :
4007 : /** Copy the content of an array into a new (generally empty) array.
4008 : *
4009 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4010 : * of some output drivers this is not recommended)
4011 : * @param poSrcArray Source array. Should NOT be nullptr.
4012 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4013 : * stop the copy. In relaxed mode, the copy will be attempted to
4014 : * be pursued.
4015 : * @param nCurCost Should be provided as a variable initially set to 0.
4016 : * @param nTotalCost Total cost from GetTotalCopyCost().
4017 : * @param pfnProgress Progress callback, or nullptr.
4018 : * @param pProgressData Progress user data, or nulptr.
4019 : *
4020 : * @return true in case of success (or partial success if bStrict == false).
4021 : */
4022 49 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4023 : const GDALMDArray *poSrcArray, bool bStrict,
4024 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4025 : GDALProgressFunc pfnProgress, void *pProgressData)
4026 : {
4027 49 : if (pfnProgress == nullptr)
4028 4 : pfnProgress = GDALDummyProgress;
4029 :
4030 49 : nCurCost += GDALMDArray::COPY_COST;
4031 :
4032 49 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4033 : pfnProgress, pProgressData))
4034 : {
4035 0 : return false;
4036 : }
4037 :
4038 49 : const auto &dims = poSrcArray->GetDimensions();
4039 49 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4040 49 : if (dims.empty())
4041 : {
4042 2 : std::vector<GByte> abyTmp(nDTSize);
4043 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4044 2 : GetDataType(), &abyTmp[0]) &&
4045 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4046 4 : &abyTmp[0])) &&
4047 : bStrict)
4048 : {
4049 0 : return false;
4050 : }
4051 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4052 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4053 0 : return false;
4054 : }
4055 : else
4056 : {
4057 47 : std::vector<GUInt64> arrayStartIdx(dims.size());
4058 47 : std::vector<GUInt64> count(dims.size());
4059 127 : for (size_t i = 0; i < dims.size(); i++)
4060 : {
4061 80 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4062 : }
4063 :
4064 : struct CopyFunc
4065 : {
4066 : GDALMDArray *poDstArray = nullptr;
4067 : std::vector<GByte> abyTmp{};
4068 : GDALProgressFunc pfnProgress = nullptr;
4069 : void *pProgressData = nullptr;
4070 : GUInt64 nCurCost = 0;
4071 : GUInt64 nTotalCost = 0;
4072 : GUInt64 nTotalBytesThisArray = 0;
4073 : bool bStop = false;
4074 :
4075 65 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4076 : const GUInt64 *chunkArrayStartIdx,
4077 : const size_t *chunkCount, GUInt64 iCurChunk,
4078 : GUInt64 nChunkCount, void *pUserData)
4079 : {
4080 65 : const auto &dt(l_poSrcArray->GetDataType());
4081 65 : auto data = static_cast<CopyFunc *>(pUserData);
4082 65 : auto poDstArray = data->poDstArray;
4083 65 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4084 65 : nullptr, dt, &data->abyTmp[0]))
4085 : {
4086 0 : return false;
4087 : }
4088 : bool bRet =
4089 65 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4090 65 : nullptr, dt, &data->abyTmp[0]);
4091 65 : if (dt.NeedsFreeDynamicMemory())
4092 : {
4093 4 : const auto l_nDTSize = dt.GetSize();
4094 4 : GByte *ptr = &data->abyTmp[0];
4095 4 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4096 4 : size_t nEltCount = 1;
4097 8 : for (size_t i = 0; i < l_nDims; ++i)
4098 : {
4099 4 : nEltCount *= chunkCount[i];
4100 : }
4101 20 : for (size_t i = 0; i < nEltCount; i++)
4102 : {
4103 16 : dt.FreeDynamicMemory(ptr);
4104 16 : ptr += l_nDTSize;
4105 : }
4106 : }
4107 65 : if (!bRet)
4108 : {
4109 0 : return false;
4110 : }
4111 :
4112 65 : double dfCurCost =
4113 65 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4114 65 : data->nTotalBytesThisArray;
4115 65 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4116 : data->pProgressData))
4117 : {
4118 0 : data->bStop = true;
4119 0 : return false;
4120 : }
4121 :
4122 65 : return true;
4123 : }
4124 : };
4125 :
4126 47 : CopyFunc copyFunc;
4127 47 : copyFunc.poDstArray = this;
4128 47 : copyFunc.nCurCost = nCurCost;
4129 47 : copyFunc.nTotalCost = nTotalCost;
4130 47 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4131 47 : copyFunc.pfnProgress = pfnProgress;
4132 47 : copyFunc.pProgressData = pProgressData;
4133 : const char *pszSwathSize =
4134 47 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4135 : const size_t nMaxChunkSize =
4136 : pszSwathSize
4137 47 : ? static_cast<size_t>(
4138 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4139 1 : CPLAtoGIntBig(pszSwathSize)))
4140 : : static_cast<size_t>(
4141 46 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4142 46 : GDALGetCacheMax64() / 4));
4143 47 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4144 47 : size_t nRealChunkSize = nDTSize;
4145 127 : for (const auto &nChunkSize : anChunkSizes)
4146 : {
4147 80 : nRealChunkSize *= nChunkSize;
4148 : }
4149 : try
4150 : {
4151 47 : copyFunc.abyTmp.resize(nRealChunkSize);
4152 : }
4153 0 : catch (const std::exception &)
4154 : {
4155 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4156 : "Cannot allocate temporary buffer");
4157 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4158 0 : return false;
4159 : }
4160 140 : if (copyFunc.nTotalBytesThisArray != 0 &&
4161 46 : !const_cast<GDALMDArray *>(poSrcArray)
4162 46 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4163 : anChunkSizes.data(), CopyFunc::f,
4164 93 : ©Func) &&
4165 0 : (bStrict || copyFunc.bStop))
4166 : {
4167 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4168 0 : return false;
4169 : }
4170 47 : nCurCost += copyFunc.nTotalBytesThisArray;
4171 : }
4172 :
4173 49 : return true;
4174 : }
4175 :
4176 : /************************************************************************/
4177 : /* GetStructuralInfo() */
4178 : /************************************************************************/
4179 :
4180 : /** Return structural information on the array.
4181 : *
4182 : * This may be the compression, etc..
4183 : *
4184 : * The return value should not be freed and is valid until GDALMDArray is
4185 : * released or this function called again.
4186 : *
4187 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4188 : */
4189 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4190 : {
4191 95 : return nullptr;
4192 : }
4193 :
4194 : /************************************************************************/
4195 : /* AdviseRead() */
4196 : /************************************************************************/
4197 :
4198 : /** Advise driver of upcoming read requests.
4199 : *
4200 : * Some GDAL drivers operate more efficiently if they know in advance what
4201 : * set of upcoming read requests will be made. The AdviseRead() method allows
4202 : * an application to notify the driver of the region of interest.
4203 : *
4204 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4205 : * accelerate access via some drivers. One such case is when reading through
4206 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4207 : * with the region of interest defined by AdviseRead())
4208 : *
4209 : * This is the same as the C function GDALMDArrayAdviseRead().
4210 : *
4211 : * @param arrayStartIdx Values representing the starting index to read
4212 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4213 : * Array of GetDimensionCount() values.
4214 : * Can be nullptr as a synonymous for [0 for i in
4215 : * range(GetDimensionCount() ]
4216 : *
4217 : * @param count Values representing the number of values to extract in
4218 : * each dimension.
4219 : * Array of GetDimensionCount() values.
4220 : * Can be nullptr as a synonymous for
4221 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4222 : * range(GetDimensionCount() ]
4223 : *
4224 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4225 : * documentation.
4226 : *
4227 : * @return true in case of success (ignoring the advice is a success)
4228 : *
4229 : * @since GDAL 3.2
4230 : */
4231 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4232 : CSLConstList papszOptions) const
4233 : {
4234 25 : const auto nDimCount = GetDimensionCount();
4235 25 : if (nDimCount == 0)
4236 2 : return true;
4237 :
4238 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4239 23 : if (arrayStartIdx == nullptr)
4240 : {
4241 0 : tmp_arrayStartIdx.resize(nDimCount);
4242 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4243 : }
4244 :
4245 46 : std::vector<size_t> tmp_count;
4246 23 : if (count == nullptr)
4247 : {
4248 0 : tmp_count.resize(nDimCount);
4249 0 : const auto &dims = GetDimensions();
4250 0 : for (size_t i = 0; i < nDimCount; i++)
4251 : {
4252 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4253 : #if SIZEOF_VOIDP < 8
4254 : if (nSize != static_cast<size_t>(nSize))
4255 : {
4256 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4257 : return false;
4258 : }
4259 : #endif
4260 0 : tmp_count[i] = static_cast<size_t>(nSize);
4261 : }
4262 0 : count = tmp_count.data();
4263 : }
4264 :
4265 46 : std::vector<GInt64> tmp_arrayStep;
4266 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4267 23 : const GInt64 *arrayStep = nullptr;
4268 23 : const GPtrDiff_t *bufferStride = nullptr;
4269 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4270 46 : GDALExtendedDataType::Create(GDT_Unknown),
4271 : nullptr, nullptr, 0, tmp_arrayStep,
4272 : tmp_bufferStride))
4273 : {
4274 1 : return false;
4275 : }
4276 :
4277 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4278 : }
4279 :
4280 : /************************************************************************/
4281 : /* IAdviseRead() */
4282 : /************************************************************************/
4283 :
4284 : //! @cond Doxygen_Suppress
4285 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4286 : CSLConstList /* papszOptions*/) const
4287 : {
4288 3 : return true;
4289 : }
4290 :
4291 : //! @endcond
4292 :
4293 : /************************************************************************/
4294 : /* MassageName() */
4295 : /************************************************************************/
4296 :
4297 : //! @cond Doxygen_Suppress
4298 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4299 : {
4300 32 : std::string ret;
4301 604 : for (const char ch : inputName)
4302 : {
4303 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4304 138 : ret += '_';
4305 : else
4306 434 : ret += ch;
4307 : }
4308 32 : return ret;
4309 : }
4310 :
4311 : //! @endcond
4312 :
4313 : /************************************************************************/
4314 : /* GetCacheRootGroup() */
4315 : /************************************************************************/
4316 :
4317 : //! @cond Doxygen_Suppress
4318 : std::shared_ptr<GDALGroup>
4319 1450 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4320 : std::string &osCacheFilenameOut) const
4321 : {
4322 1450 : const auto &osFilename = GetFilename();
4323 1450 : if (osFilename.empty())
4324 : {
4325 1 : CPLError(CE_Failure, CPLE_AppDefined,
4326 : "Cannot cache an array with an empty filename");
4327 1 : return nullptr;
4328 : }
4329 :
4330 1449 : osCacheFilenameOut = osFilename + ".gmac";
4331 1449 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4332 : {
4333 0 : const auto nPosQuestionMark = osFilename.find('?');
4334 0 : if (nPosQuestionMark != std::string::npos)
4335 : {
4336 : osCacheFilenameOut =
4337 0 : osFilename.substr(0, nPosQuestionMark)
4338 0 : .append(".gmac")
4339 0 : .append(osFilename.substr(nPosQuestionMark));
4340 : }
4341 : }
4342 1449 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4343 1449 : if (pszProxy != nullptr)
4344 7 : osCacheFilenameOut = pszProxy;
4345 :
4346 1449 : std::unique_ptr<GDALDataset> poDS;
4347 : VSIStatBufL sStat;
4348 1449 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4349 : {
4350 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4351 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4352 : nullptr, nullptr, nullptr));
4353 : }
4354 1449 : if (poDS)
4355 : {
4356 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4357 28 : return poDS->GetRootGroup();
4358 : }
4359 :
4360 1421 : if (bCanCreate)
4361 : {
4362 4 : const char *pszDrvName = "netCDF";
4363 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4364 4 : if (poDrv == nullptr)
4365 : {
4366 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4367 : pszDrvName);
4368 0 : return nullptr;
4369 : }
4370 : {
4371 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4372 8 : CPLErrorStateBackuper oErrorStateBackuper;
4373 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4374 : nullptr, nullptr));
4375 : }
4376 4 : if (!poDS)
4377 : {
4378 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4379 1 : if (pszProxy)
4380 : {
4381 1 : osCacheFilenameOut = pszProxy;
4382 1 : poDS.reset(poDrv->CreateMultiDimensional(
4383 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4384 : }
4385 : }
4386 4 : if (poDS)
4387 : {
4388 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4389 4 : return poDS->GetRootGroup();
4390 : }
4391 : else
4392 : {
4393 0 : CPLError(CE_Failure, CPLE_AppDefined,
4394 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4395 : "configuration option to write the cache in "
4396 : "another directory",
4397 : osCacheFilenameOut.c_str());
4398 : }
4399 : }
4400 :
4401 1417 : return nullptr;
4402 : }
4403 :
4404 : //! @endcond
4405 :
4406 : /************************************************************************/
4407 : /* Cache() */
4408 : /************************************************************************/
4409 :
4410 : /** Cache the content of the array into an auxiliary filename.
4411 : *
4412 : * The main purpose of this method is to be able to cache views that are
4413 : * expensive to compute, such as transposed arrays.
4414 : *
4415 : * The array will be stored in a file whose name is the one of
4416 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4417 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4418 : *
4419 : * If the .gmac file cannot be written next to the dataset, the
4420 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4421 : * directory.
4422 : *
4423 : * The GDALMDArray::Read() method will automatically use the cache when it
4424 : * exists. There is no timestamp checks between the source array and the cached
4425 : * array. If the source arrays changes, the cache must be manually deleted.
4426 : *
4427 : * This is the same as the C function GDALMDArrayCache()
4428 : *
4429 : * @note Driver implementation: optionally implemented.
4430 : *
4431 : * @param papszOptions List of options, null terminated, or NULL. Currently
4432 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4433 : * to specify the block size of the cached array.
4434 : * @return true in case of success.
4435 : */
4436 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4437 : {
4438 14 : std::string osCacheFilename;
4439 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4440 7 : if (!poRG)
4441 1 : return false;
4442 :
4443 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4444 6 : if (poRG->OpenMDArray(osCachedArrayName))
4445 : {
4446 2 : CPLError(CE_Failure, CPLE_NotSupported,
4447 : "An array with same name %s already exists in %s",
4448 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4449 2 : return false;
4450 : }
4451 :
4452 8 : CPLStringList aosOptions;
4453 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4454 4 : const auto &aoDims = GetDimensions();
4455 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4456 4 : if (!aoDims.empty())
4457 : {
4458 : std::string osBlockSize(
4459 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4460 4 : if (osBlockSize.empty())
4461 : {
4462 6 : const auto anBlockSize = GetBlockSize();
4463 3 : int idxDim = 0;
4464 10 : for (auto nBlockSize : anBlockSize)
4465 : {
4466 7 : if (idxDim > 0)
4467 4 : osBlockSize += ',';
4468 7 : if (nBlockSize == 0)
4469 7 : nBlockSize = 256;
4470 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4471 : osBlockSize +=
4472 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4473 7 : idxDim++;
4474 : }
4475 : }
4476 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4477 :
4478 4 : int idxDim = 0;
4479 13 : for (const auto &poDim : aoDims)
4480 : {
4481 9 : auto poNewDim = poRG->CreateDimension(
4482 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4483 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4484 9 : if (!poNewDim)
4485 0 : return false;
4486 9 : aoNewDims.emplace_back(poNewDim);
4487 9 : idxDim++;
4488 : }
4489 : }
4490 :
4491 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4492 8 : GetDataType(), aosOptions.List());
4493 4 : if (!poCachedArray)
4494 : {
4495 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4496 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4497 0 : return false;
4498 : }
4499 :
4500 4 : GUInt64 nCost = 0;
4501 8 : return poCachedArray->CopyFrom(nullptr, this,
4502 : false, // strict
4503 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4504 : }
4505 :
4506 : /************************************************************************/
4507 : /* Read() */
4508 : /************************************************************************/
4509 :
4510 3928 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4511 : const GInt64 *arrayStep, // step in elements
4512 : const GPtrDiff_t *bufferStride, // stride in elements
4513 : const GDALExtendedDataType &bufferDataType,
4514 : void *pDstBuffer, const void *pDstBufferAllocStart,
4515 : size_t nDstBufferAllocSize) const
4516 : {
4517 3928 : if (!m_bHasTriedCachedArray)
4518 : {
4519 1760 : m_bHasTriedCachedArray = true;
4520 1760 : if (IsCacheable())
4521 : {
4522 1760 : const auto &osFilename = GetFilename();
4523 3006 : if (!osFilename.empty() &&
4524 3006 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4525 : {
4526 2472 : std::string osCacheFilename;
4527 2472 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4528 1236 : if (poRG)
4529 : {
4530 : const std::string osCachedArrayName(
4531 32 : MassageName(GetFullName()));
4532 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4533 16 : if (m_poCachedArray)
4534 : {
4535 6 : const auto &dims = GetDimensions();
4536 : const auto &cachedDims =
4537 6 : m_poCachedArray->GetDimensions();
4538 6 : const size_t nDims = dims.size();
4539 : bool ok =
4540 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4541 6 : cachedDims.size() == nDims;
4542 19 : for (size_t i = 0; ok && i < nDims; ++i)
4543 : {
4544 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4545 : }
4546 6 : if (ok)
4547 : {
4548 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4549 : osCachedArrayName.c_str(),
4550 : osCacheFilename.c_str());
4551 : }
4552 : else
4553 : {
4554 0 : CPLError(CE_Warning, CPLE_AppDefined,
4555 : "Cached array %s in %s has incompatible "
4556 : "characteristics with current array.",
4557 : osCachedArrayName.c_str(),
4558 : osCacheFilename.c_str());
4559 0 : m_poCachedArray.reset();
4560 : }
4561 : }
4562 : }
4563 : }
4564 : }
4565 : }
4566 :
4567 3928 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4568 3928 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4569 : {
4570 0 : CPLError(CE_Failure, CPLE_AppDefined,
4571 : "Array data type is not convertible to buffer data type");
4572 0 : return false;
4573 : }
4574 :
4575 7856 : std::vector<GInt64> tmp_arrayStep;
4576 7856 : std::vector<GPtrDiff_t> tmp_bufferStride;
4577 3928 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4578 : bufferStride, bufferDataType, pDstBuffer,
4579 : pDstBufferAllocStart, nDstBufferAllocSize,
4580 : tmp_arrayStep, tmp_bufferStride))
4581 : {
4582 9 : return false;
4583 : }
4584 :
4585 3919 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4586 3919 : bufferDataType, pDstBuffer);
4587 : }
4588 :
4589 : /************************************************************************/
4590 : /* GetRootGroup() */
4591 : /************************************************************************/
4592 :
4593 : /** Return the root group to which this arrays belongs too.
4594 : *
4595 : * Note that arrays may be free standing and some drivers may not implement
4596 : * this method, hence nullptr may be returned.
4597 : *
4598 : * It is used internally by the GetResampled() method to detect if GLT
4599 : * orthorectification is available.
4600 : *
4601 : * @return the root group, or nullptr.
4602 : * @since GDAL 3.8
4603 : */
4604 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4605 : {
4606 0 : return nullptr;
4607 : }
4608 :
4609 : //! @cond Doxygen_Suppress
4610 :
4611 : /************************************************************************/
4612 : /* IsTransposedRequest() */
4613 : /************************************************************************/
4614 :
4615 799 : bool GDALMDArray::IsTransposedRequest(
4616 : const size_t *count,
4617 : const GPtrDiff_t *bufferStride) const // stride in elements
4618 : {
4619 : /*
4620 : For example:
4621 : count = [2,3,4]
4622 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4623 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4624 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4625 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4626 : */
4627 799 : const size_t nDims(GetDimensionCount());
4628 799 : size_t nCurStrideForRowMajorStrides = 1;
4629 799 : bool bRowMajorStrides = true;
4630 799 : size_t nElts = 1;
4631 799 : size_t nLastIdx = 0;
4632 2195 : for (size_t i = nDims; i > 0;)
4633 : {
4634 1396 : --i;
4635 1396 : if (bufferStride[i] < 0)
4636 0 : return false;
4637 1396 : if (static_cast<size_t>(bufferStride[i]) !=
4638 : nCurStrideForRowMajorStrides)
4639 : {
4640 265 : bRowMajorStrides = false;
4641 : }
4642 : // Integer overflows have already been checked in CheckReadWriteParams()
4643 1396 : nCurStrideForRowMajorStrides *= count[i];
4644 1396 : nElts *= count[i];
4645 1396 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4646 : }
4647 799 : if (bRowMajorStrides)
4648 608 : return false;
4649 191 : return nLastIdx == nElts - 1;
4650 : }
4651 :
4652 : /************************************************************************/
4653 : /* CopyToFinalBufferSameDataType() */
4654 : /************************************************************************/
4655 :
4656 : template <size_t N>
4657 64 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4658 : size_t nDims, const size_t *count,
4659 : const GPtrDiff_t *bufferStride)
4660 : {
4661 128 : std::vector<size_t> anStackCount(nDims);
4662 128 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4663 64 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4664 : #if defined(__GNUC__)
4665 : #pragma GCC diagnostic push
4666 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4667 : #endif
4668 64 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4669 : #if defined(__GNUC__)
4670 : #pragma GCC diagnostic pop
4671 : #endif
4672 64 : size_t iDim = 0;
4673 :
4674 753 : lbl_next_depth:
4675 753 : if (iDim == nDims - 1)
4676 : {
4677 665 : size_t n = count[iDim];
4678 665 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4679 665 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4680 29194 : while (n > 0)
4681 : {
4682 28529 : --n;
4683 28529 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4684 28529 : pabyDstBuffer += bufferStrideLastDim;
4685 28529 : pabySrcBuffer += N;
4686 : }
4687 : }
4688 : else
4689 : {
4690 88 : anStackCount[iDim] = count[iDim];
4691 : while (true)
4692 : {
4693 689 : ++iDim;
4694 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4695 689 : goto lbl_next_depth;
4696 689 : lbl_return_to_caller_in_loop:
4697 689 : --iDim;
4698 689 : --anStackCount[iDim];
4699 689 : if (anStackCount[iDim] == 0)
4700 88 : break;
4701 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4702 : }
4703 : }
4704 753 : if (iDim > 0)
4705 689 : goto lbl_return_to_caller_in_loop;
4706 64 : }
4707 :
4708 : /************************************************************************/
4709 : /* CopyToFinalBuffer() */
4710 : /************************************************************************/
4711 :
4712 170 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4713 : const GDALExtendedDataType &eSrcDataType,
4714 : void *pDstBuffer,
4715 : const GDALExtendedDataType &eDstDataType,
4716 : size_t nDims, const size_t *count,
4717 : const GPtrDiff_t *bufferStride)
4718 : {
4719 170 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4720 : // Use specialized implementation for well-known data types when no
4721 : // type conversion is needed
4722 170 : if (eSrcDataType == eDstDataType)
4723 : {
4724 114 : if (nSrcDataTypeSize == 1)
4725 : {
4726 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4727 : count, bufferStride);
4728 64 : return;
4729 : }
4730 73 : else if (nSrcDataTypeSize == 2)
4731 : {
4732 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4733 : count, bufferStride);
4734 1 : return;
4735 : }
4736 72 : else if (nSrcDataTypeSize == 4)
4737 : {
4738 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4739 : count, bufferStride);
4740 14 : return;
4741 : }
4742 58 : else if (nSrcDataTypeSize == 8)
4743 : {
4744 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4745 : count, bufferStride);
4746 8 : return;
4747 : }
4748 : }
4749 :
4750 106 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4751 212 : std::vector<size_t> anStackCount(nDims);
4752 212 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4753 106 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4754 106 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4755 106 : size_t iDim = 0;
4756 :
4757 375 : lbl_next_depth:
4758 375 : if (iDim == nDims - 1)
4759 : {
4760 365 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4761 365 : pabyDstBufferStack[iDim], eDstDataType,
4762 365 : bufferStride[iDim], count[iDim]);
4763 365 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4764 : }
4765 : else
4766 : {
4767 10 : anStackCount[iDim] = count[iDim];
4768 : while (true)
4769 : {
4770 269 : ++iDim;
4771 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4772 269 : goto lbl_next_depth;
4773 269 : lbl_return_to_caller_in_loop:
4774 269 : --iDim;
4775 269 : --anStackCount[iDim];
4776 269 : if (anStackCount[iDim] == 0)
4777 10 : break;
4778 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4779 : }
4780 : }
4781 375 : if (iDim > 0)
4782 269 : goto lbl_return_to_caller_in_loop;
4783 : }
4784 :
4785 : /************************************************************************/
4786 : /* TransposeLast2Dims() */
4787 : /************************************************************************/
4788 :
4789 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4790 : const GDALExtendedDataType &eDT,
4791 : const size_t nDims, const size_t *count,
4792 : const size_t nEltsNonLast2Dims)
4793 : {
4794 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4795 19 : const auto nDTSize = eDT.GetSize();
4796 : void *pTempBufferForLast2DimsTranspose =
4797 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4798 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4799 0 : return false;
4800 :
4801 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4802 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4803 : {
4804 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4805 : pTempBufferForLast2DimsTranspose,
4806 39 : eDT.GetNumericDataType(), count[nDims - 1],
4807 39 : count[nDims - 2]);
4808 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4809 : nDTSize * nEltsLast2Dims);
4810 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4811 : }
4812 :
4813 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4814 :
4815 19 : return true;
4816 : }
4817 :
4818 : /************************************************************************/
4819 : /* ReadForTransposedRequest() */
4820 : /************************************************************************/
4821 :
4822 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4823 : // transposed view yield to extremely poor/unusable performance. This fixes
4824 : // this by using temporary memory to read in a contiguous buffer in a
4825 : // row-major order, and then do the transposition to the final buffer.
4826 :
4827 189 : bool GDALMDArray::ReadForTransposedRequest(
4828 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4829 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4830 : void *pDstBuffer) const
4831 : {
4832 189 : const size_t nDims(GetDimensionCount());
4833 189 : if (nDims == 0)
4834 : {
4835 0 : CPLAssert(false);
4836 : return false; // shouldn't happen
4837 : }
4838 189 : size_t nElts = 1;
4839 500 : for (size_t i = 0; i < nDims; ++i)
4840 311 : nElts *= count[i];
4841 :
4842 378 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4843 189 : tmpBufferStrides.back() = 1;
4844 311 : for (size_t i = nDims - 1; i > 0;)
4845 : {
4846 122 : --i;
4847 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4848 : }
4849 :
4850 189 : const auto &eDT = GetDataType();
4851 189 : const auto nDTSize = eDT.GetSize();
4852 322 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4853 338 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4854 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4855 : {
4856 : // Optimization of the optimization if only the last 2 dims are
4857 : // transposed that saves on temporary buffer allocation
4858 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4859 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4860 23 : bool bRowMajorStridesForNonLast2Dims = true;
4861 23 : size_t nEltsNonLast2Dims = 1;
4862 40 : for (size_t i = nDims - 2; i > 0;)
4863 : {
4864 17 : --i;
4865 17 : if (static_cast<size_t>(bufferStride[i]) !=
4866 : nCurStrideForRowMajorStrides)
4867 : {
4868 4 : bRowMajorStridesForNonLast2Dims = false;
4869 : }
4870 : // Integer overflows have already been checked in
4871 : // CheckReadWriteParams()
4872 17 : nCurStrideForRowMajorStrides *= count[i];
4873 17 : nEltsNonLast2Dims *= count[i];
4874 : }
4875 23 : if (bRowMajorStridesForNonLast2Dims)
4876 : {
4877 : // We read in the final buffer!
4878 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4879 19 : eDT, pDstBuffer))
4880 : {
4881 0 : return false;
4882 : }
4883 :
4884 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4885 19 : nEltsNonLast2Dims);
4886 : }
4887 : }
4888 :
4889 170 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4890 170 : if (pTempBuffer == nullptr)
4891 0 : return false;
4892 :
4893 170 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4894 170 : pTempBuffer))
4895 : {
4896 0 : VSIFree(pTempBuffer);
4897 0 : return false;
4898 : }
4899 170 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4900 : count, bufferStride);
4901 :
4902 170 : if (eDT.NeedsFreeDynamicMemory())
4903 : {
4904 95 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4905 190 : for (size_t i = 0; i < nElts; ++i)
4906 : {
4907 95 : eDT.FreeDynamicMemory(pabyPtr);
4908 95 : pabyPtr += nDTSize;
4909 : }
4910 : }
4911 :
4912 170 : VSIFree(pTempBuffer);
4913 170 : return true;
4914 : }
4915 :
4916 : /************************************************************************/
4917 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4918 : /************************************************************************/
4919 :
4920 : // Returns true if at all following conditions are met:
4921 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4922 : // defines a row-major ordered contiguous buffer.
4923 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4924 : const size_t *count, const GInt64 *arrayStep,
4925 : const GPtrDiff_t *bufferStride,
4926 : const GDALExtendedDataType &bufferDataType) const
4927 : {
4928 78 : if (bufferDataType != GetDataType())
4929 5 : return false;
4930 73 : size_t nExpectedStride = 1;
4931 166 : for (size_t i = GetDimensionCount(); i > 0;)
4932 : {
4933 96 : --i;
4934 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4935 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4936 : {
4937 3 : return false;
4938 : }
4939 93 : nExpectedStride *= count[i];
4940 : }
4941 70 : return true;
4942 : }
4943 :
4944 : /************************************************************************/
4945 : /* ReadUsingContiguousIRead() */
4946 : /************************************************************************/
4947 :
4948 : // Used for example by the TileDB driver when requesting it with
4949 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4950 : // not defining a row-major ordered contiguous buffer.
4951 : // Should only be called when at least one of the above conditions are true,
4952 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4953 : // returning none.
4954 : // This method will call IRead() again with arrayStep[] == 1,
4955 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4956 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4957 : // content of that temporary buffer onto pDstBuffer.
4958 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4959 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4960 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4961 : void *pDstBuffer) const
4962 : {
4963 7 : const size_t nDims(GetDimensionCount());
4964 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4965 14 : std::vector<size_t> anTmpCount(nDims);
4966 7 : const auto &oType = GetDataType();
4967 7 : size_t nMemArraySize = oType.GetSize();
4968 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4969 7 : GPtrDiff_t nStride = 1;
4970 18 : for (size_t i = nDims; i > 0;)
4971 : {
4972 11 : --i;
4973 11 : if (arrayStep[i] > 0)
4974 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4975 : else
4976 2 : anTmpStartIdx[i] =
4977 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4978 : const uint64_t nCount =
4979 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4980 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4981 : {
4982 0 : CPLError(CE_Failure, CPLE_AppDefined,
4983 : "Read() failed due to too large memory requirement");
4984 0 : return false;
4985 : }
4986 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4987 11 : nMemArraySize *= anTmpCount[i];
4988 11 : anTmpStride[i] = nStride;
4989 11 : nStride *= anTmpCount[i];
4990 : }
4991 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4992 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4993 7 : if (!pTmpBuffer)
4994 0 : return false;
4995 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4996 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4997 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4998 : {
4999 0 : return false;
5000 : }
5001 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5002 18 : for (size_t i = 0; i < nDims; ++i)
5003 : {
5004 11 : if (arrayStep[i] > 0)
5005 9 : anTmpStartIdx[i] = 0;
5006 : else
5007 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5008 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5009 22 : std::string(), std::string(), std::string(), std::string(),
5010 22 : anTmpCount[i]);
5011 : }
5012 : auto poMEMArray =
5013 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5014 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5015 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5016 7 : bufferStride, bufferDataType, pDstBuffer);
5017 : }
5018 :
5019 : //! @endcond
5020 :
5021 : /************************************************************************/
5022 : /* GDALSlicedMDArray */
5023 : /************************************************************************/
5024 :
5025 : class GDALSlicedMDArray final : public GDALPamMDArray
5026 : {
5027 : private:
5028 : std::shared_ptr<GDALMDArray> m_poParent{};
5029 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5030 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5031 : std::vector<Range>
5032 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5033 :
5034 : mutable std::vector<GUInt64> m_parentStart;
5035 : mutable std::vector<size_t> m_parentCount;
5036 : mutable std::vector<GInt64> m_parentStep;
5037 : mutable std::vector<GPtrDiff_t> m_parentStride;
5038 :
5039 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5040 : const GInt64 *arrayStep,
5041 : const GPtrDiff_t *bufferStride) const;
5042 :
5043 : protected:
5044 582 : explicit GDALSlicedMDArray(
5045 : const std::shared_ptr<GDALMDArray> &poParent,
5046 : const std::string &viewExpr,
5047 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5048 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5049 : std::vector<Range> &&parentRanges)
5050 1746 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5051 1746 : poParent->GetFullName() +
5052 1164 : " (" + viewExpr + ")"),
5053 1164 : GDALPamMDArray(std::string(),
5054 1164 : "Sliced view of " + poParent->GetFullName() + " (" +
5055 1164 : viewExpr + ")",
5056 1164 : GDALPamMultiDim::GetPAM(poParent),
5057 : poParent->GetContext()),
5058 1164 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5059 582 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5060 582 : m_parentRanges(std::move(parentRanges)),
5061 582 : m_parentStart(m_poParent->GetDimensionCount()),
5062 582 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5063 582 : m_parentStep(m_poParent->GetDimensionCount()),
5064 4656 : m_parentStride(m_poParent->GetDimensionCount())
5065 : {
5066 582 : }
5067 :
5068 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5069 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5070 : const GDALExtendedDataType &bufferDataType,
5071 : void *pDstBuffer) const override;
5072 :
5073 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5074 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5075 : const GDALExtendedDataType &bufferDataType,
5076 : const void *pSrcBuffer) override;
5077 :
5078 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5079 : CSLConstList papszOptions) const override;
5080 :
5081 : public:
5082 : static std::shared_ptr<GDALSlicedMDArray>
5083 582 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5084 : const std::string &viewExpr,
5085 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5086 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5087 : std::vector<Range> &&parentRanges)
5088 : {
5089 582 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5090 582 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5091 :
5092 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5093 582 : poParent, viewExpr, std::move(dims),
5094 582 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5095 582 : newAr->SetSelf(newAr);
5096 582 : return newAr;
5097 : }
5098 :
5099 56 : bool IsWritable() const override
5100 : {
5101 56 : return m_poParent->IsWritable();
5102 : }
5103 :
5104 990 : const std::string &GetFilename() const override
5105 : {
5106 990 : return m_poParent->GetFilename();
5107 : }
5108 :
5109 : const std::vector<std::shared_ptr<GDALDimension>> &
5110 3687 : GetDimensions() const override
5111 : {
5112 3687 : return m_dims;
5113 : }
5114 :
5115 1402 : const GDALExtendedDataType &GetDataType() const override
5116 : {
5117 1402 : return m_poParent->GetDataType();
5118 : }
5119 :
5120 2 : const std::string &GetUnit() const override
5121 : {
5122 2 : return m_poParent->GetUnit();
5123 : }
5124 :
5125 : // bool SetUnit(const std::string& osUnit) override { return
5126 : // m_poParent->SetUnit(osUnit); }
5127 :
5128 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5129 : {
5130 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5131 2 : if (!poSrcSRS)
5132 1 : return nullptr;
5133 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5134 2 : std::vector<int> dstMapping;
5135 3 : for (int srcAxis : srcMapping)
5136 : {
5137 2 : bool bFound = false;
5138 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5139 : {
5140 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5141 3 : srcAxis - 1)
5142 : {
5143 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5144 2 : bFound = true;
5145 2 : break;
5146 : }
5147 : }
5148 2 : if (!bFound)
5149 : {
5150 0 : dstMapping.push_back(0);
5151 : }
5152 : }
5153 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5154 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5155 1 : return poClone;
5156 : }
5157 :
5158 59 : const void *GetRawNoDataValue() const override
5159 : {
5160 59 : return m_poParent->GetRawNoDataValue();
5161 : }
5162 :
5163 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5164 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5165 :
5166 2 : double GetOffset(bool *pbHasOffset,
5167 : GDALDataType *peStorageType) const override
5168 : {
5169 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5170 : }
5171 :
5172 2 : double GetScale(bool *pbHasScale,
5173 : GDALDataType *peStorageType) const override
5174 : {
5175 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5176 : }
5177 :
5178 : // bool SetOffset(double dfOffset) override { return
5179 : // m_poParent->SetOffset(dfOffset); }
5180 :
5181 : // bool SetScale(double dfScale) override { return
5182 : // m_poParent->SetScale(dfScale); }
5183 :
5184 198 : std::vector<GUInt64> GetBlockSize() const override
5185 : {
5186 198 : std::vector<GUInt64> ret(GetDimensionCount());
5187 396 : const auto parentBlockSize(m_poParent->GetBlockSize());
5188 598 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5189 : {
5190 400 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5191 400 : if (iOldAxis != static_cast<size_t>(-1))
5192 : {
5193 400 : ret[i] = parentBlockSize[iOldAxis];
5194 : }
5195 : }
5196 396 : return ret;
5197 : }
5198 :
5199 : std::shared_ptr<GDALAttribute>
5200 6 : GetAttribute(const std::string &osName) const override
5201 : {
5202 6 : return m_poParent->GetAttribute(osName);
5203 : }
5204 :
5205 : std::vector<std::shared_ptr<GDALAttribute>>
5206 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5207 : {
5208 24 : return m_poParent->GetAttributes(papszOptions);
5209 : }
5210 : };
5211 :
5212 : /************************************************************************/
5213 : /* PrepareParentArrays() */
5214 : /************************************************************************/
5215 :
5216 480 : void GDALSlicedMDArray::PrepareParentArrays(
5217 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5218 : const GPtrDiff_t *bufferStride) const
5219 : {
5220 480 : const size_t nParentDimCount = m_parentRanges.size();
5221 1496 : for (size_t i = 0; i < nParentDimCount; i++)
5222 : {
5223 : // For dimensions in parent that have no existence in sliced array
5224 1016 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5225 : }
5226 :
5227 1265 : for (size_t i = 0; i < m_dims.size(); i++)
5228 : {
5229 785 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5230 785 : if (iParent != static_cast<size_t>(-1))
5231 : {
5232 783 : m_parentStart[iParent] =
5233 783 : m_parentRanges[iParent].m_nIncr >= 0
5234 783 : ? m_parentRanges[iParent].m_nStartIdx +
5235 749 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5236 34 : : m_parentRanges[iParent].m_nStartIdx -
5237 68 : arrayStartIdx[i] *
5238 34 : static_cast<GUInt64>(
5239 34 : -m_parentRanges[iParent].m_nIncr);
5240 783 : m_parentCount[iParent] = count[i];
5241 783 : if (arrayStep)
5242 : {
5243 782 : m_parentStep[iParent] =
5244 782 : count[i] == 1 ? 1 :
5245 : // other checks should have ensured this does
5246 : // not overflow
5247 592 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5248 : }
5249 783 : if (bufferStride)
5250 : {
5251 782 : m_parentStride[iParent] = bufferStride[i];
5252 : }
5253 : }
5254 : }
5255 480 : }
5256 :
5257 : /************************************************************************/
5258 : /* IRead() */
5259 : /************************************************************************/
5260 :
5261 447 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5262 : const GInt64 *arrayStep,
5263 : const GPtrDiff_t *bufferStride,
5264 : const GDALExtendedDataType &bufferDataType,
5265 : void *pDstBuffer) const
5266 : {
5267 447 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5268 894 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5269 447 : m_parentStep.data(), m_parentStride.data(),
5270 447 : bufferDataType, pDstBuffer);
5271 : }
5272 :
5273 : /************************************************************************/
5274 : /* IWrite() */
5275 : /************************************************************************/
5276 :
5277 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5278 : const size_t *count, const GInt64 *arrayStep,
5279 : const GPtrDiff_t *bufferStride,
5280 : const GDALExtendedDataType &bufferDataType,
5281 : const void *pSrcBuffer)
5282 : {
5283 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5284 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5285 32 : m_parentStep.data(), m_parentStride.data(),
5286 32 : bufferDataType, pSrcBuffer);
5287 : }
5288 :
5289 : /************************************************************************/
5290 : /* IAdviseRead() */
5291 : /************************************************************************/
5292 :
5293 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5294 : const size_t *count,
5295 : CSLConstList papszOptions) const
5296 : {
5297 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5298 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5299 1 : papszOptions);
5300 : }
5301 :
5302 : /************************************************************************/
5303 : /* CreateSlicedArray() */
5304 : /************************************************************************/
5305 :
5306 : static std::shared_ptr<GDALMDArray>
5307 600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5308 : const std::string &viewExpr, const std::string &activeSlice,
5309 : bool bRenameDimensions,
5310 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5311 : {
5312 600 : const auto &srcDims(self->GetDimensions());
5313 600 : if (srcDims.empty())
5314 : {
5315 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5316 2 : return nullptr;
5317 : }
5318 :
5319 1196 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5320 598 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5321 :
5322 1196 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5323 1196 : std::vector<size_t> mapDimIdxToParentDimIdx;
5324 1196 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5325 598 : newDims.reserve(nTokens);
5326 598 : mapDimIdxToParentDimIdx.reserve(nTokens);
5327 598 : parentRanges.reserve(nTokens);
5328 :
5329 598 : bool bGotEllipsis = false;
5330 598 : size_t nCurSrcDim = 0;
5331 1768 : for (size_t i = 0; i < nTokens; i++)
5332 : {
5333 1186 : const char *pszIdxSpec = aosTokens[i];
5334 1186 : if (EQUAL(pszIdxSpec, "..."))
5335 : {
5336 38 : if (bGotEllipsis)
5337 : {
5338 2 : CPLError(CE_Failure, CPLE_AppDefined,
5339 : "Only one single ellipsis is supported");
5340 2 : return nullptr;
5341 : }
5342 36 : bGotEllipsis = true;
5343 36 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5344 79 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5345 : {
5346 43 : parentRanges.emplace_back(0, 1);
5347 43 : newDims.push_back(srcDims[nCurSrcDim]);
5348 43 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5349 : }
5350 36 : continue;
5351 : }
5352 1148 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5353 1145 : EQUAL(pszIdxSpec, "np.newaxis"))
5354 : {
5355 3 : newDims.push_back(std::make_shared<GDALDimension>(
5356 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5357 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5358 3 : continue;
5359 : }
5360 1145 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5361 : {
5362 325 : if (nCurSrcDim >= srcDims.size())
5363 : {
5364 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5365 : activeSlice.c_str());
5366 7 : return nullptr;
5367 : }
5368 :
5369 323 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5370 323 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5371 323 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5372 319 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5373 : {
5374 5 : CPLError(CE_Failure, CPLE_AppDefined,
5375 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5376 5 : return nullptr;
5377 : }
5378 318 : if (nVal < 0)
5379 0 : nVal += nDimSize;
5380 318 : parentRanges.emplace_back(nVal, 0);
5381 : }
5382 : else
5383 : {
5384 820 : if (nCurSrcDim >= srcDims.size())
5385 : {
5386 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5387 : activeSlice.c_str());
5388 7 : return nullptr;
5389 : }
5390 :
5391 : CPLStringList aosRangeTokens(
5392 819 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5393 819 : int nRangeTokens = aosRangeTokens.size();
5394 819 : if (nRangeTokens > 3)
5395 : {
5396 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5397 : pszIdxSpec);
5398 1 : return nullptr;
5399 : }
5400 818 : if (nRangeTokens <= 1)
5401 : {
5402 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5403 : pszIdxSpec);
5404 1 : return nullptr;
5405 : }
5406 817 : const char *pszStart = aosRangeTokens[0];
5407 817 : const char *pszEnd = aosRangeTokens[1];
5408 817 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5409 817 : GDALSlicedMDArray::Range range;
5410 817 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5411 817 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5412 1633 : if (range.m_nIncr == 0 ||
5413 816 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5414 : {
5415 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5416 1 : return nullptr;
5417 : }
5418 816 : auto startIdx(CPLAtoGIntBig(pszStart));
5419 816 : if (startIdx < 0)
5420 : {
5421 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5422 0 : startIdx = 0;
5423 : else
5424 0 : startIdx = nDimSize + startIdx;
5425 : }
5426 816 : const bool bPosIncr = range.m_nIncr > 0;
5427 816 : range.m_nStartIdx = startIdx;
5428 1632 : range.m_nStartIdx = EQUAL(pszStart, "")
5429 816 : ? (bPosIncr ? 0 : nDimSize - 1)
5430 : : range.m_nStartIdx;
5431 816 : if (range.m_nStartIdx >= nDimSize - 1)
5432 186 : range.m_nStartIdx = nDimSize - 1;
5433 816 : auto endIdx(CPLAtoGIntBig(pszEnd));
5434 816 : if (endIdx < 0)
5435 : {
5436 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5437 1 : if (nDimSize < positiveEndIdx)
5438 0 : endIdx = 0;
5439 : else
5440 1 : endIdx = nDimSize - positiveEndIdx;
5441 : }
5442 816 : GUInt64 nEndIdx = endIdx;
5443 816 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5444 816 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5445 814 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5446 : {
5447 3 : CPLError(CE_Failure, CPLE_AppDefined,
5448 : "Output dimension of size 0 is not allowed");
5449 3 : return nullptr;
5450 : }
5451 813 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5452 813 : const auto nAbsIncr = std::abs(range.m_nIncr);
5453 813 : const GUInt64 newSize =
5454 : bPosIncr
5455 847 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5456 34 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5457 1341 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5458 528 : newSize == srcDims[nCurSrcDim]->GetSize())
5459 : {
5460 159 : newDims.push_back(srcDims[nCurSrcDim]);
5461 : }
5462 : else
5463 : {
5464 654 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5465 654 : if (bRenameDimensions)
5466 : {
5467 : osNewDimName =
5468 1212 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5469 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5470 : "_" CPL_FRMT_GUIB,
5471 606 : static_cast<GUIntBig>(range.m_nStartIdx),
5472 606 : static_cast<GIntBig>(range.m_nIncr),
5473 606 : static_cast<GUIntBig>(newSize));
5474 : }
5475 654 : newDims.push_back(std::make_shared<GDALDimension>(
5476 1308 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5477 1308 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5478 : : std::string(),
5479 : newSize));
5480 : }
5481 813 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5482 813 : parentRanges.emplace_back(range);
5483 : }
5484 :
5485 1131 : nCurSrcDim++;
5486 : }
5487 655 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5488 : {
5489 73 : parentRanges.emplace_back(0, 1);
5490 73 : newDims.push_back(srcDims[nCurSrcDim]);
5491 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5492 : }
5493 :
5494 582 : GDALMDArray::ViewSpec viewSpec;
5495 582 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5496 582 : viewSpec.m_parentRanges = parentRanges;
5497 582 : viewSpecs.emplace_back(std::move(viewSpec));
5498 :
5499 1164 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5500 582 : std::move(mapDimIdxToParentDimIdx),
5501 1164 : std::move(parentRanges));
5502 : }
5503 :
5504 : /************************************************************************/
5505 : /* GDALExtractFieldMDArray */
5506 : /************************************************************************/
5507 :
5508 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5509 : {
5510 : private:
5511 : std::shared_ptr<GDALMDArray> m_poParent{};
5512 : GDALExtendedDataType m_dt;
5513 : std::string m_srcCompName;
5514 : mutable std::vector<GByte> m_pabyNoData{};
5515 :
5516 : protected:
5517 82 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5518 : const std::string &fieldName,
5519 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5520 328 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5521 164 : " of " +
5522 82 : poParent->GetFullName()),
5523 : GDALPamMDArray(
5524 164 : std::string(),
5525 164 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5526 164 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5527 : m_poParent(poParent), m_dt(srcComp->GetType()),
5528 410 : m_srcCompName(srcComp->GetName())
5529 : {
5530 82 : m_pabyNoData.resize(m_dt.GetSize());
5531 82 : }
5532 :
5533 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5534 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5535 : const GDALExtendedDataType &bufferDataType,
5536 : void *pDstBuffer) const override;
5537 :
5538 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5539 : CSLConstList papszOptions) const override
5540 : {
5541 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5542 : }
5543 :
5544 : public:
5545 : static std::shared_ptr<GDALExtractFieldMDArray>
5546 82 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5547 : const std::string &fieldName,
5548 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5549 : {
5550 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5551 82 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5552 82 : newAr->SetSelf(newAr);
5553 82 : return newAr;
5554 : }
5555 :
5556 164 : ~GDALExtractFieldMDArray() override
5557 82 : {
5558 82 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5559 164 : }
5560 :
5561 41 : bool IsWritable() const override
5562 : {
5563 41 : return m_poParent->IsWritable();
5564 : }
5565 :
5566 247 : const std::string &GetFilename() const override
5567 : {
5568 247 : return m_poParent->GetFilename();
5569 : }
5570 :
5571 : const std::vector<std::shared_ptr<GDALDimension>> &
5572 351 : GetDimensions() const override
5573 : {
5574 351 : return m_poParent->GetDimensions();
5575 : }
5576 :
5577 295 : const GDALExtendedDataType &GetDataType() const override
5578 : {
5579 295 : return m_dt;
5580 : }
5581 :
5582 2 : const std::string &GetUnit() const override
5583 : {
5584 2 : return m_poParent->GetUnit();
5585 : }
5586 :
5587 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5588 : {
5589 2 : return m_poParent->GetSpatialRef();
5590 : }
5591 :
5592 60 : const void *GetRawNoDataValue() const override
5593 : {
5594 60 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5595 60 : if (parentNoData == nullptr)
5596 1 : return nullptr;
5597 :
5598 59 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5599 59 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5600 :
5601 118 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5602 118 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5603 118 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5604 59 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5605 177 : std::move(comps)));
5606 :
5607 59 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5608 59 : &m_pabyNoData[0], tmpDT);
5609 :
5610 59 : return &m_pabyNoData[0];
5611 : }
5612 :
5613 2 : double GetOffset(bool *pbHasOffset,
5614 : GDALDataType *peStorageType) const override
5615 : {
5616 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5617 : }
5618 :
5619 2 : double GetScale(bool *pbHasScale,
5620 : GDALDataType *peStorageType) const override
5621 : {
5622 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5623 : }
5624 :
5625 42 : std::vector<GUInt64> GetBlockSize() const override
5626 : {
5627 42 : return m_poParent->GetBlockSize();
5628 : }
5629 : };
5630 :
5631 : /************************************************************************/
5632 : /* IRead() */
5633 : /************************************************************************/
5634 :
5635 88 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5636 : const size_t *count,
5637 : const GInt64 *arrayStep,
5638 : const GPtrDiff_t *bufferStride,
5639 : const GDALExtendedDataType &bufferDataType,
5640 : void *pDstBuffer) const
5641 : {
5642 176 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5643 176 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5644 176 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5645 : auto tmpDT(GDALExtendedDataType::Create(
5646 176 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5647 :
5648 88 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5649 176 : tmpDT, pDstBuffer);
5650 : }
5651 :
5652 : /************************************************************************/
5653 : /* CreateFieldNameExtractArray() */
5654 : /************************************************************************/
5655 :
5656 : static std::shared_ptr<GDALMDArray>
5657 83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5658 : const std::string &fieldName)
5659 : {
5660 83 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5661 83 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5662 202 : for (const auto &comp : self->GetDataType().GetComponents())
5663 : {
5664 201 : if (comp->GetName() == fieldName)
5665 : {
5666 82 : srcComp = ∁
5667 82 : break;
5668 : }
5669 : }
5670 83 : if (srcComp == nullptr)
5671 : {
5672 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5673 : fieldName.c_str());
5674 1 : return nullptr;
5675 : }
5676 82 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5677 : }
5678 :
5679 : /************************************************************************/
5680 : /* GetView() */
5681 : /************************************************************************/
5682 :
5683 : // clang-format off
5684 : /** Return a view of the array using slicing or field access.
5685 : *
5686 : * The slice expression uses the same syntax as NumPy basic slicing and
5687 : * indexing. See
5688 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5689 : * Or it can use field access by name. See
5690 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5691 : *
5692 : * Multiple [] bracket elements can be concatenated, with a slice expression
5693 : * or field name inside each.
5694 : *
5695 : * For basic slicing and indexing, inside each [] bracket element, a list of
5696 : * indexes that apply to successive source dimensions, can be specified, using
5697 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5698 : * or newaxis, using a comma separator.
5699 : *
5700 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5701 : * <ul>
5702 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5703 : * at index 1 in the first dimension, and index 2 in the second dimension
5704 : * from the source array. That is 5</li>
5705 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5706 : * implemented internally doing this intermediate slicing approach.</li>
5707 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5708 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5709 : * first dimension. That is [4,5,6,7].</li>
5710 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5711 : * second dimension. That is [2,6].</li>
5712 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5713 : * the second dimension. That is [[2],[6]].</li>
5714 : * <li>GetView("[::,2]"): Same as
5715 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5716 : * ellipsis only expands to one dimension here.</li>
5717 : * <li>GetView("[:,::2]"):
5718 : * returns a 2-dimensional array, with even-indexed elements of the second
5719 : * dimension. That is [[0,2],[4,6]].</li>
5720 : * <li>GetView("[:,1::2]"): returns a
5721 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5722 : * is [[1,3],[5,7]].</li>
5723 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5724 : * array, with elements of the second dimension with index in the range [1,3[.
5725 : * That is [[1,2],[5,6]].</li>
5726 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5727 : * array, with the values in first dimension reversed. That is
5728 : * [[4,5,6,7],[0,1,2,3]].</li>
5729 : * <li>GetView("[newaxis,...]"): returns a
5730 : * 3-dimensional array, with an additional dimension of size 1 put at the
5731 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5732 : * </ul>
5733 : *
5734 : * One difference with NumPy behavior is that ranges that would result in
5735 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5736 : * GDAL multidimensional model).
5737 : *
5738 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5739 : * Multiple field specification is not supported currently.
5740 : *
5741 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5742 : *
5743 : * \note When using the GDAL Python bindings, natural Python syntax can be
5744 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5745 : * ar.GetView("[0,::,1]['foo']")
5746 : * \note When using the C++ API and integer indexing only, you may use the
5747 : * at(idx0, idx1, ...) method.
5748 : *
5749 : * The returned array holds a reference to the original one, and thus is
5750 : * a view of it (not a copy). If the content of the original array changes,
5751 : * the content of the view array too. When using basic slicing and indexing,
5752 : * the view can be written if the underlying array is writable.
5753 : *
5754 : * This is the same as the C function GDALMDArrayGetView()
5755 : *
5756 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5757 : * access.
5758 : * @return a new array, that holds a reference to the original one, and thus is
5759 : * a view of it (not a copy), or nullptr in case of error.
5760 : */
5761 : // clang-format on
5762 :
5763 : std::shared_ptr<GDALMDArray>
5764 619 : GDALMDArray::GetView(const std::string &viewExpr) const
5765 : {
5766 1238 : std::vector<ViewSpec> viewSpecs;
5767 1238 : return GetView(viewExpr, true, viewSpecs);
5768 : }
5769 :
5770 : //! @cond Doxygen_Suppress
5771 : std::shared_ptr<GDALMDArray>
5772 689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5773 : std::vector<ViewSpec> &viewSpecs) const
5774 : {
5775 1378 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5776 689 : if (!self)
5777 : {
5778 1 : CPLError(CE_Failure, CPLE_AppDefined,
5779 : "Driver implementation issue: m_pSelf not set !");
5780 1 : return nullptr;
5781 : }
5782 688 : std::string curExpr(viewExpr);
5783 : while (true)
5784 : {
5785 691 : if (curExpr.empty() || curExpr[0] != '[')
5786 : {
5787 2 : CPLError(CE_Failure, CPLE_AppDefined,
5788 : "Slice string should start with ['");
5789 688 : return nullptr;
5790 : }
5791 :
5792 689 : std::string fieldName;
5793 : size_t endExpr;
5794 689 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5795 : {
5796 87 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5797 : {
5798 2 : CPLError(CE_Failure, CPLE_AppDefined,
5799 : "Field access not allowed on non-compound data type");
5800 2 : return nullptr;
5801 : }
5802 85 : size_t idx = 2;
5803 768 : for (; idx < curExpr.size(); idx++)
5804 : {
5805 767 : const char ch = curExpr[idx];
5806 767 : if (ch == curExpr[1])
5807 84 : break;
5808 683 : if (ch == '\\' && idx + 1 < curExpr.size())
5809 : {
5810 1 : fieldName += curExpr[idx + 1];
5811 1 : idx++;
5812 : }
5813 : else
5814 : {
5815 682 : fieldName += ch;
5816 : }
5817 : }
5818 85 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5819 : {
5820 2 : CPLError(CE_Failure, CPLE_AppDefined,
5821 : "Invalid field access specification");
5822 2 : return nullptr;
5823 : }
5824 83 : endExpr = idx + 1;
5825 : }
5826 : else
5827 : {
5828 602 : endExpr = curExpr.find(']');
5829 : }
5830 685 : if (endExpr == std::string::npos)
5831 : {
5832 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5833 1 : return nullptr;
5834 : }
5835 684 : if (endExpr == 1)
5836 : {
5837 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5838 1 : return nullptr;
5839 : }
5840 683 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5841 :
5842 683 : if (!fieldName.empty())
5843 : {
5844 166 : ViewSpec viewSpec;
5845 83 : viewSpec.m_osFieldName = fieldName;
5846 83 : viewSpecs.emplace_back(std::move(viewSpec));
5847 : }
5848 :
5849 683 : auto newArray = !fieldName.empty()
5850 : ? CreateFieldNameExtractArray(self, fieldName)
5851 : : CreateSlicedArray(self, viewExpr, activeSlice,
5852 683 : bRenameDimensions, viewSpecs);
5853 :
5854 683 : if (endExpr == curExpr.size() - 1)
5855 : {
5856 680 : return newArray;
5857 : }
5858 3 : self = std::move(newArray);
5859 3 : curExpr = curExpr.substr(endExpr + 1);
5860 3 : }
5861 : }
5862 :
5863 : //! @endcond
5864 :
5865 : std::shared_ptr<GDALMDArray>
5866 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5867 : {
5868 19 : std::string osExpr("[");
5869 19 : bool bFirst = true;
5870 45 : for (const auto &idx : indices)
5871 : {
5872 26 : if (!bFirst)
5873 7 : osExpr += ',';
5874 26 : bFirst = false;
5875 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5876 : }
5877 57 : return GetView(osExpr + ']');
5878 : }
5879 :
5880 : /************************************************************************/
5881 : /* operator[] */
5882 : /************************************************************************/
5883 :
5884 : /** Return a view of the array using field access
5885 : *
5886 : * Equivalent of GetView("['fieldName']")
5887 : *
5888 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5889 : */
5890 : std::shared_ptr<GDALMDArray>
5891 2 : GDALMDArray::operator[](const std::string &fieldName) const
5892 : {
5893 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5894 4 : .replaceAll('\\', "\\\\")
5895 4 : .replaceAll('\'', "\\\'")
5896 6 : .c_str()));
5897 : }
5898 :
5899 : /************************************************************************/
5900 : /* GDALMDArrayTransposed */
5901 : /************************************************************************/
5902 :
5903 : class GDALMDArrayTransposed final : public GDALPamMDArray
5904 : {
5905 : private:
5906 : std::shared_ptr<GDALMDArray> m_poParent{};
5907 : std::vector<int> m_anMapNewAxisToOldAxis{};
5908 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5909 :
5910 : mutable std::vector<GUInt64> m_parentStart;
5911 : mutable std::vector<size_t> m_parentCount;
5912 : mutable std::vector<GInt64> m_parentStep;
5913 : mutable std::vector<GPtrDiff_t> m_parentStride;
5914 :
5915 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5916 : const GInt64 *arrayStep,
5917 : const GPtrDiff_t *bufferStride) const;
5918 :
5919 : static std::string
5920 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5921 : {
5922 84 : std::string ret;
5923 84 : ret += '[';
5924 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5925 : {
5926 228 : if (i > 0)
5927 144 : ret += ',';
5928 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5929 : }
5930 84 : ret += ']';
5931 84 : return ret;
5932 : }
5933 :
5934 : protected:
5935 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5936 : const std::vector<int> &anMapNewAxisToOldAxis,
5937 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5938 84 : : GDALAbstractMDArray(std::string(),
5939 84 : "Transposed view of " + poParent->GetFullName() +
5940 84 : " along " +
5941 42 : MappingToStr(anMapNewAxisToOldAxis)),
5942 84 : GDALPamMDArray(std::string(),
5943 84 : "Transposed view of " + poParent->GetFullName() +
5944 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5945 84 : GDALPamMultiDim::GetPAM(poParent),
5946 : poParent->GetContext()),
5947 42 : m_poParent(std::move(poParent)),
5948 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5949 42 : m_dims(std::move(dims)),
5950 42 : m_parentStart(m_poParent->GetDimensionCount()),
5951 42 : m_parentCount(m_poParent->GetDimensionCount()),
5952 42 : m_parentStep(m_poParent->GetDimensionCount()),
5953 336 : m_parentStride(m_poParent->GetDimensionCount())
5954 : {
5955 42 : }
5956 :
5957 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5958 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5959 : const GDALExtendedDataType &bufferDataType,
5960 : void *pDstBuffer) const override;
5961 :
5962 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5963 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5964 : const GDALExtendedDataType &bufferDataType,
5965 : const void *pSrcBuffer) override;
5966 :
5967 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5968 : CSLConstList papszOptions) const override;
5969 :
5970 : public:
5971 : static std::shared_ptr<GDALMDArrayTransposed>
5972 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5973 : const std::vector<int> &anMapNewAxisToOldAxis)
5974 : {
5975 42 : const auto &parentDims(poParent->GetDimensions());
5976 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5977 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5978 : {
5979 114 : if (iOldAxis < 0)
5980 : {
5981 1 : dims.push_back(std::make_shared<GDALDimension>(
5982 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5983 : }
5984 : else
5985 : {
5986 113 : dims.emplace_back(parentDims[iOldAxis]);
5987 : }
5988 : }
5989 :
5990 : auto newAr(
5991 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5992 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5993 42 : newAr->SetSelf(newAr);
5994 84 : return newAr;
5995 : }
5996 :
5997 1 : bool IsWritable() const override
5998 : {
5999 1 : return m_poParent->IsWritable();
6000 : }
6001 :
6002 84 : const std::string &GetFilename() const override
6003 : {
6004 84 : return m_poParent->GetFilename();
6005 : }
6006 :
6007 : const std::vector<std::shared_ptr<GDALDimension>> &
6008 358 : GetDimensions() const override
6009 : {
6010 358 : return m_dims;
6011 : }
6012 :
6013 141 : const GDALExtendedDataType &GetDataType() const override
6014 : {
6015 141 : return m_poParent->GetDataType();
6016 : }
6017 :
6018 4 : const std::string &GetUnit() const override
6019 : {
6020 4 : return m_poParent->GetUnit();
6021 : }
6022 :
6023 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6024 : {
6025 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6026 5 : if (!poSrcSRS)
6027 2 : return nullptr;
6028 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6029 6 : std::vector<int> dstMapping;
6030 9 : for (int srcAxis : srcMapping)
6031 : {
6032 6 : bool bFound = false;
6033 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6034 : {
6035 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6036 : {
6037 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6038 6 : bFound = true;
6039 6 : break;
6040 : }
6041 : }
6042 6 : if (!bFound)
6043 : {
6044 0 : dstMapping.push_back(0);
6045 : }
6046 : }
6047 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6048 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6049 3 : return poClone;
6050 : }
6051 :
6052 4 : const void *GetRawNoDataValue() const override
6053 : {
6054 4 : return m_poParent->GetRawNoDataValue();
6055 : }
6056 :
6057 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6058 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6059 :
6060 4 : double GetOffset(bool *pbHasOffset,
6061 : GDALDataType *peStorageType) const override
6062 : {
6063 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6064 : }
6065 :
6066 4 : double GetScale(bool *pbHasScale,
6067 : GDALDataType *peStorageType) const override
6068 : {
6069 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6070 : }
6071 :
6072 : // bool SetOffset(double dfOffset) override { return
6073 : // m_poParent->SetOffset(dfOffset); }
6074 :
6075 : // bool SetScale(double dfScale) override { return
6076 : // m_poParent->SetScale(dfScale); }
6077 :
6078 3 : std::vector<GUInt64> GetBlockSize() const override
6079 : {
6080 3 : std::vector<GUInt64> ret(GetDimensionCount());
6081 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6082 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6083 : {
6084 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6085 8 : if (iOldAxis >= 0)
6086 : {
6087 7 : ret[i] = parentBlockSize[iOldAxis];
6088 : }
6089 : }
6090 6 : return ret;
6091 : }
6092 :
6093 : std::shared_ptr<GDALAttribute>
6094 1 : GetAttribute(const std::string &osName) const override
6095 : {
6096 1 : return m_poParent->GetAttribute(osName);
6097 : }
6098 :
6099 : std::vector<std::shared_ptr<GDALAttribute>>
6100 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6101 : {
6102 6 : return m_poParent->GetAttributes(papszOptions);
6103 : }
6104 : };
6105 :
6106 : /************************************************************************/
6107 : /* PrepareParentArrays() */
6108 : /************************************************************************/
6109 :
6110 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6111 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6112 : const GPtrDiff_t *bufferStride) const
6113 : {
6114 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6115 : {
6116 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6117 129 : if (iOldAxis >= 0)
6118 : {
6119 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6120 128 : m_parentCount[iOldAxis] = count[i];
6121 128 : if (arrayStep) // only null when called from IAdviseRead()
6122 : {
6123 126 : m_parentStep[iOldAxis] = arrayStep[i];
6124 : }
6125 128 : if (bufferStride) // only null when called from IAdviseRead()
6126 : {
6127 126 : m_parentStride[iOldAxis] = bufferStride[i];
6128 : }
6129 : }
6130 : }
6131 47 : }
6132 :
6133 : /************************************************************************/
6134 : /* IRead() */
6135 : /************************************************************************/
6136 :
6137 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6138 : const size_t *count, const GInt64 *arrayStep,
6139 : const GPtrDiff_t *bufferStride,
6140 : const GDALExtendedDataType &bufferDataType,
6141 : void *pDstBuffer) const
6142 : {
6143 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6144 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6145 44 : m_parentStep.data(), m_parentStride.data(),
6146 44 : bufferDataType, pDstBuffer);
6147 : }
6148 :
6149 : /************************************************************************/
6150 : /* IWrite() */
6151 : /************************************************************************/
6152 :
6153 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6154 : const size_t *count, const GInt64 *arrayStep,
6155 : const GPtrDiff_t *bufferStride,
6156 : const GDALExtendedDataType &bufferDataType,
6157 : const void *pSrcBuffer)
6158 : {
6159 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6160 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6161 2 : m_parentStep.data(), m_parentStride.data(),
6162 2 : bufferDataType, pSrcBuffer);
6163 : }
6164 :
6165 : /************************************************************************/
6166 : /* IAdviseRead() */
6167 : /************************************************************************/
6168 :
6169 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6170 : const size_t *count,
6171 : CSLConstList papszOptions) const
6172 : {
6173 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6174 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6175 1 : papszOptions);
6176 : }
6177 :
6178 : /************************************************************************/
6179 : /* Transpose() */
6180 : /************************************************************************/
6181 :
6182 : /** Return a view of the array whose axis have been reordered.
6183 : *
6184 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6185 : * and GetDimensionCount() - 1, and each only once.
6186 : * -1 can be used as a special index value to ask for the insertion of a new
6187 : * axis of size 1.
6188 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6189 : * index of one of its dimension, it corresponds to the axis of index
6190 : * anMapNewAxisToOldAxis[i] from the current array.
6191 : *
6192 : * This is similar to the numpy.transpose() method
6193 : *
6194 : * The returned array holds a reference to the original one, and thus is
6195 : * a view of it (not a copy). If the content of the original array changes,
6196 : * the content of the view array too. The view can be written if the underlying
6197 : * array is writable.
6198 : *
6199 : * Note that I/O performance in such a transposed view might be poor.
6200 : *
6201 : * This is the same as the C function GDALMDArrayTranspose().
6202 : *
6203 : * @return a new array, that holds a reference to the original one, and thus is
6204 : * a view of it (not a copy), or nullptr in case of error.
6205 : */
6206 : std::shared_ptr<GDALMDArray>
6207 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6208 : {
6209 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6210 50 : if (!self)
6211 : {
6212 0 : CPLError(CE_Failure, CPLE_AppDefined,
6213 : "Driver implementation issue: m_pSelf not set !");
6214 0 : return nullptr;
6215 : }
6216 50 : const int nDims = static_cast<int>(GetDimensionCount());
6217 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6218 50 : int nCountOldAxis = 0;
6219 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6220 : {
6221 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6222 : {
6223 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6224 4 : return nullptr;
6225 : }
6226 130 : if (iOldAxis >= 0)
6227 : {
6228 128 : if (alreadyUsedOldAxis[iOldAxis])
6229 : {
6230 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6231 : iOldAxis);
6232 1 : return nullptr;
6233 : }
6234 127 : alreadyUsedOldAxis[iOldAxis] = true;
6235 127 : nCountOldAxis++;
6236 : }
6237 : }
6238 46 : if (nCountOldAxis != nDims)
6239 : {
6240 4 : CPLError(CE_Failure, CPLE_AppDefined,
6241 : "One or several original axis missing");
6242 4 : return nullptr;
6243 : }
6244 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6245 : }
6246 :
6247 : /************************************************************************/
6248 : /* IRead() */
6249 : /************************************************************************/
6250 :
6251 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6252 : const size_t *count, const GInt64 *arrayStep,
6253 : const GPtrDiff_t *bufferStride,
6254 : const GDALExtendedDataType &bufferDataType,
6255 : void *pDstBuffer) const
6256 : {
6257 16 : const double dfScale = m_dfScale;
6258 16 : const double dfOffset = m_dfOffset;
6259 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6260 : const auto dtDouble =
6261 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6262 16 : const size_t nDTSize = dtDouble.GetSize();
6263 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6264 :
6265 16 : double adfSrcNoData[2] = {0, 0};
6266 16 : if (m_bHasNoData)
6267 : {
6268 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6269 9 : m_poParent->GetDataType(),
6270 : &adfSrcNoData[0], dtDouble);
6271 : }
6272 :
6273 16 : const auto nDims = GetDimensions().size();
6274 16 : if (nDims == 0)
6275 : {
6276 : double adfVal[2];
6277 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6278 : dtDouble, &adfVal[0]))
6279 : {
6280 0 : return false;
6281 : }
6282 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6283 : {
6284 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6285 6 : if (bDTIsComplex)
6286 : {
6287 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6288 : }
6289 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6290 : bufferDataType);
6291 : }
6292 : else
6293 : {
6294 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6295 : pDstBuffer, bufferDataType);
6296 : }
6297 9 : return true;
6298 : }
6299 :
6300 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6301 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6302 7 : void *pTempBuffer = pDstBuffer;
6303 7 : if (bTempBufferNeeded)
6304 : {
6305 2 : size_t nElts = 1;
6306 2 : actualBufferStrideVector.resize(nDims);
6307 7 : for (size_t i = 0; i < nDims; i++)
6308 5 : nElts *= count[i];
6309 2 : actualBufferStrideVector.back() = 1;
6310 5 : for (size_t i = nDims - 1; i > 0;)
6311 : {
6312 3 : --i;
6313 3 : actualBufferStrideVector[i] =
6314 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6315 : }
6316 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6317 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6318 2 : if (!pTempBuffer)
6319 0 : return false;
6320 : }
6321 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6322 : actualBufferStridePtr, dtDouble, pTempBuffer))
6323 : {
6324 0 : if (bTempBufferNeeded)
6325 0 : VSIFree(pTempBuffer);
6326 0 : return false;
6327 : }
6328 :
6329 : struct Stack
6330 : {
6331 : size_t nIters = 0;
6332 : double *src_ptr = nullptr;
6333 : GByte *dst_ptr = nullptr;
6334 : GPtrDiff_t src_inc_offset = 0;
6335 : GPtrDiff_t dst_inc_offset = 0;
6336 : };
6337 :
6338 7 : std::vector<Stack> stack(nDims);
6339 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6340 23 : for (size_t i = 0; i < nDims; i++)
6341 : {
6342 32 : stack[i].src_inc_offset =
6343 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6344 16 : stack[i].dst_inc_offset =
6345 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6346 : }
6347 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6348 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6349 :
6350 7 : size_t dimIdx = 0;
6351 7 : const size_t nDimsMinus1 = nDims - 1;
6352 : GByte abyDstNoData[16];
6353 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6354 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6355 : bufferDataType);
6356 :
6357 37 : lbl_next_depth:
6358 37 : if (dimIdx == nDimsMinus1)
6359 : {
6360 25 : auto nIters = count[dimIdx];
6361 25 : double *padfVal = stack[dimIdx].src_ptr;
6362 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6363 : while (true)
6364 : {
6365 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6366 : {
6367 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6368 88 : if (bDTIsComplex)
6369 : {
6370 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6371 : }
6372 88 : if (bTempBufferNeeded)
6373 : {
6374 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6375 : dst_ptr, bufferDataType);
6376 : }
6377 : }
6378 : else
6379 : {
6380 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6381 : }
6382 :
6383 92 : if ((--nIters) == 0)
6384 25 : break;
6385 67 : padfVal += stack[dimIdx].src_inc_offset;
6386 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6387 : }
6388 : }
6389 : else
6390 : {
6391 12 : stack[dimIdx].nIters = count[dimIdx];
6392 : while (true)
6393 : {
6394 30 : dimIdx++;
6395 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6396 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6397 30 : goto lbl_next_depth;
6398 30 : lbl_return_to_caller:
6399 30 : dimIdx--;
6400 30 : if ((--stack[dimIdx].nIters) == 0)
6401 12 : break;
6402 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6403 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6404 : }
6405 : }
6406 37 : if (dimIdx > 0)
6407 30 : goto lbl_return_to_caller;
6408 :
6409 7 : if (bTempBufferNeeded)
6410 2 : VSIFree(pTempBuffer);
6411 7 : return true;
6412 : }
6413 :
6414 : /************************************************************************/
6415 : /* IWrite() */
6416 : /************************************************************************/
6417 :
6418 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6419 : const size_t *count, const GInt64 *arrayStep,
6420 : const GPtrDiff_t *bufferStride,
6421 : const GDALExtendedDataType &bufferDataType,
6422 : const void *pSrcBuffer)
6423 : {
6424 16 : const double dfScale = m_dfScale;
6425 16 : const double dfOffset = m_dfOffset;
6426 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6427 : const auto dtDouble =
6428 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6429 16 : const size_t nDTSize = dtDouble.GetSize();
6430 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6431 : const bool bSelfAndParentHaveNoData =
6432 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6433 16 : double dfNoData = 0;
6434 16 : if (m_bHasNoData)
6435 : {
6436 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6437 : &dfNoData, GDT_Float64, 0, 1);
6438 : }
6439 :
6440 16 : double adfSrcNoData[2] = {0, 0};
6441 16 : if (bSelfAndParentHaveNoData)
6442 : {
6443 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6444 7 : m_poParent->GetDataType(),
6445 : &adfSrcNoData[0], dtDouble);
6446 : }
6447 :
6448 16 : const auto nDims = GetDimensions().size();
6449 16 : if (nDims == 0)
6450 : {
6451 : double adfVal[2];
6452 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6453 : dtDouble);
6454 16 : if (bSelfAndParentHaveNoData &&
6455 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6456 : {
6457 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6458 2 : bufferStride, m_poParent->GetDataType(),
6459 4 : m_poParent->GetRawNoDataValue());
6460 : }
6461 : else
6462 : {
6463 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6464 8 : if (bDTIsComplex)
6465 : {
6466 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6467 : }
6468 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6469 8 : bufferStride, dtDouble, &adfVal[0]);
6470 : }
6471 : }
6472 :
6473 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6474 6 : size_t nElts = 1;
6475 6 : tmpBufferStrideVector.resize(nDims);
6476 20 : for (size_t i = 0; i < nDims; i++)
6477 14 : nElts *= count[i];
6478 6 : tmpBufferStrideVector.back() = 1;
6479 14 : for (size_t i = nDims - 1; i > 0;)
6480 : {
6481 8 : --i;
6482 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6483 : }
6484 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6485 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6486 6 : if (!pTempBuffer)
6487 0 : return false;
6488 :
6489 : struct Stack
6490 : {
6491 : size_t nIters = 0;
6492 : double *dst_ptr = nullptr;
6493 : const GByte *src_ptr = nullptr;
6494 : GPtrDiff_t src_inc_offset = 0;
6495 : GPtrDiff_t dst_inc_offset = 0;
6496 : };
6497 :
6498 6 : std::vector<Stack> stack(nDims);
6499 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6500 20 : for (size_t i = 0; i < nDims; i++)
6501 : {
6502 28 : stack[i].dst_inc_offset =
6503 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6504 14 : stack[i].src_inc_offset =
6505 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6506 : }
6507 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6508 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6509 :
6510 6 : size_t dimIdx = 0;
6511 6 : const size_t nDimsMinus1 = nDims - 1;
6512 :
6513 34 : lbl_next_depth:
6514 34 : if (dimIdx == nDimsMinus1)
6515 : {
6516 23 : auto nIters = count[dimIdx];
6517 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6518 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6519 : while (true)
6520 : {
6521 : double adfVal[2];
6522 : const double *padfSrcVal;
6523 86 : if (bIsBufferDataTypeNativeDataType)
6524 : {
6525 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6526 : }
6527 : else
6528 : {
6529 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6530 : &adfVal[0], dtDouble);
6531 36 : padfSrcVal = adfVal;
6532 : }
6533 :
6534 148 : if (bSelfAndParentHaveNoData &&
6535 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6536 : {
6537 3 : dst_ptr[0] = adfSrcNoData[0];
6538 3 : if (bDTIsComplex)
6539 : {
6540 1 : dst_ptr[1] = adfSrcNoData[1];
6541 : }
6542 : }
6543 : else
6544 : {
6545 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6546 83 : if (bDTIsComplex)
6547 : {
6548 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6549 : }
6550 : }
6551 :
6552 86 : if ((--nIters) == 0)
6553 23 : break;
6554 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6555 63 : src_ptr += stack[dimIdx].src_inc_offset;
6556 63 : }
6557 : }
6558 : else
6559 : {
6560 11 : stack[dimIdx].nIters = count[dimIdx];
6561 : while (true)
6562 : {
6563 28 : dimIdx++;
6564 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6565 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6566 28 : goto lbl_next_depth;
6567 28 : lbl_return_to_caller:
6568 28 : dimIdx--;
6569 28 : if ((--stack[dimIdx].nIters) == 0)
6570 11 : break;
6571 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6572 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6573 : }
6574 : }
6575 34 : if (dimIdx > 0)
6576 28 : goto lbl_return_to_caller;
6577 :
6578 : // If the parent array is not double/complex-double, then convert the
6579 : // values to it, before calling Write(), as some implementations can be
6580 : // very slow when doing the type conversion.
6581 6 : const auto &eParentDT = m_poParent->GetDataType();
6582 6 : const size_t nParentDTSize = eParentDT.GetSize();
6583 6 : if (nParentDTSize <= nDTSize / 2)
6584 : {
6585 : // Copy in-place by making sure that source and target do not overlap
6586 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6587 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6588 :
6589 : // Copy first element
6590 : {
6591 6 : std::vector<GByte> abyTemp(nParentDTSize);
6592 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6593 6 : static_cast<int>(nDTSize), &abyTemp[0],
6594 : eParentNumericDT, static_cast<int>(nParentDTSize),
6595 : 1);
6596 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6597 : }
6598 : // Remaining elements
6599 86 : for (size_t i = 1; i < nElts; ++i)
6600 : {
6601 80 : GDALCopyWords64(
6602 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6603 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6604 : eParentNumericDT, 0, 1);
6605 : }
6606 : }
6607 :
6608 : const bool ret =
6609 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6610 : eParentDT, pTempBuffer);
6611 :
6612 6 : VSIFree(pTempBuffer);
6613 6 : return ret;
6614 : }
6615 :
6616 : /************************************************************************/
6617 : /* GetUnscaled() */
6618 : /************************************************************************/
6619 :
6620 : /** Return an array that is the unscaled version of the current one.
6621 : *
6622 : * That is each value of the unscaled array will be
6623 : * unscaled_value = raw_value * GetScale() + GetOffset()
6624 : *
6625 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6626 : * from unscaled values to raw values.
6627 : *
6628 : * This is the same as the C function GDALMDArrayGetUnscaled().
6629 : *
6630 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6631 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6632 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6633 : * @return a new array, that holds a reference to the original one, and thus is
6634 : * a view of it (not a copy), or nullptr in case of error.
6635 : */
6636 : std::shared_ptr<GDALMDArray>
6637 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6638 : double dfOverriddenDstNodata) const
6639 : {
6640 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6641 17 : if (!self)
6642 : {
6643 0 : CPLError(CE_Failure, CPLE_AppDefined,
6644 : "Driver implementation issue: m_pSelf not set !");
6645 0 : return nullptr;
6646 : }
6647 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6648 : {
6649 0 : CPLError(CE_Failure, CPLE_AppDefined,
6650 : "GetUnscaled() only supports numeric data type");
6651 0 : return nullptr;
6652 : }
6653 : const double dfScale =
6654 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6655 : const double dfOffset =
6656 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6657 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6658 4 : return self;
6659 :
6660 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6661 13 : ? GDT_CFloat64
6662 13 : : GDT_Float64;
6663 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6664 : {
6665 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6666 0 : eDT = GDT_Float16;
6667 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6668 1 : eDT = GDT_Float32;
6669 : }
6670 :
6671 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6672 13 : dfOverriddenDstNodata, eDT);
6673 : }
6674 :
6675 : /************************************************************************/
6676 : /* GDALMDArrayMask */
6677 : /************************************************************************/
6678 :
6679 : class GDALMDArrayMask final : public GDALPamMDArray
6680 : {
6681 : private:
6682 : std::shared_ptr<GDALMDArray> m_poParent{};
6683 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6684 : double m_dfMissingValue = 0.0;
6685 : bool m_bHasMissingValue = false;
6686 : double m_dfFillValue = 0.0;
6687 : bool m_bHasFillValue = false;
6688 : double m_dfValidMin = 0.0;
6689 : bool m_bHasValidMin = false;
6690 : double m_dfValidMax = 0.0;
6691 : bool m_bHasValidMax = false;
6692 : std::vector<uint32_t> m_anValidFlagMasks{};
6693 : std::vector<uint32_t> m_anValidFlagValues{};
6694 :
6695 : bool Init(CSLConstList papszOptions);
6696 :
6697 : template <typename Type>
6698 : void
6699 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6700 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6701 : const void *pTempBuffer,
6702 : const GDALExtendedDataType &oTmpBufferDT,
6703 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6704 :
6705 : protected:
6706 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6707 96 : : GDALAbstractMDArray(std::string(),
6708 96 : "Mask of " + poParent->GetFullName()),
6709 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6710 96 : GDALPamMultiDim::GetPAM(poParent),
6711 : poParent->GetContext()),
6712 240 : m_poParent(std::move(poParent))
6713 : {
6714 48 : }
6715 :
6716 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6717 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6718 : const GDALExtendedDataType &bufferDataType,
6719 : void *pDstBuffer) const override;
6720 :
6721 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6722 : CSLConstList papszOptions) const override
6723 : {
6724 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6725 : }
6726 :
6727 : public:
6728 : static std::shared_ptr<GDALMDArrayMask>
6729 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6730 : CSLConstList papszOptions);
6731 :
6732 1 : bool IsWritable() const override
6733 : {
6734 1 : return false;
6735 : }
6736 :
6737 54 : const std::string &GetFilename() const override
6738 : {
6739 54 : return m_poParent->GetFilename();
6740 : }
6741 :
6742 : const std::vector<std::shared_ptr<GDALDimension>> &
6743 382 : GetDimensions() const override
6744 : {
6745 382 : return m_poParent->GetDimensions();
6746 : }
6747 :
6748 138 : const GDALExtendedDataType &GetDataType() const override
6749 : {
6750 138 : return m_dt;
6751 : }
6752 :
6753 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6754 : {
6755 1 : return m_poParent->GetSpatialRef();
6756 : }
6757 :
6758 2 : std::vector<GUInt64> GetBlockSize() const override
6759 : {
6760 2 : return m_poParent->GetBlockSize();
6761 : }
6762 : };
6763 :
6764 : /************************************************************************/
6765 : /* GDALMDArrayMask::Create() */
6766 : /************************************************************************/
6767 :
6768 : /* static */ std::shared_ptr<GDALMDArrayMask>
6769 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6770 : CSLConstList papszOptions)
6771 : {
6772 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6773 48 : newAr->SetSelf(newAr);
6774 48 : if (!newAr->Init(papszOptions))
6775 6 : return nullptr;
6776 42 : return newAr;
6777 : }
6778 :
6779 : /************************************************************************/
6780 : /* GDALMDArrayMask::Init() */
6781 : /************************************************************************/
6782 :
6783 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6784 : {
6785 : const auto GetSingleValNumericAttr =
6786 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6787 : {
6788 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6789 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6790 : {
6791 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6792 21 : if (anDimSizes.empty() ||
6793 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6794 : {
6795 11 : bHasVal = true;
6796 11 : dfVal = poAttr->ReadAsDouble();
6797 : }
6798 : }
6799 192 : };
6800 :
6801 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6802 48 : m_dfMissingValue);
6803 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6804 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6805 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6806 :
6807 : {
6808 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6809 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6810 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6811 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6812 : {
6813 6 : m_bHasValidMin = true;
6814 6 : m_bHasValidMax = true;
6815 6 : auto vals = poValidRange->ReadAsDoubleArray();
6816 6 : CPLAssert(vals.size() == 2);
6817 6 : m_dfValidMin = vals[0];
6818 6 : m_dfValidMax = vals[1];
6819 : }
6820 : }
6821 :
6822 : // Take into account
6823 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6824 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6825 : const char *pszUnmaskFlags =
6826 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6827 48 : if (pszUnmaskFlags)
6828 : {
6829 : const auto IsScalarStringAttr =
6830 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6831 : {
6832 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6833 26 : (poAttr->GetDimensionsSize().empty() ||
6834 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6835 26 : poAttr->GetDimensionsSize()[0] == 1));
6836 : };
6837 :
6838 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6839 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6840 : {
6841 1 : CPLError(CE_Failure, CPLE_AppDefined,
6842 : "UNMASK_FLAGS option specified but array has no "
6843 : "flag_meanings attribute");
6844 1 : return false;
6845 : }
6846 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6847 13 : if (!pszFlagMeanings)
6848 : {
6849 1 : CPLError(CE_Failure, CPLE_AppDefined,
6850 : "Cannot read flag_meanings attribute");
6851 1 : return false;
6852 : }
6853 :
6854 : const auto IsSingleDimNumericAttr =
6855 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6856 : {
6857 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6858 26 : poAttr->GetDimensionsSize().size() == 1;
6859 : };
6860 :
6861 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6862 : const bool bHasFlagValues =
6863 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6864 :
6865 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6866 : const bool bHasFlagMasks =
6867 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6868 :
6869 12 : if (!bHasFlagValues && !bHasFlagMasks)
6870 : {
6871 1 : CPLError(CE_Failure, CPLE_AppDefined,
6872 : "Cannot find flag_values and/or flag_masks attribute");
6873 1 : return false;
6874 : }
6875 :
6876 : const CPLStringList aosUnmaskFlags(
6877 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6878 : const CPLStringList aosFlagMeanings(
6879 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6880 :
6881 11 : if (bHasFlagValues)
6882 : {
6883 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6884 : // We could support Int64 or UInt64, but more work...
6885 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6886 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6887 : {
6888 0 : CPLError(CE_Failure, CPLE_NotSupported,
6889 : "Unsupported data type for flag_values attribute: %s",
6890 : GDALGetDataTypeName(eType));
6891 0 : return false;
6892 : }
6893 : }
6894 :
6895 11 : if (bHasFlagMasks)
6896 : {
6897 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6898 : // We could support Int64 or UInt64, but more work...
6899 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6900 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6901 : {
6902 0 : CPLError(CE_Failure, CPLE_NotSupported,
6903 : "Unsupported data type for flag_masks attribute: %s",
6904 : GDALGetDataTypeName(eType));
6905 0 : return false;
6906 : }
6907 : }
6908 :
6909 : const std::vector<double> adfValues(
6910 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6911 11 : : std::vector<double>());
6912 : const std::vector<double> adfMasks(
6913 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6914 11 : : std::vector<double>());
6915 :
6916 18 : if (bHasFlagValues &&
6917 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6918 : {
6919 1 : CPLError(CE_Failure, CPLE_AppDefined,
6920 : "Number of values in flag_values attribute is different "
6921 : "from the one in flag_meanings");
6922 1 : return false;
6923 : }
6924 :
6925 16 : if (bHasFlagMasks &&
6926 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6927 : {
6928 1 : CPLError(CE_Failure, CPLE_AppDefined,
6929 : "Number of values in flag_masks attribute is different "
6930 : "from the one in flag_meanings");
6931 1 : return false;
6932 : }
6933 :
6934 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6935 : {
6936 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6937 11 : if (nIdxFlag < 0)
6938 : {
6939 1 : CPLError(
6940 : CE_Failure, CPLE_AppDefined,
6941 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6942 : aosUnmaskFlags[i], pszFlagMeanings);
6943 1 : return false;
6944 : }
6945 :
6946 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6947 : {
6948 0 : CPLError(CE_Failure, CPLE_AppDefined,
6949 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6950 0 : adfValues[nIdxFlag]);
6951 0 : return false;
6952 : }
6953 :
6954 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6955 : {
6956 0 : CPLError(CE_Failure, CPLE_AppDefined,
6957 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6958 0 : adfMasks[nIdxFlag]);
6959 0 : return false;
6960 : }
6961 :
6962 10 : if (bHasFlagValues)
6963 : {
6964 12 : m_anValidFlagValues.push_back(
6965 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6966 : }
6967 :
6968 10 : if (bHasFlagMasks)
6969 : {
6970 12 : m_anValidFlagMasks.push_back(
6971 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6972 : }
6973 : }
6974 : }
6975 :
6976 42 : return true;
6977 : }
6978 :
6979 : /************************************************************************/
6980 : /* IRead() */
6981 : /************************************************************************/
6982 :
6983 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6984 : const GInt64 *arrayStep,
6985 : const GPtrDiff_t *bufferStride,
6986 : const GDALExtendedDataType &bufferDataType,
6987 : void *pDstBuffer) const
6988 : {
6989 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
6990 : {
6991 0 : CPLError(CE_Failure, CPLE_AppDefined,
6992 : "%s: only reading to a numeric data type is supported",
6993 : __func__);
6994 0 : return false;
6995 : }
6996 51 : size_t nElts = 1;
6997 51 : const size_t nDims = GetDimensionCount();
6998 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6999 139 : for (size_t i = 0; i < nDims; i++)
7000 88 : nElts *= count[i];
7001 51 : if (nDims > 0)
7002 : {
7003 46 : tmpBufferStrideVector.back() = 1;
7004 88 : for (size_t i = nDims - 1; i > 0;)
7005 : {
7006 42 : --i;
7007 42 : tmpBufferStrideVector[i] =
7008 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7009 : }
7010 : }
7011 :
7012 : /* Optimized case: if we are an integer data type and that there is no */
7013 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7014 : /* directly */
7015 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7016 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7017 34 : m_anValidFlagMasks.empty() &&
7018 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7019 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7020 : {
7021 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7022 7 : if (bBufferDataTypeIsByte) // Byte case
7023 : {
7024 4 : bool bContiguous = true;
7025 10 : for (size_t i = 0; i < nDims; i++)
7026 : {
7027 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7028 : {
7029 1 : bContiguous = false;
7030 1 : break;
7031 : }
7032 : }
7033 4 : if (bContiguous)
7034 : {
7035 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7036 3 : memset(pDstBuffer, 1, nElts);
7037 3 : return true;
7038 : }
7039 : }
7040 :
7041 : struct Stack
7042 : {
7043 : size_t nIters = 0;
7044 : GByte *dst_ptr = nullptr;
7045 : GPtrDiff_t dst_inc_offset = 0;
7046 : };
7047 :
7048 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7049 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7050 13 : for (size_t i = 0; i < nDims; i++)
7051 : {
7052 9 : stack[i].dst_inc_offset =
7053 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7054 : }
7055 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7056 :
7057 4 : size_t dimIdx = 0;
7058 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7059 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7060 4 : CPLAssert(nBufferDTSize <= 16);
7061 4 : const GByte flag = 1;
7062 4 : GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7063 : bufferDataType.GetNumericDataType(), 0, 1);
7064 :
7065 28 : lbl_next_depth:
7066 28 : if (dimIdx == nDimsMinus1)
7067 : {
7068 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7069 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7070 :
7071 : while (true)
7072 : {
7073 : // cppcheck-suppress knownConditionTrueFalse
7074 73 : if (bBufferDataTypeIsByte)
7075 : {
7076 24 : *dst_ptr = flag;
7077 : }
7078 : else
7079 : {
7080 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7081 : }
7082 :
7083 73 : if ((--nIters) == 0)
7084 19 : break;
7085 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7086 : }
7087 : }
7088 : else
7089 : {
7090 9 : stack[dimIdx].nIters = count[dimIdx];
7091 : while (true)
7092 : {
7093 24 : dimIdx++;
7094 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7095 24 : goto lbl_next_depth;
7096 24 : lbl_return_to_caller:
7097 24 : dimIdx--;
7098 24 : if ((--stack[dimIdx].nIters) == 0)
7099 9 : break;
7100 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7101 : }
7102 : }
7103 28 : if (dimIdx > 0)
7104 24 : goto lbl_return_to_caller;
7105 :
7106 4 : return true;
7107 : }
7108 :
7109 : const auto oTmpBufferDT =
7110 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7111 : ? GDALExtendedDataType::Create(GDT_Float64)
7112 88 : : m_poParent->GetDataType();
7113 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7114 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7115 44 : if (!pTempBuffer)
7116 0 : return false;
7117 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7118 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7119 : pTempBuffer))
7120 : {
7121 0 : VSIFree(pTempBuffer);
7122 0 : return false;
7123 : }
7124 :
7125 44 : switch (oTmpBufferDT.GetNumericDataType())
7126 : {
7127 7 : case GDT_Byte:
7128 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7129 : pTempBuffer, oTmpBufferDT,
7130 : tmpBufferStrideVector);
7131 7 : break;
7132 :
7133 0 : case GDT_Int8:
7134 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7135 : pTempBuffer, oTmpBufferDT,
7136 : tmpBufferStrideVector);
7137 0 : break;
7138 :
7139 1 : case GDT_UInt16:
7140 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7141 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7142 : tmpBufferStrideVector);
7143 1 : break;
7144 :
7145 14 : case GDT_Int16:
7146 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7147 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7148 : tmpBufferStrideVector);
7149 14 : break;
7150 :
7151 1 : case GDT_UInt32:
7152 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7153 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7154 : tmpBufferStrideVector);
7155 1 : break;
7156 :
7157 5 : case GDT_Int32:
7158 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7159 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7160 : tmpBufferStrideVector);
7161 5 : break;
7162 :
7163 0 : case GDT_UInt64:
7164 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7165 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7166 : tmpBufferStrideVector);
7167 0 : break;
7168 :
7169 0 : case GDT_Int64:
7170 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7171 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7172 : tmpBufferStrideVector);
7173 0 : break;
7174 :
7175 0 : case GDT_Float16:
7176 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7177 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7178 : tmpBufferStrideVector);
7179 0 : break;
7180 :
7181 7 : case GDT_Float32:
7182 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7183 : pTempBuffer, oTmpBufferDT,
7184 : tmpBufferStrideVector);
7185 7 : break;
7186 :
7187 9 : case GDT_Float64:
7188 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7189 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7190 : tmpBufferStrideVector);
7191 9 : break;
7192 0 : case GDT_Unknown:
7193 : case GDT_CInt16:
7194 : case GDT_CInt32:
7195 : case GDT_CFloat16:
7196 : case GDT_CFloat32:
7197 : case GDT_CFloat64:
7198 : case GDT_TypeCount:
7199 0 : CPLAssert(false);
7200 : break;
7201 : }
7202 :
7203 44 : VSIFree(pTempBuffer);
7204 :
7205 44 : return true;
7206 : }
7207 :
7208 : /************************************************************************/
7209 : /* IsValidForDT() */
7210 : /************************************************************************/
7211 :
7212 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7213 : {
7214 40 : if (std::isnan(dfVal))
7215 0 : return false;
7216 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7217 0 : return false;
7218 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7219 0 : return false;
7220 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7221 : }
7222 :
7223 9 : template <> bool IsValidForDT<double>(double)
7224 : {
7225 9 : return true;
7226 : }
7227 :
7228 : /************************************************************************/
7229 : /* IsNan() */
7230 : /************************************************************************/
7231 :
7232 1438 : template <typename Type> inline bool IsNan(Type)
7233 : {
7234 1438 : return false;
7235 : }
7236 :
7237 65 : template <> bool IsNan<double>(double val)
7238 : {
7239 65 : return std::isnan(val);
7240 : }
7241 :
7242 26 : template <> bool IsNan<float>(float val)
7243 : {
7244 26 : return std::isnan(val);
7245 : }
7246 :
7247 : /************************************************************************/
7248 : /* ReadInternal() */
7249 : /************************************************************************/
7250 :
7251 : template <typename Type>
7252 44 : void GDALMDArrayMask::ReadInternal(
7253 : const size_t *count, const GPtrDiff_t *bufferStride,
7254 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7255 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7256 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7257 : {
7258 44 : const size_t nDims = GetDimensionCount();
7259 :
7260 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7261 : {
7262 220 : if (bHasVal)
7263 : {
7264 49 : if (IsValidForDT<Type>(dfVal))
7265 : {
7266 49 : return static_cast<Type>(dfVal);
7267 : }
7268 : else
7269 : {
7270 0 : bHasVal = false;
7271 : }
7272 : }
7273 171 : return 0;
7274 : };
7275 :
7276 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7277 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7278 : const Type nNoDataValue =
7279 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7280 44 : bool bHasMissingValue = m_bHasMissingValue;
7281 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7282 44 : bool bHasFillValue = m_bHasFillValue;
7283 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7284 44 : bool bHasValidMin = m_bHasValidMin;
7285 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7286 44 : bool bHasValidMax = m_bHasValidMax;
7287 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7288 44 : const bool bHasValidFlags =
7289 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7290 :
7291 351 : const auto IsValidFlag = [this](Type v)
7292 : {
7293 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7294 : {
7295 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7296 : {
7297 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7298 : m_anValidFlagValues[i])
7299 : {
7300 4 : return true;
7301 : }
7302 : }
7303 : }
7304 42 : else if (!m_anValidFlagValues.empty())
7305 : {
7306 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7307 : {
7308 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7309 : {
7310 4 : return true;
7311 : }
7312 : }
7313 : }
7314 : else /* if( !m_anValidFlagMasks.empty() ) */
7315 : {
7316 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7317 : {
7318 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7319 : {
7320 9 : return true;
7321 : }
7322 : }
7323 : }
7324 37 : return false;
7325 : };
7326 :
7327 : #define GET_MASK_FOR_SAMPLE(v) \
7328 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7329 : !(bHasMissingValue && v == nMissingValue) && \
7330 : !(bHasFillValue && v == nFillValue) && \
7331 : !(bHasValidMin && v < nValidMin) && \
7332 : !(bHasValidMax && v > nValidMax) && \
7333 : (!bHasValidFlags || IsValidFlag(v)));
7334 :
7335 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7336 : /* Optimized case: Byte output and output buffer is contiguous */
7337 44 : if (bBufferDataTypeIsByte)
7338 : {
7339 40 : bool bContiguous = true;
7340 103 : for (size_t i = 0; i < nDims; i++)
7341 : {
7342 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7343 : {
7344 1 : bContiguous = false;
7345 1 : break;
7346 : }
7347 : }
7348 40 : if (bContiguous)
7349 : {
7350 39 : size_t nElts = 1;
7351 102 : for (size_t i = 0; i < nDims; i++)
7352 63 : nElts *= count[i];
7353 :
7354 1113 : for (size_t i = 0; i < nElts; i++)
7355 : {
7356 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7357 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7358 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7359 : }
7360 39 : return;
7361 : }
7362 : }
7363 :
7364 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7365 :
7366 : struct Stack
7367 : {
7368 : size_t nIters = 0;
7369 : const GByte *src_ptr = nullptr;
7370 : GByte *dst_ptr = nullptr;
7371 : GPtrDiff_t src_inc_offset = 0;
7372 : GPtrDiff_t dst_inc_offset = 0;
7373 : };
7374 :
7375 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7376 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7377 15 : for (size_t i = 0; i < nDims; i++)
7378 : {
7379 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7380 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7381 10 : stack[i].dst_inc_offset =
7382 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7383 : }
7384 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7385 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7386 :
7387 5 : size_t dimIdx = 0;
7388 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7389 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7390 5 : CPLAssert(nBufferDTSize <= 16);
7391 15 : for (GByte flag = 0; flag <= 1; flag++)
7392 : {
7393 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7394 : bufferDataType.GetNumericDataType(), 0, 1);
7395 : }
7396 :
7397 43 : lbl_next_depth:
7398 43 : if (dimIdx == nDimsMinus1)
7399 : {
7400 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7401 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7402 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7403 :
7404 420 : while (true)
7405 : {
7406 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7407 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7408 :
7409 455 : if (bBufferDataTypeIsByte)
7410 : {
7411 24 : *dst_ptr = flag;
7412 : }
7413 : else
7414 : {
7415 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7416 : }
7417 :
7418 455 : if ((--nIters) == 0)
7419 35 : break;
7420 420 : src_ptr += stack[dimIdx].src_inc_offset;
7421 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7422 : }
7423 : }
7424 : else
7425 : {
7426 8 : stack[dimIdx].nIters = count[dimIdx];
7427 : while (true)
7428 : {
7429 38 : dimIdx++;
7430 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7431 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7432 38 : goto lbl_next_depth;
7433 38 : lbl_return_to_caller:
7434 38 : dimIdx--;
7435 38 : if ((--stack[dimIdx].nIters) == 0)
7436 8 : break;
7437 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7438 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7439 : }
7440 : }
7441 43 : if (dimIdx > 0)
7442 38 : goto lbl_return_to_caller;
7443 : }
7444 :
7445 : /************************************************************************/
7446 : /* GetMask() */
7447 : /************************************************************************/
7448 :
7449 : /** Return an array that is a mask for the current array
7450 :
7451 : This array will be of type Byte, with values set to 0 to indicate invalid
7452 : pixels of the current array, and values set to 1 to indicate valid pixels.
7453 :
7454 : The generic implementation honours the NoDataValue, as well as various
7455 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7456 : and valid_range.
7457 :
7458 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7459 : can be used to specify strings of the "flag_meanings" attribute
7460 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7461 : for which pixels matching any of those flags will be set at 1 in the mask array,
7462 : and pixels matching none of those flags will be set at 0.
7463 : For example, let's consider the following netCDF variable defined with:
7464 : \verbatim
7465 : l2p_flags:valid_min = 0s ;
7466 : l2p_flags:valid_max = 256s ;
7467 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7468 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7469 : \endverbatim
7470 :
7471 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7472 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7473 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7474 : will be 1.
7475 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7476 : will be 0.
7477 :
7478 : This is the same as the C function GDALMDArrayGetMask().
7479 :
7480 : @param papszOptions NULL-terminated list of options, or NULL.
7481 :
7482 : @return a new array, that holds a reference to the original one, and thus is
7483 : a view of it (not a copy), or nullptr in case of error.
7484 : */
7485 : std::shared_ptr<GDALMDArray>
7486 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7487 : {
7488 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7489 49 : if (!self)
7490 : {
7491 0 : CPLError(CE_Failure, CPLE_AppDefined,
7492 : "Driver implementation issue: m_pSelf not set !");
7493 0 : return nullptr;
7494 : }
7495 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7496 : {
7497 1 : CPLError(CE_Failure, CPLE_AppDefined,
7498 : "GetMask() only supports numeric data type");
7499 1 : return nullptr;
7500 : }
7501 48 : return GDALMDArrayMask::Create(self, papszOptions);
7502 : }
7503 :
7504 : /************************************************************************/
7505 : /* IsRegularlySpaced() */
7506 : /************************************************************************/
7507 :
7508 : /** Returns whether an array is a 1D regularly spaced array.
7509 : *
7510 : * @param[out] dfStart First value in the array
7511 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7512 : * @return true if the array is regularly spaced.
7513 : */
7514 193 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7515 : {
7516 193 : dfStart = 0;
7517 193 : dfIncrement = 0;
7518 193 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7519 0 : return false;
7520 193 : const auto nSize = GetDimensions()[0]->GetSize();
7521 193 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7522 2 : return false;
7523 :
7524 191 : size_t nCount = static_cast<size_t>(nSize);
7525 382 : std::vector<double> adfTmp;
7526 : try
7527 : {
7528 191 : adfTmp.resize(nCount);
7529 : }
7530 0 : catch (const std::exception &)
7531 : {
7532 0 : return false;
7533 : }
7534 :
7535 191 : GUInt64 anStart[1] = {0};
7536 191 : size_t anCount[1] = {nCount};
7537 :
7538 : const auto IsRegularlySpacedInternal =
7539 84274 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7540 : {
7541 265 : dfStart = adfTmp[0];
7542 265 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7543 265 : if (dfIncrement == 0)
7544 : {
7545 3 : return false;
7546 : }
7547 21000 : for (size_t i = 1; i < anCount[0]; i++)
7548 : {
7549 20738 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7550 20738 : 1e-3 * fabs(dfIncrement))
7551 : {
7552 0 : return false;
7553 : }
7554 : }
7555 262 : return true;
7556 191 : };
7557 :
7558 : // First try with the first block(s). This can avoid excessive processing
7559 : // time, for example with Zarr datasets.
7560 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7561 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7562 191 : const auto nBlockSize = GetBlockSize()[0];
7563 191 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7564 : {
7565 : size_t nReducedCount =
7566 77 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7567 440 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7568 363 : nReducedCount *= 2;
7569 77 : anCount[0] = nReducedCount;
7570 77 : if (!Read(anStart, anCount, nullptr, nullptr,
7571 154 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7572 : {
7573 0 : return false;
7574 : }
7575 77 : if (!IsRegularlySpacedInternal())
7576 : {
7577 3 : return false;
7578 : }
7579 :
7580 : // Get next values
7581 74 : anStart[0] = nReducedCount;
7582 74 : anCount[0] = nCount - nReducedCount;
7583 : }
7584 :
7585 188 : if (!Read(anStart, anCount, nullptr, nullptr,
7586 376 : GDALExtendedDataType::Create(GDT_Float64),
7587 188 : &adfTmp[static_cast<size_t>(anStart[0])]))
7588 : {
7589 0 : return false;
7590 : }
7591 :
7592 188 : return IsRegularlySpacedInternal();
7593 : }
7594 :
7595 : /************************************************************************/
7596 : /* GuessGeoTransform() */
7597 : /************************************************************************/
7598 :
7599 : /** Returns whether 2 specified dimensions form a geotransform
7600 : *
7601 : * @param nDimX Index of the X axis.
7602 : * @param nDimY Index of the Y axis.
7603 : * @param bPixelIsPoint Whether the geotransform should be returned
7604 : * with the pixel-is-point (pixel-center) convention
7605 : * (bPixelIsPoint = true), or with the pixel-is-area
7606 : * (top left corner convention)
7607 : * (bPixelIsPoint = false)
7608 : * @param[out] gt Computed geotransform
7609 : * @return true if a geotransform could be computed.
7610 : */
7611 232 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7612 : bool bPixelIsPoint,
7613 : GDALGeoTransform >) const
7614 : {
7615 232 : const auto &dims(GetDimensions());
7616 464 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7617 464 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7618 232 : double dfXStart = 0.0;
7619 232 : double dfXSpacing = 0.0;
7620 232 : double dfYStart = 0.0;
7621 232 : double dfYSpacing = 0.0;
7622 524 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7623 292 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7624 340 : poVarY && poVarY->GetDimensionCount() == 1 &&
7625 97 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7626 470 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7627 92 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7628 : {
7629 92 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7630 92 : gt[1] = dfXSpacing;
7631 92 : gt[2] = 0;
7632 92 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7633 92 : gt[4] = 0;
7634 92 : gt[5] = dfYSpacing;
7635 92 : return true;
7636 : }
7637 140 : return false;
7638 : }
7639 :
7640 : /** Returns whether 2 specified dimensions form a geotransform
7641 : *
7642 : * @param nDimX Index of the X axis.
7643 : * @param nDimY Index of the Y axis.
7644 : * @param bPixelIsPoint Whether the geotransform should be returned
7645 : * with the pixel-is-point (pixel-center) convention
7646 : * (bPixelIsPoint = true), or with the pixel-is-area
7647 : * (top left corner convention)
7648 : * (bPixelIsPoint = false)
7649 : * @param[out] adfGeoTransform Computed geotransform
7650 : * @return true if a geotransform could be computed.
7651 : */
7652 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7653 : bool bPixelIsPoint,
7654 : double adfGeoTransform[6]) const
7655 : {
7656 0 : GDALGeoTransform *gt =
7657 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7658 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7659 : }
7660 :
7661 : /************************************************************************/
7662 : /* GDALMDArrayResampled */
7663 : /************************************************************************/
7664 :
7665 : class GDALMDArrayResampledDataset;
7666 :
7667 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7668 : {
7669 : protected:
7670 : CPLErr IReadBlock(int, int, void *) override;
7671 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7672 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7673 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7674 : GSpacing nLineSpaceBuf,
7675 : GDALRasterIOExtraArg *psExtraArg) override;
7676 :
7677 : public:
7678 : explicit GDALMDArrayResampledDatasetRasterBand(
7679 : GDALMDArrayResampledDataset *poDSIn);
7680 :
7681 : double GetNoDataValue(int *pbHasNoData) override;
7682 : };
7683 :
7684 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7685 : {
7686 : friend class GDALMDArrayResampled;
7687 : friend class GDALMDArrayResampledDatasetRasterBand;
7688 :
7689 : std::shared_ptr<GDALMDArray> m_poArray;
7690 : const size_t m_iXDim;
7691 : const size_t m_iYDim;
7692 : GDALGeoTransform m_gt{};
7693 : bool m_bHasGT = false;
7694 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7695 :
7696 : std::vector<GUInt64> m_anOffset{};
7697 : std::vector<size_t> m_anCount{};
7698 : std::vector<GPtrDiff_t> m_anStride{};
7699 :
7700 : std::string m_osFilenameLong{};
7701 : std::string m_osFilenameLat{};
7702 :
7703 : public:
7704 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7705 : size_t iXDim, size_t iYDim)
7706 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7707 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7708 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7709 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7710 : {
7711 24 : const auto &dims(m_poArray->GetDimensions());
7712 :
7713 24 : nRasterYSize = static_cast<int>(
7714 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7715 24 : nRasterXSize = static_cast<int>(
7716 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7717 :
7718 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7719 :
7720 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7721 24 : }
7722 :
7723 : ~GDALMDArrayResampledDataset() override;
7724 :
7725 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7726 : {
7727 43 : gt = m_gt;
7728 43 : return m_bHasGT ? CE_None : CE_Failure;
7729 : }
7730 :
7731 105 : const OGRSpatialReference *GetSpatialRef() const override
7732 : {
7733 105 : m_poSRS = m_poArray->GetSpatialRef();
7734 105 : if (m_poSRS)
7735 : {
7736 79 : m_poSRS.reset(m_poSRS->Clone());
7737 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7738 237 : for (auto &m : axisMapping)
7739 : {
7740 158 : if (m == static_cast<int>(m_iXDim) + 1)
7741 79 : m = 1;
7742 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7743 79 : m = 2;
7744 : }
7745 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7746 : }
7747 105 : return m_poSRS.get();
7748 : }
7749 :
7750 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7751 : const std::string &osFilenameLat)
7752 : {
7753 5 : m_osFilenameLong = osFilenameLong;
7754 5 : m_osFilenameLat = osFilenameLat;
7755 10 : CPLStringList aosGeoLoc;
7756 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7757 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7758 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7759 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7760 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7761 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7762 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7763 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7764 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7765 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7766 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7767 5 : }
7768 : };
7769 :
7770 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7771 : {
7772 24 : if (!m_osFilenameLong.empty())
7773 5 : VSIUnlink(m_osFilenameLong.c_str());
7774 24 : if (!m_osFilenameLat.empty())
7775 5 : VSIUnlink(m_osFilenameLat.c_str());
7776 48 : }
7777 :
7778 : /************************************************************************/
7779 : /* GDALMDArrayResampledDatasetRasterBand() */
7780 : /************************************************************************/
7781 :
7782 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7783 24 : GDALMDArrayResampledDataset *poDSIn)
7784 : {
7785 24 : const auto &poArray(poDSIn->m_poArray);
7786 24 : const auto blockSize(poArray->GetBlockSize());
7787 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7788 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7789 13 : blockSize[poDSIn->m_iYDim]))
7790 24 : : 1;
7791 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7792 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7793 13 : blockSize[poDSIn->m_iXDim]))
7794 24 : : poDSIn->GetRasterXSize();
7795 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7796 24 : eAccess = poDSIn->eAccess;
7797 24 : }
7798 :
7799 : /************************************************************************/
7800 : /* GetNoDataValue() */
7801 : /************************************************************************/
7802 :
7803 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7804 : {
7805 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7806 50 : const auto &poArray(l_poDS->m_poArray);
7807 50 : bool bHasNodata = false;
7808 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7809 50 : if (pbHasNoData)
7810 46 : *pbHasNoData = bHasNodata;
7811 50 : return dfRes;
7812 : }
7813 :
7814 : /************************************************************************/
7815 : /* IReadBlock() */
7816 : /************************************************************************/
7817 :
7818 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7819 : int nBlockYOff,
7820 : void *pImage)
7821 : {
7822 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7823 0 : const int nXOff = nBlockXOff * nBlockXSize;
7824 0 : const int nYOff = nBlockYOff * nBlockYSize;
7825 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7826 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7827 : GDALRasterIOExtraArg sExtraArg;
7828 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7829 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7830 : nReqXSize, nReqYSize, eDataType, nDTSize,
7831 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7832 : }
7833 :
7834 : /************************************************************************/
7835 : /* IRasterIO() */
7836 : /************************************************************************/
7837 :
7838 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7839 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7840 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7841 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7842 : GDALRasterIOExtraArg *psExtraArg)
7843 : {
7844 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7845 32 : const auto &poArray(l_poDS->m_poArray);
7846 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7847 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7848 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7849 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7850 : {
7851 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7852 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7853 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7854 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7855 :
7856 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7857 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7858 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7859 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7860 :
7861 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7862 32 : l_poDS->m_anCount.data(), nullptr,
7863 32 : l_poDS->m_anStride.data(),
7864 64 : GDALExtendedDataType::Create(eBufType), pData)
7865 32 : ? CE_None
7866 32 : : CE_Failure;
7867 : }
7868 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7869 : pData, nBufXSize, nBufYSize, eBufType,
7870 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7871 : }
7872 :
7873 : class GDALMDArrayResampled final : public GDALPamMDArray
7874 : {
7875 : private:
7876 : std::shared_ptr<GDALMDArray> m_poParent{};
7877 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7878 : std::vector<GUInt64> m_anBlockSize;
7879 : GDALExtendedDataType m_dt;
7880 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7881 : std::shared_ptr<GDALMDArray> m_poVarX{};
7882 : std::shared_ptr<GDALMDArray> m_poVarY{};
7883 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7884 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7885 :
7886 : protected:
7887 21 : GDALMDArrayResampled(
7888 : const std::shared_ptr<GDALMDArray> &poParent,
7889 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7890 : const std::vector<GUInt64> &anBlockSize)
7891 42 : : GDALAbstractMDArray(std::string(),
7892 42 : "Resampled view of " + poParent->GetFullName()),
7893 : GDALPamMDArray(
7894 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7895 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7896 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7897 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7898 : {
7899 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7900 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7901 21 : }
7902 :
7903 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7904 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7905 : const GDALExtendedDataType &bufferDataType,
7906 : void *pDstBuffer) const override;
7907 :
7908 : public:
7909 : static std::shared_ptr<GDALMDArray>
7910 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7911 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7912 : GDALRIOResampleAlg resampleAlg,
7913 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7914 :
7915 42 : ~GDALMDArrayResampled() override
7916 21 : {
7917 : // First close the warped VRT
7918 21 : m_poReprojectedDS.reset();
7919 21 : m_poParentDS.reset();
7920 42 : }
7921 :
7922 11 : bool IsWritable() const override
7923 : {
7924 11 : return false;
7925 : }
7926 :
7927 74 : const std::string &GetFilename() const override
7928 : {
7929 74 : return m_poParent->GetFilename();
7930 : }
7931 :
7932 : const std::vector<std::shared_ptr<GDALDimension>> &
7933 257 : GetDimensions() const override
7934 : {
7935 257 : return m_apoDims;
7936 : }
7937 :
7938 109 : const GDALExtendedDataType &GetDataType() const override
7939 : {
7940 109 : return m_dt;
7941 : }
7942 :
7943 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7944 : {
7945 21 : return m_poSRS;
7946 : }
7947 :
7948 12 : std::vector<GUInt64> GetBlockSize() const override
7949 : {
7950 12 : return m_anBlockSize;
7951 : }
7952 :
7953 : std::shared_ptr<GDALAttribute>
7954 1 : GetAttribute(const std::string &osName) const override
7955 : {
7956 1 : return m_poParent->GetAttribute(osName);
7957 : }
7958 :
7959 : std::vector<std::shared_ptr<GDALAttribute>>
7960 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7961 : {
7962 12 : return m_poParent->GetAttributes(papszOptions);
7963 : }
7964 :
7965 1 : const std::string &GetUnit() const override
7966 : {
7967 1 : return m_poParent->GetUnit();
7968 : }
7969 :
7970 1 : const void *GetRawNoDataValue() const override
7971 : {
7972 1 : return m_poParent->GetRawNoDataValue();
7973 : }
7974 :
7975 1 : double GetOffset(bool *pbHasOffset,
7976 : GDALDataType *peStorageType) const override
7977 : {
7978 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7979 : }
7980 :
7981 1 : double GetScale(bool *pbHasScale,
7982 : GDALDataType *peStorageType) const override
7983 : {
7984 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7985 : }
7986 : };
7987 :
7988 : /************************************************************************/
7989 : /* GDALMDArrayResampled::Create() */
7990 : /************************************************************************/
7991 :
7992 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7993 : const std::shared_ptr<GDALMDArray> &poParent,
7994 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7995 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7996 : CSLConstList /* papszOptions */)
7997 : {
7998 29 : const char *pszResampleAlg = "nearest";
7999 29 : bool unsupported = false;
8000 29 : switch (resampleAlg)
8001 : {
8002 16 : case GRIORA_NearestNeighbour:
8003 16 : pszResampleAlg = "nearest";
8004 16 : break;
8005 2 : case GRIORA_Bilinear:
8006 2 : pszResampleAlg = "bilinear";
8007 2 : break;
8008 5 : case GRIORA_Cubic:
8009 5 : pszResampleAlg = "cubic";
8010 5 : break;
8011 1 : case GRIORA_CubicSpline:
8012 1 : pszResampleAlg = "cubicspline";
8013 1 : break;
8014 1 : case GRIORA_Lanczos:
8015 1 : pszResampleAlg = "lanczos";
8016 1 : break;
8017 1 : case GRIORA_Average:
8018 1 : pszResampleAlg = "average";
8019 1 : break;
8020 1 : case GRIORA_Mode:
8021 1 : pszResampleAlg = "mode";
8022 1 : break;
8023 1 : case GRIORA_Gauss:
8024 1 : unsupported = true;
8025 1 : break;
8026 0 : case GRIORA_RESERVED_START:
8027 0 : unsupported = true;
8028 0 : break;
8029 0 : case GRIORA_RESERVED_END:
8030 0 : unsupported = true;
8031 0 : break;
8032 1 : case GRIORA_RMS:
8033 1 : pszResampleAlg = "rms";
8034 1 : break;
8035 : }
8036 29 : if (unsupported)
8037 : {
8038 1 : CPLError(CE_Failure, CPLE_NotSupported,
8039 : "Unsupported resample method for GetResampled()");
8040 1 : return nullptr;
8041 : }
8042 :
8043 28 : if (poParent->GetDimensionCount() < 2)
8044 : {
8045 1 : CPLError(CE_Failure, CPLE_NotSupported,
8046 : "GetResampled() only supports 2 dimensions or more");
8047 1 : return nullptr;
8048 : }
8049 :
8050 27 : const auto &aoParentDims = poParent->GetDimensions();
8051 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8052 : {
8053 2 : CPLError(CE_Failure, CPLE_AppDefined,
8054 : "GetResampled(): apoNewDims size should be the same as "
8055 : "GetDimensionCount()");
8056 2 : return nullptr;
8057 : }
8058 :
8059 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8060 25 : apoNewDims.reserve(apoNewDimsIn.size());
8061 :
8062 50 : std::vector<GUInt64> anBlockSize;
8063 25 : anBlockSize.reserve(apoNewDimsIn.size());
8064 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8065 :
8066 50 : auto apoParentDims = poParent->GetDimensions();
8067 : // Special case for NASA EMIT datasets
8068 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8069 7 : apoParentDims[0]->GetName() == "downtrack" &&
8070 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8071 2 : apoParentDims[2]->GetName() == "bands");
8072 :
8073 : const size_t iYDimParent =
8074 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8075 : const size_t iXDimParent =
8076 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8077 :
8078 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8079 : {
8080 53 : if (i == iYDimParent || i == iXDimParent)
8081 48 : continue;
8082 5 : if (apoNewDimsIn[i] == nullptr)
8083 : {
8084 3 : apoNewDims.emplace_back(aoParentDims[i]);
8085 : }
8086 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8087 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8088 : {
8089 1 : CPLError(CE_Failure, CPLE_AppDefined,
8090 : "GetResampled(): apoNewDims[%u] should be the same "
8091 : "as its parent",
8092 : i);
8093 1 : return nullptr;
8094 : }
8095 : else
8096 : {
8097 1 : apoNewDims.emplace_back(aoParentDims[i]);
8098 : }
8099 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8100 : }
8101 :
8102 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8103 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8104 :
8105 24 : double dfXStart = 0.0;
8106 24 : double dfXSpacing = 0.0;
8107 24 : bool gotXSpacing = false;
8108 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8109 24 : if (poNewDimX)
8110 : {
8111 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8112 : {
8113 0 : CPLError(CE_Failure, CPLE_NotSupported,
8114 : "Too big size for X dimension");
8115 0 : return nullptr;
8116 : }
8117 4 : auto var = poNewDimX->GetIndexingVariable();
8118 4 : if (var)
8119 : {
8120 2 : if (var->GetDimensionCount() != 1 ||
8121 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8122 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8123 : {
8124 0 : CPLError(CE_Failure, CPLE_NotSupported,
8125 : "New X dimension should be indexed by a regularly "
8126 : "spaced variable");
8127 0 : return nullptr;
8128 : }
8129 1 : gotXSpacing = true;
8130 : }
8131 : }
8132 :
8133 24 : double dfYStart = 0.0;
8134 24 : double dfYSpacing = 0.0;
8135 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8136 24 : bool gotYSpacing = false;
8137 24 : if (poNewDimY)
8138 : {
8139 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8140 : {
8141 0 : CPLError(CE_Failure, CPLE_NotSupported,
8142 : "Too big size for Y dimension");
8143 0 : return nullptr;
8144 : }
8145 4 : auto var = poNewDimY->GetIndexingVariable();
8146 4 : if (var)
8147 : {
8148 2 : if (var->GetDimensionCount() != 1 ||
8149 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8150 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8151 : {
8152 0 : CPLError(CE_Failure, CPLE_NotSupported,
8153 : "New Y dimension should be indexed by a regularly "
8154 : "spaced variable");
8155 0 : return nullptr;
8156 : }
8157 1 : gotYSpacing = true;
8158 : }
8159 : }
8160 :
8161 : // This limitation could probably be removed
8162 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8163 : {
8164 0 : CPLError(CE_Failure, CPLE_NotSupported,
8165 : "Either none of new X or Y dimension should have an indexing "
8166 : "variable, or both should both should have one.");
8167 0 : return nullptr;
8168 : }
8169 :
8170 48 : std::string osDstWKT;
8171 24 : if (poTargetSRS)
8172 : {
8173 2 : char *pszDstWKT = nullptr;
8174 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8175 : {
8176 0 : CPLFree(pszDstWKT);
8177 0 : return nullptr;
8178 : }
8179 2 : osDstWKT = pszDstWKT;
8180 2 : CPLFree(pszDstWKT);
8181 : }
8182 :
8183 : // Use coordinate variables for geolocation array
8184 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8185 24 : bool useGeolocationArray = false;
8186 24 : if (apoCoordinateVars.size() >= 2)
8187 : {
8188 0 : std::shared_ptr<GDALMDArray> poLongVar;
8189 0 : std::shared_ptr<GDALMDArray> poLatVar;
8190 15 : for (const auto &poCoordVar : apoCoordinateVars)
8191 : {
8192 10 : const auto &osName = poCoordVar->GetName();
8193 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8194 20 : std::string osStandardName;
8195 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8196 2 : poAttr->GetDimensionCount() == 0)
8197 : {
8198 2 : const char *pszStandardName = poAttr->ReadAsString();
8199 2 : if (pszStandardName)
8200 2 : osStandardName = pszStandardName;
8201 : }
8202 21 : if (osName == "lon" || osName == "longitude" ||
8203 21 : osName == "Longitude" || osStandardName == "longitude")
8204 : {
8205 5 : poLongVar = poCoordVar;
8206 : }
8207 6 : else if (osName == "lat" || osName == "latitude" ||
8208 6 : osName == "Latitude" || osStandardName == "latitude")
8209 : {
8210 5 : poLatVar = poCoordVar;
8211 : }
8212 : }
8213 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8214 : {
8215 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8216 5 : const auto &longDims = poLongVar->GetDimensions();
8217 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8218 5 : const auto &latDims = poLatVar->GetDimensions();
8219 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8220 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8221 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8222 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8223 : {
8224 : // Geolocation arrays are 1D, and of consistent size with
8225 : // the variable
8226 0 : useGeolocationArray = true;
8227 : }
8228 1 : else if ((longDimCount == 2 ||
8229 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8230 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8231 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8232 1 : (latDimCount == 2 ||
8233 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8234 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8235 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8236 :
8237 : {
8238 : // Geolocation arrays are 2D (or 3D with first dimension of
8239 : // size 1, as found in Sentinel 5P products), and of consistent
8240 : // size with the variable
8241 5 : useGeolocationArray = true;
8242 : }
8243 : else
8244 : {
8245 0 : CPLDebug(
8246 : "GDAL",
8247 : "Longitude and latitude coordinate variables found, "
8248 : "but their characteristics are not compatible of using "
8249 : "them as geolocation arrays");
8250 : }
8251 5 : if (useGeolocationArray)
8252 : {
8253 10 : CPLDebug("GDAL",
8254 : "Setting geolocation array from variables %s and %s",
8255 5 : poLongVar->GetName().c_str(),
8256 5 : poLatVar->GetName().c_str());
8257 : const std::string osFilenameLong =
8258 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8259 : const std::string osFilenameLat =
8260 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8261 : std::unique_ptr<GDALDataset> poTmpLongDS(
8262 : longDimCount == 1
8263 0 : ? poLongVar->AsClassicDataset(0, 0)
8264 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8265 15 : longDimCount - 2));
8266 5 : auto hTIFFLongDS = GDALTranslate(
8267 : osFilenameLong.c_str(),
8268 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8269 : std::unique_ptr<GDALDataset> poTmpLatDS(
8270 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8271 20 : : poLatVar->AsClassicDataset(
8272 15 : latDimCount - 1, latDimCount - 2));
8273 5 : auto hTIFFLatDS = GDALTranslate(
8274 : osFilenameLat.c_str(),
8275 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8276 5 : const bool bError =
8277 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8278 5 : GDALClose(hTIFFLongDS);
8279 5 : GDALClose(hTIFFLatDS);
8280 5 : if (bError)
8281 : {
8282 0 : VSIUnlink(osFilenameLong.c_str());
8283 0 : VSIUnlink(osFilenameLat.c_str());
8284 0 : return nullptr;
8285 : }
8286 :
8287 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8288 : }
8289 : }
8290 : else
8291 : {
8292 0 : CPLDebug("GDAL",
8293 : "Coordinate variables available for %s, but "
8294 : "longitude and/or latitude variables were not identified",
8295 0 : poParent->GetName().c_str());
8296 : }
8297 : }
8298 :
8299 : // Build gdalwarp arguments
8300 48 : CPLStringList aosArgv;
8301 :
8302 24 : aosArgv.AddString("-of");
8303 24 : aosArgv.AddString("VRT");
8304 :
8305 24 : aosArgv.AddString("-r");
8306 24 : aosArgv.AddString(pszResampleAlg);
8307 :
8308 24 : if (!osDstWKT.empty())
8309 : {
8310 2 : aosArgv.AddString("-t_srs");
8311 2 : aosArgv.AddString(osDstWKT.c_str());
8312 : }
8313 :
8314 24 : if (useGeolocationArray)
8315 5 : aosArgv.AddString("-geoloc");
8316 :
8317 24 : if (gotXSpacing && gotYSpacing)
8318 : {
8319 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8320 : const double dfXMax =
8321 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8322 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8323 : const double dfYMin =
8324 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8325 1 : aosArgv.AddString("-te");
8326 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8327 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8328 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8329 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8330 : }
8331 :
8332 24 : if (poNewDimX && poNewDimY)
8333 : {
8334 3 : aosArgv.AddString("-ts");
8335 : aosArgv.AddString(
8336 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8337 : aosArgv.AddString(
8338 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8339 : }
8340 21 : else if (poNewDimX)
8341 : {
8342 1 : aosArgv.AddString("-ts");
8343 : aosArgv.AddString(
8344 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8345 1 : aosArgv.AddString("0");
8346 : }
8347 20 : else if (poNewDimY)
8348 : {
8349 1 : aosArgv.AddString("-ts");
8350 1 : aosArgv.AddString("0");
8351 : aosArgv.AddString(
8352 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8353 : }
8354 :
8355 : // Create a warped VRT dataset
8356 : GDALWarpAppOptions *psOptions =
8357 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8358 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8359 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8360 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8361 24 : GDALWarpAppOptionsFree(psOptions);
8362 24 : if (poReprojectedDS == nullptr)
8363 3 : return nullptr;
8364 :
8365 : int nBlockXSize;
8366 : int nBlockYSize;
8367 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8368 21 : anBlockSize.emplace_back(nBlockYSize);
8369 21 : anBlockSize.emplace_back(nBlockXSize);
8370 :
8371 21 : GDALGeoTransform gt;
8372 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8373 21 : CPLAssert(eErr == CE_None);
8374 21 : CPL_IGNORE_RET_VAL(eErr);
8375 :
8376 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8377 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8378 42 : poReprojectedDS->GetRasterYSize());
8379 : auto varY = GDALMDArrayRegularlySpaced::Create(
8380 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8381 21 : poDimY->SetIndexingVariable(varY);
8382 :
8383 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8384 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8385 42 : poReprojectedDS->GetRasterXSize());
8386 : auto varX = GDALMDArrayRegularlySpaced::Create(
8387 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8388 21 : poDimX->SetIndexingVariable(varX);
8389 :
8390 21 : apoNewDims.emplace_back(poDimY);
8391 21 : apoNewDims.emplace_back(poDimX);
8392 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8393 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8394 21 : newAr->SetSelf(newAr);
8395 21 : if (poTargetSRS)
8396 : {
8397 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8398 : }
8399 : else
8400 : {
8401 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8402 : }
8403 21 : newAr->m_poVarX = varX;
8404 21 : newAr->m_poVarY = varY;
8405 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8406 21 : newAr->m_poParentDS = std::move(poParentDS);
8407 :
8408 : // If the input array is y,x,band ordered, the above newAr is
8409 : // actually band,y,x ordered as it is more convenient for
8410 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8411 : // array to the order of the input array
8412 21 : if (bYXBandOrder)
8413 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8414 :
8415 19 : return newAr;
8416 : }
8417 :
8418 : /************************************************************************/
8419 : /* GDALMDArrayResampled::IRead() */
8420 : /************************************************************************/
8421 :
8422 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8423 : const size_t *count, const GInt64 *arrayStep,
8424 : const GPtrDiff_t *bufferStride,
8425 : const GDALExtendedDataType &bufferDataType,
8426 : void *pDstBuffer) const
8427 : {
8428 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8429 0 : return false;
8430 :
8431 : struct Stack
8432 : {
8433 : size_t nIters = 0;
8434 : GByte *dst_ptr = nullptr;
8435 : GPtrDiff_t dst_inc_offset = 0;
8436 : };
8437 :
8438 29 : const auto nDims = GetDimensionCount();
8439 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8440 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8441 92 : for (size_t i = 0; i < nDims; i++)
8442 : {
8443 63 : stack[i].dst_inc_offset =
8444 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8445 : }
8446 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8447 :
8448 29 : size_t dimIdx = 0;
8449 29 : const size_t iDimY = nDims - 2;
8450 29 : const size_t iDimX = nDims - 1;
8451 : // Use an array to avoid a false positive warning from CLang Static
8452 : // Analyzer about flushCaches being never read
8453 29 : bool flushCaches[] = {false};
8454 : const bool bYXBandOrder =
8455 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8456 :
8457 38 : lbl_next_depth:
8458 38 : if (dimIdx == iDimY)
8459 : {
8460 33 : if (flushCaches[0])
8461 : {
8462 5 : flushCaches[0] = false;
8463 : // When changing of 2D slice, flush GDAL 2D buffers
8464 5 : m_poParentDS->FlushCache(false);
8465 5 : m_poReprojectedDS->FlushCache(false);
8466 : }
8467 :
8468 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8469 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8470 : arrayStep, bufferStride, bufferDataType,
8471 33 : stack[dimIdx].dst_ptr))
8472 : {
8473 0 : return false;
8474 : }
8475 : }
8476 : else
8477 : {
8478 5 : stack[dimIdx].nIters = count[dimIdx];
8479 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8480 5 : arrayStartIdx[dimIdx])
8481 : {
8482 1 : flushCaches[0] = true;
8483 : }
8484 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8485 5 : arrayStartIdx[dimIdx];
8486 : while (true)
8487 : {
8488 9 : dimIdx++;
8489 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8490 9 : goto lbl_next_depth;
8491 9 : lbl_return_to_caller:
8492 9 : dimIdx--;
8493 9 : if ((--stack[dimIdx].nIters) == 0)
8494 5 : break;
8495 4 : flushCaches[0] = true;
8496 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8497 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8498 : }
8499 : }
8500 38 : if (dimIdx > 0)
8501 9 : goto lbl_return_to_caller;
8502 :
8503 29 : return true;
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* GetResampled() */
8508 : /************************************************************************/
8509 :
8510 : /** Return an array that is a resampled / reprojected view of the current array
8511 : *
8512 : * This is the same as the C function GDALMDArrayGetResampled().
8513 : *
8514 : * Currently this method can only resample along the last 2 dimensions, unless
8515 : * orthorectifying a NASA EMIT dataset.
8516 : *
8517 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8518 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8519 : *
8520 : * Options available are:
8521 : * <ul>
8522 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8523 : * Can be set to NO to use generic reprojection method.
8524 : * </li>
8525 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8526 : * orthorectification to take into account the value of the
8527 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8528 : * current array along the band dimension are valid.</li>
8529 : * </ul>
8530 : *
8531 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8532 : * apoNewDims[i] can be NULL to let the method automatically
8533 : * determine it.
8534 : * @param resampleAlg Resampling algorithm
8535 : * @param poTargetSRS Target SRS, or nullptr
8536 : * @param papszOptions NULL-terminated list of options, or NULL.
8537 : *
8538 : * @return a new array, that holds a reference to the original one, and thus is
8539 : * a view of it (not a copy), or nullptr in case of error.
8540 : *
8541 : * @since 3.4
8542 : */
8543 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8544 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8545 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8546 : CSLConstList papszOptions) const
8547 : {
8548 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8549 38 : if (!self)
8550 : {
8551 0 : CPLError(CE_Failure, CPLE_AppDefined,
8552 : "Driver implementation issue: m_pSelf not set !");
8553 0 : return nullptr;
8554 : }
8555 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8556 : {
8557 0 : CPLError(CE_Failure, CPLE_AppDefined,
8558 : "GetResampled() only supports numeric data type");
8559 0 : return nullptr;
8560 : }
8561 :
8562 : // Special case for NASA EMIT datasets
8563 76 : auto apoDims = GetDimensions();
8564 36 : if (poTargetSRS == nullptr &&
8565 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8566 20 : apoDims[1]->GetName() == "crosstrack" &&
8567 10 : apoDims[2]->GetName() == "bands" &&
8568 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8569 1 : apoNewDims ==
8570 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8571 30 : apoDims[2]})) ||
8572 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8573 3 : apoDims[1]->GetName() == "crosstrack" &&
8574 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8575 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8576 : "EMIT_ORTHORECTIFICATION", "YES")))
8577 : {
8578 9 : auto poRootGroup = GetRootGroup();
8579 9 : if (poRootGroup)
8580 : {
8581 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8582 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8583 9 : if (poAttrGeotransform &&
8584 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8585 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8586 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8587 9 : poLocationGroup)
8588 : {
8589 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8590 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8591 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8592 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8593 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8594 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8595 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8596 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8597 : {
8598 : return CreateGLTOrthorectified(
8599 : self, poRootGroup, poGLT_X, poGLT_Y,
8600 : /* nGLTIndexOffset = */ -1,
8601 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8602 : }
8603 : }
8604 : }
8605 : }
8606 :
8607 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8608 : "EMIT_ORTHORECTIFICATION", "NO")))
8609 : {
8610 0 : CPLError(CE_Failure, CPLE_AppDefined,
8611 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8612 : "parameters are not compatible with it");
8613 0 : return nullptr;
8614 : }
8615 :
8616 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8617 29 : poTargetSRS, papszOptions);
8618 : }
8619 :
8620 : /************************************************************************/
8621 : /* GDALDatasetFromArray() */
8622 : /************************************************************************/
8623 :
8624 : class GDALDatasetFromArray;
8625 :
8626 : namespace
8627 : {
8628 : struct MetadataItem
8629 : {
8630 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8631 : std::string osName{};
8632 : std::string osDefinition{};
8633 : bool bDefinitionUsesPctForG = false;
8634 : };
8635 :
8636 : struct BandImageryMetadata
8637 : {
8638 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8639 : double dfCentralWavelengthToMicrometer = 1.0;
8640 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8641 : double dfFWHMToMicrometer = 1.0;
8642 : };
8643 :
8644 : } // namespace
8645 :
8646 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8647 : {
8648 : std::vector<GUInt64> m_anOffset{};
8649 : std::vector<size_t> m_anCount{};
8650 : std::vector<GPtrDiff_t> m_anStride{};
8651 :
8652 : protected:
8653 : CPLErr IReadBlock(int, int, void *) override;
8654 : CPLErr IWriteBlock(int, int, void *) override;
8655 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8656 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8657 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8658 : GSpacing nLineSpaceBuf,
8659 : GDALRasterIOExtraArg *psExtraArg) override;
8660 :
8661 : public:
8662 : explicit GDALRasterBandFromArray(
8663 : GDALDatasetFromArray *poDSIn,
8664 : const std::vector<GUInt64> &anOtherDimCoord,
8665 : const std::vector<std::vector<MetadataItem>>
8666 : &aoBandParameterMetadataItems,
8667 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8668 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8669 :
8670 : double GetNoDataValue(int *pbHasNoData) override;
8671 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8672 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8673 : double GetOffset(int *pbHasOffset) override;
8674 : double GetScale(int *pbHasScale) override;
8675 : const char *GetUnitType() override;
8676 : GDALColorInterp GetColorInterpretation() override;
8677 : };
8678 :
8679 : class GDALDatasetFromArray final : public GDALPamDataset
8680 : {
8681 : friend class GDALRasterBandFromArray;
8682 :
8683 : std::shared_ptr<GDALMDArray> m_poArray;
8684 : size_t m_iXDim;
8685 : size_t m_iYDim;
8686 : GDALGeoTransform m_gt{};
8687 : bool m_bHasGT = false;
8688 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8689 : GDALMultiDomainMetadata m_oMDD{};
8690 : std::string m_osOvrFilename{};
8691 :
8692 : public:
8693 208 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8694 : size_t iXDim, size_t iYDim)
8695 208 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8696 : {
8697 : // Initialize an overview filename from the filename of the array
8698 : // and its name.
8699 208 : const std::string &osFilename = m_poArray->GetFilename();
8700 208 : if (!osFilename.empty())
8701 : {
8702 183 : m_osOvrFilename = osFilename;
8703 183 : m_osOvrFilename += '.';
8704 6640 : for (char ch : m_poArray->GetName())
8705 : {
8706 6457 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8707 5723 : (ch >= 'a' && ch <= 'z') || ch == '_')
8708 : {
8709 5196 : m_osOvrFilename += ch;
8710 : }
8711 : else
8712 : {
8713 1261 : m_osOvrFilename += '_';
8714 : }
8715 : }
8716 183 : m_osOvrFilename += ".ovr";
8717 183 : oOvManager.Initialize(this);
8718 : }
8719 208 : }
8720 :
8721 : static GDALDatasetFromArray *
8722 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8723 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8724 : CSLConstList papszOptions);
8725 :
8726 : ~GDALDatasetFromArray() override;
8727 :
8728 342 : CPLErr Close() override
8729 : {
8730 342 : CPLErr eErr = CE_None;
8731 342 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8732 : {
8733 342 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8734 : CE_None)
8735 0 : eErr = CE_Failure;
8736 342 : m_poArray.reset();
8737 : }
8738 342 : return eErr;
8739 : }
8740 :
8741 54 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8742 : {
8743 54 : gt = m_gt;
8744 54 : return m_bHasGT ? CE_None : CE_Failure;
8745 : }
8746 :
8747 62 : const OGRSpatialReference *GetSpatialRef() const override
8748 : {
8749 62 : if (m_poArray->GetDimensionCount() < 2)
8750 3 : return nullptr;
8751 59 : m_poSRS = m_poArray->GetSpatialRef();
8752 59 : if (m_poSRS)
8753 : {
8754 20 : m_poSRS.reset(m_poSRS->Clone());
8755 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8756 60 : for (auto &m : axisMapping)
8757 : {
8758 40 : if (m == static_cast<int>(m_iXDim) + 1)
8759 20 : m = 1;
8760 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8761 20 : m = 2;
8762 : }
8763 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8764 : }
8765 59 : return m_poSRS.get();
8766 : }
8767 :
8768 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8769 : {
8770 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8771 : }
8772 :
8773 165 : char **GetMetadata(const char *pszDomain) override
8774 : {
8775 165 : return m_oMDD.GetMetadata(pszDomain);
8776 : }
8777 :
8778 233 : const char *GetMetadataItem(const char *pszName,
8779 : const char *pszDomain) override
8780 : {
8781 421 : if (!m_osOvrFilename.empty() && pszName &&
8782 435 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8783 14 : EQUAL(pszDomain, "OVERVIEWS"))
8784 : {
8785 14 : return m_osOvrFilename.c_str();
8786 : }
8787 219 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8788 : }
8789 : };
8790 :
8791 416 : GDALDatasetFromArray::~GDALDatasetFromArray()
8792 : {
8793 208 : GDALDatasetFromArray::Close();
8794 416 : }
8795 :
8796 : /************************************************************************/
8797 : /* GDALRasterBandFromArray() */
8798 : /************************************************************************/
8799 :
8800 282 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8801 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8802 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8803 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8804 282 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8805 : {
8806 282 : const auto &poArray(poDSIn->m_poArray);
8807 282 : const auto &dims(poArray->GetDimensions());
8808 282 : const auto nDimCount(dims.size());
8809 564 : const auto blockSize(poArray->GetBlockSize());
8810 269 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8811 551 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8812 140 : blockSize[poDSIn->m_iYDim]))
8813 : : 1;
8814 282 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8815 152 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8816 152 : blockSize[poDSIn->m_iXDim]))
8817 282 : : poDSIn->GetRasterXSize();
8818 282 : eDataType = poArray->GetDataType().GetNumericDataType();
8819 282 : eAccess = poDSIn->eAccess;
8820 282 : m_anOffset.resize(nDimCount);
8821 282 : m_anCount.resize(nDimCount, 1);
8822 282 : m_anStride.resize(nDimCount);
8823 959 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8824 : {
8825 677 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8826 : {
8827 252 : std::string dimName(dims[i]->GetName());
8828 126 : GUInt64 nIndex = anOtherDimCoord[j];
8829 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8830 : // subsetted dimensions as generated by GetView()
8831 126 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8832 : {
8833 : CPLStringList aosTokens(
8834 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8835 6 : if (aosTokens.size() == 5)
8836 : {
8837 6 : dimName = aosTokens[1];
8838 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8839 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8840 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8841 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8842 0 : : nStartDim - (nIndex * -nIncrDim);
8843 : }
8844 : }
8845 126 : if (nDimCount != 3 || dimName != "Band")
8846 : {
8847 70 : SetMetadataItem(
8848 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8849 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8850 : }
8851 :
8852 126 : auto indexingVar = dims[i]->GetIndexingVariable();
8853 :
8854 : // If the indexing variable is also listed in band parameter arrays,
8855 : // then don't use our default formatting
8856 126 : if (indexingVar)
8857 : {
8858 42 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8859 : {
8860 14 : if (oItem.poArray->GetFullName() ==
8861 14 : indexingVar->GetFullName())
8862 : {
8863 12 : indexingVar.reset();
8864 12 : break;
8865 : }
8866 : }
8867 : }
8868 :
8869 154 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8870 28 : indexingVar->GetDimensions()[0]->GetSize() ==
8871 28 : dims[i]->GetSize())
8872 : {
8873 28 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8874 : {
8875 0 : if (!bHasWarned)
8876 : {
8877 0 : CPLError(
8878 : CE_Warning, CPLE_AppDefined,
8879 : "Maximum delay to load band metadata from "
8880 : "dimension indexing variables has expired. "
8881 : "Increase the value of the "
8882 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8883 : "option of GDALMDArray::AsClassicDataset() "
8884 : "(also accessible as the "
8885 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8886 : "configuration option), "
8887 : "or set it to 'unlimited' for unlimited delay. ");
8888 0 : bHasWarned = true;
8889 : }
8890 : }
8891 : else
8892 : {
8893 28 : size_t nCount = 1;
8894 28 : const auto &dt(indexingVar->GetDataType());
8895 56 : std::vector<GByte> abyTmp(dt.GetSize());
8896 56 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8897 28 : nullptr, nullptr, dt, &abyTmp[0]))
8898 : {
8899 28 : char *pszTmp = nullptr;
8900 28 : GDALExtendedDataType::CopyValue(
8901 28 : &abyTmp[0], dt, &pszTmp,
8902 56 : GDALExtendedDataType::CreateString());
8903 28 : if (pszTmp)
8904 : {
8905 28 : SetMetadataItem(
8906 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8907 : pszTmp);
8908 28 : CPLFree(pszTmp);
8909 : }
8910 :
8911 28 : const auto &unit(indexingVar->GetUnit());
8912 28 : if (!unit.empty())
8913 : {
8914 12 : SetMetadataItem(
8915 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8916 : unit.c_str());
8917 : }
8918 : }
8919 : }
8920 : }
8921 :
8922 144 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8923 : {
8924 36 : CPLString osVal;
8925 :
8926 18 : size_t nCount = 1;
8927 18 : const auto &dt(oItem.poArray->GetDataType());
8928 18 : if (oItem.bDefinitionUsesPctForG)
8929 : {
8930 : // There is one and only one %[x][.y]f|g in osDefinition
8931 16 : std::vector<GByte> abyTmp(dt.GetSize());
8932 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8933 8 : nullptr, nullptr, dt, &abyTmp[0]))
8934 : {
8935 8 : double dfVal = 0;
8936 8 : GDALExtendedDataType::CopyValue(
8937 8 : &abyTmp[0], dt, &dfVal,
8938 16 : GDALExtendedDataType::Create(GDT_Float64));
8939 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8940 : }
8941 : }
8942 : else
8943 : {
8944 : // There should be zero or one %s in osDefinition
8945 10 : char *pszValue = nullptr;
8946 10 : if (dt.GetClass() == GEDTC_STRING)
8947 : {
8948 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8949 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8950 2 : dt, &pszValue));
8951 : }
8952 : else
8953 : {
8954 16 : std::vector<GByte> abyTmp(dt.GetSize());
8955 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8956 : nullptr, nullptr, dt,
8957 8 : &abyTmp[0]))
8958 : {
8959 8 : GDALExtendedDataType::CopyValue(
8960 8 : &abyTmp[0], dt, &pszValue,
8961 16 : GDALExtendedDataType::CreateString());
8962 : }
8963 : }
8964 :
8965 10 : if (pszValue)
8966 : {
8967 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8968 10 : CPLFree(pszValue);
8969 : }
8970 : }
8971 18 : if (!osVal.empty())
8972 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
8973 : }
8974 :
8975 126 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8976 : {
8977 : auto &poCentralWavelengthArray =
8978 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8979 4 : size_t nCount = 1;
8980 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
8981 8 : std::vector<GByte> abyTmp(dt.GetSize());
8982 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8983 : &nCount, nullptr, nullptr,
8984 4 : dt, &abyTmp[0]))
8985 : {
8986 4 : double dfVal = 0;
8987 4 : GDALExtendedDataType::CopyValue(
8988 4 : &abyTmp[0], dt, &dfVal,
8989 8 : GDALExtendedDataType::Create(GDT_Float64));
8990 4 : SetMetadataItem(
8991 : "CENTRAL_WAVELENGTH_UM",
8992 : CPLSPrintf(
8993 4 : "%g", dfVal * aoBandImageryMetadata[j]
8994 4 : .dfCentralWavelengthToMicrometer),
8995 : "IMAGERY");
8996 : }
8997 : }
8998 :
8999 126 : if (aoBandImageryMetadata[j].poFWHMArray)
9000 : {
9001 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9002 2 : size_t nCount = 1;
9003 2 : const auto &dt(poFWHMArray->GetDataType());
9004 4 : std::vector<GByte> abyTmp(dt.GetSize());
9005 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9006 2 : nullptr, dt, &abyTmp[0]))
9007 : {
9008 2 : double dfVal = 0;
9009 2 : GDALExtendedDataType::CopyValue(
9010 2 : &abyTmp[0], dt, &dfVal,
9011 4 : GDALExtendedDataType::Create(GDT_Float64));
9012 2 : SetMetadataItem(
9013 : "FWHM_UM",
9014 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9015 2 : .dfFWHMToMicrometer),
9016 : "IMAGERY");
9017 : }
9018 : }
9019 :
9020 126 : m_anOffset[i] = anOtherDimCoord[j];
9021 126 : j++;
9022 : }
9023 : }
9024 282 : }
9025 :
9026 : /************************************************************************/
9027 : /* GetNoDataValue() */
9028 : /************************************************************************/
9029 :
9030 111 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9031 : {
9032 111 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9033 111 : const auto &poArray(l_poDS->m_poArray);
9034 111 : bool bHasNodata = false;
9035 111 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9036 111 : if (pbHasNoData)
9037 99 : *pbHasNoData = bHasNodata;
9038 111 : return res;
9039 : }
9040 :
9041 : /************************************************************************/
9042 : /* GetNoDataValueAsInt64() */
9043 : /************************************************************************/
9044 :
9045 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9046 : {
9047 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9048 1 : const auto &poArray(l_poDS->m_poArray);
9049 1 : bool bHasNodata = false;
9050 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9051 1 : if (pbHasNoData)
9052 1 : *pbHasNoData = bHasNodata;
9053 1 : return nodata;
9054 : }
9055 :
9056 : /************************************************************************/
9057 : /* GetNoDataValueAsUInt64() */
9058 : /************************************************************************/
9059 :
9060 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9061 : {
9062 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9063 1 : const auto &poArray(l_poDS->m_poArray);
9064 1 : bool bHasNodata = false;
9065 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9066 1 : if (pbHasNoData)
9067 1 : *pbHasNoData = bHasNodata;
9068 1 : return nodata;
9069 : }
9070 :
9071 : /************************************************************************/
9072 : /* GetOffset() */
9073 : /************************************************************************/
9074 :
9075 38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9076 : {
9077 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9078 38 : const auto &poArray(l_poDS->m_poArray);
9079 38 : bool bHasValue = false;
9080 38 : double dfRes = poArray->GetOffset(&bHasValue);
9081 38 : if (pbHasOffset)
9082 19 : *pbHasOffset = bHasValue;
9083 38 : return dfRes;
9084 : }
9085 :
9086 : /************************************************************************/
9087 : /* GetUnitType() */
9088 : /************************************************************************/
9089 :
9090 44 : const char *GDALRasterBandFromArray::GetUnitType()
9091 : {
9092 44 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9093 44 : const auto &poArray(l_poDS->m_poArray);
9094 44 : return poArray->GetUnit().c_str();
9095 : }
9096 :
9097 : /************************************************************************/
9098 : /* GetScale() */
9099 : /************************************************************************/
9100 :
9101 36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9102 : {
9103 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9104 36 : const auto &poArray(l_poDS->m_poArray);
9105 36 : bool bHasValue = false;
9106 36 : double dfRes = poArray->GetScale(&bHasValue);
9107 36 : if (pbHasScale)
9108 17 : *pbHasScale = bHasValue;
9109 36 : return dfRes;
9110 : }
9111 :
9112 : /************************************************************************/
9113 : /* IReadBlock() */
9114 : /************************************************************************/
9115 :
9116 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9117 : void *pImage)
9118 : {
9119 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9120 68 : const int nXOff = nBlockXOff * nBlockXSize;
9121 68 : const int nYOff = nBlockYOff * nBlockYSize;
9122 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9123 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9124 : GDALRasterIOExtraArg sExtraArg;
9125 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9126 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9127 : nReqXSize, nReqYSize, eDataType, nDTSize,
9128 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9129 : }
9130 :
9131 : /************************************************************************/
9132 : /* IWriteBlock() */
9133 : /************************************************************************/
9134 :
9135 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9136 : void *pImage)
9137 : {
9138 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9139 1 : const int nXOff = nBlockXOff * nBlockXSize;
9140 1 : const int nYOff = nBlockYOff * nBlockYSize;
9141 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9142 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9143 : GDALRasterIOExtraArg sExtraArg;
9144 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9145 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9146 : nReqXSize, nReqYSize, eDataType, nDTSize,
9147 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9148 : }
9149 :
9150 : /************************************************************************/
9151 : /* IRasterIO() */
9152 : /************************************************************************/
9153 :
9154 333 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9155 : int nYOff, int nXSize, int nYSize,
9156 : void *pData, int nBufXSize,
9157 : int nBufYSize, GDALDataType eBufType,
9158 : GSpacing nPixelSpaceBuf,
9159 : GSpacing nLineSpaceBuf,
9160 : GDALRasterIOExtraArg *psExtraArg)
9161 : {
9162 333 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9163 333 : const auto &poArray(l_poDS->m_poArray);
9164 333 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9165 333 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9166 333 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9167 333 : (nLineSpaceBuf % nBufferDTSize) == 0)
9168 : {
9169 333 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9170 333 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9171 666 : m_anStride[l_poDS->m_iXDim] =
9172 333 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9173 333 : if (poArray->GetDimensionCount() >= 2)
9174 : {
9175 323 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9176 323 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9177 323 : m_anStride[l_poDS->m_iYDim] =
9178 323 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9179 : }
9180 333 : if (eRWFlag == GF_Read)
9181 : {
9182 654 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9183 327 : m_anStride.data(),
9184 654 : GDALExtendedDataType::Create(eBufType), pData)
9185 327 : ? CE_None
9186 327 : : CE_Failure;
9187 : }
9188 : else
9189 : {
9190 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9191 6 : m_anStride.data(),
9192 12 : GDALExtendedDataType::Create(eBufType), pData)
9193 6 : ? CE_None
9194 6 : : CE_Failure;
9195 : }
9196 : }
9197 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9198 : pData, nBufXSize, nBufYSize, eBufType,
9199 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9200 : }
9201 :
9202 : /************************************************************************/
9203 : /* GetColorInterpretation() */
9204 : /************************************************************************/
9205 :
9206 60 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9207 : {
9208 60 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9209 60 : const auto &poArray(l_poDS->m_poArray);
9210 180 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9211 60 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9212 : {
9213 6 : bool bOK = false;
9214 6 : GUInt64 nStartIndex = 0;
9215 6 : if (poArray->GetDimensionCount() == 2 &&
9216 0 : poAttr->GetDimensionCount() == 0)
9217 : {
9218 0 : bOK = true;
9219 : }
9220 6 : else if (poArray->GetDimensionCount() == 3)
9221 : {
9222 6 : uint64_t nExtraDimSamples = 1;
9223 6 : const auto &apoDims = poArray->GetDimensions();
9224 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9225 : {
9226 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9227 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9228 : }
9229 6 : if (poAttr->GetDimensionsSize() ==
9230 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9231 : {
9232 6 : bOK = true;
9233 : }
9234 6 : nStartIndex = nBand - 1;
9235 : }
9236 6 : if (bOK)
9237 : {
9238 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9239 6 : const size_t nCount = 1;
9240 6 : const GInt64 arrayStep = 1;
9241 6 : const GPtrDiff_t bufferStride = 1;
9242 6 : char *pszValue = nullptr;
9243 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9244 6 : oStringDT, &pszValue);
9245 6 : if (pszValue)
9246 : {
9247 : const auto eColorInterp =
9248 6 : GDALGetColorInterpretationByName(pszValue);
9249 6 : CPLFree(pszValue);
9250 6 : return eColorInterp;
9251 : }
9252 : }
9253 : }
9254 54 : return GCI_Undefined;
9255 : }
9256 :
9257 : /************************************************************************/
9258 : /* GDALDatasetFromArray::Create() */
9259 : /************************************************************************/
9260 :
9261 260 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9262 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9263 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9264 :
9265 : {
9266 260 : const auto nDimCount(array->GetDimensionCount());
9267 260 : if (nDimCount == 0)
9268 : {
9269 1 : CPLError(CE_Failure, CPLE_NotSupported,
9270 : "Unsupported number of dimensions");
9271 1 : return nullptr;
9272 : }
9273 517 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9274 258 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9275 : {
9276 1 : CPLError(CE_Failure, CPLE_NotSupported,
9277 : "Only arrays with numeric data types "
9278 : "can be exposed as classic GDALDataset");
9279 1 : return nullptr;
9280 : }
9281 258 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9282 238 : (nDimCount >= 2 && iXDim == iYDim))
9283 : {
9284 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9285 8 : return nullptr;
9286 : }
9287 250 : GUInt64 nTotalBands = 1;
9288 250 : const auto &dims(array->GetDimensions());
9289 825 : for (size_t i = 0; i < nDimCount; ++i)
9290 : {
9291 576 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9292 : {
9293 91 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9294 : {
9295 1 : CPLError(CE_Failure, CPLE_AppDefined,
9296 : "Too many bands. Operate on a sliced view");
9297 1 : return nullptr;
9298 : }
9299 90 : nTotalBands *= dims[i]->GetSize();
9300 : }
9301 : }
9302 :
9303 498 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9304 498 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9305 824 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9306 : {
9307 575 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9308 : {
9309 90 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9310 90 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9311 90 : ++j;
9312 : }
9313 : }
9314 :
9315 249 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9316 :
9317 : const char *pszBandMetadata =
9318 249 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9319 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9320 498 : nNewDimCount);
9321 249 : if (pszBandMetadata)
9322 : {
9323 32 : if (!poRootGroup)
9324 : {
9325 1 : CPLError(CE_Failure, CPLE_AppDefined,
9326 : "Root group should be provided when BAND_METADATA is set");
9327 24 : return nullptr;
9328 : }
9329 31 : CPLJSONDocument oDoc;
9330 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9331 : {
9332 1 : CPLError(CE_Failure, CPLE_AppDefined,
9333 : "Invalid JSON content for BAND_METADATA");
9334 1 : return nullptr;
9335 : }
9336 30 : auto oRoot = oDoc.GetRoot();
9337 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9338 : {
9339 1 : CPLError(CE_Failure, CPLE_AppDefined,
9340 : "Value of BAND_METADATA should be an array");
9341 1 : return nullptr;
9342 : }
9343 :
9344 29 : auto oArray = oRoot.ToArray();
9345 38 : for (int j = 0; j < oArray.Size(); ++j)
9346 : {
9347 30 : const auto oJsonItem = oArray[j];
9348 30 : MetadataItem oItem;
9349 30 : size_t iExtraDimIdx = 0;
9350 :
9351 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9352 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9353 0 : std::shared_ptr<GDALMDArray> poArray;
9354 0 : std::shared_ptr<GDALAttribute> poAttribute;
9355 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9356 : {
9357 1 : CPLError(CE_Failure, CPLE_AppDefined,
9358 : "BAND_METADATA[%d][\"array\"] or "
9359 : "BAND_METADATA[%d][\"attribute\"] is missing",
9360 : j, j);
9361 1 : return nullptr;
9362 : }
9363 48 : else if (!osBandArrayFullname.empty() &&
9364 19 : !osBandAttributeName.empty())
9365 : {
9366 1 : CPLError(
9367 : CE_Failure, CPLE_AppDefined,
9368 : "BAND_METADATA[%d][\"array\"] and "
9369 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9370 : j, j);
9371 1 : return nullptr;
9372 : }
9373 28 : else if (!osBandArrayFullname.empty())
9374 : {
9375 : poArray =
9376 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9377 18 : if (!poArray)
9378 : {
9379 1 : CPLError(CE_Failure, CPLE_AppDefined,
9380 : "Array %s cannot be found",
9381 : osBandArrayFullname.c_str());
9382 3 : return nullptr;
9383 : }
9384 17 : if (poArray->GetDimensionCount() != 1)
9385 : {
9386 1 : CPLError(CE_Failure, CPLE_AppDefined,
9387 : "Array %s is not a 1D array",
9388 : osBandArrayFullname.c_str());
9389 1 : return nullptr;
9390 : }
9391 : const auto &osAuxArrayDimName =
9392 16 : poArray->GetDimensions()[0]->GetName();
9393 : auto oIter =
9394 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9395 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9396 : {
9397 1 : CPLError(
9398 : CE_Failure, CPLE_AppDefined,
9399 : "Dimension %s of array %s is not a non-X/Y dimension "
9400 : "of array %s",
9401 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9402 1 : array->GetName().c_str());
9403 1 : return nullptr;
9404 : }
9405 15 : iExtraDimIdx = oIter->second;
9406 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9407 : }
9408 : else
9409 : {
9410 10 : CPLAssert(!osBandAttributeName.empty());
9411 10 : poAttribute = !osBandAttributeName.empty() &&
9412 10 : osBandAttributeName[0] == '/'
9413 24 : ? poRootGroup->OpenAttributeFromFullname(
9414 : osBandAttributeName)
9415 10 : : array->GetAttribute(osBandAttributeName);
9416 10 : if (!poAttribute)
9417 : {
9418 2 : CPLError(CE_Failure, CPLE_AppDefined,
9419 : "Attribute %s cannot be found",
9420 : osBandAttributeName.c_str());
9421 8 : return nullptr;
9422 : }
9423 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9424 8 : if (aoAttrDims.size() != 1)
9425 : {
9426 4 : CPLError(CE_Failure, CPLE_AppDefined,
9427 : "Attribute %s is not a 1D array",
9428 : osBandAttributeName.c_str());
9429 4 : return nullptr;
9430 : }
9431 4 : bool found = false;
9432 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9433 : {
9434 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9435 5 : ->GetSize() == aoAttrDims[0])
9436 : {
9437 4 : if (found)
9438 : {
9439 2 : CPLError(CE_Failure, CPLE_AppDefined,
9440 : "Several dimensions of %s have the same "
9441 : "size as attribute %s. Cannot infer which "
9442 : "one to bind to!",
9443 1 : array->GetName().c_str(),
9444 : osBandAttributeName.c_str());
9445 1 : return nullptr;
9446 : }
9447 3 : found = true;
9448 3 : iExtraDimIdx = iter.second;
9449 : }
9450 : }
9451 3 : if (!found)
9452 : {
9453 2 : CPLError(
9454 : CE_Failure, CPLE_AppDefined,
9455 : "No dimension of %s has the same size as attribute %s",
9456 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9457 1 : return nullptr;
9458 : }
9459 : }
9460 :
9461 17 : oItem.osName = oJsonItem.GetString("item_name");
9462 17 : if (oItem.osName.empty())
9463 : {
9464 1 : CPLError(CE_Failure, CPLE_AppDefined,
9465 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9466 1 : return nullptr;
9467 : }
9468 :
9469 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9470 :
9471 : // Check correctness of definition
9472 16 : bool bFirstNumericFormatter = true;
9473 16 : std::string osModDefinition;
9474 16 : bool bDefinitionUsesPctForG = false;
9475 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9476 : {
9477 70 : if (osDefinition[k] == '%')
9478 : {
9479 15 : osModDefinition += osDefinition[k];
9480 15 : if (k + 1 == osDefinition.size())
9481 : {
9482 1 : CPLError(CE_Failure, CPLE_AppDefined,
9483 : "Value of "
9484 : "BAND_METADATA[%d][\"item_value\"] = "
9485 : "%s is invalid at offset %d",
9486 : j, osDefinition.c_str(), int(k));
9487 1 : return nullptr;
9488 : }
9489 14 : ++k;
9490 14 : if (osDefinition[k] == '%')
9491 : {
9492 1 : osModDefinition += osDefinition[k];
9493 1 : continue;
9494 : }
9495 13 : if (!bFirstNumericFormatter)
9496 : {
9497 1 : CPLError(CE_Failure, CPLE_AppDefined,
9498 : "Value of "
9499 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9500 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9501 : "formatters should be specified at most once",
9502 : j, osDefinition.c_str(), int(k));
9503 1 : return nullptr;
9504 : }
9505 12 : bFirstNumericFormatter = false;
9506 19 : for (; k < osDefinition.size(); ++k)
9507 : {
9508 19 : osModDefinition += osDefinition[k];
9509 38 : if (!((osDefinition[k] >= '0' &&
9510 16 : osDefinition[k] <= '9') ||
9511 15 : osDefinition[k] == '.'))
9512 12 : break;
9513 : }
9514 24 : if (k == osDefinition.size() ||
9515 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9516 5 : osDefinition[k] != 's'))
9517 : {
9518 1 : CPLError(CE_Failure, CPLE_AppDefined,
9519 : "Value of "
9520 : "BAND_METADATA[%d][\"item_value\"] = "
9521 : "%s is invalid at offset %d: only "
9522 : "%%[x][.y]f|g or %%s formatters are accepted",
9523 : j, osDefinition.c_str(), int(k));
9524 1 : return nullptr;
9525 : }
9526 11 : bDefinitionUsesPctForG =
9527 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9528 11 : if (bDefinitionUsesPctForG)
9529 : {
9530 12 : if (poArray &&
9531 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9532 : {
9533 1 : CPLError(CE_Failure, CPLE_AppDefined,
9534 : "Data type of %s array is not numeric",
9535 1 : poArray->GetName().c_str());
9536 1 : return nullptr;
9537 : }
9538 8 : else if (poAttribute &&
9539 2 : poAttribute->GetDataType().GetClass() !=
9540 6 : GEDTC_NUMERIC)
9541 : {
9542 0 : CPLError(CE_Failure, CPLE_AppDefined,
9543 : "Data type of %s attribute is not numeric",
9544 0 : poAttribute->GetFullName().c_str());
9545 0 : return nullptr;
9546 : }
9547 : }
9548 : }
9549 62 : else if (osDefinition[k] == '$' &&
9550 62 : k + 1 < osDefinition.size() &&
9551 7 : osDefinition[k + 1] == '{')
9552 : {
9553 7 : const auto nPos = osDefinition.find('}', k);
9554 7 : if (nPos == std::string::npos)
9555 : {
9556 1 : CPLError(CE_Failure, CPLE_AppDefined,
9557 : "Value of "
9558 : "BAND_METADATA[%d][\"item_value\"] = "
9559 : "%s is invalid at offset %d",
9560 : j, osDefinition.c_str(), int(k));
9561 3 : return nullptr;
9562 : }
9563 : const auto osAttrName =
9564 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9565 0 : std::shared_ptr<GDALAttribute> poAttr;
9566 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9567 : {
9568 4 : poAttr = poArray->GetAttribute(osAttrName);
9569 4 : if (!poAttr)
9570 : {
9571 1 : CPLError(
9572 : CE_Failure, CPLE_AppDefined,
9573 : "Value of "
9574 : "BAND_METADATA[%d][\"item_value\"] = "
9575 : "%s is invalid: %s is not an attribute of %s",
9576 : j, osDefinition.c_str(), osAttrName.c_str(),
9577 1 : poArray->GetName().c_str());
9578 1 : return nullptr;
9579 : }
9580 : }
9581 : else
9582 : {
9583 : poAttr =
9584 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9585 2 : if (!poAttr)
9586 : {
9587 1 : CPLError(CE_Failure, CPLE_AppDefined,
9588 : "Value of "
9589 : "BAND_METADATA[%d][\"item_value\"] = "
9590 : "%s is invalid: %s is not an attribute",
9591 : j, osDefinition.c_str(),
9592 : osAttrName.c_str());
9593 1 : return nullptr;
9594 : }
9595 : }
9596 4 : k = nPos;
9597 4 : const char *pszValue = poAttr->ReadAsString();
9598 4 : if (!pszValue)
9599 : {
9600 0 : CPLError(CE_Failure, CPLE_AppDefined,
9601 : "Cannot get value of attribute %s as a "
9602 : "string",
9603 : osAttrName.c_str());
9604 0 : return nullptr;
9605 : }
9606 4 : osModDefinition += pszValue;
9607 : }
9608 : else
9609 : {
9610 48 : osModDefinition += osDefinition[k];
9611 : }
9612 : }
9613 :
9614 9 : if (poArray)
9615 8 : oItem.poArray = std::move(poArray);
9616 : else
9617 1 : oItem.poArray = std::move(poAttribute);
9618 9 : oItem.osDefinition = std::move(osModDefinition);
9619 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9620 :
9621 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9622 9 : std::move(oItem));
9623 : }
9624 : }
9625 :
9626 450 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9627 : const char *pszBandImageryMetadata =
9628 225 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9629 225 : if (pszBandImageryMetadata)
9630 : {
9631 20 : if (!poRootGroup)
9632 : {
9633 1 : CPLError(CE_Failure, CPLE_AppDefined,
9634 : "Root group should be provided when BAND_IMAGERY_METADATA "
9635 : "is set");
9636 17 : return nullptr;
9637 : }
9638 19 : CPLJSONDocument oDoc;
9639 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9640 : {
9641 1 : CPLError(CE_Failure, CPLE_AppDefined,
9642 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9643 1 : return nullptr;
9644 : }
9645 18 : auto oRoot = oDoc.GetRoot();
9646 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9647 : {
9648 1 : CPLError(CE_Failure, CPLE_AppDefined,
9649 : "Value of BAND_IMAGERY_METADATA should be an object");
9650 1 : return nullptr;
9651 : }
9652 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9653 : {
9654 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9655 20 : oJsonItem.GetName() == "FWHM_UM")
9656 : {
9657 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9658 : const auto osBandAttributeName =
9659 34 : oJsonItem.GetString("attribute");
9660 0 : std::shared_ptr<GDALMDArray> poArray;
9661 0 : std::shared_ptr<GDALAttribute> poAttribute;
9662 17 : size_t iExtraDimIdx = 0;
9663 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9664 : {
9665 2 : CPLError(CE_Failure, CPLE_AppDefined,
9666 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9667 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9668 : "missing",
9669 2 : oJsonItem.GetName().c_str(),
9670 2 : oJsonItem.GetName().c_str());
9671 1 : return nullptr;
9672 : }
9673 25 : else if (!osBandArrayFullname.empty() &&
9674 9 : !osBandAttributeName.empty())
9675 : {
9676 2 : CPLError(CE_Failure, CPLE_AppDefined,
9677 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9678 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9679 : "mutually exclusive",
9680 2 : oJsonItem.GetName().c_str(),
9681 2 : oJsonItem.GetName().c_str());
9682 1 : return nullptr;
9683 : }
9684 15 : else if (!osBandArrayFullname.empty())
9685 : {
9686 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9687 8 : osBandArrayFullname);
9688 8 : if (!poArray)
9689 : {
9690 1 : CPLError(CE_Failure, CPLE_AppDefined,
9691 : "Array %s cannot be found",
9692 : osBandArrayFullname.c_str());
9693 3 : return nullptr;
9694 : }
9695 7 : if (poArray->GetDimensionCount() != 1)
9696 : {
9697 1 : CPLError(CE_Failure, CPLE_AppDefined,
9698 : "Array %s is not a 1D array",
9699 : osBandArrayFullname.c_str());
9700 1 : return nullptr;
9701 : }
9702 : const auto &osAuxArrayDimName =
9703 6 : poArray->GetDimensions()[0]->GetName();
9704 : auto oIter =
9705 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9706 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9707 : {
9708 1 : CPLError(CE_Failure, CPLE_AppDefined,
9709 : "Dimension \"%s\" of array \"%s\" is not a "
9710 : "non-X/Y dimension of array \"%s\"",
9711 : osAuxArrayDimName.c_str(),
9712 : osBandArrayFullname.c_str(),
9713 1 : array->GetName().c_str());
9714 1 : return nullptr;
9715 : }
9716 5 : iExtraDimIdx = oIter->second;
9717 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9718 : }
9719 : else
9720 : {
9721 : poAttribute =
9722 7 : !osBandAttributeName.empty() &&
9723 7 : osBandAttributeName[0] == '/'
9724 16 : ? poRootGroup->OpenAttributeFromFullname(
9725 : osBandAttributeName)
9726 7 : : array->GetAttribute(osBandAttributeName);
9727 7 : if (!poAttribute)
9728 : {
9729 2 : CPLError(CE_Failure, CPLE_AppDefined,
9730 : "Attribute %s cannot be found",
9731 : osBandAttributeName.c_str());
9732 6 : return nullptr;
9733 : }
9734 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9735 5 : if (aoAttrDims.size() != 1)
9736 : {
9737 2 : CPLError(CE_Failure, CPLE_AppDefined,
9738 : "Attribute %s is not a 1D array",
9739 : osBandAttributeName.c_str());
9740 2 : return nullptr;
9741 : }
9742 3 : bool found = false;
9743 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9744 : {
9745 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9746 4 : ->GetSize() == aoAttrDims[0])
9747 : {
9748 3 : if (found)
9749 : {
9750 2 : CPLError(CE_Failure, CPLE_AppDefined,
9751 : "Several dimensions of %s have the "
9752 : "same size as attribute %s. Cannot "
9753 : "infer which one to bind to!",
9754 1 : array->GetName().c_str(),
9755 : osBandAttributeName.c_str());
9756 1 : return nullptr;
9757 : }
9758 2 : found = true;
9759 2 : iExtraDimIdx = iter.second;
9760 : }
9761 : }
9762 2 : if (!found)
9763 : {
9764 2 : CPLError(CE_Failure, CPLE_AppDefined,
9765 : "No dimension of %s has the same size as "
9766 : "attribute %s",
9767 1 : array->GetName().c_str(),
9768 : osBandAttributeName.c_str());
9769 1 : return nullptr;
9770 : }
9771 : }
9772 :
9773 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9774 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9775 : {
9776 4 : if (osUnit.back() != '}')
9777 : {
9778 2 : CPLError(CE_Failure, CPLE_AppDefined,
9779 : "Value of "
9780 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9781 : "%s is invalid",
9782 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9783 2 : return nullptr;
9784 : }
9785 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9786 0 : std::shared_ptr<GDALAttribute> poAttr;
9787 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9788 : {
9789 2 : poAttr = poArray->GetAttribute(osAttrName);
9790 2 : if (!poAttr)
9791 : {
9792 2 : CPLError(
9793 : CE_Failure, CPLE_AppDefined,
9794 : "Value of "
9795 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9796 : "%s is invalid: %s is not an attribute of %s",
9797 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9798 : osAttrName.c_str(),
9799 : osBandArrayFullname.c_str());
9800 1 : return nullptr;
9801 : }
9802 : }
9803 : else
9804 : {
9805 : poAttr =
9806 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9807 1 : if (!poAttr)
9808 : {
9809 0 : CPLError(
9810 : CE_Failure, CPLE_AppDefined,
9811 : "Value of "
9812 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9813 : "%s is invalid: %s is not an attribute",
9814 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9815 : osAttrName.c_str());
9816 0 : return nullptr;
9817 : }
9818 : }
9819 :
9820 2 : const char *pszValue = poAttr->ReadAsString();
9821 2 : if (!pszValue)
9822 : {
9823 0 : CPLError(CE_Failure, CPLE_AppDefined,
9824 : "Cannot get value of attribute %s of %s as a "
9825 : "string",
9826 : osAttrName.c_str(),
9827 : osBandArrayFullname.c_str());
9828 0 : return nullptr;
9829 : }
9830 2 : osUnit = pszValue;
9831 : }
9832 4 : double dfConvToUM = 1.0;
9833 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9834 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9835 3 : osUnit == "nanometers")
9836 : {
9837 1 : dfConvToUM = 1e-3;
9838 : }
9839 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9840 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9841 1 : osUnit == "micrometers"))
9842 : {
9843 2 : CPLError(CE_Failure, CPLE_AppDefined,
9844 : "Unhandled value for "
9845 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9846 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9847 1 : return nullptr;
9848 : }
9849 :
9850 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9851 :
9852 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9853 3 : if (poArray)
9854 2 : abstractArray = std::move(poArray);
9855 : else
9856 1 : abstractArray = std::move(poAttribute);
9857 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9858 : {
9859 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9860 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9861 : }
9862 : else
9863 : {
9864 1 : item.poFWHMArray = std::move(abstractArray);
9865 1 : item.dfFWHMToMicrometer = dfConvToUM;
9866 : }
9867 : }
9868 : else
9869 : {
9870 1 : CPLError(CE_Warning, CPLE_AppDefined,
9871 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9872 2 : oJsonItem.GetName().c_str());
9873 : }
9874 : }
9875 : }
9876 :
9877 416 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9878 :
9879 208 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9880 :
9881 208 : poDS->nRasterYSize =
9882 208 : nDimCount < 2 ? 1
9883 195 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9884 195 : dims[iYDim]->GetSize()));
9885 416 : poDS->nRasterXSize = static_cast<int>(
9886 208 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9887 :
9888 416 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9889 416 : std::vector<GUInt64> anStackIters(nDimCount);
9890 416 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9891 658 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9892 : {
9893 450 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9894 : {
9895 47 : anMapNewToOld[j] = i;
9896 47 : j++;
9897 : }
9898 : }
9899 :
9900 208 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9901 :
9902 416 : const auto attrs(array->GetAttributes());
9903 321 : for (const auto &attr : attrs)
9904 : {
9905 113 : if (attr->GetName() != "COLOR_INTERPRETATION")
9906 : {
9907 204 : auto stringArray = attr->ReadAsStringArray();
9908 204 : std::string val;
9909 102 : if (stringArray.size() > 1)
9910 : {
9911 44 : val += '{';
9912 : }
9913 452 : for (int i = 0; i < stringArray.size(); ++i)
9914 : {
9915 350 : if (i > 0)
9916 248 : val += ',';
9917 350 : val += stringArray[i];
9918 : }
9919 102 : if (stringArray.size() > 1)
9920 : {
9921 44 : val += '}';
9922 : }
9923 102 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9924 : }
9925 : }
9926 :
9927 208 : const char *pszDelay = CSLFetchNameValueDef(
9928 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9929 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9930 : const double dfDelay =
9931 208 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9932 208 : const auto nStartTime = time(nullptr);
9933 208 : bool bHasWarned = false;
9934 : // Instantiate bands by iterating over non-XY variables
9935 208 : size_t iDim = 0;
9936 208 : int nCurBand = 1;
9937 331 : lbl_next_depth:
9938 331 : if (iDim < nNewDimCount)
9939 : {
9940 49 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9941 49 : anOtherDimCoord[iDim] = 0;
9942 : while (true)
9943 : {
9944 123 : ++iDim;
9945 123 : goto lbl_next_depth;
9946 123 : lbl_return_to_caller:
9947 123 : --iDim;
9948 123 : --anStackIters[iDim];
9949 123 : if (anStackIters[iDim] == 0)
9950 49 : break;
9951 74 : ++anOtherDimCoord[iDim];
9952 : }
9953 : }
9954 : else
9955 : {
9956 564 : poDS->SetBand(nCurBand,
9957 : new GDALRasterBandFromArray(
9958 282 : poDS.get(), anOtherDimCoord,
9959 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9960 282 : dfDelay, nStartTime, bHasWarned));
9961 282 : ++nCurBand;
9962 : }
9963 331 : if (iDim > 0)
9964 123 : goto lbl_return_to_caller;
9965 :
9966 208 : if (!array->GetFilename().empty())
9967 : {
9968 183 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9969 : std::string osDerivedDatasetName(
9970 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9971 366 : int(iYDim), array->GetFullName().c_str()));
9972 183 : if (!array->GetContext().empty())
9973 : {
9974 2 : osDerivedDatasetName += " with context ";
9975 2 : osDerivedDatasetName += array->GetContext();
9976 : }
9977 183 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9978 183 : poDS->TryLoadXML();
9979 :
9980 2 : for (const auto &[pszKey, pszValue] :
9981 : cpl::IterateNameValue(static_cast<CSLConstList>(
9982 185 : poDS->GDALPamDataset::GetMetadata())))
9983 : {
9984 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9985 : }
9986 : }
9987 :
9988 208 : return poDS.release();
9989 : }
9990 :
9991 : /************************************************************************/
9992 : /* AsClassicDataset() */
9993 : /************************************************************************/
9994 :
9995 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9996 : *
9997 : * In the case of > 2D arrays, additional dimensions will be represented as
9998 : * raster bands.
9999 : *
10000 : * The "reverse" method is GDALRasterBand::AsMDArray().
10001 : *
10002 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10003 : *
10004 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10005 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10006 : * Ignored if the dimension count is 1.
10007 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10008 : * and BAND_IMAGERY_METADATA option.
10009 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10010 : * nullptr. Current supported options are:
10011 : * <ul>
10012 : * <li>BAND_METADATA: JSON serialized array defining which
10013 : * arrays of the poRootGroup, indexed by non-X and Y
10014 : * dimensions, should be mapped as band metadata items.
10015 : * Each array item should be an object with the
10016 : * following members:
10017 : * - "array": full name of a band parameter array.
10018 : * Such array must be a one
10019 : * dimensional array, and its dimension must be one of
10020 : * the dimensions of the array on which the method is
10021 : * called (excluding the X and Y dimensions).
10022 : * - "attribute": name relative to *this array or full
10023 : * name of a single dimension numeric array whose size
10024 : * must be one of the dimensions of *this array
10025 : * (excluding the X and Y dimensions).
10026 : * "array" and "attribute" are mutually exclusive.
10027 : * - "item_name": band metadata item name
10028 : * - "item_value": (optional) String, where "%[x][.y]f",
10029 : * "%[x][.y]g" or "%s" printf-like formatting can be
10030 : * used to format the corresponding value of the
10031 : * parameter array. The percentage character should be
10032 : * repeated: "%%"
10033 : * "${attribute_name}" can also be used to include the
10034 : * value of an attribute for "array" when set and if
10035 : * not starting with '/'. Otherwise if starting with
10036 : * '/', it is the full path to the attribute.
10037 : *
10038 : * If "item_value" is not provided, a default formatting
10039 : * of the value will be applied.
10040 : *
10041 : * Example:
10042 : * [
10043 : * {
10044 : * "array": "/sensor_band_parameters/wavelengths",
10045 : * "item_name": "WAVELENGTH",
10046 : * "item_value": "%.1f ${units}"
10047 : * },
10048 : * {
10049 : * "array": "/sensor_band_parameters/fwhm",
10050 : * "item_name": "FWHM"
10051 : * },
10052 : * {
10053 : * "array": "/sensor_band_parameters/fwhm",
10054 : * "item_name": "FWHM_UNIT",
10055 : * "item_value": "${units}"
10056 : * }
10057 : * ]
10058 : *
10059 : * Example for Planet Labs Tanager radiance products:
10060 : * [
10061 : * {
10062 : * "attribute": "center_wavelengths",
10063 : * "item_name": "WAVELENGTH",
10064 : * "item_value": "%.1f ${center_wavelengths_units}"
10065 : * }
10066 : * ]
10067 : *
10068 : * </li>
10069 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10070 : * JSON serialized object defining which arrays of the
10071 : * poRootGroup, indexed by non-X and Y dimensions,
10072 : * should be mapped as band metadata items in the
10073 : * band IMAGERY domain.
10074 : * The object currently accepts 2 members:
10075 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10076 : * micrometers.
10077 : * - "FWHM_UM": Full-width half-maximum
10078 : * in micrometers.
10079 : * The value of each member should be an object with the
10080 : * following members:
10081 : * - "array": full name of a band parameter array.
10082 : * Such array must be a one dimensional array, and its
10083 : * dimension must be one of the dimensions of the
10084 : * array on which the method is called
10085 : * (excluding the X and Y dimensions).
10086 : * - "attribute": name relative to *this array or full
10087 : * name of a single dimension numeric array whose size
10088 : * must be one of the dimensions of *this array
10089 : * (excluding the X and Y dimensions).
10090 : * "array" and "attribute" are mutually exclusive,
10091 : * and one of them is required.
10092 : * - "unit": (optional) unit of the values pointed in
10093 : * the above array.
10094 : * Can be a literal string or a string of the form
10095 : * "${attribute_name}" to point to an attribute for
10096 : * "array" when set and if no starting
10097 : * with '/'. Otherwise if starting with '/', it is
10098 : * the full path to the attribute.
10099 : * Accepted values are "um", "micrometer"
10100 : * (with UK vs US spelling, singular or plural), "nm",
10101 : * "nanometer" (with UK vs US spelling, singular or
10102 : * plural)
10103 : * If not provided, micrometer is assumed.
10104 : *
10105 : * Example for EMIT datasets:
10106 : * {
10107 : * "CENTRAL_WAVELENGTH_UM": {
10108 : * "array": "/sensor_band_parameters/wavelengths",
10109 : * "unit": "${units}"
10110 : * },
10111 : * "FWHM_UM": {
10112 : * "array": "/sensor_band_parameters/fwhm",
10113 : * "unit": "${units}"
10114 : * }
10115 : * }
10116 : *
10117 : * Example for Planet Labs Tanager radiance products:
10118 : * {
10119 : * "CENTRAL_WAVELENGTH_UM": {
10120 : * "attribute": "center_wavelengths",
10121 : * "unit": "${center_wavelengths_units}"
10122 : * },
10123 : * "FWHM_UM": {
10124 : * "attribute": "fwhm",
10125 : * "unit": "${fwhm_units}"
10126 : * }
10127 : * }
10128 : *
10129 : * </li>
10130 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10131 : * seconds allowed to set the DIM_{dimname}_VALUE band
10132 : * metadata items from the indexing variable of the
10133 : * dimensions.
10134 : * Default value is 5. 'unlimited' can be used to mean
10135 : * unlimited delay. Can also be defined globally with
10136 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10137 : * option.</li>
10138 : * </ul>
10139 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10140 : */
10141 : GDALDataset *
10142 260 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10143 : const std::shared_ptr<GDALGroup> &poRootGroup,
10144 : CSLConstList papszOptions) const
10145 : {
10146 520 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10147 260 : if (!self)
10148 : {
10149 0 : CPLError(CE_Failure, CPLE_AppDefined,
10150 : "Driver implementation issue: m_pSelf not set !");
10151 0 : return nullptr;
10152 : }
10153 260 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10154 260 : papszOptions);
10155 : }
10156 :
10157 : /************************************************************************/
10158 : /* GetStatistics() */
10159 : /************************************************************************/
10160 :
10161 : /**
10162 : * \brief Fetch statistics.
10163 : *
10164 : * Returns the minimum, maximum, mean and standard deviation of all
10165 : * pixel values in this array.
10166 : *
10167 : * If bForce is FALSE results will only be returned if it can be done
10168 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10169 : * results cannot be returned efficiently, the method will return CE_Warning
10170 : * but no warning will have been issued. This is a non-standard use of
10171 : * the CE_Warning return value to indicate "nothing done".
10172 : *
10173 : * When cached statistics are not available, and bForce is TRUE,
10174 : * ComputeStatistics() is called.
10175 : *
10176 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10177 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10178 : * after the first request.
10179 : *
10180 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10181 : *
10182 : * This method is the same as the C function GDALMDArrayGetStatistics().
10183 : *
10184 : * @param bApproxOK Currently ignored. In the future, should be set to true
10185 : * if statistics on the whole array are wished, or to false if a subset of it
10186 : * may be used.
10187 : *
10188 : * @param bForce If false statistics will only be returned if it can
10189 : * be done without rescanning the image.
10190 : *
10191 : * @param pdfMin Location into which to load image minimum (may be NULL).
10192 : *
10193 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10194 : *
10195 : * @param pdfMean Location into which to load image mean (may be NULL).
10196 : *
10197 : * @param pdfStdDev Location into which to load image standard deviation
10198 : * (may be NULL).
10199 : *
10200 : * @param pnValidCount Number of samples whose value is different from the
10201 : * nodata value. (may be NULL)
10202 : *
10203 : * @param pfnProgress a function to call to report progress, or NULL.
10204 : *
10205 : * @param pProgressData application data to pass to the progress function.
10206 : *
10207 : * @return CE_None on success, CE_Warning if no values returned,
10208 : * CE_Failure if an error occurs.
10209 : *
10210 : * @since GDAL 3.2
10211 : */
10212 :
10213 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10214 : double *pdfMax, double *pdfMean,
10215 : double *pdfStdDev, GUInt64 *pnValidCount,
10216 : GDALProgressFunc pfnProgress,
10217 : void *pProgressData)
10218 : {
10219 10 : if (!bForce)
10220 1 : return CE_Warning;
10221 :
10222 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10223 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10224 9 : ? CE_None
10225 9 : : CE_Failure;
10226 : }
10227 :
10228 : /************************************************************************/
10229 : /* ComputeStatistics() */
10230 : /************************************************************************/
10231 :
10232 : /**
10233 : * \brief Compute statistics.
10234 : *
10235 : * Returns the minimum, maximum, mean and standard deviation of all
10236 : * pixel values in this array.
10237 : *
10238 : * Pixels taken into account in statistics are those whose mask value
10239 : * (as determined by GetMask()) is non-zero.
10240 : *
10241 : * Once computed, the statistics will generally be "set" back on the
10242 : * owing dataset.
10243 : *
10244 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10245 : *
10246 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10247 : * and GDALMDArrayComputeStatisticsEx().
10248 : *
10249 : * @param bApproxOK Currently ignored. In the future, should be set to true
10250 : * if statistics on the whole array are wished, or to false if a subset of it
10251 : * may be used.
10252 : *
10253 : * @param pdfMin Location into which to load image minimum (may be NULL).
10254 : *
10255 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10256 : *
10257 : * @param pdfMean Location into which to load image mean (may be NULL).
10258 : *
10259 : * @param pdfStdDev Location into which to load image standard deviation
10260 : * (may be NULL).
10261 : *
10262 : * @param pnValidCount Number of samples whose value is different from the
10263 : * nodata value. (may be NULL)
10264 : *
10265 : * @param pfnProgress a function to call to report progress, or NULL.
10266 : *
10267 : * @param pProgressData application data to pass to the progress function.
10268 : *
10269 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10270 : * Options are driver specific. For now the netCDF and Zarr
10271 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10272 : * to add or update the actual_range attribute with the
10273 : * computed min/max, only if done on the full array, in non
10274 : * approximate mode, and the dataset is opened in update
10275 : * mode.
10276 : *
10277 : * @return true on success
10278 : *
10279 : * @since GDAL 3.2
10280 : */
10281 :
10282 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10283 : double *pdfMax, double *pdfMean,
10284 : double *pdfStdDev, GUInt64 *pnValidCount,
10285 : GDALProgressFunc pfnProgress,
10286 : void *pProgressData,
10287 : CSLConstList papszOptions)
10288 : {
10289 : struct StatsPerChunkType
10290 : {
10291 : const GDALMDArray *array = nullptr;
10292 : std::shared_ptr<GDALMDArray> poMask{};
10293 : double dfMin = cpl::NumericLimits<double>::max();
10294 : double dfMax = -cpl::NumericLimits<double>::max();
10295 : double dfMean = 0.0;
10296 : double dfM2 = 0.0;
10297 : GUInt64 nValidCount = 0;
10298 : std::vector<GByte> abyData{};
10299 : std::vector<double> adfData{};
10300 : std::vector<GByte> abyMaskData{};
10301 : GDALProgressFunc pfnProgress = nullptr;
10302 : void *pProgressData = nullptr;
10303 : };
10304 :
10305 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10306 : const GUInt64 *chunkArrayStartIdx,
10307 : const size_t *chunkCount, GUInt64 iCurChunk,
10308 : GUInt64 nChunkCount, void *pUserData)
10309 : {
10310 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10311 13 : const GDALMDArray *array = data->array;
10312 13 : const GDALMDArray *poMask = data->poMask.get();
10313 13 : const size_t nDims = array->GetDimensionCount();
10314 13 : size_t nVals = 1;
10315 34 : for (size_t i = 0; i < nDims; i++)
10316 21 : nVals *= chunkCount[i];
10317 :
10318 : // Get mask
10319 13 : data->abyMaskData.resize(nVals);
10320 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10321 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10322 : {
10323 0 : return false;
10324 : }
10325 :
10326 : // Get data
10327 13 : const auto &oType = array->GetDataType();
10328 13 : if (oType.GetNumericDataType() == GDT_Float64)
10329 : {
10330 6 : data->adfData.resize(nVals);
10331 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10332 6 : oType, &data->adfData[0]))
10333 : {
10334 0 : return false;
10335 : }
10336 : }
10337 : else
10338 : {
10339 7 : data->abyData.resize(nVals * oType.GetSize());
10340 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10341 7 : oType, &data->abyData[0]))
10342 : {
10343 0 : return false;
10344 : }
10345 7 : data->adfData.resize(nVals);
10346 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10347 7 : static_cast<int>(oType.GetSize()),
10348 7 : &data->adfData[0], GDT_Float64,
10349 : static_cast<int>(sizeof(double)),
10350 : static_cast<GPtrDiff_t>(nVals));
10351 : }
10352 912 : for (size_t i = 0; i < nVals; i++)
10353 : {
10354 899 : if (data->abyMaskData[i])
10355 : {
10356 894 : const double dfValue = data->adfData[i];
10357 894 : data->dfMin = std::min(data->dfMin, dfValue);
10358 894 : data->dfMax = std::max(data->dfMax, dfValue);
10359 894 : data->nValidCount++;
10360 894 : const double dfDelta = dfValue - data->dfMean;
10361 894 : data->dfMean += dfDelta / data->nValidCount;
10362 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10363 : }
10364 : }
10365 13 : if (data->pfnProgress &&
10366 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10367 : "", data->pProgressData))
10368 : {
10369 0 : return false;
10370 : }
10371 13 : return true;
10372 : };
10373 :
10374 13 : const auto &oType = GetDataType();
10375 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10376 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10377 : {
10378 0 : CPLError(
10379 : CE_Failure, CPLE_NotSupported,
10380 : "Statistics can only be computed on non-complex numeric data type");
10381 0 : return false;
10382 : }
10383 :
10384 13 : const size_t nDims = GetDimensionCount();
10385 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10386 26 : std::vector<GUInt64> count(nDims);
10387 13 : const auto &poDims = GetDimensions();
10388 34 : for (size_t i = 0; i < nDims; i++)
10389 : {
10390 21 : count[i] = poDims[i]->GetSize();
10391 : }
10392 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10393 : const size_t nMaxChunkSize =
10394 : pszSwathSize
10395 13 : ? static_cast<size_t>(
10396 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10397 0 : CPLAtoGIntBig(pszSwathSize)))
10398 : : static_cast<size_t>(
10399 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10400 13 : GDALGetCacheMax64() / 4));
10401 26 : StatsPerChunkType sData;
10402 13 : sData.array = this;
10403 13 : sData.poMask = GetMask(nullptr);
10404 13 : if (sData.poMask == nullptr)
10405 : {
10406 0 : return false;
10407 : }
10408 13 : sData.pfnProgress = pfnProgress;
10409 13 : sData.pProgressData = pProgressData;
10410 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10411 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10412 13 : PerChunkFunc, &sData))
10413 : {
10414 0 : return false;
10415 : }
10416 :
10417 13 : if (pdfMin)
10418 13 : *pdfMin = sData.dfMin;
10419 :
10420 13 : if (pdfMax)
10421 13 : *pdfMax = sData.dfMax;
10422 :
10423 13 : if (pdfMean)
10424 11 : *pdfMean = sData.dfMean;
10425 :
10426 : const double dfStdDev =
10427 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10428 13 : if (pdfStdDev)
10429 11 : *pdfStdDev = dfStdDev;
10430 :
10431 13 : if (pnValidCount)
10432 11 : *pnValidCount = sData.nValidCount;
10433 :
10434 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10435 13 : sData.nValidCount, papszOptions);
10436 :
10437 13 : return true;
10438 : }
10439 :
10440 : /************************************************************************/
10441 : /* SetStatistics() */
10442 : /************************************************************************/
10443 : //! @cond Doxygen_Suppress
10444 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10445 : double /* dfMax */, double /* dfMean */,
10446 : double /* dfStdDev */,
10447 : GUInt64 /* nValidCount */,
10448 : CSLConstList /* papszOptions */)
10449 : {
10450 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10451 5 : return false;
10452 : }
10453 :
10454 : //! @endcond
10455 :
10456 : /************************************************************************/
10457 : /* ClearStatistics() */
10458 : /************************************************************************/
10459 :
10460 : /**
10461 : * \brief Clear statistics.
10462 : *
10463 : * @since GDAL 3.4
10464 : */
10465 0 : void GDALMDArray::ClearStatistics()
10466 : {
10467 0 : }
10468 :
10469 : /************************************************************************/
10470 : /* GetCoordinateVariables() */
10471 : /************************************************************************/
10472 :
10473 : /**
10474 : * \brief Return coordinate variables.
10475 : *
10476 : * Coordinate variables are an alternate way of indexing an array that can
10477 : * be sometimes used. For example, an array collected through remote sensing
10478 : * might be indexed by (scanline, pixel). But there can be
10479 : * a longitude and latitude arrays alongside that are also both indexed by
10480 : * (scanline, pixel), and are referenced from operational arrays for
10481 : * reprojection purposes.
10482 : *
10483 : * For netCDF, this will return the arrays referenced by the "coordinates"
10484 : * attribute.
10485 : *
10486 : * This method is the same as the C function
10487 : * GDALMDArrayGetCoordinateVariables().
10488 : *
10489 : * @return a vector of arrays
10490 : *
10491 : * @since GDAL 3.4
10492 : */
10493 :
10494 : std::vector<std::shared_ptr<GDALMDArray>>
10495 13 : GDALMDArray::GetCoordinateVariables() const
10496 : {
10497 13 : return {};
10498 : }
10499 :
10500 : /************************************************************************/
10501 : /* ~GDALExtendedDataType() */
10502 : /************************************************************************/
10503 :
10504 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10505 :
10506 : /************************************************************************/
10507 : /* GDALExtendedDataType() */
10508 : /************************************************************************/
10509 :
10510 9863 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10511 9863 : GDALExtendedDataTypeSubType eSubType)
10512 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10513 9863 : m_nMaxStringLength(nMaxStringLength)
10514 : {
10515 9863 : }
10516 :
10517 : /************************************************************************/
10518 : /* GDALExtendedDataType() */
10519 : /************************************************************************/
10520 :
10521 38020 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10522 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10523 38020 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10524 : {
10525 38020 : }
10526 :
10527 : /************************************************************************/
10528 : /* GDALExtendedDataType() */
10529 : /************************************************************************/
10530 :
10531 63 : GDALExtendedDataType::GDALExtendedDataType(
10532 : const std::string &osName, GDALDataType eBaseType,
10533 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10534 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10535 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10536 : {
10537 63 : }
10538 :
10539 : /************************************************************************/
10540 : /* GDALExtendedDataType() */
10541 : /************************************************************************/
10542 :
10543 871 : GDALExtendedDataType::GDALExtendedDataType(
10544 : const std::string &osName, size_t nTotalSize,
10545 871 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10546 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10547 871 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10548 : {
10549 871 : }
10550 :
10551 : /************************************************************************/
10552 : /* GDALExtendedDataType() */
10553 : /************************************************************************/
10554 :
10555 : /** Move constructor. */
10556 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10557 :
10558 : /************************************************************************/
10559 : /* GDALExtendedDataType() */
10560 : /************************************************************************/
10561 :
10562 : /** Copy constructor. */
10563 16175 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10564 32350 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10565 16175 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10566 16175 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10567 16175 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10568 : {
10569 16175 : if (m_eClass == GEDTC_COMPOUND)
10570 : {
10571 481 : for (const auto &elt : other.m_aoComponents)
10572 : {
10573 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10574 : }
10575 : }
10576 16175 : }
10577 :
10578 : /************************************************************************/
10579 : /* operator= () */
10580 : /************************************************************************/
10581 :
10582 : /** Copy assignment. */
10583 : GDALExtendedDataType &
10584 1066 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10585 : {
10586 1066 : if (this != &other)
10587 : {
10588 1066 : m_osName = other.m_osName;
10589 1066 : m_eClass = other.m_eClass;
10590 1066 : m_eSubType = other.m_eSubType;
10591 1066 : m_eNumericDT = other.m_eNumericDT;
10592 1066 : m_nSize = other.m_nSize;
10593 1066 : m_nMaxStringLength = other.m_nMaxStringLength;
10594 1066 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10595 1066 : m_aoComponents.clear();
10596 1066 : if (m_eClass == GEDTC_COMPOUND)
10597 : {
10598 0 : for (const auto &elt : other.m_aoComponents)
10599 : {
10600 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10601 : }
10602 : }
10603 : }
10604 1066 : return *this;
10605 : }
10606 :
10607 : /************************************************************************/
10608 : /* operator= () */
10609 : /************************************************************************/
10610 :
10611 : /** Move assignment. */
10612 : GDALExtendedDataType &
10613 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10614 :
10615 : /************************************************************************/
10616 : /* Create() */
10617 : /************************************************************************/
10618 :
10619 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10620 : *
10621 : * This is the same as the C function GDALExtendedDataTypeCreate()
10622 : *
10623 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10624 : * GDT_TypeCount
10625 : */
10626 38013 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10627 : {
10628 38013 : return GDALExtendedDataType(eType);
10629 : }
10630 :
10631 : /************************************************************************/
10632 : /* Create() */
10633 : /************************************************************************/
10634 :
10635 : /** Return a new GDALExtendedDataType from a raster attribute table.
10636 : *
10637 : * @param osName Type name
10638 : * @param eBaseType Base integer data type.
10639 : * @param poRAT Raster attribute table. Must not be NULL.
10640 : * @since 3.12
10641 : */
10642 : GDALExtendedDataType
10643 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10644 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10645 : {
10646 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10647 : }
10648 :
10649 : /************************************************************************/
10650 : /* Create() */
10651 : /************************************************************************/
10652 :
10653 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10654 : *
10655 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10656 : *
10657 : * @param osName Type name.
10658 : * @param nTotalSize Total size of the type in bytes.
10659 : * Should be large enough to store all components.
10660 : * @param components Components of the compound type.
10661 : */
10662 878 : GDALExtendedDataType GDALExtendedDataType::Create(
10663 : const std::string &osName, size_t nTotalSize,
10664 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10665 : {
10666 878 : size_t nLastOffset = 0;
10667 : // Some arbitrary threshold to avoid potential integer overflows
10668 878 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10669 : {
10670 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10671 2 : return GDALExtendedDataType(GDT_Unknown);
10672 : }
10673 4138 : for (const auto &comp : components)
10674 : {
10675 : // Check alignment too ?
10676 3263 : if (comp->GetOffset() < nLastOffset)
10677 : {
10678 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10679 1 : return GDALExtendedDataType(GDT_Unknown);
10680 : }
10681 3262 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10682 : }
10683 875 : if (nTotalSize < nLastOffset)
10684 : {
10685 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10686 1 : return GDALExtendedDataType(GDT_Unknown);
10687 : }
10688 874 : if (nTotalSize == 0 || components.empty())
10689 : {
10690 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10691 3 : return GDALExtendedDataType(GDT_Unknown);
10692 : }
10693 871 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10694 : }
10695 :
10696 : /************************************************************************/
10697 : /* Create() */
10698 : /************************************************************************/
10699 :
10700 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10701 : *
10702 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10703 : *
10704 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10705 : * unknown/unlimited
10706 : * @param eSubType Subtype.
10707 : */
10708 : GDALExtendedDataType
10709 9863 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10710 : GDALExtendedDataTypeSubType eSubType)
10711 : {
10712 9863 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10713 : }
10714 :
10715 : /************************************************************************/
10716 : /* operator==() */
10717 : /************************************************************************/
10718 :
10719 : /** Equality operator.
10720 : *
10721 : * This is the same as the C function GDALExtendedDataTypeEquals().
10722 : */
10723 2767 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10724 : {
10725 2740 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10726 5507 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10727 : {
10728 258 : return false;
10729 : }
10730 2509 : if (m_eClass == GEDTC_NUMERIC)
10731 : {
10732 882 : return m_eNumericDT == other.m_eNumericDT;
10733 : }
10734 1627 : if (m_eClass == GEDTC_STRING)
10735 : {
10736 1409 : return true;
10737 : }
10738 218 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10739 218 : if (m_aoComponents.size() != other.m_aoComponents.size())
10740 : {
10741 2 : return false;
10742 : }
10743 1139 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10744 : {
10745 923 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10746 : {
10747 0 : return false;
10748 : }
10749 : }
10750 216 : return true;
10751 : }
10752 :
10753 : /************************************************************************/
10754 : /* CanConvertTo() */
10755 : /************************************************************************/
10756 :
10757 : /** Return whether this data type can be converted to the other one.
10758 : *
10759 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10760 : *
10761 : * @param other Target data type for the conversion being considered.
10762 : */
10763 8859 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10764 : {
10765 8859 : if (m_eClass == GEDTC_NUMERIC)
10766 : {
10767 6060 : if (m_eNumericDT == GDT_Unknown)
10768 0 : return false;
10769 6060 : if (other.m_eClass == GEDTC_NUMERIC &&
10770 5951 : other.m_eNumericDT == GDT_Unknown)
10771 0 : return false;
10772 6169 : return other.m_eClass == GEDTC_NUMERIC ||
10773 6169 : other.m_eClass == GEDTC_STRING;
10774 : }
10775 2799 : if (m_eClass == GEDTC_STRING)
10776 : {
10777 2613 : return other.m_eClass == m_eClass;
10778 : }
10779 186 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10780 186 : if (other.m_eClass != GEDTC_COMPOUND)
10781 0 : return false;
10782 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10783 372 : srcComponents;
10784 929 : for (const auto &srcComp : m_aoComponents)
10785 : {
10786 743 : srcComponents[srcComp->GetName()] = &srcComp;
10787 : }
10788 513 : for (const auto &dstComp : other.m_aoComponents)
10789 : {
10790 328 : auto oIter = srcComponents.find(dstComp->GetName());
10791 328 : if (oIter == srcComponents.end())
10792 1 : return false;
10793 327 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10794 0 : return false;
10795 : }
10796 185 : return true;
10797 : }
10798 :
10799 : /************************************************************************/
10800 : /* NeedsFreeDynamicMemory() */
10801 : /************************************************************************/
10802 :
10803 : /** Return whether the data type holds dynamically allocated memory, that
10804 : * needs to be freed with FreeDynamicMemory().
10805 : *
10806 : */
10807 3773 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10808 : {
10809 3773 : switch (m_eClass)
10810 : {
10811 959 : case GEDTC_STRING:
10812 959 : return true;
10813 :
10814 2704 : case GEDTC_NUMERIC:
10815 2704 : return false;
10816 :
10817 110 : case GEDTC_COMPOUND:
10818 : {
10819 223 : for (const auto &comp : m_aoComponents)
10820 : {
10821 209 : if (comp->GetType().NeedsFreeDynamicMemory())
10822 96 : return true;
10823 : }
10824 : }
10825 : }
10826 14 : return false;
10827 : }
10828 :
10829 : /************************************************************************/
10830 : /* FreeDynamicMemory() */
10831 : /************************************************************************/
10832 :
10833 : /** Release the dynamic memory (strings typically) from a raw value.
10834 : *
10835 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10836 : *
10837 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10838 : */
10839 3902 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10840 : {
10841 3902 : switch (m_eClass)
10842 : {
10843 2823 : case GEDTC_STRING:
10844 : {
10845 : char *pszStr;
10846 2823 : memcpy(&pszStr, pBuffer, sizeof(char *));
10847 2823 : if (pszStr)
10848 : {
10849 2265 : VSIFree(pszStr);
10850 : }
10851 2823 : break;
10852 : }
10853 :
10854 903 : case GEDTC_NUMERIC:
10855 : {
10856 903 : break;
10857 : }
10858 :
10859 176 : case GEDTC_COMPOUND:
10860 : {
10861 176 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10862 938 : for (const auto &comp : m_aoComponents)
10863 : {
10864 1524 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10865 762 : comp->GetOffset());
10866 : }
10867 176 : break;
10868 : }
10869 : }
10870 3902 : }
10871 :
10872 : /************************************************************************/
10873 : /* ~GDALEDTComponent() */
10874 : /************************************************************************/
10875 :
10876 : GDALEDTComponent::~GDALEDTComponent() = default;
10877 :
10878 : /************************************************************************/
10879 : /* GDALEDTComponent() */
10880 : /************************************************************************/
10881 :
10882 : /** constructor of a GDALEDTComponent
10883 : *
10884 : * This is the same as the C function GDALEDTComponendCreate()
10885 : *
10886 : * @param name Component name
10887 : * @param offset Offset in byte of the component in the compound data type.
10888 : * In case of nesting of compound data type, this should be
10889 : * the offset to the immediate belonging data type, not to the
10890 : * higher level one.
10891 : * @param type Component data type.
10892 : */
10893 3254 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10894 3254 : const GDALExtendedDataType &type)
10895 3254 : : m_osName(name), m_nOffset(offset), m_oType(type)
10896 : {
10897 3254 : }
10898 :
10899 : /************************************************************************/
10900 : /* GDALEDTComponent() */
10901 : /************************************************************************/
10902 :
10903 : /** Copy constructor. */
10904 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10905 :
10906 : /************************************************************************/
10907 : /* operator==() */
10908 : /************************************************************************/
10909 :
10910 : /** Equality operator.
10911 : */
10912 923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10913 : {
10914 1846 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10915 1846 : m_oType == other.m_oType;
10916 : }
10917 :
10918 : /************************************************************************/
10919 : /* ~GDALDimension() */
10920 : /************************************************************************/
10921 :
10922 : GDALDimension::~GDALDimension() = default;
10923 :
10924 : /************************************************************************/
10925 : /* GDALDimension() */
10926 : /************************************************************************/
10927 :
10928 : //! @cond Doxygen_Suppress
10929 : /** Constructor.
10930 : *
10931 : * @param osParentName Parent name
10932 : * @param osName name
10933 : * @param osType type. See GetType().
10934 : * @param osDirection direction. See GetDirection().
10935 : * @param nSize size.
10936 : */
10937 8729 : GDALDimension::GDALDimension(const std::string &osParentName,
10938 : const std::string &osName,
10939 : const std::string &osType,
10940 8729 : const std::string &osDirection, GUInt64 nSize)
10941 : : m_osName(osName),
10942 : m_osFullName(
10943 8729 : !osParentName.empty()
10944 12780 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10945 : : osName),
10946 30238 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10947 : {
10948 8729 : }
10949 :
10950 : //! @endcond
10951 :
10952 : /************************************************************************/
10953 : /* GetIndexingVariable() */
10954 : /************************************************************************/
10955 :
10956 : /** Return the variable that is used to index the dimension (if there is one).
10957 : *
10958 : * This is the array, typically one-dimensional, describing the values taken
10959 : * by the dimension.
10960 : */
10961 73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10962 : {
10963 73 : return nullptr;
10964 : }
10965 :
10966 : /************************************************************************/
10967 : /* SetIndexingVariable() */
10968 : /************************************************************************/
10969 :
10970 : /** Set the variable that is used to index the dimension.
10971 : *
10972 : * This is the array, typically one-dimensional, describing the values taken
10973 : * by the dimension.
10974 : *
10975 : * Optionally implemented by drivers.
10976 : *
10977 : * Drivers known to implement it: MEM.
10978 : *
10979 : * @param poArray Variable to use to index the dimension.
10980 : * @return true in case of success.
10981 : */
10982 3 : bool GDALDimension::SetIndexingVariable(
10983 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10984 : {
10985 3 : CPLError(CE_Failure, CPLE_NotSupported,
10986 : "SetIndexingVariable() not implemented");
10987 3 : return false;
10988 : }
10989 :
10990 : /************************************************************************/
10991 : /* Rename() */
10992 : /************************************************************************/
10993 :
10994 : /** Rename the dimension.
10995 : *
10996 : * This is not implemented by all drivers.
10997 : *
10998 : * Drivers known to implement it: MEM, netCDF, ZARR.
10999 : *
11000 : * This is the same as the C function GDALDimensionRename().
11001 : *
11002 : * @param osNewName New name.
11003 : *
11004 : * @return true in case of success
11005 : * @since GDAL 3.8
11006 : */
11007 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11008 : {
11009 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11010 0 : return false;
11011 : }
11012 :
11013 : /************************************************************************/
11014 : /* BaseRename() */
11015 : /************************************************************************/
11016 :
11017 : //! @cond Doxygen_Suppress
11018 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11019 : {
11020 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11021 8 : m_osFullName += osNewName;
11022 8 : m_osName = osNewName;
11023 8 : }
11024 :
11025 : //! @endcond
11026 :
11027 : //! @cond Doxygen_Suppress
11028 : /************************************************************************/
11029 : /* ParentRenamed() */
11030 : /************************************************************************/
11031 :
11032 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11033 : {
11034 8 : m_osFullName = osNewParentFullName;
11035 8 : m_osFullName += "/";
11036 8 : m_osFullName += m_osName;
11037 8 : }
11038 :
11039 : //! @endcond
11040 :
11041 : //! @cond Doxygen_Suppress
11042 : /************************************************************************/
11043 : /* ParentDeleted() */
11044 : /************************************************************************/
11045 :
11046 4 : void GDALDimension::ParentDeleted()
11047 : {
11048 4 : }
11049 :
11050 : //! @endcond
11051 :
11052 : /************************************************************************/
11053 : /************************************************************************/
11054 : /************************************************************************/
11055 : /* C API */
11056 : /************************************************************************/
11057 : /************************************************************************/
11058 : /************************************************************************/
11059 :
11060 : /************************************************************************/
11061 : /* GDALExtendedDataTypeCreate() */
11062 : /************************************************************************/
11063 :
11064 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11065 : *
11066 : * This is the same as the C++ method GDALExtendedDataType::Create()
11067 : *
11068 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11069 : *
11070 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11071 : * GDT_TypeCount
11072 : *
11073 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11074 : */
11075 2012 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11076 : {
11077 2012 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11078 : {
11079 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11080 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11081 0 : return nullptr;
11082 : }
11083 : return new GDALExtendedDataTypeHS(
11084 2012 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11085 : }
11086 :
11087 : /************************************************************************/
11088 : /* GDALExtendedDataTypeCreateString() */
11089 : /************************************************************************/
11090 :
11091 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11092 : *
11093 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11094 : *
11095 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11096 : *
11097 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11098 : */
11099 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11100 : {
11101 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11102 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11103 : }
11104 :
11105 : /************************************************************************/
11106 : /* GDALExtendedDataTypeCreateStringEx() */
11107 : /************************************************************************/
11108 :
11109 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11110 : *
11111 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11112 : *
11113 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11114 : *
11115 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11116 : * @since GDAL 3.4
11117 : */
11118 : GDALExtendedDataTypeH
11119 195 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11120 : GDALExtendedDataTypeSubType eSubType)
11121 : {
11122 195 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11123 195 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11124 : }
11125 :
11126 : /************************************************************************/
11127 : /* GDALExtendedDataTypeCreateCompound() */
11128 : /************************************************************************/
11129 :
11130 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11131 : *
11132 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11133 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11134 : *
11135 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11136 : *
11137 : * @param pszName Type name.
11138 : * @param nTotalSize Total size of the type in bytes.
11139 : * Should be large enough to store all components.
11140 : * @param nComponents Number of components in comps array.
11141 : * @param comps Components.
11142 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11143 : */
11144 : GDALExtendedDataTypeH
11145 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11146 : size_t nComponents,
11147 : const GDALEDTComponentH *comps)
11148 : {
11149 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11150 54 : for (size_t i = 0; i < nComponents; i++)
11151 : {
11152 : compsCpp.emplace_back(
11153 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11154 : }
11155 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11156 66 : std::move(compsCpp));
11157 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11158 6 : return nullptr;
11159 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11160 : }
11161 :
11162 : /************************************************************************/
11163 : /* GDALExtendedDataTypeRelease() */
11164 : /************************************************************************/
11165 :
11166 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11167 : *
11168 : * Note: when applied on a object coming from a driver, this does not
11169 : * destroy the object in the file, database, etc...
11170 : */
11171 6643 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11172 : {
11173 6643 : delete hEDT;
11174 6643 : }
11175 :
11176 : /************************************************************************/
11177 : /* GDALExtendedDataTypeGetName() */
11178 : /************************************************************************/
11179 :
11180 : /** Return type name.
11181 : *
11182 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11183 : */
11184 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11185 : {
11186 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11187 8 : return hEDT->m_poImpl->GetName().c_str();
11188 : }
11189 :
11190 : /************************************************************************/
11191 : /* GDALExtendedDataTypeGetClass() */
11192 : /************************************************************************/
11193 :
11194 : /** Return type class.
11195 : *
11196 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11197 : */
11198 : GDALExtendedDataTypeClass
11199 9203 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11200 : {
11201 9203 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11202 9203 : return hEDT->m_poImpl->GetClass();
11203 : }
11204 :
11205 : /************************************************************************/
11206 : /* GDALExtendedDataTypeGetNumericDataType() */
11207 : /************************************************************************/
11208 :
11209 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11210 : *
11211 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11212 : */
11213 2077 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11214 : {
11215 2077 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11216 2077 : return hEDT->m_poImpl->GetNumericDataType();
11217 : }
11218 :
11219 : /************************************************************************/
11220 : /* GDALExtendedDataTypeGetSize() */
11221 : /************************************************************************/
11222 :
11223 : /** Return data type size in bytes.
11224 : *
11225 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11226 : */
11227 2543 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11228 : {
11229 2543 : VALIDATE_POINTER1(hEDT, __func__, 0);
11230 2543 : return hEDT->m_poImpl->GetSize();
11231 : }
11232 :
11233 : /************************************************************************/
11234 : /* GDALExtendedDataTypeGetMaxStringLength() */
11235 : /************************************************************************/
11236 :
11237 : /** Return the maximum length of a string in bytes.
11238 : *
11239 : * 0 indicates unknown/unlimited string.
11240 : *
11241 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11242 : */
11243 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11244 : {
11245 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11246 3 : return hEDT->m_poImpl->GetMaxStringLength();
11247 : }
11248 :
11249 : /************************************************************************/
11250 : /* GDALExtendedDataTypeCanConvertTo() */
11251 : /************************************************************************/
11252 :
11253 : /** Return whether this data type can be converted to the other one.
11254 : *
11255 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11256 : *
11257 : * @param hSourceEDT Source data type for the conversion being considered.
11258 : * @param hTargetEDT Target data type for the conversion being considered.
11259 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11260 : */
11261 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11262 : GDALExtendedDataTypeH hTargetEDT)
11263 : {
11264 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11265 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11266 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11267 : }
11268 :
11269 : /************************************************************************/
11270 : /* GDALExtendedDataTypeEquals() */
11271 : /************************************************************************/
11272 :
11273 : /** Return whether this data type is equal to another one.
11274 : *
11275 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11276 : *
11277 : * @param hFirstEDT First data type.
11278 : * @param hSecondEDT Second data type.
11279 : * @return TRUE if they are equal. FALSE otherwise.
11280 : */
11281 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11282 : GDALExtendedDataTypeH hSecondEDT)
11283 : {
11284 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11285 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11286 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11287 : }
11288 :
11289 : /************************************************************************/
11290 : /* GDALExtendedDataTypeGetSubType() */
11291 : /************************************************************************/
11292 :
11293 : /** Return the subtype of a type.
11294 : *
11295 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11296 : *
11297 : * @param hEDT Data type.
11298 : * @return subtype.
11299 : * @since 3.4
11300 : */
11301 : GDALExtendedDataTypeSubType
11302 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11303 : {
11304 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11305 104 : return hEDT->m_poImpl->GetSubType();
11306 : }
11307 :
11308 : /************************************************************************/
11309 : /* GDALExtendedDataTypeGetRAT() */
11310 : /************************************************************************/
11311 :
11312 : /** Return associated raster attribute table, when there is one.
11313 : *
11314 : * * For the netCDF driver, the RAT will capture enumerated types, with
11315 : * a "value" column with an integer value and a "name" column with the
11316 : * associated name.
11317 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11318 : *
11319 : * @param hEDT Data type.
11320 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11321 : * @since 3.12
11322 : */
11323 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11324 : {
11325 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11326 1 : return GDALRasterAttributeTable::ToHandle(
11327 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11328 : }
11329 :
11330 : /************************************************************************/
11331 : /* GDALExtendedDataTypeGetComponents() */
11332 : /************************************************************************/
11333 :
11334 : /** Return the components of the data type (only valid when GetClass() ==
11335 : * GEDTC_COMPOUND)
11336 : *
11337 : * The returned array and its content must be freed with
11338 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11339 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11340 : * individual array members).
11341 : *
11342 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11343 : *
11344 : * @param hEDT Data type
11345 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11346 : * @return an array of *pnCount components.
11347 : */
11348 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11349 : size_t *pnCount)
11350 : {
11351 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11352 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11353 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11354 : auto ret = static_cast<GDALEDTComponentH *>(
11355 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11356 131 : for (size_t i = 0; i < components.size(); i++)
11357 : {
11358 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11359 : }
11360 44 : *pnCount = components.size();
11361 44 : return ret;
11362 : }
11363 :
11364 : /************************************************************************/
11365 : /* GDALExtendedDataTypeFreeComponents() */
11366 : /************************************************************************/
11367 :
11368 : /** Free the return of GDALExtendedDataTypeGetComponents().
11369 : *
11370 : * @param components return value of GDALExtendedDataTypeGetComponents()
11371 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11372 : */
11373 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11374 : size_t nCount)
11375 : {
11376 131 : for (size_t i = 0; i < nCount; i++)
11377 : {
11378 87 : delete components[i];
11379 : }
11380 44 : CPLFree(components);
11381 44 : }
11382 :
11383 : /************************************************************************/
11384 : /* GDALEDTComponentCreate() */
11385 : /************************************************************************/
11386 :
11387 : /** Create a new GDALEDTComponent.
11388 : *
11389 : * The returned value must be freed with GDALEDTComponentRelease().
11390 : *
11391 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11392 : */
11393 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11394 : GDALExtendedDataTypeH hType)
11395 : {
11396 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11397 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11398 : return new GDALEDTComponentHS(
11399 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11400 : }
11401 :
11402 : /************************************************************************/
11403 : /* GDALEDTComponentRelease() */
11404 : /************************************************************************/
11405 :
11406 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11407 : *
11408 : * Note: when applied on a object coming from a driver, this does not
11409 : * destroy the object in the file, database, etc...
11410 : */
11411 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11412 : {
11413 61 : delete hComp;
11414 61 : }
11415 :
11416 : /************************************************************************/
11417 : /* GDALEDTComponentGetName() */
11418 : /************************************************************************/
11419 :
11420 : /** Return the name.
11421 : *
11422 : * The returned pointer is valid until hComp is released.
11423 : *
11424 : * This is the same as the C++ method GDALEDTComponent::GetName().
11425 : */
11426 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11427 : {
11428 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11429 33 : return hComp->m_poImpl->GetName().c_str();
11430 : }
11431 :
11432 : /************************************************************************/
11433 : /* GDALEDTComponentGetOffset() */
11434 : /************************************************************************/
11435 :
11436 : /** Return the offset (in bytes) of the component in the compound data type.
11437 : *
11438 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11439 : */
11440 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11441 : {
11442 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11443 31 : return hComp->m_poImpl->GetOffset();
11444 : }
11445 :
11446 : /************************************************************************/
11447 : /* GDALEDTComponentGetType() */
11448 : /************************************************************************/
11449 :
11450 : /** Return the data type of the component.
11451 : *
11452 : * This is the same as the C++ method GDALEDTComponent::GetType().
11453 : */
11454 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11455 : {
11456 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11457 : return new GDALExtendedDataTypeHS(
11458 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11459 : }
11460 :
11461 : /************************************************************************/
11462 : /* GDALGroupRelease() */
11463 : /************************************************************************/
11464 :
11465 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11466 : *
11467 : * Note: when applied on a object coming from a driver, this does not
11468 : * destroy the object in the file, database, etc...
11469 : */
11470 1450 : void GDALGroupRelease(GDALGroupH hGroup)
11471 : {
11472 1450 : delete hGroup;
11473 1450 : }
11474 :
11475 : /************************************************************************/
11476 : /* GDALGroupGetName() */
11477 : /************************************************************************/
11478 :
11479 : /** Return the name of the group.
11480 : *
11481 : * The returned pointer is valid until hGroup is released.
11482 : *
11483 : * This is the same as the C++ method GDALGroup::GetName().
11484 : */
11485 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11486 : {
11487 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11488 95 : return hGroup->m_poImpl->GetName().c_str();
11489 : }
11490 :
11491 : /************************************************************************/
11492 : /* GDALGroupGetFullName() */
11493 : /************************************************************************/
11494 :
11495 : /** Return the full name of the group.
11496 : *
11497 : * The returned pointer is valid until hGroup is released.
11498 : *
11499 : * This is the same as the C++ method GDALGroup::GetFullName().
11500 : */
11501 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11502 : {
11503 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11504 47 : return hGroup->m_poImpl->GetFullName().c_str();
11505 : }
11506 :
11507 : /************************************************************************/
11508 : /* GDALGroupGetMDArrayNames() */
11509 : /************************************************************************/
11510 :
11511 : /** Return the list of multidimensional array names contained in this group.
11512 : *
11513 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11514 : *
11515 : * @return the array names, to be freed with CSLDestroy()
11516 : */
11517 329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11518 : {
11519 329 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11520 658 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11521 658 : CPLStringList res;
11522 829 : for (const auto &name : names)
11523 : {
11524 500 : res.AddString(name.c_str());
11525 : }
11526 329 : return res.StealList();
11527 : }
11528 :
11529 : /************************************************************************/
11530 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11531 : /************************************************************************/
11532 :
11533 : /** Return the list of multidimensional array full names contained in this
11534 : * group and its subgroups.
11535 : *
11536 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11537 : *
11538 : * @return the array names, to be freed with CSLDestroy()
11539 : *
11540 : * @since 3.11
11541 : */
11542 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11543 : CSLConstList papszGroupOptions,
11544 : CSLConstList papszArrayOptions)
11545 : {
11546 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11547 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11548 2 : papszGroupOptions, papszArrayOptions);
11549 2 : CPLStringList res;
11550 5 : for (const auto &name : names)
11551 : {
11552 4 : res.AddString(name.c_str());
11553 : }
11554 1 : return res.StealList();
11555 : }
11556 :
11557 : /************************************************************************/
11558 : /* GDALGroupOpenMDArray() */
11559 : /************************************************************************/
11560 :
11561 : /** Open and return a multidimensional array.
11562 : *
11563 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11564 : *
11565 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11566 : */
11567 807 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11568 : CSLConstList papszOptions)
11569 : {
11570 807 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11571 807 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11572 2421 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11573 2421 : papszOptions);
11574 807 : if (!array)
11575 30 : return nullptr;
11576 777 : return new GDALMDArrayHS(array);
11577 : }
11578 :
11579 : /************************************************************************/
11580 : /* GDALGroupOpenMDArrayFromFullname() */
11581 : /************************************************************************/
11582 :
11583 : /** Open and return a multidimensional array from its fully qualified name.
11584 : *
11585 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11586 : *
11587 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11588 : *
11589 : * @since GDAL 3.2
11590 : */
11591 17 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11592 : const char *pszFullname,
11593 : CSLConstList papszOptions)
11594 : {
11595 17 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11596 17 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11597 17 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11598 51 : std::string(pszFullname), papszOptions);
11599 17 : if (!array)
11600 2 : return nullptr;
11601 15 : return new GDALMDArrayHS(array);
11602 : }
11603 :
11604 : /************************************************************************/
11605 : /* GDALGroupResolveMDArray() */
11606 : /************************************************************************/
11607 :
11608 : /** Locate an array in a group and its subgroups by name.
11609 : *
11610 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11611 : * @since GDAL 3.2
11612 : */
11613 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11614 : const char *pszStartingPoint,
11615 : CSLConstList papszOptions)
11616 : {
11617 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11618 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11619 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11620 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11621 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11622 19 : if (!array)
11623 2 : return nullptr;
11624 17 : return new GDALMDArrayHS(array);
11625 : }
11626 :
11627 : /************************************************************************/
11628 : /* GDALGroupGetGroupNames() */
11629 : /************************************************************************/
11630 :
11631 : /** Return the list of sub-groups contained in this group.
11632 : *
11633 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11634 : *
11635 : * @return the group names, to be freed with CSLDestroy()
11636 : */
11637 97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11638 : {
11639 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11640 194 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11641 194 : CPLStringList res;
11642 219 : for (const auto &name : names)
11643 : {
11644 122 : res.AddString(name.c_str());
11645 : }
11646 97 : return res.StealList();
11647 : }
11648 :
11649 : /************************************************************************/
11650 : /* GDALGroupOpenGroup() */
11651 : /************************************************************************/
11652 :
11653 : /** Open and return a sub-group.
11654 : *
11655 : * This is the same as the C++ method GDALGroup::OpenGroup().
11656 : *
11657 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11658 : */
11659 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11660 : CSLConstList papszOptions)
11661 : {
11662 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11663 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11664 : auto subGroup =
11665 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11666 163 : if (!subGroup)
11667 30 : return nullptr;
11668 133 : return new GDALGroupHS(subGroup);
11669 : }
11670 :
11671 : /************************************************************************/
11672 : /* GDALGroupGetVectorLayerNames() */
11673 : /************************************************************************/
11674 :
11675 : /** Return the list of layer names contained in this group.
11676 : *
11677 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11678 : *
11679 : * @return the group names, to be freed with CSLDestroy()
11680 : * @since 3.4
11681 : */
11682 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11683 : CSLConstList papszOptions)
11684 : {
11685 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11686 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11687 16 : CPLStringList res;
11688 18 : for (const auto &name : names)
11689 : {
11690 10 : res.AddString(name.c_str());
11691 : }
11692 8 : return res.StealList();
11693 : }
11694 :
11695 : /************************************************************************/
11696 : /* GDALGroupOpenVectorLayer() */
11697 : /************************************************************************/
11698 :
11699 : /** Open and return a vector layer.
11700 : *
11701 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11702 : *
11703 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11704 : * the returned handled if only valid while the parent GDALDatasetH is kept
11705 : * opened.
11706 : *
11707 : * @return the vector layer, or nullptr.
11708 : * @since 3.4
11709 : */
11710 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11711 : const char *pszVectorLayerName,
11712 : CSLConstList papszOptions)
11713 : {
11714 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11715 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11716 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11717 24 : std::string(pszVectorLayerName), papszOptions));
11718 : }
11719 :
11720 : /************************************************************************/
11721 : /* GDALGroupOpenMDArrayFromFullname() */
11722 : /************************************************************************/
11723 :
11724 : /** Open and return a sub-group from its fully qualified name.
11725 : *
11726 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11727 : *
11728 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11729 : *
11730 : * @since GDAL 3.2
11731 : */
11732 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11733 : const char *pszFullname,
11734 : CSLConstList papszOptions)
11735 : {
11736 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11737 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11738 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11739 9 : std::string(pszFullname), papszOptions);
11740 3 : if (!subGroup)
11741 2 : return nullptr;
11742 1 : return new GDALGroupHS(subGroup);
11743 : }
11744 :
11745 : /************************************************************************/
11746 : /* GDALGroupGetDimensions() */
11747 : /************************************************************************/
11748 :
11749 : /** Return the list of dimensions contained in this group and used by its
11750 : * arrays.
11751 : *
11752 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11753 : * array itself needs to be freed, CPLFree() should be called (and
11754 : * GDALDimensionRelease() on individual array members).
11755 : *
11756 : * This is the same as the C++ method GDALGroup::GetDimensions().
11757 : *
11758 : * @param hGroup Group.
11759 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11760 : * @param papszOptions Driver specific options determining how dimensions
11761 : * should be retrieved. Pass nullptr for default behavior.
11762 : *
11763 : * @return an array of *pnCount dimensions.
11764 : */
11765 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11766 : CSLConstList papszOptions)
11767 : {
11768 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11769 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11770 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11771 : auto ret = static_cast<GDALDimensionH *>(
11772 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11773 230 : for (size_t i = 0; i < dims.size(); i++)
11774 : {
11775 157 : ret[i] = new GDALDimensionHS(dims[i]);
11776 : }
11777 73 : *pnCount = dims.size();
11778 73 : return ret;
11779 : }
11780 :
11781 : /************************************************************************/
11782 : /* GDALGroupGetAttribute() */
11783 : /************************************************************************/
11784 :
11785 : /** Return an attribute by its name.
11786 : *
11787 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11788 : *
11789 : * The returned attribute must be freed with GDALAttributeRelease().
11790 : */
11791 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11792 : {
11793 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11794 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11795 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11796 80 : if (attr)
11797 76 : return new GDALAttributeHS(attr);
11798 4 : return nullptr;
11799 : }
11800 :
11801 : /************************************************************************/
11802 : /* GDALGroupGetAttributes() */
11803 : /************************************************************************/
11804 :
11805 : /** Return the list of attributes contained in this group.
11806 : *
11807 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11808 : * array itself needs to be freed, CPLFree() should be called (and
11809 : * GDALAttributeRelease() on individual array members).
11810 : *
11811 : * This is the same as the C++ method GDALGroup::GetAttributes().
11812 : *
11813 : * @param hGroup Group.
11814 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11815 : * @param papszOptions Driver specific options determining how attributes
11816 : * should be retrieved. Pass nullptr for default behavior.
11817 : *
11818 : * @return an array of *pnCount attributes.
11819 : */
11820 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11821 : CSLConstList papszOptions)
11822 : {
11823 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11824 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11825 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11826 : auto ret = static_cast<GDALAttributeH *>(
11827 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11828 229 : for (size_t i = 0; i < attrs.size(); i++)
11829 : {
11830 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11831 : }
11832 71 : *pnCount = attrs.size();
11833 71 : return ret;
11834 : }
11835 :
11836 : /************************************************************************/
11837 : /* GDALGroupGetStructuralInfo() */
11838 : /************************************************************************/
11839 :
11840 : /** Return structural information on the group.
11841 : *
11842 : * This may be the compression, etc..
11843 : *
11844 : * The return value should not be freed and is valid until GDALGroup is
11845 : * released or this function called again.
11846 : *
11847 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11848 : */
11849 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11850 : {
11851 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11852 4 : return hGroup->m_poImpl->GetStructuralInfo();
11853 : }
11854 :
11855 : /************************************************************************/
11856 : /* GDALGroupGetDataTypeCount() */
11857 : /************************************************************************/
11858 :
11859 : /** Return the number of data types associated with the group
11860 : * (typically enumerations).
11861 : *
11862 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11863 : *
11864 : * @since 3.12
11865 : */
11866 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11867 : {
11868 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11869 4 : return hGroup->m_poImpl->GetDataTypes().size();
11870 : }
11871 :
11872 : /************************************************************************/
11873 : /* GDALGroupGetDataType() */
11874 : /************************************************************************/
11875 :
11876 : /** Return one of the data types associated with the group.
11877 : *
11878 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11879 : *
11880 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11881 : * or nullptr in case of error.
11882 : * @since 3.12
11883 : */
11884 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11885 : {
11886 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11887 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11888 0 : return nullptr;
11889 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11890 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11891 : }
11892 :
11893 : /************************************************************************/
11894 : /* GDALReleaseAttributes() */
11895 : /************************************************************************/
11896 :
11897 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11898 : *
11899 : * @param attributes return pointer of above methods
11900 : * @param nCount *pnCount value returned by above methods
11901 : */
11902 129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11903 : {
11904 416 : for (size_t i = 0; i < nCount; i++)
11905 : {
11906 287 : delete attributes[i];
11907 : }
11908 129 : CPLFree(attributes);
11909 129 : }
11910 :
11911 : /************************************************************************/
11912 : /* GDALGroupCreateGroup() */
11913 : /************************************************************************/
11914 :
11915 : /** Create a sub-group within a group.
11916 : *
11917 : * This is the same as the C++ method GDALGroup::CreateGroup().
11918 : *
11919 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11920 : */
11921 179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11922 : CSLConstList papszOptions)
11923 : {
11924 179 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11925 179 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11926 537 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11927 537 : papszOptions);
11928 179 : if (!ret)
11929 49 : return nullptr;
11930 130 : return new GDALGroupHS(ret);
11931 : }
11932 :
11933 : /************************************************************************/
11934 : /* GDALGroupDeleteGroup() */
11935 : /************************************************************************/
11936 :
11937 : /** Delete a sub-group from a group.
11938 : *
11939 : * After this call, if a previously obtained instance of the deleted object
11940 : * is still alive, no method other than for freeing it should be invoked.
11941 : *
11942 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11943 : *
11944 : * @return true in case of success.
11945 : * @since GDAL 3.8
11946 : */
11947 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11948 : CSLConstList papszOptions)
11949 : {
11950 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11951 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11952 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11953 20 : papszOptions);
11954 : }
11955 :
11956 : /************************************************************************/
11957 : /* GDALGroupCreateDimension() */
11958 : /************************************************************************/
11959 :
11960 : /** Create a dimension within a group.
11961 : *
11962 : * This is the same as the C++ method GDALGroup::CreateDimension().
11963 : *
11964 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11965 : */
11966 669 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11967 : const char *pszType,
11968 : const char *pszDirection, GUInt64 nSize,
11969 : CSLConstList papszOptions)
11970 : {
11971 669 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11972 669 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11973 669 : auto ret = hGroup->m_poImpl->CreateDimension(
11974 1338 : std::string(pszName), std::string(pszType ? pszType : ""),
11975 2676 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11976 669 : if (!ret)
11977 9 : return nullptr;
11978 660 : return new GDALDimensionHS(ret);
11979 : }
11980 :
11981 : /************************************************************************/
11982 : /* GDALGroupCreateMDArray() */
11983 : /************************************************************************/
11984 :
11985 : /** Create a multidimensional array within a group.
11986 : *
11987 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11988 : *
11989 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11990 : */
11991 612 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11992 : size_t nDimensions,
11993 : GDALDimensionH *pahDimensions,
11994 : GDALExtendedDataTypeH hEDT,
11995 : CSLConstList papszOptions)
11996 : {
11997 612 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11998 612 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11999 612 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12000 1224 : std::vector<std::shared_ptr<GDALDimension>> dims;
12001 612 : dims.reserve(nDimensions);
12002 1443 : for (size_t i = 0; i < nDimensions; i++)
12003 831 : dims.push_back(pahDimensions[i]->m_poImpl);
12004 1836 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12005 1836 : *(hEDT->m_poImpl), papszOptions);
12006 612 : if (!ret)
12007 65 : return nullptr;
12008 547 : return new GDALMDArrayHS(ret);
12009 : }
12010 :
12011 : /************************************************************************/
12012 : /* GDALGroupDeleteMDArray() */
12013 : /************************************************************************/
12014 :
12015 : /** Delete an array from a group.
12016 : *
12017 : * After this call, if a previously obtained instance of the deleted object
12018 : * is still alive, no method other than for freeing it should be invoked.
12019 : *
12020 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12021 : *
12022 : * @return true in case of success.
12023 : * @since GDAL 3.8
12024 : */
12025 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12026 : CSLConstList papszOptions)
12027 : {
12028 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12029 20 : VALIDATE_POINTER1(pszName, __func__, false);
12030 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12031 : }
12032 :
12033 : /************************************************************************/
12034 : /* GDALGroupCreateAttribute() */
12035 : /************************************************************************/
12036 :
12037 : /** Create a attribute within a group.
12038 : *
12039 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12040 : *
12041 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12042 : */
12043 125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12044 : size_t nDimensions,
12045 : const GUInt64 *panDimensions,
12046 : GDALExtendedDataTypeH hEDT,
12047 : CSLConstList papszOptions)
12048 : {
12049 125 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12050 125 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12051 250 : std::vector<GUInt64> dims;
12052 125 : dims.reserve(nDimensions);
12053 175 : for (size_t i = 0; i < nDimensions; i++)
12054 50 : dims.push_back(panDimensions[i]);
12055 125 : auto ret = hGroup->m_poImpl->CreateAttribute(
12056 375 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12057 125 : if (!ret)
12058 14 : return nullptr;
12059 111 : return new GDALAttributeHS(ret);
12060 : }
12061 :
12062 : /************************************************************************/
12063 : /* GDALGroupDeleteAttribute() */
12064 : /************************************************************************/
12065 :
12066 : /** Delete an attribute from a group.
12067 : *
12068 : * After this call, if a previously obtained instance of the deleted object
12069 : * is still alive, no method other than for freeing it should be invoked.
12070 : *
12071 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12072 : *
12073 : * @return true in case of success.
12074 : * @since GDAL 3.8
12075 : */
12076 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12077 : CSLConstList papszOptions)
12078 : {
12079 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12080 25 : VALIDATE_POINTER1(pszName, __func__, false);
12081 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12082 25 : papszOptions);
12083 : }
12084 :
12085 : /************************************************************************/
12086 : /* GDALGroupRename() */
12087 : /************************************************************************/
12088 :
12089 : /** Rename the group.
12090 : *
12091 : * This is not implemented by all drivers.
12092 : *
12093 : * Drivers known to implement it: MEM, netCDF.
12094 : *
12095 : * This is the same as the C++ method GDALGroup::Rename()
12096 : *
12097 : * @return true in case of success
12098 : * @since GDAL 3.8
12099 : */
12100 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12101 : {
12102 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12103 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12104 45 : return hGroup->m_poImpl->Rename(pszNewName);
12105 : }
12106 :
12107 : /************************************************************************/
12108 : /* GDALGroupSubsetDimensionFromSelection() */
12109 : /************************************************************************/
12110 :
12111 : /** Return a virtual group whose one dimension has been subset according to a
12112 : * selection.
12113 : *
12114 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12115 : *
12116 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12117 : */
12118 : GDALGroupH
12119 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12120 : const char *pszSelection,
12121 : CPL_UNUSED CSLConstList papszOptions)
12122 : {
12123 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12124 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12125 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12126 42 : std::string(pszSelection));
12127 14 : if (!hNewGroup)
12128 8 : return nullptr;
12129 6 : return new GDALGroupHS(hNewGroup);
12130 : }
12131 :
12132 : /************************************************************************/
12133 : /* GDALMDArrayRelease() */
12134 : /************************************************************************/
12135 :
12136 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12137 : *
12138 : * Note: when applied on a object coming from a driver, this does not
12139 : * destroy the object in the file, database, etc...
12140 : */
12141 2024 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12142 : {
12143 2024 : delete hMDArray;
12144 2024 : }
12145 :
12146 : /************************************************************************/
12147 : /* GDALMDArrayGetName() */
12148 : /************************************************************************/
12149 :
12150 : /** Return array name.
12151 : *
12152 : * This is the same as the C++ method GDALMDArray::GetName()
12153 : */
12154 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12155 : {
12156 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12157 83 : return hArray->m_poImpl->GetName().c_str();
12158 : }
12159 :
12160 : /************************************************************************/
12161 : /* GDALMDArrayGetFullName() */
12162 : /************************************************************************/
12163 :
12164 : /** Return array full name.
12165 : *
12166 : * This is the same as the C++ method GDALMDArray::GetFullName()
12167 : */
12168 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12169 : {
12170 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12171 50 : return hArray->m_poImpl->GetFullName().c_str();
12172 : }
12173 :
12174 : /************************************************************************/
12175 : /* GDALMDArrayGetName() */
12176 : /************************************************************************/
12177 :
12178 : /** Return the total number of values in the array.
12179 : *
12180 : * This is the same as the C++ method
12181 : * GDALAbstractMDArray::GetTotalElementsCount()
12182 : */
12183 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12184 : {
12185 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12186 6 : return hArray->m_poImpl->GetTotalElementsCount();
12187 : }
12188 :
12189 : /************************************************************************/
12190 : /* GDALMDArrayGetDimensionCount() */
12191 : /************************************************************************/
12192 :
12193 : /** Return the number of dimensions.
12194 : *
12195 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12196 : */
12197 10391 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12198 : {
12199 10391 : VALIDATE_POINTER1(hArray, __func__, 0);
12200 10391 : return hArray->m_poImpl->GetDimensionCount();
12201 : }
12202 :
12203 : /************************************************************************/
12204 : /* GDALMDArrayGetDimensions() */
12205 : /************************************************************************/
12206 :
12207 : /** Return the dimensions of the array
12208 : *
12209 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12210 : * array itself needs to be freed, CPLFree() should be called (and
12211 : * GDALDimensionRelease() on individual array members).
12212 : *
12213 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12214 : *
12215 : * @param hArray Array.
12216 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12217 : *
12218 : * @return an array of *pnCount dimensions.
12219 : */
12220 2305 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12221 : {
12222 2305 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12223 2305 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12224 2305 : const auto &dims(hArray->m_poImpl->GetDimensions());
12225 : auto ret = static_cast<GDALDimensionH *>(
12226 2305 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12227 6482 : for (size_t i = 0; i < dims.size(); i++)
12228 : {
12229 4177 : ret[i] = new GDALDimensionHS(dims[i]);
12230 : }
12231 2305 : *pnCount = dims.size();
12232 2305 : return ret;
12233 : }
12234 :
12235 : /************************************************************************/
12236 : /* GDALReleaseDimensions() */
12237 : /************************************************************************/
12238 :
12239 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12240 : *
12241 : * @param dims return pointer of above methods
12242 : * @param nCount *pnCount value returned by above methods
12243 : */
12244 2378 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12245 : {
12246 6712 : for (size_t i = 0; i < nCount; i++)
12247 : {
12248 4334 : delete dims[i];
12249 : }
12250 2378 : CPLFree(dims);
12251 2378 : }
12252 :
12253 : /************************************************************************/
12254 : /* GDALMDArrayGetDataType() */
12255 : /************************************************************************/
12256 :
12257 : /** Return the data type
12258 : *
12259 : * The return must be freed with GDALExtendedDataTypeRelease().
12260 : */
12261 3918 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12262 : {
12263 3918 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12264 : return new GDALExtendedDataTypeHS(
12265 3918 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12266 : }
12267 :
12268 : /************************************************************************/
12269 : /* GDALMDArrayRead() */
12270 : /************************************************************************/
12271 :
12272 : /** Read part or totality of a multidimensional array.
12273 : *
12274 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12275 : *
12276 : * @return TRUE in case of success.
12277 : */
12278 1960 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12279 : const size_t *count, const GInt64 *arrayStep,
12280 : const GPtrDiff_t *bufferStride,
12281 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12282 : const void *pDstBufferAllocStart,
12283 : size_t nDstBufferAllocSize)
12284 : {
12285 1960 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12286 1960 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12287 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12288 : {
12289 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12290 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12291 : }
12292 1960 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12293 1960 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12294 3920 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12295 1960 : *(bufferDataType->m_poImpl), pDstBuffer,
12296 1960 : pDstBufferAllocStart, nDstBufferAllocSize);
12297 : }
12298 :
12299 : /************************************************************************/
12300 : /* GDALMDArrayWrite() */
12301 : /************************************************************************/
12302 :
12303 : /** Write part or totality of a multidimensional array.
12304 : *
12305 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12306 : *
12307 : * @return TRUE in case of success.
12308 : */
12309 559 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12310 : const size_t *count, const GInt64 *arrayStep,
12311 : const GPtrDiff_t *bufferStride,
12312 : GDALExtendedDataTypeH bufferDataType,
12313 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12314 : size_t nSrcBufferAllocSize)
12315 : {
12316 559 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12317 559 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12318 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12319 : {
12320 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12321 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12322 : }
12323 559 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12324 559 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12325 1118 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12326 559 : bufferStride, *(bufferDataType->m_poImpl),
12327 : pSrcBuffer, pSrcBufferAllocStart,
12328 559 : nSrcBufferAllocSize);
12329 : }
12330 :
12331 : /************************************************************************/
12332 : /* GDALMDArrayAdviseRead() */
12333 : /************************************************************************/
12334 :
12335 : /** Advise driver of upcoming read requests.
12336 : *
12337 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12338 : *
12339 : * @return TRUE in case of success.
12340 : *
12341 : * @since GDAL 3.2
12342 : */
12343 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12344 : const size_t *count)
12345 : {
12346 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12347 : }
12348 :
12349 : /************************************************************************/
12350 : /* GDALMDArrayAdviseReadEx() */
12351 : /************************************************************************/
12352 :
12353 : /** Advise driver of upcoming read requests.
12354 : *
12355 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12356 : *
12357 : * @return TRUE in case of success.
12358 : *
12359 : * @since GDAL 3.4
12360 : */
12361 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12362 : const size_t *count, CSLConstList papszOptions)
12363 : {
12364 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12365 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12366 : }
12367 :
12368 : /************************************************************************/
12369 : /* GDALMDArrayGetAttribute() */
12370 : /************************************************************************/
12371 :
12372 : /** Return an attribute by its name.
12373 : *
12374 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12375 : *
12376 : * The returned attribute must be freed with GDALAttributeRelease().
12377 : */
12378 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12379 : {
12380 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12381 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12382 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12383 119 : if (attr)
12384 110 : return new GDALAttributeHS(attr);
12385 9 : return nullptr;
12386 : }
12387 :
12388 : /************************************************************************/
12389 : /* GDALMDArrayGetAttributes() */
12390 : /************************************************************************/
12391 :
12392 : /** Return the list of attributes contained in this array.
12393 : *
12394 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12395 : * array itself needs to be freed, CPLFree() should be called (and
12396 : * GDALAttributeRelease() on individual array members).
12397 : *
12398 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12399 : *
12400 : * @param hArray Array.
12401 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12402 : * @param papszOptions Driver specific options determining how attributes
12403 : * should be retrieved. Pass nullptr for default behavior.
12404 : *
12405 : * @return an array of *pnCount attributes.
12406 : */
12407 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12408 : CSLConstList papszOptions)
12409 : {
12410 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12411 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12412 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12413 : auto ret = static_cast<GDALAttributeH *>(
12414 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12415 187 : for (size_t i = 0; i < attrs.size(); i++)
12416 : {
12417 129 : ret[i] = new GDALAttributeHS(attrs[i]);
12418 : }
12419 58 : *pnCount = attrs.size();
12420 58 : return ret;
12421 : }
12422 :
12423 : /************************************************************************/
12424 : /* GDALMDArrayCreateAttribute() */
12425 : /************************************************************************/
12426 :
12427 : /** Create a attribute within an array.
12428 : *
12429 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12430 : *
12431 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12432 : */
12433 162 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12434 : const char *pszName,
12435 : size_t nDimensions,
12436 : const GUInt64 *panDimensions,
12437 : GDALExtendedDataTypeH hEDT,
12438 : CSLConstList papszOptions)
12439 : {
12440 162 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12441 162 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12442 162 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12443 324 : std::vector<GUInt64> dims;
12444 162 : dims.reserve(nDimensions);
12445 197 : for (size_t i = 0; i < nDimensions; i++)
12446 35 : dims.push_back(panDimensions[i]);
12447 162 : auto ret = hArray->m_poImpl->CreateAttribute(
12448 486 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12449 162 : if (!ret)
12450 9 : return nullptr;
12451 153 : return new GDALAttributeHS(ret);
12452 : }
12453 :
12454 : /************************************************************************/
12455 : /* GDALMDArrayDeleteAttribute() */
12456 : /************************************************************************/
12457 :
12458 : /** Delete an attribute from an array.
12459 : *
12460 : * After this call, if a previously obtained instance of the deleted object
12461 : * is still alive, no method other than for freeing it should be invoked.
12462 : *
12463 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12464 : *
12465 : * @return true in case of success.
12466 : * @since GDAL 3.8
12467 : */
12468 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12469 : CSLConstList papszOptions)
12470 : {
12471 24 : VALIDATE_POINTER1(hArray, __func__, false);
12472 24 : VALIDATE_POINTER1(pszName, __func__, false);
12473 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12474 24 : papszOptions);
12475 : }
12476 :
12477 : /************************************************************************/
12478 : /* GDALMDArrayGetRawNoDataValue() */
12479 : /************************************************************************/
12480 :
12481 : /** Return the nodata value as a "raw" value.
12482 : *
12483 : * The value returned might be nullptr in case of no nodata value. When
12484 : * a nodata value is registered, a non-nullptr will be returned whose size in
12485 : * bytes is GetDataType().GetSize().
12486 : *
12487 : * The returned value should not be modified or freed.
12488 : *
12489 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12490 : *
12491 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12492 : */
12493 76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12494 : {
12495 76 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12496 76 : return hArray->m_poImpl->GetRawNoDataValue();
12497 : }
12498 :
12499 : /************************************************************************/
12500 : /* GDALMDArrayGetNoDataValueAsDouble() */
12501 : /************************************************************************/
12502 :
12503 : /** Return the nodata value as a double.
12504 : *
12505 : * The value returned might be nullptr in case of no nodata value. When
12506 : * a nodata value is registered, a non-nullptr will be returned whose size in
12507 : * bytes is GetDataType().GetSize().
12508 : *
12509 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12510 : *
12511 : * @param hArray Array handle.
12512 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12513 : * if a nodata value exists and can be converted to double. Might be nullptr.
12514 : *
12515 : * @return the nodata value as a double. A 0.0 value might also indicate the
12516 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12517 : * will be set to false then).
12518 : */
12519 120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12520 : int *pbHasNoDataValue)
12521 : {
12522 120 : VALIDATE_POINTER1(hArray, __func__, 0);
12523 120 : bool bHasNodataValue = false;
12524 120 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12525 120 : if (pbHasNoDataValue)
12526 120 : *pbHasNoDataValue = bHasNodataValue;
12527 120 : return ret;
12528 : }
12529 :
12530 : /************************************************************************/
12531 : /* GDALMDArrayGetNoDataValueAsInt64() */
12532 : /************************************************************************/
12533 :
12534 : /** Return the nodata value as a Int64.
12535 : *
12536 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12537 : *
12538 : * @param hArray Array handle.
12539 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12540 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12541 : *
12542 : * @return the nodata value as a Int64.
12543 : * @since GDAL 3.5
12544 : */
12545 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12546 : int *pbHasNoDataValue)
12547 : {
12548 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12549 11 : bool bHasNodataValue = false;
12550 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12551 11 : if (pbHasNoDataValue)
12552 11 : *pbHasNoDataValue = bHasNodataValue;
12553 11 : return ret;
12554 : }
12555 :
12556 : /************************************************************************/
12557 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12558 : /************************************************************************/
12559 :
12560 : /** Return the nodata value as a UInt64.
12561 : *
12562 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12563 : *
12564 : * @param hArray Array handle.
12565 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12566 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12567 : *
12568 : * @return the nodata value as a UInt64.
12569 : * @since GDAL 3.5
12570 : */
12571 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12572 : int *pbHasNoDataValue)
12573 : {
12574 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12575 7 : bool bHasNodataValue = false;
12576 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12577 7 : if (pbHasNoDataValue)
12578 7 : *pbHasNoDataValue = bHasNodataValue;
12579 7 : return ret;
12580 : }
12581 :
12582 : /************************************************************************/
12583 : /* GDALMDArraySetRawNoDataValue() */
12584 : /************************************************************************/
12585 :
12586 : /** Set the nodata value as a "raw" value.
12587 : *
12588 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12589 : * void*).
12590 : *
12591 : * @return TRUE in case of success.
12592 : */
12593 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12594 : {
12595 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12596 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12597 : }
12598 :
12599 : /************************************************************************/
12600 : /* GDALMDArraySetNoDataValueAsDouble() */
12601 : /************************************************************************/
12602 :
12603 : /** Set the nodata value as a double.
12604 : *
12605 : * If the natural data type of the attribute/array is not double, type
12606 : * conversion will occur to the type returned by GetDataType().
12607 : *
12608 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12609 : *
12610 : * @return TRUE in case of success.
12611 : */
12612 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12613 : {
12614 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12615 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12616 : }
12617 :
12618 : /************************************************************************/
12619 : /* GDALMDArraySetNoDataValueAsInt64() */
12620 : /************************************************************************/
12621 :
12622 : /** Set the nodata value as a Int64.
12623 : *
12624 : * If the natural data type of the attribute/array is not Int64, type conversion
12625 : * will occur to the type returned by GetDataType().
12626 : *
12627 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12628 : *
12629 : * @return TRUE in case of success.
12630 : * @since GDAL 3.5
12631 : */
12632 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12633 : {
12634 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12635 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12636 : }
12637 :
12638 : /************************************************************************/
12639 : /* GDALMDArraySetNoDataValueAsUInt64() */
12640 : /************************************************************************/
12641 :
12642 : /** Set the nodata value as a UInt64.
12643 : *
12644 : * If the natural data type of the attribute/array is not UInt64, type
12645 : * conversion will occur to the type returned by GetDataType().
12646 : *
12647 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12648 : *
12649 : * @return TRUE in case of success.
12650 : * @since GDAL 3.5
12651 : */
12652 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12653 : uint64_t nNoDataValue)
12654 : {
12655 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12656 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12657 : }
12658 :
12659 : /************************************************************************/
12660 : /* GDALMDArrayResize() */
12661 : /************************************************************************/
12662 :
12663 : /** Resize an array to new dimensions.
12664 : *
12665 : * Not all drivers may allow this operation, and with restrictions (e.g.
12666 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12667 : *
12668 : * Resizing a dimension used in other arrays will cause those other arrays
12669 : * to be resized.
12670 : *
12671 : * This is the same as the C++ method GDALMDArray::Resize().
12672 : *
12673 : * @param hArray Array.
12674 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12675 : * new size of each indexing dimension.
12676 : * @param papszOptions Options. (Driver specific)
12677 : * @return true in case of success.
12678 : * @since GDAL 3.7
12679 : */
12680 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12681 : CSLConstList papszOptions)
12682 : {
12683 42 : VALIDATE_POINTER1(hArray, __func__, false);
12684 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12685 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12686 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12687 : {
12688 83 : anNewDimSizes[i] = panNewDimSizes[i];
12689 : }
12690 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12691 : }
12692 :
12693 : /************************************************************************/
12694 : /* GDALMDArraySetScale() */
12695 : /************************************************************************/
12696 :
12697 : /** Set the scale value to apply to raw values.
12698 : *
12699 : * unscaled_value = raw_value * GetScale() + GetOffset()
12700 : *
12701 : * This is the same as the C++ method GDALMDArray::SetScale().
12702 : *
12703 : * @return TRUE in case of success.
12704 : */
12705 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12706 : {
12707 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12708 0 : return hArray->m_poImpl->SetScale(dfScale);
12709 : }
12710 :
12711 : /************************************************************************/
12712 : /* GDALMDArraySetScaleEx() */
12713 : /************************************************************************/
12714 :
12715 : /** Set the scale value to apply to raw values.
12716 : *
12717 : * unscaled_value = raw_value * GetScale() + GetOffset()
12718 : *
12719 : * This is the same as the C++ method GDALMDArray::SetScale().
12720 : *
12721 : * @return TRUE in case of success.
12722 : * @since GDAL 3.3
12723 : */
12724 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12725 : GDALDataType eStorageType)
12726 : {
12727 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12728 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12729 : }
12730 :
12731 : /************************************************************************/
12732 : /* GDALMDArraySetOffset() */
12733 : /************************************************************************/
12734 :
12735 : /** Set the scale value to apply to raw values.
12736 : *
12737 : * unscaled_value = raw_value * GetScale() + GetOffset()
12738 : *
12739 : * This is the same as the C++ method GDALMDArray::SetOffset().
12740 : *
12741 : * @return TRUE in case of success.
12742 : */
12743 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12744 : {
12745 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12746 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12747 : }
12748 :
12749 : /************************************************************************/
12750 : /* GDALMDArraySetOffsetEx() */
12751 : /************************************************************************/
12752 :
12753 : /** Set the scale value to apply to raw values.
12754 : *
12755 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12756 : *
12757 : * This is the same as the C++ method GDALMDArray::SetOffset().
12758 : *
12759 : * @return TRUE in case of success.
12760 : * @since GDAL 3.3
12761 : */
12762 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12763 : GDALDataType eStorageType)
12764 : {
12765 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12766 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12767 : }
12768 :
12769 : /************************************************************************/
12770 : /* GDALMDArrayGetScale() */
12771 : /************************************************************************/
12772 :
12773 : /** Get the scale value to apply to raw values.
12774 : *
12775 : * unscaled_value = raw_value * GetScale() + GetOffset()
12776 : *
12777 : * This is the same as the C++ method GDALMDArray::GetScale().
12778 : *
12779 : * @return the scale value
12780 : */
12781 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12782 : {
12783 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12784 103 : bool bHasValue = false;
12785 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12786 103 : if (pbHasValue)
12787 103 : *pbHasValue = bHasValue;
12788 103 : return dfRet;
12789 : }
12790 :
12791 : /************************************************************************/
12792 : /* GDALMDArrayGetScaleEx() */
12793 : /************************************************************************/
12794 :
12795 : /** Get the scale value to apply to raw values.
12796 : *
12797 : * unscaled_value = raw_value * GetScale() + GetScale()
12798 : *
12799 : * This is the same as the C++ method GDALMDArray::GetScale().
12800 : *
12801 : * @return the scale value
12802 : * @since GDAL 3.3
12803 : */
12804 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12805 : GDALDataType *peStorageType)
12806 : {
12807 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12808 5 : bool bHasValue = false;
12809 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12810 5 : if (pbHasValue)
12811 5 : *pbHasValue = bHasValue;
12812 5 : return dfRet;
12813 : }
12814 :
12815 : /************************************************************************/
12816 : /* GDALMDArrayGetOffset() */
12817 : /************************************************************************/
12818 :
12819 : /** Get the scale value to apply to raw values.
12820 : *
12821 : * unscaled_value = raw_value * GetScale() + GetOffset()
12822 : *
12823 : * This is the same as the C++ method GDALMDArray::GetOffset().
12824 : *
12825 : * @return the scale value
12826 : */
12827 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12828 : {
12829 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12830 100 : bool bHasValue = false;
12831 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12832 100 : if (pbHasValue)
12833 100 : *pbHasValue = bHasValue;
12834 100 : return dfRet;
12835 : }
12836 :
12837 : /************************************************************************/
12838 : /* GDALMDArrayGetOffsetEx() */
12839 : /************************************************************************/
12840 :
12841 : /** Get the scale value to apply to raw values.
12842 : *
12843 : * unscaled_value = raw_value * GetScale() + GetOffset()
12844 : *
12845 : * This is the same as the C++ method GDALMDArray::GetOffset().
12846 : *
12847 : * @return the scale value
12848 : * @since GDAL 3.3
12849 : */
12850 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12851 : GDALDataType *peStorageType)
12852 : {
12853 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12854 5 : bool bHasValue = false;
12855 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12856 5 : if (pbHasValue)
12857 5 : *pbHasValue = bHasValue;
12858 5 : return dfRet;
12859 : }
12860 :
12861 : /************************************************************************/
12862 : /* GDALMDArrayGetBlockSize() */
12863 : /************************************************************************/
12864 :
12865 : /** Return the "natural" block size of the array along all dimensions.
12866 : *
12867 : * Some drivers might organize the array in tiles/blocks and reading/writing
12868 : * aligned on those tile/block boundaries will be more efficient.
12869 : *
12870 : * The returned number of elements in the vector is the same as
12871 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12872 : * the natural block size along the considered dimension.
12873 : * "Flat" arrays will typically return a vector of values set to 0.
12874 : *
12875 : * The default implementation will return a vector of values set to 0.
12876 : *
12877 : * This method is used by GetProcessingChunkSize().
12878 : *
12879 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12880 : * theoretical case of a 32-bit platform, this might exceed its size_t
12881 : * allocation capabilities.
12882 : *
12883 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12884 : *
12885 : * @return the block size, in number of elements along each dimension.
12886 : */
12887 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12888 : {
12889 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12890 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12891 93 : auto res = hArray->m_poImpl->GetBlockSize();
12892 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12893 285 : for (size_t i = 0; i < res.size(); i++)
12894 : {
12895 192 : ret[i] = res[i];
12896 : }
12897 93 : *pnCount = res.size();
12898 93 : return ret;
12899 : }
12900 :
12901 : /***********************************************************************/
12902 : /* GDALMDArrayGetProcessingChunkSize() */
12903 : /************************************************************************/
12904 :
12905 : /** \brief Return an optimal chunk size for read/write operations, given the
12906 : * natural block size and memory constraints specified.
12907 : *
12908 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12909 : * multiple of those returned by GetBlockSize() (unless the block define by
12910 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12911 : * returned by this method).
12912 : *
12913 : * This is the same as the C++ method
12914 : * GDALAbstractMDArray::GetProcessingChunkSize().
12915 : *
12916 : * @param hArray Array.
12917 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12918 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12919 : * chunk.
12920 : *
12921 : * @return the chunk size, in number of elements along each dimension.
12922 : */
12923 :
12924 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12925 : size_t nMaxChunkMemory)
12926 : {
12927 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12928 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12929 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12930 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12931 3 : for (size_t i = 0; i < res.size(); i++)
12932 : {
12933 2 : ret[i] = res[i];
12934 : }
12935 1 : *pnCount = res.size();
12936 1 : return ret;
12937 : }
12938 :
12939 : /************************************************************************/
12940 : /* GDALMDArrayGetStructuralInfo() */
12941 : /************************************************************************/
12942 :
12943 : /** Return structural information on the array.
12944 : *
12945 : * This may be the compression, etc..
12946 : *
12947 : * The return value should not be freed and is valid until GDALMDArray is
12948 : * released or this function called again.
12949 : *
12950 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12951 : */
12952 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12953 : {
12954 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12955 15 : return hArray->m_poImpl->GetStructuralInfo();
12956 : }
12957 :
12958 : /************************************************************************/
12959 : /* GDALMDArrayGetView() */
12960 : /************************************************************************/
12961 :
12962 : /** Return a view of the array using slicing or field access.
12963 : *
12964 : * The returned object should be released with GDALMDArrayRelease().
12965 : *
12966 : * This is the same as the C++ method GDALMDArray::GetView().
12967 : */
12968 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12969 : {
12970 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12971 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12972 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12973 430 : if (!sliced)
12974 22 : return nullptr;
12975 408 : return new GDALMDArrayHS(sliced);
12976 : }
12977 :
12978 : /************************************************************************/
12979 : /* GDALMDArrayTranspose() */
12980 : /************************************************************************/
12981 :
12982 : /** Return a view of the array whose axis have been reordered.
12983 : *
12984 : * The returned object should be released with GDALMDArrayRelease().
12985 : *
12986 : * This is the same as the C++ method GDALMDArray::Transpose().
12987 : */
12988 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12989 : const int *panMapNewAxisToOldAxis)
12990 : {
12991 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12992 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12993 44 : if (nNewAxisCount)
12994 : {
12995 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12996 : nNewAxisCount * sizeof(int));
12997 : }
12998 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12999 44 : if (!reordered)
13000 7 : return nullptr;
13001 37 : return new GDALMDArrayHS(reordered);
13002 : }
13003 :
13004 : /************************************************************************/
13005 : /* GDALMDArrayGetUnscaled() */
13006 : /************************************************************************/
13007 :
13008 : /** Return an array that is the unscaled version of the current one.
13009 : *
13010 : * That is each value of the unscaled array will be
13011 : * unscaled_value = raw_value * GetScale() + GetOffset()
13012 : *
13013 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13014 : * from unscaled values to raw values.
13015 : *
13016 : * The returned object should be released with GDALMDArrayRelease().
13017 : *
13018 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13019 : */
13020 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13021 : {
13022 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13023 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13024 13 : if (!unscaled)
13025 0 : return nullptr;
13026 13 : return new GDALMDArrayHS(unscaled);
13027 : }
13028 :
13029 : /************************************************************************/
13030 : /* GDALMDArrayGetMask() */
13031 : /************************************************************************/
13032 :
13033 : /** Return an array that is a mask for the current array
13034 : *
13035 : * This array will be of type Byte, with values set to 0 to indicate invalid
13036 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13037 : *
13038 : * The returned object should be released with GDALMDArrayRelease().
13039 : *
13040 : * This is the same as the C++ method GDALMDArray::GetMask().
13041 : */
13042 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13043 : {
13044 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13045 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13046 35 : if (!unscaled)
13047 7 : return nullptr;
13048 28 : return new GDALMDArrayHS(unscaled);
13049 : }
13050 :
13051 : /************************************************************************/
13052 : /* GDALMDArrayGetResampled() */
13053 : /************************************************************************/
13054 :
13055 : /** Return an array that is a resampled / reprojected view of the current array
13056 : *
13057 : * This is the same as the C++ method GDALMDArray::GetResampled().
13058 : *
13059 : * Currently this method can only resample along the last 2 dimensions, unless
13060 : * orthorectifying a NASA EMIT dataset.
13061 : *
13062 : * The returned object should be released with GDALMDArrayRelease().
13063 : *
13064 : * @since 3.4
13065 : */
13066 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13067 : const GDALDimensionH *pahNewDims,
13068 : GDALRIOResampleAlg resampleAlg,
13069 : OGRSpatialReferenceH hTargetSRS,
13070 : CSLConstList papszOptions)
13071 : {
13072 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13073 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13074 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13075 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13076 : {
13077 78 : if (pahNewDims[i])
13078 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13079 : }
13080 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13081 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13082 68 : papszOptions);
13083 34 : if (!poNewArray)
13084 8 : return nullptr;
13085 26 : return new GDALMDArrayHS(poNewArray);
13086 : }
13087 :
13088 : /************************************************************************/
13089 : /* GDALMDArraySetUnit() */
13090 : /************************************************************************/
13091 :
13092 : /** Set the variable unit.
13093 : *
13094 : * Values should conform as much as possible with those allowed by
13095 : * the NetCDF CF conventions:
13096 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13097 : * but others might be returned.
13098 : *
13099 : * Few examples are "meter", "degrees", "second", ...
13100 : * Empty value means unknown.
13101 : *
13102 : * This is the same as the C function GDALMDArraySetUnit()
13103 : *
13104 : * @param hArray array.
13105 : * @param pszUnit unit name.
13106 : * @return TRUE in case of success.
13107 : */
13108 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13109 : {
13110 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13111 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13112 : }
13113 :
13114 : /************************************************************************/
13115 : /* GDALMDArrayGetUnit() */
13116 : /************************************************************************/
13117 :
13118 : /** Return the array unit.
13119 : *
13120 : * Values should conform as much as possible with those allowed by
13121 : * the NetCDF CF conventions:
13122 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13123 : * but others might be returned.
13124 : *
13125 : * Few examples are "meter", "degrees", "second", ...
13126 : * Empty value means unknown.
13127 : *
13128 : * The return value should not be freed and is valid until GDALMDArray is
13129 : * released or this function called again.
13130 : *
13131 : * This is the same as the C++ method GDALMDArray::GetUnit().
13132 : */
13133 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13134 : {
13135 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13136 111 : return hArray->m_poImpl->GetUnit().c_str();
13137 : }
13138 :
13139 : /************************************************************************/
13140 : /* GDALMDArrayGetSpatialRef() */
13141 : /************************************************************************/
13142 :
13143 : /** Assign a spatial reference system object to the array.
13144 : *
13145 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13146 : * @return TRUE in case of success.
13147 : */
13148 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13149 : {
13150 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13151 60 : return hArray->m_poImpl->SetSpatialRef(
13152 60 : OGRSpatialReference::FromHandle(hSRS));
13153 : }
13154 :
13155 : /************************************************************************/
13156 : /* GDALMDArrayGetSpatialRef() */
13157 : /************************************************************************/
13158 :
13159 : /** Return the spatial reference system object associated with the array.
13160 : *
13161 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13162 : *
13163 : * The returned object must be freed with OSRDestroySpatialReference().
13164 : */
13165 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13166 : {
13167 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13168 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13169 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13170 : }
13171 :
13172 : /************************************************************************/
13173 : /* GDALMDArrayGetStatistics() */
13174 : /************************************************************************/
13175 :
13176 : /**
13177 : * \brief Fetch statistics.
13178 : *
13179 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13180 : *
13181 : * @since GDAL 3.2
13182 : */
13183 :
13184 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13185 : int bApproxOK, int bForce, double *pdfMin,
13186 : double *pdfMax, double *pdfMean,
13187 : double *pdfStdDev, GUInt64 *pnValidCount,
13188 : GDALProgressFunc pfnProgress,
13189 : void *pProgressData)
13190 : {
13191 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13192 30 : return hArray->m_poImpl->GetStatistics(
13193 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13194 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13195 : }
13196 :
13197 : /************************************************************************/
13198 : /* GDALMDArrayComputeStatistics() */
13199 : /************************************************************************/
13200 :
13201 : /**
13202 : * \brief Compute statistics.
13203 : *
13204 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13205 : *
13206 : * @since GDAL 3.2
13207 : * @see GDALMDArrayComputeStatisticsEx()
13208 : */
13209 :
13210 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13211 : int bApproxOK, double *pdfMin, double *pdfMax,
13212 : double *pdfMean, double *pdfStdDev,
13213 : GUInt64 *pnValidCount,
13214 : GDALProgressFunc pfnProgress,
13215 : void *pProgressData)
13216 : {
13217 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13218 0 : return hArray->m_poImpl->ComputeStatistics(
13219 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13220 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13221 : }
13222 :
13223 : /************************************************************************/
13224 : /* GDALMDArrayComputeStatisticsEx() */
13225 : /************************************************************************/
13226 :
13227 : /**
13228 : * \brief Compute statistics.
13229 : *
13230 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13231 : *
13232 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13233 : *
13234 : * @since GDAL 3.8
13235 : */
13236 :
13237 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13238 : int bApproxOK, double *pdfMin,
13239 : double *pdfMax, double *pdfMean,
13240 : double *pdfStdDev, GUInt64 *pnValidCount,
13241 : GDALProgressFunc pfnProgress,
13242 : void *pProgressData,
13243 : CSLConstList papszOptions)
13244 : {
13245 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13246 8 : return hArray->m_poImpl->ComputeStatistics(
13247 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13248 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13249 : }
13250 :
13251 : /************************************************************************/
13252 : /* GDALMDArrayGetCoordinateVariables() */
13253 : /************************************************************************/
13254 :
13255 : /** Return coordinate variables.
13256 : *
13257 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13258 : * itself needs to be freed, CPLFree() should be called (and
13259 : * GDALMDArrayRelease() on individual array members).
13260 : *
13261 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13262 : *
13263 : * @param hArray Array.
13264 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13265 : *
13266 : * @return an array of *pnCount arrays.
13267 : * @since 3.4
13268 : */
13269 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13270 : size_t *pnCount)
13271 : {
13272 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13273 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13274 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13275 : auto ret = static_cast<GDALMDArrayH *>(
13276 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13277 29 : for (size_t i = 0; i < coordinates.size(); i++)
13278 : {
13279 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13280 : }
13281 13 : *pnCount = coordinates.size();
13282 13 : return ret;
13283 : }
13284 :
13285 : /************************************************************************/
13286 : /* GDALMDArrayGetGridded() */
13287 : /************************************************************************/
13288 :
13289 : /** Return a gridded array from scattered point data, that is from an array
13290 : * whose last dimension is the indexing variable of X and Y arrays.
13291 : *
13292 : * The returned object should be released with GDALMDArrayRelease().
13293 : *
13294 : * This is the same as the C++ method GDALMDArray::GetGridded().
13295 : *
13296 : * @since GDAL 3.7
13297 : */
13298 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13299 : const char *pszGridOptions,
13300 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13301 : CSLConstList papszOptions)
13302 : {
13303 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13304 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13305 22 : auto gridded = hArray->m_poImpl->GetGridded(
13306 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13307 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13308 22 : if (!gridded)
13309 19 : return nullptr;
13310 3 : return new GDALMDArrayHS(gridded);
13311 : }
13312 :
13313 : /************************************************************************/
13314 : /* GDALMDArrayGetMeshGrid() */
13315 : /************************************************************************/
13316 :
13317 : /** Return a list of multidimensional arrays from a list of one-dimensional
13318 : * arrays.
13319 : *
13320 : * This is typically used to transform one-dimensional longitude, latitude
13321 : * arrays into 2D ones.
13322 : *
13323 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13324 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13325 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13326 : * repeated to fill the matrix along the first dimension for x1, the second
13327 : * for x2 and so on.
13328 : *
13329 : * For example, if x = [1, 2], and y = [3, 4, 5],
13330 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13331 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13332 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13333 : *
13334 : * and
13335 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13336 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13337 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13338 : *
13339 : * The currently supported options are:
13340 : * <ul>
13341 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13342 : * output.
13343 : * </li>
13344 : * </ul>
13345 : *
13346 : * This is the same as
13347 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13348 : * function.
13349 : *
13350 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13351 : * If only the array itself needs to be freed, CPLFree() should be called
13352 : * (and GDALMDArrayRelease() on individual array members).
13353 : *
13354 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13355 : *
13356 : * @param pahInputArrays Input arrays
13357 : * @param nCountInputArrays Number of input arrays
13358 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13359 : * @param papszOptions NULL, or NULL terminated list of options.
13360 : *
13361 : * @return an array of *pnCountOutputArrays arrays.
13362 : * @since 3.10
13363 : */
13364 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13365 : size_t nCountInputArrays,
13366 : size_t *pnCountOutputArrays,
13367 : CSLConstList papszOptions)
13368 : {
13369 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13370 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13371 :
13372 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13373 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13374 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13375 :
13376 : const auto apoOutputArrays =
13377 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13378 : auto ret = static_cast<GDALMDArrayH *>(
13379 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13380 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13381 : {
13382 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13383 : }
13384 7 : *pnCountOutputArrays = apoOutputArrays.size();
13385 7 : return ret;
13386 : }
13387 :
13388 : /************************************************************************/
13389 : /* GDALReleaseArrays() */
13390 : /************************************************************************/
13391 :
13392 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13393 : *
13394 : * @param arrays return pointer of above methods
13395 : * @param nCount *pnCount value returned by above methods
13396 : */
13397 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13398 : {
13399 46 : for (size_t i = 0; i < nCount; i++)
13400 : {
13401 26 : delete arrays[i];
13402 : }
13403 20 : CPLFree(arrays);
13404 20 : }
13405 :
13406 : /************************************************************************/
13407 : /* GDALMDArrayCache() */
13408 : /************************************************************************/
13409 :
13410 : /**
13411 : * \brief Cache the content of the array into an auxiliary filename.
13412 : *
13413 : * This is the same as the C++ method GDALMDArray::Cache().
13414 : *
13415 : * @since GDAL 3.4
13416 : */
13417 :
13418 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13419 : {
13420 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13421 7 : return hArray->m_poImpl->Cache(papszOptions);
13422 : }
13423 :
13424 : /************************************************************************/
13425 : /* GDALMDArrayRename() */
13426 : /************************************************************************/
13427 :
13428 : /** Rename the array.
13429 : *
13430 : * This is not implemented by all drivers.
13431 : *
13432 : * Drivers known to implement it: MEM, netCDF, Zarr.
13433 : *
13434 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13435 : *
13436 : * @return true in case of success
13437 : * @since GDAL 3.8
13438 : */
13439 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13440 : {
13441 28 : VALIDATE_POINTER1(hArray, __func__, false);
13442 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13443 28 : return hArray->m_poImpl->Rename(pszNewName);
13444 : }
13445 :
13446 : /************************************************************************/
13447 : /* GDALAttributeRelease() */
13448 : /************************************************************************/
13449 :
13450 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13451 : *
13452 : * Note: when applied on a object coming from a driver, this does not
13453 : * destroy the object in the file, database, etc...
13454 : */
13455 737 : void GDALAttributeRelease(GDALAttributeH hAttr)
13456 : {
13457 737 : delete hAttr;
13458 737 : }
13459 :
13460 : /************************************************************************/
13461 : /* GDALAttributeGetName() */
13462 : /************************************************************************/
13463 :
13464 : /** Return the name of the attribute.
13465 : *
13466 : * The returned pointer is valid until hAttr is released.
13467 : *
13468 : * This is the same as the C++ method GDALAttribute::GetName().
13469 : */
13470 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13471 : {
13472 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13473 361 : return hAttr->m_poImpl->GetName().c_str();
13474 : }
13475 :
13476 : /************************************************************************/
13477 : /* GDALAttributeGetFullName() */
13478 : /************************************************************************/
13479 :
13480 : /** Return the full name of the attribute.
13481 : *
13482 : * The returned pointer is valid until hAttr is released.
13483 : *
13484 : * This is the same as the C++ method GDALAttribute::GetFullName().
13485 : */
13486 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13487 : {
13488 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13489 49 : return hAttr->m_poImpl->GetFullName().c_str();
13490 : }
13491 :
13492 : /************************************************************************/
13493 : /* GDALAttributeGetTotalElementsCount() */
13494 : /************************************************************************/
13495 :
13496 : /** Return the total number of values in the attribute.
13497 : *
13498 : * This is the same as the C++ method
13499 : * GDALAbstractMDArray::GetTotalElementsCount()
13500 : */
13501 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13502 : {
13503 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13504 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13505 : }
13506 :
13507 : /************************************************************************/
13508 : /* GDALAttributeGetDimensionCount() */
13509 : /************************************************************************/
13510 :
13511 : /** Return the number of dimensions.
13512 : *
13513 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13514 : */
13515 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13516 : {
13517 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13518 12 : return hAttr->m_poImpl->GetDimensionCount();
13519 : }
13520 :
13521 : /************************************************************************/
13522 : /* GDALAttributeGetDimensionsSize() */
13523 : /************************************************************************/
13524 :
13525 : /** Return the dimension sizes of the attribute.
13526 : *
13527 : * The returned array must be freed with CPLFree()
13528 : *
13529 : * @param hAttr Attribute.
13530 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13531 : *
13532 : * @return an array of *pnCount values.
13533 : */
13534 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13535 : {
13536 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13537 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13538 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13539 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13540 22 : for (size_t i = 0; i < dims.size(); i++)
13541 : {
13542 11 : ret[i] = dims[i]->GetSize();
13543 : }
13544 11 : *pnCount = dims.size();
13545 11 : return ret;
13546 : }
13547 :
13548 : /************************************************************************/
13549 : /* GDALAttributeGetDataType() */
13550 : /************************************************************************/
13551 :
13552 : /** Return the data type
13553 : *
13554 : * The return must be freed with GDALExtendedDataTypeRelease().
13555 : */
13556 433 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13557 : {
13558 433 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13559 : return new GDALExtendedDataTypeHS(
13560 433 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13561 : }
13562 :
13563 : /************************************************************************/
13564 : /* GDALAttributeReadAsRaw() */
13565 : /************************************************************************/
13566 :
13567 : /** Return the raw value of an attribute.
13568 : *
13569 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13570 : *
13571 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13572 : *
13573 : * @param hAttr Attribute.
13574 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13575 : *
13576 : * @return a buffer of *pnSize bytes.
13577 : */
13578 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13579 : {
13580 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13581 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13582 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13583 6 : *pnSize = res.size();
13584 6 : auto ret = res.StealData();
13585 6 : if (!ret)
13586 : {
13587 0 : *pnSize = 0;
13588 0 : return nullptr;
13589 : }
13590 6 : return ret;
13591 : }
13592 :
13593 : /************************************************************************/
13594 : /* GDALAttributeFreeRawResult() */
13595 : /************************************************************************/
13596 :
13597 : /** Free the return of GDALAttributeAsRaw()
13598 : */
13599 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13600 : CPL_UNUSED size_t nSize)
13601 : {
13602 6 : VALIDATE_POINTER0(hAttr, __func__);
13603 6 : if (raw)
13604 : {
13605 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13606 6 : const auto nDTSize(dt.GetSize());
13607 6 : GByte *pabyPtr = raw;
13608 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13609 6 : CPLAssert(nSize == nDTSize * nEltCount);
13610 12 : for (size_t i = 0; i < nEltCount; ++i)
13611 : {
13612 6 : dt.FreeDynamicMemory(pabyPtr);
13613 6 : pabyPtr += nDTSize;
13614 : }
13615 6 : CPLFree(raw);
13616 : }
13617 : }
13618 :
13619 : /************************************************************************/
13620 : /* GDALAttributeReadAsString() */
13621 : /************************************************************************/
13622 :
13623 : /** Return the value of an attribute as a string.
13624 : *
13625 : * The returned string should not be freed, and its lifetime does not
13626 : * excess a next call to ReadAsString() on the same object, or the deletion
13627 : * of the object itself.
13628 : *
13629 : * This function will only return the first element if there are several.
13630 : *
13631 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13632 : *
13633 : * @return a string, or nullptr.
13634 : */
13635 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13636 : {
13637 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13638 107 : return hAttr->m_poImpl->ReadAsString();
13639 : }
13640 :
13641 : /************************************************************************/
13642 : /* GDALAttributeReadAsInt() */
13643 : /************************************************************************/
13644 :
13645 : /** Return the value of an attribute as a integer.
13646 : *
13647 : * This function will only return the first element if there are several.
13648 : *
13649 : * It can fail if its value can not be converted to integer.
13650 : *
13651 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13652 : *
13653 : * @return a integer, or INT_MIN in case of error.
13654 : */
13655 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13656 : {
13657 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13658 22 : return hAttr->m_poImpl->ReadAsInt();
13659 : }
13660 :
13661 : /************************************************************************/
13662 : /* GDALAttributeReadAsInt64() */
13663 : /************************************************************************/
13664 :
13665 : /** Return the value of an attribute as a int64_t.
13666 : *
13667 : * This function will only return the first element if there are several.
13668 : *
13669 : * It can fail if its value can not be converted to integer.
13670 : *
13671 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13672 : *
13673 : * @return an int64_t, or INT64_MIN in case of error.
13674 : */
13675 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13676 : {
13677 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13678 15 : return hAttr->m_poImpl->ReadAsInt64();
13679 : }
13680 :
13681 : /************************************************************************/
13682 : /* GDALAttributeReadAsDouble() */
13683 : /************************************************************************/
13684 :
13685 : /** Return the value of an attribute as a double.
13686 : *
13687 : * This function will only return the first element if there are several.
13688 : *
13689 : * It can fail if its value can not be converted to double.
13690 : *
13691 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13692 : *
13693 : * @return a double value.
13694 : */
13695 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13696 : {
13697 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13698 40 : return hAttr->m_poImpl->ReadAsDouble();
13699 : }
13700 :
13701 : /************************************************************************/
13702 : /* GDALAttributeReadAsStringArray() */
13703 : /************************************************************************/
13704 :
13705 : /** Return the value of an attribute as an array of strings.
13706 : *
13707 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13708 : *
13709 : * The return value must be freed with CSLDestroy().
13710 : */
13711 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13712 : {
13713 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13714 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13715 : }
13716 :
13717 : /************************************************************************/
13718 : /* GDALAttributeReadAsIntArray() */
13719 : /************************************************************************/
13720 :
13721 : /** Return the value of an attribute as an array of integers.
13722 : *
13723 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13724 : *
13725 : * @param hAttr Attribute
13726 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13727 : * @return array to be freed with CPLFree(), or nullptr.
13728 : */
13729 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13730 : {
13731 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13732 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13733 15 : *pnCount = 0;
13734 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13735 15 : if (tmp.empty())
13736 0 : return nullptr;
13737 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13738 15 : if (!ret)
13739 0 : return nullptr;
13740 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13741 15 : *pnCount = tmp.size();
13742 15 : return ret;
13743 : }
13744 :
13745 : /************************************************************************/
13746 : /* GDALAttributeReadAsInt64Array() */
13747 : /************************************************************************/
13748 :
13749 : /** Return the value of an attribute as an array of int64_t.
13750 : *
13751 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13752 : *
13753 : * @param hAttr Attribute
13754 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13755 : * @return array to be freed with CPLFree(), or nullptr.
13756 : */
13757 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13758 : {
13759 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13760 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13761 14 : *pnCount = 0;
13762 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13763 14 : if (tmp.empty())
13764 0 : return nullptr;
13765 : auto ret = static_cast<int64_t *>(
13766 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13767 14 : if (!ret)
13768 0 : return nullptr;
13769 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13770 14 : *pnCount = tmp.size();
13771 14 : return ret;
13772 : }
13773 :
13774 : /************************************************************************/
13775 : /* GDALAttributeReadAsDoubleArray() */
13776 : /************************************************************************/
13777 :
13778 : /** Return the value of an attribute as an array of doubles.
13779 : *
13780 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13781 : *
13782 : * @param hAttr Attribute
13783 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13784 : * @return array to be freed with CPLFree(), or nullptr.
13785 : */
13786 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13787 : {
13788 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13789 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13790 29 : *pnCount = 0;
13791 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13792 29 : if (tmp.empty())
13793 0 : return nullptr;
13794 : auto ret =
13795 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13796 29 : if (!ret)
13797 0 : return nullptr;
13798 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13799 29 : *pnCount = tmp.size();
13800 29 : return ret;
13801 : }
13802 :
13803 : /************************************************************************/
13804 : /* GDALAttributeWriteRaw() */
13805 : /************************************************************************/
13806 :
13807 : /** Write an attribute from raw values expressed in GetDataType()
13808 : *
13809 : * The values should be provided in the type of GetDataType() and there should
13810 : * be exactly GetTotalElementsCount() of them.
13811 : * If GetDataType() is a string, each value should be a char* pointer.
13812 : *
13813 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13814 : *
13815 : * @param hAttr Attribute
13816 : * @param pabyValue Buffer of nLen bytes.
13817 : * @param nLength Size of pabyValue in bytes. Should be equal to
13818 : * GetTotalElementsCount() * GetDataType().GetSize()
13819 : * @return TRUE in case of success.
13820 : */
13821 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13822 : size_t nLength)
13823 : {
13824 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13825 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13826 : }
13827 :
13828 : /************************************************************************/
13829 : /* GDALAttributeWriteString() */
13830 : /************************************************************************/
13831 :
13832 : /** Write an attribute from a string value.
13833 : *
13834 : * Type conversion will be performed if needed. If the attribute contains
13835 : * multiple values, only the first one will be updated.
13836 : *
13837 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13838 : *
13839 : * @param hAttr Attribute
13840 : * @param pszVal Pointer to a string.
13841 : * @return TRUE in case of success.
13842 : */
13843 181 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13844 : {
13845 181 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13846 181 : return hAttr->m_poImpl->Write(pszVal);
13847 : }
13848 :
13849 : /************************************************************************/
13850 : /* GDALAttributeWriteInt() */
13851 : /************************************************************************/
13852 :
13853 : /** Write an attribute from a integer value.
13854 : *
13855 : * Type conversion will be performed if needed. If the attribute contains
13856 : * multiple values, only the first one will be updated.
13857 : *
13858 : * This is the same as the C++ method GDALAttribute::WriteInt()
13859 : *
13860 : * @param hAttr Attribute
13861 : * @param nVal Value.
13862 : * @return TRUE in case of success.
13863 : */
13864 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13865 : {
13866 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13867 22 : return hAttr->m_poImpl->WriteInt(nVal);
13868 : }
13869 :
13870 : /************************************************************************/
13871 : /* GDALAttributeWriteInt64() */
13872 : /************************************************************************/
13873 :
13874 : /** Write an attribute from an int64_t value.
13875 : *
13876 : * Type conversion will be performed if needed. If the attribute contains
13877 : * multiple values, only the first one will be updated.
13878 : *
13879 : * This is the same as the C++ method GDALAttribute::WriteLong()
13880 : *
13881 : * @param hAttr Attribute
13882 : * @param nVal Value.
13883 : * @return TRUE in case of success.
13884 : */
13885 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13886 : {
13887 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13888 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13889 : }
13890 :
13891 : /************************************************************************/
13892 : /* GDALAttributeWriteDouble() */
13893 : /************************************************************************/
13894 :
13895 : /** Write an attribute from a double value.
13896 : *
13897 : * Type conversion will be performed if needed. If the attribute contains
13898 : * multiple values, only the first one will be updated.
13899 : *
13900 : * This is the same as the C++ method GDALAttribute::Write(double);
13901 : *
13902 : * @param hAttr Attribute
13903 : * @param dfVal Value.
13904 : *
13905 : * @return TRUE in case of success.
13906 : */
13907 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13908 : {
13909 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13910 11 : return hAttr->m_poImpl->Write(dfVal);
13911 : }
13912 :
13913 : /************************************************************************/
13914 : /* GDALAttributeWriteStringArray() */
13915 : /************************************************************************/
13916 :
13917 : /** Write an attribute from an array of strings.
13918 : *
13919 : * Type conversion will be performed if needed.
13920 : *
13921 : * Exactly GetTotalElementsCount() strings must be provided
13922 : *
13923 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13924 : *
13925 : * @param hAttr Attribute
13926 : * @param papszValues Array of strings.
13927 : * @return TRUE in case of success.
13928 : */
13929 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13930 : CSLConstList papszValues)
13931 : {
13932 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13933 8 : return hAttr->m_poImpl->Write(papszValues);
13934 : }
13935 :
13936 : /************************************************************************/
13937 : /* GDALAttributeWriteIntArray() */
13938 : /************************************************************************/
13939 :
13940 : /** Write an attribute from an array of int.
13941 : *
13942 : * Type conversion will be performed if needed.
13943 : *
13944 : * Exactly GetTotalElementsCount() strings must be provided
13945 : *
13946 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13947 : * size_t)
13948 : *
13949 : * @param hAttr Attribute
13950 : * @param panValues Array of int.
13951 : * @param nCount Should be equal to GetTotalElementsCount().
13952 : * @return TRUE in case of success.
13953 : */
13954 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13955 : size_t nCount)
13956 : {
13957 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13958 11 : return hAttr->m_poImpl->Write(panValues, nCount);
13959 : }
13960 :
13961 : /************************************************************************/
13962 : /* GDALAttributeWriteInt64Array() */
13963 : /************************************************************************/
13964 :
13965 : /** Write an attribute from an array of int64_t.
13966 : *
13967 : * Type conversion will be performed if needed.
13968 : *
13969 : * Exactly GetTotalElementsCount() strings must be provided
13970 : *
13971 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13972 : * size_t)
13973 : *
13974 : * @param hAttr Attribute
13975 : * @param panValues Array of int64_t.
13976 : * @param nCount Should be equal to GetTotalElementsCount().
13977 : * @return TRUE in case of success.
13978 : */
13979 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13980 : size_t nCount)
13981 : {
13982 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13983 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13984 : }
13985 :
13986 : /************************************************************************/
13987 : /* GDALAttributeWriteDoubleArray() */
13988 : /************************************************************************/
13989 :
13990 : /** Write an attribute from an array of double.
13991 : *
13992 : * Type conversion will be performed if needed.
13993 : *
13994 : * Exactly GetTotalElementsCount() strings must be provided
13995 : *
13996 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13997 : * size_t)
13998 : *
13999 : * @param hAttr Attribute
14000 : * @param padfValues Array of double.
14001 : * @param nCount Should be equal to GetTotalElementsCount().
14002 : * @return TRUE in case of success.
14003 : */
14004 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14005 : const double *padfValues, size_t nCount)
14006 : {
14007 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14008 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14009 : }
14010 :
14011 : /************************************************************************/
14012 : /* GDALAttributeRename() */
14013 : /************************************************************************/
14014 :
14015 : /** Rename the attribute.
14016 : *
14017 : * This is not implemented by all drivers.
14018 : *
14019 : * Drivers known to implement it: MEM, netCDF.
14020 : *
14021 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14022 : *
14023 : * @return true in case of success
14024 : * @since GDAL 3.8
14025 : */
14026 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14027 : {
14028 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14029 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14030 27 : return hAttr->m_poImpl->Rename(pszNewName);
14031 : }
14032 :
14033 : /************************************************************************/
14034 : /* GDALDimensionRelease() */
14035 : /************************************************************************/
14036 :
14037 : /** Release the GDAL in-memory object associated with a GDALDimension.
14038 : *
14039 : * Note: when applied on a object coming from a driver, this does not
14040 : * destroy the object in the file, database, etc...
14041 : */
14042 4924 : void GDALDimensionRelease(GDALDimensionH hDim)
14043 : {
14044 4924 : delete hDim;
14045 4924 : }
14046 :
14047 : /************************************************************************/
14048 : /* GDALDimensionGetName() */
14049 : /************************************************************************/
14050 :
14051 : /** Return dimension name.
14052 : *
14053 : * This is the same as the C++ method GDALDimension::GetName()
14054 : */
14055 286 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14056 : {
14057 286 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14058 286 : return hDim->m_poImpl->GetName().c_str();
14059 : }
14060 :
14061 : /************************************************************************/
14062 : /* GDALDimensionGetFullName() */
14063 : /************************************************************************/
14064 :
14065 : /** Return dimension full name.
14066 : *
14067 : * This is the same as the C++ method GDALDimension::GetFullName()
14068 : */
14069 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14070 : {
14071 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14072 82 : return hDim->m_poImpl->GetFullName().c_str();
14073 : }
14074 :
14075 : /************************************************************************/
14076 : /* GDALDimensionGetType() */
14077 : /************************************************************************/
14078 :
14079 : /** Return dimension type.
14080 : *
14081 : * This is the same as the C++ method GDALDimension::GetType()
14082 : */
14083 64 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14084 : {
14085 64 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14086 64 : return hDim->m_poImpl->GetType().c_str();
14087 : }
14088 :
14089 : /************************************************************************/
14090 : /* GDALDimensionGetDirection() */
14091 : /************************************************************************/
14092 :
14093 : /** Return dimension direction.
14094 : *
14095 : * This is the same as the C++ method GDALDimension::GetDirection()
14096 : */
14097 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14098 : {
14099 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14100 32 : return hDim->m_poImpl->GetDirection().c_str();
14101 : }
14102 :
14103 : /************************************************************************/
14104 : /* GDALDimensionGetSize() */
14105 : /************************************************************************/
14106 :
14107 : /** Return the size, that is the number of values along the dimension.
14108 : *
14109 : * This is the same as the C++ method GDALDimension::GetSize()
14110 : */
14111 3696 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14112 : {
14113 3696 : VALIDATE_POINTER1(hDim, __func__, 0);
14114 3696 : return hDim->m_poImpl->GetSize();
14115 : }
14116 :
14117 : /************************************************************************/
14118 : /* GDALDimensionGetIndexingVariable() */
14119 : /************************************************************************/
14120 :
14121 : /** Return the variable that is used to index the dimension (if there is one).
14122 : *
14123 : * This is the array, typically one-dimensional, describing the values taken
14124 : * by the dimension.
14125 : *
14126 : * The returned value should be freed with GDALMDArrayRelease().
14127 : *
14128 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14129 : */
14130 120 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14131 : {
14132 120 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14133 240 : auto var(hDim->m_poImpl->GetIndexingVariable());
14134 120 : if (!var)
14135 10 : return nullptr;
14136 110 : return new GDALMDArrayHS(var);
14137 : }
14138 :
14139 : /************************************************************************/
14140 : /* GDALDimensionSetIndexingVariable() */
14141 : /************************************************************************/
14142 :
14143 : /** Set the variable that is used to index the dimension.
14144 : *
14145 : * This is the array, typically one-dimensional, describing the values taken
14146 : * by the dimension.
14147 : *
14148 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14149 : *
14150 : * @return TRUE in case of success.
14151 : */
14152 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14153 : {
14154 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14155 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14156 46 : : nullptr);
14157 : }
14158 :
14159 : /************************************************************************/
14160 : /* GDALDimensionRename() */
14161 : /************************************************************************/
14162 :
14163 : /** Rename the dimension.
14164 : *
14165 : * This is not implemented by all drivers.
14166 : *
14167 : * Drivers known to implement it: MEM, netCDF.
14168 : *
14169 : * This is the same as the C++ method GDALDimension::Rename()
14170 : *
14171 : * @return true in case of success
14172 : * @since GDAL 3.8
14173 : */
14174 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14175 : {
14176 31 : VALIDATE_POINTER1(hDim, __func__, false);
14177 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14178 31 : return hDim->m_poImpl->Rename(pszNewName);
14179 : }
14180 :
14181 : /************************************************************************/
14182 : /* GDALDatasetGetRootGroup() */
14183 : /************************************************************************/
14184 :
14185 : /** Return the root GDALGroup of this dataset.
14186 : *
14187 : * Only valid for multidimensional datasets.
14188 : *
14189 : * The returned value must be freed with GDALGroupRelease().
14190 : *
14191 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14192 : *
14193 : * @since GDAL 3.1
14194 : */
14195 1184 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14196 : {
14197 1184 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14198 1184 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14199 1184 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14200 : }
14201 :
14202 : /************************************************************************/
14203 : /* GDALRasterBandAsMDArray() */
14204 : /************************************************************************/
14205 :
14206 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14207 : *
14208 : * The band must be linked to a GDALDataset. If this dataset is not already
14209 : * marked as shared, it will be, so that the returned array holds a reference
14210 : * to it.
14211 : *
14212 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14213 : * returned array will have an associated indexing variable.
14214 : *
14215 : * The returned pointer must be released with GDALMDArrayRelease().
14216 : *
14217 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14218 : *
14219 : * @return a new array, or NULL.
14220 : *
14221 : * @since GDAL 3.1
14222 : */
14223 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14224 : {
14225 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14226 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14227 21 : if (!poArray)
14228 0 : return nullptr;
14229 21 : return new GDALMDArrayHS(poArray);
14230 : }
14231 :
14232 : /************************************************************************/
14233 : /* GDALMDArrayAsClassicDataset() */
14234 : /************************************************************************/
14235 :
14236 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14237 : *
14238 : * Only 2D or more arrays are supported.
14239 : *
14240 : * In the case of > 2D arrays, additional dimensions will be represented as
14241 : * raster bands.
14242 : *
14243 : * The "reverse" method is GDALRasterBand::AsMDArray().
14244 : *
14245 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14246 : *
14247 : * @param hArray Array.
14248 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14249 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14250 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14251 : */
14252 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14253 : size_t iYDim)
14254 : {
14255 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14256 0 : return GDALDataset::ToHandle(
14257 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14258 : }
14259 :
14260 : /************************************************************************/
14261 : /* GDALMDArrayAsClassicDatasetEx() */
14262 : /************************************************************************/
14263 :
14264 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14265 : *
14266 : * Only 2D or more arrays are supported.
14267 : *
14268 : * In the case of > 2D arrays, additional dimensions will be represented as
14269 : * raster bands.
14270 : *
14271 : * The "reverse" method is GDALRasterBand::AsMDArray().
14272 : *
14273 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14274 : * @param hArray Array.
14275 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14276 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14277 : * Ignored if the dimension count is 1.
14278 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14279 : * BAND_IMAGERY_METADATA option.
14280 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14281 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14282 : * @since GDAL 3.8
14283 : */
14284 93 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14285 : size_t iYDim, GDALGroupH hRootGroup,
14286 : CSLConstList papszOptions)
14287 : {
14288 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14289 186 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14290 186 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14291 186 : papszOptions));
14292 : }
14293 :
14294 : //! @cond Doxygen_Suppress
14295 :
14296 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14297 : const std::string &osName,
14298 : const std::string &osValue,
14299 180 : GDALExtendedDataTypeSubType eSubType)
14300 : : GDALAbstractMDArray(osParentName, osName),
14301 : GDALAttribute(osParentName, osName),
14302 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14303 : {
14304 180 : }
14305 :
14306 : const std::vector<std::shared_ptr<GDALDimension>> &
14307 30 : GDALAttributeString::GetDimensions() const
14308 : {
14309 30 : return m_dims;
14310 : }
14311 :
14312 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14313 : {
14314 21 : return m_dt;
14315 : }
14316 :
14317 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14318 : const GPtrDiff_t *,
14319 : const GDALExtendedDataType &bufferDataType,
14320 : void *pDstBuffer) const
14321 : {
14322 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14323 0 : return false;
14324 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14325 10 : if (!pszStr)
14326 0 : return false;
14327 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14328 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14329 10 : return true;
14330 : }
14331 :
14332 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14333 : const std::string &osName,
14334 66 : double dfValue)
14335 : : GDALAbstractMDArray(osParentName, osName),
14336 : GDALAttribute(osParentName, osName),
14337 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14338 : {
14339 66 : }
14340 :
14341 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14342 : const std::string &osName,
14343 27 : int nValue)
14344 : : GDALAbstractMDArray(osParentName, osName),
14345 : GDALAttribute(osParentName, osName),
14346 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14347 : {
14348 27 : }
14349 :
14350 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14351 : const std::string &osName,
14352 7 : const std::vector<GUInt32> &anValues)
14353 : : GDALAbstractMDArray(osParentName, osName),
14354 : GDALAttribute(osParentName, osName),
14355 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14356 : {
14357 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14358 14 : std::string(), "dim0", std::string(), std::string(),
14359 7 : m_anValuesUInt32.size()));
14360 7 : }
14361 :
14362 : const std::vector<std::shared_ptr<GDALDimension>> &
14363 14 : GDALAttributeNumeric::GetDimensions() const
14364 : {
14365 14 : return m_dims;
14366 : }
14367 :
14368 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14369 : {
14370 8 : return m_dt;
14371 : }
14372 :
14373 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14374 : const size_t *count, const GInt64 *arrayStep,
14375 : const GPtrDiff_t *bufferStride,
14376 : const GDALExtendedDataType &bufferDataType,
14377 : void *pDstBuffer) const
14378 : {
14379 4 : if (m_dims.empty())
14380 : {
14381 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14382 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14383 : bufferDataType);
14384 : else
14385 : {
14386 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14387 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14388 : bufferDataType);
14389 : }
14390 : }
14391 : else
14392 : {
14393 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14394 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14395 30 : for (size_t i = 0; i < count[0]; ++i)
14396 : {
14397 29 : GDALExtendedDataType::CopyValue(
14398 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14399 29 : i * arrayStep[0])],
14400 29 : m_dt, pabyDstBuffer, bufferDataType);
14401 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14402 : }
14403 : }
14404 4 : return true;
14405 : }
14406 :
14407 196 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14408 : const std::string &osParentName, const std::string &osName,
14409 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14410 196 : double dfIncrement, double dfOffsetInIncrement)
14411 : : GDALAbstractMDArray(osParentName, osName),
14412 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14413 : m_dfIncrement(dfIncrement),
14414 392 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14415 : {
14416 196 : }
14417 :
14418 196 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14419 : const std::string &osParentName, const std::string &osName,
14420 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14421 : double dfIncrement, double dfOffsetInIncrement)
14422 : {
14423 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14424 196 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14425 196 : poArray->SetSelf(poArray);
14426 196 : return poArray;
14427 : }
14428 :
14429 : const std::vector<std::shared_ptr<GDALDimension>> &
14430 802 : GDALMDArrayRegularlySpaced::GetDimensions() const
14431 : {
14432 802 : return m_dims;
14433 : }
14434 :
14435 322 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14436 : {
14437 322 : return m_dt;
14438 : }
14439 :
14440 : std::vector<std::shared_ptr<GDALAttribute>>
14441 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14442 : {
14443 4 : return m_attributes;
14444 : }
14445 :
14446 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14447 : const std::shared_ptr<GDALAttribute> &poAttr)
14448 : {
14449 0 : m_attributes.emplace_back(poAttr);
14450 0 : }
14451 :
14452 190 : bool GDALMDArrayRegularlySpaced::IRead(
14453 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14454 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14455 : void *pDstBuffer) const
14456 : {
14457 190 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14458 14730 : for (size_t i = 0; i < count[0]; i++)
14459 : {
14460 14540 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
14461 14540 : m_dfOffsetInIncrement) *
14462 14540 : m_dfIncrement;
14463 14540 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14464 : bufferDataType);
14465 14540 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14466 : }
14467 190 : return true;
14468 : }
14469 :
14470 3047 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14471 : const std::string &osParentName, const std::string &osName,
14472 3047 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14473 3047 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14474 : {
14475 3047 : }
14476 :
14477 : std::shared_ptr<GDALMDArray>
14478 743 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14479 : {
14480 743 : return m_poIndexingVariable.lock();
14481 : }
14482 :
14483 : // cppcheck-suppress passedByValue
14484 510 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14485 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14486 : {
14487 510 : m_poIndexingVariable = poIndexingVariable;
14488 510 : return true;
14489 : }
14490 :
14491 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14492 : {
14493 33 : m_nSize = nNewSize;
14494 33 : }
14495 :
14496 : /************************************************************************/
14497 : /* GDALPamMultiDim::Private */
14498 : /************************************************************************/
14499 :
14500 : struct GDALPamMultiDim::Private
14501 : {
14502 : std::string m_osFilename{};
14503 : std::string m_osPamFilename{};
14504 :
14505 : struct Statistics
14506 : {
14507 : bool bHasStats = false;
14508 : bool bApproxStats = false;
14509 : double dfMin = 0;
14510 : double dfMax = 0;
14511 : double dfMean = 0;
14512 : double dfStdDev = 0;
14513 : GUInt64 nValidCount = 0;
14514 : };
14515 :
14516 : struct ArrayInfo
14517 : {
14518 : std::shared_ptr<OGRSpatialReference> poSRS{};
14519 : // cppcheck-suppress unusedStructMember
14520 : Statistics stats{};
14521 : };
14522 :
14523 : typedef std::pair<std::string, std::string> NameContext;
14524 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14525 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14526 : bool m_bDirty = false;
14527 : bool m_bLoaded = false;
14528 : };
14529 :
14530 : /************************************************************************/
14531 : /* GDALPamMultiDim */
14532 : /************************************************************************/
14533 :
14534 1463 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14535 1463 : : d(new Private())
14536 : {
14537 1463 : d->m_osFilename = osFilename;
14538 1463 : }
14539 :
14540 : /************************************************************************/
14541 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14542 : /************************************************************************/
14543 :
14544 1463 : GDALPamMultiDim::~GDALPamMultiDim()
14545 : {
14546 1463 : if (d->m_bDirty)
14547 30 : Save();
14548 1463 : }
14549 :
14550 : /************************************************************************/
14551 : /* GDALPamMultiDim::Load() */
14552 : /************************************************************************/
14553 :
14554 107 : void GDALPamMultiDim::Load()
14555 : {
14556 107 : if (d->m_bLoaded)
14557 96 : return;
14558 45 : d->m_bLoaded = true;
14559 :
14560 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14561 45 : d->m_osPamFilename =
14562 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14563 45 : CPLXMLTreeCloser oTree(nullptr);
14564 : {
14565 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14566 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14567 : }
14568 45 : if (!oTree)
14569 : {
14570 34 : return;
14571 : }
14572 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14573 11 : if (!poPAMMultiDim)
14574 0 : return;
14575 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14576 24 : psIter = psIter->psNext)
14577 : {
14578 24 : if (psIter->eType == CXT_Element &&
14579 24 : strcmp(psIter->pszValue, "Array") == 0)
14580 : {
14581 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14582 13 : if (!pszName)
14583 0 : continue;
14584 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14585 : const auto oKey =
14586 26 : std::pair<std::string, std::string>(pszName, pszContext);
14587 :
14588 : /* --------------------------------------------------------------------
14589 : */
14590 : /* Check for an SRS node. */
14591 : /* --------------------------------------------------------------------
14592 : */
14593 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14594 13 : if (psSRSNode)
14595 : {
14596 : std::shared_ptr<OGRSpatialReference> poSRS =
14597 6 : std::make_shared<OGRSpatialReference>();
14598 3 : poSRS->SetFromUserInput(
14599 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14600 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14601 3 : const char *pszMapping = CPLGetXMLValue(
14602 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14603 3 : if (pszMapping)
14604 : {
14605 : char **papszTokens =
14606 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14607 6 : std::vector<int> anMapping;
14608 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14609 : {
14610 6 : anMapping.push_back(atoi(papszTokens[i]));
14611 : }
14612 3 : CSLDestroy(papszTokens);
14613 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14614 : }
14615 : else
14616 : {
14617 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14618 : }
14619 :
14620 : const char *pszCoordinateEpoch =
14621 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14622 3 : if (pszCoordinateEpoch)
14623 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14624 :
14625 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14626 : }
14627 :
14628 : const CPLXMLNode *psStatistics =
14629 13 : CPLGetXMLNode(psIter, "Statistics");
14630 13 : if (psStatistics)
14631 : {
14632 7 : Private::Statistics sStats;
14633 7 : sStats.bHasStats = true;
14634 7 : sStats.bApproxStats = CPLTestBool(
14635 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14636 7 : sStats.dfMin =
14637 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14638 7 : sStats.dfMax =
14639 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14640 7 : sStats.dfMean =
14641 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14642 7 : sStats.dfStdDev =
14643 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14644 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14645 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14646 7 : d->m_oMapArray[oKey].stats = sStats;
14647 13 : }
14648 : }
14649 : else
14650 : {
14651 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14652 11 : psIter->psNext = nullptr;
14653 11 : d->m_apoOtherNodes.emplace_back(
14654 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14655 11 : psIter->psNext = psNextBackup;
14656 : }
14657 : }
14658 : }
14659 :
14660 : /************************************************************************/
14661 : /* GDALPamMultiDim::Save() */
14662 : /************************************************************************/
14663 :
14664 30 : void GDALPamMultiDim::Save()
14665 : {
14666 : CPLXMLTreeCloser oTree(
14667 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14668 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14669 : {
14670 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14671 : }
14672 112 : for (const auto &kv : d->m_oMapArray)
14673 : {
14674 : CPLXMLNode *psArrayNode =
14675 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14676 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14677 82 : if (!kv.first.second.empty())
14678 : {
14679 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14680 : kv.first.second.c_str());
14681 : }
14682 82 : if (kv.second.poSRS)
14683 : {
14684 71 : char *pszWKT = nullptr;
14685 : {
14686 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14687 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14688 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14689 : }
14690 : CPLXMLNode *psSRSNode =
14691 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14692 71 : CPLFree(pszWKT);
14693 : const auto &mapping =
14694 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14695 142 : CPLString osMapping;
14696 213 : for (size_t i = 0; i < mapping.size(); ++i)
14697 : {
14698 142 : if (!osMapping.empty())
14699 71 : osMapping += ",";
14700 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14701 : }
14702 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14703 : osMapping.c_str());
14704 :
14705 : const double dfCoordinateEpoch =
14706 71 : kv.second.poSRS->GetCoordinateEpoch();
14707 71 : if (dfCoordinateEpoch > 0)
14708 : {
14709 : std::string osCoordinateEpoch =
14710 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14711 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14712 : {
14713 6 : while (osCoordinateEpoch.back() == '0')
14714 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14715 : }
14716 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14717 : osCoordinateEpoch.c_str());
14718 : }
14719 : }
14720 :
14721 82 : if (kv.second.stats.bHasStats)
14722 : {
14723 : CPLXMLNode *psMDArray =
14724 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14725 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14726 8 : kv.second.stats.bApproxStats ? "1"
14727 : : "0");
14728 8 : CPLCreateXMLElementAndValue(
14729 : psMDArray, "Minimum",
14730 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14731 8 : CPLCreateXMLElementAndValue(
14732 : psMDArray, "Maximum",
14733 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14734 8 : CPLCreateXMLElementAndValue(
14735 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14736 8 : CPLCreateXMLElementAndValue(
14737 : psMDArray, "StdDev",
14738 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14739 8 : CPLCreateXMLElementAndValue(
14740 : psMDArray, "ValidSampleCount",
14741 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14742 : }
14743 : }
14744 :
14745 : int bSaved;
14746 60 : CPLErrorAccumulator oErrorAccumulator;
14747 : {
14748 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14749 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14750 : bSaved =
14751 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14752 : }
14753 :
14754 30 : const char *pszNewPam = nullptr;
14755 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14756 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14757 : {
14758 0 : CPLErrorReset();
14759 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14760 : }
14761 : else
14762 : {
14763 30 : oErrorAccumulator.ReplayErrors();
14764 : }
14765 30 : }
14766 :
14767 : /************************************************************************/
14768 : /* GDALPamMultiDim::GetSpatialRef() */
14769 : /************************************************************************/
14770 :
14771 : std::shared_ptr<OGRSpatialReference>
14772 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14773 : const std::string &osContext)
14774 : {
14775 10 : Load();
14776 : auto oIter =
14777 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14778 10 : if (oIter != d->m_oMapArray.end())
14779 2 : return oIter->second.poSRS;
14780 8 : return nullptr;
14781 : }
14782 :
14783 : /************************************************************************/
14784 : /* GDALPamMultiDim::SetSpatialRef() */
14785 : /************************************************************************/
14786 :
14787 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14788 : const std::string &osContext,
14789 : const OGRSpatialReference *poSRS)
14790 : {
14791 72 : Load();
14792 72 : d->m_bDirty = true;
14793 72 : if (poSRS && !poSRS->IsEmpty())
14794 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14795 : poSRS->Clone());
14796 : else
14797 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14798 1 : .poSRS.reset();
14799 72 : }
14800 :
14801 : /************************************************************************/
14802 : /* GetStatistics() */
14803 : /************************************************************************/
14804 :
14805 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14806 : const std::string &osContext,
14807 : bool bApproxOK, double *pdfMin,
14808 : double *pdfMax, double *pdfMean,
14809 : double *pdfStdDev, GUInt64 *pnValidCount)
14810 : {
14811 16 : Load();
14812 : auto oIter =
14813 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14814 16 : if (oIter == d->m_oMapArray.end())
14815 9 : return CE_Failure;
14816 7 : const auto &stats = oIter->second.stats;
14817 7 : if (!stats.bHasStats)
14818 1 : return CE_Failure;
14819 6 : if (!bApproxOK && stats.bApproxStats)
14820 0 : return CE_Failure;
14821 6 : if (pdfMin)
14822 6 : *pdfMin = stats.dfMin;
14823 6 : if (pdfMax)
14824 6 : *pdfMax = stats.dfMax;
14825 6 : if (pdfMean)
14826 6 : *pdfMean = stats.dfMean;
14827 6 : if (pdfStdDev)
14828 6 : *pdfStdDev = stats.dfStdDev;
14829 6 : if (pnValidCount)
14830 6 : *pnValidCount = stats.nValidCount;
14831 6 : return CE_None;
14832 : }
14833 :
14834 : /************************************************************************/
14835 : /* SetStatistics() */
14836 : /************************************************************************/
14837 :
14838 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14839 : const std::string &osContext,
14840 : bool bApproxStats, double dfMin,
14841 : double dfMax, double dfMean,
14842 : double dfStdDev, GUInt64 nValidCount)
14843 : {
14844 8 : Load();
14845 8 : d->m_bDirty = true;
14846 : auto &stats =
14847 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14848 8 : stats.bHasStats = true;
14849 8 : stats.bApproxStats = bApproxStats;
14850 8 : stats.dfMin = dfMin;
14851 8 : stats.dfMax = dfMax;
14852 8 : stats.dfMean = dfMean;
14853 8 : stats.dfStdDev = dfStdDev;
14854 8 : stats.nValidCount = nValidCount;
14855 8 : }
14856 :
14857 : /************************************************************************/
14858 : /* ClearStatistics() */
14859 : /************************************************************************/
14860 :
14861 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14862 : const std::string &osContext)
14863 : {
14864 0 : Load();
14865 0 : d->m_bDirty = true;
14866 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14867 : false;
14868 0 : }
14869 :
14870 : /************************************************************************/
14871 : /* ClearStatistics() */
14872 : /************************************************************************/
14873 :
14874 1 : void GDALPamMultiDim::ClearStatistics()
14875 : {
14876 1 : Load();
14877 1 : d->m_bDirty = true;
14878 3 : for (auto &kv : d->m_oMapArray)
14879 2 : kv.second.stats.bHasStats = false;
14880 1 : }
14881 :
14882 : /************************************************************************/
14883 : /* GetPAM() */
14884 : /************************************************************************/
14885 :
14886 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14887 815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14888 : {
14889 815 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14890 815 : if (poPamArray)
14891 567 : return poPamArray->GetPAM();
14892 248 : return nullptr;
14893 : }
14894 :
14895 : /************************************************************************/
14896 : /* GDALPamMDArray */
14897 : /************************************************************************/
14898 :
14899 3782 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14900 : const std::string &osName,
14901 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14902 0 : const std::string &osContext)
14903 : :
14904 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14905 : GDALAbstractMDArray(osParentName, osName),
14906 : #endif
14907 3782 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14908 : {
14909 3782 : }
14910 :
14911 : /************************************************************************/
14912 : /* GDALPamMDArray::SetSpatialRef() */
14913 : /************************************************************************/
14914 :
14915 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14916 : {
14917 72 : if (!m_poPam)
14918 0 : return false;
14919 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14920 72 : return true;
14921 : }
14922 :
14923 : /************************************************************************/
14924 : /* GDALPamMDArray::GetSpatialRef() */
14925 : /************************************************************************/
14926 :
14927 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14928 : {
14929 10 : if (!m_poPam)
14930 0 : return nullptr;
14931 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14932 : }
14933 :
14934 : /************************************************************************/
14935 : /* GetStatistics() */
14936 : /************************************************************************/
14937 :
14938 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14939 : double *pdfMin, double *pdfMax,
14940 : double *pdfMean, double *pdfStdDev,
14941 : GUInt64 *pnValidCount,
14942 : GDALProgressFunc pfnProgress,
14943 : void *pProgressData)
14944 : {
14945 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14946 : bApproxOK, pdfMin, pdfMax, pdfMean,
14947 16 : pdfStdDev, pnValidCount) == CE_None)
14948 : {
14949 6 : return CE_None;
14950 : }
14951 10 : if (!bForce)
14952 4 : return CE_Warning;
14953 :
14954 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14955 : pdfMean, pdfStdDev, pnValidCount,
14956 6 : pfnProgress, pProgressData);
14957 : }
14958 :
14959 : /************************************************************************/
14960 : /* SetStatistics() */
14961 : /************************************************************************/
14962 :
14963 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14964 : double dfMax, double dfMean, double dfStdDev,
14965 : GUInt64 nValidCount,
14966 : CSLConstList /* papszOptions */)
14967 : {
14968 8 : if (!m_poPam)
14969 0 : return false;
14970 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14971 : dfMax, dfMean, dfStdDev, nValidCount);
14972 8 : return true;
14973 : }
14974 :
14975 : /************************************************************************/
14976 : /* ClearStatistics() */
14977 : /************************************************************************/
14978 :
14979 0 : void GDALPamMDArray::ClearStatistics()
14980 : {
14981 0 : if (!m_poPam)
14982 0 : return;
14983 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14984 : }
14985 :
14986 : //! @endcond
|