Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_pam_multidim.h"
31 : #include "gdal_rat.h"
32 : #include "gdal_utils.h"
33 : #include "cpl_safemaths.hpp"
34 : #include "memmultidim.h"
35 : #include "ogrsf_frmts.h"
36 : #include "gdalmultidim_priv.h"
37 :
38 : #if defined(__clang__) || defined(_MSC_VER)
39 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40 : #endif
41 :
42 : /************************************************************************/
43 : /* GDALMDArrayUnscaled */
44 : /************************************************************************/
45 :
46 : class GDALMDArrayUnscaled final : public GDALPamMDArray
47 : {
48 : private:
49 : std::shared_ptr<GDALMDArray> m_poParent{};
50 : const GDALExtendedDataType m_dt;
51 : bool m_bHasNoData;
52 : const double m_dfScale;
53 : const double m_dfOffset;
54 : std::vector<GByte> m_abyRawNoData{};
55 :
56 : protected:
57 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58 : double dfScale, double dfOffset,
59 : double dfOverriddenDstNodata, GDALDataType eDT)
60 26 : : GDALAbstractMDArray(std::string(),
61 26 : "Unscaled view of " + poParent->GetFullName()),
62 : GDALPamMDArray(
63 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
64 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65 13 : m_poParent(std::move(poParent)),
66 : m_dt(GDALExtendedDataType::Create(eDT)),
67 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
69 : {
70 13 : m_abyRawNoData.resize(m_dt.GetSize());
71 : const auto eNonComplexDT =
72 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73 26 : GDALCopyWords64(
74 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77 13 : }
78 :
79 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81 : const GDALExtendedDataType &bufferDataType,
82 : void *pDstBuffer) const override;
83 :
84 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86 : const GDALExtendedDataType &bufferDataType,
87 : const void *pSrcBuffer) override;
88 :
89 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90 : CSLConstList papszOptions) const override
91 : {
92 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93 : }
94 :
95 : public:
96 : static std::shared_ptr<GDALMDArrayUnscaled>
97 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98 : double dfOffset, double dfDstNodata, GDALDataType eDT)
99 : {
100 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102 13 : newAr->SetSelf(newAr);
103 13 : return newAr;
104 : }
105 :
106 1 : bool IsWritable() const override
107 : {
108 1 : return m_poParent->IsWritable();
109 : }
110 :
111 15 : const std::string &GetFilename() const override
112 : {
113 15 : return m_poParent->GetFilename();
114 : }
115 :
116 : const std::vector<std::shared_ptr<GDALDimension>> &
117 220 : GetDimensions() const override
118 : {
119 220 : return m_poParent->GetDimensions();
120 : }
121 :
122 103 : const GDALExtendedDataType &GetDataType() const override
123 : {
124 103 : return m_dt;
125 : }
126 :
127 1 : const std::string &GetUnit() const override
128 : {
129 1 : return m_poParent->GetUnit();
130 : }
131 :
132 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133 : {
134 1 : return m_poParent->GetSpatialRef();
135 : }
136 :
137 6 : const void *GetRawNoDataValue() const override
138 : {
139 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140 : }
141 :
142 1 : bool SetRawNoDataValue(const void *pRawNoData) override
143 : {
144 1 : m_bHasNoData = true;
145 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146 1 : return true;
147 : }
148 :
149 4 : std::vector<GUInt64> GetBlockSize() const override
150 : {
151 4 : return m_poParent->GetBlockSize();
152 : }
153 :
154 : std::shared_ptr<GDALAttribute>
155 0 : GetAttribute(const std::string &osName) const override
156 : {
157 0 : return m_poParent->GetAttribute(osName);
158 : }
159 :
160 : std::vector<std::shared_ptr<GDALAttribute>>
161 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
162 : {
163 1 : return m_poParent->GetAttributes(papszOptions);
164 : }
165 :
166 0 : bool SetUnit(const std::string &osUnit) override
167 : {
168 0 : return m_poParent->SetUnit(osUnit);
169 : }
170 :
171 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172 : {
173 0 : return m_poParent->SetSpatialRef(poSRS);
174 : }
175 :
176 : std::shared_ptr<GDALAttribute>
177 1 : CreateAttribute(const std::string &osName,
178 : const std::vector<GUInt64> &anDimensions,
179 : const GDALExtendedDataType &oDataType,
180 : CSLConstList papszOptions = nullptr) override
181 : {
182 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183 1 : papszOptions);
184 : }
185 : };
186 :
187 : /************************************************************************/
188 : /* ~GDALIHasAttribute() */
189 : /************************************************************************/
190 :
191 : GDALIHasAttribute::~GDALIHasAttribute() = default;
192 :
193 : /************************************************************************/
194 : /* GetAttribute() */
195 : /************************************************************************/
196 :
197 : /** Return an attribute by its name.
198 : *
199 : * If the attribute does not exist, nullptr should be silently returned.
200 : *
201 : * @note Driver implementation: this method will fallback to
202 : * GetAttributeFromAttributes() is not explicitly implemented
203 : *
204 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
205 : *
206 : * This is the same as the C function GDALGroupGetAttribute() or
207 : * GDALMDArrayGetAttribute().
208 : *
209 : * @param osName Attribute name
210 : * @return the attribute, or nullptr if it does not exist or an error occurred.
211 : */
212 : std::shared_ptr<GDALAttribute>
213 1064 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
214 : {
215 1064 : return GetAttributeFromAttributes(osName);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetAttributeFromAttributes() */
220 : /************************************************************************/
221 :
222 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
223 : */
224 : std::shared_ptr<GDALAttribute>
225 1064 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226 : {
227 2128 : auto attrs(GetAttributes());
228 5500 : for (const auto &attr : attrs)
229 : {
230 5175 : if (attr->GetName() == osName)
231 739 : return attr;
232 : }
233 325 : return nullptr;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetAttributes() */
238 : /************************************************************************/
239 :
240 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241 : *
242 : * If the attribute does not exist, nullptr should be silently returned.
243 : *
244 : * @note Driver implementation: optionally implemented. If implemented,
245 : * GetAttribute() should also be implemented.
246 : *
247 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
248 : *
249 : * This is the same as the C function GDALGroupGetAttributes() or
250 : * GDALMDArrayGetAttributes().
251 :
252 : * @param papszOptions Driver specific options determining how attributes
253 : * should be retrieved. Pass nullptr for default behavior.
254 : *
255 : * @return the attributes.
256 : */
257 : std::vector<std::shared_ptr<GDALAttribute>>
258 44 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259 : {
260 44 : return {};
261 : }
262 :
263 : /************************************************************************/
264 : /* CreateAttribute() */
265 : /************************************************************************/
266 :
267 : /** Create an attribute within a GDALMDArray or GDALGroup.
268 : *
269 : * The attribute might not be "physically" created until a value is written
270 : * into it.
271 : *
272 : * Optionally implemented.
273 : *
274 : * Drivers known to implement it: MEM, netCDF
275 : *
276 : * This is the same as the C function GDALGroupCreateAttribute() or
277 : * GDALMDArrayCreateAttribute()
278 : *
279 : * @param osName Attribute name.
280 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
281 : * dimension first to the fastest varying dimension last.
282 : * Empty for a scalar attribute (common case)
283 : * @param oDataType Attribute data type.
284 : * @param papszOptions Driver specific options determining how the attribute.
285 : * should be created.
286 : *
287 : * @return the new attribute, or nullptr if case of error
288 : */
289 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290 : CPL_UNUSED const std::string &osName,
291 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292 : CPL_UNUSED const GDALExtendedDataType &oDataType,
293 : CPL_UNUSED CSLConstList papszOptions)
294 : {
295 0 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "CreateAttribute() not implemented");
297 0 : return nullptr;
298 : }
299 :
300 : /************************************************************************/
301 : /* DeleteAttribute() */
302 : /************************************************************************/
303 :
304 : /** Delete an attribute from a GDALMDArray or GDALGroup.
305 : *
306 : * Optionally implemented.
307 : *
308 : * After this call, if a previously obtained instance of the deleted object
309 : * is still alive, no method other than for freeing it should be invoked.
310 : *
311 : * Drivers known to implement it: MEM, netCDF
312 : *
313 : * This is the same as the C function GDALGroupDeleteAttribute() or
314 : * GDALMDArrayDeleteAttribute()
315 : *
316 : * @param osName Attribute name.
317 : * @param papszOptions Driver specific options determining how the attribute.
318 : * should be deleted.
319 : *
320 : * @return true in case of success
321 : * @since GDAL 3.8
322 : */
323 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324 : CPL_UNUSED CSLConstList papszOptions)
325 : {
326 0 : CPLError(CE_Failure, CPLE_NotSupported,
327 : "DeleteAttribute() not implemented");
328 0 : return false;
329 : }
330 :
331 : /************************************************************************/
332 : /* GDALGroup() */
333 : /************************************************************************/
334 :
335 : //! @cond Doxygen_Suppress
336 6988 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 6988 : const std::string &osContext)
338 6988 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 13976 : !osParentName.empty()
341 10817 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 17805 : m_osContext(osContext)
344 : {
345 6988 : }
346 :
347 : //! @endcond
348 :
349 : /************************************************************************/
350 : /* ~GDALGroup() */
351 : /************************************************************************/
352 :
353 : GDALGroup::~GDALGroup() = default;
354 :
355 : /************************************************************************/
356 : /* GetMDArrayNames() */
357 : /************************************************************************/
358 :
359 : /** Return the list of multidimensional array names contained in this group.
360 : *
361 : * @note Driver implementation: optionally implemented. If implemented,
362 : * OpenMDArray() should also be implemented.
363 : *
364 : * Drivers known to implement it: MEM, netCDF.
365 : *
366 : * This is the same as the C function GDALGroupGetMDArrayNames().
367 : *
368 : * @param papszOptions Driver specific options determining how arrays
369 : * should be retrieved. Pass nullptr for default behavior.
370 : *
371 : * @return the array names.
372 : */
373 : std::vector<std::string>
374 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375 : {
376 0 : return {};
377 : }
378 :
379 : /************************************************************************/
380 : /* GetMDArrayFullNamesRecursive() */
381 : /************************************************************************/
382 :
383 : /** Return the list of multidimensional array full names contained in this
384 : * group and its subgroups.
385 : *
386 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387 : *
388 : * @param papszGroupOptions Driver specific options determining how groups
389 : * should be retrieved. Pass nullptr for default behavior.
390 : * @param papszArrayOptions Driver specific options determining how arrays
391 : * should be retrieved. Pass nullptr for default behavior.
392 : *
393 : * @return the array full names.
394 : *
395 : * @since 3.11
396 : */
397 : std::vector<std::string>
398 3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399 : CSLConstList papszArrayOptions) const
400 : {
401 3 : std::vector<std::string> ret;
402 6 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
403 3 : stackGroups.push_back(nullptr); // nullptr means this
404 9 : while (!stackGroups.empty())
405 : {
406 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407 6 : stackGroups.erase(stackGroups.begin());
408 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409 10 : for (const std::string &arrayName :
410 26 : poCurGroup->GetMDArrayNames(papszArrayOptions))
411 : {
412 20 : std::string osFullName = poCurGroup->GetFullName();
413 10 : if (!osFullName.empty() && osFullName.back() != '/')
414 3 : osFullName += '/';
415 10 : osFullName += arrayName;
416 10 : ret.push_back(std::move(osFullName));
417 : }
418 6 : auto insertionPoint = stackGroups.begin();
419 3 : for (const auto &osSubGroup :
420 12 : poCurGroup->GetGroupNames(papszGroupOptions))
421 : {
422 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423 3 : if (poSubGroup)
424 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
425 : }
426 : }
427 :
428 6 : return ret;
429 : }
430 :
431 : /************************************************************************/
432 : /* OpenMDArray() */
433 : /************************************************************************/
434 :
435 : /** Open and return a multidimensional array.
436 : *
437 : * @note Driver implementation: optionally implemented. If implemented,
438 : * GetMDArrayNames() should also be implemented.
439 : *
440 : * Drivers known to implement it: MEM, netCDF.
441 : *
442 : * This is the same as the C function GDALGroupOpenMDArray().
443 : *
444 : * @param osName Array name.
445 : * @param papszOptions Driver specific options determining how the array should
446 : * be opened. Pass nullptr for default behavior.
447 : *
448 : * @return the array, or nullptr.
449 : */
450 : std::shared_ptr<GDALMDArray>
451 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452 : CPL_UNUSED CSLConstList papszOptions) const
453 : {
454 0 : return nullptr;
455 : }
456 :
457 : /************************************************************************/
458 : /* GetGroupNames() */
459 : /************************************************************************/
460 :
461 : /** Return the list of sub-groups contained in this group.
462 : *
463 : * @note Driver implementation: optionally implemented. If implemented,
464 : * OpenGroup() should also be implemented.
465 : *
466 : * Drivers known to implement it: MEM, netCDF.
467 : *
468 : * This is the same as the C function GDALGroupGetGroupNames().
469 : *
470 : * @param papszOptions Driver specific options determining how groups
471 : * should be retrieved. Pass nullptr for default behavior.
472 : *
473 : * @return the group names.
474 : */
475 : std::vector<std::string>
476 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477 : {
478 4 : return {};
479 : }
480 :
481 : /************************************************************************/
482 : /* OpenGroup() */
483 : /************************************************************************/
484 :
485 : /** Open and return a sub-group.
486 : *
487 : * @note Driver implementation: optionally implemented. If implemented,
488 : * GetGroupNames() should also be implemented.
489 : *
490 : * Drivers known to implement it: MEM, netCDF.
491 : *
492 : * This is the same as the C function GDALGroupOpenGroup().
493 : *
494 : * @param osName Sub-group name.
495 : * @param papszOptions Driver specific options determining how the sub-group
496 : * should be opened. Pass nullptr for default behavior.
497 : *
498 : * @return the group, or nullptr.
499 : */
500 : std::shared_ptr<GDALGroup>
501 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502 : CPL_UNUSED CSLConstList papszOptions) const
503 : {
504 4 : return nullptr;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetVectorLayerNames() */
509 : /************************************************************************/
510 :
511 : /** Return the list of layer names contained in this group.
512 : *
513 : * @note Driver implementation: optionally implemented. If implemented,
514 : * OpenVectorLayer() should also be implemented.
515 : *
516 : * Drivers known to implement it: OpenFileGDB, FileGDB
517 : *
518 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519 : * GDALDataset::GetLayer() should then be used.
520 : *
521 : * This is the same as the C function GDALGroupGetVectorLayerNames().
522 : *
523 : * @param papszOptions Driver specific options determining how layers
524 : * should be retrieved. Pass nullptr for default behavior.
525 : *
526 : * @return the vector layer names.
527 : * @since GDAL 3.4
528 : */
529 : std::vector<std::string>
530 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 1 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* OpenVectorLayer() */
537 : /************************************************************************/
538 :
539 : /** Open and return a vector layer.
540 : *
541 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542 : * lifetime of the returned OGRLayer* is linked to the one of the owner
543 : * dataset (contrary to the general design of this class where objects can be
544 : * used independently of the object that returned them)
545 : *
546 : * @note Driver implementation: optionally implemented. If implemented,
547 : * GetVectorLayerNames() should also be implemented.
548 : *
549 : * Drivers known to implement it: MEM, netCDF.
550 : *
551 : * This is the same as the C function GDALGroupOpenVectorLayer().
552 : *
553 : * @param osName Vector layer name.
554 : * @param papszOptions Driver specific options determining how the layer should
555 : * be opened. Pass nullptr for default behavior.
556 : *
557 : * @return the group, or nullptr.
558 : */
559 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560 : CPL_UNUSED CSLConstList papszOptions) const
561 : {
562 2 : return nullptr;
563 : }
564 :
565 : /************************************************************************/
566 : /* GetDimensions() */
567 : /************************************************************************/
568 :
569 : /** Return the list of dimensions contained in this group and used by its
570 : * arrays.
571 : *
572 : * This is for dimensions that can potentially be used by several arrays.
573 : * Not all drivers might implement this. To retrieve the dimensions used by
574 : * a specific array, use GDALMDArray::GetDimensions().
575 : *
576 : * Drivers known to implement it: MEM, netCDF
577 : *
578 : * This is the same as the C function GDALGroupGetDimensions().
579 : *
580 : * @param papszOptions Driver specific options determining how groups
581 : * should be retrieved. Pass nullptr for default behavior.
582 : *
583 : * @return the dimensions.
584 : */
585 : std::vector<std::shared_ptr<GDALDimension>>
586 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587 : {
588 11 : return {};
589 : }
590 :
591 : /************************************************************************/
592 : /* GetStructuralInfo() */
593 : /************************************************************************/
594 :
595 : /** Return structural information on the group.
596 : *
597 : * This may be the compression, etc..
598 : *
599 : * The return value should not be freed and is valid until GDALGroup is
600 : * released or this function called again.
601 : *
602 : * This is the same as the C function GDALGroupGetStructuralInfo().
603 : */
604 42 : CSLConstList GDALGroup::GetStructuralInfo() const
605 : {
606 42 : return nullptr;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateGroup() */
611 : /************************************************************************/
612 :
613 : /** Create a sub-group within a group.
614 : *
615 : * Optionally implemented by drivers.
616 : *
617 : * Drivers known to implement it: MEM, netCDF
618 : *
619 : * This is the same as the C function GDALGroupCreateGroup().
620 : *
621 : * @param osName Sub-group name.
622 : * @param papszOptions Driver specific options determining how the sub-group
623 : * should be created.
624 : *
625 : * @return the new sub-group, or nullptr in case of error.
626 : */
627 : std::shared_ptr<GDALGroup>
628 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629 : CPL_UNUSED CSLConstList papszOptions)
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632 0 : return nullptr;
633 : }
634 :
635 : /************************************************************************/
636 : /* DeleteGroup() */
637 : /************************************************************************/
638 :
639 : /** Delete a sub-group from a group.
640 : *
641 : * Optionally implemented.
642 : *
643 : * After this call, if a previously obtained instance of the deleted object
644 : * is still alive, no method other than for freeing it should be invoked.
645 : *
646 : * Drivers known to implement it: MEM, Zarr
647 : *
648 : * This is the same as the C function GDALGroupDeleteGroup().
649 : *
650 : * @param osName Sub-group name.
651 : * @param papszOptions Driver specific options determining how the group.
652 : * should be deleted.
653 : *
654 : * @return true in case of success
655 : * @since GDAL 3.8
656 : */
657 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658 : CPL_UNUSED CSLConstList papszOptions)
659 : {
660 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661 0 : return false;
662 : }
663 :
664 : /************************************************************************/
665 : /* CreateDimension() */
666 : /************************************************************************/
667 :
668 : /** Create a dimension within a group.
669 : *
670 : * @note Driver implementation: drivers supporting CreateDimension() should
671 : * implement this method, but do not have necessarily to implement
672 : * GDALGroup::GetDimensions().
673 : *
674 : * Drivers known to implement it: MEM, netCDF
675 : *
676 : * This is the same as the C function GDALGroupCreateDimension().
677 : *
678 : * @param osName Dimension name.
679 : * @param osType Dimension type (might be empty, and ignored by drivers)
680 : * @param osDirection Dimension direction (might be empty, and ignored by
681 : * drivers)
682 : * @param nSize Number of values indexed by this dimension. Should be > 0.
683 : * @param papszOptions Driver specific options determining how the dimension
684 : * should be created.
685 : *
686 : * @return the new dimension, or nullptr if case of error
687 : */
688 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691 : CPL_UNUSED CSLConstList papszOptions)
692 : {
693 0 : CPLError(CE_Failure, CPLE_NotSupported,
694 : "CreateDimension() not implemented");
695 0 : return nullptr;
696 : }
697 :
698 : /************************************************************************/
699 : /* CreateMDArray() */
700 : /************************************************************************/
701 :
702 : /** Create a multidimensional array within a group.
703 : *
704 : * It is recommended that the GDALDimension objects passed in aoDimensions
705 : * belong to this group, either by retrieving them with GetDimensions()
706 : * or creating a new one with CreateDimension().
707 : *
708 : * Optionally implemented.
709 : *
710 : * Drivers known to implement it: MEM, netCDF
711 : *
712 : * This is the same as the C function GDALGroupCreateMDArray().
713 : *
714 : * @note Driver implementation: drivers should take into account the possibility
715 : * that GDALDimension object passed in aoDimensions might belong to a different
716 : * group / dataset / driver and act accordingly.
717 : *
718 : * @param osName Array name.
719 : * @param aoDimensions List of dimensions, ordered from the slowest varying
720 : * dimension first to the fastest varying dimension last.
721 : * Might be empty for a scalar array (if supported by
722 : * driver)
723 : * @param oDataType Array data type.
724 : * @param papszOptions Driver specific options determining how the array
725 : * should be created.
726 : *
727 : * @return the new array, or nullptr in case of error
728 : */
729 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730 : CPL_UNUSED const std::string &osName,
731 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732 : CPL_UNUSED const GDALExtendedDataType &oDataType,
733 : CPL_UNUSED CSLConstList papszOptions)
734 : {
735 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736 0 : return nullptr;
737 : }
738 :
739 : /************************************************************************/
740 : /* DeleteMDArray() */
741 : /************************************************************************/
742 :
743 : /** Delete an array from a group.
744 : *
745 : * Optionally implemented.
746 : *
747 : * After this call, if a previously obtained instance of the deleted object
748 : * is still alive, no method other than for freeing it should be invoked.
749 : *
750 : * Drivers known to implement it: MEM, Zarr
751 : *
752 : * This is the same as the C function GDALGroupDeleteMDArray().
753 : *
754 : * @param osName Arrayname.
755 : * @param papszOptions Driver specific options determining how the array.
756 : * should be deleted.
757 : *
758 : * @return true in case of success
759 : * @since GDAL 3.8
760 : */
761 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762 : CPL_UNUSED CSLConstList papszOptions)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765 0 : return false;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetTotalCopyCost() */
770 : /************************************************************************/
771 :
772 : /** Return a total "cost" to copy the group.
773 : *
774 : * Used as a parameter for CopFrom()
775 : */
776 28 : GUInt64 GDALGroup::GetTotalCopyCost() const
777 : {
778 28 : GUInt64 nCost = COPY_COST;
779 28 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780 :
781 56 : auto groupNames = GetGroupNames();
782 34 : for (const auto &name : groupNames)
783 : {
784 12 : auto subGroup = OpenGroup(name);
785 6 : if (subGroup)
786 : {
787 6 : nCost += subGroup->GetTotalCopyCost();
788 : }
789 : }
790 :
791 28 : auto arrayNames = GetMDArrayNames();
792 82 : for (const auto &name : arrayNames)
793 : {
794 108 : auto array = OpenMDArray(name);
795 54 : if (array)
796 : {
797 54 : nCost += array->GetTotalCopyCost();
798 : }
799 : }
800 56 : return nCost;
801 : }
802 :
803 : /************************************************************************/
804 : /* CopyFrom() */
805 : /************************************************************************/
806 :
807 : /** Copy the content of a group into a new (generally empty) group.
808 : *
809 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
811 : * of some output drivers this is not recommended)
812 : * @param poSrcGroup Source group. Must NOT be nullptr.
813 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
814 : * stop the copy. In relaxed mode, the copy will be attempted to
815 : * be pursued.
816 : * @param nCurCost Should be provided as a variable initially set to 0.
817 : * @param nTotalCost Total cost from GetTotalCopyCost().
818 : * @param pfnProgress Progress callback, or nullptr.
819 : * @param pProgressData Progress user data, or nulptr.
820 : * @param papszOptions Creation options. Currently, only array creation
821 : * options are supported. They must be prefixed with
822 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
823 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
824 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
825 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
826 : * Restriction to arrays of a given name is done with adding
827 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
828 : * a full qualified name.
829 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
830 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
831 : * scaled to UInt16 with scale and offset values being computed from the minimum
832 : * and maximum of the source array. The integer data type used can be set with
833 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
834 : *
835 : * @return true in case of success (or partial success if bStrict == false).
836 : */
837 28 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
838 : GDALDataset *poSrcDS,
839 : const std::shared_ptr<GDALGroup> &poSrcGroup,
840 : bool bStrict, GUInt64 &nCurCost,
841 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
842 : void *pProgressData, CSLConstList papszOptions)
843 : {
844 28 : if (pfnProgress == nullptr)
845 0 : pfnProgress = GDALDummyProgress;
846 :
847 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
848 : if (!(x)) \
849 : { \
850 : if (bStrict) \
851 : return false; \
852 : continue; \
853 : } \
854 : (void)0
855 :
856 : try
857 : {
858 28 : nCurCost += GDALGroup::COPY_COST;
859 :
860 56 : const auto srcDims = poSrcGroup->GetDimensions();
861 : std::map<std::string, std::shared_ptr<GDALDimension>>
862 56 : mapExistingDstDims;
863 56 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
864 72 : for (const auto &dim : srcDims)
865 : {
866 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
867 44 : dim->GetDirection(), dim->GetSize());
868 44 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
869 44 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
870 88 : auto poIndexingVarSrc(dim->GetIndexingVariable());
871 44 : if (poIndexingVarSrc)
872 : {
873 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
874 25 : ->GetName()] =
875 50 : dim->GetName();
876 : }
877 : }
878 :
879 56 : auto attrs = poSrcGroup->GetAttributes();
880 38 : for (const auto &attr : attrs)
881 : {
882 : auto dstAttr =
883 10 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
884 20 : attr->GetDataType());
885 10 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
886 10 : auto raw(attr->ReadAsRaw());
887 10 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
888 0 : return false;
889 : }
890 28 : if (!attrs.empty())
891 : {
892 6 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
893 6 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
894 0 : return false;
895 : }
896 :
897 : const auto CopyArray =
898 54 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
899 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
900 : papszOptions, bStrict, &nCurCost,
901 486 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
902 : {
903 : // Map source dimensions to target dimensions
904 108 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
905 54 : const auto &srcArrayDims(srcArray->GetDimensions());
906 137 : for (const auto &dim : srcArrayDims)
907 : {
908 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
909 83 : dim->GetFullName());
910 83 : if (dstDim && dstDim->GetSize() == dim->GetSize())
911 : {
912 73 : dstArrayDims.emplace_back(dstDim);
913 : }
914 : else
915 : {
916 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
917 19 : if (oIter != mapExistingDstDims.end() &&
918 9 : oIter->second->GetSize() == dim->GetSize())
919 : {
920 8 : dstArrayDims.emplace_back(oIter->second);
921 : }
922 : else
923 : {
924 2 : std::string newDimName;
925 2 : if (oIter == mapExistingDstDims.end())
926 : {
927 1 : newDimName = dim->GetName();
928 : }
929 : else
930 : {
931 1 : std::string newDimNamePrefix(srcArray->GetName() +
932 3 : '_' + dim->GetName());
933 1 : newDimName = newDimNamePrefix;
934 1 : int nIterCount = 2;
935 0 : while (
936 1 : cpl::contains(mapExistingDstDims, newDimName))
937 : {
938 0 : newDimName = newDimNamePrefix +
939 0 : CPLSPrintf("_%d", nIterCount);
940 0 : nIterCount++;
941 : }
942 : }
943 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
944 : dim->GetDirection(),
945 4 : dim->GetSize());
946 2 : if (!dstDim)
947 0 : return false;
948 2 : mapExistingDstDims[newDimName] = dstDim;
949 2 : dstArrayDims.emplace_back(dstDim);
950 : }
951 : }
952 : }
953 :
954 108 : CPLStringList aosArrayCO;
955 54 : bool bAutoScale = false;
956 54 : GDALDataType eAutoScaleType = GDT_UInt16;
957 61 : for (const char *pszItem : cpl::Iterate(papszOptions))
958 : {
959 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
960 : {
961 7 : const char *pszOption = pszItem + strlen("ARRAY:");
962 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
963 : {
964 1 : const char *pszNext = strchr(pszOption, ':');
965 1 : if (pszNext != nullptr)
966 : {
967 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
968 1 : if (static_cast<size_t>(nDim) ==
969 1 : dstArrayDims.size())
970 : {
971 1 : pszOption = pszNext + 1;
972 : }
973 : else
974 : {
975 0 : pszOption = nullptr;
976 : }
977 : }
978 : }
979 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
980 : {
981 2 : const char *pszName = pszOption + strlen("IF(NAME=");
982 2 : const char *pszNext = strchr(pszName, ':');
983 2 : if (pszNext != nullptr && pszNext > pszName &&
984 2 : pszNext[-1] == ')')
985 : {
986 4 : CPLString osName;
987 2 : osName.assign(pszName, pszNext - pszName - 1);
988 3 : if (osName == srcArray->GetName() ||
989 1 : osName == srcArray->GetFullName())
990 : {
991 2 : pszOption = pszNext + 1;
992 : }
993 : else
994 : {
995 0 : pszOption = nullptr;
996 : }
997 : }
998 : }
999 7 : if (pszOption)
1000 : {
1001 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1002 : {
1003 : bAutoScale =
1004 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1005 : }
1006 5 : else if (STARTS_WITH_CI(pszOption,
1007 : "AUTOSCALE_DATA_TYPE="))
1008 : {
1009 1 : const char *pszDataType =
1010 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1011 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1012 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1013 1 : GDALDataTypeIsFloating(eAutoScaleType))
1014 : {
1015 0 : CPLError(CE_Failure, CPLE_NotSupported,
1016 : "Unsupported value for "
1017 : "AUTOSCALE_DATA_TYPE");
1018 0 : return false;
1019 : }
1020 : }
1021 : else
1022 : {
1023 4 : aosArrayCO.AddString(pszOption);
1024 : }
1025 : }
1026 : }
1027 : }
1028 :
1029 : auto oIterDimName =
1030 54 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1031 54 : const auto &srcArrayType = srcArray->GetDataType();
1032 :
1033 54 : std::shared_ptr<GDALMDArray> dstArray;
1034 :
1035 : // Only autoscale non-indexing variables
1036 54 : bool bHasOffset = false;
1037 54 : bool bHasScale = false;
1038 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1039 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1040 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1041 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1042 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1043 58 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1044 56 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1045 : {
1046 2 : constexpr bool bApproxOK = false;
1047 2 : constexpr bool bForce = true;
1048 2 : double dfMin = 0.0;
1049 2 : double dfMax = 0.0;
1050 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1051 : nullptr, nullptr, nullptr, nullptr,
1052 2 : nullptr) != CE_None)
1053 : {
1054 0 : CPLError(CE_Failure, CPLE_AppDefined,
1055 : "Could not retrieve statistics for array %s",
1056 0 : srcArray->GetName().c_str());
1057 0 : return false;
1058 : }
1059 2 : double dfDTMin = 0;
1060 2 : double dfDTMax = 0;
1061 : #define setDTMinMax(ctype) \
1062 : do \
1063 : { \
1064 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1065 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1066 : } while (0)
1067 :
1068 2 : switch (eAutoScaleType)
1069 : {
1070 0 : case GDT_Byte:
1071 0 : setDTMinMax(GByte);
1072 0 : break;
1073 0 : case GDT_Int8:
1074 0 : setDTMinMax(GInt8);
1075 0 : break;
1076 1 : case GDT_UInt16:
1077 1 : setDTMinMax(GUInt16);
1078 1 : break;
1079 1 : case GDT_Int16:
1080 1 : setDTMinMax(GInt16);
1081 1 : break;
1082 0 : case GDT_UInt32:
1083 0 : setDTMinMax(GUInt32);
1084 0 : break;
1085 0 : case GDT_Int32:
1086 0 : setDTMinMax(GInt32);
1087 0 : break;
1088 0 : case GDT_UInt64:
1089 0 : setDTMinMax(std::uint64_t);
1090 0 : break;
1091 0 : case GDT_Int64:
1092 0 : setDTMinMax(std::int64_t);
1093 0 : break;
1094 0 : case GDT_Float16:
1095 : case GDT_Float32:
1096 : case GDT_Float64:
1097 : case GDT_Unknown:
1098 : case GDT_CInt16:
1099 : case GDT_CInt32:
1100 : case GDT_CFloat16:
1101 : case GDT_CFloat32:
1102 : case GDT_CFloat64:
1103 : case GDT_TypeCount:
1104 0 : CPLAssert(false);
1105 : }
1106 :
1107 : dstArray =
1108 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1109 4 : GDALExtendedDataType::Create(eAutoScaleType),
1110 4 : aosArrayCO.List());
1111 2 : if (!dstArray)
1112 0 : return !bStrict;
1113 :
1114 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1115 : {
1116 : // If there's a nodata value in the source array, reserve
1117 : // DTMax for that purpose in the target scaled array
1118 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1119 : {
1120 0 : CPLError(CE_Failure, CPLE_AppDefined,
1121 : "Cannot set nodata value");
1122 0 : return false;
1123 : }
1124 1 : dfDTMax--;
1125 : }
1126 2 : const double dfScale =
1127 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1128 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1129 :
1130 4 : if (!dstArray->SetOffset(dfOffset) ||
1131 2 : !dstArray->SetScale(dfScale))
1132 : {
1133 0 : CPLError(CE_Failure, CPLE_AppDefined,
1134 : "Cannot set scale/offset");
1135 0 : return false;
1136 : }
1137 :
1138 2 : auto poUnscaled = dstArray->GetUnscaled();
1139 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1140 : {
1141 1 : poUnscaled->SetNoDataValue(
1142 : srcArray->GetNoDataValueAsDouble());
1143 : }
1144 :
1145 : // Copy source array into unscaled array
1146 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1147 : nCurCost, nTotalCost, pfnProgress,
1148 2 : pProgressData))
1149 : {
1150 0 : return false;
1151 : }
1152 : }
1153 : else
1154 : {
1155 104 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1156 104 : srcArrayType, aosArrayCO.List());
1157 52 : if (!dstArray)
1158 0 : return !bStrict;
1159 :
1160 104 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1161 : nCurCost, nTotalCost, pfnProgress,
1162 52 : pProgressData))
1163 : {
1164 0 : return false;
1165 : }
1166 : }
1167 :
1168 : // If this array is the indexing variable of a dimension, link them
1169 : // together.
1170 54 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1171 : {
1172 : auto oCorrespondingDimIter =
1173 25 : mapExistingDstDims.find(oIterDimName->second);
1174 25 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1175 : {
1176 : CPLErrorStateBackuper oErrorStateBackuper(
1177 25 : CPLQuietErrorHandler);
1178 50 : oCorrespondingDimIter->second->SetIndexingVariable(
1179 25 : std::move(dstArray));
1180 : }
1181 : }
1182 :
1183 54 : return true;
1184 28 : };
1185 :
1186 56 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1187 :
1188 : // Start by copying arrays that are indexing variables of dimensions
1189 82 : for (const auto &name : arrayNames)
1190 : {
1191 54 : auto srcArray = poSrcGroup->OpenMDArray(name);
1192 54 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1193 :
1194 54 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1195 54 : srcArray->GetName()))
1196 : {
1197 25 : if (!CopyArray(srcArray))
1198 0 : return false;
1199 : }
1200 : }
1201 :
1202 : // Then copy regular arrays
1203 82 : for (const auto &name : arrayNames)
1204 : {
1205 54 : auto srcArray = poSrcGroup->OpenMDArray(name);
1206 54 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1207 :
1208 54 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1209 54 : srcArray->GetName()))
1210 : {
1211 29 : if (!CopyArray(srcArray))
1212 0 : return false;
1213 : }
1214 : }
1215 :
1216 56 : const auto groupNames = poSrcGroup->GetGroupNames();
1217 34 : for (const auto &name : groupNames)
1218 : {
1219 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1220 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1221 6 : auto dstSubGroup = CreateGroup(name);
1222 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1223 12 : if (!dstSubGroup->CopyFrom(
1224 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1225 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1226 0 : return false;
1227 : }
1228 :
1229 28 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1230 0 : return false;
1231 :
1232 28 : return true;
1233 : }
1234 0 : catch (const std::exception &e)
1235 : {
1236 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1237 0 : return false;
1238 : }
1239 : }
1240 :
1241 : /************************************************************************/
1242 : /* GetInnerMostGroup() */
1243 : /************************************************************************/
1244 :
1245 : //! @cond Doxygen_Suppress
1246 : const GDALGroup *
1247 1328 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1248 : std::shared_ptr<GDALGroup> &curGroupHolder,
1249 : std::string &osLastPart) const
1250 : {
1251 1328 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1252 6 : return nullptr;
1253 1322 : const GDALGroup *poCurGroup = this;
1254 : CPLStringList aosTokens(
1255 2644 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1256 1322 : if (aosTokens.size() == 0)
1257 : {
1258 0 : return nullptr;
1259 : }
1260 :
1261 1667 : for (int i = 0; i < aosTokens.size() - 1; i++)
1262 : {
1263 357 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1264 357 : if (!curGroupHolder)
1265 : {
1266 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1267 : aosTokens[i]);
1268 12 : return nullptr;
1269 : }
1270 345 : poCurGroup = curGroupHolder.get();
1271 : }
1272 1310 : osLastPart = aosTokens[aosTokens.size() - 1];
1273 1310 : return poCurGroup;
1274 : }
1275 :
1276 : //! @endcond
1277 :
1278 : /************************************************************************/
1279 : /* OpenMDArrayFromFullname() */
1280 : /************************************************************************/
1281 :
1282 : /** Get an array from its fully qualified name */
1283 : std::shared_ptr<GDALMDArray>
1284 563 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1285 : CSLConstList papszOptions) const
1286 : {
1287 1126 : std::string osName;
1288 563 : std::shared_ptr<GDALGroup> curGroupHolder;
1289 563 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1290 563 : if (poGroup == nullptr)
1291 12 : return nullptr;
1292 551 : return poGroup->OpenMDArray(osName, papszOptions);
1293 : }
1294 :
1295 : /************************************************************************/
1296 : /* OpenAttributeFromFullname() */
1297 : /************************************************************************/
1298 :
1299 : /** Get an attribute from its fully qualified name */
1300 : std::shared_ptr<GDALAttribute>
1301 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1302 : CSLConstList papszOptions) const
1303 : {
1304 9 : const auto pos = osFullName.rfind('/');
1305 9 : if (pos == std::string::npos)
1306 0 : return nullptr;
1307 18 : const std::string attrName = osFullName.substr(pos + 1);
1308 9 : if (pos == 0)
1309 2 : return GetAttribute(attrName);
1310 14 : const std::string container = osFullName.substr(0, pos);
1311 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1312 7 : if (poArray)
1313 4 : return poArray->GetAttribute(attrName);
1314 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1315 3 : if (poGroup)
1316 1 : return poGroup->GetAttribute(attrName);
1317 2 : return nullptr;
1318 : }
1319 :
1320 : /************************************************************************/
1321 : /* ResolveMDArray() */
1322 : /************************************************************************/
1323 :
1324 : /** Locate an array in a group and its subgroups by name.
1325 : *
1326 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1327 : * used
1328 : * Otherwise the search will start from the group identified by osStartingPath,
1329 : * and an array whose name is osName will be looked for in this group (if
1330 : * osStartingPath is empty or "/", then the current group is used). If there
1331 : * is no match, then a recursive descendent search will be made in its
1332 : * subgroups. If there is no match in the subgroups, then the parent (if
1333 : * existing) of the group pointed by osStartingPath will be used as the new
1334 : * starting point for the search.
1335 : *
1336 : * @param osName name, qualified or not
1337 : * @param osStartingPath fully qualified name of the (sub-)group from which
1338 : * the search should be started. If this is a non-empty
1339 : * string, the group on which this method is called should
1340 : * nominally be the root group (otherwise the path will
1341 : * be interpreted as from the current group)
1342 : * @param papszOptions options to pass to OpenMDArray()
1343 : * @since GDAL 3.2
1344 : */
1345 : std::shared_ptr<GDALMDArray>
1346 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1347 : const std::string &osStartingPath,
1348 : CSLConstList papszOptions) const
1349 : {
1350 19 : if (!osName.empty() && osName[0] == '/')
1351 : {
1352 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1353 1 : if (poArray)
1354 1 : return poArray;
1355 : }
1356 36 : std::string osPath(osStartingPath);
1357 36 : std::set<std::string> oSetAlreadyVisited;
1358 :
1359 : while (true)
1360 : {
1361 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1362 0 : std::shared_ptr<GDALGroup> poGroup;
1363 :
1364 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1365 22 : bool goOn = false;
1366 22 : if (osPath.empty() || osPath == "/")
1367 : {
1368 11 : goOn = true;
1369 : }
1370 : else
1371 : {
1372 22 : std::string osLastPart;
1373 : const GDALGroup *poGroupPtr =
1374 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1375 11 : if (poGroupPtr)
1376 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1377 22 : if (poGroup &&
1378 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1379 : {
1380 11 : oQueue.push(poGroup);
1381 11 : goOn = true;
1382 : }
1383 : }
1384 :
1385 22 : if (goOn)
1386 : {
1387 17 : do
1388 : {
1389 : const GDALGroup *groupPtr;
1390 39 : if (!oQueue.empty())
1391 : {
1392 28 : poGroup = oQueue.front();
1393 28 : oQueue.pop();
1394 28 : groupPtr = poGroup.get();
1395 : }
1396 : else
1397 : {
1398 11 : groupPtr = this;
1399 : }
1400 :
1401 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1402 39 : if (poArray)
1403 16 : return poArray;
1404 :
1405 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1406 47 : for (const auto &osGroupName : aosGroupNames)
1407 : {
1408 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1409 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1410 48 : poSubGroup->GetFullName()))
1411 : {
1412 24 : oQueue.push(poSubGroup);
1413 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1414 : }
1415 : }
1416 23 : } while (!oQueue.empty());
1417 : }
1418 :
1419 6 : if (osPath.empty() || osPath == "/")
1420 2 : break;
1421 :
1422 4 : const auto nPos = osPath.rfind('/');
1423 4 : if (nPos == 0)
1424 1 : osPath = "/";
1425 : else
1426 : {
1427 3 : if (nPos == std::string::npos)
1428 0 : break;
1429 3 : osPath.resize(nPos);
1430 : }
1431 4 : }
1432 2 : return nullptr;
1433 : }
1434 :
1435 : /************************************************************************/
1436 : /* OpenGroupFromFullname() */
1437 : /************************************************************************/
1438 :
1439 : /** Get a group from its fully qualified name.
1440 : * @since GDAL 3.2
1441 : */
1442 : std::shared_ptr<GDALGroup>
1443 569 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1444 : CSLConstList papszOptions) const
1445 : {
1446 1138 : std::string osName;
1447 569 : std::shared_ptr<GDALGroup> curGroupHolder;
1448 569 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1449 569 : if (poGroup == nullptr)
1450 4 : return nullptr;
1451 565 : return poGroup->OpenGroup(osName, papszOptions);
1452 : }
1453 :
1454 : /************************************************************************/
1455 : /* OpenDimensionFromFullname() */
1456 : /************************************************************************/
1457 :
1458 : /** Get a dimension from its fully qualified name */
1459 : std::shared_ptr<GDALDimension>
1460 185 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1461 : {
1462 370 : std::string osName;
1463 185 : std::shared_ptr<GDALGroup> curGroupHolder;
1464 185 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1465 185 : if (poGroup == nullptr)
1466 2 : return nullptr;
1467 366 : auto dims(poGroup->GetDimensions());
1468 318 : for (auto &dim : dims)
1469 : {
1470 266 : if (dim->GetName() == osName)
1471 131 : return dim;
1472 : }
1473 52 : return nullptr;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* ClearStatistics() */
1478 : /************************************************************************/
1479 :
1480 : /**
1481 : * \brief Clear statistics.
1482 : *
1483 : * @since GDAL 3.4
1484 : */
1485 0 : void GDALGroup::ClearStatistics()
1486 : {
1487 0 : auto groupNames = GetGroupNames();
1488 0 : for (const auto &name : groupNames)
1489 : {
1490 0 : auto subGroup = OpenGroup(name);
1491 0 : if (subGroup)
1492 : {
1493 0 : subGroup->ClearStatistics();
1494 : }
1495 : }
1496 :
1497 0 : auto arrayNames = GetMDArrayNames();
1498 0 : for (const auto &name : arrayNames)
1499 : {
1500 0 : auto array = OpenMDArray(name);
1501 0 : if (array)
1502 : {
1503 0 : array->ClearStatistics();
1504 : }
1505 : }
1506 0 : }
1507 :
1508 : /************************************************************************/
1509 : /* Rename() */
1510 : /************************************************************************/
1511 :
1512 : /** Rename the group.
1513 : *
1514 : * This is not implemented by all drivers.
1515 : *
1516 : * Drivers known to implement it: MEM, netCDF, ZARR.
1517 : *
1518 : * This is the same as the C function GDALGroupRename().
1519 : *
1520 : * @param osNewName New name.
1521 : *
1522 : * @return true in case of success
1523 : * @since GDAL 3.8
1524 : */
1525 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1526 : {
1527 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1528 0 : return false;
1529 : }
1530 :
1531 : /************************************************************************/
1532 : /* BaseRename() */
1533 : /************************************************************************/
1534 :
1535 : //! @cond Doxygen_Suppress
1536 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1537 : {
1538 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1539 8 : m_osFullName += osNewName;
1540 8 : m_osName = osNewName;
1541 :
1542 8 : NotifyChildrenOfRenaming();
1543 8 : }
1544 :
1545 : //! @endcond
1546 :
1547 : /************************************************************************/
1548 : /* ParentRenamed() */
1549 : /************************************************************************/
1550 :
1551 : //! @cond Doxygen_Suppress
1552 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1553 : {
1554 7 : m_osFullName = osNewParentFullName;
1555 7 : m_osFullName += "/";
1556 7 : m_osFullName += m_osName;
1557 :
1558 7 : NotifyChildrenOfRenaming();
1559 7 : }
1560 :
1561 : //! @endcond
1562 :
1563 : /************************************************************************/
1564 : /* Deleted() */
1565 : /************************************************************************/
1566 :
1567 : //! @cond Doxygen_Suppress
1568 22 : void GDALGroup::Deleted()
1569 : {
1570 22 : m_bValid = false;
1571 :
1572 22 : NotifyChildrenOfDeletion();
1573 22 : }
1574 :
1575 : //! @endcond
1576 :
1577 : /************************************************************************/
1578 : /* ParentDeleted() */
1579 : /************************************************************************/
1580 :
1581 : //! @cond Doxygen_Suppress
1582 3 : void GDALGroup::ParentDeleted()
1583 : {
1584 3 : Deleted();
1585 3 : }
1586 :
1587 : //! @endcond
1588 :
1589 : /************************************************************************/
1590 : /* CheckValidAndErrorOutIfNot() */
1591 : /************************************************************************/
1592 :
1593 : //! @cond Doxygen_Suppress
1594 16863 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1595 : {
1596 16863 : if (!m_bValid)
1597 : {
1598 14 : CPLError(CE_Failure, CPLE_AppDefined,
1599 : "This object has been deleted. No action on it is possible");
1600 : }
1601 16863 : return m_bValid;
1602 : }
1603 :
1604 : //! @endcond
1605 :
1606 : /************************************************************************/
1607 : /* ~GDALAbstractMDArray() */
1608 : /************************************************************************/
1609 :
1610 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1611 :
1612 : /************************************************************************/
1613 : /* GDALAbstractMDArray() */
1614 : /************************************************************************/
1615 :
1616 : //! @cond Doxygen_Suppress
1617 21538 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1618 21538 : const std::string &osName)
1619 : : m_osName(osName),
1620 : m_osFullName(
1621 21538 : !osParentName.empty()
1622 41230 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1623 84306 : : osName)
1624 : {
1625 21538 : }
1626 :
1627 : //! @endcond
1628 :
1629 : /************************************************************************/
1630 : /* GetDimensions() */
1631 : /************************************************************************/
1632 :
1633 : /** \fn GDALAbstractMDArray::GetDimensions() const
1634 : * \brief Return the dimensions of an attribute/array.
1635 : *
1636 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1637 : * similar to GDALAttributeGetDimensionsSize().
1638 : */
1639 :
1640 : /************************************************************************/
1641 : /* GetDataType() */
1642 : /************************************************************************/
1643 :
1644 : /** \fn GDALAbstractMDArray::GetDataType() const
1645 : * \brief Return the data type of an attribute/array.
1646 : *
1647 : * This is the same as the C functions GDALMDArrayGetDataType() and
1648 : * GDALAttributeGetDataType()
1649 : */
1650 :
1651 : /************************************************************************/
1652 : /* GetDimensionCount() */
1653 : /************************************************************************/
1654 :
1655 : /** Return the number of dimensions.
1656 : *
1657 : * Default implementation is GetDimensions().size(), and may be overridden by
1658 : * drivers if they have a faster / less expensive implementations.
1659 : *
1660 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1661 : * GDALAttributeGetDimensionCount().
1662 : *
1663 : */
1664 24987 : size_t GDALAbstractMDArray::GetDimensionCount() const
1665 : {
1666 24987 : return GetDimensions().size();
1667 : }
1668 :
1669 : /************************************************************************/
1670 : /* Rename() */
1671 : /************************************************************************/
1672 :
1673 : /** Rename the attribute/array.
1674 : *
1675 : * This is not implemented by all drivers.
1676 : *
1677 : * Drivers known to implement it: MEM, netCDF, Zarr.
1678 : *
1679 : * This is the same as the C functions GDALMDArrayRename() or
1680 : * GDALAttributeRename().
1681 : *
1682 : * @param osNewName New name.
1683 : *
1684 : * @return true in case of success
1685 : * @since GDAL 3.8
1686 : */
1687 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1688 : {
1689 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1690 0 : return false;
1691 : }
1692 :
1693 : /************************************************************************/
1694 : /* CopyValue() */
1695 : /************************************************************************/
1696 :
1697 : /** Convert a value from a source type to a destination type.
1698 : *
1699 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1700 : * that must be freed with CPLFree().
1701 : */
1702 83819 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1703 : const GDALExtendedDataType &srcType,
1704 : void *pDst,
1705 : const GDALExtendedDataType &dstType)
1706 : {
1707 162655 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1708 78836 : dstType.GetClass() == GEDTC_NUMERIC)
1709 : {
1710 77969 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1711 : dstType.GetNumericDataType(), 0, 1);
1712 77969 : return true;
1713 : }
1714 10617 : if (srcType.GetClass() == GEDTC_STRING &&
1715 4767 : dstType.GetClass() == GEDTC_STRING)
1716 : {
1717 : const char *srcStrPtr;
1718 3533 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1719 3533 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1720 3533 : *reinterpret_cast<void **>(pDst) = pszDup;
1721 3533 : return true;
1722 : }
1723 3184 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1724 867 : dstType.GetClass() == GEDTC_STRING)
1725 : {
1726 867 : const char *str = nullptr;
1727 867 : switch (srcType.GetNumericDataType())
1728 : {
1729 0 : case GDT_Unknown:
1730 0 : break;
1731 0 : case GDT_Byte:
1732 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1733 0 : break;
1734 3 : case GDT_Int8:
1735 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1736 3 : break;
1737 48 : case GDT_UInt16:
1738 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1739 48 : break;
1740 0 : case GDT_Int16:
1741 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1742 0 : break;
1743 8 : case GDT_UInt32:
1744 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1745 8 : break;
1746 61 : case GDT_Int32:
1747 61 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1748 61 : break;
1749 0 : case GDT_UInt64:
1750 : str =
1751 0 : CPLSPrintf(CPL_FRMT_GUIB,
1752 : static_cast<GUIntBig>(
1753 : *static_cast<const std::uint64_t *>(pSrc)));
1754 0 : break;
1755 24 : case GDT_Int64:
1756 24 : str = CPLSPrintf(CPL_FRMT_GIB,
1757 : static_cast<GIntBig>(
1758 : *static_cast<const std::int64_t *>(pSrc)));
1759 24 : break;
1760 0 : case GDT_Float16:
1761 0 : str = CPLSPrintf("%.5g",
1762 : double(*static_cast<const GFloat16 *>(pSrc)));
1763 0 : break;
1764 17 : case GDT_Float32:
1765 34 : str = CPLSPrintf(
1766 : "%.9g",
1767 17 : static_cast<double>(*static_cast<const float *>(pSrc)));
1768 17 : break;
1769 704 : case GDT_Float64:
1770 704 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1771 704 : break;
1772 2 : case GDT_CInt16:
1773 : {
1774 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1775 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1776 2 : break;
1777 : }
1778 0 : case GDT_CInt32:
1779 : {
1780 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1781 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1782 0 : break;
1783 : }
1784 0 : case GDT_CFloat16:
1785 : {
1786 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1787 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1788 0 : break;
1789 : }
1790 0 : case GDT_CFloat32:
1791 : {
1792 0 : const float *src = static_cast<const float *>(pSrc);
1793 0 : str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1794 0 : break;
1795 : }
1796 0 : case GDT_CFloat64:
1797 : {
1798 0 : const double *src = static_cast<const double *>(pSrc);
1799 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1800 0 : break;
1801 : }
1802 0 : case GDT_TypeCount:
1803 0 : CPLAssert(false);
1804 : break;
1805 : }
1806 867 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1807 867 : *reinterpret_cast<void **>(pDst) = pszDup;
1808 867 : return true;
1809 : }
1810 2684 : if (srcType.GetClass() == GEDTC_STRING &&
1811 1234 : dstType.GetClass() == GEDTC_NUMERIC)
1812 : {
1813 : const char *srcStrPtr;
1814 1234 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1815 1234 : if (dstType.GetNumericDataType() == GDT_Int64)
1816 : {
1817 2 : *(static_cast<int64_t *>(pDst)) =
1818 2 : srcStrPtr == nullptr ? 0
1819 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1820 : }
1821 1232 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1822 : {
1823 2 : *(static_cast<uint64_t *>(pDst)) =
1824 2 : srcStrPtr == nullptr
1825 2 : ? 0
1826 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1827 : }
1828 : else
1829 : {
1830 1230 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1831 1230 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1832 : dstType.GetNumericDataType(), 0, 1);
1833 : }
1834 1234 : 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 8803 : 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 8803 : const auto &dims = GetDimensions();
1936 8803 : if (dims.empty())
1937 : {
1938 3449 : if (buffer_alloc_start)
1939 : {
1940 3064 : const size_t elementSize = bufferDataType.GetSize();
1941 3064 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1942 3064 : const GByte *paby_buffer_alloc_start =
1943 : static_cast<const GByte *>(buffer_alloc_start);
1944 3064 : const GByte *paby_buffer_alloc_end =
1945 : paby_buffer_alloc_start + buffer_alloc_size;
1946 :
1947 3064 : if (paby_buffer < paby_buffer_alloc_start ||
1948 3064 : paby_buffer + elementSize > paby_buffer_alloc_end)
1949 : {
1950 0 : lamda_error();
1951 0 : return false;
1952 : }
1953 : }
1954 3449 : return true;
1955 : }
1956 :
1957 5354 : if (arrayStep == nullptr)
1958 : {
1959 1544 : tmp_arrayStep.resize(dims.size(), 1);
1960 1544 : arrayStep = tmp_arrayStep.data();
1961 : }
1962 14821 : for (size_t i = 0; i < dims.size(); i++)
1963 : {
1964 9467 : assert(count);
1965 9467 : 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 5355 : bool bufferStride_all_positive = true;
1973 5355 : if (bufferStride == nullptr)
1974 : {
1975 1246 : GPtrDiff_t stride = 1;
1976 1246 : 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 2792 : for (size_t i = dims.size(); i != 0;)
1980 : {
1981 1546 : --i;
1982 1546 : tmp_bufferStride.push_back(stride);
1983 1546 : GUInt64 newStride = 0;
1984 : bool bOK;
1985 : try
1986 : {
1987 1546 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1988 3092 : CPLSM(static_cast<uint64_t>(count[i])))
1989 1546 : .v();
1990 1546 : bOK = static_cast<size_t>(newStride) == newStride &&
1991 1546 : newStride < std::numeric_limits<size_t>::max() / 2;
1992 : }
1993 0 : catch (...)
1994 : {
1995 0 : bOK = false;
1996 : }
1997 1546 : if (!bOK)
1998 : {
1999 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2000 0 : return false;
2001 : }
2002 1546 : stride = static_cast<GPtrDiff_t>(newStride);
2003 : }
2004 1246 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2005 1246 : bufferStride = tmp_bufferStride.data();
2006 : }
2007 : else
2008 : {
2009 12029 : for (size_t i = 0; i < dims.size(); i++)
2010 : {
2011 7921 : if (bufferStride[i] < 0)
2012 : {
2013 1 : bufferStride_all_positive = false;
2014 1 : break;
2015 : }
2016 : }
2017 : }
2018 14791 : for (size_t i = 0; i < dims.size(); i++)
2019 : {
2020 9448 : assert(arrayStartIdx);
2021 9448 : assert(count);
2022 9448 : 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 9447 : if (arrayStep[i] >= 0)
2033 : {
2034 : try
2035 : {
2036 8815 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2037 8817 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2038 35265 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2039 8815 : .v() >= dims[i]->GetSize();
2040 : }
2041 1 : catch (...)
2042 : {
2043 1 : bOverflow = true;
2044 : }
2045 8816 : 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 630 : bOverflow =
2061 630 : arrayStartIdx[i] <
2062 630 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2063 1260 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2064 : ? (static_cast<uint64_t>(1) << 63)
2065 1260 : : static_cast<uint64_t>(-arrayStep[i])))
2066 630 : .v();
2067 : }
2068 0 : catch (...)
2069 : {
2070 0 : bOverflow = true;
2071 : }
2072 630 : 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 5344 : if (buffer_alloc_start)
2085 : {
2086 2652 : const size_t elementSize = bufferDataType.GetSize();
2087 2652 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2088 2652 : const GByte *paby_buffer_alloc_start =
2089 : static_cast<const GByte *>(buffer_alloc_start);
2090 2652 : const GByte *paby_buffer_alloc_end =
2091 : paby_buffer_alloc_start + buffer_alloc_size;
2092 2652 : if (bufferStride_all_positive)
2093 : {
2094 2652 : if (paby_buffer < paby_buffer_alloc_start)
2095 : {
2096 0 : lamda_error();
2097 0 : return false;
2098 : }
2099 2652 : GUInt64 nOffset = elementSize;
2100 7572 : for (size_t i = 0; i < dims.size(); i++)
2101 : {
2102 : try
2103 : {
2104 4920 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2105 4920 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2106 9839 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2107 19679 : CPLSM(static_cast<uint64_t>(elementSize)))
2108 4920 : .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 2653 : 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 1 : 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 5344 : 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 2772 : 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 2772 : 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 5544 : std::vector<GInt64> tmp_arrayStep;
2236 5544 : std::vector<GPtrDiff_t> tmp_bufferStride;
2237 2772 : 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 2772 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2246 2772 : 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 1885 : 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 1885 : 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 3770 : std::vector<GInt64> tmp_arrayStep;
2341 3770 : std::vector<GPtrDiff_t> tmp_bufferStride;
2342 1885 : 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 1885 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2351 1885 : 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 1237 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2365 : {
2366 1237 : const auto &dims = GetDimensions();
2367 1237 : if (dims.empty())
2368 616 : return 1;
2369 621 : GUInt64 nElts = 1;
2370 1370 : for (const auto &dim : dims)
2371 : {
2372 : try
2373 : {
2374 749 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2375 2247 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2376 749 : .v();
2377 : }
2378 0 : catch (...)
2379 : {
2380 0 : return 0;
2381 : }
2382 : }
2383 621 : 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 371 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2413 : {
2414 371 : 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 78 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2438 : {
2439 78 : const auto &dims = GetDimensions();
2440 78 : const auto &nDTSize = GetDataType().GetSize();
2441 78 : std::vector<size_t> anChunkSize;
2442 156 : auto blockSize = GetBlockSize();
2443 78 : CPLAssert(blockSize.size() == dims.size());
2444 78 : size_t nChunkSize = nDTSize;
2445 78 : bool bOverflow = false;
2446 78 : 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 218 : for (size_t i = 0; i < dims.size(); i++)
2451 : {
2452 : const auto sizeDimI =
2453 280 : std::max(static_cast<size_t>(1),
2454 280 : static_cast<size_t>(
2455 280 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2456 140 : std::min(blockSize[i], dims[i]->GetSize()))));
2457 140 : anChunkSize.push_back(sizeDimI);
2458 140 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2459 : {
2460 4 : bOverflow = true;
2461 : }
2462 : else
2463 : {
2464 136 : nChunkSize *= sizeDimI;
2465 : }
2466 : }
2467 78 : 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 78 : 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 78 : nChunkSize = nDTSize;
2492 156 : std::vector<size_t> anAccBlockSizeFromStart;
2493 218 : for (size_t i = 0; i < dims.size(); i++)
2494 : {
2495 140 : nChunkSize *= anChunkSize[i];
2496 140 : anAccBlockSizeFromStart.push_back(nChunkSize);
2497 : }
2498 78 : if (nChunkSize <= nMaxChunkMemory / 2)
2499 : {
2500 74 : size_t nVoxelsFromEnd = 1;
2501 206 : for (size_t i = dims.size(); i > 0;)
2502 : {
2503 132 : --i;
2504 : const auto nCurBlockSize =
2505 132 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2506 132 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2507 132 : if (nMul >= 2)
2508 : {
2509 124 : const auto nSizeThisDim(dims[i]->GetSize());
2510 : const auto nBlocksThisDim =
2511 124 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2512 124 : anChunkSize[i] = static_cast<size_t>(std::min(
2513 124 : anChunkSize[i] *
2514 248 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2515 124 : nSizeThisDim));
2516 : }
2517 132 : nVoxelsFromEnd *= anChunkSize[i];
2518 : }
2519 : }
2520 78 : 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 6231 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2587 : {
2588 6231 : 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 6231 : 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 22490 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2716 : {
2717 22490 : const void *pNoData = GetRawNoDataValue();
2718 22490 : double dfNoData = 0.0;
2719 22490 : const auto &eDT = GetDataType();
2720 22490 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2721 22490 : if (ok)
2722 : {
2723 22193 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2724 : GDT_Float64, 0, 1);
2725 : }
2726 22490 : if (pbHasNoData)
2727 451 : *pbHasNoData = ok;
2728 22490 : 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 76 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3089 : const GUInt64 *count,
3090 : const size_t *chunkSize,
3091 : FuncProcessPerChunkType pfnFunc,
3092 : void *pUserData)
3093 : {
3094 76 : const auto &dims = GetDimensions();
3095 76 : if (dims.empty())
3096 : {
3097 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3098 : }
3099 :
3100 : // Sanity check
3101 74 : size_t nTotalChunkSize = 1;
3102 191 : for (size_t i = 0; i < dims.size(); i++)
3103 : {
3104 124 : const auto nSizeThisDim(dims[i]->GetSize());
3105 124 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3106 122 : 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 238 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3114 118 : 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 117 : nTotalChunkSize *= chunkSize[i];
3121 : }
3122 :
3123 67 : size_t dimIdx = 0;
3124 134 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3125 134 : 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 134 : std::vector<Stack> stack(dims.size());
3136 67 : GUInt64 iCurChunk = 0;
3137 67 : GUInt64 nChunkCount = 1;
3138 183 : for (size_t i = 0; i < dims.size(); i++)
3139 : {
3140 116 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3141 116 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3142 116 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3143 116 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3144 116 : if (stack[i].nBlocksMinusOne == 0)
3145 : {
3146 111 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3147 111 : 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 67 : lbl_next_depth:
3157 293 : if (dimIdx == dims.size())
3158 : {
3159 100 : ++iCurChunk;
3160 100 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3161 : iCurChunk, nChunkCount, pUserData))
3162 : {
3163 1 : return false;
3164 : }
3165 : }
3166 : else
3167 : {
3168 193 : 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 193 : dimIdx++;
3193 193 : goto lbl_next_depth;
3194 191 : lbl_return_to_caller_end_of_loop:
3195 191 : if (dimIdx == 0)
3196 66 : goto end;
3197 : }
3198 :
3199 224 : assert(dimIdx > 0);
3200 224 : dimIdx--;
3201 : // cppcheck-suppress negativeContainerIndex
3202 224 : switch (stack[dimIdx].return_point)
3203 : {
3204 191 : case Caller::CALLER_END_OF_LOOP:
3205 191 : 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 66 : end:
3210 66 : return true;
3211 : }
3212 :
3213 : /************************************************************************/
3214 : /* GDALAttribute() */
3215 : /************************************************************************/
3216 :
3217 : //! @cond Doxygen_Suppress
3218 14691 : 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 14691 : : GDALAbstractMDArray(osParentName, osName)
3222 : #endif
3223 : {
3224 14691 : }
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 618 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3241 : {
3242 618 : const auto &dims = GetDimensions();
3243 618 : std::vector<GUInt64> ret;
3244 618 : ret.reserve(dims.size());
3245 768 : for (const auto &dim : dims)
3246 150 : ret.push_back(dim->GetSize());
3247 618 : return ret;
3248 : }
3249 :
3250 : /************************************************************************/
3251 : /* GDALRawResult() */
3252 : /************************************************************************/
3253 :
3254 : //! @cond Doxygen_Suppress
3255 173 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3256 173 : size_t nEltCount)
3257 346 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3258 173 : m_raw(raw)
3259 : {
3260 173 : }
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 173 : void GDALRawResult::FreeMe()
3283 : {
3284 173 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3285 : {
3286 66 : GByte *pabyPtr = m_raw;
3287 66 : const auto nDTSize(m_dt.GetSize());
3288 132 : for (size_t i = 0; i < m_nEltCount; ++i)
3289 : {
3290 66 : m_dt.FreeDynamicMemory(pabyPtr);
3291 66 : pabyPtr += nDTSize;
3292 : }
3293 : }
3294 173 : VSIFree(m_raw);
3295 173 : }
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 173 : GDALRawResult::~GDALRawResult()
3321 : {
3322 173 : FreeMe();
3323 173 : }
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 173 : GDALRawResult GDALAttribute::ReadAsRaw() const
3354 : {
3355 173 : const auto nEltCount(GetTotalElementsCount());
3356 173 : const auto &dt(GetDataType());
3357 173 : const auto nDTSize(dt.GetSize());
3358 : GByte *res = static_cast<GByte *>(
3359 173 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3360 173 : if (!res)
3361 0 : return GDALRawResult(nullptr, dt, 0);
3362 173 : const auto &dims = GetDimensions();
3363 173 : const auto nDims = GetDimensionCount();
3364 346 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3365 346 : std::vector<size_t> count(1 + nDims);
3366 196 : for (size_t i = 0; i < nDims; i++)
3367 : {
3368 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3369 : }
3370 173 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3371 173 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3372 : {
3373 0 : VSIFree(res);
3374 0 : return GDALRawResult(nullptr, dt, 0);
3375 : }
3376 173 : 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 1556 : const char *GDALAttribute::ReadAsString() const
3396 : {
3397 1556 : const auto nDims = GetDimensionCount();
3398 3112 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3399 3112 : std::vector<size_t> count(1 + nDims, 1);
3400 1556 : char *szRet = nullptr;
3401 1556 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3402 1556 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3403 4667 : sizeof(szRet)) ||
3404 1555 : szRet == nullptr)
3405 : {
3406 5 : return nullptr;
3407 : }
3408 1551 : m_osCachedVal = szRet;
3409 1551 : CPLFree(szRet);
3410 1551 : 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 147 : CPLStringList GDALAttribute::ReadAsStringArray() const
3498 : {
3499 147 : const auto nElts = GetTotalElementsCount();
3500 147 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3501 0 : return CPLStringList();
3502 : char **papszList = static_cast<char **>(
3503 147 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3504 147 : const auto &dims = GetDimensions();
3505 147 : const auto nDims = GetDimensionCount();
3506 294 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3507 294 : std::vector<size_t> count(1 + nDims);
3508 231 : for (size_t i = 0; i < nDims; i++)
3509 : {
3510 84 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3511 : }
3512 147 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3513 147 : GDALExtendedDataType::CreateString(), papszList, papszList,
3514 147 : sizeof(char *) * static_cast<int>(nElts));
3515 589 : for (int i = 0; i < static_cast<int>(nElts); i++)
3516 : {
3517 442 : if (papszList[i] == nullptr)
3518 13 : papszList[i] = CPLStrdup("");
3519 : }
3520 147 : 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 114 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3631 : {
3632 114 : 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 114 : const auto &dims = GetDimensions();
3639 114 : const auto nDims = GetDimensionCount();
3640 228 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3641 228 : std::vector<size_t> count(1 + nDims);
3642 137 : for (size_t i = 0; i < nDims; i++)
3643 : {
3644 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3645 : }
3646 114 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3647 114 : 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 39 : bool GDALAttribute::Write(double dfVal)
3737 : {
3738 39 : const auto nDims = GetDimensionCount();
3739 78 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3740 39 : std::vector<size_t> count(1 + nDims, 1);
3741 39 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3742 78 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3743 78 : 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 6847 : 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 6847 : m_osContext(osContext)
3895 : {
3896 6847 : }
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 58 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3909 : {
3910 116 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3911 116 : GetTotalElementsCount() * GetDataType().GetSize();
3912 : }
3913 :
3914 : /************************************************************************/
3915 : /* CopyFromAllExceptValues() */
3916 : /************************************************************************/
3917 :
3918 : //! @cond Doxygen_Suppress
3919 :
3920 187 : 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 187 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3928 187 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3929 : {
3930 13 : SetRawNoDataValue(pNoData);
3931 : }
3932 :
3933 187 : const bool bThisIsUnscaledArray =
3934 187 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3935 374 : auto attrs = poSrcArray->GetAttributes();
3936 245 : for (const auto &attr : attrs)
3937 : {
3938 58 : const auto &osAttrName = attr->GetName();
3939 58 : 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 57 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3950 114 : attr->GetDataType());
3951 57 : if (!dstAttr)
3952 : {
3953 0 : if (bStrict)
3954 0 : return false;
3955 0 : continue;
3956 : }
3957 57 : auto raw = attr->ReadAsRaw();
3958 57 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3959 0 : return false;
3960 : }
3961 187 : if (!attrs.empty())
3962 : {
3963 33 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3964 56 : if (pfnProgress &&
3965 23 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3966 0 : return false;
3967 : }
3968 :
3969 187 : auto srcSRS = poSrcArray->GetSpatialRef();
3970 187 : if (srcSRS)
3971 : {
3972 16 : SetSpatialRef(srcSRS.get());
3973 : }
3974 :
3975 187 : const std::string &osUnit(poSrcArray->GetUnit());
3976 187 : if (!osUnit.empty())
3977 : {
3978 20 : SetUnit(osUnit);
3979 : }
3980 :
3981 187 : bool bGotValue = false;
3982 187 : GDALDataType eOffsetStorageType = GDT_Unknown;
3983 : const double dfOffset =
3984 187 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3985 187 : if (bGotValue)
3986 : {
3987 3 : SetOffset(dfOffset, eOffsetStorageType);
3988 : }
3989 :
3990 187 : bGotValue = false;
3991 187 : GDALDataType eScaleStorageType = GDT_Unknown;
3992 187 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3993 187 : if (bGotValue)
3994 : {
3995 3 : SetScale(dfScale, eScaleStorageType);
3996 : }
3997 :
3998 187 : 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 56 : 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 56 : if (pfnProgress == nullptr)
4028 4 : pfnProgress = GDALDummyProgress;
4029 :
4030 56 : nCurCost += GDALMDArray::COPY_COST;
4031 :
4032 56 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4033 : pfnProgress, pProgressData))
4034 : {
4035 0 : return false;
4036 : }
4037 :
4038 56 : const auto &dims = poSrcArray->GetDimensions();
4039 56 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4040 56 : 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 54 : std::vector<GUInt64> arrayStartIdx(dims.size());
4058 54 : std::vector<GUInt64> count(dims.size());
4059 144 : for (size_t i = 0; i < dims.size(); i++)
4060 : {
4061 90 : 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 72 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4076 : const GUInt64 *chunkArrayStartIdx,
4077 : const size_t *chunkCount, GUInt64 iCurChunk,
4078 : GUInt64 nChunkCount, void *pUserData)
4079 : {
4080 72 : const auto &dt(l_poSrcArray->GetDataType());
4081 72 : auto data = static_cast<CopyFunc *>(pUserData);
4082 72 : auto poDstArray = data->poDstArray;
4083 72 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4084 72 : nullptr, dt, &data->abyTmp[0]))
4085 : {
4086 1 : return false;
4087 : }
4088 : bool bRet =
4089 71 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4090 71 : nullptr, dt, &data->abyTmp[0]);
4091 71 : if (dt.NeedsFreeDynamicMemory())
4092 : {
4093 5 : const auto l_nDTSize = dt.GetSize();
4094 5 : GByte *ptr = &data->abyTmp[0];
4095 5 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4096 5 : size_t nEltCount = 1;
4097 10 : for (size_t i = 0; i < l_nDims; ++i)
4098 : {
4099 5 : nEltCount *= chunkCount[i];
4100 : }
4101 22 : for (size_t i = 0; i < nEltCount; i++)
4102 : {
4103 17 : dt.FreeDynamicMemory(ptr);
4104 17 : ptr += l_nDTSize;
4105 : }
4106 : }
4107 71 : if (!bRet)
4108 : {
4109 0 : return false;
4110 : }
4111 :
4112 71 : double dfCurCost =
4113 71 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4114 71 : data->nTotalBytesThisArray;
4115 71 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4116 : data->pProgressData))
4117 : {
4118 0 : data->bStop = true;
4119 0 : return false;
4120 : }
4121 :
4122 71 : return true;
4123 : }
4124 : };
4125 :
4126 54 : CopyFunc copyFunc;
4127 54 : copyFunc.poDstArray = this;
4128 54 : copyFunc.nCurCost = nCurCost;
4129 54 : copyFunc.nTotalCost = nTotalCost;
4130 54 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4131 54 : copyFunc.pfnProgress = pfnProgress;
4132 54 : copyFunc.pProgressData = pProgressData;
4133 : const char *pszSwathSize =
4134 54 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4135 : const size_t nMaxChunkSize =
4136 : pszSwathSize
4137 54 : ? 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 53 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4142 53 : GDALGetCacheMax64() / 4));
4143 54 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4144 54 : size_t nRealChunkSize = nDTSize;
4145 144 : for (const auto &nChunkSize : anChunkSizes)
4146 : {
4147 90 : nRealChunkSize *= nChunkSize;
4148 : }
4149 : try
4150 : {
4151 54 : 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 161 : if (copyFunc.nTotalBytesThisArray != 0 &&
4161 53 : !const_cast<GDALMDArray *>(poSrcArray)
4162 53 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4163 : anChunkSizes.data(), CopyFunc::f,
4164 108 : ©Func) &&
4165 1 : (bStrict || copyFunc.bStop))
4166 : {
4167 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4168 0 : return false;
4169 : }
4170 54 : nCurCost += copyFunc.nTotalBytesThisArray;
4171 : }
4172 :
4173 56 : 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 1469 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4320 : std::string &osCacheFilenameOut) const
4321 : {
4322 1469 : const auto &osFilename = GetFilename();
4323 1469 : 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 1468 : osCacheFilenameOut = osFilename + ".gmac";
4331 1468 : 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 1468 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4343 1468 : if (pszProxy != nullptr)
4344 7 : osCacheFilenameOut = pszProxy;
4345 :
4346 1468 : std::unique_ptr<GDALDataset> poDS;
4347 : VSIStatBufL sStat;
4348 1468 : 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 1468 : if (poDS)
4355 : {
4356 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4357 28 : return poDS->GetRootGroup();
4358 : }
4359 :
4360 1440 : 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 1436 : 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 4124 : 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 4124 : if (!m_bHasTriedCachedArray)
4518 : {
4519 1873 : m_bHasTriedCachedArray = true;
4520 1873 : if (IsCacheable())
4521 : {
4522 1873 : const auto &osFilename = GetFilename();
4523 3137 : if (!osFilename.empty() &&
4524 3137 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4525 : {
4526 2508 : std::string osCacheFilename;
4527 2508 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4528 1254 : 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 4124 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4568 4124 : 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 8248 : std::vector<GInt64> tmp_arrayStep;
4576 8246 : std::vector<GPtrDiff_t> tmp_bufferStride;
4577 4123 : 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 4114 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4586 4114 : 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 815 : 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 815 : const size_t nDims(GetDimensionCount());
4628 815 : size_t nCurStrideForRowMajorStrides = 1;
4629 815 : bool bRowMajorStrides = true;
4630 815 : size_t nElts = 1;
4631 815 : size_t nLastIdx = 0;
4632 2227 : for (size_t i = nDims; i > 0;)
4633 : {
4634 1412 : --i;
4635 1412 : if (bufferStride[i] < 0)
4636 0 : return false;
4637 1412 : 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 1412 : nCurStrideForRowMajorStrides *= count[i];
4644 1412 : nElts *= count[i];
4645 1412 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4646 : }
4647 815 : if (bRowMajorStrides)
4648 624 : 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<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5032 : std::vector<Range>
5033 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5034 :
5035 : mutable std::vector<GUInt64> m_parentStart;
5036 : mutable std::vector<size_t> m_parentCount;
5037 : mutable std::vector<GInt64> m_parentStep;
5038 : mutable std::vector<GPtrDiff_t> m_parentStride;
5039 :
5040 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5041 : const GInt64 *arrayStep,
5042 : const GPtrDiff_t *bufferStride) const;
5043 :
5044 : protected:
5045 668 : explicit GDALSlicedMDArray(
5046 : const std::shared_ptr<GDALMDArray> &poParent,
5047 : const std::string &viewExpr,
5048 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5049 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5050 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5051 : std::vector<Range> &&parentRanges)
5052 2004 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5053 2004 : poParent->GetFullName() +
5054 1336 : " (" + viewExpr + ")"),
5055 1336 : GDALPamMDArray(std::string(),
5056 1336 : "Sliced view of " + poParent->GetFullName() + " (" +
5057 1336 : viewExpr + ")",
5058 1336 : GDALPamMultiDim::GetPAM(poParent),
5059 : poParent->GetContext()),
5060 1336 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5061 668 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5062 668 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5063 668 : m_parentRanges(std::move(parentRanges)),
5064 668 : m_parentStart(m_poParent->GetDimensionCount()),
5065 668 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5066 668 : m_parentStep(m_poParent->GetDimensionCount()),
5067 5344 : m_parentStride(m_poParent->GetDimensionCount())
5068 : {
5069 668 : }
5070 :
5071 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5072 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5073 : const GDALExtendedDataType &bufferDataType,
5074 : void *pDstBuffer) const override;
5075 :
5076 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5077 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5078 : const GDALExtendedDataType &bufferDataType,
5079 : const void *pSrcBuffer) override;
5080 :
5081 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5082 : CSLConstList papszOptions) const override;
5083 :
5084 : public:
5085 : static std::shared_ptr<GDALSlicedMDArray>
5086 668 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5087 : const std::string &viewExpr,
5088 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5089 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5090 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5091 : std::vector<Range> &&parentRanges)
5092 : {
5093 668 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5094 668 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5095 :
5096 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5097 668 : poParent, viewExpr, std::move(dims),
5098 668 : std::move(mapDimIdxToParentDimIdx),
5099 668 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5100 668 : newAr->SetSelf(newAr);
5101 668 : return newAr;
5102 : }
5103 :
5104 58 : bool IsWritable() const override
5105 : {
5106 58 : return m_poParent->IsWritable();
5107 : }
5108 :
5109 1025 : const std::string &GetFilename() const override
5110 : {
5111 1025 : return m_poParent->GetFilename();
5112 : }
5113 :
5114 : const std::vector<std::shared_ptr<GDALDimension>> &
5115 3943 : GetDimensions() const override
5116 : {
5117 3943 : return m_dims;
5118 : }
5119 :
5120 1494 : const GDALExtendedDataType &GetDataType() const override
5121 : {
5122 1494 : return m_poParent->GetDataType();
5123 : }
5124 :
5125 2 : const std::string &GetUnit() const override
5126 : {
5127 2 : return m_poParent->GetUnit();
5128 : }
5129 :
5130 : // bool SetUnit(const std::string& osUnit) override { return
5131 : // m_poParent->SetUnit(osUnit); }
5132 :
5133 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5134 : {
5135 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5136 2 : if (!poSrcSRS)
5137 1 : return nullptr;
5138 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5139 2 : std::vector<int> dstMapping;
5140 3 : for (int srcAxis : srcMapping)
5141 : {
5142 2 : bool bFound = false;
5143 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5144 : {
5145 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5146 3 : srcAxis - 1)
5147 : {
5148 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5149 2 : bFound = true;
5150 2 : break;
5151 : }
5152 : }
5153 2 : if (!bFound)
5154 : {
5155 0 : dstMapping.push_back(0);
5156 : }
5157 : }
5158 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5159 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5160 1 : return poClone;
5161 : }
5162 :
5163 59 : const void *GetRawNoDataValue() const override
5164 : {
5165 59 : return m_poParent->GetRawNoDataValue();
5166 : }
5167 :
5168 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5169 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5170 :
5171 2 : double GetOffset(bool *pbHasOffset,
5172 : GDALDataType *peStorageType) const override
5173 : {
5174 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5175 : }
5176 :
5177 2 : double GetScale(bool *pbHasScale,
5178 : GDALDataType *peStorageType) const override
5179 : {
5180 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5181 : }
5182 :
5183 : // bool SetOffset(double dfOffset) override { return
5184 : // m_poParent->SetOffset(dfOffset); }
5185 :
5186 : // bool SetScale(double dfScale) override { return
5187 : // m_poParent->SetScale(dfScale); }
5188 :
5189 239 : std::vector<GUInt64> GetBlockSize() const override
5190 : {
5191 239 : std::vector<GUInt64> ret(GetDimensionCount());
5192 478 : const auto parentBlockSize(m_poParent->GetBlockSize());
5193 682 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5194 : {
5195 443 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5196 443 : if (iOldAxis != static_cast<size_t>(-1))
5197 : {
5198 443 : ret[i] = parentBlockSize[iOldAxis];
5199 : }
5200 : }
5201 478 : return ret;
5202 : }
5203 :
5204 : std::shared_ptr<GDALAttribute>
5205 6 : GetAttribute(const std::string &osName) const override
5206 : {
5207 6 : return m_poParent->GetAttribute(osName);
5208 : }
5209 :
5210 : std::vector<std::shared_ptr<GDALAttribute>>
5211 26 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5212 : {
5213 26 : return m_poParent->GetAttributes(papszOptions);
5214 : }
5215 : };
5216 :
5217 : /************************************************************************/
5218 : /* PrepareParentArrays() */
5219 : /************************************************************************/
5220 :
5221 525 : void GDALSlicedMDArray::PrepareParentArrays(
5222 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5223 : const GPtrDiff_t *bufferStride) const
5224 : {
5225 525 : const size_t nParentDimCount = m_parentRanges.size();
5226 1588 : for (size_t i = 0; i < nParentDimCount; i++)
5227 : {
5228 : // For dimensions in parent that have no existence in sliced array
5229 1063 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5230 : }
5231 :
5232 1357 : for (size_t i = 0; i < m_dims.size(); i++)
5233 : {
5234 832 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5235 832 : if (iParent != static_cast<size_t>(-1))
5236 : {
5237 830 : m_parentStart[iParent] =
5238 830 : m_parentRanges[iParent].m_nIncr >= 0
5239 830 : ? m_parentRanges[iParent].m_nStartIdx +
5240 762 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5241 68 : : m_parentRanges[iParent].m_nStartIdx -
5242 136 : arrayStartIdx[i] *
5243 68 : static_cast<GUInt64>(
5244 68 : -m_parentRanges[iParent].m_nIncr);
5245 830 : m_parentCount[iParent] = count[i];
5246 830 : if (arrayStep)
5247 : {
5248 829 : m_parentStep[iParent] =
5249 829 : count[i] == 1 ? 1 :
5250 : // other checks should have ensured this does
5251 : // not overflow
5252 638 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5253 : }
5254 830 : if (bufferStride)
5255 : {
5256 829 : m_parentStride[iParent] = bufferStride[i];
5257 : }
5258 : }
5259 : }
5260 525 : }
5261 :
5262 : /************************************************************************/
5263 : /* IRead() */
5264 : /************************************************************************/
5265 :
5266 492 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5267 : const GInt64 *arrayStep,
5268 : const GPtrDiff_t *bufferStride,
5269 : const GDALExtendedDataType &bufferDataType,
5270 : void *pDstBuffer) const
5271 : {
5272 492 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5273 984 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5274 492 : m_parentStep.data(), m_parentStride.data(),
5275 492 : bufferDataType, pDstBuffer);
5276 : }
5277 :
5278 : /************************************************************************/
5279 : /* IWrite() */
5280 : /************************************************************************/
5281 :
5282 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5283 : const size_t *count, const GInt64 *arrayStep,
5284 : const GPtrDiff_t *bufferStride,
5285 : const GDALExtendedDataType &bufferDataType,
5286 : const void *pSrcBuffer)
5287 : {
5288 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5289 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5290 32 : m_parentStep.data(), m_parentStride.data(),
5291 32 : bufferDataType, pSrcBuffer);
5292 : }
5293 :
5294 : /************************************************************************/
5295 : /* IAdviseRead() */
5296 : /************************************************************************/
5297 :
5298 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5299 : const size_t *count,
5300 : CSLConstList papszOptions) const
5301 : {
5302 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5303 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5304 1 : papszOptions);
5305 : }
5306 :
5307 : /************************************************************************/
5308 : /* CreateSlicedArray() */
5309 : /************************************************************************/
5310 :
5311 : static std::shared_ptr<GDALMDArray>
5312 606 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5313 : const std::string &viewExpr, const std::string &activeSlice,
5314 : bool bRenameDimensions,
5315 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5316 : {
5317 606 : const auto &srcDims(self->GetDimensions());
5318 606 : if (srcDims.empty())
5319 : {
5320 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5321 2 : return nullptr;
5322 : }
5323 :
5324 1208 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5325 604 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5326 :
5327 1208 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5328 1208 : std::vector<size_t> mapDimIdxToParentDimIdx;
5329 1208 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5330 604 : newDims.reserve(nTokens);
5331 604 : mapDimIdxToParentDimIdx.reserve(nTokens);
5332 604 : parentRanges.reserve(nTokens);
5333 :
5334 604 : bool bGotEllipsis = false;
5335 604 : size_t nCurSrcDim = 0;
5336 1208 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5337 1788 : for (size_t i = 0; i < nTokens; i++)
5338 : {
5339 1200 : const char *pszIdxSpec = aosTokens[i];
5340 1200 : if (EQUAL(pszIdxSpec, "..."))
5341 : {
5342 39 : if (bGotEllipsis)
5343 : {
5344 2 : CPLError(CE_Failure, CPLE_AppDefined,
5345 : "Only one single ellipsis is supported");
5346 2 : return nullptr;
5347 : }
5348 37 : bGotEllipsis = true;
5349 37 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5350 83 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5351 : {
5352 46 : parentRanges.emplace_back(0, 1);
5353 46 : newDims.push_back(srcDims[nCurSrcDim]);
5354 46 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5355 : }
5356 37 : continue;
5357 : }
5358 1161 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5359 1158 : EQUAL(pszIdxSpec, "np.newaxis"))
5360 : {
5361 3 : newDims.push_back(std::make_shared<GDALDimension>(
5362 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5363 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5364 3 : continue;
5365 : }
5366 1158 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5367 : {
5368 326 : if (nCurSrcDim >= srcDims.size())
5369 : {
5370 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5371 : activeSlice.c_str());
5372 7 : return nullptr;
5373 : }
5374 :
5375 324 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5376 324 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5377 324 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5378 320 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5379 : {
5380 5 : CPLError(CE_Failure, CPLE_AppDefined,
5381 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5382 5 : return nullptr;
5383 : }
5384 319 : if (nVal < 0)
5385 0 : nVal += nDimSize;
5386 319 : parentRanges.emplace_back(nVal, 0);
5387 : }
5388 : else
5389 : {
5390 832 : if (nCurSrcDim >= srcDims.size())
5391 : {
5392 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5393 : activeSlice.c_str());
5394 7 : return nullptr;
5395 : }
5396 :
5397 : CPLStringList aosRangeTokens(
5398 831 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5399 831 : int nRangeTokens = aosRangeTokens.size();
5400 831 : if (nRangeTokens > 3)
5401 : {
5402 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5403 : pszIdxSpec);
5404 1 : return nullptr;
5405 : }
5406 830 : if (nRangeTokens <= 1)
5407 : {
5408 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5409 : pszIdxSpec);
5410 1 : return nullptr;
5411 : }
5412 829 : const char *pszStart = aosRangeTokens[0];
5413 829 : const char *pszEnd = aosRangeTokens[1];
5414 829 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5415 829 : GDALSlicedMDArray::Range range;
5416 829 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5417 829 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5418 1657 : if (range.m_nIncr == 0 ||
5419 828 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5420 : {
5421 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5422 1 : return nullptr;
5423 : }
5424 828 : auto startIdx(CPLAtoGIntBig(pszStart));
5425 828 : if (startIdx < 0)
5426 : {
5427 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5428 0 : startIdx = 0;
5429 : else
5430 0 : startIdx = nDimSize + startIdx;
5431 : }
5432 828 : const bool bPosIncr = range.m_nIncr > 0;
5433 828 : range.m_nStartIdx = startIdx;
5434 1656 : range.m_nStartIdx = EQUAL(pszStart, "")
5435 828 : ? (bPosIncr ? 0 : nDimSize - 1)
5436 : : range.m_nStartIdx;
5437 828 : if (range.m_nStartIdx >= nDimSize - 1)
5438 188 : range.m_nStartIdx = nDimSize - 1;
5439 828 : auto endIdx(CPLAtoGIntBig(pszEnd));
5440 828 : if (endIdx < 0)
5441 : {
5442 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5443 1 : if (nDimSize < positiveEndIdx)
5444 0 : endIdx = 0;
5445 : else
5446 1 : endIdx = nDimSize - positiveEndIdx;
5447 : }
5448 828 : GUInt64 nEndIdx = endIdx;
5449 828 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5450 828 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5451 826 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5452 : {
5453 3 : CPLError(CE_Failure, CPLE_AppDefined,
5454 : "Output dimension of size 0 is not allowed");
5455 3 : return nullptr;
5456 : }
5457 825 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5458 825 : const auto nAbsIncr = std::abs(range.m_nIncr);
5459 825 : const GUInt64 newSize =
5460 : bPosIncr
5461 859 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5462 34 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5463 825 : const auto &poSrcDim = srcDims[nCurSrcDim];
5464 1361 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5465 536 : newSize == poSrcDim->GetSize())
5466 : {
5467 167 : newDims.push_back(poSrcDim);
5468 : }
5469 : else
5470 : {
5471 1316 : std::string osNewDimName(poSrcDim->GetName());
5472 658 : if (bRenameDimensions)
5473 : {
5474 : osNewDimName =
5475 1220 : "subset_" + poSrcDim->GetName() +
5476 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5477 : "_" CPL_FRMT_GUIB,
5478 610 : static_cast<GUIntBig>(range.m_nStartIdx),
5479 610 : static_cast<GIntBig>(range.m_nIncr),
5480 610 : static_cast<GUIntBig>(newSize));
5481 : }
5482 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5483 1316 : std::string(), osNewDimName, poSrcDim->GetType(),
5484 658 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5485 : : std::string(),
5486 1316 : newSize);
5487 658 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5488 738 : if (poSrcIndexingVar &&
5489 738 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5490 80 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5491 : {
5492 : std::vector<std::shared_ptr<GDALDimension>>
5493 320 : indexingVarNewDims{poNewDim};
5494 160 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5495 : std::vector<std::shared_ptr<GDALMDArray>>
5496 160 : indexingVarNewIndexingVar;
5497 : std::vector<GDALSlicedMDArray::Range>
5498 160 : indexingVarParentRanges{range};
5499 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5500 : poSrcIndexingVar, pszIdxSpec,
5501 80 : std::move(indexingVarNewDims),
5502 80 : std::move(indexingVarMapDimIdxToParentDimIdx),
5503 80 : std::move(indexingVarNewIndexingVar),
5504 240 : std::move(indexingVarParentRanges));
5505 80 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5506 80 : apoNewIndexingVariables.push_back(
5507 80 : std::move(poNewIndexingVar));
5508 : }
5509 658 : newDims.push_back(std::move(poNewDim));
5510 : }
5511 825 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5512 825 : parentRanges.emplace_back(range);
5513 : }
5514 :
5515 1144 : nCurSrcDim++;
5516 : }
5517 661 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5518 : {
5519 73 : parentRanges.emplace_back(0, 1);
5520 73 : newDims.push_back(srcDims[nCurSrcDim]);
5521 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5522 : }
5523 :
5524 588 : GDALMDArray::ViewSpec viewSpec;
5525 588 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5526 588 : viewSpec.m_parentRanges = parentRanges;
5527 588 : viewSpecs.emplace_back(std::move(viewSpec));
5528 :
5529 1176 : return GDALSlicedMDArray::Create(
5530 588 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5531 1176 : std::move(apoNewIndexingVariables), std::move(parentRanges));
5532 : }
5533 :
5534 : /************************************************************************/
5535 : /* GDALExtractFieldMDArray */
5536 : /************************************************************************/
5537 :
5538 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5539 : {
5540 : private:
5541 : std::shared_ptr<GDALMDArray> m_poParent{};
5542 : GDALExtendedDataType m_dt;
5543 : std::string m_srcCompName;
5544 : mutable std::vector<GByte> m_pabyNoData{};
5545 :
5546 : protected:
5547 82 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5548 : const std::string &fieldName,
5549 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5550 328 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5551 164 : " of " +
5552 82 : poParent->GetFullName()),
5553 : GDALPamMDArray(
5554 164 : std::string(),
5555 164 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5556 164 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5557 : m_poParent(poParent), m_dt(srcComp->GetType()),
5558 410 : m_srcCompName(srcComp->GetName())
5559 : {
5560 82 : m_pabyNoData.resize(m_dt.GetSize());
5561 82 : }
5562 :
5563 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5564 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5565 : const GDALExtendedDataType &bufferDataType,
5566 : void *pDstBuffer) const override;
5567 :
5568 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5569 : CSLConstList papszOptions) const override
5570 : {
5571 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5572 : }
5573 :
5574 : public:
5575 : static std::shared_ptr<GDALExtractFieldMDArray>
5576 82 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5577 : const std::string &fieldName,
5578 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5579 : {
5580 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5581 82 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5582 82 : newAr->SetSelf(newAr);
5583 82 : return newAr;
5584 : }
5585 :
5586 164 : ~GDALExtractFieldMDArray() override
5587 82 : {
5588 82 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5589 164 : }
5590 :
5591 41 : bool IsWritable() const override
5592 : {
5593 41 : return m_poParent->IsWritable();
5594 : }
5595 :
5596 247 : const std::string &GetFilename() const override
5597 : {
5598 247 : return m_poParent->GetFilename();
5599 : }
5600 :
5601 : const std::vector<std::shared_ptr<GDALDimension>> &
5602 351 : GetDimensions() const override
5603 : {
5604 351 : return m_poParent->GetDimensions();
5605 : }
5606 :
5607 295 : const GDALExtendedDataType &GetDataType() const override
5608 : {
5609 295 : return m_dt;
5610 : }
5611 :
5612 2 : const std::string &GetUnit() const override
5613 : {
5614 2 : return m_poParent->GetUnit();
5615 : }
5616 :
5617 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5618 : {
5619 2 : return m_poParent->GetSpatialRef();
5620 : }
5621 :
5622 60 : const void *GetRawNoDataValue() const override
5623 : {
5624 60 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5625 60 : if (parentNoData == nullptr)
5626 1 : return nullptr;
5627 :
5628 59 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5629 59 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5630 :
5631 118 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5632 118 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5633 118 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5634 59 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5635 177 : std::move(comps)));
5636 :
5637 59 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5638 59 : &m_pabyNoData[0], tmpDT);
5639 :
5640 59 : return &m_pabyNoData[0];
5641 : }
5642 :
5643 2 : double GetOffset(bool *pbHasOffset,
5644 : GDALDataType *peStorageType) const override
5645 : {
5646 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5647 : }
5648 :
5649 2 : double GetScale(bool *pbHasScale,
5650 : GDALDataType *peStorageType) const override
5651 : {
5652 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5653 : }
5654 :
5655 42 : std::vector<GUInt64> GetBlockSize() const override
5656 : {
5657 42 : return m_poParent->GetBlockSize();
5658 : }
5659 : };
5660 :
5661 : /************************************************************************/
5662 : /* IRead() */
5663 : /************************************************************************/
5664 :
5665 88 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5666 : const size_t *count,
5667 : const GInt64 *arrayStep,
5668 : const GPtrDiff_t *bufferStride,
5669 : const GDALExtendedDataType &bufferDataType,
5670 : void *pDstBuffer) const
5671 : {
5672 176 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5673 176 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5674 176 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5675 : auto tmpDT(GDALExtendedDataType::Create(
5676 176 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5677 :
5678 88 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5679 176 : tmpDT, pDstBuffer);
5680 : }
5681 :
5682 : /************************************************************************/
5683 : /* CreateFieldNameExtractArray() */
5684 : /************************************************************************/
5685 :
5686 : static std::shared_ptr<GDALMDArray>
5687 83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5688 : const std::string &fieldName)
5689 : {
5690 83 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5691 83 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5692 202 : for (const auto &comp : self->GetDataType().GetComponents())
5693 : {
5694 201 : if (comp->GetName() == fieldName)
5695 : {
5696 82 : srcComp = ∁
5697 82 : break;
5698 : }
5699 : }
5700 83 : if (srcComp == nullptr)
5701 : {
5702 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5703 : fieldName.c_str());
5704 1 : return nullptr;
5705 : }
5706 82 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5707 : }
5708 :
5709 : /************************************************************************/
5710 : /* GetView() */
5711 : /************************************************************************/
5712 :
5713 : // clang-format off
5714 : /** Return a view of the array using slicing or field access.
5715 : *
5716 : * The slice expression uses the same syntax as NumPy basic slicing and
5717 : * indexing. See
5718 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5719 : * Or it can use field access by name. See
5720 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5721 : *
5722 : * Multiple [] bracket elements can be concatenated, with a slice expression
5723 : * or field name inside each.
5724 : *
5725 : * For basic slicing and indexing, inside each [] bracket element, a list of
5726 : * indexes that apply to successive source dimensions, can be specified, using
5727 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5728 : * or newaxis, using a comma separator.
5729 : *
5730 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5731 : * <ul>
5732 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5733 : * at index 1 in the first dimension, and index 2 in the second dimension
5734 : * from the source array. That is 5</li>
5735 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5736 : * implemented internally doing this intermediate slicing approach.</li>
5737 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5738 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5739 : * first dimension. That is [4,5,6,7].</li>
5740 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5741 : * second dimension. That is [2,6].</li>
5742 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5743 : * the second dimension. That is [[2],[6]].</li>
5744 : * <li>GetView("[::,2]"): Same as
5745 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5746 : * ellipsis only expands to one dimension here.</li>
5747 : * <li>GetView("[:,::2]"):
5748 : * returns a 2-dimensional array, with even-indexed elements of the second
5749 : * dimension. That is [[0,2],[4,6]].</li>
5750 : * <li>GetView("[:,1::2]"): returns a
5751 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5752 : * is [[1,3],[5,7]].</li>
5753 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5754 : * array, with elements of the second dimension with index in the range [1,3[.
5755 : * That is [[1,2],[5,6]].</li>
5756 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5757 : * array, with the values in first dimension reversed. That is
5758 : * [[4,5,6,7],[0,1,2,3]].</li>
5759 : * <li>GetView("[newaxis,...]"): returns a
5760 : * 3-dimensional array, with an additional dimension of size 1 put at the
5761 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5762 : * </ul>
5763 : *
5764 : * One difference with NumPy behavior is that ranges that would result in
5765 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5766 : * GDAL multidimensional model).
5767 : *
5768 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5769 : * Multiple field specification is not supported currently.
5770 : *
5771 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5772 : *
5773 : * \note When using the GDAL Python bindings, natural Python syntax can be
5774 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5775 : * ar.GetView("[0,::,1]['foo']")
5776 : * \note When using the C++ API and integer indexing only, you may use the
5777 : * at(idx0, idx1, ...) method.
5778 : *
5779 : * The returned array holds a reference to the original one, and thus is
5780 : * a view of it (not a copy). If the content of the original array changes,
5781 : * the content of the view array too. When using basic slicing and indexing,
5782 : * the view can be written if the underlying array is writable.
5783 : *
5784 : * This is the same as the C function GDALMDArrayGetView()
5785 : *
5786 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5787 : * access.
5788 : * @return a new array, that holds a reference to the original one, and thus is
5789 : * a view of it (not a copy), or nullptr in case of error.
5790 : */
5791 : // clang-format on
5792 :
5793 : std::shared_ptr<GDALMDArray>
5794 623 : GDALMDArray::GetView(const std::string &viewExpr) const
5795 : {
5796 1246 : std::vector<ViewSpec> viewSpecs;
5797 1246 : return GetView(viewExpr, true, viewSpecs);
5798 : }
5799 :
5800 : //! @cond Doxygen_Suppress
5801 : std::shared_ptr<GDALMDArray>
5802 695 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5803 : std::vector<ViewSpec> &viewSpecs) const
5804 : {
5805 1390 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5806 695 : if (!self)
5807 : {
5808 1 : CPLError(CE_Failure, CPLE_AppDefined,
5809 : "Driver implementation issue: m_pSelf not set !");
5810 1 : return nullptr;
5811 : }
5812 694 : std::string curExpr(viewExpr);
5813 : while (true)
5814 : {
5815 697 : if (curExpr.empty() || curExpr[0] != '[')
5816 : {
5817 2 : CPLError(CE_Failure, CPLE_AppDefined,
5818 : "Slice string should start with ['");
5819 694 : return nullptr;
5820 : }
5821 :
5822 695 : std::string fieldName;
5823 : size_t endExpr;
5824 695 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5825 : {
5826 87 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5827 : {
5828 2 : CPLError(CE_Failure, CPLE_AppDefined,
5829 : "Field access not allowed on non-compound data type");
5830 2 : return nullptr;
5831 : }
5832 85 : size_t idx = 2;
5833 768 : for (; idx < curExpr.size(); idx++)
5834 : {
5835 767 : const char ch = curExpr[idx];
5836 767 : if (ch == curExpr[1])
5837 84 : break;
5838 683 : if (ch == '\\' && idx + 1 < curExpr.size())
5839 : {
5840 1 : fieldName += curExpr[idx + 1];
5841 1 : idx++;
5842 : }
5843 : else
5844 : {
5845 682 : fieldName += ch;
5846 : }
5847 : }
5848 85 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5849 : {
5850 2 : CPLError(CE_Failure, CPLE_AppDefined,
5851 : "Invalid field access specification");
5852 2 : return nullptr;
5853 : }
5854 83 : endExpr = idx + 1;
5855 : }
5856 : else
5857 : {
5858 608 : endExpr = curExpr.find(']');
5859 : }
5860 691 : if (endExpr == std::string::npos)
5861 : {
5862 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5863 1 : return nullptr;
5864 : }
5865 690 : if (endExpr == 1)
5866 : {
5867 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5868 1 : return nullptr;
5869 : }
5870 689 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5871 :
5872 689 : if (!fieldName.empty())
5873 : {
5874 166 : ViewSpec viewSpec;
5875 83 : viewSpec.m_osFieldName = fieldName;
5876 83 : viewSpecs.emplace_back(std::move(viewSpec));
5877 : }
5878 :
5879 689 : auto newArray = !fieldName.empty()
5880 : ? CreateFieldNameExtractArray(self, fieldName)
5881 : : CreateSlicedArray(self, viewExpr, activeSlice,
5882 689 : bRenameDimensions, viewSpecs);
5883 :
5884 689 : if (endExpr == curExpr.size() - 1)
5885 : {
5886 686 : return newArray;
5887 : }
5888 3 : self = std::move(newArray);
5889 3 : curExpr = curExpr.substr(endExpr + 1);
5890 3 : }
5891 : }
5892 :
5893 : //! @endcond
5894 :
5895 : std::shared_ptr<GDALMDArray>
5896 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5897 : {
5898 19 : std::string osExpr("[");
5899 19 : bool bFirst = true;
5900 45 : for (const auto &idx : indices)
5901 : {
5902 26 : if (!bFirst)
5903 7 : osExpr += ',';
5904 26 : bFirst = false;
5905 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5906 : }
5907 57 : return GetView(osExpr + ']');
5908 : }
5909 :
5910 : /************************************************************************/
5911 : /* operator[] */
5912 : /************************************************************************/
5913 :
5914 : /** Return a view of the array using field access
5915 : *
5916 : * Equivalent of GetView("['fieldName']")
5917 : *
5918 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5919 : */
5920 : std::shared_ptr<GDALMDArray>
5921 2 : GDALMDArray::operator[](const std::string &fieldName) const
5922 : {
5923 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5924 4 : .replaceAll('\\', "\\\\")
5925 4 : .replaceAll('\'', "\\\'")
5926 6 : .c_str()));
5927 : }
5928 :
5929 : /************************************************************************/
5930 : /* GDALMDArrayTransposed */
5931 : /************************************************************************/
5932 :
5933 : class GDALMDArrayTransposed final : public GDALPamMDArray
5934 : {
5935 : private:
5936 : std::shared_ptr<GDALMDArray> m_poParent{};
5937 : std::vector<int> m_anMapNewAxisToOldAxis{};
5938 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5939 :
5940 : mutable std::vector<GUInt64> m_parentStart;
5941 : mutable std::vector<size_t> m_parentCount;
5942 : mutable std::vector<GInt64> m_parentStep;
5943 : mutable std::vector<GPtrDiff_t> m_parentStride;
5944 :
5945 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5946 : const GInt64 *arrayStep,
5947 : const GPtrDiff_t *bufferStride) const;
5948 :
5949 : static std::string
5950 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5951 : {
5952 84 : std::string ret;
5953 84 : ret += '[';
5954 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5955 : {
5956 228 : if (i > 0)
5957 144 : ret += ',';
5958 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5959 : }
5960 84 : ret += ']';
5961 84 : return ret;
5962 : }
5963 :
5964 : protected:
5965 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5966 : const std::vector<int> &anMapNewAxisToOldAxis,
5967 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5968 84 : : GDALAbstractMDArray(std::string(),
5969 84 : "Transposed view of " + poParent->GetFullName() +
5970 84 : " along " +
5971 42 : MappingToStr(anMapNewAxisToOldAxis)),
5972 84 : GDALPamMDArray(std::string(),
5973 84 : "Transposed view of " + poParent->GetFullName() +
5974 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5975 84 : GDALPamMultiDim::GetPAM(poParent),
5976 : poParent->GetContext()),
5977 42 : m_poParent(std::move(poParent)),
5978 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5979 42 : m_dims(std::move(dims)),
5980 42 : m_parentStart(m_poParent->GetDimensionCount()),
5981 42 : m_parentCount(m_poParent->GetDimensionCount()),
5982 42 : m_parentStep(m_poParent->GetDimensionCount()),
5983 336 : m_parentStride(m_poParent->GetDimensionCount())
5984 : {
5985 42 : }
5986 :
5987 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5988 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5989 : const GDALExtendedDataType &bufferDataType,
5990 : void *pDstBuffer) const override;
5991 :
5992 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5993 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5994 : const GDALExtendedDataType &bufferDataType,
5995 : const void *pSrcBuffer) override;
5996 :
5997 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5998 : CSLConstList papszOptions) const override;
5999 :
6000 : public:
6001 : static std::shared_ptr<GDALMDArrayTransposed>
6002 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6003 : const std::vector<int> &anMapNewAxisToOldAxis)
6004 : {
6005 42 : const auto &parentDims(poParent->GetDimensions());
6006 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
6007 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6008 : {
6009 114 : if (iOldAxis < 0)
6010 : {
6011 1 : dims.push_back(std::make_shared<GDALDimension>(
6012 2 : std::string(), "newaxis", std::string(), std::string(), 1));
6013 : }
6014 : else
6015 : {
6016 113 : dims.emplace_back(parentDims[iOldAxis]);
6017 : }
6018 : }
6019 :
6020 : auto newAr(
6021 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6022 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
6023 42 : newAr->SetSelf(newAr);
6024 84 : return newAr;
6025 : }
6026 :
6027 1 : bool IsWritable() const override
6028 : {
6029 1 : return m_poParent->IsWritable();
6030 : }
6031 :
6032 84 : const std::string &GetFilename() const override
6033 : {
6034 84 : return m_poParent->GetFilename();
6035 : }
6036 :
6037 : const std::vector<std::shared_ptr<GDALDimension>> &
6038 358 : GetDimensions() const override
6039 : {
6040 358 : return m_dims;
6041 : }
6042 :
6043 141 : const GDALExtendedDataType &GetDataType() const override
6044 : {
6045 141 : return m_poParent->GetDataType();
6046 : }
6047 :
6048 4 : const std::string &GetUnit() const override
6049 : {
6050 4 : return m_poParent->GetUnit();
6051 : }
6052 :
6053 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6054 : {
6055 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6056 5 : if (!poSrcSRS)
6057 2 : return nullptr;
6058 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6059 6 : std::vector<int> dstMapping;
6060 9 : for (int srcAxis : srcMapping)
6061 : {
6062 6 : bool bFound = false;
6063 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6064 : {
6065 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6066 : {
6067 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6068 6 : bFound = true;
6069 6 : break;
6070 : }
6071 : }
6072 6 : if (!bFound)
6073 : {
6074 0 : dstMapping.push_back(0);
6075 : }
6076 : }
6077 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6078 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6079 3 : return poClone;
6080 : }
6081 :
6082 4 : const void *GetRawNoDataValue() const override
6083 : {
6084 4 : return m_poParent->GetRawNoDataValue();
6085 : }
6086 :
6087 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6088 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6089 :
6090 4 : double GetOffset(bool *pbHasOffset,
6091 : GDALDataType *peStorageType) const override
6092 : {
6093 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6094 : }
6095 :
6096 4 : double GetScale(bool *pbHasScale,
6097 : GDALDataType *peStorageType) const override
6098 : {
6099 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6100 : }
6101 :
6102 : // bool SetOffset(double dfOffset) override { return
6103 : // m_poParent->SetOffset(dfOffset); }
6104 :
6105 : // bool SetScale(double dfScale) override { return
6106 : // m_poParent->SetScale(dfScale); }
6107 :
6108 3 : std::vector<GUInt64> GetBlockSize() const override
6109 : {
6110 3 : std::vector<GUInt64> ret(GetDimensionCount());
6111 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6112 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6113 : {
6114 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6115 8 : if (iOldAxis >= 0)
6116 : {
6117 7 : ret[i] = parentBlockSize[iOldAxis];
6118 : }
6119 : }
6120 6 : return ret;
6121 : }
6122 :
6123 : std::shared_ptr<GDALAttribute>
6124 1 : GetAttribute(const std::string &osName) const override
6125 : {
6126 1 : return m_poParent->GetAttribute(osName);
6127 : }
6128 :
6129 : std::vector<std::shared_ptr<GDALAttribute>>
6130 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6131 : {
6132 6 : return m_poParent->GetAttributes(papszOptions);
6133 : }
6134 : };
6135 :
6136 : /************************************************************************/
6137 : /* PrepareParentArrays() */
6138 : /************************************************************************/
6139 :
6140 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6141 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6142 : const GPtrDiff_t *bufferStride) const
6143 : {
6144 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6145 : {
6146 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6147 129 : if (iOldAxis >= 0)
6148 : {
6149 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6150 128 : m_parentCount[iOldAxis] = count[i];
6151 128 : if (arrayStep) // only null when called from IAdviseRead()
6152 : {
6153 126 : m_parentStep[iOldAxis] = arrayStep[i];
6154 : }
6155 128 : if (bufferStride) // only null when called from IAdviseRead()
6156 : {
6157 126 : m_parentStride[iOldAxis] = bufferStride[i];
6158 : }
6159 : }
6160 : }
6161 47 : }
6162 :
6163 : /************************************************************************/
6164 : /* IRead() */
6165 : /************************************************************************/
6166 :
6167 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6168 : const size_t *count, const GInt64 *arrayStep,
6169 : const GPtrDiff_t *bufferStride,
6170 : const GDALExtendedDataType &bufferDataType,
6171 : void *pDstBuffer) const
6172 : {
6173 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6174 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6175 44 : m_parentStep.data(), m_parentStride.data(),
6176 44 : bufferDataType, pDstBuffer);
6177 : }
6178 :
6179 : /************************************************************************/
6180 : /* IWrite() */
6181 : /************************************************************************/
6182 :
6183 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6184 : const size_t *count, const GInt64 *arrayStep,
6185 : const GPtrDiff_t *bufferStride,
6186 : const GDALExtendedDataType &bufferDataType,
6187 : const void *pSrcBuffer)
6188 : {
6189 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6190 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6191 2 : m_parentStep.data(), m_parentStride.data(),
6192 2 : bufferDataType, pSrcBuffer);
6193 : }
6194 :
6195 : /************************************************************************/
6196 : /* IAdviseRead() */
6197 : /************************************************************************/
6198 :
6199 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6200 : const size_t *count,
6201 : CSLConstList papszOptions) const
6202 : {
6203 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6204 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6205 1 : papszOptions);
6206 : }
6207 :
6208 : /************************************************************************/
6209 : /* Transpose() */
6210 : /************************************************************************/
6211 :
6212 : /** Return a view of the array whose axis have been reordered.
6213 : *
6214 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6215 : * and GetDimensionCount() - 1, and each only once.
6216 : * -1 can be used as a special index value to ask for the insertion of a new
6217 : * axis of size 1.
6218 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6219 : * index of one of its dimension, it corresponds to the axis of index
6220 : * anMapNewAxisToOldAxis[i] from the current array.
6221 : *
6222 : * This is similar to the numpy.transpose() method
6223 : *
6224 : * The returned array holds a reference to the original one, and thus is
6225 : * a view of it (not a copy). If the content of the original array changes,
6226 : * the content of the view array too. The view can be written if the underlying
6227 : * array is writable.
6228 : *
6229 : * Note that I/O performance in such a transposed view might be poor.
6230 : *
6231 : * This is the same as the C function GDALMDArrayTranspose().
6232 : *
6233 : * @return a new array, that holds a reference to the original one, and thus is
6234 : * a view of it (not a copy), or nullptr in case of error.
6235 : */
6236 : std::shared_ptr<GDALMDArray>
6237 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6238 : {
6239 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6240 50 : if (!self)
6241 : {
6242 0 : CPLError(CE_Failure, CPLE_AppDefined,
6243 : "Driver implementation issue: m_pSelf not set !");
6244 0 : return nullptr;
6245 : }
6246 50 : const int nDims = static_cast<int>(GetDimensionCount());
6247 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6248 50 : int nCountOldAxis = 0;
6249 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6250 : {
6251 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6252 : {
6253 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6254 4 : return nullptr;
6255 : }
6256 130 : if (iOldAxis >= 0)
6257 : {
6258 128 : if (alreadyUsedOldAxis[iOldAxis])
6259 : {
6260 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6261 : iOldAxis);
6262 1 : return nullptr;
6263 : }
6264 127 : alreadyUsedOldAxis[iOldAxis] = true;
6265 127 : nCountOldAxis++;
6266 : }
6267 : }
6268 46 : if (nCountOldAxis != nDims)
6269 : {
6270 4 : CPLError(CE_Failure, CPLE_AppDefined,
6271 : "One or several original axis missing");
6272 4 : return nullptr;
6273 : }
6274 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6275 : }
6276 :
6277 : /************************************************************************/
6278 : /* IRead() */
6279 : /************************************************************************/
6280 :
6281 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6282 : const size_t *count, const GInt64 *arrayStep,
6283 : const GPtrDiff_t *bufferStride,
6284 : const GDALExtendedDataType &bufferDataType,
6285 : void *pDstBuffer) const
6286 : {
6287 16 : const double dfScale = m_dfScale;
6288 16 : const double dfOffset = m_dfOffset;
6289 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6290 : const auto dtDouble =
6291 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6292 16 : const size_t nDTSize = dtDouble.GetSize();
6293 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6294 :
6295 16 : double adfSrcNoData[2] = {0, 0};
6296 16 : if (m_bHasNoData)
6297 : {
6298 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6299 9 : m_poParent->GetDataType(),
6300 : &adfSrcNoData[0], dtDouble);
6301 : }
6302 :
6303 16 : const auto nDims = GetDimensions().size();
6304 16 : if (nDims == 0)
6305 : {
6306 : double adfVal[2];
6307 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6308 : dtDouble, &adfVal[0]))
6309 : {
6310 0 : return false;
6311 : }
6312 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6313 : {
6314 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6315 6 : if (bDTIsComplex)
6316 : {
6317 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6318 : }
6319 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6320 : bufferDataType);
6321 : }
6322 : else
6323 : {
6324 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6325 : pDstBuffer, bufferDataType);
6326 : }
6327 9 : return true;
6328 : }
6329 :
6330 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6331 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6332 7 : void *pTempBuffer = pDstBuffer;
6333 7 : if (bTempBufferNeeded)
6334 : {
6335 2 : size_t nElts = 1;
6336 2 : actualBufferStrideVector.resize(nDims);
6337 7 : for (size_t i = 0; i < nDims; i++)
6338 5 : nElts *= count[i];
6339 2 : actualBufferStrideVector.back() = 1;
6340 5 : for (size_t i = nDims - 1; i > 0;)
6341 : {
6342 3 : --i;
6343 3 : actualBufferStrideVector[i] =
6344 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6345 : }
6346 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6347 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6348 2 : if (!pTempBuffer)
6349 0 : return false;
6350 : }
6351 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6352 : actualBufferStridePtr, dtDouble, pTempBuffer))
6353 : {
6354 0 : if (bTempBufferNeeded)
6355 0 : VSIFree(pTempBuffer);
6356 0 : return false;
6357 : }
6358 :
6359 : struct Stack
6360 : {
6361 : size_t nIters = 0;
6362 : double *src_ptr = nullptr;
6363 : GByte *dst_ptr = nullptr;
6364 : GPtrDiff_t src_inc_offset = 0;
6365 : GPtrDiff_t dst_inc_offset = 0;
6366 : };
6367 :
6368 7 : std::vector<Stack> stack(nDims);
6369 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6370 23 : for (size_t i = 0; i < nDims; i++)
6371 : {
6372 32 : stack[i].src_inc_offset =
6373 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6374 16 : stack[i].dst_inc_offset =
6375 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6376 : }
6377 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6378 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6379 :
6380 7 : size_t dimIdx = 0;
6381 7 : const size_t nDimsMinus1 = nDims - 1;
6382 : GByte abyDstNoData[16];
6383 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6384 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6385 : bufferDataType);
6386 :
6387 37 : lbl_next_depth:
6388 37 : if (dimIdx == nDimsMinus1)
6389 : {
6390 25 : auto nIters = count[dimIdx];
6391 25 : double *padfVal = stack[dimIdx].src_ptr;
6392 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6393 : while (true)
6394 : {
6395 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6396 : {
6397 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6398 88 : if (bDTIsComplex)
6399 : {
6400 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6401 : }
6402 88 : if (bTempBufferNeeded)
6403 : {
6404 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6405 : dst_ptr, bufferDataType);
6406 : }
6407 : }
6408 : else
6409 : {
6410 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6411 : }
6412 :
6413 92 : if ((--nIters) == 0)
6414 25 : break;
6415 67 : padfVal += stack[dimIdx].src_inc_offset;
6416 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6417 : }
6418 : }
6419 : else
6420 : {
6421 12 : stack[dimIdx].nIters = count[dimIdx];
6422 : while (true)
6423 : {
6424 30 : dimIdx++;
6425 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6426 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6427 30 : goto lbl_next_depth;
6428 30 : lbl_return_to_caller:
6429 30 : dimIdx--;
6430 30 : if ((--stack[dimIdx].nIters) == 0)
6431 12 : break;
6432 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6433 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6434 : }
6435 : }
6436 37 : if (dimIdx > 0)
6437 30 : goto lbl_return_to_caller;
6438 :
6439 7 : if (bTempBufferNeeded)
6440 2 : VSIFree(pTempBuffer);
6441 7 : return true;
6442 : }
6443 :
6444 : /************************************************************************/
6445 : /* IWrite() */
6446 : /************************************************************************/
6447 :
6448 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6449 : const size_t *count, const GInt64 *arrayStep,
6450 : const GPtrDiff_t *bufferStride,
6451 : const GDALExtendedDataType &bufferDataType,
6452 : const void *pSrcBuffer)
6453 : {
6454 16 : const double dfScale = m_dfScale;
6455 16 : const double dfOffset = m_dfOffset;
6456 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6457 : const auto dtDouble =
6458 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6459 16 : const size_t nDTSize = dtDouble.GetSize();
6460 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6461 : const bool bSelfAndParentHaveNoData =
6462 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6463 16 : double dfNoData = 0;
6464 16 : if (m_bHasNoData)
6465 : {
6466 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6467 : &dfNoData, GDT_Float64, 0, 1);
6468 : }
6469 :
6470 16 : double adfSrcNoData[2] = {0, 0};
6471 16 : if (bSelfAndParentHaveNoData)
6472 : {
6473 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6474 7 : m_poParent->GetDataType(),
6475 : &adfSrcNoData[0], dtDouble);
6476 : }
6477 :
6478 16 : const auto nDims = GetDimensions().size();
6479 16 : if (nDims == 0)
6480 : {
6481 : double adfVal[2];
6482 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6483 : dtDouble);
6484 16 : if (bSelfAndParentHaveNoData &&
6485 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6486 : {
6487 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6488 2 : bufferStride, m_poParent->GetDataType(),
6489 4 : m_poParent->GetRawNoDataValue());
6490 : }
6491 : else
6492 : {
6493 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6494 8 : if (bDTIsComplex)
6495 : {
6496 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6497 : }
6498 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6499 8 : bufferStride, dtDouble, &adfVal[0]);
6500 : }
6501 : }
6502 :
6503 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6504 6 : size_t nElts = 1;
6505 6 : tmpBufferStrideVector.resize(nDims);
6506 20 : for (size_t i = 0; i < nDims; i++)
6507 14 : nElts *= count[i];
6508 6 : tmpBufferStrideVector.back() = 1;
6509 14 : for (size_t i = nDims - 1; i > 0;)
6510 : {
6511 8 : --i;
6512 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6513 : }
6514 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6515 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6516 6 : if (!pTempBuffer)
6517 0 : return false;
6518 :
6519 : struct Stack
6520 : {
6521 : size_t nIters = 0;
6522 : double *dst_ptr = nullptr;
6523 : const GByte *src_ptr = nullptr;
6524 : GPtrDiff_t src_inc_offset = 0;
6525 : GPtrDiff_t dst_inc_offset = 0;
6526 : };
6527 :
6528 6 : std::vector<Stack> stack(nDims);
6529 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6530 20 : for (size_t i = 0; i < nDims; i++)
6531 : {
6532 28 : stack[i].dst_inc_offset =
6533 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6534 14 : stack[i].src_inc_offset =
6535 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6536 : }
6537 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6538 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6539 :
6540 6 : size_t dimIdx = 0;
6541 6 : const size_t nDimsMinus1 = nDims - 1;
6542 :
6543 34 : lbl_next_depth:
6544 34 : if (dimIdx == nDimsMinus1)
6545 : {
6546 23 : auto nIters = count[dimIdx];
6547 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6548 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6549 : while (true)
6550 : {
6551 : double adfVal[2];
6552 : const double *padfSrcVal;
6553 86 : if (bIsBufferDataTypeNativeDataType)
6554 : {
6555 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6556 : }
6557 : else
6558 : {
6559 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6560 : &adfVal[0], dtDouble);
6561 36 : padfSrcVal = adfVal;
6562 : }
6563 :
6564 148 : if (bSelfAndParentHaveNoData &&
6565 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6566 : {
6567 3 : dst_ptr[0] = adfSrcNoData[0];
6568 3 : if (bDTIsComplex)
6569 : {
6570 1 : dst_ptr[1] = adfSrcNoData[1];
6571 : }
6572 : }
6573 : else
6574 : {
6575 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6576 83 : if (bDTIsComplex)
6577 : {
6578 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6579 : }
6580 : }
6581 :
6582 86 : if ((--nIters) == 0)
6583 23 : break;
6584 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6585 63 : src_ptr += stack[dimIdx].src_inc_offset;
6586 63 : }
6587 : }
6588 : else
6589 : {
6590 11 : stack[dimIdx].nIters = count[dimIdx];
6591 : while (true)
6592 : {
6593 28 : dimIdx++;
6594 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6595 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6596 28 : goto lbl_next_depth;
6597 28 : lbl_return_to_caller:
6598 28 : dimIdx--;
6599 28 : if ((--stack[dimIdx].nIters) == 0)
6600 11 : break;
6601 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6602 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6603 : }
6604 : }
6605 34 : if (dimIdx > 0)
6606 28 : goto lbl_return_to_caller;
6607 :
6608 : // If the parent array is not double/complex-double, then convert the
6609 : // values to it, before calling Write(), as some implementations can be
6610 : // very slow when doing the type conversion.
6611 6 : const auto &eParentDT = m_poParent->GetDataType();
6612 6 : const size_t nParentDTSize = eParentDT.GetSize();
6613 6 : if (nParentDTSize <= nDTSize / 2)
6614 : {
6615 : // Copy in-place by making sure that source and target do not overlap
6616 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6617 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6618 :
6619 : // Copy first element
6620 : {
6621 6 : std::vector<GByte> abyTemp(nParentDTSize);
6622 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6623 6 : static_cast<int>(nDTSize), &abyTemp[0],
6624 : eParentNumericDT, static_cast<int>(nParentDTSize),
6625 : 1);
6626 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6627 : }
6628 : // Remaining elements
6629 86 : for (size_t i = 1; i < nElts; ++i)
6630 : {
6631 80 : GDALCopyWords64(
6632 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6633 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6634 : eParentNumericDT, 0, 1);
6635 : }
6636 : }
6637 :
6638 : const bool ret =
6639 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6640 : eParentDT, pTempBuffer);
6641 :
6642 6 : VSIFree(pTempBuffer);
6643 6 : return ret;
6644 : }
6645 :
6646 : /************************************************************************/
6647 : /* GetUnscaled() */
6648 : /************************************************************************/
6649 :
6650 : /** Return an array that is the unscaled version of the current one.
6651 : *
6652 : * That is each value of the unscaled array will be
6653 : * unscaled_value = raw_value * GetScale() + GetOffset()
6654 : *
6655 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6656 : * from unscaled values to raw values.
6657 : *
6658 : * This is the same as the C function GDALMDArrayGetUnscaled().
6659 : *
6660 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6661 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6662 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6663 : * @return a new array, that holds a reference to the original one, and thus is
6664 : * a view of it (not a copy), or nullptr in case of error.
6665 : */
6666 : std::shared_ptr<GDALMDArray>
6667 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6668 : double dfOverriddenDstNodata) const
6669 : {
6670 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6671 17 : if (!self)
6672 : {
6673 0 : CPLError(CE_Failure, CPLE_AppDefined,
6674 : "Driver implementation issue: m_pSelf not set !");
6675 0 : return nullptr;
6676 : }
6677 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6678 : {
6679 0 : CPLError(CE_Failure, CPLE_AppDefined,
6680 : "GetUnscaled() only supports numeric data type");
6681 0 : return nullptr;
6682 : }
6683 : const double dfScale =
6684 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6685 : const double dfOffset =
6686 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6687 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6688 4 : return self;
6689 :
6690 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6691 13 : ? GDT_CFloat64
6692 13 : : GDT_Float64;
6693 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6694 : {
6695 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6696 0 : eDT = GDT_Float16;
6697 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6698 1 : eDT = GDT_Float32;
6699 : }
6700 :
6701 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6702 13 : dfOverriddenDstNodata, eDT);
6703 : }
6704 :
6705 : /************************************************************************/
6706 : /* GDALMDArrayMask */
6707 : /************************************************************************/
6708 :
6709 : class GDALMDArrayMask final : public GDALPamMDArray
6710 : {
6711 : private:
6712 : std::shared_ptr<GDALMDArray> m_poParent{};
6713 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6714 : double m_dfMissingValue = 0.0;
6715 : bool m_bHasMissingValue = false;
6716 : double m_dfFillValue = 0.0;
6717 : bool m_bHasFillValue = false;
6718 : double m_dfValidMin = 0.0;
6719 : bool m_bHasValidMin = false;
6720 : double m_dfValidMax = 0.0;
6721 : bool m_bHasValidMax = false;
6722 : std::vector<uint32_t> m_anValidFlagMasks{};
6723 : std::vector<uint32_t> m_anValidFlagValues{};
6724 :
6725 : bool Init(CSLConstList papszOptions);
6726 :
6727 : template <typename Type>
6728 : void
6729 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6730 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6731 : const void *pTempBuffer,
6732 : const GDALExtendedDataType &oTmpBufferDT,
6733 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6734 :
6735 : protected:
6736 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6737 96 : : GDALAbstractMDArray(std::string(),
6738 96 : "Mask of " + poParent->GetFullName()),
6739 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6740 96 : GDALPamMultiDim::GetPAM(poParent),
6741 : poParent->GetContext()),
6742 240 : m_poParent(std::move(poParent))
6743 : {
6744 48 : }
6745 :
6746 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6747 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6748 : const GDALExtendedDataType &bufferDataType,
6749 : void *pDstBuffer) const override;
6750 :
6751 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6752 : CSLConstList papszOptions) const override
6753 : {
6754 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6755 : }
6756 :
6757 : public:
6758 : static std::shared_ptr<GDALMDArrayMask>
6759 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6760 : CSLConstList papszOptions);
6761 :
6762 1 : bool IsWritable() const override
6763 : {
6764 1 : return false;
6765 : }
6766 :
6767 54 : const std::string &GetFilename() const override
6768 : {
6769 54 : return m_poParent->GetFilename();
6770 : }
6771 :
6772 : const std::vector<std::shared_ptr<GDALDimension>> &
6773 382 : GetDimensions() const override
6774 : {
6775 382 : return m_poParent->GetDimensions();
6776 : }
6777 :
6778 138 : const GDALExtendedDataType &GetDataType() const override
6779 : {
6780 138 : return m_dt;
6781 : }
6782 :
6783 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6784 : {
6785 1 : return m_poParent->GetSpatialRef();
6786 : }
6787 :
6788 2 : std::vector<GUInt64> GetBlockSize() const override
6789 : {
6790 2 : return m_poParent->GetBlockSize();
6791 : }
6792 : };
6793 :
6794 : /************************************************************************/
6795 : /* GDALMDArrayMask::Create() */
6796 : /************************************************************************/
6797 :
6798 : /* static */ std::shared_ptr<GDALMDArrayMask>
6799 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6800 : CSLConstList papszOptions)
6801 : {
6802 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6803 48 : newAr->SetSelf(newAr);
6804 48 : if (!newAr->Init(papszOptions))
6805 6 : return nullptr;
6806 42 : return newAr;
6807 : }
6808 :
6809 : /************************************************************************/
6810 : /* GDALMDArrayMask::Init() */
6811 : /************************************************************************/
6812 :
6813 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6814 : {
6815 : const auto GetSingleValNumericAttr =
6816 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6817 : {
6818 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6819 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6820 : {
6821 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6822 21 : if (anDimSizes.empty() ||
6823 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6824 : {
6825 11 : bHasVal = true;
6826 11 : dfVal = poAttr->ReadAsDouble();
6827 : }
6828 : }
6829 192 : };
6830 :
6831 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6832 48 : m_dfMissingValue);
6833 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6834 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6835 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6836 :
6837 : {
6838 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6839 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6840 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6841 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6842 : {
6843 6 : m_bHasValidMin = true;
6844 6 : m_bHasValidMax = true;
6845 6 : auto vals = poValidRange->ReadAsDoubleArray();
6846 6 : CPLAssert(vals.size() == 2);
6847 6 : m_dfValidMin = vals[0];
6848 6 : m_dfValidMax = vals[1];
6849 : }
6850 : }
6851 :
6852 : // Take into account
6853 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6854 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6855 : const char *pszUnmaskFlags =
6856 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6857 48 : if (pszUnmaskFlags)
6858 : {
6859 : const auto IsScalarStringAttr =
6860 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6861 : {
6862 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6863 26 : (poAttr->GetDimensionsSize().empty() ||
6864 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6865 26 : poAttr->GetDimensionsSize()[0] == 1));
6866 : };
6867 :
6868 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6869 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6870 : {
6871 1 : CPLError(CE_Failure, CPLE_AppDefined,
6872 : "UNMASK_FLAGS option specified but array has no "
6873 : "flag_meanings attribute");
6874 1 : return false;
6875 : }
6876 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6877 13 : if (!pszFlagMeanings)
6878 : {
6879 1 : CPLError(CE_Failure, CPLE_AppDefined,
6880 : "Cannot read flag_meanings attribute");
6881 1 : return false;
6882 : }
6883 :
6884 : const auto IsSingleDimNumericAttr =
6885 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6886 : {
6887 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6888 26 : poAttr->GetDimensionsSize().size() == 1;
6889 : };
6890 :
6891 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6892 : const bool bHasFlagValues =
6893 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6894 :
6895 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6896 : const bool bHasFlagMasks =
6897 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6898 :
6899 12 : if (!bHasFlagValues && !bHasFlagMasks)
6900 : {
6901 1 : CPLError(CE_Failure, CPLE_AppDefined,
6902 : "Cannot find flag_values and/or flag_masks attribute");
6903 1 : return false;
6904 : }
6905 :
6906 : const CPLStringList aosUnmaskFlags(
6907 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6908 : const CPLStringList aosFlagMeanings(
6909 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6910 :
6911 11 : if (bHasFlagValues)
6912 : {
6913 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6914 : // We could support Int64 or UInt64, but more work...
6915 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6916 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6917 : {
6918 0 : CPLError(CE_Failure, CPLE_NotSupported,
6919 : "Unsupported data type for flag_values attribute: %s",
6920 : GDALGetDataTypeName(eType));
6921 0 : return false;
6922 : }
6923 : }
6924 :
6925 11 : if (bHasFlagMasks)
6926 : {
6927 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6928 : // We could support Int64 or UInt64, but more work...
6929 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6930 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6931 : {
6932 0 : CPLError(CE_Failure, CPLE_NotSupported,
6933 : "Unsupported data type for flag_masks attribute: %s",
6934 : GDALGetDataTypeName(eType));
6935 0 : return false;
6936 : }
6937 : }
6938 :
6939 : const std::vector<double> adfValues(
6940 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6941 11 : : std::vector<double>());
6942 : const std::vector<double> adfMasks(
6943 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6944 11 : : std::vector<double>());
6945 :
6946 18 : if (bHasFlagValues &&
6947 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6948 : {
6949 1 : CPLError(CE_Failure, CPLE_AppDefined,
6950 : "Number of values in flag_values attribute is different "
6951 : "from the one in flag_meanings");
6952 1 : return false;
6953 : }
6954 :
6955 16 : if (bHasFlagMasks &&
6956 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6957 : {
6958 1 : CPLError(CE_Failure, CPLE_AppDefined,
6959 : "Number of values in flag_masks attribute is different "
6960 : "from the one in flag_meanings");
6961 1 : return false;
6962 : }
6963 :
6964 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6965 : {
6966 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6967 11 : if (nIdxFlag < 0)
6968 : {
6969 1 : CPLError(
6970 : CE_Failure, CPLE_AppDefined,
6971 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6972 : aosUnmaskFlags[i], pszFlagMeanings);
6973 1 : return false;
6974 : }
6975 :
6976 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6977 : {
6978 0 : CPLError(CE_Failure, CPLE_AppDefined,
6979 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6980 0 : adfValues[nIdxFlag]);
6981 0 : return false;
6982 : }
6983 :
6984 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6985 : {
6986 0 : CPLError(CE_Failure, CPLE_AppDefined,
6987 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6988 0 : adfMasks[nIdxFlag]);
6989 0 : return false;
6990 : }
6991 :
6992 10 : if (bHasFlagValues)
6993 : {
6994 12 : m_anValidFlagValues.push_back(
6995 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6996 : }
6997 :
6998 10 : if (bHasFlagMasks)
6999 : {
7000 12 : m_anValidFlagMasks.push_back(
7001 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
7002 : }
7003 : }
7004 : }
7005 :
7006 42 : return true;
7007 : }
7008 :
7009 : /************************************************************************/
7010 : /* IRead() */
7011 : /************************************************************************/
7012 :
7013 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7014 : const GInt64 *arrayStep,
7015 : const GPtrDiff_t *bufferStride,
7016 : const GDALExtendedDataType &bufferDataType,
7017 : void *pDstBuffer) const
7018 : {
7019 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7020 : {
7021 0 : CPLError(CE_Failure, CPLE_AppDefined,
7022 : "%s: only reading to a numeric data type is supported",
7023 : __func__);
7024 0 : return false;
7025 : }
7026 51 : size_t nElts = 1;
7027 51 : const size_t nDims = GetDimensionCount();
7028 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7029 139 : for (size_t i = 0; i < nDims; i++)
7030 88 : nElts *= count[i];
7031 51 : if (nDims > 0)
7032 : {
7033 46 : tmpBufferStrideVector.back() = 1;
7034 88 : for (size_t i = nDims - 1; i > 0;)
7035 : {
7036 42 : --i;
7037 42 : tmpBufferStrideVector[i] =
7038 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7039 : }
7040 : }
7041 :
7042 : /* Optimized case: if we are an integer data type and that there is no */
7043 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7044 : /* directly */
7045 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7046 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7047 34 : m_anValidFlagMasks.empty() &&
7048 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7049 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7050 : {
7051 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7052 7 : if (bBufferDataTypeIsByte) // Byte case
7053 : {
7054 4 : bool bContiguous = true;
7055 10 : for (size_t i = 0; i < nDims; i++)
7056 : {
7057 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7058 : {
7059 1 : bContiguous = false;
7060 1 : break;
7061 : }
7062 : }
7063 4 : if (bContiguous)
7064 : {
7065 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7066 3 : memset(pDstBuffer, 1, nElts);
7067 3 : return true;
7068 : }
7069 : }
7070 :
7071 : struct Stack
7072 : {
7073 : size_t nIters = 0;
7074 : GByte *dst_ptr = nullptr;
7075 : GPtrDiff_t dst_inc_offset = 0;
7076 : };
7077 :
7078 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7079 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7080 13 : for (size_t i = 0; i < nDims; i++)
7081 : {
7082 9 : stack[i].dst_inc_offset =
7083 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7084 : }
7085 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7086 :
7087 4 : size_t dimIdx = 0;
7088 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7089 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7090 4 : CPLAssert(nBufferDTSize <= 16);
7091 4 : const GByte flag = 1;
7092 4 : GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7093 : bufferDataType.GetNumericDataType(), 0, 1);
7094 :
7095 28 : lbl_next_depth:
7096 28 : if (dimIdx == nDimsMinus1)
7097 : {
7098 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7099 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7100 :
7101 : while (true)
7102 : {
7103 : // cppcheck-suppress knownConditionTrueFalse
7104 73 : if (bBufferDataTypeIsByte)
7105 : {
7106 24 : *dst_ptr = flag;
7107 : }
7108 : else
7109 : {
7110 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7111 : }
7112 :
7113 73 : if ((--nIters) == 0)
7114 19 : break;
7115 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7116 : }
7117 : }
7118 : else
7119 : {
7120 9 : stack[dimIdx].nIters = count[dimIdx];
7121 : while (true)
7122 : {
7123 24 : dimIdx++;
7124 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7125 24 : goto lbl_next_depth;
7126 24 : lbl_return_to_caller:
7127 24 : dimIdx--;
7128 24 : if ((--stack[dimIdx].nIters) == 0)
7129 9 : break;
7130 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7131 : }
7132 : }
7133 28 : if (dimIdx > 0)
7134 24 : goto lbl_return_to_caller;
7135 :
7136 4 : return true;
7137 : }
7138 :
7139 : const auto oTmpBufferDT =
7140 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7141 : ? GDALExtendedDataType::Create(GDT_Float64)
7142 88 : : m_poParent->GetDataType();
7143 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7144 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7145 44 : if (!pTempBuffer)
7146 0 : return false;
7147 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7148 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7149 : pTempBuffer))
7150 : {
7151 0 : VSIFree(pTempBuffer);
7152 0 : return false;
7153 : }
7154 :
7155 44 : switch (oTmpBufferDT.GetNumericDataType())
7156 : {
7157 7 : case GDT_Byte:
7158 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7159 : pTempBuffer, oTmpBufferDT,
7160 : tmpBufferStrideVector);
7161 7 : break;
7162 :
7163 0 : case GDT_Int8:
7164 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7165 : pTempBuffer, oTmpBufferDT,
7166 : tmpBufferStrideVector);
7167 0 : break;
7168 :
7169 1 : case GDT_UInt16:
7170 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7171 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7172 : tmpBufferStrideVector);
7173 1 : break;
7174 :
7175 14 : case GDT_Int16:
7176 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7177 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7178 : tmpBufferStrideVector);
7179 14 : break;
7180 :
7181 1 : case GDT_UInt32:
7182 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7183 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7184 : tmpBufferStrideVector);
7185 1 : break;
7186 :
7187 5 : case GDT_Int32:
7188 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7189 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7190 : tmpBufferStrideVector);
7191 5 : break;
7192 :
7193 0 : case GDT_UInt64:
7194 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7195 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7196 : tmpBufferStrideVector);
7197 0 : break;
7198 :
7199 0 : case GDT_Int64:
7200 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7201 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7202 : tmpBufferStrideVector);
7203 0 : break;
7204 :
7205 0 : case GDT_Float16:
7206 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7207 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7208 : tmpBufferStrideVector);
7209 0 : break;
7210 :
7211 7 : case GDT_Float32:
7212 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7213 : pTempBuffer, oTmpBufferDT,
7214 : tmpBufferStrideVector);
7215 7 : break;
7216 :
7217 9 : case GDT_Float64:
7218 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7219 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7220 : tmpBufferStrideVector);
7221 9 : break;
7222 0 : case GDT_Unknown:
7223 : case GDT_CInt16:
7224 : case GDT_CInt32:
7225 : case GDT_CFloat16:
7226 : case GDT_CFloat32:
7227 : case GDT_CFloat64:
7228 : case GDT_TypeCount:
7229 0 : CPLAssert(false);
7230 : break;
7231 : }
7232 :
7233 44 : VSIFree(pTempBuffer);
7234 :
7235 44 : return true;
7236 : }
7237 :
7238 : /************************************************************************/
7239 : /* IsValidForDT() */
7240 : /************************************************************************/
7241 :
7242 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7243 : {
7244 40 : if (std::isnan(dfVal))
7245 0 : return false;
7246 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7247 0 : return false;
7248 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7249 0 : return false;
7250 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7251 : }
7252 :
7253 9 : template <> bool IsValidForDT<double>(double)
7254 : {
7255 9 : return true;
7256 : }
7257 :
7258 : /************************************************************************/
7259 : /* IsNan() */
7260 : /************************************************************************/
7261 :
7262 1438 : template <typename Type> inline bool IsNan(Type)
7263 : {
7264 1438 : return false;
7265 : }
7266 :
7267 65 : template <> bool IsNan<double>(double val)
7268 : {
7269 65 : return std::isnan(val);
7270 : }
7271 :
7272 26 : template <> bool IsNan<float>(float val)
7273 : {
7274 26 : return std::isnan(val);
7275 : }
7276 :
7277 : /************************************************************************/
7278 : /* ReadInternal() */
7279 : /************************************************************************/
7280 :
7281 : template <typename Type>
7282 44 : void GDALMDArrayMask::ReadInternal(
7283 : const size_t *count, const GPtrDiff_t *bufferStride,
7284 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7285 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7286 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7287 : {
7288 44 : const size_t nDims = GetDimensionCount();
7289 :
7290 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7291 : {
7292 220 : if (bHasVal)
7293 : {
7294 49 : if (IsValidForDT<Type>(dfVal))
7295 : {
7296 49 : return static_cast<Type>(dfVal);
7297 : }
7298 : else
7299 : {
7300 0 : bHasVal = false;
7301 : }
7302 : }
7303 171 : return 0;
7304 : };
7305 :
7306 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7307 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7308 : const Type nNoDataValue =
7309 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7310 44 : bool bHasMissingValue = m_bHasMissingValue;
7311 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7312 44 : bool bHasFillValue = m_bHasFillValue;
7313 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7314 44 : bool bHasValidMin = m_bHasValidMin;
7315 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7316 44 : bool bHasValidMax = m_bHasValidMax;
7317 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7318 44 : const bool bHasValidFlags =
7319 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7320 :
7321 351 : const auto IsValidFlag = [this](Type v)
7322 : {
7323 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7324 : {
7325 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7326 : {
7327 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7328 : m_anValidFlagValues[i])
7329 : {
7330 4 : return true;
7331 : }
7332 : }
7333 : }
7334 42 : else if (!m_anValidFlagValues.empty())
7335 : {
7336 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7337 : {
7338 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7339 : {
7340 4 : return true;
7341 : }
7342 : }
7343 : }
7344 : else /* if( !m_anValidFlagMasks.empty() ) */
7345 : {
7346 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7347 : {
7348 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7349 : {
7350 9 : return true;
7351 : }
7352 : }
7353 : }
7354 37 : return false;
7355 : };
7356 :
7357 : #define GET_MASK_FOR_SAMPLE(v) \
7358 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7359 : !(bHasMissingValue && v == nMissingValue) && \
7360 : !(bHasFillValue && v == nFillValue) && \
7361 : !(bHasValidMin && v < nValidMin) && \
7362 : !(bHasValidMax && v > nValidMax) && \
7363 : (!bHasValidFlags || IsValidFlag(v)));
7364 :
7365 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7366 : /* Optimized case: Byte output and output buffer is contiguous */
7367 44 : if (bBufferDataTypeIsByte)
7368 : {
7369 40 : bool bContiguous = true;
7370 103 : for (size_t i = 0; i < nDims; i++)
7371 : {
7372 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7373 : {
7374 1 : bContiguous = false;
7375 1 : break;
7376 : }
7377 : }
7378 40 : if (bContiguous)
7379 : {
7380 39 : size_t nElts = 1;
7381 102 : for (size_t i = 0; i < nDims; i++)
7382 63 : nElts *= count[i];
7383 :
7384 1113 : for (size_t i = 0; i < nElts; i++)
7385 : {
7386 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7387 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7388 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7389 : }
7390 39 : return;
7391 : }
7392 : }
7393 :
7394 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7395 :
7396 : struct Stack
7397 : {
7398 : size_t nIters = 0;
7399 : const GByte *src_ptr = nullptr;
7400 : GByte *dst_ptr = nullptr;
7401 : GPtrDiff_t src_inc_offset = 0;
7402 : GPtrDiff_t dst_inc_offset = 0;
7403 : };
7404 :
7405 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7406 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7407 15 : for (size_t i = 0; i < nDims; i++)
7408 : {
7409 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7410 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7411 10 : stack[i].dst_inc_offset =
7412 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7413 : }
7414 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7415 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7416 :
7417 5 : size_t dimIdx = 0;
7418 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7419 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7420 5 : CPLAssert(nBufferDTSize <= 16);
7421 15 : for (GByte flag = 0; flag <= 1; flag++)
7422 : {
7423 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7424 : bufferDataType.GetNumericDataType(), 0, 1);
7425 : }
7426 :
7427 43 : lbl_next_depth:
7428 43 : if (dimIdx == nDimsMinus1)
7429 : {
7430 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7431 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7432 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7433 :
7434 420 : while (true)
7435 : {
7436 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7437 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7438 :
7439 455 : if (bBufferDataTypeIsByte)
7440 : {
7441 24 : *dst_ptr = flag;
7442 : }
7443 : else
7444 : {
7445 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7446 : }
7447 :
7448 455 : if ((--nIters) == 0)
7449 35 : break;
7450 420 : src_ptr += stack[dimIdx].src_inc_offset;
7451 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7452 : }
7453 : }
7454 : else
7455 : {
7456 8 : stack[dimIdx].nIters = count[dimIdx];
7457 : while (true)
7458 : {
7459 38 : dimIdx++;
7460 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7461 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7462 38 : goto lbl_next_depth;
7463 38 : lbl_return_to_caller:
7464 38 : dimIdx--;
7465 38 : if ((--stack[dimIdx].nIters) == 0)
7466 8 : break;
7467 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7468 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7469 : }
7470 : }
7471 43 : if (dimIdx > 0)
7472 38 : goto lbl_return_to_caller;
7473 : }
7474 :
7475 : /************************************************************************/
7476 : /* GetMask() */
7477 : /************************************************************************/
7478 :
7479 : /** Return an array that is a mask for the current array
7480 :
7481 : This array will be of type Byte, with values set to 0 to indicate invalid
7482 : pixels of the current array, and values set to 1 to indicate valid pixels.
7483 :
7484 : The generic implementation honours the NoDataValue, as well as various
7485 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7486 : and valid_range.
7487 :
7488 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7489 : can be used to specify strings of the "flag_meanings" attribute
7490 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7491 : for which pixels matching any of those flags will be set at 1 in the mask array,
7492 : and pixels matching none of those flags will be set at 0.
7493 : For example, let's consider the following netCDF variable defined with:
7494 : \verbatim
7495 : l2p_flags:valid_min = 0s ;
7496 : l2p_flags:valid_max = 256s ;
7497 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7498 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7499 : \endverbatim
7500 :
7501 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7502 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7503 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7504 : will be 1.
7505 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7506 : will be 0.
7507 :
7508 : This is the same as the C function GDALMDArrayGetMask().
7509 :
7510 : @param papszOptions NULL-terminated list of options, or NULL.
7511 :
7512 : @return a new array, that holds a reference to the original one, and thus is
7513 : a view of it (not a copy), or nullptr in case of error.
7514 : */
7515 : std::shared_ptr<GDALMDArray>
7516 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7517 : {
7518 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7519 49 : if (!self)
7520 : {
7521 0 : CPLError(CE_Failure, CPLE_AppDefined,
7522 : "Driver implementation issue: m_pSelf not set !");
7523 0 : return nullptr;
7524 : }
7525 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7526 : {
7527 1 : CPLError(CE_Failure, CPLE_AppDefined,
7528 : "GetMask() only supports numeric data type");
7529 1 : return nullptr;
7530 : }
7531 48 : return GDALMDArrayMask::Create(self, papszOptions);
7532 : }
7533 :
7534 : /************************************************************************/
7535 : /* IsRegularlySpaced() */
7536 : /************************************************************************/
7537 :
7538 : /** Returns whether an array is a 1D regularly spaced array.
7539 : *
7540 : * @param[out] dfStart First value in the array
7541 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7542 : * @return true if the array is regularly spaced.
7543 : */
7544 283 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7545 : {
7546 283 : dfStart = 0;
7547 283 : dfIncrement = 0;
7548 283 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7549 0 : return false;
7550 283 : const auto nSize = GetDimensions()[0]->GetSize();
7551 283 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7552 2 : return false;
7553 :
7554 281 : size_t nCount = static_cast<size_t>(nSize);
7555 562 : std::vector<double> adfTmp;
7556 : try
7557 : {
7558 281 : adfTmp.resize(nCount);
7559 : }
7560 0 : catch (const std::exception &)
7561 : {
7562 0 : return false;
7563 : }
7564 :
7565 281 : GUInt64 anStart[1] = {0};
7566 281 : size_t anCount[1] = {nCount};
7567 :
7568 : const auto IsRegularlySpacedInternal =
7569 88774 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7570 : {
7571 377 : dfStart = adfTmp[0];
7572 377 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7573 377 : if (dfIncrement == 0)
7574 : {
7575 3 : return false;
7576 : }
7577 22097 : for (size_t i = 1; i < anCount[0]; i++)
7578 : {
7579 21723 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7580 21723 : 1e-3 * fabs(dfIncrement))
7581 : {
7582 0 : return false;
7583 : }
7584 : }
7585 374 : return true;
7586 281 : };
7587 :
7588 : // First try with the first block(s). This can avoid excessive processing
7589 : // time, for example with Zarr datasets.
7590 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7591 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7592 281 : const auto nBlockSize = GetBlockSize()[0];
7593 281 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7594 : {
7595 : size_t nReducedCount =
7596 99 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7597 508 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7598 409 : nReducedCount *= 2;
7599 99 : anCount[0] = nReducedCount;
7600 99 : if (!Read(anStart, anCount, nullptr, nullptr,
7601 198 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7602 : {
7603 0 : return false;
7604 : }
7605 99 : if (!IsRegularlySpacedInternal())
7606 : {
7607 3 : return false;
7608 : }
7609 :
7610 : // Get next values
7611 96 : anStart[0] = nReducedCount;
7612 96 : anCount[0] = nCount - nReducedCount;
7613 : }
7614 :
7615 278 : if (!Read(anStart, anCount, nullptr, nullptr,
7616 556 : GDALExtendedDataType::Create(GDT_Float64),
7617 278 : &adfTmp[static_cast<size_t>(anStart[0])]))
7618 : {
7619 0 : return false;
7620 : }
7621 :
7622 278 : return IsRegularlySpacedInternal();
7623 : }
7624 :
7625 : /************************************************************************/
7626 : /* GuessGeoTransform() */
7627 : /************************************************************************/
7628 :
7629 : /** Returns whether 2 specified dimensions form a geotransform
7630 : *
7631 : * @param nDimX Index of the X axis.
7632 : * @param nDimY Index of the Y axis.
7633 : * @param bPixelIsPoint Whether the geotransform should be returned
7634 : * with the pixel-is-point (pixel-center) convention
7635 : * (bPixelIsPoint = true), or with the pixel-is-area
7636 : * (top left corner convention)
7637 : * (bPixelIsPoint = false)
7638 : * @param[out] gt Computed geotransform
7639 : * @return true if a geotransform could be computed.
7640 : */
7641 239 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7642 : bool bPixelIsPoint,
7643 : GDALGeoTransform >) const
7644 : {
7645 239 : const auto &dims(GetDimensions());
7646 478 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7647 478 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7648 239 : double dfXStart = 0.0;
7649 239 : double dfXSpacing = 0.0;
7650 239 : double dfYStart = 0.0;
7651 239 : double dfYSpacing = 0.0;
7652 545 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7653 306 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7654 437 : poVarY && poVarY->GetDimensionCount() == 1 &&
7655 142 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7656 529 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7657 137 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7658 : {
7659 137 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7660 137 : gt[1] = dfXSpacing;
7661 137 : gt[2] = 0;
7662 137 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7663 137 : gt[4] = 0;
7664 137 : gt[5] = dfYSpacing;
7665 137 : return true;
7666 : }
7667 102 : return false;
7668 : }
7669 :
7670 : /** Returns whether 2 specified dimensions form a geotransform
7671 : *
7672 : * @param nDimX Index of the X axis.
7673 : * @param nDimY Index of the Y axis.
7674 : * @param bPixelIsPoint Whether the geotransform should be returned
7675 : * with the pixel-is-point (pixel-center) convention
7676 : * (bPixelIsPoint = true), or with the pixel-is-area
7677 : * (top left corner convention)
7678 : * (bPixelIsPoint = false)
7679 : * @param[out] adfGeoTransform Computed geotransform
7680 : * @return true if a geotransform could be computed.
7681 : */
7682 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7683 : bool bPixelIsPoint,
7684 : double adfGeoTransform[6]) const
7685 : {
7686 0 : GDALGeoTransform *gt =
7687 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7688 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7689 : }
7690 :
7691 : /************************************************************************/
7692 : /* GDALMDArrayResampled */
7693 : /************************************************************************/
7694 :
7695 : class GDALMDArrayResampledDataset;
7696 :
7697 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7698 : {
7699 : protected:
7700 : CPLErr IReadBlock(int, int, void *) override;
7701 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7702 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7703 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7704 : GSpacing nLineSpaceBuf,
7705 : GDALRasterIOExtraArg *psExtraArg) override;
7706 :
7707 : public:
7708 : explicit GDALMDArrayResampledDatasetRasterBand(
7709 : GDALMDArrayResampledDataset *poDSIn);
7710 :
7711 : double GetNoDataValue(int *pbHasNoData) override;
7712 : };
7713 :
7714 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7715 : {
7716 : friend class GDALMDArrayResampled;
7717 : friend class GDALMDArrayResampledDatasetRasterBand;
7718 :
7719 : std::shared_ptr<GDALMDArray> m_poArray;
7720 : const size_t m_iXDim;
7721 : const size_t m_iYDim;
7722 : GDALGeoTransform m_gt{};
7723 : bool m_bHasGT = false;
7724 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7725 :
7726 : std::vector<GUInt64> m_anOffset{};
7727 : std::vector<size_t> m_anCount{};
7728 : std::vector<GPtrDiff_t> m_anStride{};
7729 :
7730 : std::string m_osFilenameLong{};
7731 : std::string m_osFilenameLat{};
7732 :
7733 : public:
7734 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7735 : size_t iXDim, size_t iYDim)
7736 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7737 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7738 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7739 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7740 : {
7741 24 : const auto &dims(m_poArray->GetDimensions());
7742 :
7743 24 : nRasterYSize = static_cast<int>(
7744 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7745 24 : nRasterXSize = static_cast<int>(
7746 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7747 :
7748 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7749 :
7750 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7751 24 : }
7752 :
7753 : ~GDALMDArrayResampledDataset() override;
7754 :
7755 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7756 : {
7757 43 : gt = m_gt;
7758 43 : return m_bHasGT ? CE_None : CE_Failure;
7759 : }
7760 :
7761 105 : const OGRSpatialReference *GetSpatialRef() const override
7762 : {
7763 105 : m_poSRS = m_poArray->GetSpatialRef();
7764 105 : if (m_poSRS)
7765 : {
7766 79 : m_poSRS.reset(m_poSRS->Clone());
7767 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7768 237 : for (auto &m : axisMapping)
7769 : {
7770 158 : if (m == static_cast<int>(m_iXDim) + 1)
7771 79 : m = 1;
7772 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7773 79 : m = 2;
7774 : }
7775 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7776 : }
7777 105 : return m_poSRS.get();
7778 : }
7779 :
7780 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7781 : const std::string &osFilenameLat)
7782 : {
7783 5 : m_osFilenameLong = osFilenameLong;
7784 5 : m_osFilenameLat = osFilenameLat;
7785 10 : CPLStringList aosGeoLoc;
7786 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7787 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7788 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7789 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7790 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7791 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7792 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7793 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7794 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7795 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7796 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7797 5 : }
7798 : };
7799 :
7800 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7801 : {
7802 24 : if (!m_osFilenameLong.empty())
7803 5 : VSIUnlink(m_osFilenameLong.c_str());
7804 24 : if (!m_osFilenameLat.empty())
7805 5 : VSIUnlink(m_osFilenameLat.c_str());
7806 48 : }
7807 :
7808 : /************************************************************************/
7809 : /* GDALMDArrayResampledDatasetRasterBand() */
7810 : /************************************************************************/
7811 :
7812 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7813 24 : GDALMDArrayResampledDataset *poDSIn)
7814 : {
7815 24 : const auto &poArray(poDSIn->m_poArray);
7816 24 : const auto blockSize(poArray->GetBlockSize());
7817 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7818 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7819 13 : blockSize[poDSIn->m_iYDim]))
7820 24 : : 1;
7821 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7822 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7823 13 : blockSize[poDSIn->m_iXDim]))
7824 24 : : poDSIn->GetRasterXSize();
7825 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7826 24 : eAccess = poDSIn->eAccess;
7827 24 : }
7828 :
7829 : /************************************************************************/
7830 : /* GetNoDataValue() */
7831 : /************************************************************************/
7832 :
7833 54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7834 : {
7835 54 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7836 54 : const auto &poArray(l_poDS->m_poArray);
7837 54 : bool bHasNodata = false;
7838 54 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7839 54 : if (pbHasNoData)
7840 48 : *pbHasNoData = bHasNodata;
7841 54 : return dfRes;
7842 : }
7843 :
7844 : /************************************************************************/
7845 : /* IReadBlock() */
7846 : /************************************************************************/
7847 :
7848 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7849 : int nBlockYOff,
7850 : void *pImage)
7851 : {
7852 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7853 0 : const int nXOff = nBlockXOff * nBlockXSize;
7854 0 : const int nYOff = nBlockYOff * nBlockYSize;
7855 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7856 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7857 : GDALRasterIOExtraArg sExtraArg;
7858 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7859 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7860 : nReqXSize, nReqYSize, eDataType, nDTSize,
7861 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7862 : }
7863 :
7864 : /************************************************************************/
7865 : /* IRasterIO() */
7866 : /************************************************************************/
7867 :
7868 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7869 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7870 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7871 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7872 : GDALRasterIOExtraArg *psExtraArg)
7873 : {
7874 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7875 32 : const auto &poArray(l_poDS->m_poArray);
7876 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7877 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7878 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7879 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7880 : {
7881 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7882 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7883 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7884 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7885 :
7886 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7887 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7888 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7889 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7890 :
7891 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7892 32 : l_poDS->m_anCount.data(), nullptr,
7893 32 : l_poDS->m_anStride.data(),
7894 64 : GDALExtendedDataType::Create(eBufType), pData)
7895 32 : ? CE_None
7896 32 : : CE_Failure;
7897 : }
7898 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7899 : pData, nBufXSize, nBufYSize, eBufType,
7900 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7901 : }
7902 :
7903 : class GDALMDArrayResampled final : public GDALPamMDArray
7904 : {
7905 : private:
7906 : std::shared_ptr<GDALMDArray> m_poParent{};
7907 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7908 : std::vector<GUInt64> m_anBlockSize;
7909 : GDALExtendedDataType m_dt;
7910 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7911 : std::shared_ptr<GDALMDArray> m_poVarX{};
7912 : std::shared_ptr<GDALMDArray> m_poVarY{};
7913 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7914 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7915 :
7916 : protected:
7917 21 : GDALMDArrayResampled(
7918 : const std::shared_ptr<GDALMDArray> &poParent,
7919 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7920 : const std::vector<GUInt64> &anBlockSize)
7921 42 : : GDALAbstractMDArray(std::string(),
7922 42 : "Resampled view of " + poParent->GetFullName()),
7923 : GDALPamMDArray(
7924 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7925 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7926 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7927 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7928 : {
7929 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7930 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7931 21 : }
7932 :
7933 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7934 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7935 : const GDALExtendedDataType &bufferDataType,
7936 : void *pDstBuffer) const override;
7937 :
7938 : public:
7939 : static std::shared_ptr<GDALMDArray>
7940 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7941 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7942 : GDALRIOResampleAlg resampleAlg,
7943 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7944 :
7945 42 : ~GDALMDArrayResampled() override
7946 21 : {
7947 : // First close the warped VRT
7948 21 : m_poReprojectedDS.reset();
7949 21 : m_poParentDS.reset();
7950 42 : }
7951 :
7952 11 : bool IsWritable() const override
7953 : {
7954 11 : return false;
7955 : }
7956 :
7957 74 : const std::string &GetFilename() const override
7958 : {
7959 74 : return m_poParent->GetFilename();
7960 : }
7961 :
7962 : const std::vector<std::shared_ptr<GDALDimension>> &
7963 257 : GetDimensions() const override
7964 : {
7965 257 : return m_apoDims;
7966 : }
7967 :
7968 109 : const GDALExtendedDataType &GetDataType() const override
7969 : {
7970 109 : return m_dt;
7971 : }
7972 :
7973 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7974 : {
7975 21 : return m_poSRS;
7976 : }
7977 :
7978 12 : std::vector<GUInt64> GetBlockSize() const override
7979 : {
7980 12 : return m_anBlockSize;
7981 : }
7982 :
7983 : std::shared_ptr<GDALAttribute>
7984 1 : GetAttribute(const std::string &osName) const override
7985 : {
7986 1 : return m_poParent->GetAttribute(osName);
7987 : }
7988 :
7989 : std::vector<std::shared_ptr<GDALAttribute>>
7990 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7991 : {
7992 12 : return m_poParent->GetAttributes(papszOptions);
7993 : }
7994 :
7995 1 : const std::string &GetUnit() const override
7996 : {
7997 1 : return m_poParent->GetUnit();
7998 : }
7999 :
8000 1 : const void *GetRawNoDataValue() const override
8001 : {
8002 1 : return m_poParent->GetRawNoDataValue();
8003 : }
8004 :
8005 1 : double GetOffset(bool *pbHasOffset,
8006 : GDALDataType *peStorageType) const override
8007 : {
8008 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
8009 : }
8010 :
8011 1 : double GetScale(bool *pbHasScale,
8012 : GDALDataType *peStorageType) const override
8013 : {
8014 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
8015 : }
8016 : };
8017 :
8018 : /************************************************************************/
8019 : /* GDALMDArrayResampled::Create() */
8020 : /************************************************************************/
8021 :
8022 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8023 : const std::shared_ptr<GDALMDArray> &poParent,
8024 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8025 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8026 : CSLConstList /* papszOptions */)
8027 : {
8028 29 : const char *pszResampleAlg = "nearest";
8029 29 : bool unsupported = false;
8030 29 : switch (resampleAlg)
8031 : {
8032 16 : case GRIORA_NearestNeighbour:
8033 16 : pszResampleAlg = "nearest";
8034 16 : break;
8035 2 : case GRIORA_Bilinear:
8036 2 : pszResampleAlg = "bilinear";
8037 2 : break;
8038 5 : case GRIORA_Cubic:
8039 5 : pszResampleAlg = "cubic";
8040 5 : break;
8041 1 : case GRIORA_CubicSpline:
8042 1 : pszResampleAlg = "cubicspline";
8043 1 : break;
8044 1 : case GRIORA_Lanczos:
8045 1 : pszResampleAlg = "lanczos";
8046 1 : break;
8047 1 : case GRIORA_Average:
8048 1 : pszResampleAlg = "average";
8049 1 : break;
8050 1 : case GRIORA_Mode:
8051 1 : pszResampleAlg = "mode";
8052 1 : break;
8053 1 : case GRIORA_Gauss:
8054 1 : unsupported = true;
8055 1 : break;
8056 0 : case GRIORA_RESERVED_START:
8057 0 : unsupported = true;
8058 0 : break;
8059 0 : case GRIORA_RESERVED_END:
8060 0 : unsupported = true;
8061 0 : break;
8062 1 : case GRIORA_RMS:
8063 1 : pszResampleAlg = "rms";
8064 1 : break;
8065 : }
8066 29 : if (unsupported)
8067 : {
8068 1 : CPLError(CE_Failure, CPLE_NotSupported,
8069 : "Unsupported resample method for GetResampled()");
8070 1 : return nullptr;
8071 : }
8072 :
8073 28 : if (poParent->GetDimensionCount() < 2)
8074 : {
8075 1 : CPLError(CE_Failure, CPLE_NotSupported,
8076 : "GetResampled() only supports 2 dimensions or more");
8077 1 : return nullptr;
8078 : }
8079 :
8080 27 : const auto &aoParentDims = poParent->GetDimensions();
8081 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8082 : {
8083 2 : CPLError(CE_Failure, CPLE_AppDefined,
8084 : "GetResampled(): apoNewDims size should be the same as "
8085 : "GetDimensionCount()");
8086 2 : return nullptr;
8087 : }
8088 :
8089 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8090 25 : apoNewDims.reserve(apoNewDimsIn.size());
8091 :
8092 50 : std::vector<GUInt64> anBlockSize;
8093 25 : anBlockSize.reserve(apoNewDimsIn.size());
8094 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8095 :
8096 50 : auto apoParentDims = poParent->GetDimensions();
8097 : // Special case for NASA EMIT datasets
8098 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8099 7 : apoParentDims[0]->GetName() == "downtrack" &&
8100 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8101 2 : apoParentDims[2]->GetName() == "bands");
8102 :
8103 : const size_t iYDimParent =
8104 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8105 : const size_t iXDimParent =
8106 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8107 :
8108 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8109 : {
8110 53 : if (i == iYDimParent || i == iXDimParent)
8111 48 : continue;
8112 5 : if (apoNewDimsIn[i] == nullptr)
8113 : {
8114 3 : apoNewDims.emplace_back(aoParentDims[i]);
8115 : }
8116 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8117 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8118 : {
8119 1 : CPLError(CE_Failure, CPLE_AppDefined,
8120 : "GetResampled(): apoNewDims[%u] should be the same "
8121 : "as its parent",
8122 : i);
8123 1 : return nullptr;
8124 : }
8125 : else
8126 : {
8127 1 : apoNewDims.emplace_back(aoParentDims[i]);
8128 : }
8129 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8130 : }
8131 :
8132 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8133 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8134 :
8135 24 : double dfXStart = 0.0;
8136 24 : double dfXSpacing = 0.0;
8137 24 : bool gotXSpacing = false;
8138 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8139 24 : if (poNewDimX)
8140 : {
8141 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8142 : {
8143 0 : CPLError(CE_Failure, CPLE_NotSupported,
8144 : "Too big size for X dimension");
8145 0 : return nullptr;
8146 : }
8147 4 : auto var = poNewDimX->GetIndexingVariable();
8148 4 : if (var)
8149 : {
8150 2 : if (var->GetDimensionCount() != 1 ||
8151 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8152 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8153 : {
8154 0 : CPLError(CE_Failure, CPLE_NotSupported,
8155 : "New X dimension should be indexed by a regularly "
8156 : "spaced variable");
8157 0 : return nullptr;
8158 : }
8159 1 : gotXSpacing = true;
8160 : }
8161 : }
8162 :
8163 24 : double dfYStart = 0.0;
8164 24 : double dfYSpacing = 0.0;
8165 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8166 24 : bool gotYSpacing = false;
8167 24 : if (poNewDimY)
8168 : {
8169 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8170 : {
8171 0 : CPLError(CE_Failure, CPLE_NotSupported,
8172 : "Too big size for Y dimension");
8173 0 : return nullptr;
8174 : }
8175 4 : auto var = poNewDimY->GetIndexingVariable();
8176 4 : if (var)
8177 : {
8178 2 : if (var->GetDimensionCount() != 1 ||
8179 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8180 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8181 : {
8182 0 : CPLError(CE_Failure, CPLE_NotSupported,
8183 : "New Y dimension should be indexed by a regularly "
8184 : "spaced variable");
8185 0 : return nullptr;
8186 : }
8187 1 : gotYSpacing = true;
8188 : }
8189 : }
8190 :
8191 : // This limitation could probably be removed
8192 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8193 : {
8194 0 : CPLError(CE_Failure, CPLE_NotSupported,
8195 : "Either none of new X or Y dimension should have an indexing "
8196 : "variable, or both should both should have one.");
8197 0 : return nullptr;
8198 : }
8199 :
8200 48 : std::string osDstWKT;
8201 24 : if (poTargetSRS)
8202 : {
8203 2 : char *pszDstWKT = nullptr;
8204 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8205 : {
8206 0 : CPLFree(pszDstWKT);
8207 0 : return nullptr;
8208 : }
8209 2 : osDstWKT = pszDstWKT;
8210 2 : CPLFree(pszDstWKT);
8211 : }
8212 :
8213 : // Use coordinate variables for geolocation array
8214 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8215 24 : bool useGeolocationArray = false;
8216 24 : if (apoCoordinateVars.size() >= 2)
8217 : {
8218 0 : std::shared_ptr<GDALMDArray> poLongVar;
8219 0 : std::shared_ptr<GDALMDArray> poLatVar;
8220 15 : for (const auto &poCoordVar : apoCoordinateVars)
8221 : {
8222 10 : const auto &osName = poCoordVar->GetName();
8223 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8224 20 : std::string osStandardName;
8225 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8226 2 : poAttr->GetDimensionCount() == 0)
8227 : {
8228 2 : const char *pszStandardName = poAttr->ReadAsString();
8229 2 : if (pszStandardName)
8230 2 : osStandardName = pszStandardName;
8231 : }
8232 21 : if (osName == "lon" || osName == "longitude" ||
8233 21 : osName == "Longitude" || osStandardName == "longitude")
8234 : {
8235 5 : poLongVar = poCoordVar;
8236 : }
8237 6 : else if (osName == "lat" || osName == "latitude" ||
8238 6 : osName == "Latitude" || osStandardName == "latitude")
8239 : {
8240 5 : poLatVar = poCoordVar;
8241 : }
8242 : }
8243 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8244 : {
8245 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8246 5 : const auto &longDims = poLongVar->GetDimensions();
8247 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8248 5 : const auto &latDims = poLatVar->GetDimensions();
8249 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8250 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8251 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8252 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8253 : {
8254 : // Geolocation arrays are 1D, and of consistent size with
8255 : // the variable
8256 0 : useGeolocationArray = true;
8257 : }
8258 1 : else if ((longDimCount == 2 ||
8259 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8260 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8261 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8262 1 : (latDimCount == 2 ||
8263 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8264 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8265 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8266 :
8267 : {
8268 : // Geolocation arrays are 2D (or 3D with first dimension of
8269 : // size 1, as found in Sentinel 5P products), and of consistent
8270 : // size with the variable
8271 5 : useGeolocationArray = true;
8272 : }
8273 : else
8274 : {
8275 0 : CPLDebug(
8276 : "GDAL",
8277 : "Longitude and latitude coordinate variables found, "
8278 : "but their characteristics are not compatible of using "
8279 : "them as geolocation arrays");
8280 : }
8281 5 : if (useGeolocationArray)
8282 : {
8283 10 : CPLDebug("GDAL",
8284 : "Setting geolocation array from variables %s and %s",
8285 5 : poLongVar->GetName().c_str(),
8286 5 : poLatVar->GetName().c_str());
8287 : const std::string osFilenameLong =
8288 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8289 : const std::string osFilenameLat =
8290 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8291 : std::unique_ptr<GDALDataset> poTmpLongDS(
8292 : longDimCount == 1
8293 0 : ? poLongVar->AsClassicDataset(0, 0)
8294 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8295 15 : longDimCount - 2));
8296 5 : auto hTIFFLongDS = GDALTranslate(
8297 : osFilenameLong.c_str(),
8298 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8299 : std::unique_ptr<GDALDataset> poTmpLatDS(
8300 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8301 20 : : poLatVar->AsClassicDataset(
8302 15 : latDimCount - 1, latDimCount - 2));
8303 5 : auto hTIFFLatDS = GDALTranslate(
8304 : osFilenameLat.c_str(),
8305 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8306 5 : const bool bError =
8307 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8308 5 : GDALClose(hTIFFLongDS);
8309 5 : GDALClose(hTIFFLatDS);
8310 5 : if (bError)
8311 : {
8312 0 : VSIUnlink(osFilenameLong.c_str());
8313 0 : VSIUnlink(osFilenameLat.c_str());
8314 0 : return nullptr;
8315 : }
8316 :
8317 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8318 : }
8319 : }
8320 : else
8321 : {
8322 0 : CPLDebug("GDAL",
8323 : "Coordinate variables available for %s, but "
8324 : "longitude and/or latitude variables were not identified",
8325 0 : poParent->GetName().c_str());
8326 : }
8327 : }
8328 :
8329 : // Build gdalwarp arguments
8330 48 : CPLStringList aosArgv;
8331 :
8332 24 : aosArgv.AddString("-of");
8333 24 : aosArgv.AddString("VRT");
8334 :
8335 24 : aosArgv.AddString("-r");
8336 24 : aosArgv.AddString(pszResampleAlg);
8337 :
8338 24 : if (!osDstWKT.empty())
8339 : {
8340 2 : aosArgv.AddString("-t_srs");
8341 2 : aosArgv.AddString(osDstWKT.c_str());
8342 : }
8343 :
8344 24 : if (useGeolocationArray)
8345 5 : aosArgv.AddString("-geoloc");
8346 :
8347 24 : if (gotXSpacing && gotYSpacing)
8348 : {
8349 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8350 : const double dfXMax =
8351 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8352 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8353 : const double dfYMin =
8354 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8355 1 : aosArgv.AddString("-te");
8356 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8357 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8358 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8359 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8360 : }
8361 :
8362 24 : if (poNewDimX && poNewDimY)
8363 : {
8364 3 : aosArgv.AddString("-ts");
8365 : aosArgv.AddString(
8366 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8367 : aosArgv.AddString(
8368 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8369 : }
8370 21 : else if (poNewDimX)
8371 : {
8372 1 : aosArgv.AddString("-ts");
8373 : aosArgv.AddString(
8374 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8375 1 : aosArgv.AddString("0");
8376 : }
8377 20 : else if (poNewDimY)
8378 : {
8379 1 : aosArgv.AddString("-ts");
8380 1 : aosArgv.AddString("0");
8381 : aosArgv.AddString(
8382 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8383 : }
8384 :
8385 : // Create a warped VRT dataset
8386 : GDALWarpAppOptions *psOptions =
8387 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8388 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8389 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8390 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8391 24 : GDALWarpAppOptionsFree(psOptions);
8392 24 : if (poReprojectedDS == nullptr)
8393 3 : return nullptr;
8394 :
8395 : int nBlockXSize;
8396 : int nBlockYSize;
8397 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8398 21 : anBlockSize.emplace_back(nBlockYSize);
8399 21 : anBlockSize.emplace_back(nBlockXSize);
8400 :
8401 21 : GDALGeoTransform gt;
8402 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8403 21 : CPLAssert(eErr == CE_None);
8404 21 : CPL_IGNORE_RET_VAL(eErr);
8405 :
8406 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8407 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8408 42 : poReprojectedDS->GetRasterYSize());
8409 : auto varY = GDALMDArrayRegularlySpaced::Create(
8410 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8411 21 : poDimY->SetIndexingVariable(varY);
8412 :
8413 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8414 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8415 42 : poReprojectedDS->GetRasterXSize());
8416 : auto varX = GDALMDArrayRegularlySpaced::Create(
8417 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8418 21 : poDimX->SetIndexingVariable(varX);
8419 :
8420 21 : apoNewDims.emplace_back(poDimY);
8421 21 : apoNewDims.emplace_back(poDimX);
8422 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8423 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8424 21 : newAr->SetSelf(newAr);
8425 21 : if (poTargetSRS)
8426 : {
8427 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8428 : }
8429 : else
8430 : {
8431 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8432 : }
8433 21 : newAr->m_poVarX = varX;
8434 21 : newAr->m_poVarY = varY;
8435 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8436 21 : newAr->m_poParentDS = std::move(poParentDS);
8437 :
8438 : // If the input array is y,x,band ordered, the above newAr is
8439 : // actually band,y,x ordered as it is more convenient for
8440 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8441 : // array to the order of the input array
8442 21 : if (bYXBandOrder)
8443 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8444 :
8445 19 : return newAr;
8446 : }
8447 :
8448 : /************************************************************************/
8449 : /* GDALMDArrayResampled::IRead() */
8450 : /************************************************************************/
8451 :
8452 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8453 : const size_t *count, const GInt64 *arrayStep,
8454 : const GPtrDiff_t *bufferStride,
8455 : const GDALExtendedDataType &bufferDataType,
8456 : void *pDstBuffer) const
8457 : {
8458 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8459 0 : return false;
8460 :
8461 : struct Stack
8462 : {
8463 : size_t nIters = 0;
8464 : GByte *dst_ptr = nullptr;
8465 : GPtrDiff_t dst_inc_offset = 0;
8466 : };
8467 :
8468 29 : const auto nDims = GetDimensionCount();
8469 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8470 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8471 92 : for (size_t i = 0; i < nDims; i++)
8472 : {
8473 63 : stack[i].dst_inc_offset =
8474 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8475 : }
8476 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8477 :
8478 29 : size_t dimIdx = 0;
8479 29 : const size_t iDimY = nDims - 2;
8480 29 : const size_t iDimX = nDims - 1;
8481 : // Use an array to avoid a false positive warning from CLang Static
8482 : // Analyzer about flushCaches being never read
8483 29 : bool flushCaches[] = {false};
8484 : const bool bYXBandOrder =
8485 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8486 :
8487 38 : lbl_next_depth:
8488 38 : if (dimIdx == iDimY)
8489 : {
8490 33 : if (flushCaches[0])
8491 : {
8492 5 : flushCaches[0] = false;
8493 : // When changing of 2D slice, flush GDAL 2D buffers
8494 5 : m_poParentDS->FlushCache(false);
8495 5 : m_poReprojectedDS->FlushCache(false);
8496 : }
8497 :
8498 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8499 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8500 : arrayStep, bufferStride, bufferDataType,
8501 33 : stack[dimIdx].dst_ptr))
8502 : {
8503 0 : return false;
8504 : }
8505 : }
8506 : else
8507 : {
8508 5 : stack[dimIdx].nIters = count[dimIdx];
8509 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8510 5 : arrayStartIdx[dimIdx])
8511 : {
8512 1 : flushCaches[0] = true;
8513 : }
8514 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8515 5 : arrayStartIdx[dimIdx];
8516 : while (true)
8517 : {
8518 9 : dimIdx++;
8519 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8520 9 : goto lbl_next_depth;
8521 9 : lbl_return_to_caller:
8522 9 : dimIdx--;
8523 9 : if ((--stack[dimIdx].nIters) == 0)
8524 5 : break;
8525 4 : flushCaches[0] = true;
8526 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8527 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8528 : }
8529 : }
8530 38 : if (dimIdx > 0)
8531 9 : goto lbl_return_to_caller;
8532 :
8533 29 : return true;
8534 : }
8535 :
8536 : /************************************************************************/
8537 : /* GetResampled() */
8538 : /************************************************************************/
8539 :
8540 : /** Return an array that is a resampled / reprojected view of the current array
8541 : *
8542 : * This is the same as the C function GDALMDArrayGetResampled().
8543 : *
8544 : * Currently this method can only resample along the last 2 dimensions, unless
8545 : * orthorectifying a NASA EMIT dataset.
8546 : *
8547 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8548 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8549 : *
8550 : * Options available are:
8551 : * <ul>
8552 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8553 : * Can be set to NO to use generic reprojection method.
8554 : * </li>
8555 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8556 : * orthorectification to take into account the value of the
8557 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8558 : * current array along the band dimension are valid.</li>
8559 : * </ul>
8560 : *
8561 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8562 : * apoNewDims[i] can be NULL to let the method automatically
8563 : * determine it.
8564 : * @param resampleAlg Resampling algorithm
8565 : * @param poTargetSRS Target SRS, or nullptr
8566 : * @param papszOptions NULL-terminated list of options, or NULL.
8567 : *
8568 : * @return a new array, that holds a reference to the original one, and thus is
8569 : * a view of it (not a copy), or nullptr in case of error.
8570 : *
8571 : * @since 3.4
8572 : */
8573 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8574 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8575 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8576 : CSLConstList papszOptions) const
8577 : {
8578 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8579 38 : if (!self)
8580 : {
8581 0 : CPLError(CE_Failure, CPLE_AppDefined,
8582 : "Driver implementation issue: m_pSelf not set !");
8583 0 : return nullptr;
8584 : }
8585 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8586 : {
8587 0 : CPLError(CE_Failure, CPLE_AppDefined,
8588 : "GetResampled() only supports numeric data type");
8589 0 : return nullptr;
8590 : }
8591 :
8592 : // Special case for NASA EMIT datasets
8593 76 : auto apoDims = GetDimensions();
8594 36 : if (poTargetSRS == nullptr &&
8595 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8596 20 : apoDims[1]->GetName() == "crosstrack" &&
8597 10 : apoDims[2]->GetName() == "bands" &&
8598 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8599 1 : apoNewDims ==
8600 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8601 30 : apoDims[2]})) ||
8602 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8603 3 : apoDims[1]->GetName() == "crosstrack" &&
8604 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8605 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8606 : "EMIT_ORTHORECTIFICATION", "YES")))
8607 : {
8608 9 : auto poRootGroup = GetRootGroup();
8609 9 : if (poRootGroup)
8610 : {
8611 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8612 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8613 9 : if (poAttrGeotransform &&
8614 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8615 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8616 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8617 9 : poLocationGroup)
8618 : {
8619 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8620 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8621 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8622 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8623 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8624 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8625 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8626 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8627 : {
8628 : return CreateGLTOrthorectified(
8629 : self, poRootGroup, poGLT_X, poGLT_Y,
8630 : /* nGLTIndexOffset = */ -1,
8631 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8632 : }
8633 : }
8634 : }
8635 : }
8636 :
8637 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8638 : "EMIT_ORTHORECTIFICATION", "NO")))
8639 : {
8640 0 : CPLError(CE_Failure, CPLE_AppDefined,
8641 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8642 : "parameters are not compatible with it");
8643 0 : return nullptr;
8644 : }
8645 :
8646 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8647 29 : poTargetSRS, papszOptions);
8648 : }
8649 :
8650 : /************************************************************************/
8651 : /* GDALDatasetFromArray() */
8652 : /************************************************************************/
8653 :
8654 : class GDALDatasetFromArray;
8655 :
8656 : namespace
8657 : {
8658 : struct MetadataItem
8659 : {
8660 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8661 : std::string osName{};
8662 : std::string osDefinition{};
8663 : bool bDefinitionUsesPctForG = false;
8664 : };
8665 :
8666 : struct BandImageryMetadata
8667 : {
8668 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8669 : double dfCentralWavelengthToMicrometer = 1.0;
8670 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8671 : double dfFWHMToMicrometer = 1.0;
8672 : };
8673 :
8674 : } // namespace
8675 :
8676 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8677 : {
8678 : std::vector<GUInt64> m_anOffset{};
8679 : std::vector<size_t> m_anCount{};
8680 : std::vector<GPtrDiff_t> m_anStride{};
8681 :
8682 : protected:
8683 : CPLErr IReadBlock(int, int, void *) override;
8684 : CPLErr IWriteBlock(int, int, void *) override;
8685 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8686 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8687 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8688 : GSpacing nLineSpaceBuf,
8689 : GDALRasterIOExtraArg *psExtraArg) override;
8690 :
8691 : public:
8692 : explicit GDALRasterBandFromArray(
8693 : GDALDatasetFromArray *poDSIn,
8694 : const std::vector<GUInt64> &anOtherDimCoord,
8695 : const std::vector<std::vector<MetadataItem>>
8696 : &aoBandParameterMetadataItems,
8697 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8698 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8699 :
8700 : double GetNoDataValue(int *pbHasNoData) override;
8701 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8702 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8703 : double GetOffset(int *pbHasOffset) override;
8704 : double GetScale(int *pbHasScale) override;
8705 : const char *GetUnitType() override;
8706 : GDALColorInterp GetColorInterpretation() override;
8707 : };
8708 :
8709 : class GDALDatasetFromArray final : public GDALPamDataset
8710 : {
8711 : friend class GDALRasterBandFromArray;
8712 :
8713 : std::shared_ptr<GDALMDArray> m_poArray;
8714 : size_t m_iXDim;
8715 : size_t m_iYDim;
8716 : GDALGeoTransform m_gt{};
8717 : bool m_bHasGT = false;
8718 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8719 : GDALMultiDomainMetadata m_oMDD{};
8720 : std::string m_osOvrFilename{};
8721 :
8722 : public:
8723 215 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8724 : size_t iXDim, size_t iYDim)
8725 215 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8726 : {
8727 : // Initialize an overview filename from the filename of the array
8728 : // and its name.
8729 215 : const std::string &osFilename = m_poArray->GetFilename();
8730 215 : if (!osFilename.empty())
8731 : {
8732 188 : m_osOvrFilename = osFilename;
8733 188 : m_osOvrFilename += '.';
8734 6813 : for (char ch : m_poArray->GetName())
8735 : {
8736 6625 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8737 5882 : (ch >= 'a' && ch <= 'z') || ch == '_')
8738 : {
8739 5314 : m_osOvrFilename += ch;
8740 : }
8741 : else
8742 : {
8743 1311 : m_osOvrFilename += '_';
8744 : }
8745 : }
8746 188 : m_osOvrFilename += ".ovr";
8747 188 : oOvManager.Initialize(this);
8748 : }
8749 215 : }
8750 :
8751 : static GDALDatasetFromArray *
8752 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8753 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8754 : CSLConstList papszOptions);
8755 :
8756 : ~GDALDatasetFromArray() override;
8757 :
8758 356 : CPLErr Close() override
8759 : {
8760 356 : CPLErr eErr = CE_None;
8761 356 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8762 : {
8763 356 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8764 : CE_None)
8765 0 : eErr = CE_Failure;
8766 356 : m_poArray.reset();
8767 : }
8768 356 : return eErr;
8769 : }
8770 :
8771 73 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8772 : {
8773 73 : gt = m_gt;
8774 73 : return m_bHasGT ? CE_None : CE_Failure;
8775 : }
8776 :
8777 68 : const OGRSpatialReference *GetSpatialRef() const override
8778 : {
8779 68 : if (m_poArray->GetDimensionCount() < 2)
8780 3 : return nullptr;
8781 65 : m_poSRS = m_poArray->GetSpatialRef();
8782 65 : if (m_poSRS)
8783 : {
8784 26 : m_poSRS.reset(m_poSRS->Clone());
8785 52 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8786 78 : for (auto &m : axisMapping)
8787 : {
8788 52 : if (m == static_cast<int>(m_iXDim) + 1)
8789 26 : m = 1;
8790 26 : else if (m == static_cast<int>(m_iYDim) + 1)
8791 26 : m = 2;
8792 : }
8793 26 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8794 : }
8795 65 : return m_poSRS.get();
8796 : }
8797 :
8798 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8799 : {
8800 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8801 : }
8802 :
8803 179 : char **GetMetadata(const char *pszDomain) override
8804 : {
8805 179 : return m_oMDD.GetMetadata(pszDomain);
8806 : }
8807 :
8808 237 : const char *GetMetadataItem(const char *pszName,
8809 : const char *pszDomain) override
8810 : {
8811 429 : if (!m_osOvrFilename.empty() && pszName &&
8812 444 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8813 15 : EQUAL(pszDomain, "OVERVIEWS"))
8814 : {
8815 15 : return m_osOvrFilename.c_str();
8816 : }
8817 222 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8818 : }
8819 : };
8820 :
8821 430 : GDALDatasetFromArray::~GDALDatasetFromArray()
8822 : {
8823 215 : GDALDatasetFromArray::Close();
8824 430 : }
8825 :
8826 : /************************************************************************/
8827 : /* GDALRasterBandFromArray() */
8828 : /************************************************************************/
8829 :
8830 291 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8831 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8832 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8833 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8834 291 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8835 : {
8836 291 : const auto &poArray(poDSIn->m_poArray);
8837 291 : const auto &dims(poArray->GetDimensions());
8838 291 : const auto nDimCount(dims.size());
8839 582 : const auto blockSize(poArray->GetBlockSize());
8840 278 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8841 569 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8842 147 : blockSize[poDSIn->m_iYDim]))
8843 : : 1;
8844 291 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8845 159 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8846 159 : blockSize[poDSIn->m_iXDim]))
8847 291 : : poDSIn->GetRasterXSize();
8848 291 : eDataType = poArray->GetDataType().GetNumericDataType();
8849 291 : eAccess = poDSIn->eAccess;
8850 291 : m_anOffset.resize(nDimCount);
8851 291 : m_anCount.resize(nDimCount, 1);
8852 291 : m_anStride.resize(nDimCount);
8853 991 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8854 : {
8855 700 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8856 : {
8857 262 : std::string dimName(dims[i]->GetName());
8858 131 : GUInt64 nIndex = anOtherDimCoord[j];
8859 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8860 : // subsetted dimensions as generated by GetView()
8861 131 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8862 : {
8863 : CPLStringList aosTokens(
8864 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8865 6 : if (aosTokens.size() == 5)
8866 : {
8867 6 : dimName = aosTokens[1];
8868 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8869 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8870 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8871 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8872 0 : : nStartDim - (nIndex * -nIncrDim);
8873 : }
8874 : }
8875 131 : if (nDimCount != 3 || dimName != "Band")
8876 : {
8877 70 : SetMetadataItem(
8878 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8879 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8880 : }
8881 :
8882 131 : auto indexingVar = dims[i]->GetIndexingVariable();
8883 :
8884 : // If the indexing variable is also listed in band parameter arrays,
8885 : // then don't use our default formatting
8886 131 : if (indexingVar)
8887 : {
8888 47 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8889 : {
8890 14 : if (oItem.poArray->GetFullName() ==
8891 14 : indexingVar->GetFullName())
8892 : {
8893 12 : indexingVar.reset();
8894 12 : break;
8895 : }
8896 : }
8897 : }
8898 :
8899 164 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8900 33 : indexingVar->GetDimensions()[0]->GetSize() ==
8901 33 : dims[i]->GetSize())
8902 : {
8903 33 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8904 : {
8905 0 : if (!bHasWarned)
8906 : {
8907 0 : CPLError(
8908 : CE_Warning, CPLE_AppDefined,
8909 : "Maximum delay to load band metadata from "
8910 : "dimension indexing variables has expired. "
8911 : "Increase the value of the "
8912 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8913 : "option of GDALMDArray::AsClassicDataset() "
8914 : "(also accessible as the "
8915 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8916 : "configuration option), "
8917 : "or set it to 'unlimited' for unlimited delay. ");
8918 0 : bHasWarned = true;
8919 : }
8920 : }
8921 : else
8922 : {
8923 33 : size_t nCount = 1;
8924 33 : const auto &dt(indexingVar->GetDataType());
8925 66 : std::vector<GByte> abyTmp(dt.GetSize());
8926 66 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8927 33 : nullptr, nullptr, dt, &abyTmp[0]))
8928 : {
8929 33 : char *pszTmp = nullptr;
8930 33 : GDALExtendedDataType::CopyValue(
8931 33 : &abyTmp[0], dt, &pszTmp,
8932 66 : GDALExtendedDataType::CreateString());
8933 33 : dt.FreeDynamicMemory(abyTmp.data());
8934 33 : if (pszTmp)
8935 : {
8936 33 : SetMetadataItem(
8937 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8938 : pszTmp);
8939 33 : CPLFree(pszTmp);
8940 : }
8941 :
8942 33 : const auto &unit(indexingVar->GetUnit());
8943 33 : if (!unit.empty())
8944 : {
8945 12 : SetMetadataItem(
8946 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8947 : unit.c_str());
8948 : }
8949 : }
8950 : }
8951 : }
8952 :
8953 149 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8954 : {
8955 36 : CPLString osVal;
8956 :
8957 18 : size_t nCount = 1;
8958 18 : const auto &dt(oItem.poArray->GetDataType());
8959 18 : if (oItem.bDefinitionUsesPctForG)
8960 : {
8961 : // There is one and only one %[x][.y]f|g in osDefinition
8962 16 : std::vector<GByte> abyTmp(dt.GetSize());
8963 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8964 8 : nullptr, nullptr, dt, &abyTmp[0]))
8965 : {
8966 8 : double dfVal = 0;
8967 8 : GDALExtendedDataType::CopyValue(
8968 8 : &abyTmp[0], dt, &dfVal,
8969 16 : GDALExtendedDataType::Create(GDT_Float64));
8970 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8971 8 : dt.FreeDynamicMemory(abyTmp.data());
8972 : }
8973 : }
8974 : else
8975 : {
8976 : // There should be zero or one %s in osDefinition
8977 10 : char *pszValue = nullptr;
8978 10 : if (dt.GetClass() == GEDTC_STRING)
8979 : {
8980 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8981 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8982 2 : dt, &pszValue));
8983 : }
8984 : else
8985 : {
8986 16 : std::vector<GByte> abyTmp(dt.GetSize());
8987 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8988 : nullptr, nullptr, dt,
8989 8 : &abyTmp[0]))
8990 : {
8991 8 : GDALExtendedDataType::CopyValue(
8992 8 : &abyTmp[0], dt, &pszValue,
8993 16 : GDALExtendedDataType::CreateString());
8994 : }
8995 : }
8996 :
8997 10 : if (pszValue)
8998 : {
8999 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9000 10 : CPLFree(pszValue);
9001 : }
9002 : }
9003 18 : if (!osVal.empty())
9004 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9005 : }
9006 :
9007 131 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9008 : {
9009 : auto &poCentralWavelengthArray =
9010 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9011 4 : size_t nCount = 1;
9012 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9013 8 : std::vector<GByte> abyTmp(dt.GetSize());
9014 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9015 : &nCount, nullptr, nullptr,
9016 4 : dt, &abyTmp[0]))
9017 : {
9018 4 : double dfVal = 0;
9019 4 : GDALExtendedDataType::CopyValue(
9020 4 : &abyTmp[0], dt, &dfVal,
9021 8 : GDALExtendedDataType::Create(GDT_Float64));
9022 4 : dt.FreeDynamicMemory(abyTmp.data());
9023 4 : SetMetadataItem(
9024 : "CENTRAL_WAVELENGTH_UM",
9025 : CPLSPrintf(
9026 4 : "%g", dfVal * aoBandImageryMetadata[j]
9027 4 : .dfCentralWavelengthToMicrometer),
9028 : "IMAGERY");
9029 : }
9030 : }
9031 :
9032 131 : if (aoBandImageryMetadata[j].poFWHMArray)
9033 : {
9034 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9035 2 : size_t nCount = 1;
9036 2 : const auto &dt(poFWHMArray->GetDataType());
9037 4 : std::vector<GByte> abyTmp(dt.GetSize());
9038 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9039 2 : nullptr, dt, &abyTmp[0]))
9040 : {
9041 2 : double dfVal = 0;
9042 2 : GDALExtendedDataType::CopyValue(
9043 2 : &abyTmp[0], dt, &dfVal,
9044 4 : GDALExtendedDataType::Create(GDT_Float64));
9045 2 : dt.FreeDynamicMemory(abyTmp.data());
9046 2 : SetMetadataItem(
9047 : "FWHM_UM",
9048 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9049 2 : .dfFWHMToMicrometer),
9050 : "IMAGERY");
9051 : }
9052 : }
9053 :
9054 131 : m_anOffset[i] = anOtherDimCoord[j];
9055 131 : j++;
9056 : }
9057 : }
9058 291 : }
9059 :
9060 : /************************************************************************/
9061 : /* GetNoDataValue() */
9062 : /************************************************************************/
9063 :
9064 114 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9065 : {
9066 114 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9067 114 : const auto &poArray(l_poDS->m_poArray);
9068 114 : bool bHasNodata = false;
9069 114 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9070 114 : if (pbHasNoData)
9071 102 : *pbHasNoData = bHasNodata;
9072 114 : return res;
9073 : }
9074 :
9075 : /************************************************************************/
9076 : /* GetNoDataValueAsInt64() */
9077 : /************************************************************************/
9078 :
9079 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9080 : {
9081 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9082 1 : const auto &poArray(l_poDS->m_poArray);
9083 1 : bool bHasNodata = false;
9084 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9085 1 : if (pbHasNoData)
9086 1 : *pbHasNoData = bHasNodata;
9087 1 : return nodata;
9088 : }
9089 :
9090 : /************************************************************************/
9091 : /* GetNoDataValueAsUInt64() */
9092 : /************************************************************************/
9093 :
9094 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9095 : {
9096 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9097 1 : const auto &poArray(l_poDS->m_poArray);
9098 1 : bool bHasNodata = false;
9099 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9100 1 : if (pbHasNoData)
9101 1 : *pbHasNoData = bHasNodata;
9102 1 : return nodata;
9103 : }
9104 :
9105 : /************************************************************************/
9106 : /* GetOffset() */
9107 : /************************************************************************/
9108 :
9109 40 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9110 : {
9111 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9112 40 : const auto &poArray(l_poDS->m_poArray);
9113 40 : bool bHasValue = false;
9114 40 : double dfRes = poArray->GetOffset(&bHasValue);
9115 40 : if (pbHasOffset)
9116 21 : *pbHasOffset = bHasValue;
9117 40 : return dfRes;
9118 : }
9119 :
9120 : /************************************************************************/
9121 : /* GetUnitType() */
9122 : /************************************************************************/
9123 :
9124 46 : const char *GDALRasterBandFromArray::GetUnitType()
9125 : {
9126 46 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9127 46 : const auto &poArray(l_poDS->m_poArray);
9128 46 : return poArray->GetUnit().c_str();
9129 : }
9130 :
9131 : /************************************************************************/
9132 : /* GetScale() */
9133 : /************************************************************************/
9134 :
9135 38 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9136 : {
9137 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9138 38 : const auto &poArray(l_poDS->m_poArray);
9139 38 : bool bHasValue = false;
9140 38 : double dfRes = poArray->GetScale(&bHasValue);
9141 38 : if (pbHasScale)
9142 19 : *pbHasScale = bHasValue;
9143 38 : return dfRes;
9144 : }
9145 :
9146 : /************************************************************************/
9147 : /* IReadBlock() */
9148 : /************************************************************************/
9149 :
9150 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9151 : void *pImage)
9152 : {
9153 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9154 68 : const int nXOff = nBlockXOff * nBlockXSize;
9155 68 : const int nYOff = nBlockYOff * nBlockYSize;
9156 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9157 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9158 : GDALRasterIOExtraArg sExtraArg;
9159 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9160 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9161 : nReqXSize, nReqYSize, eDataType, nDTSize,
9162 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9163 : }
9164 :
9165 : /************************************************************************/
9166 : /* IWriteBlock() */
9167 : /************************************************************************/
9168 :
9169 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9170 : void *pImage)
9171 : {
9172 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9173 1 : const int nXOff = nBlockXOff * nBlockXSize;
9174 1 : const int nYOff = nBlockYOff * nBlockYSize;
9175 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9176 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9177 : GDALRasterIOExtraArg sExtraArg;
9178 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9179 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9180 : nReqXSize, nReqYSize, eDataType, nDTSize,
9181 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9182 : }
9183 :
9184 : /************************************************************************/
9185 : /* IRasterIO() */
9186 : /************************************************************************/
9187 :
9188 337 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9189 : int nYOff, int nXSize, int nYSize,
9190 : void *pData, int nBufXSize,
9191 : int nBufYSize, GDALDataType eBufType,
9192 : GSpacing nPixelSpaceBuf,
9193 : GSpacing nLineSpaceBuf,
9194 : GDALRasterIOExtraArg *psExtraArg)
9195 : {
9196 337 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9197 337 : const auto &poArray(l_poDS->m_poArray);
9198 337 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9199 337 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9200 337 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9201 337 : (nLineSpaceBuf % nBufferDTSize) == 0)
9202 : {
9203 337 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9204 337 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9205 674 : m_anStride[l_poDS->m_iXDim] =
9206 337 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9207 337 : if (poArray->GetDimensionCount() >= 2)
9208 : {
9209 327 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9210 327 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9211 327 : m_anStride[l_poDS->m_iYDim] =
9212 327 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9213 : }
9214 337 : if (eRWFlag == GF_Read)
9215 : {
9216 662 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9217 331 : m_anStride.data(),
9218 662 : GDALExtendedDataType::Create(eBufType), pData)
9219 331 : ? CE_None
9220 331 : : CE_Failure;
9221 : }
9222 : else
9223 : {
9224 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9225 6 : m_anStride.data(),
9226 12 : GDALExtendedDataType::Create(eBufType), pData)
9227 6 : ? CE_None
9228 6 : : CE_Failure;
9229 : }
9230 : }
9231 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9232 : pData, nBufXSize, nBufYSize, eBufType,
9233 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9234 : }
9235 :
9236 : /************************************************************************/
9237 : /* GetColorInterpretation() */
9238 : /************************************************************************/
9239 :
9240 66 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9241 : {
9242 66 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9243 66 : const auto &poArray(l_poDS->m_poArray);
9244 198 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9245 66 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9246 : {
9247 6 : bool bOK = false;
9248 6 : GUInt64 nStartIndex = 0;
9249 6 : if (poArray->GetDimensionCount() == 2 &&
9250 0 : poAttr->GetDimensionCount() == 0)
9251 : {
9252 0 : bOK = true;
9253 : }
9254 6 : else if (poArray->GetDimensionCount() == 3)
9255 : {
9256 6 : uint64_t nExtraDimSamples = 1;
9257 6 : const auto &apoDims = poArray->GetDimensions();
9258 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9259 : {
9260 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9261 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9262 : }
9263 6 : if (poAttr->GetDimensionsSize() ==
9264 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9265 : {
9266 6 : bOK = true;
9267 : }
9268 6 : nStartIndex = nBand - 1;
9269 : }
9270 6 : if (bOK)
9271 : {
9272 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9273 6 : const size_t nCount = 1;
9274 6 : const GInt64 arrayStep = 1;
9275 6 : const GPtrDiff_t bufferStride = 1;
9276 6 : char *pszValue = nullptr;
9277 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9278 6 : oStringDT, &pszValue);
9279 6 : if (pszValue)
9280 : {
9281 : const auto eColorInterp =
9282 6 : GDALGetColorInterpretationByName(pszValue);
9283 6 : CPLFree(pszValue);
9284 6 : return eColorInterp;
9285 : }
9286 : }
9287 : }
9288 60 : return GCI_Undefined;
9289 : }
9290 :
9291 : /************************************************************************/
9292 : /* GDALDatasetFromArray::Create() */
9293 : /************************************************************************/
9294 :
9295 267 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9296 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9297 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9298 :
9299 : {
9300 267 : const auto nDimCount(array->GetDimensionCount());
9301 267 : if (nDimCount == 0)
9302 : {
9303 1 : CPLError(CE_Failure, CPLE_NotSupported,
9304 : "Unsupported number of dimensions");
9305 1 : return nullptr;
9306 : }
9307 531 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9308 265 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9309 : {
9310 1 : CPLError(CE_Failure, CPLE_NotSupported,
9311 : "Only arrays with numeric data types "
9312 : "can be exposed as classic GDALDataset");
9313 1 : return nullptr;
9314 : }
9315 265 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9316 245 : (nDimCount >= 2 && iXDim == iYDim))
9317 : {
9318 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9319 8 : return nullptr;
9320 : }
9321 257 : GUInt64 nTotalBands = 1;
9322 257 : const auto &dims(array->GetDimensions());
9323 849 : for (size_t i = 0; i < nDimCount; ++i)
9324 : {
9325 593 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9326 : {
9327 94 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9328 : {
9329 1 : CPLError(CE_Failure, CPLE_AppDefined,
9330 : "Too many bands. Operate on a sliced view");
9331 1 : return nullptr;
9332 : }
9333 93 : nTotalBands *= dims[i]->GetSize();
9334 : }
9335 : }
9336 :
9337 512 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9338 512 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9339 848 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9340 : {
9341 592 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9342 : {
9343 93 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9344 93 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9345 93 : ++j;
9346 : }
9347 : }
9348 :
9349 256 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9350 :
9351 : const char *pszBandMetadata =
9352 256 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9353 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9354 512 : nNewDimCount);
9355 256 : if (pszBandMetadata)
9356 : {
9357 32 : if (!poRootGroup)
9358 : {
9359 1 : CPLError(CE_Failure, CPLE_AppDefined,
9360 : "Root group should be provided when BAND_METADATA is set");
9361 24 : return nullptr;
9362 : }
9363 31 : CPLJSONDocument oDoc;
9364 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9365 : {
9366 1 : CPLError(CE_Failure, CPLE_AppDefined,
9367 : "Invalid JSON content for BAND_METADATA");
9368 1 : return nullptr;
9369 : }
9370 30 : auto oRoot = oDoc.GetRoot();
9371 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9372 : {
9373 1 : CPLError(CE_Failure, CPLE_AppDefined,
9374 : "Value of BAND_METADATA should be an array");
9375 1 : return nullptr;
9376 : }
9377 :
9378 29 : auto oArray = oRoot.ToArray();
9379 38 : for (int j = 0; j < oArray.Size(); ++j)
9380 : {
9381 30 : const auto oJsonItem = oArray[j];
9382 30 : MetadataItem oItem;
9383 30 : size_t iExtraDimIdx = 0;
9384 :
9385 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9386 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9387 0 : std::shared_ptr<GDALMDArray> poArray;
9388 0 : std::shared_ptr<GDALAttribute> poAttribute;
9389 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9390 : {
9391 1 : CPLError(CE_Failure, CPLE_AppDefined,
9392 : "BAND_METADATA[%d][\"array\"] or "
9393 : "BAND_METADATA[%d][\"attribute\"] is missing",
9394 : j, j);
9395 1 : return nullptr;
9396 : }
9397 48 : else if (!osBandArrayFullname.empty() &&
9398 19 : !osBandAttributeName.empty())
9399 : {
9400 1 : CPLError(
9401 : CE_Failure, CPLE_AppDefined,
9402 : "BAND_METADATA[%d][\"array\"] and "
9403 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9404 : j, j);
9405 1 : return nullptr;
9406 : }
9407 28 : else if (!osBandArrayFullname.empty())
9408 : {
9409 : poArray =
9410 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9411 18 : if (!poArray)
9412 : {
9413 1 : CPLError(CE_Failure, CPLE_AppDefined,
9414 : "Array %s cannot be found",
9415 : osBandArrayFullname.c_str());
9416 3 : return nullptr;
9417 : }
9418 17 : if (poArray->GetDimensionCount() != 1)
9419 : {
9420 1 : CPLError(CE_Failure, CPLE_AppDefined,
9421 : "Array %s is not a 1D array",
9422 : osBandArrayFullname.c_str());
9423 1 : return nullptr;
9424 : }
9425 : const auto &osAuxArrayDimName =
9426 16 : poArray->GetDimensions()[0]->GetName();
9427 : auto oIter =
9428 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9429 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9430 : {
9431 1 : CPLError(
9432 : CE_Failure, CPLE_AppDefined,
9433 : "Dimension %s of array %s is not a non-X/Y dimension "
9434 : "of array %s",
9435 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9436 1 : array->GetName().c_str());
9437 1 : return nullptr;
9438 : }
9439 15 : iExtraDimIdx = oIter->second;
9440 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9441 : }
9442 : else
9443 : {
9444 10 : CPLAssert(!osBandAttributeName.empty());
9445 10 : poAttribute = !osBandAttributeName.empty() &&
9446 10 : osBandAttributeName[0] == '/'
9447 24 : ? poRootGroup->OpenAttributeFromFullname(
9448 : osBandAttributeName)
9449 10 : : array->GetAttribute(osBandAttributeName);
9450 10 : if (!poAttribute)
9451 : {
9452 2 : CPLError(CE_Failure, CPLE_AppDefined,
9453 : "Attribute %s cannot be found",
9454 : osBandAttributeName.c_str());
9455 8 : return nullptr;
9456 : }
9457 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9458 8 : if (aoAttrDims.size() != 1)
9459 : {
9460 4 : CPLError(CE_Failure, CPLE_AppDefined,
9461 : "Attribute %s is not a 1D array",
9462 : osBandAttributeName.c_str());
9463 4 : return nullptr;
9464 : }
9465 4 : bool found = false;
9466 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9467 : {
9468 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9469 5 : ->GetSize() == aoAttrDims[0])
9470 : {
9471 4 : if (found)
9472 : {
9473 2 : CPLError(CE_Failure, CPLE_AppDefined,
9474 : "Several dimensions of %s have the same "
9475 : "size as attribute %s. Cannot infer which "
9476 : "one to bind to!",
9477 1 : array->GetName().c_str(),
9478 : osBandAttributeName.c_str());
9479 1 : return nullptr;
9480 : }
9481 3 : found = true;
9482 3 : iExtraDimIdx = iter.second;
9483 : }
9484 : }
9485 3 : if (!found)
9486 : {
9487 2 : CPLError(
9488 : CE_Failure, CPLE_AppDefined,
9489 : "No dimension of %s has the same size as attribute %s",
9490 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9491 1 : return nullptr;
9492 : }
9493 : }
9494 :
9495 17 : oItem.osName = oJsonItem.GetString("item_name");
9496 17 : if (oItem.osName.empty())
9497 : {
9498 1 : CPLError(CE_Failure, CPLE_AppDefined,
9499 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9500 1 : return nullptr;
9501 : }
9502 :
9503 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9504 :
9505 : // Check correctness of definition
9506 16 : bool bFirstNumericFormatter = true;
9507 16 : std::string osModDefinition;
9508 16 : bool bDefinitionUsesPctForG = false;
9509 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9510 : {
9511 70 : if (osDefinition[k] == '%')
9512 : {
9513 15 : osModDefinition += osDefinition[k];
9514 15 : if (k + 1 == osDefinition.size())
9515 : {
9516 1 : CPLError(CE_Failure, CPLE_AppDefined,
9517 : "Value of "
9518 : "BAND_METADATA[%d][\"item_value\"] = "
9519 : "%s is invalid at offset %d",
9520 : j, osDefinition.c_str(), int(k));
9521 1 : return nullptr;
9522 : }
9523 14 : ++k;
9524 14 : if (osDefinition[k] == '%')
9525 : {
9526 1 : osModDefinition += osDefinition[k];
9527 1 : continue;
9528 : }
9529 13 : if (!bFirstNumericFormatter)
9530 : {
9531 1 : CPLError(CE_Failure, CPLE_AppDefined,
9532 : "Value of "
9533 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9534 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9535 : "formatters should be specified at most once",
9536 : j, osDefinition.c_str(), int(k));
9537 1 : return nullptr;
9538 : }
9539 12 : bFirstNumericFormatter = false;
9540 19 : for (; k < osDefinition.size(); ++k)
9541 : {
9542 19 : osModDefinition += osDefinition[k];
9543 38 : if (!((osDefinition[k] >= '0' &&
9544 16 : osDefinition[k] <= '9') ||
9545 15 : osDefinition[k] == '.'))
9546 12 : break;
9547 : }
9548 24 : if (k == osDefinition.size() ||
9549 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9550 5 : osDefinition[k] != 's'))
9551 : {
9552 1 : CPLError(CE_Failure, CPLE_AppDefined,
9553 : "Value of "
9554 : "BAND_METADATA[%d][\"item_value\"] = "
9555 : "%s is invalid at offset %d: only "
9556 : "%%[x][.y]f|g or %%s formatters are accepted",
9557 : j, osDefinition.c_str(), int(k));
9558 1 : return nullptr;
9559 : }
9560 11 : bDefinitionUsesPctForG =
9561 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9562 11 : if (bDefinitionUsesPctForG)
9563 : {
9564 12 : if (poArray &&
9565 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9566 : {
9567 1 : CPLError(CE_Failure, CPLE_AppDefined,
9568 : "Data type of %s array is not numeric",
9569 1 : poArray->GetName().c_str());
9570 1 : return nullptr;
9571 : }
9572 8 : else if (poAttribute &&
9573 2 : poAttribute->GetDataType().GetClass() !=
9574 6 : GEDTC_NUMERIC)
9575 : {
9576 0 : CPLError(CE_Failure, CPLE_AppDefined,
9577 : "Data type of %s attribute is not numeric",
9578 0 : poAttribute->GetFullName().c_str());
9579 0 : return nullptr;
9580 : }
9581 : }
9582 : }
9583 62 : else if (osDefinition[k] == '$' &&
9584 62 : k + 1 < osDefinition.size() &&
9585 7 : osDefinition[k + 1] == '{')
9586 : {
9587 7 : const auto nPos = osDefinition.find('}', k);
9588 7 : if (nPos == std::string::npos)
9589 : {
9590 1 : CPLError(CE_Failure, CPLE_AppDefined,
9591 : "Value of "
9592 : "BAND_METADATA[%d][\"item_value\"] = "
9593 : "%s is invalid at offset %d",
9594 : j, osDefinition.c_str(), int(k));
9595 3 : return nullptr;
9596 : }
9597 : const auto osAttrName =
9598 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9599 0 : std::shared_ptr<GDALAttribute> poAttr;
9600 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9601 : {
9602 4 : poAttr = poArray->GetAttribute(osAttrName);
9603 4 : if (!poAttr)
9604 : {
9605 1 : CPLError(
9606 : CE_Failure, CPLE_AppDefined,
9607 : "Value of "
9608 : "BAND_METADATA[%d][\"item_value\"] = "
9609 : "%s is invalid: %s is not an attribute of %s",
9610 : j, osDefinition.c_str(), osAttrName.c_str(),
9611 1 : poArray->GetName().c_str());
9612 1 : return nullptr;
9613 : }
9614 : }
9615 : else
9616 : {
9617 : poAttr =
9618 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9619 2 : if (!poAttr)
9620 : {
9621 1 : CPLError(CE_Failure, CPLE_AppDefined,
9622 : "Value of "
9623 : "BAND_METADATA[%d][\"item_value\"] = "
9624 : "%s is invalid: %s is not an attribute",
9625 : j, osDefinition.c_str(),
9626 : osAttrName.c_str());
9627 1 : return nullptr;
9628 : }
9629 : }
9630 4 : k = nPos;
9631 4 : const char *pszValue = poAttr->ReadAsString();
9632 4 : if (!pszValue)
9633 : {
9634 0 : CPLError(CE_Failure, CPLE_AppDefined,
9635 : "Cannot get value of attribute %s as a "
9636 : "string",
9637 : osAttrName.c_str());
9638 0 : return nullptr;
9639 : }
9640 4 : osModDefinition += pszValue;
9641 : }
9642 : else
9643 : {
9644 48 : osModDefinition += osDefinition[k];
9645 : }
9646 : }
9647 :
9648 9 : if (poArray)
9649 8 : oItem.poArray = std::move(poArray);
9650 : else
9651 1 : oItem.poArray = std::move(poAttribute);
9652 9 : oItem.osDefinition = std::move(osModDefinition);
9653 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9654 :
9655 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9656 9 : std::move(oItem));
9657 : }
9658 : }
9659 :
9660 464 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9661 : const char *pszBandImageryMetadata =
9662 232 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9663 232 : if (pszBandImageryMetadata)
9664 : {
9665 20 : if (!poRootGroup)
9666 : {
9667 1 : CPLError(CE_Failure, CPLE_AppDefined,
9668 : "Root group should be provided when BAND_IMAGERY_METADATA "
9669 : "is set");
9670 17 : return nullptr;
9671 : }
9672 19 : CPLJSONDocument oDoc;
9673 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9674 : {
9675 1 : CPLError(CE_Failure, CPLE_AppDefined,
9676 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9677 1 : return nullptr;
9678 : }
9679 18 : auto oRoot = oDoc.GetRoot();
9680 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9681 : {
9682 1 : CPLError(CE_Failure, CPLE_AppDefined,
9683 : "Value of BAND_IMAGERY_METADATA should be an object");
9684 1 : return nullptr;
9685 : }
9686 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9687 : {
9688 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9689 20 : oJsonItem.GetName() == "FWHM_UM")
9690 : {
9691 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9692 : const auto osBandAttributeName =
9693 34 : oJsonItem.GetString("attribute");
9694 0 : std::shared_ptr<GDALMDArray> poArray;
9695 0 : std::shared_ptr<GDALAttribute> poAttribute;
9696 17 : size_t iExtraDimIdx = 0;
9697 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9698 : {
9699 2 : CPLError(CE_Failure, CPLE_AppDefined,
9700 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9701 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9702 : "missing",
9703 2 : oJsonItem.GetName().c_str(),
9704 2 : oJsonItem.GetName().c_str());
9705 1 : return nullptr;
9706 : }
9707 25 : else if (!osBandArrayFullname.empty() &&
9708 9 : !osBandAttributeName.empty())
9709 : {
9710 2 : CPLError(CE_Failure, CPLE_AppDefined,
9711 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9712 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9713 : "mutually exclusive",
9714 2 : oJsonItem.GetName().c_str(),
9715 2 : oJsonItem.GetName().c_str());
9716 1 : return nullptr;
9717 : }
9718 15 : else if (!osBandArrayFullname.empty())
9719 : {
9720 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9721 8 : osBandArrayFullname);
9722 8 : if (!poArray)
9723 : {
9724 1 : CPLError(CE_Failure, CPLE_AppDefined,
9725 : "Array %s cannot be found",
9726 : osBandArrayFullname.c_str());
9727 3 : return nullptr;
9728 : }
9729 7 : if (poArray->GetDimensionCount() != 1)
9730 : {
9731 1 : CPLError(CE_Failure, CPLE_AppDefined,
9732 : "Array %s is not a 1D array",
9733 : osBandArrayFullname.c_str());
9734 1 : return nullptr;
9735 : }
9736 : const auto &osAuxArrayDimName =
9737 6 : poArray->GetDimensions()[0]->GetName();
9738 : auto oIter =
9739 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9740 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9741 : {
9742 1 : CPLError(CE_Failure, CPLE_AppDefined,
9743 : "Dimension \"%s\" of array \"%s\" is not a "
9744 : "non-X/Y dimension of array \"%s\"",
9745 : osAuxArrayDimName.c_str(),
9746 : osBandArrayFullname.c_str(),
9747 1 : array->GetName().c_str());
9748 1 : return nullptr;
9749 : }
9750 5 : iExtraDimIdx = oIter->second;
9751 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9752 : }
9753 : else
9754 : {
9755 : poAttribute =
9756 7 : !osBandAttributeName.empty() &&
9757 7 : osBandAttributeName[0] == '/'
9758 16 : ? poRootGroup->OpenAttributeFromFullname(
9759 : osBandAttributeName)
9760 7 : : array->GetAttribute(osBandAttributeName);
9761 7 : if (!poAttribute)
9762 : {
9763 2 : CPLError(CE_Failure, CPLE_AppDefined,
9764 : "Attribute %s cannot be found",
9765 : osBandAttributeName.c_str());
9766 6 : return nullptr;
9767 : }
9768 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9769 5 : if (aoAttrDims.size() != 1)
9770 : {
9771 2 : CPLError(CE_Failure, CPLE_AppDefined,
9772 : "Attribute %s is not a 1D array",
9773 : osBandAttributeName.c_str());
9774 2 : return nullptr;
9775 : }
9776 3 : bool found = false;
9777 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9778 : {
9779 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9780 4 : ->GetSize() == aoAttrDims[0])
9781 : {
9782 3 : if (found)
9783 : {
9784 2 : CPLError(CE_Failure, CPLE_AppDefined,
9785 : "Several dimensions of %s have the "
9786 : "same size as attribute %s. Cannot "
9787 : "infer which one to bind to!",
9788 1 : array->GetName().c_str(),
9789 : osBandAttributeName.c_str());
9790 1 : return nullptr;
9791 : }
9792 2 : found = true;
9793 2 : iExtraDimIdx = iter.second;
9794 : }
9795 : }
9796 2 : if (!found)
9797 : {
9798 2 : CPLError(CE_Failure, CPLE_AppDefined,
9799 : "No dimension of %s has the same size as "
9800 : "attribute %s",
9801 1 : array->GetName().c_str(),
9802 : osBandAttributeName.c_str());
9803 1 : return nullptr;
9804 : }
9805 : }
9806 :
9807 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9808 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9809 : {
9810 4 : if (osUnit.back() != '}')
9811 : {
9812 2 : CPLError(CE_Failure, CPLE_AppDefined,
9813 : "Value of "
9814 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9815 : "%s is invalid",
9816 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9817 2 : return nullptr;
9818 : }
9819 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9820 0 : std::shared_ptr<GDALAttribute> poAttr;
9821 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9822 : {
9823 2 : poAttr = poArray->GetAttribute(osAttrName);
9824 2 : if (!poAttr)
9825 : {
9826 2 : CPLError(
9827 : CE_Failure, CPLE_AppDefined,
9828 : "Value of "
9829 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9830 : "%s is invalid: %s is not an attribute of %s",
9831 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9832 : osAttrName.c_str(),
9833 : osBandArrayFullname.c_str());
9834 1 : return nullptr;
9835 : }
9836 : }
9837 : else
9838 : {
9839 : poAttr =
9840 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9841 1 : if (!poAttr)
9842 : {
9843 0 : CPLError(
9844 : CE_Failure, CPLE_AppDefined,
9845 : "Value of "
9846 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9847 : "%s is invalid: %s is not an attribute",
9848 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9849 : osAttrName.c_str());
9850 0 : return nullptr;
9851 : }
9852 : }
9853 :
9854 2 : const char *pszValue = poAttr->ReadAsString();
9855 2 : if (!pszValue)
9856 : {
9857 0 : CPLError(CE_Failure, CPLE_AppDefined,
9858 : "Cannot get value of attribute %s of %s as a "
9859 : "string",
9860 : osAttrName.c_str(),
9861 : osBandArrayFullname.c_str());
9862 0 : return nullptr;
9863 : }
9864 2 : osUnit = pszValue;
9865 : }
9866 4 : double dfConvToUM = 1.0;
9867 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9868 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9869 3 : osUnit == "nanometers")
9870 : {
9871 1 : dfConvToUM = 1e-3;
9872 : }
9873 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9874 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9875 1 : osUnit == "micrometers"))
9876 : {
9877 2 : CPLError(CE_Failure, CPLE_AppDefined,
9878 : "Unhandled value for "
9879 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9880 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9881 1 : return nullptr;
9882 : }
9883 :
9884 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9885 :
9886 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9887 3 : if (poArray)
9888 2 : abstractArray = std::move(poArray);
9889 : else
9890 1 : abstractArray = std::move(poAttribute);
9891 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9892 : {
9893 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9894 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9895 : }
9896 : else
9897 : {
9898 1 : item.poFWHMArray = std::move(abstractArray);
9899 1 : item.dfFWHMToMicrometer = dfConvToUM;
9900 : }
9901 : }
9902 : else
9903 : {
9904 1 : CPLError(CE_Warning, CPLE_AppDefined,
9905 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9906 2 : oJsonItem.GetName().c_str());
9907 : }
9908 : }
9909 : }
9910 :
9911 430 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9912 :
9913 215 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9914 :
9915 215 : poDS->nRasterYSize =
9916 215 : nDimCount < 2 ? 1
9917 202 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9918 202 : dims[iYDim]->GetSize()));
9919 430 : poDS->nRasterXSize = static_cast<int>(
9920 215 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9921 :
9922 430 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9923 430 : std::vector<GUInt64> anStackIters(nDimCount);
9924 430 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9925 682 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9926 : {
9927 467 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9928 : {
9929 50 : anMapNewToOld[j] = i;
9930 50 : j++;
9931 : }
9932 : }
9933 :
9934 215 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9935 :
9936 430 : const auto attrs(array->GetAttributes());
9937 334 : for (const auto &attr : attrs)
9938 : {
9939 119 : if (attr->GetName() != "COLOR_INTERPRETATION")
9940 : {
9941 216 : auto stringArray = attr->ReadAsStringArray();
9942 216 : std::string val;
9943 108 : if (stringArray.size() > 1)
9944 : {
9945 44 : val += '{';
9946 : }
9947 464 : for (int i = 0; i < stringArray.size(); ++i)
9948 : {
9949 356 : if (i > 0)
9950 248 : val += ',';
9951 356 : val += stringArray[i];
9952 : }
9953 108 : if (stringArray.size() > 1)
9954 : {
9955 44 : val += '}';
9956 : }
9957 108 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9958 : }
9959 : }
9960 :
9961 215 : const char *pszDelay = CSLFetchNameValueDef(
9962 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9963 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9964 : const double dfDelay =
9965 215 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9966 215 : const auto nStartTime = time(nullptr);
9967 215 : bool bHasWarned = false;
9968 : // Instantiate bands by iterating over non-XY variables
9969 215 : size_t iDim = 0;
9970 215 : int nCurBand = 1;
9971 343 : lbl_next_depth:
9972 343 : if (iDim < nNewDimCount)
9973 : {
9974 52 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9975 52 : anOtherDimCoord[iDim] = 0;
9976 : while (true)
9977 : {
9978 128 : ++iDim;
9979 128 : goto lbl_next_depth;
9980 128 : lbl_return_to_caller:
9981 128 : --iDim;
9982 128 : --anStackIters[iDim];
9983 128 : if (anStackIters[iDim] == 0)
9984 52 : break;
9985 76 : ++anOtherDimCoord[iDim];
9986 : }
9987 : }
9988 : else
9989 : {
9990 582 : poDS->SetBand(nCurBand,
9991 : new GDALRasterBandFromArray(
9992 291 : poDS.get(), anOtherDimCoord,
9993 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9994 291 : dfDelay, nStartTime, bHasWarned));
9995 291 : ++nCurBand;
9996 : }
9997 343 : if (iDim > 0)
9998 128 : goto lbl_return_to_caller;
9999 :
10000 215 : if (!array->GetFilename().empty())
10001 : {
10002 188 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10003 : std::string osDerivedDatasetName(
10004 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10005 376 : int(iYDim), array->GetFullName().c_str()));
10006 188 : if (!array->GetContext().empty())
10007 : {
10008 2 : osDerivedDatasetName += " with context ";
10009 2 : osDerivedDatasetName += array->GetContext();
10010 : }
10011 188 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10012 188 : poDS->TryLoadXML();
10013 :
10014 2 : for (const auto &[pszKey, pszValue] :
10015 : cpl::IterateNameValue(static_cast<CSLConstList>(
10016 190 : poDS->GDALPamDataset::GetMetadata())))
10017 : {
10018 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10019 : }
10020 : }
10021 :
10022 215 : return poDS.release();
10023 : }
10024 :
10025 : /************************************************************************/
10026 : /* AsClassicDataset() */
10027 : /************************************************************************/
10028 :
10029 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10030 : *
10031 : * In the case of > 2D arrays, additional dimensions will be represented as
10032 : * raster bands.
10033 : *
10034 : * The "reverse" method is GDALRasterBand::AsMDArray().
10035 : *
10036 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10037 : *
10038 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10039 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10040 : * Ignored if the dimension count is 1.
10041 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10042 : * and BAND_IMAGERY_METADATA option.
10043 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10044 : * nullptr. Current supported options are:
10045 : * <ul>
10046 : * <li>BAND_METADATA: JSON serialized array defining which
10047 : * arrays of the poRootGroup, indexed by non-X and Y
10048 : * dimensions, should be mapped as band metadata items.
10049 : * Each array item should be an object with the
10050 : * following members:
10051 : * - "array": full name of a band parameter array.
10052 : * Such array must be a one
10053 : * dimensional array, and its dimension must be one of
10054 : * the dimensions of the array on which the method is
10055 : * called (excluding the X and Y dimensions).
10056 : * - "attribute": name relative to *this array or full
10057 : * name of a single dimension numeric array whose size
10058 : * must be one of the dimensions of *this array
10059 : * (excluding the X and Y dimensions).
10060 : * "array" and "attribute" are mutually exclusive.
10061 : * - "item_name": band metadata item name
10062 : * - "item_value": (optional) String, where "%[x][.y]f",
10063 : * "%[x][.y]g" or "%s" printf-like formatting can be
10064 : * used to format the corresponding value of the
10065 : * parameter array. The percentage character should be
10066 : * repeated: "%%"
10067 : * "${attribute_name}" can also be used to include the
10068 : * value of an attribute for "array" when set and if
10069 : * not starting with '/'. Otherwise if starting with
10070 : * '/', it is the full path to the attribute.
10071 : *
10072 : * If "item_value" is not provided, a default formatting
10073 : * of the value will be applied.
10074 : *
10075 : * Example:
10076 : * [
10077 : * {
10078 : * "array": "/sensor_band_parameters/wavelengths",
10079 : * "item_name": "WAVELENGTH",
10080 : * "item_value": "%.1f ${units}"
10081 : * },
10082 : * {
10083 : * "array": "/sensor_band_parameters/fwhm",
10084 : * "item_name": "FWHM"
10085 : * },
10086 : * {
10087 : * "array": "/sensor_band_parameters/fwhm",
10088 : * "item_name": "FWHM_UNIT",
10089 : * "item_value": "${units}"
10090 : * }
10091 : * ]
10092 : *
10093 : * Example for Planet Labs Tanager radiance products:
10094 : * [
10095 : * {
10096 : * "attribute": "center_wavelengths",
10097 : * "item_name": "WAVELENGTH",
10098 : * "item_value": "%.1f ${center_wavelengths_units}"
10099 : * }
10100 : * ]
10101 : *
10102 : * </li>
10103 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10104 : * JSON serialized object defining which arrays of the
10105 : * poRootGroup, indexed by non-X and Y dimensions,
10106 : * should be mapped as band metadata items in the
10107 : * band IMAGERY domain.
10108 : * The object currently accepts 2 members:
10109 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10110 : * micrometers.
10111 : * - "FWHM_UM": Full-width half-maximum
10112 : * in micrometers.
10113 : * The value of each member should be an object with the
10114 : * following members:
10115 : * - "array": full name of a band parameter array.
10116 : * Such array must be a one dimensional array, and its
10117 : * dimension must be one of the dimensions of the
10118 : * array on which the method is called
10119 : * (excluding the X and Y dimensions).
10120 : * - "attribute": name relative to *this array or full
10121 : * name of a single dimension numeric array whose size
10122 : * must be one of the dimensions of *this array
10123 : * (excluding the X and Y dimensions).
10124 : * "array" and "attribute" are mutually exclusive,
10125 : * and one of them is required.
10126 : * - "unit": (optional) unit of the values pointed in
10127 : * the above array.
10128 : * Can be a literal string or a string of the form
10129 : * "${attribute_name}" to point to an attribute for
10130 : * "array" when set and if no starting
10131 : * with '/'. Otherwise if starting with '/', it is
10132 : * the full path to the attribute.
10133 : * Accepted values are "um", "micrometer"
10134 : * (with UK vs US spelling, singular or plural), "nm",
10135 : * "nanometer" (with UK vs US spelling, singular or
10136 : * plural)
10137 : * If not provided, micrometer is assumed.
10138 : *
10139 : * Example for EMIT datasets:
10140 : * {
10141 : * "CENTRAL_WAVELENGTH_UM": {
10142 : * "array": "/sensor_band_parameters/wavelengths",
10143 : * "unit": "${units}"
10144 : * },
10145 : * "FWHM_UM": {
10146 : * "array": "/sensor_band_parameters/fwhm",
10147 : * "unit": "${units}"
10148 : * }
10149 : * }
10150 : *
10151 : * Example for Planet Labs Tanager radiance products:
10152 : * {
10153 : * "CENTRAL_WAVELENGTH_UM": {
10154 : * "attribute": "center_wavelengths",
10155 : * "unit": "${center_wavelengths_units}"
10156 : * },
10157 : * "FWHM_UM": {
10158 : * "attribute": "fwhm",
10159 : * "unit": "${fwhm_units}"
10160 : * }
10161 : * }
10162 : *
10163 : * </li>
10164 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10165 : * seconds allowed to set the DIM_{dimname}_VALUE band
10166 : * metadata items from the indexing variable of the
10167 : * dimensions.
10168 : * Default value is 5. 'unlimited' can be used to mean
10169 : * unlimited delay. Can also be defined globally with
10170 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10171 : * option.</li>
10172 : * </ul>
10173 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10174 : */
10175 : GDALDataset *
10176 267 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10177 : const std::shared_ptr<GDALGroup> &poRootGroup,
10178 : CSLConstList papszOptions) const
10179 : {
10180 534 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10181 267 : if (!self)
10182 : {
10183 0 : CPLError(CE_Failure, CPLE_AppDefined,
10184 : "Driver implementation issue: m_pSelf not set !");
10185 0 : return nullptr;
10186 : }
10187 267 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10188 267 : papszOptions);
10189 : }
10190 :
10191 : /************************************************************************/
10192 : /* GetStatistics() */
10193 : /************************************************************************/
10194 :
10195 : /**
10196 : * \brief Fetch statistics.
10197 : *
10198 : * Returns the minimum, maximum, mean and standard deviation of all
10199 : * pixel values in this array.
10200 : *
10201 : * If bForce is FALSE results will only be returned if it can be done
10202 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10203 : * results cannot be returned efficiently, the method will return CE_Warning
10204 : * but no warning will have been issued. This is a non-standard use of
10205 : * the CE_Warning return value to indicate "nothing done".
10206 : *
10207 : * When cached statistics are not available, and bForce is TRUE,
10208 : * ComputeStatistics() is called.
10209 : *
10210 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10211 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10212 : * after the first request.
10213 : *
10214 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10215 : *
10216 : * This method is the same as the C function GDALMDArrayGetStatistics().
10217 : *
10218 : * @param bApproxOK Currently ignored. In the future, should be set to true
10219 : * if statistics on the whole array are wished, or to false if a subset of it
10220 : * may be used.
10221 : *
10222 : * @param bForce If false statistics will only be returned if it can
10223 : * be done without rescanning the image.
10224 : *
10225 : * @param pdfMin Location into which to load image minimum (may be NULL).
10226 : *
10227 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10228 : *
10229 : * @param pdfMean Location into which to load image mean (may be NULL).
10230 : *
10231 : * @param pdfStdDev Location into which to load image standard deviation
10232 : * (may be NULL).
10233 : *
10234 : * @param pnValidCount Number of samples whose value is different from the
10235 : * nodata value. (may be NULL)
10236 : *
10237 : * @param pfnProgress a function to call to report progress, or NULL.
10238 : *
10239 : * @param pProgressData application data to pass to the progress function.
10240 : *
10241 : * @return CE_None on success, CE_Warning if no values returned,
10242 : * CE_Failure if an error occurs.
10243 : *
10244 : * @since GDAL 3.2
10245 : */
10246 :
10247 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10248 : double *pdfMax, double *pdfMean,
10249 : double *pdfStdDev, GUInt64 *pnValidCount,
10250 : GDALProgressFunc pfnProgress,
10251 : void *pProgressData)
10252 : {
10253 10 : if (!bForce)
10254 1 : return CE_Warning;
10255 :
10256 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10257 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10258 9 : ? CE_None
10259 9 : : CE_Failure;
10260 : }
10261 :
10262 : /************************************************************************/
10263 : /* ComputeStatistics() */
10264 : /************************************************************************/
10265 :
10266 : /**
10267 : * \brief Compute statistics.
10268 : *
10269 : * Returns the minimum, maximum, mean and standard deviation of all
10270 : * pixel values in this array.
10271 : *
10272 : * Pixels taken into account in statistics are those whose mask value
10273 : * (as determined by GetMask()) is non-zero.
10274 : *
10275 : * Once computed, the statistics will generally be "set" back on the
10276 : * owing dataset.
10277 : *
10278 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10279 : *
10280 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10281 : * and GDALMDArrayComputeStatisticsEx().
10282 : *
10283 : * @param bApproxOK Currently ignored. In the future, should be set to true
10284 : * if statistics on the whole array are wished, or to false if a subset of it
10285 : * may be used.
10286 : *
10287 : * @param pdfMin Location into which to load image minimum (may be NULL).
10288 : *
10289 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10290 : *
10291 : * @param pdfMean Location into which to load image mean (may be NULL).
10292 : *
10293 : * @param pdfStdDev Location into which to load image standard deviation
10294 : * (may be NULL).
10295 : *
10296 : * @param pnValidCount Number of samples whose value is different from the
10297 : * nodata value. (may be NULL)
10298 : *
10299 : * @param pfnProgress a function to call to report progress, or NULL.
10300 : *
10301 : * @param pProgressData application data to pass to the progress function.
10302 : *
10303 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10304 : * Options are driver specific. For now the netCDF and Zarr
10305 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10306 : * to add or update the actual_range attribute with the
10307 : * computed min/max, only if done on the full array, in non
10308 : * approximate mode, and the dataset is opened in update
10309 : * mode.
10310 : *
10311 : * @return true on success
10312 : *
10313 : * @since GDAL 3.2
10314 : */
10315 :
10316 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10317 : double *pdfMax, double *pdfMean,
10318 : double *pdfStdDev, GUInt64 *pnValidCount,
10319 : GDALProgressFunc pfnProgress,
10320 : void *pProgressData,
10321 : CSLConstList papszOptions)
10322 : {
10323 : struct StatsPerChunkType
10324 : {
10325 : const GDALMDArray *array = nullptr;
10326 : std::shared_ptr<GDALMDArray> poMask{};
10327 : double dfMin = cpl::NumericLimits<double>::max();
10328 : double dfMax = -cpl::NumericLimits<double>::max();
10329 : double dfMean = 0.0;
10330 : double dfM2 = 0.0;
10331 : GUInt64 nValidCount = 0;
10332 : std::vector<GByte> abyData{};
10333 : std::vector<double> adfData{};
10334 : std::vector<GByte> abyMaskData{};
10335 : GDALProgressFunc pfnProgress = nullptr;
10336 : void *pProgressData = nullptr;
10337 : };
10338 :
10339 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10340 : const GUInt64 *chunkArrayStartIdx,
10341 : const size_t *chunkCount, GUInt64 iCurChunk,
10342 : GUInt64 nChunkCount, void *pUserData)
10343 : {
10344 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10345 13 : const GDALMDArray *array = data->array;
10346 13 : const GDALMDArray *poMask = data->poMask.get();
10347 13 : const size_t nDims = array->GetDimensionCount();
10348 13 : size_t nVals = 1;
10349 34 : for (size_t i = 0; i < nDims; i++)
10350 21 : nVals *= chunkCount[i];
10351 :
10352 : // Get mask
10353 13 : data->abyMaskData.resize(nVals);
10354 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10355 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10356 : {
10357 0 : return false;
10358 : }
10359 :
10360 : // Get data
10361 13 : const auto &oType = array->GetDataType();
10362 13 : if (oType.GetNumericDataType() == GDT_Float64)
10363 : {
10364 6 : data->adfData.resize(nVals);
10365 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10366 6 : oType, &data->adfData[0]))
10367 : {
10368 0 : return false;
10369 : }
10370 : }
10371 : else
10372 : {
10373 7 : data->abyData.resize(nVals * oType.GetSize());
10374 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10375 7 : oType, &data->abyData[0]))
10376 : {
10377 0 : return false;
10378 : }
10379 7 : data->adfData.resize(nVals);
10380 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10381 7 : static_cast<int>(oType.GetSize()),
10382 7 : &data->adfData[0], GDT_Float64,
10383 : static_cast<int>(sizeof(double)),
10384 : static_cast<GPtrDiff_t>(nVals));
10385 : }
10386 912 : for (size_t i = 0; i < nVals; i++)
10387 : {
10388 899 : if (data->abyMaskData[i])
10389 : {
10390 894 : const double dfValue = data->adfData[i];
10391 894 : data->dfMin = std::min(data->dfMin, dfValue);
10392 894 : data->dfMax = std::max(data->dfMax, dfValue);
10393 894 : data->nValidCount++;
10394 894 : const double dfDelta = dfValue - data->dfMean;
10395 894 : data->dfMean += dfDelta / data->nValidCount;
10396 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10397 : }
10398 : }
10399 13 : if (data->pfnProgress &&
10400 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10401 : "", data->pProgressData))
10402 : {
10403 0 : return false;
10404 : }
10405 13 : return true;
10406 : };
10407 :
10408 13 : const auto &oType = GetDataType();
10409 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10410 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10411 : {
10412 0 : CPLError(
10413 : CE_Failure, CPLE_NotSupported,
10414 : "Statistics can only be computed on non-complex numeric data type");
10415 0 : return false;
10416 : }
10417 :
10418 13 : const size_t nDims = GetDimensionCount();
10419 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10420 26 : std::vector<GUInt64> count(nDims);
10421 13 : const auto &poDims = GetDimensions();
10422 34 : for (size_t i = 0; i < nDims; i++)
10423 : {
10424 21 : count[i] = poDims[i]->GetSize();
10425 : }
10426 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10427 : const size_t nMaxChunkSize =
10428 : pszSwathSize
10429 13 : ? static_cast<size_t>(
10430 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10431 0 : CPLAtoGIntBig(pszSwathSize)))
10432 : : static_cast<size_t>(
10433 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10434 13 : GDALGetCacheMax64() / 4));
10435 26 : StatsPerChunkType sData;
10436 13 : sData.array = this;
10437 13 : sData.poMask = GetMask(nullptr);
10438 13 : if (sData.poMask == nullptr)
10439 : {
10440 0 : return false;
10441 : }
10442 13 : sData.pfnProgress = pfnProgress;
10443 13 : sData.pProgressData = pProgressData;
10444 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10445 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10446 13 : PerChunkFunc, &sData))
10447 : {
10448 0 : return false;
10449 : }
10450 :
10451 13 : if (pdfMin)
10452 13 : *pdfMin = sData.dfMin;
10453 :
10454 13 : if (pdfMax)
10455 13 : *pdfMax = sData.dfMax;
10456 :
10457 13 : if (pdfMean)
10458 11 : *pdfMean = sData.dfMean;
10459 :
10460 : const double dfStdDev =
10461 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10462 13 : if (pdfStdDev)
10463 11 : *pdfStdDev = dfStdDev;
10464 :
10465 13 : if (pnValidCount)
10466 11 : *pnValidCount = sData.nValidCount;
10467 :
10468 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10469 13 : sData.nValidCount, papszOptions);
10470 :
10471 13 : return true;
10472 : }
10473 :
10474 : /************************************************************************/
10475 : /* SetStatistics() */
10476 : /************************************************************************/
10477 : //! @cond Doxygen_Suppress
10478 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10479 : double /* dfMax */, double /* dfMean */,
10480 : double /* dfStdDev */,
10481 : GUInt64 /* nValidCount */,
10482 : CSLConstList /* papszOptions */)
10483 : {
10484 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10485 5 : return false;
10486 : }
10487 :
10488 : //! @endcond
10489 :
10490 : /************************************************************************/
10491 : /* ClearStatistics() */
10492 : /************************************************************************/
10493 :
10494 : /**
10495 : * \brief Clear statistics.
10496 : *
10497 : * @since GDAL 3.4
10498 : */
10499 0 : void GDALMDArray::ClearStatistics()
10500 : {
10501 0 : }
10502 :
10503 : /************************************************************************/
10504 : /* GetCoordinateVariables() */
10505 : /************************************************************************/
10506 :
10507 : /**
10508 : * \brief Return coordinate variables.
10509 : *
10510 : * Coordinate variables are an alternate way of indexing an array that can
10511 : * be sometimes used. For example, an array collected through remote sensing
10512 : * might be indexed by (scanline, pixel). But there can be
10513 : * a longitude and latitude arrays alongside that are also both indexed by
10514 : * (scanline, pixel), and are referenced from operational arrays for
10515 : * reprojection purposes.
10516 : *
10517 : * For netCDF, this will return the arrays referenced by the "coordinates"
10518 : * attribute.
10519 : *
10520 : * This method is the same as the C function
10521 : * GDALMDArrayGetCoordinateVariables().
10522 : *
10523 : * @return a vector of arrays
10524 : *
10525 : * @since GDAL 3.4
10526 : */
10527 :
10528 : std::vector<std::shared_ptr<GDALMDArray>>
10529 13 : GDALMDArray::GetCoordinateVariables() const
10530 : {
10531 13 : return {};
10532 : }
10533 :
10534 : /************************************************************************/
10535 : /* ~GDALExtendedDataType() */
10536 : /************************************************************************/
10537 :
10538 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10539 :
10540 : /************************************************************************/
10541 : /* GDALExtendedDataType() */
10542 : /************************************************************************/
10543 :
10544 9926 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10545 9926 : GDALExtendedDataTypeSubType eSubType)
10546 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10547 9926 : m_nMaxStringLength(nMaxStringLength)
10548 : {
10549 9926 : }
10550 :
10551 : /************************************************************************/
10552 : /* GDALExtendedDataType() */
10553 : /************************************************************************/
10554 :
10555 38211 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10556 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10557 38211 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10558 : {
10559 38214 : }
10560 :
10561 : /************************************************************************/
10562 : /* GDALExtendedDataType() */
10563 : /************************************************************************/
10564 :
10565 63 : GDALExtendedDataType::GDALExtendedDataType(
10566 : const std::string &osName, GDALDataType eBaseType,
10567 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10568 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10569 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10570 : {
10571 63 : }
10572 :
10573 : /************************************************************************/
10574 : /* GDALExtendedDataType() */
10575 : /************************************************************************/
10576 :
10577 871 : GDALExtendedDataType::GDALExtendedDataType(
10578 : const std::string &osName, size_t nTotalSize,
10579 871 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10580 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10581 871 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10582 : {
10583 871 : }
10584 :
10585 : /************************************************************************/
10586 : /* GDALExtendedDataType() */
10587 : /************************************************************************/
10588 :
10589 : /** Move constructor. */
10590 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10591 :
10592 : /************************************************************************/
10593 : /* GDALExtendedDataType() */
10594 : /************************************************************************/
10595 :
10596 : /** Copy constructor. */
10597 16283 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10598 32568 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10599 16285 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10600 16285 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10601 16283 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10602 : {
10603 16284 : if (m_eClass == GEDTC_COMPOUND)
10604 : {
10605 481 : for (const auto &elt : other.m_aoComponents)
10606 : {
10607 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10608 : }
10609 : }
10610 16284 : }
10611 :
10612 : /************************************************************************/
10613 : /* operator= () */
10614 : /************************************************************************/
10615 :
10616 : /** Copy assignment. */
10617 : GDALExtendedDataType &
10618 1066 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10619 : {
10620 1066 : if (this != &other)
10621 : {
10622 1066 : m_osName = other.m_osName;
10623 1066 : m_eClass = other.m_eClass;
10624 1066 : m_eSubType = other.m_eSubType;
10625 1066 : m_eNumericDT = other.m_eNumericDT;
10626 1066 : m_nSize = other.m_nSize;
10627 1066 : m_nMaxStringLength = other.m_nMaxStringLength;
10628 1066 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10629 1066 : m_aoComponents.clear();
10630 1066 : if (m_eClass == GEDTC_COMPOUND)
10631 : {
10632 0 : for (const auto &elt : other.m_aoComponents)
10633 : {
10634 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10635 : }
10636 : }
10637 : }
10638 1066 : return *this;
10639 : }
10640 :
10641 : /************************************************************************/
10642 : /* operator= () */
10643 : /************************************************************************/
10644 :
10645 : /** Move assignment. */
10646 : GDALExtendedDataType &
10647 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10648 :
10649 : /************************************************************************/
10650 : /* Create() */
10651 : /************************************************************************/
10652 :
10653 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10654 : *
10655 : * This is the same as the C function GDALExtendedDataTypeCreate()
10656 : *
10657 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10658 : * GDT_TypeCount
10659 : */
10660 38204 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10661 : {
10662 38204 : return GDALExtendedDataType(eType);
10663 : }
10664 :
10665 : /************************************************************************/
10666 : /* Create() */
10667 : /************************************************************************/
10668 :
10669 : /** Return a new GDALExtendedDataType from a raster attribute table.
10670 : *
10671 : * @param osName Type name
10672 : * @param eBaseType Base integer data type.
10673 : * @param poRAT Raster attribute table. Must not be NULL.
10674 : * @since 3.12
10675 : */
10676 : GDALExtendedDataType
10677 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10678 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10679 : {
10680 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10681 : }
10682 :
10683 : /************************************************************************/
10684 : /* Create() */
10685 : /************************************************************************/
10686 :
10687 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10688 : *
10689 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10690 : *
10691 : * @param osName Type name.
10692 : * @param nTotalSize Total size of the type in bytes.
10693 : * Should be large enough to store all components.
10694 : * @param components Components of the compound type.
10695 : */
10696 878 : GDALExtendedDataType GDALExtendedDataType::Create(
10697 : const std::string &osName, size_t nTotalSize,
10698 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10699 : {
10700 878 : size_t nLastOffset = 0;
10701 : // Some arbitrary threshold to avoid potential integer overflows
10702 878 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10703 : {
10704 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10705 2 : return GDALExtendedDataType(GDT_Unknown);
10706 : }
10707 4138 : for (const auto &comp : components)
10708 : {
10709 : // Check alignment too ?
10710 3263 : if (comp->GetOffset() < nLastOffset)
10711 : {
10712 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10713 1 : return GDALExtendedDataType(GDT_Unknown);
10714 : }
10715 3262 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10716 : }
10717 875 : if (nTotalSize < nLastOffset)
10718 : {
10719 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10720 1 : return GDALExtendedDataType(GDT_Unknown);
10721 : }
10722 874 : if (nTotalSize == 0 || components.empty())
10723 : {
10724 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10725 3 : return GDALExtendedDataType(GDT_Unknown);
10726 : }
10727 871 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10728 : }
10729 :
10730 : /************************************************************************/
10731 : /* Create() */
10732 : /************************************************************************/
10733 :
10734 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10735 : *
10736 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10737 : *
10738 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10739 : * unknown/unlimited
10740 : * @param eSubType Subtype.
10741 : */
10742 : GDALExtendedDataType
10743 9926 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10744 : GDALExtendedDataTypeSubType eSubType)
10745 : {
10746 9926 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10747 : }
10748 :
10749 : /************************************************************************/
10750 : /* operator==() */
10751 : /************************************************************************/
10752 :
10753 : /** Equality operator.
10754 : *
10755 : * This is the same as the C function GDALExtendedDataTypeEquals().
10756 : */
10757 2771 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10758 : {
10759 2744 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10760 5515 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10761 : {
10762 258 : return false;
10763 : }
10764 2513 : if (m_eClass == GEDTC_NUMERIC)
10765 : {
10766 882 : return m_eNumericDT == other.m_eNumericDT;
10767 : }
10768 1631 : if (m_eClass == GEDTC_STRING)
10769 : {
10770 1413 : return true;
10771 : }
10772 218 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10773 218 : if (m_aoComponents.size() != other.m_aoComponents.size())
10774 : {
10775 2 : return false;
10776 : }
10777 1139 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10778 : {
10779 923 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10780 : {
10781 0 : return false;
10782 : }
10783 : }
10784 216 : return true;
10785 : }
10786 :
10787 : /************************************************************************/
10788 : /* CanConvertTo() */
10789 : /************************************************************************/
10790 :
10791 : /** Return whether this data type can be converted to the other one.
10792 : *
10793 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10794 : *
10795 : * @param other Target data type for the conversion being considered.
10796 : */
10797 9115 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10798 : {
10799 9115 : if (m_eClass == GEDTC_NUMERIC)
10800 : {
10801 6258 : if (m_eNumericDT == GDT_Unknown)
10802 0 : return false;
10803 6258 : if (other.m_eClass == GEDTC_NUMERIC &&
10804 6148 : other.m_eNumericDT == GDT_Unknown)
10805 0 : return false;
10806 6368 : return other.m_eClass == GEDTC_NUMERIC ||
10807 6368 : other.m_eClass == GEDTC_STRING;
10808 : }
10809 2857 : if (m_eClass == GEDTC_STRING)
10810 : {
10811 2671 : return other.m_eClass == m_eClass;
10812 : }
10813 186 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10814 186 : if (other.m_eClass != GEDTC_COMPOUND)
10815 0 : return false;
10816 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10817 372 : srcComponents;
10818 929 : for (const auto &srcComp : m_aoComponents)
10819 : {
10820 743 : srcComponents[srcComp->GetName()] = &srcComp;
10821 : }
10822 513 : for (const auto &dstComp : other.m_aoComponents)
10823 : {
10824 328 : auto oIter = srcComponents.find(dstComp->GetName());
10825 328 : if (oIter == srcComponents.end())
10826 1 : return false;
10827 327 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10828 0 : return false;
10829 : }
10830 185 : return true;
10831 : }
10832 :
10833 : /************************************************************************/
10834 : /* NeedsFreeDynamicMemory() */
10835 : /************************************************************************/
10836 :
10837 : /** Return whether the data type holds dynamically allocated memory, that
10838 : * needs to be freed with FreeDynamicMemory().
10839 : *
10840 : */
10841 3852 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10842 : {
10843 3852 : switch (m_eClass)
10844 : {
10845 1006 : case GEDTC_STRING:
10846 1006 : return true;
10847 :
10848 2736 : case GEDTC_NUMERIC:
10849 2736 : return false;
10850 :
10851 110 : case GEDTC_COMPOUND:
10852 : {
10853 223 : for (const auto &comp : m_aoComponents)
10854 : {
10855 209 : if (comp->GetType().NeedsFreeDynamicMemory())
10856 96 : return true;
10857 : }
10858 : }
10859 : }
10860 14 : return false;
10861 : }
10862 :
10863 : /************************************************************************/
10864 : /* FreeDynamicMemory() */
10865 : /************************************************************************/
10866 :
10867 : /** Release the dynamic memory (strings typically) from a raw value.
10868 : *
10869 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10870 : *
10871 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10872 : */
10873 4016 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10874 : {
10875 4016 : switch (m_eClass)
10876 : {
10877 2894 : case GEDTC_STRING:
10878 : {
10879 : char *pszStr;
10880 2894 : memcpy(&pszStr, pBuffer, sizeof(char *));
10881 2894 : if (pszStr)
10882 : {
10883 2312 : VSIFree(pszStr);
10884 : }
10885 2894 : break;
10886 : }
10887 :
10888 946 : case GEDTC_NUMERIC:
10889 : {
10890 946 : break;
10891 : }
10892 :
10893 176 : case GEDTC_COMPOUND:
10894 : {
10895 176 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10896 938 : for (const auto &comp : m_aoComponents)
10897 : {
10898 1524 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10899 762 : comp->GetOffset());
10900 : }
10901 176 : break;
10902 : }
10903 : }
10904 4016 : }
10905 :
10906 : /************************************************************************/
10907 : /* ~GDALEDTComponent() */
10908 : /************************************************************************/
10909 :
10910 : GDALEDTComponent::~GDALEDTComponent() = default;
10911 :
10912 : /************************************************************************/
10913 : /* GDALEDTComponent() */
10914 : /************************************************************************/
10915 :
10916 : /** constructor of a GDALEDTComponent
10917 : *
10918 : * This is the same as the C function GDALEDTComponendCreate()
10919 : *
10920 : * @param name Component name
10921 : * @param offset Offset in byte of the component in the compound data type.
10922 : * In case of nesting of compound data type, this should be
10923 : * the offset to the immediate belonging data type, not to the
10924 : * higher level one.
10925 : * @param type Component data type.
10926 : */
10927 3254 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10928 3254 : const GDALExtendedDataType &type)
10929 3254 : : m_osName(name), m_nOffset(offset), m_oType(type)
10930 : {
10931 3254 : }
10932 :
10933 : /************************************************************************/
10934 : /* GDALEDTComponent() */
10935 : /************************************************************************/
10936 :
10937 : /** Copy constructor. */
10938 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10939 :
10940 : /************************************************************************/
10941 : /* operator==() */
10942 : /************************************************************************/
10943 :
10944 : /** Equality operator.
10945 : */
10946 923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10947 : {
10948 1846 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10949 1846 : m_oType == other.m_oType;
10950 : }
10951 :
10952 : /************************************************************************/
10953 : /* ~GDALDimension() */
10954 : /************************************************************************/
10955 :
10956 : GDALDimension::~GDALDimension() = default;
10957 :
10958 : /************************************************************************/
10959 : /* GDALDimension() */
10960 : /************************************************************************/
10961 :
10962 : //! @cond Doxygen_Suppress
10963 : /** Constructor.
10964 : *
10965 : * @param osParentName Parent name
10966 : * @param osName name
10967 : * @param osType type. See GetType().
10968 : * @param osDirection direction. See GetDirection().
10969 : * @param nSize size.
10970 : */
10971 8807 : GDALDimension::GDALDimension(const std::string &osParentName,
10972 : const std::string &osName,
10973 : const std::string &osType,
10974 8807 : const std::string &osDirection, GUInt64 nSize)
10975 : : m_osName(osName),
10976 : m_osFullName(
10977 8808 : !osParentName.empty()
10978 12931 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10979 : : osName),
10980 30545 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10981 : {
10982 8808 : }
10983 :
10984 : //! @endcond
10985 :
10986 : /************************************************************************/
10987 : /* GetIndexingVariable() */
10988 : /************************************************************************/
10989 :
10990 : /** Return the variable that is used to index the dimension (if there is one).
10991 : *
10992 : * This is the array, typically one-dimensional, describing the values taken
10993 : * by the dimension.
10994 : */
10995 33 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10996 : {
10997 33 : return nullptr;
10998 : }
10999 :
11000 : /************************************************************************/
11001 : /* SetIndexingVariable() */
11002 : /************************************************************************/
11003 :
11004 : /** Set the variable that is used to index the dimension.
11005 : *
11006 : * This is the array, typically one-dimensional, describing the values taken
11007 : * by the dimension.
11008 : *
11009 : * Optionally implemented by drivers.
11010 : *
11011 : * Drivers known to implement it: MEM.
11012 : *
11013 : * @param poArray Variable to use to index the dimension.
11014 : * @return true in case of success.
11015 : */
11016 3 : bool GDALDimension::SetIndexingVariable(
11017 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11018 : {
11019 3 : CPLError(CE_Failure, CPLE_NotSupported,
11020 : "SetIndexingVariable() not implemented");
11021 3 : return false;
11022 : }
11023 :
11024 : /************************************************************************/
11025 : /* Rename() */
11026 : /************************************************************************/
11027 :
11028 : /** Rename the dimension.
11029 : *
11030 : * This is not implemented by all drivers.
11031 : *
11032 : * Drivers known to implement it: MEM, netCDF, ZARR.
11033 : *
11034 : * This is the same as the C function GDALDimensionRename().
11035 : *
11036 : * @param osNewName New name.
11037 : *
11038 : * @return true in case of success
11039 : * @since GDAL 3.8
11040 : */
11041 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11042 : {
11043 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11044 0 : return false;
11045 : }
11046 :
11047 : /************************************************************************/
11048 : /* BaseRename() */
11049 : /************************************************************************/
11050 :
11051 : //! @cond Doxygen_Suppress
11052 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11053 : {
11054 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11055 8 : m_osFullName += osNewName;
11056 8 : m_osName = osNewName;
11057 8 : }
11058 :
11059 : //! @endcond
11060 :
11061 : //! @cond Doxygen_Suppress
11062 : /************************************************************************/
11063 : /* ParentRenamed() */
11064 : /************************************************************************/
11065 :
11066 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11067 : {
11068 8 : m_osFullName = osNewParentFullName;
11069 8 : m_osFullName += "/";
11070 8 : m_osFullName += m_osName;
11071 8 : }
11072 :
11073 : //! @endcond
11074 :
11075 : //! @cond Doxygen_Suppress
11076 : /************************************************************************/
11077 : /* ParentDeleted() */
11078 : /************************************************************************/
11079 :
11080 4 : void GDALDimension::ParentDeleted()
11081 : {
11082 4 : }
11083 :
11084 : //! @endcond
11085 :
11086 : /************************************************************************/
11087 : /************************************************************************/
11088 : /************************************************************************/
11089 : /* C API */
11090 : /************************************************************************/
11091 : /************************************************************************/
11092 : /************************************************************************/
11093 :
11094 : /************************************************************************/
11095 : /* GDALExtendedDataTypeCreate() */
11096 : /************************************************************************/
11097 :
11098 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11099 : *
11100 : * This is the same as the C++ method GDALExtendedDataType::Create()
11101 : *
11102 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11103 : *
11104 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11105 : * GDT_TypeCount
11106 : *
11107 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11108 : */
11109 2012 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11110 : {
11111 2012 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11112 : {
11113 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11114 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11115 0 : return nullptr;
11116 : }
11117 : return new GDALExtendedDataTypeHS(
11118 2012 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11119 : }
11120 :
11121 : /************************************************************************/
11122 : /* GDALExtendedDataTypeCreateString() */
11123 : /************************************************************************/
11124 :
11125 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11126 : *
11127 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11128 : *
11129 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11130 : *
11131 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11132 : */
11133 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11134 : {
11135 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11136 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11137 : }
11138 :
11139 : /************************************************************************/
11140 : /* GDALExtendedDataTypeCreateStringEx() */
11141 : /************************************************************************/
11142 :
11143 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11144 : *
11145 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11146 : *
11147 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11148 : *
11149 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11150 : * @since GDAL 3.4
11151 : */
11152 : GDALExtendedDataTypeH
11153 195 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11154 : GDALExtendedDataTypeSubType eSubType)
11155 : {
11156 195 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11157 195 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11158 : }
11159 :
11160 : /************************************************************************/
11161 : /* GDALExtendedDataTypeCreateCompound() */
11162 : /************************************************************************/
11163 :
11164 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11165 : *
11166 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11167 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11168 : *
11169 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11170 : *
11171 : * @param pszName Type name.
11172 : * @param nTotalSize Total size of the type in bytes.
11173 : * Should be large enough to store all components.
11174 : * @param nComponents Number of components in comps array.
11175 : * @param comps Components.
11176 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11177 : */
11178 : GDALExtendedDataTypeH
11179 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11180 : size_t nComponents,
11181 : const GDALEDTComponentH *comps)
11182 : {
11183 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11184 54 : for (size_t i = 0; i < nComponents; i++)
11185 : {
11186 : compsCpp.emplace_back(
11187 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11188 : }
11189 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11190 66 : std::move(compsCpp));
11191 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11192 6 : return nullptr;
11193 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11194 : }
11195 :
11196 : /************************************************************************/
11197 : /* GDALExtendedDataTypeRelease() */
11198 : /************************************************************************/
11199 :
11200 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11201 : *
11202 : * Note: when applied on a object coming from a driver, this does not
11203 : * destroy the object in the file, database, etc...
11204 : */
11205 6680 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11206 : {
11207 6680 : delete hEDT;
11208 6680 : }
11209 :
11210 : /************************************************************************/
11211 : /* GDALExtendedDataTypeGetName() */
11212 : /************************************************************************/
11213 :
11214 : /** Return type name.
11215 : *
11216 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11217 : */
11218 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11219 : {
11220 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11221 8 : return hEDT->m_poImpl->GetName().c_str();
11222 : }
11223 :
11224 : /************************************************************************/
11225 : /* GDALExtendedDataTypeGetClass() */
11226 : /************************************************************************/
11227 :
11228 : /** Return type class.
11229 : *
11230 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11231 : */
11232 : GDALExtendedDataTypeClass
11233 9282 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11234 : {
11235 9282 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11236 9282 : return hEDT->m_poImpl->GetClass();
11237 : }
11238 :
11239 : /************************************************************************/
11240 : /* GDALExtendedDataTypeGetNumericDataType() */
11241 : /************************************************************************/
11242 :
11243 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11244 : *
11245 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11246 : */
11247 2100 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11248 : {
11249 2100 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11250 2100 : return hEDT->m_poImpl->GetNumericDataType();
11251 : }
11252 :
11253 : /************************************************************************/
11254 : /* GDALExtendedDataTypeGetSize() */
11255 : /************************************************************************/
11256 :
11257 : /** Return data type size in bytes.
11258 : *
11259 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11260 : */
11261 2555 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11262 : {
11263 2555 : VALIDATE_POINTER1(hEDT, __func__, 0);
11264 2555 : return hEDT->m_poImpl->GetSize();
11265 : }
11266 :
11267 : /************************************************************************/
11268 : /* GDALExtendedDataTypeGetMaxStringLength() */
11269 : /************************************************************************/
11270 :
11271 : /** Return the maximum length of a string in bytes.
11272 : *
11273 : * 0 indicates unknown/unlimited string.
11274 : *
11275 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11276 : */
11277 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11278 : {
11279 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11280 3 : return hEDT->m_poImpl->GetMaxStringLength();
11281 : }
11282 :
11283 : /************************************************************************/
11284 : /* GDALExtendedDataTypeCanConvertTo() */
11285 : /************************************************************************/
11286 :
11287 : /** Return whether this data type can be converted to the other one.
11288 : *
11289 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11290 : *
11291 : * @param hSourceEDT Source data type for the conversion being considered.
11292 : * @param hTargetEDT Target data type for the conversion being considered.
11293 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11294 : */
11295 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11296 : GDALExtendedDataTypeH hTargetEDT)
11297 : {
11298 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11299 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11300 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11301 : }
11302 :
11303 : /************************************************************************/
11304 : /* GDALExtendedDataTypeEquals() */
11305 : /************************************************************************/
11306 :
11307 : /** Return whether this data type is equal to another one.
11308 : *
11309 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11310 : *
11311 : * @param hFirstEDT First data type.
11312 : * @param hSecondEDT Second data type.
11313 : * @return TRUE if they are equal. FALSE otherwise.
11314 : */
11315 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11316 : GDALExtendedDataTypeH hSecondEDT)
11317 : {
11318 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11319 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11320 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11321 : }
11322 :
11323 : /************************************************************************/
11324 : /* GDALExtendedDataTypeGetSubType() */
11325 : /************************************************************************/
11326 :
11327 : /** Return the subtype of a type.
11328 : *
11329 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11330 : *
11331 : * @param hEDT Data type.
11332 : * @return subtype.
11333 : * @since 3.4
11334 : */
11335 : GDALExtendedDataTypeSubType
11336 105 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11337 : {
11338 105 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11339 105 : return hEDT->m_poImpl->GetSubType();
11340 : }
11341 :
11342 : /************************************************************************/
11343 : /* GDALExtendedDataTypeGetRAT() */
11344 : /************************************************************************/
11345 :
11346 : /** Return associated raster attribute table, when there is one.
11347 : *
11348 : * * For the netCDF driver, the RAT will capture enumerated types, with
11349 : * a "value" column with an integer value and a "name" column with the
11350 : * associated name.
11351 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11352 : *
11353 : * @param hEDT Data type.
11354 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11355 : * @since 3.12
11356 : */
11357 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11358 : {
11359 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11360 1 : return GDALRasterAttributeTable::ToHandle(
11361 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11362 : }
11363 :
11364 : /************************************************************************/
11365 : /* GDALExtendedDataTypeGetComponents() */
11366 : /************************************************************************/
11367 :
11368 : /** Return the components of the data type (only valid when GetClass() ==
11369 : * GEDTC_COMPOUND)
11370 : *
11371 : * The returned array and its content must be freed with
11372 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11373 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11374 : * individual array members).
11375 : *
11376 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11377 : *
11378 : * @param hEDT Data type
11379 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11380 : * @return an array of *pnCount components.
11381 : */
11382 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11383 : size_t *pnCount)
11384 : {
11385 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11386 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11387 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11388 : auto ret = static_cast<GDALEDTComponentH *>(
11389 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11390 131 : for (size_t i = 0; i < components.size(); i++)
11391 : {
11392 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11393 : }
11394 44 : *pnCount = components.size();
11395 44 : return ret;
11396 : }
11397 :
11398 : /************************************************************************/
11399 : /* GDALExtendedDataTypeFreeComponents() */
11400 : /************************************************************************/
11401 :
11402 : /** Free the return of GDALExtendedDataTypeGetComponents().
11403 : *
11404 : * @param components return value of GDALExtendedDataTypeGetComponents()
11405 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11406 : */
11407 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11408 : size_t nCount)
11409 : {
11410 131 : for (size_t i = 0; i < nCount; i++)
11411 : {
11412 87 : delete components[i];
11413 : }
11414 44 : CPLFree(components);
11415 44 : }
11416 :
11417 : /************************************************************************/
11418 : /* GDALEDTComponentCreate() */
11419 : /************************************************************************/
11420 :
11421 : /** Create a new GDALEDTComponent.
11422 : *
11423 : * The returned value must be freed with GDALEDTComponentRelease().
11424 : *
11425 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11426 : */
11427 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11428 : GDALExtendedDataTypeH hType)
11429 : {
11430 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11431 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11432 : return new GDALEDTComponentHS(
11433 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11434 : }
11435 :
11436 : /************************************************************************/
11437 : /* GDALEDTComponentRelease() */
11438 : /************************************************************************/
11439 :
11440 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11441 : *
11442 : * Note: when applied on a object coming from a driver, this does not
11443 : * destroy the object in the file, database, etc...
11444 : */
11445 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11446 : {
11447 61 : delete hComp;
11448 61 : }
11449 :
11450 : /************************************************************************/
11451 : /* GDALEDTComponentGetName() */
11452 : /************************************************************************/
11453 :
11454 : /** Return the name.
11455 : *
11456 : * The returned pointer is valid until hComp is released.
11457 : *
11458 : * This is the same as the C++ method GDALEDTComponent::GetName().
11459 : */
11460 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11461 : {
11462 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11463 33 : return hComp->m_poImpl->GetName().c_str();
11464 : }
11465 :
11466 : /************************************************************************/
11467 : /* GDALEDTComponentGetOffset() */
11468 : /************************************************************************/
11469 :
11470 : /** Return the offset (in bytes) of the component in the compound data type.
11471 : *
11472 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11473 : */
11474 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11475 : {
11476 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11477 31 : return hComp->m_poImpl->GetOffset();
11478 : }
11479 :
11480 : /************************************************************************/
11481 : /* GDALEDTComponentGetType() */
11482 : /************************************************************************/
11483 :
11484 : /** Return the data type of the component.
11485 : *
11486 : * This is the same as the C++ method GDALEDTComponent::GetType().
11487 : */
11488 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11489 : {
11490 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11491 : return new GDALExtendedDataTypeHS(
11492 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11493 : }
11494 :
11495 : /************************************************************************/
11496 : /* GDALGroupRelease() */
11497 : /************************************************************************/
11498 :
11499 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11500 : *
11501 : * Note: when applied on a object coming from a driver, this does not
11502 : * destroy the object in the file, database, etc...
11503 : */
11504 1455 : void GDALGroupRelease(GDALGroupH hGroup)
11505 : {
11506 1455 : delete hGroup;
11507 1455 : }
11508 :
11509 : /************************************************************************/
11510 : /* GDALGroupGetName() */
11511 : /************************************************************************/
11512 :
11513 : /** Return the name of the group.
11514 : *
11515 : * The returned pointer is valid until hGroup is released.
11516 : *
11517 : * This is the same as the C++ method GDALGroup::GetName().
11518 : */
11519 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11520 : {
11521 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11522 95 : return hGroup->m_poImpl->GetName().c_str();
11523 : }
11524 :
11525 : /************************************************************************/
11526 : /* GDALGroupGetFullName() */
11527 : /************************************************************************/
11528 :
11529 : /** Return the full name of the group.
11530 : *
11531 : * The returned pointer is valid until hGroup is released.
11532 : *
11533 : * This is the same as the C++ method GDALGroup::GetFullName().
11534 : */
11535 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11536 : {
11537 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11538 47 : return hGroup->m_poImpl->GetFullName().c_str();
11539 : }
11540 :
11541 : /************************************************************************/
11542 : /* GDALGroupGetMDArrayNames() */
11543 : /************************************************************************/
11544 :
11545 : /** Return the list of multidimensional array names contained in this group.
11546 : *
11547 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11548 : *
11549 : * @return the array names, to be freed with CSLDestroy()
11550 : */
11551 329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11552 : {
11553 329 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11554 658 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11555 658 : CPLStringList res;
11556 829 : for (const auto &name : names)
11557 : {
11558 500 : res.AddString(name.c_str());
11559 : }
11560 329 : return res.StealList();
11561 : }
11562 :
11563 : /************************************************************************/
11564 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11565 : /************************************************************************/
11566 :
11567 : /** Return the list of multidimensional array full names contained in this
11568 : * group and its subgroups.
11569 : *
11570 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11571 : *
11572 : * @return the array names, to be freed with CSLDestroy()
11573 : *
11574 : * @since 3.11
11575 : */
11576 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11577 : CSLConstList papszGroupOptions,
11578 : CSLConstList papszArrayOptions)
11579 : {
11580 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11581 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11582 2 : papszGroupOptions, papszArrayOptions);
11583 2 : CPLStringList res;
11584 5 : for (const auto &name : names)
11585 : {
11586 4 : res.AddString(name.c_str());
11587 : }
11588 1 : return res.StealList();
11589 : }
11590 :
11591 : /************************************************************************/
11592 : /* GDALGroupOpenMDArray() */
11593 : /************************************************************************/
11594 :
11595 : /** Open and return a multidimensional array.
11596 : *
11597 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11598 : *
11599 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11600 : */
11601 811 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11602 : CSLConstList papszOptions)
11603 : {
11604 811 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11605 811 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11606 2433 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11607 2433 : papszOptions);
11608 811 : if (!array)
11609 30 : return nullptr;
11610 780 : return new GDALMDArrayHS(array);
11611 : }
11612 :
11613 : /************************************************************************/
11614 : /* GDALGroupOpenMDArrayFromFullname() */
11615 : /************************************************************************/
11616 :
11617 : /** Open and return a multidimensional array from its fully qualified name.
11618 : *
11619 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11620 : *
11621 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11622 : *
11623 : * @since GDAL 3.2
11624 : */
11625 17 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11626 : const char *pszFullname,
11627 : CSLConstList papszOptions)
11628 : {
11629 17 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11630 17 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11631 17 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11632 51 : std::string(pszFullname), papszOptions);
11633 17 : if (!array)
11634 2 : return nullptr;
11635 15 : return new GDALMDArrayHS(array);
11636 : }
11637 :
11638 : /************************************************************************/
11639 : /* GDALGroupResolveMDArray() */
11640 : /************************************************************************/
11641 :
11642 : /** Locate an array in a group and its subgroups by name.
11643 : *
11644 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11645 : * @since GDAL 3.2
11646 : */
11647 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11648 : const char *pszStartingPoint,
11649 : CSLConstList papszOptions)
11650 : {
11651 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11652 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11653 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11654 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11655 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11656 19 : if (!array)
11657 2 : return nullptr;
11658 17 : return new GDALMDArrayHS(array);
11659 : }
11660 :
11661 : /************************************************************************/
11662 : /* GDALGroupGetGroupNames() */
11663 : /************************************************************************/
11664 :
11665 : /** Return the list of sub-groups contained in this group.
11666 : *
11667 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11668 : *
11669 : * @return the group names, to be freed with CSLDestroy()
11670 : */
11671 98 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11672 : {
11673 98 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11674 196 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11675 196 : CPLStringList res;
11676 221 : for (const auto &name : names)
11677 : {
11678 123 : res.AddString(name.c_str());
11679 : }
11680 98 : return res.StealList();
11681 : }
11682 :
11683 : /************************************************************************/
11684 : /* GDALGroupOpenGroup() */
11685 : /************************************************************************/
11686 :
11687 : /** Open and return a sub-group.
11688 : *
11689 : * This is the same as the C++ method GDALGroup::OpenGroup().
11690 : *
11691 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11692 : */
11693 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11694 : CSLConstList papszOptions)
11695 : {
11696 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11697 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11698 : auto subGroup =
11699 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11700 163 : if (!subGroup)
11701 30 : return nullptr;
11702 133 : return new GDALGroupHS(subGroup);
11703 : }
11704 :
11705 : /************************************************************************/
11706 : /* GDALGroupGetVectorLayerNames() */
11707 : /************************************************************************/
11708 :
11709 : /** Return the list of layer names contained in this group.
11710 : *
11711 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11712 : *
11713 : * @return the group names, to be freed with CSLDestroy()
11714 : * @since 3.4
11715 : */
11716 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11717 : CSLConstList papszOptions)
11718 : {
11719 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11720 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11721 16 : CPLStringList res;
11722 18 : for (const auto &name : names)
11723 : {
11724 10 : res.AddString(name.c_str());
11725 : }
11726 8 : return res.StealList();
11727 : }
11728 :
11729 : /************************************************************************/
11730 : /* GDALGroupOpenVectorLayer() */
11731 : /************************************************************************/
11732 :
11733 : /** Open and return a vector layer.
11734 : *
11735 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11736 : *
11737 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11738 : * the returned handled if only valid while the parent GDALDatasetH is kept
11739 : * opened.
11740 : *
11741 : * @return the vector layer, or nullptr.
11742 : * @since 3.4
11743 : */
11744 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11745 : const char *pszVectorLayerName,
11746 : CSLConstList papszOptions)
11747 : {
11748 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11749 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11750 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11751 24 : std::string(pszVectorLayerName), papszOptions));
11752 : }
11753 :
11754 : /************************************************************************/
11755 : /* GDALGroupOpenMDArrayFromFullname() */
11756 : /************************************************************************/
11757 :
11758 : /** Open and return a sub-group from its fully qualified name.
11759 : *
11760 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11761 : *
11762 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11763 : *
11764 : * @since GDAL 3.2
11765 : */
11766 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11767 : const char *pszFullname,
11768 : CSLConstList papszOptions)
11769 : {
11770 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11771 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11772 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11773 9 : std::string(pszFullname), papszOptions);
11774 3 : if (!subGroup)
11775 2 : return nullptr;
11776 1 : return new GDALGroupHS(subGroup);
11777 : }
11778 :
11779 : /************************************************************************/
11780 : /* GDALGroupGetDimensions() */
11781 : /************************************************************************/
11782 :
11783 : /** Return the list of dimensions contained in this group and used by its
11784 : * arrays.
11785 : *
11786 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11787 : * array itself needs to be freed, CPLFree() should be called (and
11788 : * GDALDimensionRelease() on individual array members).
11789 : *
11790 : * This is the same as the C++ method GDALGroup::GetDimensions().
11791 : *
11792 : * @param hGroup Group.
11793 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11794 : * @param papszOptions Driver specific options determining how dimensions
11795 : * should be retrieved. Pass nullptr for default behavior.
11796 : *
11797 : * @return an array of *pnCount dimensions.
11798 : */
11799 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11800 : CSLConstList papszOptions)
11801 : {
11802 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11803 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11804 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11805 : auto ret = static_cast<GDALDimensionH *>(
11806 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11807 230 : for (size_t i = 0; i < dims.size(); i++)
11808 : {
11809 157 : ret[i] = new GDALDimensionHS(dims[i]);
11810 : }
11811 73 : *pnCount = dims.size();
11812 73 : return ret;
11813 : }
11814 :
11815 : /************************************************************************/
11816 : /* GDALGroupGetAttribute() */
11817 : /************************************************************************/
11818 :
11819 : /** Return an attribute by its name.
11820 : *
11821 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11822 : *
11823 : * The returned attribute must be freed with GDALAttributeRelease().
11824 : */
11825 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11826 : {
11827 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11828 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11829 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11830 80 : if (attr)
11831 76 : return new GDALAttributeHS(attr);
11832 4 : return nullptr;
11833 : }
11834 :
11835 : /************************************************************************/
11836 : /* GDALGroupGetAttributes() */
11837 : /************************************************************************/
11838 :
11839 : /** Return the list of attributes contained in this group.
11840 : *
11841 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11842 : * array itself needs to be freed, CPLFree() should be called (and
11843 : * GDALAttributeRelease() on individual array members).
11844 : *
11845 : * This is the same as the C++ method GDALGroup::GetAttributes().
11846 : *
11847 : * @param hGroup Group.
11848 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11849 : * @param papszOptions Driver specific options determining how attributes
11850 : * should be retrieved. Pass nullptr for default behavior.
11851 : *
11852 : * @return an array of *pnCount attributes.
11853 : */
11854 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11855 : CSLConstList papszOptions)
11856 : {
11857 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11858 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11859 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11860 : auto ret = static_cast<GDALAttributeH *>(
11861 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11862 229 : for (size_t i = 0; i < attrs.size(); i++)
11863 : {
11864 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11865 : }
11866 71 : *pnCount = attrs.size();
11867 71 : return ret;
11868 : }
11869 :
11870 : /************************************************************************/
11871 : /* GDALGroupGetStructuralInfo() */
11872 : /************************************************************************/
11873 :
11874 : /** Return structural information on the group.
11875 : *
11876 : * This may be the compression, etc..
11877 : *
11878 : * The return value should not be freed and is valid until GDALGroup is
11879 : * released or this function called again.
11880 : *
11881 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11882 : */
11883 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11884 : {
11885 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11886 4 : return hGroup->m_poImpl->GetStructuralInfo();
11887 : }
11888 :
11889 : /************************************************************************/
11890 : /* GDALGroupGetDataTypeCount() */
11891 : /************************************************************************/
11892 :
11893 : /** Return the number of data types associated with the group
11894 : * (typically enumerations).
11895 : *
11896 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11897 : *
11898 : * @since 3.12
11899 : */
11900 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11901 : {
11902 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11903 4 : return hGroup->m_poImpl->GetDataTypes().size();
11904 : }
11905 :
11906 : /************************************************************************/
11907 : /* GDALGroupGetDataType() */
11908 : /************************************************************************/
11909 :
11910 : /** Return one of the data types associated with the group.
11911 : *
11912 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11913 : *
11914 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11915 : * or nullptr in case of error.
11916 : * @since 3.12
11917 : */
11918 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11919 : {
11920 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11921 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11922 0 : return nullptr;
11923 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11924 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11925 : }
11926 :
11927 : /************************************************************************/
11928 : /* GDALReleaseAttributes() */
11929 : /************************************************************************/
11930 :
11931 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11932 : *
11933 : * @param attributes return pointer of above methods
11934 : * @param nCount *pnCount value returned by above methods
11935 : */
11936 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11937 : {
11938 418 : for (size_t i = 0; i < nCount; i++)
11939 : {
11940 288 : delete attributes[i];
11941 : }
11942 130 : CPLFree(attributes);
11943 130 : }
11944 :
11945 : /************************************************************************/
11946 : /* GDALGroupCreateGroup() */
11947 : /************************************************************************/
11948 :
11949 : /** Create a sub-group within a group.
11950 : *
11951 : * This is the same as the C++ method GDALGroup::CreateGroup().
11952 : *
11953 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11954 : */
11955 179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11956 : CSLConstList papszOptions)
11957 : {
11958 179 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11959 179 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11960 537 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11961 537 : papszOptions);
11962 179 : if (!ret)
11963 49 : return nullptr;
11964 130 : return new GDALGroupHS(ret);
11965 : }
11966 :
11967 : /************************************************************************/
11968 : /* GDALGroupDeleteGroup() */
11969 : /************************************************************************/
11970 :
11971 : /** Delete a sub-group from a group.
11972 : *
11973 : * After this call, if a previously obtained instance of the deleted object
11974 : * is still alive, no method other than for freeing it should be invoked.
11975 : *
11976 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11977 : *
11978 : * @return true in case of success.
11979 : * @since GDAL 3.8
11980 : */
11981 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11982 : CSLConstList papszOptions)
11983 : {
11984 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11985 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11986 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11987 20 : papszOptions);
11988 : }
11989 :
11990 : /************************************************************************/
11991 : /* GDALGroupCreateDimension() */
11992 : /************************************************************************/
11993 :
11994 : /** Create a dimension within a group.
11995 : *
11996 : * This is the same as the C++ method GDALGroup::CreateDimension().
11997 : *
11998 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11999 : */
12000 669 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12001 : const char *pszType,
12002 : const char *pszDirection, GUInt64 nSize,
12003 : CSLConstList papszOptions)
12004 : {
12005 669 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12006 669 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12007 669 : auto ret = hGroup->m_poImpl->CreateDimension(
12008 1338 : std::string(pszName), std::string(pszType ? pszType : ""),
12009 2676 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12010 669 : if (!ret)
12011 9 : return nullptr;
12012 660 : return new GDALDimensionHS(ret);
12013 : }
12014 :
12015 : /************************************************************************/
12016 : /* GDALGroupCreateMDArray() */
12017 : /************************************************************************/
12018 :
12019 : /** Create a multidimensional array within a group.
12020 : *
12021 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12022 : *
12023 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12024 : */
12025 612 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12026 : size_t nDimensions,
12027 : GDALDimensionH *pahDimensions,
12028 : GDALExtendedDataTypeH hEDT,
12029 : CSLConstList papszOptions)
12030 : {
12031 612 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12032 612 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12033 612 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12034 1224 : std::vector<std::shared_ptr<GDALDimension>> dims;
12035 612 : dims.reserve(nDimensions);
12036 1443 : for (size_t i = 0; i < nDimensions; i++)
12037 831 : dims.push_back(pahDimensions[i]->m_poImpl);
12038 1836 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12039 1836 : *(hEDT->m_poImpl), papszOptions);
12040 612 : if (!ret)
12041 65 : return nullptr;
12042 547 : return new GDALMDArrayHS(ret);
12043 : }
12044 :
12045 : /************************************************************************/
12046 : /* GDALGroupDeleteMDArray() */
12047 : /************************************************************************/
12048 :
12049 : /** Delete an array from a group.
12050 : *
12051 : * After this call, if a previously obtained instance of the deleted object
12052 : * is still alive, no method other than for freeing it should be invoked.
12053 : *
12054 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12055 : *
12056 : * @return true in case of success.
12057 : * @since GDAL 3.8
12058 : */
12059 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12060 : CSLConstList papszOptions)
12061 : {
12062 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12063 20 : VALIDATE_POINTER1(pszName, __func__, false);
12064 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12065 : }
12066 :
12067 : /************************************************************************/
12068 : /* GDALGroupCreateAttribute() */
12069 : /************************************************************************/
12070 :
12071 : /** Create a attribute within a group.
12072 : *
12073 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12074 : *
12075 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12076 : */
12077 125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12078 : size_t nDimensions,
12079 : const GUInt64 *panDimensions,
12080 : GDALExtendedDataTypeH hEDT,
12081 : CSLConstList papszOptions)
12082 : {
12083 125 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12084 125 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12085 250 : std::vector<GUInt64> dims;
12086 125 : dims.reserve(nDimensions);
12087 175 : for (size_t i = 0; i < nDimensions; i++)
12088 50 : dims.push_back(panDimensions[i]);
12089 125 : auto ret = hGroup->m_poImpl->CreateAttribute(
12090 375 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12091 125 : if (!ret)
12092 14 : return nullptr;
12093 111 : return new GDALAttributeHS(ret);
12094 : }
12095 :
12096 : /************************************************************************/
12097 : /* GDALGroupDeleteAttribute() */
12098 : /************************************************************************/
12099 :
12100 : /** Delete an attribute from a group.
12101 : *
12102 : * After this call, if a previously obtained instance of the deleted object
12103 : * is still alive, no method other than for freeing it should be invoked.
12104 : *
12105 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12106 : *
12107 : * @return true in case of success.
12108 : * @since GDAL 3.8
12109 : */
12110 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12111 : CSLConstList papszOptions)
12112 : {
12113 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12114 25 : VALIDATE_POINTER1(pszName, __func__, false);
12115 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12116 25 : papszOptions);
12117 : }
12118 :
12119 : /************************************************************************/
12120 : /* GDALGroupRename() */
12121 : /************************************************************************/
12122 :
12123 : /** Rename the group.
12124 : *
12125 : * This is not implemented by all drivers.
12126 : *
12127 : * Drivers known to implement it: MEM, netCDF.
12128 : *
12129 : * This is the same as the C++ method GDALGroup::Rename()
12130 : *
12131 : * @return true in case of success
12132 : * @since GDAL 3.8
12133 : */
12134 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12135 : {
12136 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12137 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12138 45 : return hGroup->m_poImpl->Rename(pszNewName);
12139 : }
12140 :
12141 : /************************************************************************/
12142 : /* GDALGroupSubsetDimensionFromSelection() */
12143 : /************************************************************************/
12144 :
12145 : /** Return a virtual group whose one dimension has been subset according to a
12146 : * selection.
12147 : *
12148 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12149 : *
12150 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12151 : */
12152 : GDALGroupH
12153 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12154 : const char *pszSelection,
12155 : CPL_UNUSED CSLConstList papszOptions)
12156 : {
12157 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12158 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12159 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12160 42 : std::string(pszSelection));
12161 14 : if (!hNewGroup)
12162 8 : return nullptr;
12163 6 : return new GDALGroupHS(hNewGroup);
12164 : }
12165 :
12166 : /************************************************************************/
12167 : /* GDALMDArrayRelease() */
12168 : /************************************************************************/
12169 :
12170 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12171 : *
12172 : * Note: when applied on a object coming from a driver, this does not
12173 : * destroy the object in the file, database, etc...
12174 : */
12175 2057 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12176 : {
12177 2057 : delete hMDArray;
12178 2057 : }
12179 :
12180 : /************************************************************************/
12181 : /* GDALMDArrayGetName() */
12182 : /************************************************************************/
12183 :
12184 : /** Return array name.
12185 : *
12186 : * This is the same as the C++ method GDALMDArray::GetName()
12187 : */
12188 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12189 : {
12190 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12191 83 : return hArray->m_poImpl->GetName().c_str();
12192 : }
12193 :
12194 : /************************************************************************/
12195 : /* GDALMDArrayGetFullName() */
12196 : /************************************************************************/
12197 :
12198 : /** Return array full name.
12199 : *
12200 : * This is the same as the C++ method GDALMDArray::GetFullName()
12201 : */
12202 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12203 : {
12204 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12205 50 : return hArray->m_poImpl->GetFullName().c_str();
12206 : }
12207 :
12208 : /************************************************************************/
12209 : /* GDALMDArrayGetName() */
12210 : /************************************************************************/
12211 :
12212 : /** Return the total number of values in the array.
12213 : *
12214 : * This is the same as the C++ method
12215 : * GDALAbstractMDArray::GetTotalElementsCount()
12216 : */
12217 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12218 : {
12219 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12220 6 : return hArray->m_poImpl->GetTotalElementsCount();
12221 : }
12222 :
12223 : /************************************************************************/
12224 : /* GDALMDArrayGetDimensionCount() */
12225 : /************************************************************************/
12226 :
12227 : /** Return the number of dimensions.
12228 : *
12229 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12230 : */
12231 10469 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12232 : {
12233 10469 : VALIDATE_POINTER1(hArray, __func__, 0);
12234 10469 : return hArray->m_poImpl->GetDimensionCount();
12235 : }
12236 :
12237 : /************************************************************************/
12238 : /* GDALMDArrayGetDimensions() */
12239 : /************************************************************************/
12240 :
12241 : /** Return the dimensions of the array
12242 : *
12243 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12244 : * array itself needs to be freed, CPLFree() should be called (and
12245 : * GDALDimensionRelease() on individual array members).
12246 : *
12247 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12248 : *
12249 : * @param hArray Array.
12250 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12251 : *
12252 : * @return an array of *pnCount dimensions.
12253 : */
12254 2339 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12255 : {
12256 2339 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12257 2339 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12258 2339 : const auto &dims(hArray->m_poImpl->GetDimensions());
12259 : auto ret = static_cast<GDALDimensionH *>(
12260 2339 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12261 6587 : for (size_t i = 0; i < dims.size(); i++)
12262 : {
12263 4248 : ret[i] = new GDALDimensionHS(dims[i]);
12264 : }
12265 2339 : *pnCount = dims.size();
12266 2339 : return ret;
12267 : }
12268 :
12269 : /************************************************************************/
12270 : /* GDALReleaseDimensions() */
12271 : /************************************************************************/
12272 :
12273 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12274 : *
12275 : * @param dims return pointer of above methods
12276 : * @param nCount *pnCount value returned by above methods
12277 : */
12278 2412 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12279 : {
12280 6817 : for (size_t i = 0; i < nCount; i++)
12281 : {
12282 4405 : delete dims[i];
12283 : }
12284 2412 : CPLFree(dims);
12285 2412 : }
12286 :
12287 : /************************************************************************/
12288 : /* GDALMDArrayGetDataType() */
12289 : /************************************************************************/
12290 :
12291 : /** Return the data type
12292 : *
12293 : * The return must be freed with GDALExtendedDataTypeRelease().
12294 : */
12295 3954 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12296 : {
12297 3954 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12298 : return new GDALExtendedDataTypeHS(
12299 3954 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12300 : }
12301 :
12302 : /************************************************************************/
12303 : /* GDALMDArrayRead() */
12304 : /************************************************************************/
12305 :
12306 : /** Read part or totality of a multidimensional array.
12307 : *
12308 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12309 : *
12310 : * @return TRUE in case of success.
12311 : */
12312 1976 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12313 : const size_t *count, const GInt64 *arrayStep,
12314 : const GPtrDiff_t *bufferStride,
12315 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12316 : const void *pDstBufferAllocStart,
12317 : size_t nDstBufferAllocSize)
12318 : {
12319 1976 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12320 1976 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12321 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12322 : {
12323 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12324 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12325 : }
12326 1976 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12327 1976 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12328 3952 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12329 1976 : *(bufferDataType->m_poImpl), pDstBuffer,
12330 1975 : pDstBufferAllocStart, nDstBufferAllocSize);
12331 : }
12332 :
12333 : /************************************************************************/
12334 : /* GDALMDArrayWrite() */
12335 : /************************************************************************/
12336 :
12337 : /** Write part or totality of a multidimensional array.
12338 : *
12339 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12340 : *
12341 : * @return TRUE in case of success.
12342 : */
12343 560 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12344 : const size_t *count, const GInt64 *arrayStep,
12345 : const GPtrDiff_t *bufferStride,
12346 : GDALExtendedDataTypeH bufferDataType,
12347 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12348 : size_t nSrcBufferAllocSize)
12349 : {
12350 560 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12351 560 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12352 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12353 : {
12354 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12355 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12356 : }
12357 560 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12358 560 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12359 1120 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12360 560 : bufferStride, *(bufferDataType->m_poImpl),
12361 : pSrcBuffer, pSrcBufferAllocStart,
12362 560 : nSrcBufferAllocSize);
12363 : }
12364 :
12365 : /************************************************************************/
12366 : /* GDALMDArrayAdviseRead() */
12367 : /************************************************************************/
12368 :
12369 : /** Advise driver of upcoming read requests.
12370 : *
12371 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12372 : *
12373 : * @return TRUE in case of success.
12374 : *
12375 : * @since GDAL 3.2
12376 : */
12377 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12378 : const size_t *count)
12379 : {
12380 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12381 : }
12382 :
12383 : /************************************************************************/
12384 : /* GDALMDArrayAdviseReadEx() */
12385 : /************************************************************************/
12386 :
12387 : /** Advise driver of upcoming read requests.
12388 : *
12389 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12390 : *
12391 : * @return TRUE in case of success.
12392 : *
12393 : * @since GDAL 3.4
12394 : */
12395 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12396 : const size_t *count, CSLConstList papszOptions)
12397 : {
12398 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12399 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12400 : }
12401 :
12402 : /************************************************************************/
12403 : /* GDALMDArrayGetAttribute() */
12404 : /************************************************************************/
12405 :
12406 : /** Return an attribute by its name.
12407 : *
12408 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12409 : *
12410 : * The returned attribute must be freed with GDALAttributeRelease().
12411 : */
12412 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12413 : {
12414 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12415 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12416 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12417 120 : if (attr)
12418 111 : return new GDALAttributeHS(attr);
12419 9 : return nullptr;
12420 : }
12421 :
12422 : /************************************************************************/
12423 : /* GDALMDArrayGetAttributes() */
12424 : /************************************************************************/
12425 :
12426 : /** Return the list of attributes contained in this array.
12427 : *
12428 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12429 : * array itself needs to be freed, CPLFree() should be called (and
12430 : * GDALAttributeRelease() on individual array members).
12431 : *
12432 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12433 : *
12434 : * @param hArray Array.
12435 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12436 : * @param papszOptions Driver specific options determining how attributes
12437 : * should be retrieved. Pass nullptr for default behavior.
12438 : *
12439 : * @return an array of *pnCount attributes.
12440 : */
12441 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12442 : CSLConstList papszOptions)
12443 : {
12444 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12445 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12446 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12447 : auto ret = static_cast<GDALAttributeH *>(
12448 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12449 189 : for (size_t i = 0; i < attrs.size(); i++)
12450 : {
12451 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12452 : }
12453 59 : *pnCount = attrs.size();
12454 59 : return ret;
12455 : }
12456 :
12457 : /************************************************************************/
12458 : /* GDALMDArrayCreateAttribute() */
12459 : /************************************************************************/
12460 :
12461 : /** Create a attribute within an array.
12462 : *
12463 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12464 : *
12465 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12466 : */
12467 162 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12468 : const char *pszName,
12469 : size_t nDimensions,
12470 : const GUInt64 *panDimensions,
12471 : GDALExtendedDataTypeH hEDT,
12472 : CSLConstList papszOptions)
12473 : {
12474 162 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12475 162 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12476 162 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12477 324 : std::vector<GUInt64> dims;
12478 162 : dims.reserve(nDimensions);
12479 197 : for (size_t i = 0; i < nDimensions; i++)
12480 35 : dims.push_back(panDimensions[i]);
12481 162 : auto ret = hArray->m_poImpl->CreateAttribute(
12482 486 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12483 162 : if (!ret)
12484 9 : return nullptr;
12485 153 : return new GDALAttributeHS(ret);
12486 : }
12487 :
12488 : /************************************************************************/
12489 : /* GDALMDArrayDeleteAttribute() */
12490 : /************************************************************************/
12491 :
12492 : /** Delete an attribute from an array.
12493 : *
12494 : * After this call, if a previously obtained instance of the deleted object
12495 : * is still alive, no method other than for freeing it should be invoked.
12496 : *
12497 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12498 : *
12499 : * @return true in case of success.
12500 : * @since GDAL 3.8
12501 : */
12502 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12503 : CSLConstList papszOptions)
12504 : {
12505 24 : VALIDATE_POINTER1(hArray, __func__, false);
12506 24 : VALIDATE_POINTER1(pszName, __func__, false);
12507 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12508 24 : papszOptions);
12509 : }
12510 :
12511 : /************************************************************************/
12512 : /* GDALMDArrayGetRawNoDataValue() */
12513 : /************************************************************************/
12514 :
12515 : /** Return the nodata value as a "raw" value.
12516 : *
12517 : * The value returned might be nullptr in case of no nodata value. When
12518 : * a nodata value is registered, a non-nullptr will be returned whose size in
12519 : * bytes is GetDataType().GetSize().
12520 : *
12521 : * The returned value should not be modified or freed.
12522 : *
12523 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12524 : *
12525 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12526 : */
12527 77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12528 : {
12529 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12530 77 : return hArray->m_poImpl->GetRawNoDataValue();
12531 : }
12532 :
12533 : /************************************************************************/
12534 : /* GDALMDArrayGetNoDataValueAsDouble() */
12535 : /************************************************************************/
12536 :
12537 : /** Return the nodata value as a double.
12538 : *
12539 : * The value returned might be nullptr in case of no nodata value. When
12540 : * a nodata value is registered, a non-nullptr will be returned whose size in
12541 : * bytes is GetDataType().GetSize().
12542 : *
12543 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12544 : *
12545 : * @param hArray Array handle.
12546 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12547 : * if a nodata value exists and can be converted to double. Might be nullptr.
12548 : *
12549 : * @return the nodata value as a double. A 0.0 value might also indicate the
12550 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12551 : * will be set to false then).
12552 : */
12553 121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12554 : int *pbHasNoDataValue)
12555 : {
12556 121 : VALIDATE_POINTER1(hArray, __func__, 0);
12557 121 : bool bHasNodataValue = false;
12558 121 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12559 121 : if (pbHasNoDataValue)
12560 121 : *pbHasNoDataValue = bHasNodataValue;
12561 121 : return ret;
12562 : }
12563 :
12564 : /************************************************************************/
12565 : /* GDALMDArrayGetNoDataValueAsInt64() */
12566 : /************************************************************************/
12567 :
12568 : /** Return the nodata value as a Int64.
12569 : *
12570 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12571 : *
12572 : * @param hArray Array handle.
12573 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12574 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12575 : *
12576 : * @return the nodata value as a Int64.
12577 : * @since GDAL 3.5
12578 : */
12579 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12580 : int *pbHasNoDataValue)
12581 : {
12582 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12583 11 : bool bHasNodataValue = false;
12584 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12585 11 : if (pbHasNoDataValue)
12586 11 : *pbHasNoDataValue = bHasNodataValue;
12587 11 : return ret;
12588 : }
12589 :
12590 : /************************************************************************/
12591 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12592 : /************************************************************************/
12593 :
12594 : /** Return the nodata value as a UInt64.
12595 : *
12596 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12597 : *
12598 : * @param hArray Array handle.
12599 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12600 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12601 : *
12602 : * @return the nodata value as a UInt64.
12603 : * @since GDAL 3.5
12604 : */
12605 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12606 : int *pbHasNoDataValue)
12607 : {
12608 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12609 7 : bool bHasNodataValue = false;
12610 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12611 7 : if (pbHasNoDataValue)
12612 7 : *pbHasNoDataValue = bHasNodataValue;
12613 7 : return ret;
12614 : }
12615 :
12616 : /************************************************************************/
12617 : /* GDALMDArraySetRawNoDataValue() */
12618 : /************************************************************************/
12619 :
12620 : /** Set the nodata value as a "raw" value.
12621 : *
12622 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12623 : * void*).
12624 : *
12625 : * @return TRUE in case of success.
12626 : */
12627 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12628 : {
12629 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12630 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12631 : }
12632 :
12633 : /************************************************************************/
12634 : /* GDALMDArraySetNoDataValueAsDouble() */
12635 : /************************************************************************/
12636 :
12637 : /** Set the nodata value as a double.
12638 : *
12639 : * If the natural data type of the attribute/array is not double, type
12640 : * conversion will occur to the type returned by GetDataType().
12641 : *
12642 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12643 : *
12644 : * @return TRUE in case of success.
12645 : */
12646 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12647 : {
12648 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12649 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12650 : }
12651 :
12652 : /************************************************************************/
12653 : /* GDALMDArraySetNoDataValueAsInt64() */
12654 : /************************************************************************/
12655 :
12656 : /** Set the nodata value as a Int64.
12657 : *
12658 : * If the natural data type of the attribute/array is not Int64, type conversion
12659 : * will occur to the type returned by GetDataType().
12660 : *
12661 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12662 : *
12663 : * @return TRUE in case of success.
12664 : * @since GDAL 3.5
12665 : */
12666 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12667 : {
12668 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12669 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12670 : }
12671 :
12672 : /************************************************************************/
12673 : /* GDALMDArraySetNoDataValueAsUInt64() */
12674 : /************************************************************************/
12675 :
12676 : /** Set the nodata value as a UInt64.
12677 : *
12678 : * If the natural data type of the attribute/array is not UInt64, type
12679 : * conversion will occur to the type returned by GetDataType().
12680 : *
12681 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12682 : *
12683 : * @return TRUE in case of success.
12684 : * @since GDAL 3.5
12685 : */
12686 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12687 : uint64_t nNoDataValue)
12688 : {
12689 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12690 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12691 : }
12692 :
12693 : /************************************************************************/
12694 : /* GDALMDArrayResize() */
12695 : /************************************************************************/
12696 :
12697 : /** Resize an array to new dimensions.
12698 : *
12699 : * Not all drivers may allow this operation, and with restrictions (e.g.
12700 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12701 : *
12702 : * Resizing a dimension used in other arrays will cause those other arrays
12703 : * to be resized.
12704 : *
12705 : * This is the same as the C++ method GDALMDArray::Resize().
12706 : *
12707 : * @param hArray Array.
12708 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12709 : * new size of each indexing dimension.
12710 : * @param papszOptions Options. (Driver specific)
12711 : * @return true in case of success.
12712 : * @since GDAL 3.7
12713 : */
12714 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12715 : CSLConstList papszOptions)
12716 : {
12717 42 : VALIDATE_POINTER1(hArray, __func__, false);
12718 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12719 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12720 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12721 : {
12722 83 : anNewDimSizes[i] = panNewDimSizes[i];
12723 : }
12724 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12725 : }
12726 :
12727 : /************************************************************************/
12728 : /* GDALMDArraySetScale() */
12729 : /************************************************************************/
12730 :
12731 : /** Set the scale value to apply to raw values.
12732 : *
12733 : * unscaled_value = raw_value * GetScale() + GetOffset()
12734 : *
12735 : * This is the same as the C++ method GDALMDArray::SetScale().
12736 : *
12737 : * @return TRUE in case of success.
12738 : */
12739 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12740 : {
12741 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12742 0 : return hArray->m_poImpl->SetScale(dfScale);
12743 : }
12744 :
12745 : /************************************************************************/
12746 : /* GDALMDArraySetScaleEx() */
12747 : /************************************************************************/
12748 :
12749 : /** Set the scale value to apply to raw values.
12750 : *
12751 : * unscaled_value = raw_value * GetScale() + GetOffset()
12752 : *
12753 : * This is the same as the C++ method GDALMDArray::SetScale().
12754 : *
12755 : * @return TRUE in case of success.
12756 : * @since GDAL 3.3
12757 : */
12758 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12759 : GDALDataType eStorageType)
12760 : {
12761 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12762 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12763 : }
12764 :
12765 : /************************************************************************/
12766 : /* GDALMDArraySetOffset() */
12767 : /************************************************************************/
12768 :
12769 : /** Set the scale value to apply to raw values.
12770 : *
12771 : * unscaled_value = raw_value * GetScale() + GetOffset()
12772 : *
12773 : * This is the same as the C++ method GDALMDArray::SetOffset().
12774 : *
12775 : * @return TRUE in case of success.
12776 : */
12777 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12778 : {
12779 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12780 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12781 : }
12782 :
12783 : /************************************************************************/
12784 : /* GDALMDArraySetOffsetEx() */
12785 : /************************************************************************/
12786 :
12787 : /** Set the scale value to apply to raw values.
12788 : *
12789 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12790 : *
12791 : * This is the same as the C++ method GDALMDArray::SetOffset().
12792 : *
12793 : * @return TRUE in case of success.
12794 : * @since GDAL 3.3
12795 : */
12796 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12797 : GDALDataType eStorageType)
12798 : {
12799 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12800 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12801 : }
12802 :
12803 : /************************************************************************/
12804 : /* GDALMDArrayGetScale() */
12805 : /************************************************************************/
12806 :
12807 : /** Get the scale value to apply to raw values.
12808 : *
12809 : * unscaled_value = raw_value * GetScale() + GetOffset()
12810 : *
12811 : * This is the same as the C++ method GDALMDArray::GetScale().
12812 : *
12813 : * @return the scale value
12814 : */
12815 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12816 : {
12817 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12818 105 : bool bHasValue = false;
12819 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12820 105 : if (pbHasValue)
12821 105 : *pbHasValue = bHasValue;
12822 105 : return dfRet;
12823 : }
12824 :
12825 : /************************************************************************/
12826 : /* GDALMDArrayGetScaleEx() */
12827 : /************************************************************************/
12828 :
12829 : /** Get the scale value to apply to raw values.
12830 : *
12831 : * unscaled_value = raw_value * GetScale() + GetScale()
12832 : *
12833 : * This is the same as the C++ method GDALMDArray::GetScale().
12834 : *
12835 : * @return the scale value
12836 : * @since GDAL 3.3
12837 : */
12838 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12839 : GDALDataType *peStorageType)
12840 : {
12841 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12842 5 : bool bHasValue = false;
12843 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12844 5 : if (pbHasValue)
12845 5 : *pbHasValue = bHasValue;
12846 5 : return dfRet;
12847 : }
12848 :
12849 : /************************************************************************/
12850 : /* GDALMDArrayGetOffset() */
12851 : /************************************************************************/
12852 :
12853 : /** Get the scale value to apply to raw values.
12854 : *
12855 : * unscaled_value = raw_value * GetScale() + GetOffset()
12856 : *
12857 : * This is the same as the C++ method GDALMDArray::GetOffset().
12858 : *
12859 : * @return the scale value
12860 : */
12861 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12862 : {
12863 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12864 102 : bool bHasValue = false;
12865 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12866 102 : if (pbHasValue)
12867 102 : *pbHasValue = bHasValue;
12868 102 : return dfRet;
12869 : }
12870 :
12871 : /************************************************************************/
12872 : /* GDALMDArrayGetOffsetEx() */
12873 : /************************************************************************/
12874 :
12875 : /** Get the scale value to apply to raw values.
12876 : *
12877 : * unscaled_value = raw_value * GetScale() + GetOffset()
12878 : *
12879 : * This is the same as the C++ method GDALMDArray::GetOffset().
12880 : *
12881 : * @return the scale value
12882 : * @since GDAL 3.3
12883 : */
12884 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12885 : GDALDataType *peStorageType)
12886 : {
12887 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12888 5 : bool bHasValue = false;
12889 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12890 5 : if (pbHasValue)
12891 5 : *pbHasValue = bHasValue;
12892 5 : return dfRet;
12893 : }
12894 :
12895 : /************************************************************************/
12896 : /* GDALMDArrayGetBlockSize() */
12897 : /************************************************************************/
12898 :
12899 : /** Return the "natural" block size of the array along all dimensions.
12900 : *
12901 : * Some drivers might organize the array in tiles/blocks and reading/writing
12902 : * aligned on those tile/block boundaries will be more efficient.
12903 : *
12904 : * The returned number of elements in the vector is the same as
12905 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12906 : * the natural block size along the considered dimension.
12907 : * "Flat" arrays will typically return a vector of values set to 0.
12908 : *
12909 : * The default implementation will return a vector of values set to 0.
12910 : *
12911 : * This method is used by GetProcessingChunkSize().
12912 : *
12913 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12914 : * theoretical case of a 32-bit platform, this might exceed its size_t
12915 : * allocation capabilities.
12916 : *
12917 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12918 : *
12919 : * @return the block size, in number of elements along each dimension.
12920 : */
12921 95 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12922 : {
12923 95 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12924 95 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12925 95 : auto res = hArray->m_poImpl->GetBlockSize();
12926 95 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12927 293 : for (size_t i = 0; i < res.size(); i++)
12928 : {
12929 198 : ret[i] = res[i];
12930 : }
12931 95 : *pnCount = res.size();
12932 95 : return ret;
12933 : }
12934 :
12935 : /***********************************************************************/
12936 : /* GDALMDArrayGetProcessingChunkSize() */
12937 : /************************************************************************/
12938 :
12939 : /** \brief Return an optimal chunk size for read/write operations, given the
12940 : * natural block size and memory constraints specified.
12941 : *
12942 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12943 : * multiple of those returned by GetBlockSize() (unless the block define by
12944 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12945 : * returned by this method).
12946 : *
12947 : * This is the same as the C++ method
12948 : * GDALAbstractMDArray::GetProcessingChunkSize().
12949 : *
12950 : * @param hArray Array.
12951 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12952 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12953 : * chunk.
12954 : *
12955 : * @return the chunk size, in number of elements along each dimension.
12956 : */
12957 :
12958 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12959 : size_t nMaxChunkMemory)
12960 : {
12961 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12962 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12963 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12964 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12965 3 : for (size_t i = 0; i < res.size(); i++)
12966 : {
12967 2 : ret[i] = res[i];
12968 : }
12969 1 : *pnCount = res.size();
12970 1 : return ret;
12971 : }
12972 :
12973 : /************************************************************************/
12974 : /* GDALMDArrayGetStructuralInfo() */
12975 : /************************************************************************/
12976 :
12977 : /** Return structural information on the array.
12978 : *
12979 : * This may be the compression, etc..
12980 : *
12981 : * The return value should not be freed and is valid until GDALMDArray is
12982 : * released or this function called again.
12983 : *
12984 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12985 : */
12986 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12987 : {
12988 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12989 15 : return hArray->m_poImpl->GetStructuralInfo();
12990 : }
12991 :
12992 : /************************************************************************/
12993 : /* GDALMDArrayGetView() */
12994 : /************************************************************************/
12995 :
12996 : /** Return a view of the array using slicing or field access.
12997 : *
12998 : * The returned object should be released with GDALMDArrayRelease().
12999 : *
13000 : * This is the same as the C++ method GDALMDArray::GetView().
13001 : */
13002 433 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13003 : {
13004 433 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13005 433 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13006 1299 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13007 433 : if (!sliced)
13008 22 : return nullptr;
13009 411 : return new GDALMDArrayHS(sliced);
13010 : }
13011 :
13012 : /************************************************************************/
13013 : /* GDALMDArrayTranspose() */
13014 : /************************************************************************/
13015 :
13016 : /** Return a view of the array whose axis have been reordered.
13017 : *
13018 : * The returned object should be released with GDALMDArrayRelease().
13019 : *
13020 : * This is the same as the C++ method GDALMDArray::Transpose().
13021 : */
13022 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13023 : const int *panMapNewAxisToOldAxis)
13024 : {
13025 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13026 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13027 44 : if (nNewAxisCount)
13028 : {
13029 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13030 : nNewAxisCount * sizeof(int));
13031 : }
13032 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13033 44 : if (!reordered)
13034 7 : return nullptr;
13035 37 : return new GDALMDArrayHS(reordered);
13036 : }
13037 :
13038 : /************************************************************************/
13039 : /* GDALMDArrayGetUnscaled() */
13040 : /************************************************************************/
13041 :
13042 : /** Return an array that is the unscaled version of the current one.
13043 : *
13044 : * That is each value of the unscaled array will be
13045 : * unscaled_value = raw_value * GetScale() + GetOffset()
13046 : *
13047 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13048 : * from unscaled values to raw values.
13049 : *
13050 : * The returned object should be released with GDALMDArrayRelease().
13051 : *
13052 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13053 : */
13054 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13055 : {
13056 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13057 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13058 13 : if (!unscaled)
13059 0 : return nullptr;
13060 13 : return new GDALMDArrayHS(unscaled);
13061 : }
13062 :
13063 : /************************************************************************/
13064 : /* GDALMDArrayGetMask() */
13065 : /************************************************************************/
13066 :
13067 : /** Return an array that is a mask for the current array
13068 : *
13069 : * This array will be of type Byte, with values set to 0 to indicate invalid
13070 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13071 : *
13072 : * The returned object should be released with GDALMDArrayRelease().
13073 : *
13074 : * This is the same as the C++ method GDALMDArray::GetMask().
13075 : */
13076 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13077 : {
13078 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13079 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13080 35 : if (!unscaled)
13081 7 : return nullptr;
13082 28 : return new GDALMDArrayHS(unscaled);
13083 : }
13084 :
13085 : /************************************************************************/
13086 : /* GDALMDArrayGetResampled() */
13087 : /************************************************************************/
13088 :
13089 : /** Return an array that is a resampled / reprojected view of the current array
13090 : *
13091 : * This is the same as the C++ method GDALMDArray::GetResampled().
13092 : *
13093 : * Currently this method can only resample along the last 2 dimensions, unless
13094 : * orthorectifying a NASA EMIT dataset.
13095 : *
13096 : * The returned object should be released with GDALMDArrayRelease().
13097 : *
13098 : * @since 3.4
13099 : */
13100 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13101 : const GDALDimensionH *pahNewDims,
13102 : GDALRIOResampleAlg resampleAlg,
13103 : OGRSpatialReferenceH hTargetSRS,
13104 : CSLConstList papszOptions)
13105 : {
13106 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13107 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13108 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13109 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13110 : {
13111 78 : if (pahNewDims[i])
13112 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13113 : }
13114 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13115 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13116 68 : papszOptions);
13117 34 : if (!poNewArray)
13118 8 : return nullptr;
13119 26 : return new GDALMDArrayHS(poNewArray);
13120 : }
13121 :
13122 : /************************************************************************/
13123 : /* GDALMDArraySetUnit() */
13124 : /************************************************************************/
13125 :
13126 : /** Set the variable unit.
13127 : *
13128 : * Values should conform as much as possible with those allowed by
13129 : * the NetCDF CF conventions:
13130 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13131 : * but others might be returned.
13132 : *
13133 : * Few examples are "meter", "degrees", "second", ...
13134 : * Empty value means unknown.
13135 : *
13136 : * This is the same as the C function GDALMDArraySetUnit()
13137 : *
13138 : * @param hArray array.
13139 : * @param pszUnit unit name.
13140 : * @return TRUE in case of success.
13141 : */
13142 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13143 : {
13144 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13145 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13146 : }
13147 :
13148 : /************************************************************************/
13149 : /* GDALMDArrayGetUnit() */
13150 : /************************************************************************/
13151 :
13152 : /** Return the array unit.
13153 : *
13154 : * Values should conform as much as possible with those allowed by
13155 : * the NetCDF CF conventions:
13156 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13157 : * but others might be returned.
13158 : *
13159 : * Few examples are "meter", "degrees", "second", ...
13160 : * Empty value means unknown.
13161 : *
13162 : * The return value should not be freed and is valid until GDALMDArray is
13163 : * released or this function called again.
13164 : *
13165 : * This is the same as the C++ method GDALMDArray::GetUnit().
13166 : */
13167 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13168 : {
13169 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13170 113 : return hArray->m_poImpl->GetUnit().c_str();
13171 : }
13172 :
13173 : /************************************************************************/
13174 : /* GDALMDArrayGetSpatialRef() */
13175 : /************************************************************************/
13176 :
13177 : /** Assign a spatial reference system object to the array.
13178 : *
13179 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13180 : * @return TRUE in case of success.
13181 : */
13182 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13183 : {
13184 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13185 60 : return hArray->m_poImpl->SetSpatialRef(
13186 60 : OGRSpatialReference::FromHandle(hSRS));
13187 : }
13188 :
13189 : /************************************************************************/
13190 : /* GDALMDArrayGetSpatialRef() */
13191 : /************************************************************************/
13192 :
13193 : /** Return the spatial reference system object associated with the array.
13194 : *
13195 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13196 : *
13197 : * The returned object must be freed with OSRDestroySpatialReference().
13198 : */
13199 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13200 : {
13201 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13202 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13203 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13204 : }
13205 :
13206 : /************************************************************************/
13207 : /* GDALMDArrayGetStatistics() */
13208 : /************************************************************************/
13209 :
13210 : /**
13211 : * \brief Fetch statistics.
13212 : *
13213 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13214 : *
13215 : * @since GDAL 3.2
13216 : */
13217 :
13218 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13219 : int bApproxOK, int bForce, double *pdfMin,
13220 : double *pdfMax, double *pdfMean,
13221 : double *pdfStdDev, GUInt64 *pnValidCount,
13222 : GDALProgressFunc pfnProgress,
13223 : void *pProgressData)
13224 : {
13225 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13226 30 : return hArray->m_poImpl->GetStatistics(
13227 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13228 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13229 : }
13230 :
13231 : /************************************************************************/
13232 : /* GDALMDArrayComputeStatistics() */
13233 : /************************************************************************/
13234 :
13235 : /**
13236 : * \brief Compute statistics.
13237 : *
13238 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13239 : *
13240 : * @since GDAL 3.2
13241 : * @see GDALMDArrayComputeStatisticsEx()
13242 : */
13243 :
13244 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13245 : int bApproxOK, double *pdfMin, double *pdfMax,
13246 : double *pdfMean, double *pdfStdDev,
13247 : GUInt64 *pnValidCount,
13248 : GDALProgressFunc pfnProgress,
13249 : void *pProgressData)
13250 : {
13251 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13252 0 : return hArray->m_poImpl->ComputeStatistics(
13253 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13254 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13255 : }
13256 :
13257 : /************************************************************************/
13258 : /* GDALMDArrayComputeStatisticsEx() */
13259 : /************************************************************************/
13260 :
13261 : /**
13262 : * \brief Compute statistics.
13263 : *
13264 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13265 : *
13266 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13267 : *
13268 : * @since GDAL 3.8
13269 : */
13270 :
13271 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13272 : int bApproxOK, double *pdfMin,
13273 : double *pdfMax, double *pdfMean,
13274 : double *pdfStdDev, GUInt64 *pnValidCount,
13275 : GDALProgressFunc pfnProgress,
13276 : void *pProgressData,
13277 : CSLConstList papszOptions)
13278 : {
13279 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13280 8 : return hArray->m_poImpl->ComputeStatistics(
13281 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13282 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13283 : }
13284 :
13285 : /************************************************************************/
13286 : /* GDALMDArrayGetCoordinateVariables() */
13287 : /************************************************************************/
13288 :
13289 : /** Return coordinate variables.
13290 : *
13291 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13292 : * itself needs to be freed, CPLFree() should be called (and
13293 : * GDALMDArrayRelease() on individual array members).
13294 : *
13295 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13296 : *
13297 : * @param hArray Array.
13298 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13299 : *
13300 : * @return an array of *pnCount arrays.
13301 : * @since 3.4
13302 : */
13303 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13304 : size_t *pnCount)
13305 : {
13306 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13307 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13308 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13309 : auto ret = static_cast<GDALMDArrayH *>(
13310 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13311 29 : for (size_t i = 0; i < coordinates.size(); i++)
13312 : {
13313 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13314 : }
13315 13 : *pnCount = coordinates.size();
13316 13 : return ret;
13317 : }
13318 :
13319 : /************************************************************************/
13320 : /* GDALMDArrayGetGridded() */
13321 : /************************************************************************/
13322 :
13323 : /** Return a gridded array from scattered point data, that is from an array
13324 : * whose last dimension is the indexing variable of X and Y arrays.
13325 : *
13326 : * The returned object should be released with GDALMDArrayRelease().
13327 : *
13328 : * This is the same as the C++ method GDALMDArray::GetGridded().
13329 : *
13330 : * @since GDAL 3.7
13331 : */
13332 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13333 : const char *pszGridOptions,
13334 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13335 : CSLConstList papszOptions)
13336 : {
13337 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13338 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13339 22 : auto gridded = hArray->m_poImpl->GetGridded(
13340 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13341 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13342 22 : if (!gridded)
13343 19 : return nullptr;
13344 3 : return new GDALMDArrayHS(gridded);
13345 : }
13346 :
13347 : /************************************************************************/
13348 : /* GDALMDArrayGetMeshGrid() */
13349 : /************************************************************************/
13350 :
13351 : /** Return a list of multidimensional arrays from a list of one-dimensional
13352 : * arrays.
13353 : *
13354 : * This is typically used to transform one-dimensional longitude, latitude
13355 : * arrays into 2D ones.
13356 : *
13357 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13358 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13359 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13360 : * repeated to fill the matrix along the first dimension for x1, the second
13361 : * for x2 and so on.
13362 : *
13363 : * For example, if x = [1, 2], and y = [3, 4, 5],
13364 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13365 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13366 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13367 : *
13368 : * and
13369 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13370 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13371 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13372 : *
13373 : * The currently supported options are:
13374 : * <ul>
13375 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13376 : * output.
13377 : * </li>
13378 : * </ul>
13379 : *
13380 : * This is the same as
13381 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13382 : * function.
13383 : *
13384 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13385 : * If only the array itself needs to be freed, CPLFree() should be called
13386 : * (and GDALMDArrayRelease() on individual array members).
13387 : *
13388 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13389 : *
13390 : * @param pahInputArrays Input arrays
13391 : * @param nCountInputArrays Number of input arrays
13392 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13393 : * @param papszOptions NULL, or NULL terminated list of options.
13394 : *
13395 : * @return an array of *pnCountOutputArrays arrays.
13396 : * @since 3.10
13397 : */
13398 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13399 : size_t nCountInputArrays,
13400 : size_t *pnCountOutputArrays,
13401 : CSLConstList papszOptions)
13402 : {
13403 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13404 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13405 :
13406 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13407 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13408 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13409 :
13410 : const auto apoOutputArrays =
13411 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13412 : auto ret = static_cast<GDALMDArrayH *>(
13413 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13414 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13415 : {
13416 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13417 : }
13418 7 : *pnCountOutputArrays = apoOutputArrays.size();
13419 7 : return ret;
13420 : }
13421 :
13422 : /************************************************************************/
13423 : /* GDALReleaseArrays() */
13424 : /************************************************************************/
13425 :
13426 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13427 : *
13428 : * @param arrays return pointer of above methods
13429 : * @param nCount *pnCount value returned by above methods
13430 : */
13431 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13432 : {
13433 46 : for (size_t i = 0; i < nCount; i++)
13434 : {
13435 26 : delete arrays[i];
13436 : }
13437 20 : CPLFree(arrays);
13438 20 : }
13439 :
13440 : /************************************************************************/
13441 : /* GDALMDArrayCache() */
13442 : /************************************************************************/
13443 :
13444 : /**
13445 : * \brief Cache the content of the array into an auxiliary filename.
13446 : *
13447 : * This is the same as the C++ method GDALMDArray::Cache().
13448 : *
13449 : * @since GDAL 3.4
13450 : */
13451 :
13452 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13453 : {
13454 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13455 7 : return hArray->m_poImpl->Cache(papszOptions);
13456 : }
13457 :
13458 : /************************************************************************/
13459 : /* GDALMDArrayRename() */
13460 : /************************************************************************/
13461 :
13462 : /** Rename the array.
13463 : *
13464 : * This is not implemented by all drivers.
13465 : *
13466 : * Drivers known to implement it: MEM, netCDF, Zarr.
13467 : *
13468 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13469 : *
13470 : * @return true in case of success
13471 : * @since GDAL 3.8
13472 : */
13473 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13474 : {
13475 28 : VALIDATE_POINTER1(hArray, __func__, false);
13476 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13477 28 : return hArray->m_poImpl->Rename(pszNewName);
13478 : }
13479 :
13480 : /************************************************************************/
13481 : /* GDALAttributeRelease() */
13482 : /************************************************************************/
13483 :
13484 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13485 : *
13486 : * Note: when applied on a object coming from a driver, this does not
13487 : * destroy the object in the file, database, etc...
13488 : */
13489 739 : void GDALAttributeRelease(GDALAttributeH hAttr)
13490 : {
13491 739 : delete hAttr;
13492 739 : }
13493 :
13494 : /************************************************************************/
13495 : /* GDALAttributeGetName() */
13496 : /************************************************************************/
13497 :
13498 : /** Return the name of the attribute.
13499 : *
13500 : * The returned pointer is valid until hAttr is released.
13501 : *
13502 : * This is the same as the C++ method GDALAttribute::GetName().
13503 : */
13504 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13505 : {
13506 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13507 361 : return hAttr->m_poImpl->GetName().c_str();
13508 : }
13509 :
13510 : /************************************************************************/
13511 : /* GDALAttributeGetFullName() */
13512 : /************************************************************************/
13513 :
13514 : /** Return the full name of the attribute.
13515 : *
13516 : * The returned pointer is valid until hAttr is released.
13517 : *
13518 : * This is the same as the C++ method GDALAttribute::GetFullName().
13519 : */
13520 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13521 : {
13522 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13523 49 : return hAttr->m_poImpl->GetFullName().c_str();
13524 : }
13525 :
13526 : /************************************************************************/
13527 : /* GDALAttributeGetTotalElementsCount() */
13528 : /************************************************************************/
13529 :
13530 : /** Return the total number of values in the attribute.
13531 : *
13532 : * This is the same as the C++ method
13533 : * GDALAbstractMDArray::GetTotalElementsCount()
13534 : */
13535 177 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13536 : {
13537 177 : VALIDATE_POINTER1(hAttr, __func__, 0);
13538 177 : return hAttr->m_poImpl->GetTotalElementsCount();
13539 : }
13540 :
13541 : /************************************************************************/
13542 : /* GDALAttributeGetDimensionCount() */
13543 : /************************************************************************/
13544 :
13545 : /** Return the number of dimensions.
13546 : *
13547 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13548 : */
13549 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13550 : {
13551 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13552 12 : return hAttr->m_poImpl->GetDimensionCount();
13553 : }
13554 :
13555 : /************************************************************************/
13556 : /* GDALAttributeGetDimensionsSize() */
13557 : /************************************************************************/
13558 :
13559 : /** Return the dimension sizes of the attribute.
13560 : *
13561 : * The returned array must be freed with CPLFree()
13562 : *
13563 : * @param hAttr Attribute.
13564 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13565 : *
13566 : * @return an array of *pnCount values.
13567 : */
13568 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13569 : {
13570 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13571 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13572 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13573 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13574 22 : for (size_t i = 0; i < dims.size(); i++)
13575 : {
13576 11 : ret[i] = dims[i]->GetSize();
13577 : }
13578 11 : *pnCount = dims.size();
13579 11 : return ret;
13580 : }
13581 :
13582 : /************************************************************************/
13583 : /* GDALAttributeGetDataType() */
13584 : /************************************************************************/
13585 :
13586 : /** Return the data type
13587 : *
13588 : * The return must be freed with GDALExtendedDataTypeRelease().
13589 : */
13590 434 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13591 : {
13592 434 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13593 : return new GDALExtendedDataTypeHS(
13594 434 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13595 : }
13596 :
13597 : /************************************************************************/
13598 : /* GDALAttributeReadAsRaw() */
13599 : /************************************************************************/
13600 :
13601 : /** Return the raw value of an attribute.
13602 : *
13603 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13604 : *
13605 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13606 : *
13607 : * @param hAttr Attribute.
13608 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13609 : *
13610 : * @return a buffer of *pnSize bytes.
13611 : */
13612 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13613 : {
13614 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13615 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13616 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13617 6 : *pnSize = res.size();
13618 6 : auto ret = res.StealData();
13619 6 : if (!ret)
13620 : {
13621 0 : *pnSize = 0;
13622 0 : return nullptr;
13623 : }
13624 6 : return ret;
13625 : }
13626 :
13627 : /************************************************************************/
13628 : /* GDALAttributeFreeRawResult() */
13629 : /************************************************************************/
13630 :
13631 : /** Free the return of GDALAttributeAsRaw()
13632 : */
13633 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13634 : CPL_UNUSED size_t nSize)
13635 : {
13636 6 : VALIDATE_POINTER0(hAttr, __func__);
13637 6 : if (raw)
13638 : {
13639 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13640 6 : const auto nDTSize(dt.GetSize());
13641 6 : GByte *pabyPtr = raw;
13642 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13643 6 : CPLAssert(nSize == nDTSize * nEltCount);
13644 12 : for (size_t i = 0; i < nEltCount; ++i)
13645 : {
13646 6 : dt.FreeDynamicMemory(pabyPtr);
13647 6 : pabyPtr += nDTSize;
13648 : }
13649 6 : CPLFree(raw);
13650 : }
13651 : }
13652 :
13653 : /************************************************************************/
13654 : /* GDALAttributeReadAsString() */
13655 : /************************************************************************/
13656 :
13657 : /** Return the value of an attribute as a string.
13658 : *
13659 : * The returned string should not be freed, and its lifetime does not
13660 : * excess a next call to ReadAsString() on the same object, or the deletion
13661 : * of the object itself.
13662 : *
13663 : * This function will only return the first element if there are several.
13664 : *
13665 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13666 : *
13667 : * @return a string, or nullptr.
13668 : */
13669 108 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13670 : {
13671 108 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13672 108 : return hAttr->m_poImpl->ReadAsString();
13673 : }
13674 :
13675 : /************************************************************************/
13676 : /* GDALAttributeReadAsInt() */
13677 : /************************************************************************/
13678 :
13679 : /** Return the value of an attribute as a integer.
13680 : *
13681 : * This function will only return the first element if there are several.
13682 : *
13683 : * It can fail if its value can not be converted to integer.
13684 : *
13685 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13686 : *
13687 : * @return a integer, or INT_MIN in case of error.
13688 : */
13689 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13690 : {
13691 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13692 22 : return hAttr->m_poImpl->ReadAsInt();
13693 : }
13694 :
13695 : /************************************************************************/
13696 : /* GDALAttributeReadAsInt64() */
13697 : /************************************************************************/
13698 :
13699 : /** Return the value of an attribute as a int64_t.
13700 : *
13701 : * This function will only return the first element if there are several.
13702 : *
13703 : * It can fail if its value can not be converted to integer.
13704 : *
13705 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13706 : *
13707 : * @return an int64_t, or INT64_MIN in case of error.
13708 : */
13709 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13710 : {
13711 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13712 15 : return hAttr->m_poImpl->ReadAsInt64();
13713 : }
13714 :
13715 : /************************************************************************/
13716 : /* GDALAttributeReadAsDouble() */
13717 : /************************************************************************/
13718 :
13719 : /** Return the value of an attribute as a double.
13720 : *
13721 : * This function will only return the first element if there are several.
13722 : *
13723 : * It can fail if its value can not be converted to double.
13724 : *
13725 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13726 : *
13727 : * @return a double value.
13728 : */
13729 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13730 : {
13731 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13732 40 : return hAttr->m_poImpl->ReadAsDouble();
13733 : }
13734 :
13735 : /************************************************************************/
13736 : /* GDALAttributeReadAsStringArray() */
13737 : /************************************************************************/
13738 :
13739 : /** Return the value of an attribute as an array of strings.
13740 : *
13741 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13742 : *
13743 : * The return value must be freed with CSLDestroy().
13744 : */
13745 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13746 : {
13747 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13748 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13749 : }
13750 :
13751 : /************************************************************************/
13752 : /* GDALAttributeReadAsIntArray() */
13753 : /************************************************************************/
13754 :
13755 : /** Return the value of an attribute as an array of integers.
13756 : *
13757 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13758 : *
13759 : * @param hAttr Attribute
13760 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13761 : * @return array to be freed with CPLFree(), or nullptr.
13762 : */
13763 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13764 : {
13765 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13766 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13767 15 : *pnCount = 0;
13768 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13769 15 : if (tmp.empty())
13770 0 : return nullptr;
13771 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13772 15 : if (!ret)
13773 0 : return nullptr;
13774 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13775 15 : *pnCount = tmp.size();
13776 15 : return ret;
13777 : }
13778 :
13779 : /************************************************************************/
13780 : /* GDALAttributeReadAsInt64Array() */
13781 : /************************************************************************/
13782 :
13783 : /** Return the value of an attribute as an array of int64_t.
13784 : *
13785 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13786 : *
13787 : * @param hAttr Attribute
13788 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13789 : * @return array to be freed with CPLFree(), or nullptr.
13790 : */
13791 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13792 : {
13793 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13794 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13795 14 : *pnCount = 0;
13796 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13797 14 : if (tmp.empty())
13798 0 : return nullptr;
13799 : auto ret = static_cast<int64_t *>(
13800 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13801 14 : if (!ret)
13802 0 : return nullptr;
13803 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13804 14 : *pnCount = tmp.size();
13805 14 : return ret;
13806 : }
13807 :
13808 : /************************************************************************/
13809 : /* GDALAttributeReadAsDoubleArray() */
13810 : /************************************************************************/
13811 :
13812 : /** Return the value of an attribute as an array of doubles.
13813 : *
13814 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13815 : *
13816 : * @param hAttr Attribute
13817 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13818 : * @return array to be freed with CPLFree(), or nullptr.
13819 : */
13820 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13821 : {
13822 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13823 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13824 29 : *pnCount = 0;
13825 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13826 29 : if (tmp.empty())
13827 0 : return nullptr;
13828 : auto ret =
13829 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13830 29 : if (!ret)
13831 0 : return nullptr;
13832 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13833 29 : *pnCount = tmp.size();
13834 29 : return ret;
13835 : }
13836 :
13837 : /************************************************************************/
13838 : /* GDALAttributeWriteRaw() */
13839 : /************************************************************************/
13840 :
13841 : /** Write an attribute from raw values expressed in GetDataType()
13842 : *
13843 : * The values should be provided in the type of GetDataType() and there should
13844 : * be exactly GetTotalElementsCount() of them.
13845 : * If GetDataType() is a string, each value should be a char* pointer.
13846 : *
13847 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13848 : *
13849 : * @param hAttr Attribute
13850 : * @param pabyValue Buffer of nLen bytes.
13851 : * @param nLength Size of pabyValue in bytes. Should be equal to
13852 : * GetTotalElementsCount() * GetDataType().GetSize()
13853 : * @return TRUE in case of success.
13854 : */
13855 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13856 : size_t nLength)
13857 : {
13858 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13859 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13860 : }
13861 :
13862 : /************************************************************************/
13863 : /* GDALAttributeWriteString() */
13864 : /************************************************************************/
13865 :
13866 : /** Write an attribute from a string value.
13867 : *
13868 : * Type conversion will be performed if needed. If the attribute contains
13869 : * multiple values, only the first one will be updated.
13870 : *
13871 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13872 : *
13873 : * @param hAttr Attribute
13874 : * @param pszVal Pointer to a string.
13875 : * @return TRUE in case of success.
13876 : */
13877 181 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13878 : {
13879 181 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13880 181 : return hAttr->m_poImpl->Write(pszVal);
13881 : }
13882 :
13883 : /************************************************************************/
13884 : /* GDALAttributeWriteInt() */
13885 : /************************************************************************/
13886 :
13887 : /** Write an attribute from a integer value.
13888 : *
13889 : * Type conversion will be performed if needed. If the attribute contains
13890 : * multiple values, only the first one will be updated.
13891 : *
13892 : * This is the same as the C++ method GDALAttribute::WriteInt()
13893 : *
13894 : * @param hAttr Attribute
13895 : * @param nVal Value.
13896 : * @return TRUE in case of success.
13897 : */
13898 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13899 : {
13900 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13901 22 : return hAttr->m_poImpl->WriteInt(nVal);
13902 : }
13903 :
13904 : /************************************************************************/
13905 : /* GDALAttributeWriteInt64() */
13906 : /************************************************************************/
13907 :
13908 : /** Write an attribute from an int64_t value.
13909 : *
13910 : * Type conversion will be performed if needed. If the attribute contains
13911 : * multiple values, only the first one will be updated.
13912 : *
13913 : * This is the same as the C++ method GDALAttribute::WriteLong()
13914 : *
13915 : * @param hAttr Attribute
13916 : * @param nVal Value.
13917 : * @return TRUE in case of success.
13918 : */
13919 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13920 : {
13921 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13922 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13923 : }
13924 :
13925 : /************************************************************************/
13926 : /* GDALAttributeWriteDouble() */
13927 : /************************************************************************/
13928 :
13929 : /** Write an attribute from a double value.
13930 : *
13931 : * Type conversion will be performed if needed. If the attribute contains
13932 : * multiple values, only the first one will be updated.
13933 : *
13934 : * This is the same as the C++ method GDALAttribute::Write(double);
13935 : *
13936 : * @param hAttr Attribute
13937 : * @param dfVal Value.
13938 : *
13939 : * @return TRUE in case of success.
13940 : */
13941 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13942 : {
13943 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13944 11 : return hAttr->m_poImpl->Write(dfVal);
13945 : }
13946 :
13947 : /************************************************************************/
13948 : /* GDALAttributeWriteStringArray() */
13949 : /************************************************************************/
13950 :
13951 : /** Write an attribute from an array of strings.
13952 : *
13953 : * Type conversion will be performed if needed.
13954 : *
13955 : * Exactly GetTotalElementsCount() strings must be provided
13956 : *
13957 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13958 : *
13959 : * @param hAttr Attribute
13960 : * @param papszValues Array of strings.
13961 : * @return TRUE in case of success.
13962 : */
13963 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13964 : CSLConstList papszValues)
13965 : {
13966 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13967 8 : return hAttr->m_poImpl->Write(papszValues);
13968 : }
13969 :
13970 : /************************************************************************/
13971 : /* GDALAttributeWriteIntArray() */
13972 : /************************************************************************/
13973 :
13974 : /** Write an attribute from an array of int.
13975 : *
13976 : * Type conversion will be performed if needed.
13977 : *
13978 : * Exactly GetTotalElementsCount() strings must be provided
13979 : *
13980 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13981 : * size_t)
13982 : *
13983 : * @param hAttr Attribute
13984 : * @param panValues Array of int.
13985 : * @param nCount Should be equal to GetTotalElementsCount().
13986 : * @return TRUE in case of success.
13987 : */
13988 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13989 : size_t nCount)
13990 : {
13991 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13992 11 : return hAttr->m_poImpl->Write(panValues, nCount);
13993 : }
13994 :
13995 : /************************************************************************/
13996 : /* GDALAttributeWriteInt64Array() */
13997 : /************************************************************************/
13998 :
13999 : /** Write an attribute from an array of int64_t.
14000 : *
14001 : * Type conversion will be performed if needed.
14002 : *
14003 : * Exactly GetTotalElementsCount() strings must be provided
14004 : *
14005 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14006 : * size_t)
14007 : *
14008 : * @param hAttr Attribute
14009 : * @param panValues Array of int64_t.
14010 : * @param nCount Should be equal to GetTotalElementsCount().
14011 : * @return TRUE in case of success.
14012 : */
14013 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14014 : size_t nCount)
14015 : {
14016 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14017 10 : return hAttr->m_poImpl->Write(panValues, nCount);
14018 : }
14019 :
14020 : /************************************************************************/
14021 : /* GDALAttributeWriteDoubleArray() */
14022 : /************************************************************************/
14023 :
14024 : /** Write an attribute from an array of double.
14025 : *
14026 : * Type conversion will be performed if needed.
14027 : *
14028 : * Exactly GetTotalElementsCount() strings must be provided
14029 : *
14030 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14031 : * size_t)
14032 : *
14033 : * @param hAttr Attribute
14034 : * @param padfValues Array of double.
14035 : * @param nCount Should be equal to GetTotalElementsCount().
14036 : * @return TRUE in case of success.
14037 : */
14038 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14039 : const double *padfValues, size_t nCount)
14040 : {
14041 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14042 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14043 : }
14044 :
14045 : /************************************************************************/
14046 : /* GDALAttributeRename() */
14047 : /************************************************************************/
14048 :
14049 : /** Rename the attribute.
14050 : *
14051 : * This is not implemented by all drivers.
14052 : *
14053 : * Drivers known to implement it: MEM, netCDF.
14054 : *
14055 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14056 : *
14057 : * @return true in case of success
14058 : * @since GDAL 3.8
14059 : */
14060 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14061 : {
14062 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14063 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14064 27 : return hAttr->m_poImpl->Rename(pszNewName);
14065 : }
14066 :
14067 : /************************************************************************/
14068 : /* GDALDimensionRelease() */
14069 : /************************************************************************/
14070 :
14071 : /** Release the GDAL in-memory object associated with a GDALDimension.
14072 : *
14073 : * Note: when applied on a object coming from a driver, this does not
14074 : * destroy the object in the file, database, etc...
14075 : */
14076 4995 : void GDALDimensionRelease(GDALDimensionH hDim)
14077 : {
14078 4995 : delete hDim;
14079 4995 : }
14080 :
14081 : /************************************************************************/
14082 : /* GDALDimensionGetName() */
14083 : /************************************************************************/
14084 :
14085 : /** Return dimension name.
14086 : *
14087 : * This is the same as the C++ method GDALDimension::GetName()
14088 : */
14089 289 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14090 : {
14091 289 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14092 289 : return hDim->m_poImpl->GetName().c_str();
14093 : }
14094 :
14095 : /************************************************************************/
14096 : /* GDALDimensionGetFullName() */
14097 : /************************************************************************/
14098 :
14099 : /** Return dimension full name.
14100 : *
14101 : * This is the same as the C++ method GDALDimension::GetFullName()
14102 : */
14103 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14104 : {
14105 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14106 82 : return hDim->m_poImpl->GetFullName().c_str();
14107 : }
14108 :
14109 : /************************************************************************/
14110 : /* GDALDimensionGetType() */
14111 : /************************************************************************/
14112 :
14113 : /** Return dimension type.
14114 : *
14115 : * This is the same as the C++ method GDALDimension::GetType()
14116 : */
14117 64 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14118 : {
14119 64 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14120 64 : return hDim->m_poImpl->GetType().c_str();
14121 : }
14122 :
14123 : /************************************************************************/
14124 : /* GDALDimensionGetDirection() */
14125 : /************************************************************************/
14126 :
14127 : /** Return dimension direction.
14128 : *
14129 : * This is the same as the C++ method GDALDimension::GetDirection()
14130 : */
14131 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14132 : {
14133 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14134 32 : return hDim->m_poImpl->GetDirection().c_str();
14135 : }
14136 :
14137 : /************************************************************************/
14138 : /* GDALDimensionGetSize() */
14139 : /************************************************************************/
14140 :
14141 : /** Return the size, that is the number of values along the dimension.
14142 : *
14143 : * This is the same as the C++ method GDALDimension::GetSize()
14144 : */
14145 3735 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14146 : {
14147 3735 : VALIDATE_POINTER1(hDim, __func__, 0);
14148 3735 : return hDim->m_poImpl->GetSize();
14149 : }
14150 :
14151 : /************************************************************************/
14152 : /* GDALDimensionGetIndexingVariable() */
14153 : /************************************************************************/
14154 :
14155 : /** Return the variable that is used to index the dimension (if there is one).
14156 : *
14157 : * This is the array, typically one-dimensional, describing the values taken
14158 : * by the dimension.
14159 : *
14160 : * The returned value should be freed with GDALMDArrayRelease().
14161 : *
14162 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14163 : */
14164 133 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14165 : {
14166 133 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14167 266 : auto var(hDim->m_poImpl->GetIndexingVariable());
14168 133 : if (!var)
14169 11 : return nullptr;
14170 122 : return new GDALMDArrayHS(var);
14171 : }
14172 :
14173 : /************************************************************************/
14174 : /* GDALDimensionSetIndexingVariable() */
14175 : /************************************************************************/
14176 :
14177 : /** Set the variable that is used to index the dimension.
14178 : *
14179 : * This is the array, typically one-dimensional, describing the values taken
14180 : * by the dimension.
14181 : *
14182 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14183 : *
14184 : * @return TRUE in case of success.
14185 : */
14186 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14187 : {
14188 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14189 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14190 46 : : nullptr);
14191 : }
14192 :
14193 : /************************************************************************/
14194 : /* GDALDimensionRename() */
14195 : /************************************************************************/
14196 :
14197 : /** Rename the dimension.
14198 : *
14199 : * This is not implemented by all drivers.
14200 : *
14201 : * Drivers known to implement it: MEM, netCDF.
14202 : *
14203 : * This is the same as the C++ method GDALDimension::Rename()
14204 : *
14205 : * @return true in case of success
14206 : * @since GDAL 3.8
14207 : */
14208 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14209 : {
14210 31 : VALIDATE_POINTER1(hDim, __func__, false);
14211 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14212 31 : return hDim->m_poImpl->Rename(pszNewName);
14213 : }
14214 :
14215 : /************************************************************************/
14216 : /* GDALDatasetGetRootGroup() */
14217 : /************************************************************************/
14218 :
14219 : /** Return the root GDALGroup of this dataset.
14220 : *
14221 : * Only valid for multidimensional datasets.
14222 : *
14223 : * The returned value must be freed with GDALGroupRelease().
14224 : *
14225 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14226 : *
14227 : * @since GDAL 3.1
14228 : */
14229 1189 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14230 : {
14231 1189 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14232 1189 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14233 1189 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14234 : }
14235 :
14236 : /************************************************************************/
14237 : /* GDALRasterBandAsMDArray() */
14238 : /************************************************************************/
14239 :
14240 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14241 : *
14242 : * The band must be linked to a GDALDataset. If this dataset is not already
14243 : * marked as shared, it will be, so that the returned array holds a reference
14244 : * to it.
14245 : *
14246 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14247 : * returned array will have an associated indexing variable.
14248 : *
14249 : * The returned pointer must be released with GDALMDArrayRelease().
14250 : *
14251 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14252 : *
14253 : * @return a new array, or NULL.
14254 : *
14255 : * @since GDAL 3.1
14256 : */
14257 24 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14258 : {
14259 24 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14260 48 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14261 24 : if (!poArray)
14262 0 : return nullptr;
14263 24 : return new GDALMDArrayHS(poArray);
14264 : }
14265 :
14266 : /************************************************************************/
14267 : /* GDALDatasetAsMDArray() */
14268 : /************************************************************************/
14269 :
14270 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14271 : *
14272 : * If this dataset is not already marked as shared, it will be, so that the
14273 : * returned array holds a reference to it.
14274 : *
14275 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14276 : * returned array will have an associated indexing variable.
14277 : *
14278 : * The currently supported list of options is:
14279 : * <ul>
14280 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14281 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14282 : * and the last (fastest changing direction) is X
14283 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14284 : * and the last (fastest changing direction) is Band.
14285 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14286 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14287 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14288 : * "Y,X,Band" is use.
14289 : * </li>
14290 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14291 : * item from which to build the band indexing variable.
14292 : * <ul>
14293 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14294 : * <li>"{None}" means that no band indexing variable must be created.</li>
14295 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14296 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14297 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14298 : * </ul>
14299 : * </li>
14300 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14301 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14302 : * Defaults to String.
14303 : * </li>
14304 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14305 : * Defaults to "Band".
14306 : * </li>
14307 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14308 : * </li>
14309 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14310 : * </li>
14311 : * </ul>
14312 : *
14313 : * The returned pointer must be released with GDALMDArrayRelease().
14314 : *
14315 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14316 : * GDALDataset::AsMDArray()
14317 : *
14318 : * This is the same as the C++ method GDALDataset::AsMDArray().
14319 : *
14320 : * @param hDS Dataset handle.
14321 : * @param papszOptions Null-terminated list of strings, or nullptr.
14322 : * @return a new array, or NULL.
14323 : *
14324 : * @since GDAL 3.12
14325 : */
14326 14 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14327 : {
14328 14 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14329 28 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14330 14 : if (!poArray)
14331 3 : return nullptr;
14332 11 : return new GDALMDArrayHS(poArray);
14333 : }
14334 :
14335 : /************************************************************************/
14336 : /* GDALMDArrayAsClassicDataset() */
14337 : /************************************************************************/
14338 :
14339 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14340 : *
14341 : * Only 2D or more arrays are supported.
14342 : *
14343 : * In the case of > 2D arrays, additional dimensions will be represented as
14344 : * raster bands.
14345 : *
14346 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14347 : * GDALDataset::AsMDArray()
14348 : *
14349 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14350 : *
14351 : * @param hArray Array.
14352 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14353 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14354 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14355 : */
14356 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14357 : size_t iYDim)
14358 : {
14359 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14360 0 : return GDALDataset::ToHandle(
14361 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14362 : }
14363 :
14364 : /************************************************************************/
14365 : /* GDALMDArrayAsClassicDatasetEx() */
14366 : /************************************************************************/
14367 :
14368 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14369 : *
14370 : * Only 2D or more arrays are supported.
14371 : *
14372 : * In the case of > 2D arrays, additional dimensions will be represented as
14373 : * raster bands.
14374 : *
14375 : * The "reverse" method is GDALRasterBand::AsMDArray().
14376 : *
14377 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14378 : * @param hArray Array.
14379 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14380 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14381 : * Ignored if the dimension count is 1.
14382 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14383 : * BAND_IMAGERY_METADATA option.
14384 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14385 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14386 : * @since GDAL 3.8
14387 : */
14388 100 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14389 : size_t iYDim, GDALGroupH hRootGroup,
14390 : CSLConstList papszOptions)
14391 : {
14392 100 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14393 200 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14394 200 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14395 200 : papszOptions));
14396 : }
14397 :
14398 : //! @cond Doxygen_Suppress
14399 :
14400 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14401 : const std::string &osName,
14402 : const std::string &osValue,
14403 180 : GDALExtendedDataTypeSubType eSubType)
14404 : : GDALAbstractMDArray(osParentName, osName),
14405 : GDALAttribute(osParentName, osName),
14406 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14407 : {
14408 180 : }
14409 :
14410 : const std::vector<std::shared_ptr<GDALDimension>> &
14411 30 : GDALAttributeString::GetDimensions() const
14412 : {
14413 30 : return m_dims;
14414 : }
14415 :
14416 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14417 : {
14418 21 : return m_dt;
14419 : }
14420 :
14421 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14422 : const GPtrDiff_t *,
14423 : const GDALExtendedDataType &bufferDataType,
14424 : void *pDstBuffer) const
14425 : {
14426 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14427 0 : return false;
14428 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14429 10 : if (!pszStr)
14430 0 : return false;
14431 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14432 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14433 10 : return true;
14434 : }
14435 :
14436 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14437 : const std::string &osName,
14438 66 : double dfValue)
14439 : : GDALAbstractMDArray(osParentName, osName),
14440 : GDALAttribute(osParentName, osName),
14441 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14442 : {
14443 66 : }
14444 :
14445 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14446 : const std::string &osName,
14447 27 : int nValue)
14448 : : GDALAbstractMDArray(osParentName, osName),
14449 : GDALAttribute(osParentName, osName),
14450 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14451 : {
14452 27 : }
14453 :
14454 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14455 : const std::string &osName,
14456 7 : const std::vector<GUInt32> &anValues)
14457 : : GDALAbstractMDArray(osParentName, osName),
14458 : GDALAttribute(osParentName, osName),
14459 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14460 : {
14461 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14462 14 : std::string(), "dim0", std::string(), std::string(),
14463 7 : m_anValuesUInt32.size()));
14464 7 : }
14465 :
14466 : const std::vector<std::shared_ptr<GDALDimension>> &
14467 14 : GDALAttributeNumeric::GetDimensions() const
14468 : {
14469 14 : return m_dims;
14470 : }
14471 :
14472 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14473 : {
14474 8 : return m_dt;
14475 : }
14476 :
14477 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14478 : const size_t *count, const GInt64 *arrayStep,
14479 : const GPtrDiff_t *bufferStride,
14480 : const GDALExtendedDataType &bufferDataType,
14481 : void *pDstBuffer) const
14482 : {
14483 4 : if (m_dims.empty())
14484 : {
14485 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14486 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14487 : bufferDataType);
14488 : else
14489 : {
14490 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14491 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14492 : bufferDataType);
14493 : }
14494 : }
14495 : else
14496 : {
14497 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14498 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14499 30 : for (size_t i = 0; i < count[0]; ++i)
14500 : {
14501 29 : GDALExtendedDataType::CopyValue(
14502 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14503 29 : i * arrayStep[0])],
14504 29 : m_dt, pabyDstBuffer, bufferDataType);
14505 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14506 : }
14507 : }
14508 4 : return true;
14509 : }
14510 :
14511 218 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14512 : const std::string &osParentName, const std::string &osName,
14513 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14514 218 : double dfIncrement, double dfOffsetInIncrement)
14515 : : GDALAbstractMDArray(osParentName, osName),
14516 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14517 : m_dfIncrement(dfIncrement),
14518 436 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14519 : {
14520 218 : }
14521 :
14522 218 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14523 : const std::string &osParentName, const std::string &osName,
14524 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14525 : double dfIncrement, double dfOffsetInIncrement)
14526 : {
14527 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14528 218 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14529 218 : poArray->SetSelf(poArray);
14530 218 : return poArray;
14531 : }
14532 :
14533 : const std::vector<std::shared_ptr<GDALDimension>> &
14534 1286 : GDALMDArrayRegularlySpaced::GetDimensions() const
14535 : {
14536 1286 : return m_dims;
14537 : }
14538 :
14539 545 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14540 : {
14541 545 : return m_dt;
14542 : }
14543 :
14544 : std::vector<std::shared_ptr<GDALAttribute>>
14545 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14546 : {
14547 4 : return m_attributes;
14548 : }
14549 :
14550 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14551 : const std::shared_ptr<GDALAttribute> &poAttr)
14552 : {
14553 0 : m_attributes.emplace_back(poAttr);
14554 0 : }
14555 :
14556 283 : bool GDALMDArrayRegularlySpaced::IRead(
14557 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14558 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14559 : void *pDstBuffer) const
14560 : {
14561 283 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14562 16197 : for (size_t i = 0; i < count[0]; i++)
14563 : {
14564 15914 : const double dfVal =
14565 15914 : m_dfStart +
14566 15914 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14567 15914 : m_dfOffsetInIncrement) *
14568 15914 : m_dfIncrement;
14569 15914 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14570 : bufferDataType);
14571 15914 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14572 : }
14573 283 : return true;
14574 : }
14575 :
14576 3765 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14577 : const std::string &osParentName, const std::string &osName,
14578 3765 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14579 3765 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14580 : {
14581 3765 : }
14582 :
14583 : std::shared_ptr<GDALMDArray>
14584 1418 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14585 : {
14586 1418 : return m_poIndexingVariable.lock();
14587 : }
14588 :
14589 : // cppcheck-suppress passedByValue
14590 631 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14591 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14592 : {
14593 631 : m_poIndexingVariable = poIndexingVariable;
14594 631 : return true;
14595 : }
14596 :
14597 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14598 : {
14599 33 : m_nSize = nNewSize;
14600 33 : }
14601 :
14602 : /************************************************************************/
14603 : /* GDALPamMultiDim::Private */
14604 : /************************************************************************/
14605 :
14606 : struct GDALPamMultiDim::Private
14607 : {
14608 : std::string m_osFilename{};
14609 : std::string m_osPamFilename{};
14610 :
14611 : struct Statistics
14612 : {
14613 : bool bHasStats = false;
14614 : bool bApproxStats = false;
14615 : double dfMin = 0;
14616 : double dfMax = 0;
14617 : double dfMean = 0;
14618 : double dfStdDev = 0;
14619 : GUInt64 nValidCount = 0;
14620 : };
14621 :
14622 : struct ArrayInfo
14623 : {
14624 : std::shared_ptr<OGRSpatialReference> poSRS{};
14625 : // cppcheck-suppress unusedStructMember
14626 : Statistics stats{};
14627 : };
14628 :
14629 : typedef std::pair<std::string, std::string> NameContext;
14630 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14631 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14632 : bool m_bDirty = false;
14633 : bool m_bLoaded = false;
14634 : };
14635 :
14636 : /************************************************************************/
14637 : /* GDALPamMultiDim */
14638 : /************************************************************************/
14639 :
14640 1465 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14641 1465 : : d(new Private())
14642 : {
14643 1465 : d->m_osFilename = osFilename;
14644 1465 : }
14645 :
14646 : /************************************************************************/
14647 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14648 : /************************************************************************/
14649 :
14650 1465 : GDALPamMultiDim::~GDALPamMultiDim()
14651 : {
14652 1464 : if (d->m_bDirty)
14653 30 : Save();
14654 1465 : }
14655 :
14656 : /************************************************************************/
14657 : /* GDALPamMultiDim::Load() */
14658 : /************************************************************************/
14659 :
14660 107 : void GDALPamMultiDim::Load()
14661 : {
14662 107 : if (d->m_bLoaded)
14663 96 : return;
14664 45 : d->m_bLoaded = true;
14665 :
14666 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14667 45 : d->m_osPamFilename =
14668 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14669 45 : CPLXMLTreeCloser oTree(nullptr);
14670 : {
14671 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14672 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14673 : }
14674 45 : if (!oTree)
14675 : {
14676 34 : return;
14677 : }
14678 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14679 11 : if (!poPAMMultiDim)
14680 0 : return;
14681 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14682 24 : psIter = psIter->psNext)
14683 : {
14684 24 : if (psIter->eType == CXT_Element &&
14685 24 : strcmp(psIter->pszValue, "Array") == 0)
14686 : {
14687 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14688 13 : if (!pszName)
14689 0 : continue;
14690 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14691 : const auto oKey =
14692 26 : std::pair<std::string, std::string>(pszName, pszContext);
14693 :
14694 : /* --------------------------------------------------------------------
14695 : */
14696 : /* Check for an SRS node. */
14697 : /* --------------------------------------------------------------------
14698 : */
14699 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14700 13 : if (psSRSNode)
14701 : {
14702 : std::shared_ptr<OGRSpatialReference> poSRS =
14703 6 : std::make_shared<OGRSpatialReference>();
14704 3 : poSRS->SetFromUserInput(
14705 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14706 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14707 3 : const char *pszMapping = CPLGetXMLValue(
14708 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14709 3 : if (pszMapping)
14710 : {
14711 : char **papszTokens =
14712 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14713 6 : std::vector<int> anMapping;
14714 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14715 : {
14716 6 : anMapping.push_back(atoi(papszTokens[i]));
14717 : }
14718 3 : CSLDestroy(papszTokens);
14719 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14720 : }
14721 : else
14722 : {
14723 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14724 : }
14725 :
14726 : const char *pszCoordinateEpoch =
14727 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14728 3 : if (pszCoordinateEpoch)
14729 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14730 :
14731 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14732 : }
14733 :
14734 : const CPLXMLNode *psStatistics =
14735 13 : CPLGetXMLNode(psIter, "Statistics");
14736 13 : if (psStatistics)
14737 : {
14738 7 : Private::Statistics sStats;
14739 7 : sStats.bHasStats = true;
14740 7 : sStats.bApproxStats = CPLTestBool(
14741 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14742 7 : sStats.dfMin =
14743 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14744 7 : sStats.dfMax =
14745 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14746 7 : sStats.dfMean =
14747 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14748 7 : sStats.dfStdDev =
14749 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14750 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14751 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14752 7 : d->m_oMapArray[oKey].stats = sStats;
14753 13 : }
14754 : }
14755 : else
14756 : {
14757 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14758 11 : psIter->psNext = nullptr;
14759 11 : d->m_apoOtherNodes.emplace_back(
14760 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14761 11 : psIter->psNext = psNextBackup;
14762 : }
14763 : }
14764 : }
14765 :
14766 : /************************************************************************/
14767 : /* GDALPamMultiDim::Save() */
14768 : /************************************************************************/
14769 :
14770 30 : void GDALPamMultiDim::Save()
14771 : {
14772 : CPLXMLTreeCloser oTree(
14773 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14774 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14775 : {
14776 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14777 : }
14778 112 : for (const auto &kv : d->m_oMapArray)
14779 : {
14780 : CPLXMLNode *psArrayNode =
14781 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14782 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14783 82 : if (!kv.first.second.empty())
14784 : {
14785 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14786 : kv.first.second.c_str());
14787 : }
14788 82 : if (kv.second.poSRS)
14789 : {
14790 71 : char *pszWKT = nullptr;
14791 : {
14792 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14793 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14794 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14795 : }
14796 : CPLXMLNode *psSRSNode =
14797 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14798 71 : CPLFree(pszWKT);
14799 : const auto &mapping =
14800 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14801 142 : CPLString osMapping;
14802 213 : for (size_t i = 0; i < mapping.size(); ++i)
14803 : {
14804 142 : if (!osMapping.empty())
14805 71 : osMapping += ",";
14806 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14807 : }
14808 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14809 : osMapping.c_str());
14810 :
14811 : const double dfCoordinateEpoch =
14812 71 : kv.second.poSRS->GetCoordinateEpoch();
14813 71 : if (dfCoordinateEpoch > 0)
14814 : {
14815 : std::string osCoordinateEpoch =
14816 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14817 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14818 : {
14819 6 : while (osCoordinateEpoch.back() == '0')
14820 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14821 : }
14822 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14823 : osCoordinateEpoch.c_str());
14824 : }
14825 : }
14826 :
14827 82 : if (kv.second.stats.bHasStats)
14828 : {
14829 : CPLXMLNode *psMDArray =
14830 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14831 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14832 8 : kv.second.stats.bApproxStats ? "1"
14833 : : "0");
14834 8 : CPLCreateXMLElementAndValue(
14835 : psMDArray, "Minimum",
14836 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14837 8 : CPLCreateXMLElementAndValue(
14838 : psMDArray, "Maximum",
14839 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14840 8 : CPLCreateXMLElementAndValue(
14841 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14842 8 : CPLCreateXMLElementAndValue(
14843 : psMDArray, "StdDev",
14844 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14845 8 : CPLCreateXMLElementAndValue(
14846 : psMDArray, "ValidSampleCount",
14847 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14848 : }
14849 : }
14850 :
14851 : int bSaved;
14852 60 : CPLErrorAccumulator oErrorAccumulator;
14853 : {
14854 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14855 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14856 : bSaved =
14857 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14858 : }
14859 :
14860 30 : const char *pszNewPam = nullptr;
14861 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14862 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14863 : {
14864 0 : CPLErrorReset();
14865 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14866 : }
14867 : else
14868 : {
14869 30 : oErrorAccumulator.ReplayErrors();
14870 : }
14871 30 : }
14872 :
14873 : /************************************************************************/
14874 : /* GDALPamMultiDim::GetSpatialRef() */
14875 : /************************************************************************/
14876 :
14877 : std::shared_ptr<OGRSpatialReference>
14878 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14879 : const std::string &osContext)
14880 : {
14881 10 : Load();
14882 : auto oIter =
14883 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14884 10 : if (oIter != d->m_oMapArray.end())
14885 2 : return oIter->second.poSRS;
14886 8 : return nullptr;
14887 : }
14888 :
14889 : /************************************************************************/
14890 : /* GDALPamMultiDim::SetSpatialRef() */
14891 : /************************************************************************/
14892 :
14893 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14894 : const std::string &osContext,
14895 : const OGRSpatialReference *poSRS)
14896 : {
14897 72 : Load();
14898 72 : d->m_bDirty = true;
14899 72 : if (poSRS && !poSRS->IsEmpty())
14900 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14901 : poSRS->Clone());
14902 : else
14903 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14904 1 : .poSRS.reset();
14905 72 : }
14906 :
14907 : /************************************************************************/
14908 : /* GetStatistics() */
14909 : /************************************************************************/
14910 :
14911 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14912 : const std::string &osContext,
14913 : bool bApproxOK, double *pdfMin,
14914 : double *pdfMax, double *pdfMean,
14915 : double *pdfStdDev, GUInt64 *pnValidCount)
14916 : {
14917 16 : Load();
14918 : auto oIter =
14919 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14920 16 : if (oIter == d->m_oMapArray.end())
14921 9 : return CE_Failure;
14922 7 : const auto &stats = oIter->second.stats;
14923 7 : if (!stats.bHasStats)
14924 1 : return CE_Failure;
14925 6 : if (!bApproxOK && stats.bApproxStats)
14926 0 : return CE_Failure;
14927 6 : if (pdfMin)
14928 6 : *pdfMin = stats.dfMin;
14929 6 : if (pdfMax)
14930 6 : *pdfMax = stats.dfMax;
14931 6 : if (pdfMean)
14932 6 : *pdfMean = stats.dfMean;
14933 6 : if (pdfStdDev)
14934 6 : *pdfStdDev = stats.dfStdDev;
14935 6 : if (pnValidCount)
14936 6 : *pnValidCount = stats.nValidCount;
14937 6 : return CE_None;
14938 : }
14939 :
14940 : /************************************************************************/
14941 : /* SetStatistics() */
14942 : /************************************************************************/
14943 :
14944 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14945 : const std::string &osContext,
14946 : bool bApproxStats, double dfMin,
14947 : double dfMax, double dfMean,
14948 : double dfStdDev, GUInt64 nValidCount)
14949 : {
14950 8 : Load();
14951 8 : d->m_bDirty = true;
14952 : auto &stats =
14953 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14954 8 : stats.bHasStats = true;
14955 8 : stats.bApproxStats = bApproxStats;
14956 8 : stats.dfMin = dfMin;
14957 8 : stats.dfMax = dfMax;
14958 8 : stats.dfMean = dfMean;
14959 8 : stats.dfStdDev = dfStdDev;
14960 8 : stats.nValidCount = nValidCount;
14961 8 : }
14962 :
14963 : /************************************************************************/
14964 : /* ClearStatistics() */
14965 : /************************************************************************/
14966 :
14967 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14968 : const std::string &osContext)
14969 : {
14970 0 : Load();
14971 0 : d->m_bDirty = true;
14972 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14973 : false;
14974 0 : }
14975 :
14976 : /************************************************************************/
14977 : /* ClearStatistics() */
14978 : /************************************************************************/
14979 :
14980 1 : void GDALPamMultiDim::ClearStatistics()
14981 : {
14982 1 : Load();
14983 1 : d->m_bDirty = true;
14984 3 : for (auto &kv : d->m_oMapArray)
14985 2 : kv.second.stats.bHasStats = false;
14986 1 : }
14987 :
14988 : /************************************************************************/
14989 : /* GetPAM() */
14990 : /************************************************************************/
14991 :
14992 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14993 901 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14994 : {
14995 901 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14996 901 : if (poPamArray)
14997 571 : return poPamArray->GetPAM();
14998 330 : return nullptr;
14999 : }
15000 :
15001 : /************************************************************************/
15002 : /* GDALPamMDArray */
15003 : /************************************************************************/
15004 :
15005 3879 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15006 : const std::string &osName,
15007 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15008 0 : const std::string &osContext)
15009 : :
15010 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15011 : GDALAbstractMDArray(osParentName, osName),
15012 : #endif
15013 3879 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15014 : {
15015 3879 : }
15016 :
15017 : /************************************************************************/
15018 : /* GDALPamMDArray::SetSpatialRef() */
15019 : /************************************************************************/
15020 :
15021 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15022 : {
15023 72 : if (!m_poPam)
15024 0 : return false;
15025 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15026 72 : return true;
15027 : }
15028 :
15029 : /************************************************************************/
15030 : /* GDALPamMDArray::GetSpatialRef() */
15031 : /************************************************************************/
15032 :
15033 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15034 : {
15035 10 : if (!m_poPam)
15036 0 : return nullptr;
15037 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15038 : }
15039 :
15040 : /************************************************************************/
15041 : /* GetStatistics() */
15042 : /************************************************************************/
15043 :
15044 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15045 : double *pdfMin, double *pdfMax,
15046 : double *pdfMean, double *pdfStdDev,
15047 : GUInt64 *pnValidCount,
15048 : GDALProgressFunc pfnProgress,
15049 : void *pProgressData)
15050 : {
15051 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15052 : bApproxOK, pdfMin, pdfMax, pdfMean,
15053 16 : pdfStdDev, pnValidCount) == CE_None)
15054 : {
15055 6 : return CE_None;
15056 : }
15057 10 : if (!bForce)
15058 4 : return CE_Warning;
15059 :
15060 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15061 : pdfMean, pdfStdDev, pnValidCount,
15062 6 : pfnProgress, pProgressData);
15063 : }
15064 :
15065 : /************************************************************************/
15066 : /* SetStatistics() */
15067 : /************************************************************************/
15068 :
15069 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15070 : double dfMax, double dfMean, double dfStdDev,
15071 : GUInt64 nValidCount,
15072 : CSLConstList /* papszOptions */)
15073 : {
15074 8 : if (!m_poPam)
15075 0 : return false;
15076 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15077 : dfMax, dfMean, dfStdDev, nValidCount);
15078 8 : return true;
15079 : }
15080 :
15081 : /************************************************************************/
15082 : /* ClearStatistics() */
15083 : /************************************************************************/
15084 :
15085 0 : void GDALPamMDArray::ClearStatistics()
15086 : {
15087 0 : if (!m_poPam)
15088 0 : return;
15089 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15090 : }
15091 :
15092 : /************************************************************************/
15093 : /* GDALMDIAsAttribute::GetDimensions() */
15094 : /************************************************************************/
15095 :
15096 : const std::vector<std::shared_ptr<GDALDimension>> &
15097 29 : GDALMDIAsAttribute::GetDimensions() const
15098 : {
15099 29 : return m_dims;
15100 : }
15101 :
15102 : //! @endcond
|