Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_rat.h"
31 : #include "gdal_utils.h"
32 : #include "cpl_safemaths.hpp"
33 : #include "memmultidim.h"
34 : #include "ogrsf_frmts.h"
35 : #include "gdalmultidim_priv.h"
36 :
37 : #if defined(__clang__) || defined(_MSC_VER)
38 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
39 : #endif
40 :
41 : /************************************************************************/
42 : /* GDALMDArrayUnscaled */
43 : /************************************************************************/
44 :
45 : class GDALMDArrayUnscaled final : public GDALPamMDArray
46 : {
47 : private:
48 : std::shared_ptr<GDALMDArray> m_poParent{};
49 : const GDALExtendedDataType m_dt;
50 : bool m_bHasNoData;
51 : const double m_dfScale;
52 : const double m_dfOffset;
53 : std::vector<GByte> m_abyRawNoData{};
54 :
55 : protected:
56 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
57 : double dfScale, double dfOffset,
58 : double dfOverriddenDstNodata, GDALDataType eDT)
59 26 : : GDALAbstractMDArray(std::string(),
60 26 : "Unscaled view of " + poParent->GetFullName()),
61 : GDALPamMDArray(
62 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
63 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
64 13 : m_poParent(std::move(poParent)),
65 : m_dt(GDALExtendedDataType::Create(eDT)),
66 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
67 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
68 : {
69 13 : m_abyRawNoData.resize(m_dt.GetSize());
70 : const auto eNonComplexDT =
71 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
72 26 : GDALCopyWords64(
73 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
74 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
75 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
76 13 : }
77 :
78 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
79 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
80 : const GDALExtendedDataType &bufferDataType,
81 : void *pDstBuffer) const override;
82 :
83 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
84 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
85 : const GDALExtendedDataType &bufferDataType,
86 : const void *pSrcBuffer) override;
87 :
88 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
89 : CSLConstList papszOptions) const override
90 : {
91 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
92 : }
93 :
94 : public:
95 : static std::shared_ptr<GDALMDArrayUnscaled>
96 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
97 : double dfOffset, double dfDstNodata, GDALDataType eDT)
98 : {
99 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
100 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
101 13 : newAr->SetSelf(newAr);
102 13 : return newAr;
103 : }
104 :
105 1 : bool IsWritable() const override
106 : {
107 1 : return m_poParent->IsWritable();
108 : }
109 :
110 15 : const std::string &GetFilename() const override
111 : {
112 15 : return m_poParent->GetFilename();
113 : }
114 :
115 : const std::vector<std::shared_ptr<GDALDimension>> &
116 220 : GetDimensions() const override
117 : {
118 220 : return m_poParent->GetDimensions();
119 : }
120 :
121 103 : const GDALExtendedDataType &GetDataType() const override
122 : {
123 103 : return m_dt;
124 : }
125 :
126 1 : const std::string &GetUnit() const override
127 : {
128 1 : return m_poParent->GetUnit();
129 : }
130 :
131 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
132 : {
133 1 : return m_poParent->GetSpatialRef();
134 : }
135 :
136 6 : const void *GetRawNoDataValue() const override
137 : {
138 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
139 : }
140 :
141 1 : bool SetRawNoDataValue(const void *pRawNoData) override
142 : {
143 1 : m_bHasNoData = true;
144 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
145 1 : return true;
146 : }
147 :
148 4 : std::vector<GUInt64> GetBlockSize() const override
149 : {
150 4 : return m_poParent->GetBlockSize();
151 : }
152 :
153 : std::shared_ptr<GDALAttribute>
154 0 : GetAttribute(const std::string &osName) const override
155 : {
156 0 : return m_poParent->GetAttribute(osName);
157 : }
158 :
159 : std::vector<std::shared_ptr<GDALAttribute>>
160 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
161 : {
162 1 : return m_poParent->GetAttributes(papszOptions);
163 : }
164 :
165 0 : bool SetUnit(const std::string &osUnit) override
166 : {
167 0 : return m_poParent->SetUnit(osUnit);
168 : }
169 :
170 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
171 : {
172 0 : return m_poParent->SetSpatialRef(poSRS);
173 : }
174 :
175 : std::shared_ptr<GDALAttribute>
176 1 : CreateAttribute(const std::string &osName,
177 : const std::vector<GUInt64> &anDimensions,
178 : const GDALExtendedDataType &oDataType,
179 : CSLConstList papszOptions = nullptr) override
180 : {
181 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
182 1 : papszOptions);
183 : }
184 : };
185 :
186 : /************************************************************************/
187 : /* ~GDALIHasAttribute() */
188 : /************************************************************************/
189 :
190 : GDALIHasAttribute::~GDALIHasAttribute() = default;
191 :
192 : /************************************************************************/
193 : /* GetAttribute() */
194 : /************************************************************************/
195 :
196 : /** Return an attribute by its name.
197 : *
198 : * If the attribute does not exist, nullptr should be silently returned.
199 : *
200 : * @note Driver implementation: this method will fallback to
201 : * GetAttributeFromAttributes() is not explicitly implemented
202 : *
203 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
204 : *
205 : * This is the same as the C function GDALGroupGetAttribute() or
206 : * GDALMDArrayGetAttribute().
207 : *
208 : * @param osName Attribute name
209 : * @return the attribute, or nullptr if it does not exist or an error occurred.
210 : */
211 : std::shared_ptr<GDALAttribute>
212 1057 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
213 : {
214 1057 : return GetAttributeFromAttributes(osName);
215 : }
216 :
217 : /************************************************************************/
218 : /* GetAttributeFromAttributes() */
219 : /************************************************************************/
220 :
221 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
222 : */
223 : std::shared_ptr<GDALAttribute>
224 1057 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
225 : {
226 2114 : auto attrs(GetAttributes());
227 5493 : for (const auto &attr : attrs)
228 : {
229 5174 : if (attr->GetName() == osName)
230 738 : return attr;
231 : }
232 319 : return nullptr;
233 : }
234 :
235 : /************************************************************************/
236 : /* GetAttributes() */
237 : /************************************************************************/
238 :
239 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
240 : *
241 : * If the attribute does not exist, nullptr should be silently returned.
242 : *
243 : * @note Driver implementation: optionally implemented. If implemented,
244 : * GetAttribute() should also be implemented.
245 : *
246 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
247 : *
248 : * This is the same as the C function GDALGroupGetAttributes() or
249 : * GDALMDArrayGetAttributes().
250 :
251 : * @param papszOptions Driver specific options determining how attributes
252 : * should be retrieved. Pass nullptr for default behavior.
253 : *
254 : * @return the attributes.
255 : */
256 : std::vector<std::shared_ptr<GDALAttribute>>
257 44 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
258 : {
259 44 : return {};
260 : }
261 :
262 : /************************************************************************/
263 : /* CreateAttribute() */
264 : /************************************************************************/
265 :
266 : /** Create an attribute within a GDALMDArray or GDALGroup.
267 : *
268 : * The attribute might not be "physically" created until a value is written
269 : * into it.
270 : *
271 : * Optionally implemented.
272 : *
273 : * Drivers known to implement it: MEM, netCDF
274 : *
275 : * This is the same as the C function GDALGroupCreateAttribute() or
276 : * GDALMDArrayCreateAttribute()
277 : *
278 : * @param osName Attribute name.
279 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
280 : * dimension first to the fastest varying dimension last.
281 : * Empty for a scalar attribute (common case)
282 : * @param oDataType Attribute data type.
283 : * @param papszOptions Driver specific options determining how the attribute.
284 : * should be created.
285 : *
286 : * @return the new attribute, or nullptr if case of error
287 : */
288 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
289 : CPL_UNUSED const std::string &osName,
290 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
291 : CPL_UNUSED const GDALExtendedDataType &oDataType,
292 : CPL_UNUSED CSLConstList papszOptions)
293 : {
294 0 : CPLError(CE_Failure, CPLE_NotSupported,
295 : "CreateAttribute() not implemented");
296 0 : return nullptr;
297 : }
298 :
299 : /************************************************************************/
300 : /* DeleteAttribute() */
301 : /************************************************************************/
302 :
303 : /** Delete an attribute from a GDALMDArray or GDALGroup.
304 : *
305 : * Optionally implemented.
306 : *
307 : * After this call, if a previously obtained instance of the deleted object
308 : * is still alive, no method other than for freeing it should be invoked.
309 : *
310 : * Drivers known to implement it: MEM, netCDF
311 : *
312 : * This is the same as the C function GDALGroupDeleteAttribute() or
313 : * GDALMDArrayDeleteAttribute()
314 : *
315 : * @param osName Attribute name.
316 : * @param papszOptions Driver specific options determining how the attribute.
317 : * should be deleted.
318 : *
319 : * @return true in case of success
320 : * @since GDAL 3.8
321 : */
322 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
323 : CPL_UNUSED CSLConstList papszOptions)
324 : {
325 0 : CPLError(CE_Failure, CPLE_NotSupported,
326 : "DeleteAttribute() not implemented");
327 0 : return false;
328 : }
329 :
330 : /************************************************************************/
331 : /* GDALGroup() */
332 : /************************************************************************/
333 :
334 : //! @cond Doxygen_Suppress
335 6919 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
336 6919 : const std::string &osContext)
337 6919 : : m_osName(osParentName.empty() ? "/" : osName),
338 : m_osFullName(
339 13838 : !osParentName.empty()
340 10713 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
341 : : "/"),
342 17632 : m_osContext(osContext)
343 : {
344 6919 : }
345 :
346 : //! @endcond
347 :
348 : /************************************************************************/
349 : /* ~GDALGroup() */
350 : /************************************************************************/
351 :
352 : GDALGroup::~GDALGroup() = default;
353 :
354 : /************************************************************************/
355 : /* GetMDArrayNames() */
356 : /************************************************************************/
357 :
358 : /** Return the list of multidimensional array names contained in this group.
359 : *
360 : * @note Driver implementation: optionally implemented. If implemented,
361 : * OpenMDArray() should also be implemented.
362 : *
363 : * Drivers known to implement it: MEM, netCDF.
364 : *
365 : * This is the same as the C function GDALGroupGetMDArrayNames().
366 : *
367 : * @param papszOptions Driver specific options determining how arrays
368 : * should be retrieved. Pass nullptr for default behavior.
369 : *
370 : * @return the array names.
371 : */
372 : std::vector<std::string>
373 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
374 : {
375 0 : return {};
376 : }
377 :
378 : /************************************************************************/
379 : /* GetMDArrayFullNamesRecursive() */
380 : /************************************************************************/
381 :
382 : /** Return the list of multidimensional array full names contained in this
383 : * group and its subgroups.
384 : *
385 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
386 : *
387 : * @param papszGroupOptions Driver specific options determining how groups
388 : * should be retrieved. Pass nullptr for default behavior.
389 : * @param papszArrayOptions Driver specific options determining how arrays
390 : * should be retrieved. Pass nullptr for default behavior.
391 : *
392 : * @return the array full names.
393 : *
394 : * @since 3.11
395 : */
396 : std::vector<std::string>
397 3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
398 : CSLConstList papszArrayOptions) const
399 : {
400 3 : std::vector<std::string> ret;
401 6 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
402 3 : stackGroups.push_back(nullptr); // nullptr means this
403 9 : while (!stackGroups.empty())
404 : {
405 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
406 6 : stackGroups.erase(stackGroups.begin());
407 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
408 10 : for (const std::string &arrayName :
409 26 : poCurGroup->GetMDArrayNames(papszArrayOptions))
410 : {
411 20 : std::string osFullName = poCurGroup->GetFullName();
412 10 : if (!osFullName.empty() && osFullName.back() != '/')
413 3 : osFullName += '/';
414 10 : osFullName += arrayName;
415 10 : ret.push_back(std::move(osFullName));
416 : }
417 6 : auto insertionPoint = stackGroups.begin();
418 3 : for (const auto &osSubGroup :
419 12 : poCurGroup->GetGroupNames(papszGroupOptions))
420 : {
421 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
422 3 : if (poSubGroup)
423 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
424 : }
425 : }
426 :
427 6 : return ret;
428 : }
429 :
430 : /************************************************************************/
431 : /* OpenMDArray() */
432 : /************************************************************************/
433 :
434 : /** Open and return a multidimensional array.
435 : *
436 : * @note Driver implementation: optionally implemented. If implemented,
437 : * GetMDArrayNames() should also be implemented.
438 : *
439 : * Drivers known to implement it: MEM, netCDF.
440 : *
441 : * This is the same as the C function GDALGroupOpenMDArray().
442 : *
443 : * @param osName Array name.
444 : * @param papszOptions Driver specific options determining how the array should
445 : * be opened. Pass nullptr for default behavior.
446 : *
447 : * @return the array, or nullptr.
448 : */
449 : std::shared_ptr<GDALMDArray>
450 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
451 : CPL_UNUSED CSLConstList papszOptions) const
452 : {
453 0 : return nullptr;
454 : }
455 :
456 : /************************************************************************/
457 : /* GetGroupNames() */
458 : /************************************************************************/
459 :
460 : /** Return the list of sub-groups contained in this group.
461 : *
462 : * @note Driver implementation: optionally implemented. If implemented,
463 : * OpenGroup() should also be implemented.
464 : *
465 : * Drivers known to implement it: MEM, netCDF.
466 : *
467 : * This is the same as the C function GDALGroupGetGroupNames().
468 : *
469 : * @param papszOptions Driver specific options determining how groups
470 : * should be retrieved. Pass nullptr for default behavior.
471 : *
472 : * @return the group names.
473 : */
474 : std::vector<std::string>
475 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
476 : {
477 4 : return {};
478 : }
479 :
480 : /************************************************************************/
481 : /* OpenGroup() */
482 : /************************************************************************/
483 :
484 : /** Open and return a sub-group.
485 : *
486 : * @note Driver implementation: optionally implemented. If implemented,
487 : * GetGroupNames() should also be implemented.
488 : *
489 : * Drivers known to implement it: MEM, netCDF.
490 : *
491 : * This is the same as the C function GDALGroupOpenGroup().
492 : *
493 : * @param osName Sub-group name.
494 : * @param papszOptions Driver specific options determining how the sub-group
495 : * should be opened. Pass nullptr for default behavior.
496 : *
497 : * @return the group, or nullptr.
498 : */
499 : std::shared_ptr<GDALGroup>
500 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
501 : CPL_UNUSED CSLConstList papszOptions) const
502 : {
503 4 : return nullptr;
504 : }
505 :
506 : /************************************************************************/
507 : /* GetVectorLayerNames() */
508 : /************************************************************************/
509 :
510 : /** Return the list of layer names contained in this group.
511 : *
512 : * @note Driver implementation: optionally implemented. If implemented,
513 : * OpenVectorLayer() should also be implemented.
514 : *
515 : * Drivers known to implement it: OpenFileGDB, FileGDB
516 : *
517 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
518 : * GDALDataset::GetLayer() should then be used.
519 : *
520 : * This is the same as the C function GDALGroupGetVectorLayerNames().
521 : *
522 : * @param papszOptions Driver specific options determining how layers
523 : * should be retrieved. Pass nullptr for default behavior.
524 : *
525 : * @return the vector layer names.
526 : * @since GDAL 3.4
527 : */
528 : std::vector<std::string>
529 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
530 : {
531 1 : return {};
532 : }
533 :
534 : /************************************************************************/
535 : /* OpenVectorLayer() */
536 : /************************************************************************/
537 :
538 : /** Open and return a vector layer.
539 : *
540 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
541 : * lifetime of the returned OGRLayer* is linked to the one of the owner
542 : * dataset (contrary to the general design of this class where objects can be
543 : * used independently of the object that returned them)
544 : *
545 : * @note Driver implementation: optionally implemented. If implemented,
546 : * GetVectorLayerNames() should also be implemented.
547 : *
548 : * Drivers known to implement it: MEM, netCDF.
549 : *
550 : * This is the same as the C function GDALGroupOpenVectorLayer().
551 : *
552 : * @param osName Vector layer name.
553 : * @param papszOptions Driver specific options determining how the layer should
554 : * be opened. Pass nullptr for default behavior.
555 : *
556 : * @return the group, or nullptr.
557 : */
558 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
559 : CPL_UNUSED CSLConstList papszOptions) const
560 : {
561 2 : return nullptr;
562 : }
563 :
564 : /************************************************************************/
565 : /* GetDimensions() */
566 : /************************************************************************/
567 :
568 : /** Return the list of dimensions contained in this group and used by its
569 : * arrays.
570 : *
571 : * This is for dimensions that can potentially be used by several arrays.
572 : * Not all drivers might implement this. To retrieve the dimensions used by
573 : * a specific array, use GDALMDArray::GetDimensions().
574 : *
575 : * Drivers known to implement it: MEM, netCDF
576 : *
577 : * This is the same as the C function GDALGroupGetDimensions().
578 : *
579 : * @param papszOptions Driver specific options determining how groups
580 : * should be retrieved. Pass nullptr for default behavior.
581 : *
582 : * @return the dimensions.
583 : */
584 : std::vector<std::shared_ptr<GDALDimension>>
585 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
586 : {
587 11 : return {};
588 : }
589 :
590 : /************************************************************************/
591 : /* GetStructuralInfo() */
592 : /************************************************************************/
593 :
594 : /** Return structural information on the group.
595 : *
596 : * This may be the compression, etc..
597 : *
598 : * The return value should not be freed and is valid until GDALGroup is
599 : * released or this function called again.
600 : *
601 : * This is the same as the C function GDALGroupGetStructuralInfo().
602 : */
603 41 : CSLConstList GDALGroup::GetStructuralInfo() const
604 : {
605 41 : return nullptr;
606 : }
607 :
608 : /************************************************************************/
609 : /* CreateGroup() */
610 : /************************************************************************/
611 :
612 : /** Create a sub-group within a group.
613 : *
614 : * Optionally implemented by drivers.
615 : *
616 : * Drivers known to implement it: MEM, netCDF
617 : *
618 : * This is the same as the C function GDALGroupCreateGroup().
619 : *
620 : * @param osName Sub-group name.
621 : * @param papszOptions Driver specific options determining how the sub-group
622 : * should be created.
623 : *
624 : * @return the new sub-group, or nullptr in case of error.
625 : */
626 : std::shared_ptr<GDALGroup>
627 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
628 : CPL_UNUSED CSLConstList papszOptions)
629 : {
630 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
631 0 : return nullptr;
632 : }
633 :
634 : /************************************************************************/
635 : /* DeleteGroup() */
636 : /************************************************************************/
637 :
638 : /** Delete a sub-group from a group.
639 : *
640 : * Optionally implemented.
641 : *
642 : * After this call, if a previously obtained instance of the deleted object
643 : * is still alive, no method other than for freeing it should be invoked.
644 : *
645 : * Drivers known to implement it: MEM, Zarr
646 : *
647 : * This is the same as the C function GDALGroupDeleteGroup().
648 : *
649 : * @param osName Sub-group name.
650 : * @param papszOptions Driver specific options determining how the group.
651 : * should be deleted.
652 : *
653 : * @return true in case of success
654 : * @since GDAL 3.8
655 : */
656 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
657 : CPL_UNUSED CSLConstList papszOptions)
658 : {
659 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
660 0 : return false;
661 : }
662 :
663 : /************************************************************************/
664 : /* CreateDimension() */
665 : /************************************************************************/
666 :
667 : /** Create a dimension within a group.
668 : *
669 : * @note Driver implementation: drivers supporting CreateDimension() should
670 : * implement this method, but do not have necessarily to implement
671 : * GDALGroup::GetDimensions().
672 : *
673 : * Drivers known to implement it: MEM, netCDF
674 : *
675 : * This is the same as the C function GDALGroupCreateDimension().
676 : *
677 : * @param osName Dimension name.
678 : * @param osType Dimension type (might be empty, and ignored by drivers)
679 : * @param osDirection Dimension direction (might be empty, and ignored by
680 : * drivers)
681 : * @param nSize Number of values indexed by this dimension. Should be > 0.
682 : * @param papszOptions Driver specific options determining how the dimension
683 : * should be created.
684 : *
685 : * @return the new dimension, or nullptr if case of error
686 : */
687 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
688 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
689 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
690 : CPL_UNUSED CSLConstList papszOptions)
691 : {
692 0 : CPLError(CE_Failure, CPLE_NotSupported,
693 : "CreateDimension() not implemented");
694 0 : return nullptr;
695 : }
696 :
697 : /************************************************************************/
698 : /* CreateMDArray() */
699 : /************************************************************************/
700 :
701 : /** Create a multidimensional array within a group.
702 : *
703 : * It is recommended that the GDALDimension objects passed in aoDimensions
704 : * belong to this group, either by retrieving them with GetDimensions()
705 : * or creating a new one with CreateDimension().
706 : *
707 : * Optionally implemented.
708 : *
709 : * Drivers known to implement it: MEM, netCDF
710 : *
711 : * This is the same as the C function GDALGroupCreateMDArray().
712 : *
713 : * @note Driver implementation: drivers should take into account the possibility
714 : * that GDALDimension object passed in aoDimensions might belong to a different
715 : * group / dataset / driver and act accordingly.
716 : *
717 : * @param osName Array name.
718 : * @param aoDimensions List of dimensions, ordered from the slowest varying
719 : * dimension first to the fastest varying dimension last.
720 : * Might be empty for a scalar array (if supported by
721 : * driver)
722 : * @param oDataType Array data type.
723 : * @param papszOptions Driver specific options determining how the array
724 : * should be created.
725 : *
726 : * @return the new array, or nullptr in case of error
727 : */
728 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
729 : CPL_UNUSED const std::string &osName,
730 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
731 : CPL_UNUSED const GDALExtendedDataType &oDataType,
732 : CPL_UNUSED CSLConstList papszOptions)
733 : {
734 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
735 0 : return nullptr;
736 : }
737 :
738 : /************************************************************************/
739 : /* DeleteMDArray() */
740 : /************************************************************************/
741 :
742 : /** Delete an array from a group.
743 : *
744 : * Optionally implemented.
745 : *
746 : * After this call, if a previously obtained instance of the deleted object
747 : * is still alive, no method other than for freeing it should be invoked.
748 : *
749 : * Drivers known to implement it: MEM, Zarr
750 : *
751 : * This is the same as the C function GDALGroupDeleteMDArray().
752 : *
753 : * @param osName Arrayname.
754 : * @param papszOptions Driver specific options determining how the array.
755 : * should be deleted.
756 : *
757 : * @return true in case of success
758 : * @since GDAL 3.8
759 : */
760 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
761 : CPL_UNUSED CSLConstList papszOptions)
762 : {
763 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
764 0 : return false;
765 : }
766 :
767 : /************************************************************************/
768 : /* GetTotalCopyCost() */
769 : /************************************************************************/
770 :
771 : /** Return a total "cost" to copy the group.
772 : *
773 : * Used as a parameter for CopFrom()
774 : */
775 25 : GUInt64 GDALGroup::GetTotalCopyCost() const
776 : {
777 25 : GUInt64 nCost = COPY_COST;
778 25 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
779 :
780 50 : auto groupNames = GetGroupNames();
781 31 : for (const auto &name : groupNames)
782 : {
783 12 : auto subGroup = OpenGroup(name);
784 6 : if (subGroup)
785 : {
786 6 : nCost += subGroup->GetTotalCopyCost();
787 : }
788 : }
789 :
790 25 : auto arrayNames = GetMDArrayNames();
791 71 : for (const auto &name : arrayNames)
792 : {
793 92 : auto array = OpenMDArray(name);
794 46 : if (array)
795 : {
796 46 : nCost += array->GetTotalCopyCost();
797 : }
798 : }
799 50 : return nCost;
800 : }
801 :
802 : /************************************************************************/
803 : /* CopyFrom() */
804 : /************************************************************************/
805 :
806 : /** Copy the content of a group into a new (generally empty) group.
807 : *
808 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
809 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
810 : * of some output drivers this is not recommended)
811 : * @param poSrcGroup Source group. Must NOT be nullptr.
812 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
813 : * stop the copy. In relaxed mode, the copy will be attempted to
814 : * be pursued.
815 : * @param nCurCost Should be provided as a variable initially set to 0.
816 : * @param nTotalCost Total cost from GetTotalCopyCost().
817 : * @param pfnProgress Progress callback, or nullptr.
818 : * @param pProgressData Progress user data, or nulptr.
819 : * @param papszOptions Creation options. Currently, only array creation
820 : * options are supported. They must be prefixed with
821 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
822 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
823 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
824 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
825 : * Restriction to arrays of a given name is done with adding
826 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
827 : * a full qualified name.
828 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
829 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
830 : * scaled to UInt16 with scale and offset values being computed from the minimum
831 : * and maximum of the source array. The integer data type used can be set with
832 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
833 : *
834 : * @return true in case of success (or partial success if bStrict == false).
835 : */
836 25 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
837 : GDALDataset *poSrcDS,
838 : const std::shared_ptr<GDALGroup> &poSrcGroup,
839 : bool bStrict, GUInt64 &nCurCost,
840 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
841 : void *pProgressData, CSLConstList papszOptions)
842 : {
843 25 : if (pfnProgress == nullptr)
844 0 : pfnProgress = GDALDummyProgress;
845 :
846 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
847 : if (!(x)) \
848 : { \
849 : if (bStrict) \
850 : return false; \
851 : continue; \
852 : } \
853 : (void)0
854 :
855 : try
856 : {
857 25 : nCurCost += GDALGroup::COPY_COST;
858 :
859 50 : const auto srcDims = poSrcGroup->GetDimensions();
860 : std::map<std::string, std::shared_ptr<GDALDimension>>
861 50 : mapExistingDstDims;
862 50 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
863 63 : for (const auto &dim : srcDims)
864 : {
865 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
866 38 : dim->GetDirection(), dim->GetSize());
867 38 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
868 38 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
869 76 : auto poIndexingVarSrc(dim->GetIndexingVariable());
870 38 : if (poIndexingVarSrc)
871 : {
872 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
873 20 : ->GetName()] =
874 40 : dim->GetName();
875 : }
876 : }
877 :
878 50 : auto attrs = poSrcGroup->GetAttributes();
879 32 : for (const auto &attr : attrs)
880 : {
881 : auto dstAttr =
882 7 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
883 14 : attr->GetDataType());
884 7 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
885 7 : auto raw(attr->ReadAsRaw());
886 7 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
887 0 : return false;
888 : }
889 25 : if (!attrs.empty())
890 : {
891 5 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
892 5 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
893 0 : return false;
894 : }
895 :
896 : const auto CopyArray =
897 46 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
898 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
899 : papszOptions, bStrict, &nCurCost,
900 417 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
901 : {
902 : // Map source dimensions to target dimensions
903 92 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
904 46 : const auto &srcArrayDims(srcArray->GetDimensions());
905 118 : for (const auto &dim : srcArrayDims)
906 : {
907 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
908 72 : dim->GetFullName());
909 72 : if (dstDim && dstDim->GetSize() == dim->GetSize())
910 : {
911 62 : dstArrayDims.emplace_back(dstDim);
912 : }
913 : else
914 : {
915 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
916 19 : if (oIter != mapExistingDstDims.end() &&
917 9 : oIter->second->GetSize() == dim->GetSize())
918 : {
919 8 : dstArrayDims.emplace_back(oIter->second);
920 : }
921 : else
922 : {
923 2 : std::string newDimName;
924 2 : if (oIter == mapExistingDstDims.end())
925 : {
926 1 : newDimName = dim->GetName();
927 : }
928 : else
929 : {
930 1 : std::string newDimNamePrefix(srcArray->GetName() +
931 3 : '_' + dim->GetName());
932 1 : newDimName = newDimNamePrefix;
933 1 : int nIterCount = 2;
934 0 : while (
935 1 : cpl::contains(mapExistingDstDims, newDimName))
936 : {
937 0 : newDimName = newDimNamePrefix +
938 0 : CPLSPrintf("_%d", nIterCount);
939 0 : nIterCount++;
940 : }
941 : }
942 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
943 : dim->GetDirection(),
944 4 : dim->GetSize());
945 2 : if (!dstDim)
946 0 : return false;
947 2 : mapExistingDstDims[newDimName] = dstDim;
948 2 : dstArrayDims.emplace_back(dstDim);
949 : }
950 : }
951 : }
952 :
953 92 : CPLStringList aosArrayCO;
954 46 : bool bAutoScale = false;
955 46 : GDALDataType eAutoScaleType = GDT_UInt16;
956 53 : for (const char *pszItem : cpl::Iterate(papszOptions))
957 : {
958 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
959 : {
960 7 : const char *pszOption = pszItem + strlen("ARRAY:");
961 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
962 : {
963 1 : const char *pszNext = strchr(pszOption, ':');
964 1 : if (pszNext != nullptr)
965 : {
966 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
967 1 : if (static_cast<size_t>(nDim) ==
968 1 : dstArrayDims.size())
969 : {
970 1 : pszOption = pszNext + 1;
971 : }
972 : else
973 : {
974 0 : pszOption = nullptr;
975 : }
976 : }
977 : }
978 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
979 : {
980 2 : const char *pszName = pszOption + strlen("IF(NAME=");
981 2 : const char *pszNext = strchr(pszName, ':');
982 2 : if (pszNext != nullptr && pszNext > pszName &&
983 2 : pszNext[-1] == ')')
984 : {
985 4 : CPLString osName;
986 2 : osName.assign(pszName, pszNext - pszName - 1);
987 3 : if (osName == srcArray->GetName() ||
988 1 : osName == srcArray->GetFullName())
989 : {
990 2 : pszOption = pszNext + 1;
991 : }
992 : else
993 : {
994 0 : pszOption = nullptr;
995 : }
996 : }
997 : }
998 7 : if (pszOption)
999 : {
1000 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1001 : {
1002 : bAutoScale =
1003 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1004 : }
1005 5 : else if (STARTS_WITH_CI(pszOption,
1006 : "AUTOSCALE_DATA_TYPE="))
1007 : {
1008 1 : const char *pszDataType =
1009 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1010 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1011 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1012 1 : GDALDataTypeIsFloating(eAutoScaleType))
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_NotSupported,
1015 : "Unsupported value for "
1016 : "AUTOSCALE_DATA_TYPE");
1017 0 : return false;
1018 : }
1019 : }
1020 : else
1021 : {
1022 4 : aosArrayCO.AddString(pszOption);
1023 : }
1024 : }
1025 : }
1026 : }
1027 :
1028 : auto oIterDimName =
1029 46 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1030 46 : const auto &srcArrayType = srcArray->GetDataType();
1031 :
1032 46 : std::shared_ptr<GDALMDArray> dstArray;
1033 :
1034 : // Only autoscale non-indexing variables
1035 46 : bool bHasOffset = false;
1036 46 : bool bHasScale = false;
1037 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1038 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1039 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1040 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1041 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1042 50 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1043 48 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1044 : {
1045 2 : constexpr bool bApproxOK = false;
1046 2 : constexpr bool bForce = true;
1047 2 : double dfMin = 0.0;
1048 2 : double dfMax = 0.0;
1049 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1050 : nullptr, nullptr, nullptr, nullptr,
1051 2 : nullptr) != CE_None)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "Could not retrieve statistics for array %s",
1055 0 : srcArray->GetName().c_str());
1056 0 : return false;
1057 : }
1058 2 : double dfDTMin = 0;
1059 2 : double dfDTMax = 0;
1060 : #define setDTMinMax(ctype) \
1061 : do \
1062 : { \
1063 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1064 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1065 : } while (0)
1066 :
1067 2 : switch (eAutoScaleType)
1068 : {
1069 0 : case GDT_Byte:
1070 0 : setDTMinMax(GByte);
1071 0 : break;
1072 0 : case GDT_Int8:
1073 0 : setDTMinMax(GInt8);
1074 0 : break;
1075 1 : case GDT_UInt16:
1076 1 : setDTMinMax(GUInt16);
1077 1 : break;
1078 1 : case GDT_Int16:
1079 1 : setDTMinMax(GInt16);
1080 1 : break;
1081 0 : case GDT_UInt32:
1082 0 : setDTMinMax(GUInt32);
1083 0 : break;
1084 0 : case GDT_Int32:
1085 0 : setDTMinMax(GInt32);
1086 0 : break;
1087 0 : case GDT_UInt64:
1088 0 : setDTMinMax(std::uint64_t);
1089 0 : break;
1090 0 : case GDT_Int64:
1091 0 : setDTMinMax(std::int64_t);
1092 0 : break;
1093 0 : case GDT_Float16:
1094 : case GDT_Float32:
1095 : case GDT_Float64:
1096 : case GDT_Unknown:
1097 : case GDT_CInt16:
1098 : case GDT_CInt32:
1099 : case GDT_CFloat16:
1100 : case GDT_CFloat32:
1101 : case GDT_CFloat64:
1102 : case GDT_TypeCount:
1103 0 : CPLAssert(false);
1104 : }
1105 :
1106 : dstArray =
1107 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1108 4 : GDALExtendedDataType::Create(eAutoScaleType),
1109 4 : aosArrayCO.List());
1110 2 : if (!dstArray)
1111 0 : return !bStrict;
1112 :
1113 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1114 : {
1115 : // If there's a nodata value in the source array, reserve
1116 : // DTMax for that purpose in the target scaled array
1117 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AppDefined,
1120 : "Cannot set nodata value");
1121 0 : return false;
1122 : }
1123 1 : dfDTMax--;
1124 : }
1125 2 : const double dfScale =
1126 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1127 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1128 :
1129 4 : if (!dstArray->SetOffset(dfOffset) ||
1130 2 : !dstArray->SetScale(dfScale))
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Cannot set scale/offset");
1134 0 : return false;
1135 : }
1136 :
1137 2 : auto poUnscaled = dstArray->GetUnscaled();
1138 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1139 : {
1140 1 : poUnscaled->SetNoDataValue(
1141 : srcArray->GetNoDataValueAsDouble());
1142 : }
1143 :
1144 : // Copy source array into unscaled array
1145 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1146 : nCurCost, nTotalCost, pfnProgress,
1147 2 : pProgressData))
1148 : {
1149 0 : return false;
1150 : }
1151 : }
1152 : else
1153 : {
1154 88 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1155 88 : srcArrayType, aosArrayCO.List());
1156 44 : if (!dstArray)
1157 0 : return !bStrict;
1158 :
1159 88 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1160 : nCurCost, nTotalCost, pfnProgress,
1161 44 : pProgressData))
1162 : {
1163 0 : return false;
1164 : }
1165 : }
1166 :
1167 : // If this array is the indexing variable of a dimension, link them
1168 : // together.
1169 46 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1170 : {
1171 : auto oCorrespondingDimIter =
1172 20 : mapExistingDstDims.find(oIterDimName->second);
1173 20 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1174 : {
1175 : CPLErrorStateBackuper oErrorStateBackuper(
1176 20 : CPLQuietErrorHandler);
1177 40 : oCorrespondingDimIter->second->SetIndexingVariable(
1178 20 : std::move(dstArray));
1179 : }
1180 : }
1181 :
1182 46 : return true;
1183 25 : };
1184 :
1185 50 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1186 :
1187 : // Start by copying arrays that are indexing variables of dimensions
1188 71 : for (const auto &name : arrayNames)
1189 : {
1190 46 : auto srcArray = poSrcGroup->OpenMDArray(name);
1191 46 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1192 :
1193 46 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1194 46 : srcArray->GetName()))
1195 : {
1196 20 : if (!CopyArray(srcArray))
1197 0 : return false;
1198 : }
1199 : }
1200 :
1201 : // Then copy regular arrays
1202 71 : for (const auto &name : arrayNames)
1203 : {
1204 46 : auto srcArray = poSrcGroup->OpenMDArray(name);
1205 46 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1206 :
1207 46 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1208 46 : srcArray->GetName()))
1209 : {
1210 26 : if (!CopyArray(srcArray))
1211 0 : return false;
1212 : }
1213 : }
1214 :
1215 50 : const auto groupNames = poSrcGroup->GetGroupNames();
1216 31 : for (const auto &name : groupNames)
1217 : {
1218 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1219 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1220 6 : auto dstSubGroup = CreateGroup(name);
1221 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1222 12 : if (!dstSubGroup->CopyFrom(
1223 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1224 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1225 0 : return false;
1226 : }
1227 :
1228 25 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1229 0 : return false;
1230 :
1231 25 : return true;
1232 : }
1233 0 : catch (const std::exception &e)
1234 : {
1235 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1236 0 : return false;
1237 : }
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* GetInnerMostGroup() */
1242 : /************************************************************************/
1243 :
1244 : //! @cond Doxygen_Suppress
1245 : const GDALGroup *
1246 1285 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1247 : std::shared_ptr<GDALGroup> &curGroupHolder,
1248 : std::string &osLastPart) const
1249 : {
1250 1285 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1251 6 : return nullptr;
1252 1279 : const GDALGroup *poCurGroup = this;
1253 : CPLStringList aosTokens(
1254 2558 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1255 1279 : if (aosTokens.size() == 0)
1256 : {
1257 0 : return nullptr;
1258 : }
1259 :
1260 1620 : for (int i = 0; i < aosTokens.size() - 1; i++)
1261 : {
1262 353 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1263 353 : if (!curGroupHolder)
1264 : {
1265 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1266 : aosTokens[i]);
1267 12 : return nullptr;
1268 : }
1269 341 : poCurGroup = curGroupHolder.get();
1270 : }
1271 1267 : osLastPart = aosTokens[aosTokens.size() - 1];
1272 1267 : return poCurGroup;
1273 : }
1274 :
1275 : //! @endcond
1276 :
1277 : /************************************************************************/
1278 : /* OpenMDArrayFromFullname() */
1279 : /************************************************************************/
1280 :
1281 : /** Get an array from its fully qualified name */
1282 : std::shared_ptr<GDALMDArray>
1283 541 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1284 : CSLConstList papszOptions) const
1285 : {
1286 1082 : std::string osName;
1287 541 : std::shared_ptr<GDALGroup> curGroupHolder;
1288 541 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1289 541 : if (poGroup == nullptr)
1290 12 : return nullptr;
1291 529 : return poGroup->OpenMDArray(osName, papszOptions);
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* OpenAttributeFromFullname() */
1296 : /************************************************************************/
1297 :
1298 : /** Get an attribute from its fully qualified name */
1299 : std::shared_ptr<GDALAttribute>
1300 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1301 : CSLConstList papszOptions) const
1302 : {
1303 9 : const auto pos = osFullName.rfind('/');
1304 9 : if (pos == std::string::npos)
1305 0 : return nullptr;
1306 18 : const std::string attrName = osFullName.substr(pos + 1);
1307 9 : if (pos == 0)
1308 2 : return GetAttribute(attrName);
1309 14 : const std::string container = osFullName.substr(0, pos);
1310 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1311 7 : if (poArray)
1312 4 : return poArray->GetAttribute(attrName);
1313 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1314 3 : if (poGroup)
1315 1 : return poGroup->GetAttribute(attrName);
1316 2 : return nullptr;
1317 : }
1318 :
1319 : /************************************************************************/
1320 : /* ResolveMDArray() */
1321 : /************************************************************************/
1322 :
1323 : /** Locate an array in a group and its subgroups by name.
1324 : *
1325 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1326 : * used
1327 : * Otherwise the search will start from the group identified by osStartingPath,
1328 : * and an array whose name is osName will be looked for in this group (if
1329 : * osStartingPath is empty or "/", then the current group is used). If there
1330 : * is no match, then a recursive descendent search will be made in its
1331 : * subgroups. If there is no match in the subgroups, then the parent (if
1332 : * existing) of the group pointed by osStartingPath will be used as the new
1333 : * starting point for the search.
1334 : *
1335 : * @param osName name, qualified or not
1336 : * @param osStartingPath fully qualified name of the (sub-)group from which
1337 : * the search should be started. If this is a non-empty
1338 : * string, the group on which this method is called should
1339 : * nominally be the root group (otherwise the path will
1340 : * be interpreted as from the current group)
1341 : * @param papszOptions options to pass to OpenMDArray()
1342 : * @since GDAL 3.2
1343 : */
1344 : std::shared_ptr<GDALMDArray>
1345 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1346 : const std::string &osStartingPath,
1347 : CSLConstList papszOptions) const
1348 : {
1349 19 : if (!osName.empty() && osName[0] == '/')
1350 : {
1351 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1352 1 : if (poArray)
1353 1 : return poArray;
1354 : }
1355 36 : std::string osPath(osStartingPath);
1356 36 : std::set<std::string> oSetAlreadyVisited;
1357 :
1358 : while (true)
1359 : {
1360 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1361 0 : std::shared_ptr<GDALGroup> poGroup;
1362 :
1363 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1364 22 : bool goOn = false;
1365 22 : if (osPath.empty() || osPath == "/")
1366 : {
1367 11 : goOn = true;
1368 : }
1369 : else
1370 : {
1371 22 : std::string osLastPart;
1372 : const GDALGroup *poGroupPtr =
1373 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1374 11 : if (poGroupPtr)
1375 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1376 22 : if (poGroup &&
1377 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1378 : {
1379 11 : oQueue.push(poGroup);
1380 11 : goOn = true;
1381 : }
1382 : }
1383 :
1384 22 : if (goOn)
1385 : {
1386 17 : do
1387 : {
1388 : const GDALGroup *groupPtr;
1389 39 : if (!oQueue.empty())
1390 : {
1391 28 : poGroup = oQueue.front();
1392 28 : oQueue.pop();
1393 28 : groupPtr = poGroup.get();
1394 : }
1395 : else
1396 : {
1397 11 : groupPtr = this;
1398 : }
1399 :
1400 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1401 39 : if (poArray)
1402 16 : return poArray;
1403 :
1404 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1405 47 : for (const auto &osGroupName : aosGroupNames)
1406 : {
1407 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1408 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1409 48 : poSubGroup->GetFullName()))
1410 : {
1411 24 : oQueue.push(poSubGroup);
1412 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1413 : }
1414 : }
1415 23 : } while (!oQueue.empty());
1416 : }
1417 :
1418 6 : if (osPath.empty() || osPath == "/")
1419 2 : break;
1420 :
1421 4 : const auto nPos = osPath.rfind('/');
1422 4 : if (nPos == 0)
1423 1 : osPath = "/";
1424 : else
1425 : {
1426 3 : if (nPos == std::string::npos)
1427 0 : break;
1428 3 : osPath.resize(nPos);
1429 : }
1430 4 : }
1431 2 : return nullptr;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* OpenGroupFromFullname() */
1436 : /************************************************************************/
1437 :
1438 : /** Get a group from its fully qualified name.
1439 : * @since GDAL 3.2
1440 : */
1441 : std::shared_ptr<GDALGroup>
1442 566 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1443 : CSLConstList papszOptions) const
1444 : {
1445 1132 : std::string osName;
1446 566 : std::shared_ptr<GDALGroup> curGroupHolder;
1447 566 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1448 566 : if (poGroup == nullptr)
1449 4 : return nullptr;
1450 562 : return poGroup->OpenGroup(osName, papszOptions);
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* OpenDimensionFromFullname() */
1455 : /************************************************************************/
1456 :
1457 : /** Get a dimension from its fully qualified name */
1458 : std::shared_ptr<GDALDimension>
1459 167 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1460 : {
1461 334 : std::string osName;
1462 167 : std::shared_ptr<GDALGroup> curGroupHolder;
1463 167 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1464 167 : if (poGroup == nullptr)
1465 2 : return nullptr;
1466 330 : auto dims(poGroup->GetDimensions());
1467 286 : for (auto &dim : dims)
1468 : {
1469 241 : if (dim->GetName() == osName)
1470 120 : return dim;
1471 : }
1472 45 : return nullptr;
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* ClearStatistics() */
1477 : /************************************************************************/
1478 :
1479 : /**
1480 : * \brief Clear statistics.
1481 : *
1482 : * @since GDAL 3.4
1483 : */
1484 0 : void GDALGroup::ClearStatistics()
1485 : {
1486 0 : auto groupNames = GetGroupNames();
1487 0 : for (const auto &name : groupNames)
1488 : {
1489 0 : auto subGroup = OpenGroup(name);
1490 0 : if (subGroup)
1491 : {
1492 0 : subGroup->ClearStatistics();
1493 : }
1494 : }
1495 :
1496 0 : auto arrayNames = GetMDArrayNames();
1497 0 : for (const auto &name : arrayNames)
1498 : {
1499 0 : auto array = OpenMDArray(name);
1500 0 : if (array)
1501 : {
1502 0 : array->ClearStatistics();
1503 : }
1504 : }
1505 0 : }
1506 :
1507 : /************************************************************************/
1508 : /* Rename() */
1509 : /************************************************************************/
1510 :
1511 : /** Rename the group.
1512 : *
1513 : * This is not implemented by all drivers.
1514 : *
1515 : * Drivers known to implement it: MEM, netCDF, ZARR.
1516 : *
1517 : * This is the same as the C function GDALGroupRename().
1518 : *
1519 : * @param osNewName New name.
1520 : *
1521 : * @return true in case of success
1522 : * @since GDAL 3.8
1523 : */
1524 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1525 : {
1526 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1527 0 : return false;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* BaseRename() */
1532 : /************************************************************************/
1533 :
1534 : //! @cond Doxygen_Suppress
1535 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1536 : {
1537 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1538 8 : m_osFullName += osNewName;
1539 8 : m_osName = osNewName;
1540 :
1541 8 : NotifyChildrenOfRenaming();
1542 8 : }
1543 :
1544 : //! @endcond
1545 :
1546 : /************************************************************************/
1547 : /* ParentRenamed() */
1548 : /************************************************************************/
1549 :
1550 : //! @cond Doxygen_Suppress
1551 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1552 : {
1553 7 : m_osFullName = osNewParentFullName;
1554 7 : m_osFullName += "/";
1555 7 : m_osFullName += m_osName;
1556 :
1557 7 : NotifyChildrenOfRenaming();
1558 7 : }
1559 :
1560 : //! @endcond
1561 :
1562 : /************************************************************************/
1563 : /* Deleted() */
1564 : /************************************************************************/
1565 :
1566 : //! @cond Doxygen_Suppress
1567 22 : void GDALGroup::Deleted()
1568 : {
1569 22 : m_bValid = false;
1570 :
1571 22 : NotifyChildrenOfDeletion();
1572 22 : }
1573 :
1574 : //! @endcond
1575 :
1576 : /************************************************************************/
1577 : /* ParentDeleted() */
1578 : /************************************************************************/
1579 :
1580 : //! @cond Doxygen_Suppress
1581 3 : void GDALGroup::ParentDeleted()
1582 : {
1583 3 : Deleted();
1584 3 : }
1585 :
1586 : //! @endcond
1587 :
1588 : /************************************************************************/
1589 : /* CheckValidAndErrorOutIfNot() */
1590 : /************************************************************************/
1591 :
1592 : //! @cond Doxygen_Suppress
1593 12487 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1594 : {
1595 12487 : if (!m_bValid)
1596 : {
1597 14 : CPLError(CE_Failure, CPLE_AppDefined,
1598 : "This object has been deleted. No action on it is possible");
1599 : }
1600 12487 : return m_bValid;
1601 : }
1602 :
1603 : //! @endcond
1604 :
1605 : /************************************************************************/
1606 : /* ~GDALAbstractMDArray() */
1607 : /************************************************************************/
1608 :
1609 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1610 :
1611 : /************************************************************************/
1612 : /* GDALAbstractMDArray() */
1613 : /************************************************************************/
1614 :
1615 : //! @cond Doxygen_Suppress
1616 20969 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1617 20969 : const std::string &osName)
1618 : : m_osName(osName),
1619 : m_osFullName(
1620 20969 : !osParentName.empty()
1621 40235 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1622 82173 : : osName)
1623 : {
1624 20969 : }
1625 :
1626 : //! @endcond
1627 :
1628 : /************************************************************************/
1629 : /* GetDimensions() */
1630 : /************************************************************************/
1631 :
1632 : /** \fn GDALAbstractMDArray::GetDimensions() const
1633 : * \brief Return the dimensions of an attribute/array.
1634 : *
1635 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1636 : * similar to GDALAttributeGetDimensionsSize().
1637 : */
1638 :
1639 : /************************************************************************/
1640 : /* GetDataType() */
1641 : /************************************************************************/
1642 :
1643 : /** \fn GDALAbstractMDArray::GetDataType() const
1644 : * \brief Return the data type of an attribute/array.
1645 : *
1646 : * This is the same as the C functions GDALMDArrayGetDataType() and
1647 : * GDALAttributeGetDataType()
1648 : */
1649 :
1650 : /************************************************************************/
1651 : /* GetDimensionCount() */
1652 : /************************************************************************/
1653 :
1654 : /** Return the number of dimensions.
1655 : *
1656 : * Default implementation is GetDimensions().size(), and may be overridden by
1657 : * drivers if they have a faster / less expensive implementations.
1658 : *
1659 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1660 : * GDALAttributeGetDimensionCount().
1661 : *
1662 : */
1663 23561 : size_t GDALAbstractMDArray::GetDimensionCount() const
1664 : {
1665 23561 : return GetDimensions().size();
1666 : }
1667 :
1668 : /************************************************************************/
1669 : /* Rename() */
1670 : /************************************************************************/
1671 :
1672 : /** Rename the attribute/array.
1673 : *
1674 : * This is not implemented by all drivers.
1675 : *
1676 : * Drivers known to implement it: MEM, netCDF, Zarr.
1677 : *
1678 : * This is the same as the C functions GDALMDArrayRename() or
1679 : * GDALAttributeRename().
1680 : *
1681 : * @param osNewName New name.
1682 : *
1683 : * @return true in case of success
1684 : * @since GDAL 3.8
1685 : */
1686 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1689 0 : return false;
1690 : }
1691 :
1692 : /************************************************************************/
1693 : /* CopyValue() */
1694 : /************************************************************************/
1695 :
1696 : /** Convert a value from a source type to a destination type.
1697 : *
1698 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1699 : * that must be freed with CPLFree().
1700 : */
1701 80790 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1702 : const GDALExtendedDataType &srcType,
1703 : void *pDst,
1704 : const GDALExtendedDataType &dstType)
1705 : {
1706 157666 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1707 76876 : dstType.GetClass() == GEDTC_NUMERIC)
1708 : {
1709 76411 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1710 : dstType.GetNumericDataType(), 0, 1);
1711 76411 : return true;
1712 : }
1713 8077 : if (srcType.GetClass() == GEDTC_STRING &&
1714 3698 : dstType.GetClass() == GEDTC_STRING)
1715 : {
1716 : const char *srcStrPtr;
1717 3265 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1718 3265 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1719 3265 : *reinterpret_cast<void **>(pDst) = pszDup;
1720 3265 : return true;
1721 : }
1722 1579 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1723 465 : dstType.GetClass() == GEDTC_STRING)
1724 : {
1725 465 : const char *str = nullptr;
1726 465 : switch (srcType.GetNumericDataType())
1727 : {
1728 0 : case GDT_Unknown:
1729 0 : break;
1730 0 : case GDT_Byte:
1731 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1732 0 : break;
1733 3 : case GDT_Int8:
1734 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1735 3 : break;
1736 48 : case GDT_UInt16:
1737 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1738 48 : break;
1739 0 : case GDT_Int16:
1740 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1741 0 : break;
1742 8 : case GDT_UInt32:
1743 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1744 8 : break;
1745 60 : case GDT_Int32:
1746 60 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1747 60 : break;
1748 0 : case GDT_UInt64:
1749 : str =
1750 0 : CPLSPrintf(CPL_FRMT_GUIB,
1751 : static_cast<GUIntBig>(
1752 : *static_cast<const std::uint64_t *>(pSrc)));
1753 0 : break;
1754 24 : case GDT_Int64:
1755 24 : str = CPLSPrintf(CPL_FRMT_GIB,
1756 : static_cast<GIntBig>(
1757 : *static_cast<const std::int64_t *>(pSrc)));
1758 24 : break;
1759 0 : case GDT_Float16:
1760 0 : str = CPLSPrintf("%.5g",
1761 : double(*static_cast<const GFloat16 *>(pSrc)));
1762 0 : break;
1763 17 : case GDT_Float32:
1764 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1765 17 : break;
1766 303 : case GDT_Float64:
1767 303 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1768 303 : break;
1769 2 : case GDT_CInt16:
1770 : {
1771 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1772 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1773 2 : break;
1774 : }
1775 0 : case GDT_CInt32:
1776 : {
1777 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1778 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1779 0 : break;
1780 : }
1781 0 : case GDT_CFloat16:
1782 : {
1783 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1784 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1785 0 : break;
1786 : }
1787 0 : case GDT_CFloat32:
1788 : {
1789 0 : const float *src = static_cast<const float *>(pSrc);
1790 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1791 0 : break;
1792 : }
1793 0 : case GDT_CFloat64:
1794 : {
1795 0 : const double *src = static_cast<const double *>(pSrc);
1796 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1797 0 : break;
1798 : }
1799 0 : case GDT_TypeCount:
1800 0 : CPLAssert(false);
1801 : break;
1802 : }
1803 465 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1804 465 : *reinterpret_cast<void **>(pDst) = pszDup;
1805 465 : return true;
1806 : }
1807 1082 : if (srcType.GetClass() == GEDTC_STRING &&
1808 433 : dstType.GetClass() == GEDTC_NUMERIC)
1809 : {
1810 : const char *srcStrPtr;
1811 433 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1812 433 : if (dstType.GetNumericDataType() == GDT_Int64)
1813 : {
1814 2 : *(static_cast<int64_t *>(pDst)) =
1815 2 : srcStrPtr == nullptr ? 0
1816 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1817 : }
1818 431 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1819 : {
1820 2 : *(static_cast<uint64_t *>(pDst)) =
1821 2 : srcStrPtr == nullptr
1822 2 : ? 0
1823 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1824 : }
1825 : else
1826 : {
1827 : // FIXME GDT_UInt64
1828 429 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1829 429 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1830 : dstType.GetNumericDataType(), 0, 1);
1831 : }
1832 433 : return true;
1833 : }
1834 432 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1835 216 : dstType.GetClass() == GEDTC_COMPOUND)
1836 : {
1837 216 : const auto &srcComponents = srcType.GetComponents();
1838 216 : const auto &dstComponents = dstType.GetComponents();
1839 216 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1840 216 : GByte *pabyDst = static_cast<GByte *>(pDst);
1841 :
1842 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1843 432 : srcComponentMap;
1844 1031 : for (const auto &srcComp : srcComponents)
1845 : {
1846 815 : srcComponentMap[srcComp->GetName()] = &srcComp;
1847 : }
1848 586 : for (const auto &dstComp : dstComponents)
1849 : {
1850 370 : auto oIter = srcComponentMap.find(dstComp->GetName());
1851 370 : if (oIter == srcComponentMap.end())
1852 0 : return false;
1853 370 : const auto &srcComp = *(oIter->second);
1854 1110 : if (!GDALExtendedDataType::CopyValue(
1855 370 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1856 370 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1857 : {
1858 0 : return false;
1859 : }
1860 : }
1861 216 : return true;
1862 : }
1863 :
1864 0 : return false;
1865 : }
1866 :
1867 : /************************************************************************/
1868 : /* CopyValues() */
1869 : /************************************************************************/
1870 :
1871 : /** Convert severals value from a source type to a destination type.
1872 : *
1873 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1874 : * that must be freed with CPLFree().
1875 : */
1876 365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1877 : const GDALExtendedDataType &srcType,
1878 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1879 : const GDALExtendedDataType &dstType,
1880 : GPtrDiff_t nDstStrideInElts,
1881 : size_t nValues)
1882 : {
1883 : const auto nSrcStrideInBytes =
1884 365 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1885 : const auto nDstStrideInBytes =
1886 365 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1887 631 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1888 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1889 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1890 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1891 897 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1892 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1893 : {
1894 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1895 : static_cast<int>(nSrcStrideInBytes), pDst,
1896 : dstType.GetNumericDataType(),
1897 : static_cast<int>(nDstStrideInBytes), nValues);
1898 : }
1899 : else
1900 : {
1901 99 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1902 99 : GByte *pabyDst = static_cast<GByte *>(pDst);
1903 198 : for (size_t i = 0; i < nValues; ++i)
1904 : {
1905 99 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1906 0 : return false;
1907 99 : pabySrc += nSrcStrideInBytes;
1908 99 : pabyDst += nDstStrideInBytes;
1909 : }
1910 : }
1911 365 : return true;
1912 : }
1913 :
1914 : /************************************************************************/
1915 : /* CheckReadWriteParams() */
1916 : /************************************************************************/
1917 : //! @cond Doxygen_Suppress
1918 8216 : bool GDALAbstractMDArray::CheckReadWriteParams(
1919 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1920 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1921 : const void *buffer, const void *buffer_alloc_start,
1922 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1923 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1924 : {
1925 0 : const auto lamda_error = []()
1926 : {
1927 0 : CPLError(CE_Failure, CPLE_AppDefined,
1928 : "Not all elements pointed by buffer will fit in "
1929 : "[buffer_alloc_start, "
1930 : "buffer_alloc_start + buffer_alloc_size]");
1931 0 : };
1932 :
1933 8216 : const auto &dims = GetDimensions();
1934 8216 : if (dims.empty())
1935 : {
1936 3151 : if (buffer_alloc_start)
1937 : {
1938 2774 : const size_t elementSize = bufferDataType.GetSize();
1939 2774 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1940 2774 : const GByte *paby_buffer_alloc_start =
1941 : static_cast<const GByte *>(buffer_alloc_start);
1942 2774 : const GByte *paby_buffer_alloc_end =
1943 : paby_buffer_alloc_start + buffer_alloc_size;
1944 :
1945 2774 : if (paby_buffer < paby_buffer_alloc_start ||
1946 2774 : paby_buffer + elementSize > paby_buffer_alloc_end)
1947 : {
1948 0 : lamda_error();
1949 0 : return false;
1950 : }
1951 : }
1952 3151 : return true;
1953 : }
1954 :
1955 5065 : if (arrayStep == nullptr)
1956 : {
1957 1364 : tmp_arrayStep.resize(dims.size(), 1);
1958 1364 : arrayStep = tmp_arrayStep.data();
1959 : }
1960 14207 : for (size_t i = 0; i < dims.size(); i++)
1961 : {
1962 9142 : assert(count);
1963 9142 : if (count[i] == 0)
1964 : {
1965 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1966 : static_cast<unsigned>(i));
1967 0 : return false;
1968 : }
1969 : }
1970 5065 : bool bufferStride_all_positive = true;
1971 5065 : if (bufferStride == nullptr)
1972 : {
1973 1066 : GPtrDiff_t stride = 1;
1974 1066 : assert(dims.empty() || count != nullptr);
1975 : // To compute strides we must proceed from the fastest varying dimension
1976 : // (the last one), and then reverse the result
1977 2427 : for (size_t i = dims.size(); i != 0;)
1978 : {
1979 1361 : --i;
1980 1361 : tmp_bufferStride.push_back(stride);
1981 1361 : GUInt64 newStride = 0;
1982 : bool bOK;
1983 : try
1984 : {
1985 1361 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1986 2722 : CPLSM(static_cast<uint64_t>(count[i])))
1987 1361 : .v();
1988 1361 : bOK = static_cast<size_t>(newStride) == newStride &&
1989 1361 : newStride < std::numeric_limits<size_t>::max() / 2;
1990 : }
1991 0 : catch (...)
1992 : {
1993 0 : bOK = false;
1994 : }
1995 1361 : if (!bOK)
1996 : {
1997 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1998 0 : return false;
1999 : }
2000 1361 : stride = static_cast<GPtrDiff_t>(newStride);
2001 : }
2002 1066 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2003 1066 : bufferStride = tmp_bufferStride.data();
2004 : }
2005 : else
2006 : {
2007 11778 : for (size_t i = 0; i < dims.size(); i++)
2008 : {
2009 7780 : if (bufferStride[i] < 0)
2010 : {
2011 1 : bufferStride_all_positive = false;
2012 1 : break;
2013 : }
2014 : }
2015 : }
2016 14178 : for (size_t i = 0; i < dims.size(); i++)
2017 : {
2018 9123 : assert(arrayStartIdx);
2019 9123 : assert(count);
2020 9123 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2021 : {
2022 2 : CPLError(CE_Failure, CPLE_AppDefined,
2023 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2024 : static_cast<unsigned>(i),
2025 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2026 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2027 2 : return false;
2028 : }
2029 : bool bOverflow;
2030 9121 : if (arrayStep[i] >= 0)
2031 : {
2032 : try
2033 : {
2034 8526 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2035 8528 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2036 34107 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2037 8526 : .v() >= dims[i]->GetSize();
2038 : }
2039 1 : catch (...)
2040 : {
2041 1 : bOverflow = true;
2042 : }
2043 8527 : if (bOverflow)
2044 : {
2045 5 : CPLError(CE_Failure, CPLE_AppDefined,
2046 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2047 : ">= " CPL_FRMT_GUIB,
2048 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2049 : static_cast<unsigned>(i),
2050 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2051 5 : return false;
2052 : }
2053 : }
2054 : else
2055 : {
2056 : try
2057 : {
2058 594 : bOverflow =
2059 594 : arrayStartIdx[i] <
2060 594 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2061 1188 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2062 : ? (static_cast<uint64_t>(1) << 63)
2063 1188 : : static_cast<uint64_t>(-arrayStep[i])))
2064 594 : .v();
2065 : }
2066 0 : catch (...)
2067 : {
2068 0 : bOverflow = true;
2069 : }
2070 594 : if (bOverflow)
2071 : {
2072 3 : CPLError(
2073 : CE_Failure, CPLE_AppDefined,
2074 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2075 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2076 : static_cast<unsigned>(i));
2077 3 : return false;
2078 : }
2079 : }
2080 : }
2081 :
2082 5055 : if (buffer_alloc_start)
2083 : {
2084 2593 : const size_t elementSize = bufferDataType.GetSize();
2085 2593 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2086 2593 : const GByte *paby_buffer_alloc_start =
2087 : static_cast<const GByte *>(buffer_alloc_start);
2088 2593 : const GByte *paby_buffer_alloc_end =
2089 : paby_buffer_alloc_start + buffer_alloc_size;
2090 2593 : if (bufferStride_all_positive)
2091 : {
2092 2593 : if (paby_buffer < paby_buffer_alloc_start)
2093 : {
2094 0 : lamda_error();
2095 0 : return false;
2096 : }
2097 2593 : GUInt64 nOffset = elementSize;
2098 7436 : for (size_t i = 0; i < dims.size(); i++)
2099 : {
2100 : try
2101 : {
2102 4843 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2103 4843 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2104 9686 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2105 19372 : CPLSM(static_cast<uint64_t>(elementSize)))
2106 4843 : .v();
2107 : }
2108 0 : catch (...)
2109 : {
2110 0 : lamda_error();
2111 0 : return false;
2112 : }
2113 : }
2114 : #if SIZEOF_VOIDP == 4
2115 : if (static_cast<size_t>(nOffset) != nOffset)
2116 : {
2117 : lamda_error();
2118 : return false;
2119 : }
2120 : #endif
2121 2593 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2122 : {
2123 0 : lamda_error();
2124 0 : return false;
2125 : }
2126 : }
2127 0 : else if (dims.size() < 31)
2128 : {
2129 : // Check all corners of the hypercube
2130 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2131 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2132 : {
2133 0 : const GByte *paby = paby_buffer;
2134 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2135 : i++)
2136 : {
2137 0 : if (iCornerCode & (1U << i))
2138 : {
2139 : // We should check for integer overflows
2140 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2141 : }
2142 : }
2143 0 : if (paby < paby_buffer_alloc_start ||
2144 0 : paby + elementSize > paby_buffer_alloc_end)
2145 : {
2146 0 : lamda_error();
2147 0 : return false;
2148 : }
2149 : }
2150 : }
2151 : }
2152 :
2153 5055 : return true;
2154 : }
2155 :
2156 : //! @endcond
2157 :
2158 : /************************************************************************/
2159 : /* Read() */
2160 : /************************************************************************/
2161 :
2162 : /** Read part or totality of a multidimensional array or attribute.
2163 : *
2164 : * This will extract the content of a hyper-rectangle from the array into
2165 : * a user supplied buffer.
2166 : *
2167 : * If bufferDataType is of type string, the values written in pDstBuffer
2168 : * will be char* pointers and the strings should be freed with CPLFree().
2169 : *
2170 : * This is the same as the C function GDALMDArrayRead().
2171 : *
2172 : * @param arrayStartIdx Values representing the starting index to read
2173 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2174 : * Array of GetDimensionCount() values. Must not be
2175 : * nullptr, unless for a zero-dimensional array.
2176 : *
2177 : * @param count Values representing the number of values to extract in
2178 : * each dimension.
2179 : * Array of GetDimensionCount() values. Must not be
2180 : * nullptr, unless for a zero-dimensional array.
2181 : *
2182 : * @param arrayStep Spacing between values to extract in each dimension.
2183 : * The spacing is in number of array elements, not bytes.
2184 : * If provided, must contain GetDimensionCount() values.
2185 : * If set to nullptr, [1, 1, ... 1] will be used as a
2186 : * default to indicate consecutive elements.
2187 : *
2188 : * @param bufferStride Spacing between values to store in pDstBuffer.
2189 : * The spacing is in number of array elements, not bytes.
2190 : * If provided, must contain GetDimensionCount() values.
2191 : * Negative values are possible (for example to reorder
2192 : * from bottom-to-top to top-to-bottom).
2193 : * If set to nullptr, will be set so that pDstBuffer is
2194 : * written in a compact way, with elements of the last /
2195 : * fastest varying dimension being consecutive.
2196 : *
2197 : * @param bufferDataType Data type of values in pDstBuffer.
2198 : *
2199 : * @param pDstBuffer User buffer to store the values read. Should be big
2200 : * enough to store the number of values indicated by
2201 : * count[] and with the spacing of bufferStride[].
2202 : *
2203 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2204 : * validity of pDstBuffer. pDstBufferAllocStart
2205 : * should be the pointer returned by the malloc() or equivalent call used to
2206 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2207 : * bufferStride[] values are all positive), but not necessarily. If specified,
2208 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2209 : * validation is needed, nullptr can be passed.
2210 : *
2211 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2212 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2213 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2214 : * set to the appropriate value.
2215 : * If no validation is needed, 0 can be passed.
2216 : *
2217 : * @return true in case of success.
2218 : */
2219 2469 : bool GDALAbstractMDArray::Read(
2220 : const GUInt64 *arrayStartIdx, const size_t *count,
2221 : const GInt64 *arrayStep, // step in elements
2222 : const GPtrDiff_t *bufferStride, // stride in elements
2223 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2224 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2225 : {
2226 2469 : if (!GetDataType().CanConvertTo(bufferDataType))
2227 : {
2228 0 : CPLError(CE_Failure, CPLE_AppDefined,
2229 : "Array data type is not convertible to buffer data type");
2230 0 : return false;
2231 : }
2232 :
2233 4938 : std::vector<GInt64> tmp_arrayStep;
2234 4938 : std::vector<GPtrDiff_t> tmp_bufferStride;
2235 2469 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2236 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2237 : nDstBufferAllocSize, tmp_arrayStep,
2238 : tmp_bufferStride))
2239 : {
2240 0 : return false;
2241 : }
2242 :
2243 2469 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2244 2469 : pDstBuffer);
2245 : }
2246 :
2247 : /************************************************************************/
2248 : /* IWrite() */
2249 : /************************************************************************/
2250 :
2251 : //! @cond Doxygen_Suppress
2252 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2253 : const GInt64 *, const GPtrDiff_t *,
2254 : const GDALExtendedDataType &, const void *)
2255 : {
2256 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2257 1 : return false;
2258 : }
2259 :
2260 : //! @endcond
2261 :
2262 : /************************************************************************/
2263 : /* Write() */
2264 : /************************************************************************/
2265 :
2266 : /** Write part or totality of a multidimensional array or attribute.
2267 : *
2268 : * This will set the content of a hyper-rectangle into the array from
2269 : * a user supplied buffer.
2270 : *
2271 : * If bufferDataType is of type string, the values read from pSrcBuffer
2272 : * will be char* pointers.
2273 : *
2274 : * This is the same as the C function GDALMDArrayWrite().
2275 : *
2276 : * @param arrayStartIdx Values representing the starting index to write
2277 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2278 : * Array of GetDimensionCount() values. Must not be
2279 : * nullptr, unless for a zero-dimensional array.
2280 : *
2281 : * @param count Values representing the number of values to write in
2282 : * each dimension.
2283 : * Array of GetDimensionCount() values. Must not be
2284 : * nullptr, unless for a zero-dimensional array.
2285 : *
2286 : * @param arrayStep Spacing between values to write in each dimension.
2287 : * The spacing is in number of array elements, not bytes.
2288 : * If provided, must contain GetDimensionCount() values.
2289 : * If set to nullptr, [1, 1, ... 1] will be used as a
2290 : * default to indicate consecutive elements.
2291 : *
2292 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2293 : * The spacing is in number of array elements, not bytes.
2294 : * If provided, must contain GetDimensionCount() values.
2295 : * Negative values are possible (for example to reorder
2296 : * from bottom-to-top to top-to-bottom).
2297 : * If set to nullptr, will be set so that pSrcBuffer is
2298 : * written in a compact way, with elements of the last /
2299 : * fastest varying dimension being consecutive.
2300 : *
2301 : * @param bufferDataType Data type of values in pSrcBuffer.
2302 : *
2303 : * @param pSrcBuffer User buffer to read the values from. Should be big
2304 : * enough to store the number of values indicated by
2305 : * count[] and with the spacing of bufferStride[].
2306 : *
2307 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2308 : * validity of pSrcBuffer. pSrcBufferAllocStart
2309 : * should be the pointer returned by the malloc() or equivalent call used to
2310 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2311 : * bufferStride[] values are all positive), but not necessarily. If specified,
2312 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2313 : * validation is needed, nullptr can be passed.
2314 : *
2315 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2316 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2317 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2318 : * set to the appropriate value.
2319 : * If no validation is needed, 0 can be passed.
2320 : *
2321 : * @return true in case of success.
2322 : */
2323 1811 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2324 : const size_t *count, const GInt64 *arrayStep,
2325 : const GPtrDiff_t *bufferStride,
2326 : const GDALExtendedDataType &bufferDataType,
2327 : const void *pSrcBuffer,
2328 : const void *pSrcBufferAllocStart,
2329 : size_t nSrcBufferAllocSize)
2330 : {
2331 1811 : if (!bufferDataType.CanConvertTo(GetDataType()))
2332 : {
2333 0 : CPLError(CE_Failure, CPLE_AppDefined,
2334 : "Buffer data type is not convertible to array data type");
2335 0 : return false;
2336 : }
2337 :
2338 3622 : std::vector<GInt64> tmp_arrayStep;
2339 3622 : std::vector<GPtrDiff_t> tmp_bufferStride;
2340 1811 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2341 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2342 : nSrcBufferAllocSize, tmp_arrayStep,
2343 : tmp_bufferStride))
2344 : {
2345 0 : return false;
2346 : }
2347 :
2348 1811 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2349 1811 : pSrcBuffer);
2350 : }
2351 :
2352 : /************************************************************************/
2353 : /* GetTotalElementsCount() */
2354 : /************************************************************************/
2355 :
2356 : /** Return the total number of values in the array.
2357 : *
2358 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2359 : * and GDALAttributeGetTotalElementsCount().
2360 : *
2361 : */
2362 1124 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2363 : {
2364 1124 : const auto &dims = GetDimensions();
2365 1124 : if (dims.empty())
2366 558 : return 1;
2367 566 : GUInt64 nElts = 1;
2368 1254 : for (const auto &dim : dims)
2369 : {
2370 : try
2371 : {
2372 688 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2373 2064 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2374 688 : .v();
2375 : }
2376 0 : catch (...)
2377 : {
2378 0 : return 0;
2379 : }
2380 : }
2381 566 : return nElts;
2382 : }
2383 :
2384 : /************************************************************************/
2385 : /* GetBlockSize() */
2386 : /************************************************************************/
2387 :
2388 : /** Return the "natural" block size of the array along all dimensions.
2389 : *
2390 : * Some drivers might organize the array in tiles/blocks and reading/writing
2391 : * aligned on those tile/block boundaries will be more efficient.
2392 : *
2393 : * The returned number of elements in the vector is the same as
2394 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2395 : * the natural block size along the considered dimension.
2396 : * "Flat" arrays will typically return a vector of values set to 0.
2397 : *
2398 : * The default implementation will return a vector of values set to 0.
2399 : *
2400 : * This method is used by GetProcessingChunkSize().
2401 : *
2402 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2403 : * theoretical case of a 32-bit platform, this might exceed its size_t
2404 : * allocation capabilities.
2405 : *
2406 : * This is the same as the C function GDALMDArrayGetBlockSize().
2407 : *
2408 : * @return the block size, in number of elements along each dimension.
2409 : */
2410 279 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2411 : {
2412 279 : return std::vector<GUInt64>(GetDimensionCount());
2413 : }
2414 :
2415 : /************************************************************************/
2416 : /* GetProcessingChunkSize() */
2417 : /************************************************************************/
2418 :
2419 : /** \brief Return an optimal chunk size for read/write operations, given the
2420 : * natural block size and memory constraints specified.
2421 : *
2422 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2423 : * multiple of those returned by GetBlockSize() (unless the block define by
2424 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2425 : * returned by this method).
2426 : *
2427 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2428 : *
2429 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2430 : * chunk.
2431 : *
2432 : * @return the chunk size, in number of elements along each dimension.
2433 : */
2434 : std::vector<size_t>
2435 70 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2436 : {
2437 70 : const auto &dims = GetDimensions();
2438 70 : const auto &nDTSize = GetDataType().GetSize();
2439 70 : std::vector<size_t> anChunkSize;
2440 140 : auto blockSize = GetBlockSize();
2441 70 : CPLAssert(blockSize.size() == dims.size());
2442 70 : size_t nChunkSize = nDTSize;
2443 70 : bool bOverflow = false;
2444 70 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2445 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2446 : // [1, min(sizet_max, dim_size[i])]
2447 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2448 199 : for (size_t i = 0; i < dims.size(); i++)
2449 : {
2450 : const auto sizeDimI =
2451 258 : std::max(static_cast<size_t>(1),
2452 258 : static_cast<size_t>(
2453 258 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2454 129 : std::min(blockSize[i], dims[i]->GetSize()))));
2455 129 : anChunkSize.push_back(sizeDimI);
2456 129 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2457 : {
2458 4 : bOverflow = true;
2459 : }
2460 : else
2461 : {
2462 125 : nChunkSize *= sizeDimI;
2463 : }
2464 : }
2465 70 : if (nChunkSize == 0)
2466 0 : return anChunkSize;
2467 :
2468 : // If the product of all anChunkSize[i] does not fit on size_t, then
2469 : // set lowest anChunkSize[i] to 1.
2470 70 : if (bOverflow)
2471 : {
2472 2 : nChunkSize = nDTSize;
2473 2 : bOverflow = false;
2474 8 : for (size_t i = dims.size(); i > 0;)
2475 : {
2476 6 : --i;
2477 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2478 : {
2479 4 : bOverflow = true;
2480 4 : anChunkSize[i] = 1;
2481 : }
2482 : else
2483 : {
2484 2 : nChunkSize *= anChunkSize[i];
2485 : }
2486 : }
2487 : }
2488 :
2489 70 : nChunkSize = nDTSize;
2490 140 : std::vector<size_t> anAccBlockSizeFromStart;
2491 199 : for (size_t i = 0; i < dims.size(); i++)
2492 : {
2493 129 : nChunkSize *= anChunkSize[i];
2494 129 : anAccBlockSizeFromStart.push_back(nChunkSize);
2495 : }
2496 70 : if (nChunkSize <= nMaxChunkMemory / 2)
2497 : {
2498 66 : size_t nVoxelsFromEnd = 1;
2499 187 : for (size_t i = dims.size(); i > 0;)
2500 : {
2501 121 : --i;
2502 : const auto nCurBlockSize =
2503 121 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2504 121 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2505 121 : if (nMul >= 2)
2506 : {
2507 113 : const auto nSizeThisDim(dims[i]->GetSize());
2508 : const auto nBlocksThisDim =
2509 113 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2510 113 : anChunkSize[i] = static_cast<size_t>(std::min(
2511 113 : anChunkSize[i] *
2512 226 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2513 113 : nSizeThisDim));
2514 : }
2515 121 : nVoxelsFromEnd *= anChunkSize[i];
2516 : }
2517 : }
2518 70 : return anChunkSize;
2519 : }
2520 :
2521 : /************************************************************************/
2522 : /* BaseRename() */
2523 : /************************************************************************/
2524 :
2525 : //! @cond Doxygen_Suppress
2526 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2527 : {
2528 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2529 18 : m_osFullName += osNewName;
2530 18 : m_osName = osNewName;
2531 :
2532 18 : NotifyChildrenOfRenaming();
2533 18 : }
2534 :
2535 : //! @endcond
2536 :
2537 : //! @cond Doxygen_Suppress
2538 : /************************************************************************/
2539 : /* ParentRenamed() */
2540 : /************************************************************************/
2541 :
2542 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2543 : {
2544 50 : m_osFullName = osNewParentFullName;
2545 50 : m_osFullName += "/";
2546 50 : m_osFullName += m_osName;
2547 :
2548 50 : NotifyChildrenOfRenaming();
2549 50 : }
2550 :
2551 : //! @endcond
2552 :
2553 : /************************************************************************/
2554 : /* Deleted() */
2555 : /************************************************************************/
2556 :
2557 : //! @cond Doxygen_Suppress
2558 52 : void GDALAbstractMDArray::Deleted()
2559 : {
2560 52 : m_bValid = false;
2561 :
2562 52 : NotifyChildrenOfDeletion();
2563 52 : }
2564 :
2565 : //! @endcond
2566 :
2567 : /************************************************************************/
2568 : /* ParentDeleted() */
2569 : /************************************************************************/
2570 :
2571 : //! @cond Doxygen_Suppress
2572 28 : void GDALAbstractMDArray::ParentDeleted()
2573 : {
2574 28 : Deleted();
2575 28 : }
2576 :
2577 : //! @endcond
2578 :
2579 : /************************************************************************/
2580 : /* CheckValidAndErrorOutIfNot() */
2581 : /************************************************************************/
2582 :
2583 : //! @cond Doxygen_Suppress
2584 5860 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2585 : {
2586 5860 : if (!m_bValid)
2587 : {
2588 26 : CPLError(CE_Failure, CPLE_AppDefined,
2589 : "This object has been deleted. No action on it is possible");
2590 : }
2591 5860 : return m_bValid;
2592 : }
2593 :
2594 : //! @endcond
2595 :
2596 : /************************************************************************/
2597 : /* SetUnit() */
2598 : /************************************************************************/
2599 :
2600 : /** Set the variable unit.
2601 : *
2602 : * Values should conform as much as possible with those allowed by
2603 : * the NetCDF CF conventions:
2604 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2605 : * but others might be returned.
2606 : *
2607 : * Few examples are "meter", "degrees", "second", ...
2608 : * Empty value means unknown.
2609 : *
2610 : * This is the same as the C function GDALMDArraySetUnit()
2611 : *
2612 : * @note Driver implementation: optionally implemented.
2613 : *
2614 : * @param osUnit unit name.
2615 : * @return true in case of success.
2616 : */
2617 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2618 : {
2619 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2620 0 : return false;
2621 : }
2622 :
2623 : /************************************************************************/
2624 : /* GetUnit() */
2625 : /************************************************************************/
2626 :
2627 : /** Return the array unit.
2628 : *
2629 : * Values should conform as much as possible with those allowed by
2630 : * the NetCDF CF conventions:
2631 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2632 : * but others might be returned.
2633 : *
2634 : * Few examples are "meter", "degrees", "second", ...
2635 : * Empty value means unknown.
2636 : *
2637 : * This is the same as the C function GDALMDArrayGetUnit()
2638 : */
2639 5 : const std::string &GDALMDArray::GetUnit() const
2640 : {
2641 5 : static const std::string emptyString;
2642 5 : return emptyString;
2643 : }
2644 :
2645 : /************************************************************************/
2646 : /* SetSpatialRef() */
2647 : /************************************************************************/
2648 :
2649 : /** Assign a spatial reference system object to the array.
2650 : *
2651 : * This is the same as the C function GDALMDArraySetSpatialRef().
2652 : */
2653 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2654 : {
2655 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2656 0 : return false;
2657 : }
2658 :
2659 : /************************************************************************/
2660 : /* GetSpatialRef() */
2661 : /************************************************************************/
2662 :
2663 : /** Return the spatial reference system object associated with the array.
2664 : *
2665 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2666 : */
2667 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2668 : {
2669 4 : return nullptr;
2670 : }
2671 :
2672 : /************************************************************************/
2673 : /* GetRawNoDataValue() */
2674 : /************************************************************************/
2675 :
2676 : /** Return the nodata value as a "raw" value.
2677 : *
2678 : * The value returned might be nullptr in case of no nodata value. When
2679 : * a nodata value is registered, a non-nullptr will be returned whose size in
2680 : * bytes is GetDataType().GetSize().
2681 : *
2682 : * The returned value should not be modified or freed. It is valid until
2683 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2684 : * SetRawNoDataValue(), or any similar methods.
2685 : *
2686 : * @note Driver implementation: this method shall be implemented if nodata
2687 : * is supported.
2688 : *
2689 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2690 : *
2691 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2692 : */
2693 5 : const void *GDALMDArray::GetRawNoDataValue() const
2694 : {
2695 5 : return nullptr;
2696 : }
2697 :
2698 : /************************************************************************/
2699 : /* GetNoDataValueAsDouble() */
2700 : /************************************************************************/
2701 :
2702 : /** Return the nodata value as a double.
2703 : *
2704 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2705 : *
2706 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2707 : * a nodata value exists and can be converted to double. Might be nullptr.
2708 : *
2709 : * @return the nodata value as a double. A 0.0 value might also indicate the
2710 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2711 : * set to false then).
2712 : */
2713 22478 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2714 : {
2715 22478 : const void *pNoData = GetRawNoDataValue();
2716 22478 : double dfNoData = 0.0;
2717 22478 : const auto &eDT = GetDataType();
2718 22478 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2719 22478 : if (ok)
2720 : {
2721 22188 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2722 : GDT_Float64, 0, 1);
2723 : }
2724 22478 : if (pbHasNoData)
2725 439 : *pbHasNoData = ok;
2726 22478 : return dfNoData;
2727 : }
2728 :
2729 : /************************************************************************/
2730 : /* GetNoDataValueAsInt64() */
2731 : /************************************************************************/
2732 :
2733 : /** Return the nodata value as a Int64.
2734 : *
2735 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2736 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2737 : *
2738 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2739 : *
2740 : * @return the nodata value as a Int64
2741 : *
2742 : * @since GDAL 3.5
2743 : */
2744 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2745 : {
2746 12 : const void *pNoData = GetRawNoDataValue();
2747 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2748 12 : const auto &eDT = GetDataType();
2749 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2750 12 : if (ok)
2751 : {
2752 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2753 : GDT_Int64, 0, 1);
2754 : }
2755 12 : if (pbHasNoData)
2756 12 : *pbHasNoData = ok;
2757 12 : return nNoData;
2758 : }
2759 :
2760 : /************************************************************************/
2761 : /* GetNoDataValueAsUInt64() */
2762 : /************************************************************************/
2763 :
2764 : /** Return the nodata value as a UInt64.
2765 : *
2766 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2767 :
2768 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2769 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2770 : *
2771 : * @return the nodata value as a UInt64
2772 : *
2773 : * @since GDAL 3.5
2774 : */
2775 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2776 : {
2777 8 : const void *pNoData = GetRawNoDataValue();
2778 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2779 8 : const auto &eDT = GetDataType();
2780 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2781 8 : if (ok)
2782 : {
2783 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2784 : GDT_UInt64, 0, 1);
2785 : }
2786 8 : if (pbHasNoData)
2787 8 : *pbHasNoData = ok;
2788 8 : return nNoData;
2789 : }
2790 :
2791 : /************************************************************************/
2792 : /* SetRawNoDataValue() */
2793 : /************************************************************************/
2794 :
2795 : /** Set the nodata value as a "raw" value.
2796 : *
2797 : * The value passed might be nullptr in case of no nodata value. When
2798 : * a nodata value is registered, a non-nullptr whose size in
2799 : * bytes is GetDataType().GetSize() must be passed.
2800 : *
2801 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2802 : *
2803 : * @note Driver implementation: this method shall be implemented if setting
2804 : nodata
2805 : * is supported.
2806 :
2807 : * @return true in case of success.
2808 : */
2809 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2810 : {
2811 0 : CPLError(CE_Failure, CPLE_NotSupported,
2812 : "SetRawNoDataValue() not implemented");
2813 0 : return false;
2814 : }
2815 :
2816 : /************************************************************************/
2817 : /* SetNoDataValue() */
2818 : /************************************************************************/
2819 :
2820 : /** Set the nodata value as a double.
2821 : *
2822 : * If the natural data type of the attribute/array is not double, type
2823 : * conversion will occur to the type returned by GetDataType().
2824 : *
2825 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2826 : *
2827 : * @return true in case of success.
2828 : */
2829 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2830 : {
2831 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2832 57 : bool bRet = false;
2833 57 : if (GDALExtendedDataType::CopyValue(
2834 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2835 57 : GetDataType()))
2836 : {
2837 57 : bRet = SetRawNoDataValue(pRawNoData);
2838 : }
2839 57 : CPLFree(pRawNoData);
2840 57 : return bRet;
2841 : }
2842 :
2843 : /************************************************************************/
2844 : /* SetNoDataValue() */
2845 : /************************************************************************/
2846 :
2847 : /** Set the nodata value as a Int64.
2848 : *
2849 : * If the natural data type of the attribute/array is not Int64, type conversion
2850 : * will occur to the type returned by GetDataType().
2851 : *
2852 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2853 : *
2854 : * @return true in case of success.
2855 : *
2856 : * @since GDAL 3.5
2857 : */
2858 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2859 : {
2860 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2861 3 : bool bRet = false;
2862 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2863 6 : GDALExtendedDataType::Create(GDT_Int64),
2864 3 : pRawNoData, GetDataType()))
2865 : {
2866 3 : bRet = SetRawNoDataValue(pRawNoData);
2867 : }
2868 3 : CPLFree(pRawNoData);
2869 3 : return bRet;
2870 : }
2871 :
2872 : /************************************************************************/
2873 : /* SetNoDataValue() */
2874 : /************************************************************************/
2875 :
2876 : /** Set the nodata value as a Int64.
2877 : *
2878 : * If the natural data type of the attribute/array is not Int64, type conversion
2879 : * will occur to the type returned by GetDataType().
2880 : *
2881 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2882 : *
2883 : * @return true in case of success.
2884 : *
2885 : * @since GDAL 3.5
2886 : */
2887 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2888 : {
2889 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2890 1 : bool bRet = false;
2891 1 : if (GDALExtendedDataType::CopyValue(
2892 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2893 1 : GetDataType()))
2894 : {
2895 1 : bRet = SetRawNoDataValue(pRawNoData);
2896 : }
2897 1 : CPLFree(pRawNoData);
2898 1 : return bRet;
2899 : }
2900 :
2901 : /************************************************************************/
2902 : /* Resize() */
2903 : /************************************************************************/
2904 :
2905 : /** Resize an array to new dimensions.
2906 : *
2907 : * Not all drivers may allow this operation, and with restrictions (e.g.
2908 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2909 : *
2910 : * Resizing a dimension used in other arrays will cause those other arrays
2911 : * to be resized.
2912 : *
2913 : * This is the same as the C function GDALMDArrayResize().
2914 : *
2915 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2916 : * new size of each indexing dimension.
2917 : * @param papszOptions Options. (Driver specific)
2918 : * @return true in case of success.
2919 : * @since GDAL 3.7
2920 : */
2921 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2922 : CPL_UNUSED CSLConstList papszOptions)
2923 : {
2924 0 : CPLError(CE_Failure, CPLE_NotSupported,
2925 : "Resize() is not supported for this array");
2926 0 : return false;
2927 : }
2928 :
2929 : /************************************************************************/
2930 : /* SetScale() */
2931 : /************************************************************************/
2932 :
2933 : /** Set the scale value to apply to raw values.
2934 : *
2935 : * unscaled_value = raw_value * GetScale() + GetOffset()
2936 : *
2937 : * This is the same as the C function GDALMDArraySetScale() /
2938 : * GDALMDArraySetScaleEx().
2939 : *
2940 : * @note Driver implementation: this method shall be implemented if setting
2941 : * scale is supported.
2942 : *
2943 : * @param dfScale scale
2944 : * @param eStorageType Data type to which create the potential attribute that
2945 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2946 : * implementation will decide automatically the data type. Note that changing
2947 : * the data type after initial setting might not be supported.
2948 : * @return true in case of success.
2949 : */
2950 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2951 : CPL_UNUSED GDALDataType eStorageType)
2952 : {
2953 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2954 0 : return false;
2955 : }
2956 :
2957 : /************************************************************************/
2958 : /* SetOffset) */
2959 : /************************************************************************/
2960 :
2961 : /** Set the offset value to apply to raw values.
2962 : *
2963 : * unscaled_value = raw_value * GetScale() + GetOffset()
2964 : *
2965 : * This is the same as the C function GDALMDArraySetOffset() /
2966 : * GDALMDArraySetOffsetEx().
2967 : *
2968 : * @note Driver implementation: this method shall be implemented if setting
2969 : * offset is supported.
2970 : *
2971 : * @param dfOffset Offset
2972 : * @param eStorageType Data type to which create the potential attribute that
2973 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2974 : * implementation will decide automatically the data type. Note that changing
2975 : * the data type after initial setting might not be supported.
2976 : * @return true in case of success.
2977 : */
2978 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2979 : CPL_UNUSED GDALDataType eStorageType)
2980 : {
2981 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2982 0 : return false;
2983 : }
2984 :
2985 : /************************************************************************/
2986 : /* GetScale() */
2987 : /************************************************************************/
2988 :
2989 : /** Get the scale value to apply to raw values.
2990 : *
2991 : * unscaled_value = raw_value * GetScale() + GetOffset()
2992 : *
2993 : * This is the same as the C function GDALMDArrayGetScale().
2994 : *
2995 : * @note Driver implementation: this method shall be implemented if gettings
2996 : * scale is supported.
2997 : *
2998 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2999 : * a scale value exists. Might be nullptr.
3000 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3001 : * the storage type of the scale value, when known/relevant. Otherwise will be
3002 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3003 : *
3004 : * @return the scale value. A 1.0 value might also indicate the
3005 : * absence of a scale value.
3006 : */
3007 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3008 : CPL_UNUSED GDALDataType *peStorageType) const
3009 : {
3010 20 : if (pbHasScale)
3011 20 : *pbHasScale = false;
3012 20 : return 1.0;
3013 : }
3014 :
3015 : /************************************************************************/
3016 : /* GetOffset() */
3017 : /************************************************************************/
3018 :
3019 : /** Get the offset value to apply to raw values.
3020 : *
3021 : * unscaled_value = raw_value * GetScale() + GetOffset()
3022 : *
3023 : * This is the same as the C function GDALMDArrayGetOffset().
3024 : *
3025 : * @note Driver implementation: this method shall be implemented if gettings
3026 : * offset is supported.
3027 : *
3028 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3029 : * a offset value exists. Might be nullptr.
3030 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3031 : * the storage type of the offset value, when known/relevant. Otherwise will be
3032 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3033 : *
3034 : * @return the offset value. A 0.0 value might also indicate the
3035 : * absence of a offset value.
3036 : */
3037 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3038 : CPL_UNUSED GDALDataType *peStorageType) const
3039 : {
3040 20 : if (pbHasOffset)
3041 20 : *pbHasOffset = false;
3042 20 : return 0.0;
3043 : }
3044 :
3045 : /************************************************************************/
3046 : /* ProcessPerChunk() */
3047 : /************************************************************************/
3048 :
3049 : namespace
3050 : {
3051 : enum class Caller
3052 : {
3053 : CALLER_END_OF_LOOP,
3054 : CALLER_IN_LOOP,
3055 : };
3056 : }
3057 :
3058 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3059 : *
3060 : * This method is to be used when doing operations on an array, or a subset of
3061 : * it, in a chunk by chunk way.
3062 : *
3063 : * @param arrayStartIdx Values representing the starting index to use
3064 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3065 : * Array of GetDimensionCount() values. Must not be
3066 : * nullptr, unless for a zero-dimensional array.
3067 : *
3068 : * @param count Values representing the number of values to use in
3069 : * each dimension.
3070 : * Array of GetDimensionCount() values. Must not be
3071 : * nullptr, unless for a zero-dimensional array.
3072 : *
3073 : * @param chunkSize Values representing the chunk size in each dimension.
3074 : * Might typically the output of GetProcessingChunkSize().
3075 : * Array of GetDimensionCount() values. Must not be
3076 : * nullptr, unless for a zero-dimensional array.
3077 : *
3078 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3079 : * Must NOT be nullptr.
3080 : *
3081 : * @param pUserData Pointer to pass as the value of the pUserData argument
3082 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3083 : *
3084 : * @return true in case of success.
3085 : */
3086 68 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3087 : const GUInt64 *count,
3088 : const size_t *chunkSize,
3089 : FuncProcessPerChunkType pfnFunc,
3090 : void *pUserData)
3091 : {
3092 68 : const auto &dims = GetDimensions();
3093 68 : if (dims.empty())
3094 : {
3095 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3096 : }
3097 :
3098 : // Sanity check
3099 66 : size_t nTotalChunkSize = 1;
3100 172 : for (size_t i = 0; i < dims.size(); i++)
3101 : {
3102 113 : const auto nSizeThisDim(dims[i]->GetSize());
3103 113 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3104 111 : arrayStartIdx[i] > nSizeThisDim - count[i])
3105 : {
3106 4 : CPLError(CE_Failure, CPLE_AppDefined,
3107 : "Inconsistent arrayStartIdx[] / count[] values "
3108 : "regarding array size");
3109 4 : return false;
3110 : }
3111 216 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3112 107 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3113 : {
3114 3 : CPLError(CE_Failure, CPLE_AppDefined,
3115 : "Inconsistent chunkSize[] values");
3116 3 : return false;
3117 : }
3118 106 : nTotalChunkSize *= chunkSize[i];
3119 : }
3120 :
3121 59 : size_t dimIdx = 0;
3122 118 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3123 118 : std::vector<size_t> chunkCount(dims.size());
3124 :
3125 : struct Stack
3126 : {
3127 : GUInt64 nBlockCounter = 0;
3128 : GUInt64 nBlocksMinusOne = 0;
3129 : size_t first_count = 0; // only used if nBlocks > 1
3130 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3131 : };
3132 :
3133 118 : std::vector<Stack> stack(dims.size());
3134 59 : GUInt64 iCurChunk = 0;
3135 59 : GUInt64 nChunkCount = 1;
3136 164 : for (size_t i = 0; i < dims.size(); i++)
3137 : {
3138 105 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3139 105 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3140 105 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3141 105 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3142 105 : if (stack[i].nBlocksMinusOne == 0)
3143 : {
3144 100 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3145 100 : chunkCount[i] = static_cast<size_t>(count[i]);
3146 : }
3147 : else
3148 : {
3149 5 : stack[i].first_count = static_cast<size_t>(
3150 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3151 : }
3152 : }
3153 :
3154 59 : lbl_next_depth:
3155 274 : if (dimIdx == dims.size())
3156 : {
3157 92 : ++iCurChunk;
3158 92 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3159 : iCurChunk, nChunkCount, pUserData))
3160 : {
3161 0 : return false;
3162 : }
3163 : }
3164 : else
3165 : {
3166 182 : if (stack[dimIdx].nBlocksMinusOne != 0)
3167 : {
3168 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3169 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3170 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3171 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3172 : while (true)
3173 : {
3174 33 : dimIdx++;
3175 33 : goto lbl_next_depth;
3176 33 : lbl_return_to_caller_in_loop:
3177 33 : --stack[dimIdx].nBlockCounter;
3178 33 : if (stack[dimIdx].nBlockCounter == 0)
3179 11 : break;
3180 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3181 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3182 : }
3183 :
3184 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3185 22 : chunkCount[dimIdx] =
3186 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3187 11 : chunkArrayStartIdx[dimIdx]);
3188 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3189 : }
3190 182 : dimIdx++;
3191 182 : goto lbl_next_depth;
3192 182 : lbl_return_to_caller_end_of_loop:
3193 182 : if (dimIdx == 0)
3194 59 : goto end;
3195 : }
3196 :
3197 215 : assert(dimIdx > 0);
3198 215 : dimIdx--;
3199 : // cppcheck-suppress negativeContainerIndex
3200 215 : switch (stack[dimIdx].return_point)
3201 : {
3202 182 : case Caller::CALLER_END_OF_LOOP:
3203 182 : goto lbl_return_to_caller_end_of_loop;
3204 33 : case Caller::CALLER_IN_LOOP:
3205 33 : goto lbl_return_to_caller_in_loop;
3206 : }
3207 59 : end:
3208 59 : return true;
3209 : }
3210 :
3211 : /************************************************************************/
3212 : /* GDALAttribute() */
3213 : /************************************************************************/
3214 :
3215 : //! @cond Doxygen_Suppress
3216 14399 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3217 0 : CPL_UNUSED const std::string &osName)
3218 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3219 14399 : : GDALAbstractMDArray(osParentName, osName)
3220 : #endif
3221 : {
3222 14399 : }
3223 :
3224 : GDALAttribute::~GDALAttribute() = default;
3225 :
3226 : //! @endcond
3227 :
3228 : /************************************************************************/
3229 : /* GetDimensionSize() */
3230 : /************************************************************************/
3231 :
3232 : /** Return the size of the dimensions of the attribute.
3233 : *
3234 : * This will be an empty array for a scalar (single value) attribute.
3235 : *
3236 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3237 : */
3238 380 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3239 : {
3240 380 : const auto &dims = GetDimensions();
3241 380 : std::vector<GUInt64> ret;
3242 380 : ret.reserve(dims.size());
3243 493 : for (const auto &dim : dims)
3244 113 : ret.push_back(dim->GetSize());
3245 380 : return ret;
3246 : }
3247 :
3248 : /************************************************************************/
3249 : /* GDALRawResult() */
3250 : /************************************************************************/
3251 :
3252 : //! @cond Doxygen_Suppress
3253 155 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3254 155 : size_t nEltCount)
3255 310 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3256 155 : m_raw(raw)
3257 : {
3258 155 : }
3259 :
3260 : //! @endcond
3261 :
3262 : /************************************************************************/
3263 : /* GDALRawResult() */
3264 : /************************************************************************/
3265 :
3266 : /** Move constructor. */
3267 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3268 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3269 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3270 : {
3271 0 : other.m_nEltCount = 0;
3272 0 : other.m_nSize = 0;
3273 0 : other.m_raw = nullptr;
3274 0 : }
3275 :
3276 : /************************************************************************/
3277 : /* FreeMe() */
3278 : /************************************************************************/
3279 :
3280 155 : void GDALRawResult::FreeMe()
3281 : {
3282 155 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3283 : {
3284 50 : GByte *pabyPtr = m_raw;
3285 50 : const auto nDTSize(m_dt.GetSize());
3286 100 : for (size_t i = 0; i < m_nEltCount; ++i)
3287 : {
3288 50 : m_dt.FreeDynamicMemory(pabyPtr);
3289 50 : pabyPtr += nDTSize;
3290 : }
3291 : }
3292 155 : VSIFree(m_raw);
3293 155 : }
3294 :
3295 : /************************************************************************/
3296 : /* operator=() */
3297 : /************************************************************************/
3298 :
3299 : /** Move assignment. */
3300 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3301 : {
3302 0 : FreeMe();
3303 0 : m_dt = std::move(other.m_dt);
3304 0 : m_nEltCount = other.m_nEltCount;
3305 0 : m_nSize = other.m_nSize;
3306 0 : m_raw = other.m_raw;
3307 0 : other.m_nEltCount = 0;
3308 0 : other.m_nSize = 0;
3309 0 : other.m_raw = nullptr;
3310 0 : return *this;
3311 : }
3312 :
3313 : /************************************************************************/
3314 : /* ~GDALRawResult() */
3315 : /************************************************************************/
3316 :
3317 : /** Destructor. */
3318 155 : GDALRawResult::~GDALRawResult()
3319 : {
3320 155 : FreeMe();
3321 155 : }
3322 :
3323 : /************************************************************************/
3324 : /* StealData() */
3325 : /************************************************************************/
3326 :
3327 : //! @cond Doxygen_Suppress
3328 : /** Return buffer to caller which becomes owner of it.
3329 : * Only to be used by GDALAttributeReadAsRaw().
3330 : */
3331 6 : GByte *GDALRawResult::StealData()
3332 : {
3333 6 : GByte *ret = m_raw;
3334 6 : m_raw = nullptr;
3335 6 : m_nEltCount = 0;
3336 6 : m_nSize = 0;
3337 6 : return ret;
3338 : }
3339 :
3340 : //! @endcond
3341 :
3342 : /************************************************************************/
3343 : /* ReadAsRaw() */
3344 : /************************************************************************/
3345 :
3346 : /** Return the raw value of an attribute.
3347 : *
3348 : *
3349 : * This is the same as the C function GDALAttributeReadAsRaw().
3350 : */
3351 155 : GDALRawResult GDALAttribute::ReadAsRaw() const
3352 : {
3353 155 : const auto nEltCount(GetTotalElementsCount());
3354 155 : const auto &dt(GetDataType());
3355 155 : const auto nDTSize(dt.GetSize());
3356 : GByte *res = static_cast<GByte *>(
3357 155 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3358 155 : if (!res)
3359 0 : return GDALRawResult(nullptr, dt, 0);
3360 155 : const auto &dims = GetDimensions();
3361 155 : const auto nDims = GetDimensionCount();
3362 310 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3363 310 : std::vector<size_t> count(1 + nDims);
3364 177 : for (size_t i = 0; i < nDims; i++)
3365 : {
3366 22 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3367 : }
3368 155 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3369 155 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3370 : {
3371 0 : VSIFree(res);
3372 0 : return GDALRawResult(nullptr, dt, 0);
3373 : }
3374 155 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3375 : }
3376 :
3377 : /************************************************************************/
3378 : /* ReadAsString() */
3379 : /************************************************************************/
3380 :
3381 : /** Return the value of an attribute as a string.
3382 : *
3383 : * The returned string should not be freed, and its lifetime does not
3384 : * excess a next call to ReadAsString() on the same object, or the deletion
3385 : * of the object itself.
3386 : *
3387 : * This function will only return the first element if there are several.
3388 : *
3389 : * This is the same as the C function GDALAttributeReadAsString()
3390 : *
3391 : * @return a string, or nullptr.
3392 : */
3393 1370 : const char *GDALAttribute::ReadAsString() const
3394 : {
3395 1370 : const auto nDims = GetDimensionCount();
3396 2740 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3397 2740 : std::vector<size_t> count(1 + nDims, 1);
3398 1370 : char *szRet = nullptr;
3399 1370 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3400 1370 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3401 4109 : sizeof(szRet)) ||
3402 1369 : szRet == nullptr)
3403 : {
3404 4 : return nullptr;
3405 : }
3406 1366 : m_osCachedVal = szRet;
3407 1366 : CPLFree(szRet);
3408 1366 : return m_osCachedVal.c_str();
3409 : }
3410 :
3411 : /************************************************************************/
3412 : /* ReadAsInt() */
3413 : /************************************************************************/
3414 :
3415 : /** Return the value of an attribute as a integer.
3416 : *
3417 : * This function will only return the first element if there are several.
3418 : *
3419 : * It can fail if its value can not be converted to integer.
3420 : *
3421 : * This is the same as the C function GDALAttributeReadAsInt()
3422 : *
3423 : * @return a integer, or INT_MIN in case of error.
3424 : */
3425 226 : int GDALAttribute::ReadAsInt() const
3426 : {
3427 226 : const auto nDims = GetDimensionCount();
3428 452 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3429 226 : std::vector<size_t> count(1 + nDims, 1);
3430 226 : int nRet = INT_MIN;
3431 226 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3432 452 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3433 452 : return nRet;
3434 : }
3435 :
3436 : /************************************************************************/
3437 : /* ReadAsInt64() */
3438 : /************************************************************************/
3439 :
3440 : /** Return the value of an attribute as an int64_t.
3441 : *
3442 : * This function will only return the first element if there are several.
3443 : *
3444 : * It can fail if its value can not be converted to long.
3445 : *
3446 : * This is the same as the C function GDALAttributeReadAsInt64()
3447 : *
3448 : * @return an int64_t, or INT64_MIN in case of error.
3449 : */
3450 54 : int64_t GDALAttribute::ReadAsInt64() const
3451 : {
3452 54 : const auto nDims = GetDimensionCount();
3453 108 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3454 54 : std::vector<size_t> count(1 + nDims, 1);
3455 54 : int64_t nRet = INT64_MIN;
3456 54 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3457 108 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3458 108 : return nRet;
3459 : }
3460 :
3461 : /************************************************************************/
3462 : /* ReadAsDouble() */
3463 : /************************************************************************/
3464 :
3465 : /** Return the value of an attribute as a double.
3466 : *
3467 : * This function will only return the first element if there are several.
3468 : *
3469 : * It can fail if its value can not be converted to double.
3470 : *
3471 : * This is the same as the C function GDALAttributeReadAsInt()
3472 : *
3473 : * @return a double value.
3474 : */
3475 349 : double GDALAttribute::ReadAsDouble() const
3476 : {
3477 349 : const auto nDims = GetDimensionCount();
3478 698 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3479 349 : std::vector<size_t> count(1 + nDims, 1);
3480 349 : double dfRet = 0;
3481 349 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3482 349 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3483 349 : sizeof(dfRet));
3484 698 : return dfRet;
3485 : }
3486 :
3487 : /************************************************************************/
3488 : /* ReadAsStringArray() */
3489 : /************************************************************************/
3490 :
3491 : /** Return the value of an attribute as an array of strings.
3492 : *
3493 : * This is the same as the C function GDALAttributeReadAsStringArray()
3494 : */
3495 132 : CPLStringList GDALAttribute::ReadAsStringArray() const
3496 : {
3497 132 : const auto nElts = GetTotalElementsCount();
3498 132 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3499 0 : return CPLStringList();
3500 : char **papszList = static_cast<char **>(
3501 132 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3502 132 : const auto &dims = GetDimensions();
3503 132 : const auto nDims = GetDimensionCount();
3504 264 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3505 264 : std::vector<size_t> count(1 + nDims);
3506 209 : for (size_t i = 0; i < nDims; i++)
3507 : {
3508 77 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3509 : }
3510 132 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3511 132 : GDALExtendedDataType::CreateString(), papszList, papszList,
3512 132 : sizeof(char *) * static_cast<int>(nElts));
3513 551 : for (int i = 0; i < static_cast<int>(nElts); i++)
3514 : {
3515 419 : if (papszList[i] == nullptr)
3516 13 : papszList[i] = CPLStrdup("");
3517 : }
3518 132 : return CPLStringList(papszList);
3519 : }
3520 :
3521 : /************************************************************************/
3522 : /* ReadAsIntArray() */
3523 : /************************************************************************/
3524 :
3525 : /** Return the value of an attribute as an array of integers.
3526 : *
3527 : * This is the same as the C function GDALAttributeReadAsIntArray().
3528 : */
3529 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3530 : {
3531 15 : const auto nElts = GetTotalElementsCount();
3532 : #if SIZEOF_VOIDP == 4
3533 : if (nElts > static_cast<size_t>(nElts))
3534 : return {};
3535 : #endif
3536 15 : std::vector<int> res(static_cast<size_t>(nElts));
3537 15 : const auto &dims = GetDimensions();
3538 15 : const auto nDims = GetDimensionCount();
3539 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3540 30 : std::vector<size_t> count(1 + nDims);
3541 32 : for (size_t i = 0; i < nDims; i++)
3542 : {
3543 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3544 : }
3545 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3546 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3547 15 : res.size() * sizeof(res[0]));
3548 30 : return res;
3549 : }
3550 :
3551 : /************************************************************************/
3552 : /* ReadAsInt64Array() */
3553 : /************************************************************************/
3554 :
3555 : /** Return the value of an attribute as an array of int64_t.
3556 : *
3557 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3558 : */
3559 38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3560 : {
3561 38 : const auto nElts = GetTotalElementsCount();
3562 : #if SIZEOF_VOIDP == 4
3563 : if (nElts > static_cast<size_t>(nElts))
3564 : return {};
3565 : #endif
3566 38 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3567 38 : const auto &dims = GetDimensions();
3568 38 : const auto nDims = GetDimensionCount();
3569 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3570 76 : std::vector<size_t> count(1 + nDims);
3571 76 : for (size_t i = 0; i < nDims; i++)
3572 : {
3573 38 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3574 : }
3575 38 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3576 76 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3577 38 : res.size() * sizeof(res[0]));
3578 76 : return res;
3579 : }
3580 :
3581 : /************************************************************************/
3582 : /* ReadAsDoubleArray() */
3583 : /************************************************************************/
3584 :
3585 : /** Return the value of an attribute as an array of double.
3586 : *
3587 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3588 : */
3589 88 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3590 : {
3591 88 : const auto nElts = GetTotalElementsCount();
3592 : #if SIZEOF_VOIDP == 4
3593 : if (nElts > static_cast<size_t>(nElts))
3594 : return {};
3595 : #endif
3596 88 : std::vector<double> res(static_cast<size_t>(nElts));
3597 88 : const auto &dims = GetDimensions();
3598 88 : const auto nDims = GetDimensionCount();
3599 176 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3600 176 : std::vector<size_t> count(1 + nDims);
3601 160 : for (size_t i = 0; i < nDims; i++)
3602 : {
3603 72 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3604 : }
3605 88 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3606 176 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3607 88 : res.size() * sizeof(res[0]));
3608 176 : return res;
3609 : }
3610 :
3611 : /************************************************************************/
3612 : /* Write() */
3613 : /************************************************************************/
3614 :
3615 : /** Write an attribute from raw values expressed in GetDataType()
3616 : *
3617 : * The values should be provided in the type of GetDataType() and there should
3618 : * be exactly GetTotalElementsCount() of them.
3619 : * If GetDataType() is a string, each value should be a char* pointer.
3620 : *
3621 : * This is the same as the C function GDALAttributeWriteRaw().
3622 : *
3623 : * @param pabyValue Buffer of nLen bytes.
3624 : * @param nLen Size of pabyValue in bytes. Should be equal to
3625 : * GetTotalElementsCount() * GetDataType().GetSize()
3626 : * @return true in case of success.
3627 : */
3628 96 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3629 : {
3630 96 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3631 : {
3632 0 : CPLError(CE_Failure, CPLE_AppDefined,
3633 : "Length is not of expected value");
3634 0 : return false;
3635 : }
3636 96 : const auto &dims = GetDimensions();
3637 96 : const auto nDims = GetDimensionCount();
3638 192 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3639 192 : std::vector<size_t> count(1 + nDims);
3640 119 : for (size_t i = 0; i < nDims; i++)
3641 : {
3642 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3643 : }
3644 96 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3645 96 : pabyValue, pabyValue, nLen);
3646 : }
3647 :
3648 : /************************************************************************/
3649 : /* Write() */
3650 : /************************************************************************/
3651 :
3652 : /** Write an attribute from a string value.
3653 : *
3654 : * Type conversion will be performed if needed. If the attribute contains
3655 : * multiple values, only the first one will be updated.
3656 : *
3657 : * This is the same as the C function GDALAttributeWriteString().
3658 : *
3659 : * @param pszValue Pointer to a string.
3660 : * @return true in case of success.
3661 : */
3662 306 : bool GDALAttribute::Write(const char *pszValue)
3663 : {
3664 306 : const auto nDims = GetDimensionCount();
3665 612 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3666 306 : std::vector<size_t> count(1 + nDims, 1);
3667 306 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3668 612 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3669 612 : sizeof(pszValue));
3670 : }
3671 :
3672 : /************************************************************************/
3673 : /* WriteInt() */
3674 : /************************************************************************/
3675 :
3676 : /** Write an attribute from a integer value.
3677 : *
3678 : * Type conversion will be performed if needed. If the attribute contains
3679 : * multiple values, only the first one will be updated.
3680 : *
3681 : * This is the same as the C function GDALAttributeWriteInt().
3682 : *
3683 : * @param nVal Value.
3684 : * @return true in case of success.
3685 : */
3686 22 : bool GDALAttribute::WriteInt(int nVal)
3687 : {
3688 22 : const auto nDims = GetDimensionCount();
3689 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3690 22 : std::vector<size_t> count(1 + nDims, 1);
3691 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3692 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3693 44 : sizeof(nVal));
3694 : }
3695 :
3696 : /************************************************************************/
3697 : /* WriteInt64() */
3698 : /************************************************************************/
3699 :
3700 : /** Write an attribute from an int64_t value.
3701 : *
3702 : * Type conversion will be performed if needed. If the attribute contains
3703 : * multiple values, only the first one will be updated.
3704 : *
3705 : * This is the same as the C function GDALAttributeWriteInt().
3706 : *
3707 : * @param nVal Value.
3708 : * @return true in case of success.
3709 : */
3710 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3711 : {
3712 11 : const auto nDims = GetDimensionCount();
3713 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3714 11 : std::vector<size_t> count(1 + nDims, 1);
3715 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3716 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3717 22 : sizeof(nVal));
3718 : }
3719 :
3720 : /************************************************************************/
3721 : /* Write() */
3722 : /************************************************************************/
3723 :
3724 : /** Write an attribute from a double value.
3725 : *
3726 : * Type conversion will be performed if needed. If the attribute contains
3727 : * multiple values, only the first one will be updated.
3728 : *
3729 : * This is the same as the C function GDALAttributeWriteDouble().
3730 : *
3731 : * @param dfVal Value.
3732 : * @return true in case of success.
3733 : */
3734 38 : bool GDALAttribute::Write(double dfVal)
3735 : {
3736 38 : const auto nDims = GetDimensionCount();
3737 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3738 38 : std::vector<size_t> count(1 + nDims, 1);
3739 38 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3740 76 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3741 76 : sizeof(dfVal));
3742 : }
3743 :
3744 : /************************************************************************/
3745 : /* Write() */
3746 : /************************************************************************/
3747 :
3748 : /** Write an attribute from an array of strings.
3749 : *
3750 : * Type conversion will be performed if needed.
3751 : *
3752 : * Exactly GetTotalElementsCount() strings must be provided
3753 : *
3754 : * This is the same as the C function GDALAttributeWriteStringArray().
3755 : *
3756 : * @param vals Array of strings.
3757 : * @return true in case of success.
3758 : */
3759 8 : bool GDALAttribute::Write(CSLConstList vals)
3760 : {
3761 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3762 : {
3763 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3764 1 : return false;
3765 : }
3766 7 : const auto nDims = GetDimensionCount();
3767 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3768 7 : std::vector<size_t> count(1 + nDims);
3769 7 : const auto &dims = GetDimensions();
3770 15 : for (size_t i = 0; i < nDims; i++)
3771 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3772 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3773 7 : GDALExtendedDataType::CreateString(), vals, vals,
3774 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3775 : }
3776 :
3777 : /************************************************************************/
3778 : /* Write() */
3779 : /************************************************************************/
3780 :
3781 : /** Write an attribute from an array of int.
3782 : *
3783 : * Type conversion will be performed if needed.
3784 : *
3785 : * Exactly GetTotalElementsCount() strings must be provided
3786 : *
3787 : * This is the same as the C function GDALAttributeWriteIntArray()
3788 : *
3789 : * @param vals Array of int.
3790 : * @param nVals Should be equal to GetTotalElementsCount().
3791 : * @return true in case of success.
3792 : */
3793 11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3794 : {
3795 11 : if (nVals != GetTotalElementsCount())
3796 : {
3797 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3798 1 : return false;
3799 : }
3800 10 : const auto nDims = GetDimensionCount();
3801 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3802 10 : std::vector<size_t> count(1 + nDims);
3803 10 : const auto &dims = GetDimensions();
3804 20 : for (size_t i = 0; i < nDims; i++)
3805 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3806 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3807 10 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3808 20 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3809 : }
3810 :
3811 : /************************************************************************/
3812 : /* Write() */
3813 : /************************************************************************/
3814 :
3815 : /** Write an attribute from an array of int64_t.
3816 : *
3817 : * Type conversion will be performed if needed.
3818 : *
3819 : * Exactly GetTotalElementsCount() strings must be provided
3820 : *
3821 : * This is the same as the C function GDALAttributeWriteLongArray()
3822 : *
3823 : * @param vals Array of int64_t.
3824 : * @param nVals Should be equal to GetTotalElementsCount().
3825 : * @return true in case of success.
3826 : */
3827 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3828 : {
3829 10 : if (nVals != GetTotalElementsCount())
3830 : {
3831 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3832 0 : return false;
3833 : }
3834 10 : const auto nDims = GetDimensionCount();
3835 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3836 10 : std::vector<size_t> count(1 + nDims);
3837 10 : const auto &dims = GetDimensions();
3838 20 : for (size_t i = 0; i < nDims; i++)
3839 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3840 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3841 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3842 10 : static_cast<size_t>(GetTotalElementsCount()) *
3843 10 : sizeof(int64_t));
3844 : }
3845 :
3846 : /************************************************************************/
3847 : /* Write() */
3848 : /************************************************************************/
3849 :
3850 : /** Write an attribute from an array of double.
3851 : *
3852 : * Type conversion will be performed if needed.
3853 : *
3854 : * Exactly GetTotalElementsCount() strings must be provided
3855 : *
3856 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3857 : *
3858 : * @param vals Array of double.
3859 : * @param nVals Should be equal to GetTotalElementsCount().
3860 : * @return true in case of success.
3861 : */
3862 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3863 : {
3864 7 : if (nVals != GetTotalElementsCount())
3865 : {
3866 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3867 1 : return false;
3868 : }
3869 6 : const auto nDims = GetDimensionCount();
3870 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3871 6 : std::vector<size_t> count(1 + nDims);
3872 6 : const auto &dims = GetDimensions();
3873 13 : for (size_t i = 0; i < nDims; i++)
3874 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3875 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3876 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3877 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3878 : }
3879 :
3880 : /************************************************************************/
3881 : /* GDALMDArray() */
3882 : /************************************************************************/
3883 :
3884 : //! @cond Doxygen_Suppress
3885 6570 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3886 : CPL_UNUSED const std::string &osName,
3887 0 : const std::string &osContext)
3888 : :
3889 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3890 : GDALAbstractMDArray(osParentName, osName),
3891 : #endif
3892 6570 : m_osContext(osContext)
3893 : {
3894 6570 : }
3895 :
3896 : //! @endcond
3897 :
3898 : /************************************************************************/
3899 : /* GetTotalCopyCost() */
3900 : /************************************************************************/
3901 :
3902 : /** Return a total "cost" to copy the array.
3903 : *
3904 : * Used as a parameter for CopyFrom()
3905 : */
3906 50 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3907 : {
3908 100 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3909 100 : GetTotalElementsCount() * GetDataType().GetSize();
3910 : }
3911 :
3912 : /************************************************************************/
3913 : /* CopyFromAllExceptValues() */
3914 : /************************************************************************/
3915 :
3916 : //! @cond Doxygen_Suppress
3917 :
3918 175 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3919 : bool bStrict, GUInt64 &nCurCost,
3920 : const GUInt64 nTotalCost,
3921 : GDALProgressFunc pfnProgress,
3922 : void *pProgressData)
3923 : {
3924 : // Nodata setting must be one of the first things done for TileDB
3925 175 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3926 175 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3927 : {
3928 13 : SetRawNoDataValue(pNoData);
3929 : }
3930 :
3931 175 : const bool bThisIsUnscaledArray =
3932 175 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3933 350 : auto attrs = poSrcArray->GetAttributes();
3934 222 : for (const auto &attr : attrs)
3935 : {
3936 47 : const auto &osAttrName = attr->GetName();
3937 47 : if (bThisIsUnscaledArray)
3938 : {
3939 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3940 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3941 1 : osAttrName == "valid_range")
3942 : {
3943 1 : continue;
3944 : }
3945 : }
3946 :
3947 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3948 92 : attr->GetDataType());
3949 46 : if (!dstAttr)
3950 : {
3951 0 : if (bStrict)
3952 0 : return false;
3953 0 : continue;
3954 : }
3955 46 : auto raw = attr->ReadAsRaw();
3956 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3957 0 : return false;
3958 : }
3959 175 : if (!attrs.empty())
3960 : {
3961 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3962 46 : if (pfnProgress &&
3963 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3964 0 : return false;
3965 : }
3966 :
3967 175 : auto srcSRS = poSrcArray->GetSpatialRef();
3968 175 : if (srcSRS)
3969 : {
3970 11 : SetSpatialRef(srcSRS.get());
3971 : }
3972 :
3973 175 : const std::string &osUnit(poSrcArray->GetUnit());
3974 175 : if (!osUnit.empty())
3975 : {
3976 18 : SetUnit(osUnit);
3977 : }
3978 :
3979 175 : bool bGotValue = false;
3980 175 : GDALDataType eOffsetStorageType = GDT_Unknown;
3981 : const double dfOffset =
3982 175 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3983 175 : if (bGotValue)
3984 : {
3985 3 : SetOffset(dfOffset, eOffsetStorageType);
3986 : }
3987 :
3988 175 : bGotValue = false;
3989 175 : GDALDataType eScaleStorageType = GDT_Unknown;
3990 175 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3991 175 : if (bGotValue)
3992 : {
3993 3 : SetScale(dfScale, eScaleStorageType);
3994 : }
3995 :
3996 175 : return true;
3997 : }
3998 :
3999 : //! @endcond
4000 :
4001 : /************************************************************************/
4002 : /* CopyFrom() */
4003 : /************************************************************************/
4004 :
4005 : /** Copy the content of an array into a new (generally empty) array.
4006 : *
4007 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4008 : * of some output drivers this is not recommended)
4009 : * @param poSrcArray Source array. Should NOT be nullptr.
4010 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4011 : * stop the copy. In relaxed mode, the copy will be attempted to
4012 : * be pursued.
4013 : * @param nCurCost Should be provided as a variable initially set to 0.
4014 : * @param nTotalCost Total cost from GetTotalCopyCost().
4015 : * @param pfnProgress Progress callback, or nullptr.
4016 : * @param pProgressData Progress user data, or nulptr.
4017 : *
4018 : * @return true in case of success (or partial success if bStrict == false).
4019 : */
4020 48 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4021 : const GDALMDArray *poSrcArray, bool bStrict,
4022 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4023 : GDALProgressFunc pfnProgress, void *pProgressData)
4024 : {
4025 48 : if (pfnProgress == nullptr)
4026 4 : pfnProgress = GDALDummyProgress;
4027 :
4028 48 : nCurCost += GDALMDArray::COPY_COST;
4029 :
4030 48 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4031 : pfnProgress, pProgressData))
4032 : {
4033 0 : return false;
4034 : }
4035 :
4036 48 : const auto &dims = poSrcArray->GetDimensions();
4037 48 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4038 48 : if (dims.empty())
4039 : {
4040 2 : std::vector<GByte> abyTmp(nDTSize);
4041 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4042 2 : GetDataType(), &abyTmp[0]) &&
4043 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4044 4 : &abyTmp[0])) &&
4045 : bStrict)
4046 : {
4047 0 : return false;
4048 : }
4049 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4050 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4051 0 : return false;
4052 : }
4053 : else
4054 : {
4055 46 : std::vector<GUInt64> arrayStartIdx(dims.size());
4056 46 : std::vector<GUInt64> count(dims.size());
4057 125 : for (size_t i = 0; i < dims.size(); i++)
4058 : {
4059 79 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4060 : }
4061 :
4062 : struct CopyFunc
4063 : {
4064 : GDALMDArray *poDstArray = nullptr;
4065 : std::vector<GByte> abyTmp{};
4066 : GDALProgressFunc pfnProgress = nullptr;
4067 : void *pProgressData = nullptr;
4068 : GUInt64 nCurCost = 0;
4069 : GUInt64 nTotalCost = 0;
4070 : GUInt64 nTotalBytesThisArray = 0;
4071 : bool bStop = false;
4072 :
4073 64 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4074 : const GUInt64 *chunkArrayStartIdx,
4075 : const size_t *chunkCount, GUInt64 iCurChunk,
4076 : GUInt64 nChunkCount, void *pUserData)
4077 : {
4078 64 : const auto &dt(l_poSrcArray->GetDataType());
4079 64 : auto data = static_cast<CopyFunc *>(pUserData);
4080 64 : auto poDstArray = data->poDstArray;
4081 64 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4082 64 : nullptr, dt, &data->abyTmp[0]))
4083 : {
4084 0 : return false;
4085 : }
4086 : bool bRet =
4087 64 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4088 64 : nullptr, dt, &data->abyTmp[0]);
4089 64 : if (dt.NeedsFreeDynamicMemory())
4090 : {
4091 4 : const auto l_nDTSize = dt.GetSize();
4092 4 : GByte *ptr = &data->abyTmp[0];
4093 4 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4094 4 : size_t nEltCount = 1;
4095 8 : for (size_t i = 0; i < l_nDims; ++i)
4096 : {
4097 4 : nEltCount *= chunkCount[i];
4098 : }
4099 20 : for (size_t i = 0; i < nEltCount; i++)
4100 : {
4101 16 : dt.FreeDynamicMemory(ptr);
4102 16 : ptr += l_nDTSize;
4103 : }
4104 : }
4105 64 : if (!bRet)
4106 : {
4107 0 : return false;
4108 : }
4109 :
4110 64 : double dfCurCost =
4111 64 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4112 64 : data->nTotalBytesThisArray;
4113 64 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4114 : data->pProgressData))
4115 : {
4116 0 : data->bStop = true;
4117 0 : return false;
4118 : }
4119 :
4120 64 : return true;
4121 : }
4122 : };
4123 :
4124 46 : CopyFunc copyFunc;
4125 46 : copyFunc.poDstArray = this;
4126 46 : copyFunc.nCurCost = nCurCost;
4127 46 : copyFunc.nTotalCost = nTotalCost;
4128 46 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4129 46 : copyFunc.pfnProgress = pfnProgress;
4130 46 : copyFunc.pProgressData = pProgressData;
4131 : const char *pszSwathSize =
4132 46 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4133 : const size_t nMaxChunkSize =
4134 : pszSwathSize
4135 46 : ? static_cast<size_t>(
4136 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4137 1 : CPLAtoGIntBig(pszSwathSize)))
4138 : : static_cast<size_t>(
4139 45 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4140 45 : GDALGetCacheMax64() / 4));
4141 46 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4142 46 : size_t nRealChunkSize = nDTSize;
4143 125 : for (const auto &nChunkSize : anChunkSizes)
4144 : {
4145 79 : nRealChunkSize *= nChunkSize;
4146 : }
4147 : try
4148 : {
4149 46 : copyFunc.abyTmp.resize(nRealChunkSize);
4150 : }
4151 0 : catch (const std::exception &)
4152 : {
4153 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4154 : "Cannot allocate temporary buffer");
4155 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4156 0 : return false;
4157 : }
4158 137 : if (copyFunc.nTotalBytesThisArray != 0 &&
4159 45 : !const_cast<GDALMDArray *>(poSrcArray)
4160 45 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4161 : anChunkSizes.data(), CopyFunc::f,
4162 91 : ©Func) &&
4163 0 : (bStrict || copyFunc.bStop))
4164 : {
4165 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4166 0 : return false;
4167 : }
4168 46 : nCurCost += copyFunc.nTotalBytesThisArray;
4169 : }
4170 :
4171 48 : return true;
4172 : }
4173 :
4174 : /************************************************************************/
4175 : /* GetStructuralInfo() */
4176 : /************************************************************************/
4177 :
4178 : /** Return structural information on the array.
4179 : *
4180 : * This may be the compression, etc..
4181 : *
4182 : * The return value should not be freed and is valid until GDALMDArray is
4183 : * released or this function called again.
4184 : *
4185 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4186 : */
4187 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4188 : {
4189 95 : return nullptr;
4190 : }
4191 :
4192 : /************************************************************************/
4193 : /* AdviseRead() */
4194 : /************************************************************************/
4195 :
4196 : /** Advise driver of upcoming read requests.
4197 : *
4198 : * Some GDAL drivers operate more efficiently if they know in advance what
4199 : * set of upcoming read requests will be made. The AdviseRead() method allows
4200 : * an application to notify the driver of the region of interest.
4201 : *
4202 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4203 : * accelerate access via some drivers. One such case is when reading through
4204 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4205 : * with the region of interest defined by AdviseRead())
4206 : *
4207 : * This is the same as the C function GDALMDArrayAdviseRead().
4208 : *
4209 : * @param arrayStartIdx Values representing the starting index to read
4210 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4211 : * Array of GetDimensionCount() values.
4212 : * Can be nullptr as a synonymous for [0 for i in
4213 : * range(GetDimensionCount() ]
4214 : *
4215 : * @param count Values representing the number of values to extract in
4216 : * each dimension.
4217 : * Array of GetDimensionCount() values.
4218 : * Can be nullptr as a synonymous for
4219 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4220 : * range(GetDimensionCount() ]
4221 : *
4222 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4223 : * documentation.
4224 : *
4225 : * @return true in case of success (ignoring the advice is a success)
4226 : *
4227 : * @since GDAL 3.2
4228 : */
4229 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4230 : CSLConstList papszOptions) const
4231 : {
4232 25 : const auto nDimCount = GetDimensionCount();
4233 25 : if (nDimCount == 0)
4234 2 : return true;
4235 :
4236 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4237 23 : if (arrayStartIdx == nullptr)
4238 : {
4239 0 : tmp_arrayStartIdx.resize(nDimCount);
4240 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4241 : }
4242 :
4243 46 : std::vector<size_t> tmp_count;
4244 23 : if (count == nullptr)
4245 : {
4246 0 : tmp_count.resize(nDimCount);
4247 0 : const auto &dims = GetDimensions();
4248 0 : for (size_t i = 0; i < nDimCount; i++)
4249 : {
4250 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4251 : #if SIZEOF_VOIDP < 8
4252 : if (nSize != static_cast<size_t>(nSize))
4253 : {
4254 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4255 : return false;
4256 : }
4257 : #endif
4258 0 : tmp_count[i] = static_cast<size_t>(nSize);
4259 : }
4260 0 : count = tmp_count.data();
4261 : }
4262 :
4263 46 : std::vector<GInt64> tmp_arrayStep;
4264 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4265 23 : const GInt64 *arrayStep = nullptr;
4266 23 : const GPtrDiff_t *bufferStride = nullptr;
4267 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4268 46 : GDALExtendedDataType::Create(GDT_Unknown),
4269 : nullptr, nullptr, 0, tmp_arrayStep,
4270 : tmp_bufferStride))
4271 : {
4272 1 : return false;
4273 : }
4274 :
4275 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4276 : }
4277 :
4278 : /************************************************************************/
4279 : /* IAdviseRead() */
4280 : /************************************************************************/
4281 :
4282 : //! @cond Doxygen_Suppress
4283 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4284 : CSLConstList /* papszOptions*/) const
4285 : {
4286 3 : return true;
4287 : }
4288 :
4289 : //! @endcond
4290 :
4291 : /************************************************************************/
4292 : /* MassageName() */
4293 : /************************************************************************/
4294 :
4295 : //! @cond Doxygen_Suppress
4296 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4297 : {
4298 32 : std::string ret;
4299 604 : for (const char ch : inputName)
4300 : {
4301 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4302 138 : ret += '_';
4303 : else
4304 434 : ret += ch;
4305 : }
4306 32 : return ret;
4307 : }
4308 :
4309 : //! @endcond
4310 :
4311 : /************************************************************************/
4312 : /* GetCacheRootGroup() */
4313 : /************************************************************************/
4314 :
4315 : //! @cond Doxygen_Suppress
4316 : std::shared_ptr<GDALGroup>
4317 1442 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4318 : std::string &osCacheFilenameOut) const
4319 : {
4320 1442 : const auto &osFilename = GetFilename();
4321 1442 : if (osFilename.empty())
4322 : {
4323 1 : CPLError(CE_Failure, CPLE_AppDefined,
4324 : "Cannot cache an array with an empty filename");
4325 1 : return nullptr;
4326 : }
4327 :
4328 1441 : osCacheFilenameOut = osFilename + ".gmac";
4329 1441 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4330 : {
4331 0 : const auto nPosQuestionMark = osFilename.find('?');
4332 0 : if (nPosQuestionMark != std::string::npos)
4333 : {
4334 : osCacheFilenameOut =
4335 0 : osFilename.substr(0, nPosQuestionMark)
4336 0 : .append(".gmac")
4337 0 : .append(osFilename.substr(nPosQuestionMark));
4338 : }
4339 : }
4340 1441 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4341 1441 : if (pszProxy != nullptr)
4342 7 : osCacheFilenameOut = pszProxy;
4343 :
4344 1441 : std::unique_ptr<GDALDataset> poDS;
4345 : VSIStatBufL sStat;
4346 1441 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4347 : {
4348 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4349 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4350 : nullptr, nullptr, nullptr));
4351 : }
4352 1441 : if (poDS)
4353 : {
4354 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4355 28 : return poDS->GetRootGroup();
4356 : }
4357 :
4358 1413 : if (bCanCreate)
4359 : {
4360 4 : const char *pszDrvName = "netCDF";
4361 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4362 4 : if (poDrv == nullptr)
4363 : {
4364 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4365 : pszDrvName);
4366 0 : return nullptr;
4367 : }
4368 : {
4369 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4370 8 : CPLErrorStateBackuper oErrorStateBackuper;
4371 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4372 : nullptr, nullptr));
4373 : }
4374 4 : if (!poDS)
4375 : {
4376 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4377 1 : if (pszProxy)
4378 : {
4379 1 : osCacheFilenameOut = pszProxy;
4380 1 : poDS.reset(poDrv->CreateMultiDimensional(
4381 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4382 : }
4383 : }
4384 4 : if (poDS)
4385 : {
4386 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4387 4 : return poDS->GetRootGroup();
4388 : }
4389 : else
4390 : {
4391 0 : CPLError(CE_Failure, CPLE_AppDefined,
4392 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4393 : "configuration option to write the cache in "
4394 : "another directory",
4395 : osCacheFilenameOut.c_str());
4396 : }
4397 : }
4398 :
4399 1409 : return nullptr;
4400 : }
4401 :
4402 : //! @endcond
4403 :
4404 : /************************************************************************/
4405 : /* Cache() */
4406 : /************************************************************************/
4407 :
4408 : /** Cache the content of the array into an auxiliary filename.
4409 : *
4410 : * The main purpose of this method is to be able to cache views that are
4411 : * expensive to compute, such as transposed arrays.
4412 : *
4413 : * The array will be stored in a file whose name is the one of
4414 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4415 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4416 : *
4417 : * If the .gmac file cannot be written next to the dataset, the
4418 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4419 : * directory.
4420 : *
4421 : * The GDALMDArray::Read() method will automatically use the cache when it
4422 : * exists. There is no timestamp checks between the source array and the cached
4423 : * array. If the source arrays changes, the cache must be manually deleted.
4424 : *
4425 : * This is the same as the C function GDALMDArrayCache()
4426 : *
4427 : * @note Driver implementation: optionally implemented.
4428 : *
4429 : * @param papszOptions List of options, null terminated, or NULL. Currently
4430 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4431 : * to specify the block size of the cached array.
4432 : * @return true in case of success.
4433 : */
4434 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4435 : {
4436 14 : std::string osCacheFilename;
4437 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4438 7 : if (!poRG)
4439 1 : return false;
4440 :
4441 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4442 6 : if (poRG->OpenMDArray(osCachedArrayName))
4443 : {
4444 2 : CPLError(CE_Failure, CPLE_NotSupported,
4445 : "An array with same name %s already exists in %s",
4446 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4447 2 : return false;
4448 : }
4449 :
4450 8 : CPLStringList aosOptions;
4451 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4452 4 : const auto &aoDims = GetDimensions();
4453 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4454 4 : if (!aoDims.empty())
4455 : {
4456 : std::string osBlockSize(
4457 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4458 4 : if (osBlockSize.empty())
4459 : {
4460 6 : const auto anBlockSize = GetBlockSize();
4461 3 : int idxDim = 0;
4462 10 : for (auto nBlockSize : anBlockSize)
4463 : {
4464 7 : if (idxDim > 0)
4465 4 : osBlockSize += ',';
4466 7 : if (nBlockSize == 0)
4467 7 : nBlockSize = 256;
4468 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4469 : osBlockSize +=
4470 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4471 7 : idxDim++;
4472 : }
4473 : }
4474 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4475 :
4476 4 : int idxDim = 0;
4477 13 : for (const auto &poDim : aoDims)
4478 : {
4479 9 : auto poNewDim = poRG->CreateDimension(
4480 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4481 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4482 9 : if (!poNewDim)
4483 0 : return false;
4484 9 : aoNewDims.emplace_back(poNewDim);
4485 9 : idxDim++;
4486 : }
4487 : }
4488 :
4489 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4490 8 : GetDataType(), aosOptions.List());
4491 4 : if (!poCachedArray)
4492 : {
4493 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4494 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4495 0 : return false;
4496 : }
4497 :
4498 4 : GUInt64 nCost = 0;
4499 8 : return poCachedArray->CopyFrom(nullptr, this,
4500 : false, // strict
4501 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4502 : }
4503 :
4504 : /************************************************************************/
4505 : /* Read() */
4506 : /************************************************************************/
4507 :
4508 3913 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4509 : const GInt64 *arrayStep, // step in elements
4510 : const GPtrDiff_t *bufferStride, // stride in elements
4511 : const GDALExtendedDataType &bufferDataType,
4512 : void *pDstBuffer, const void *pDstBufferAllocStart,
4513 : size_t nDstBufferAllocSize) const
4514 : {
4515 3913 : if (!m_bHasTriedCachedArray)
4516 : {
4517 1748 : m_bHasTriedCachedArray = true;
4518 1748 : if (IsCacheable())
4519 : {
4520 1748 : const auto &osFilename = GetFilename();
4521 2986 : if (!osFilename.empty() &&
4522 2986 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4523 : {
4524 2456 : std::string osCacheFilename;
4525 2456 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4526 1228 : if (poRG)
4527 : {
4528 : const std::string osCachedArrayName(
4529 32 : MassageName(GetFullName()));
4530 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4531 16 : if (m_poCachedArray)
4532 : {
4533 6 : const auto &dims = GetDimensions();
4534 : const auto &cachedDims =
4535 6 : m_poCachedArray->GetDimensions();
4536 6 : const size_t nDims = dims.size();
4537 : bool ok =
4538 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4539 6 : cachedDims.size() == nDims;
4540 19 : for (size_t i = 0; ok && i < nDims; ++i)
4541 : {
4542 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4543 : }
4544 6 : if (ok)
4545 : {
4546 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4547 : osCachedArrayName.c_str(),
4548 : osCacheFilename.c_str());
4549 : }
4550 : else
4551 : {
4552 0 : CPLError(CE_Warning, CPLE_AppDefined,
4553 : "Cached array %s in %s has incompatible "
4554 : "characteristics with current array.",
4555 : osCachedArrayName.c_str(),
4556 : osCacheFilename.c_str());
4557 0 : m_poCachedArray.reset();
4558 : }
4559 : }
4560 : }
4561 : }
4562 : }
4563 : }
4564 :
4565 3913 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4566 3913 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4567 : {
4568 0 : CPLError(CE_Failure, CPLE_AppDefined,
4569 : "Array data type is not convertible to buffer data type");
4570 0 : return false;
4571 : }
4572 :
4573 7826 : std::vector<GInt64> tmp_arrayStep;
4574 7826 : std::vector<GPtrDiff_t> tmp_bufferStride;
4575 3913 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4576 : bufferStride, bufferDataType, pDstBuffer,
4577 : pDstBufferAllocStart, nDstBufferAllocSize,
4578 : tmp_arrayStep, tmp_bufferStride))
4579 : {
4580 9 : return false;
4581 : }
4582 :
4583 3904 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4584 3904 : bufferDataType, pDstBuffer);
4585 : }
4586 :
4587 : /************************************************************************/
4588 : /* GetRootGroup() */
4589 : /************************************************************************/
4590 :
4591 : /** Return the root group to which this arrays belongs too.
4592 : *
4593 : * Note that arrays may be free standing and some drivers may not implement
4594 : * this method, hence nullptr may be returned.
4595 : *
4596 : * It is used internally by the GetResampled() method to detect if GLT
4597 : * orthorectification is available.
4598 : *
4599 : * @return the root group, or nullptr.
4600 : * @since GDAL 3.8
4601 : */
4602 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4603 : {
4604 0 : return nullptr;
4605 : }
4606 :
4607 : //! @cond Doxygen_Suppress
4608 :
4609 : /************************************************************************/
4610 : /* IsTransposedRequest() */
4611 : /************************************************************************/
4612 :
4613 788 : bool GDALMDArray::IsTransposedRequest(
4614 : const size_t *count,
4615 : const GPtrDiff_t *bufferStride) const // stride in elements
4616 : {
4617 : /*
4618 : For example:
4619 : count = [2,3,4]
4620 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4621 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4622 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4623 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4624 : */
4625 788 : const size_t nDims(GetDimensionCount());
4626 788 : size_t nCurStrideForRowMajorStrides = 1;
4627 788 : bool bRowMajorStrides = true;
4628 788 : size_t nElts = 1;
4629 788 : size_t nLastIdx = 0;
4630 2169 : for (size_t i = nDims; i > 0;)
4631 : {
4632 1381 : --i;
4633 1381 : if (bufferStride[i] < 0)
4634 0 : return false;
4635 1381 : if (static_cast<size_t>(bufferStride[i]) !=
4636 : nCurStrideForRowMajorStrides)
4637 : {
4638 258 : bRowMajorStrides = false;
4639 : }
4640 : // Integer overflows have already been checked in CheckReadWriteParams()
4641 1381 : nCurStrideForRowMajorStrides *= count[i];
4642 1381 : nElts *= count[i];
4643 1381 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4644 : }
4645 788 : if (bRowMajorStrides)
4646 602 : return false;
4647 186 : return nLastIdx == nElts - 1;
4648 : }
4649 :
4650 : /************************************************************************/
4651 : /* CopyToFinalBufferSameDataType() */
4652 : /************************************************************************/
4653 :
4654 : template <size_t N>
4655 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4656 : size_t nDims, const size_t *count,
4657 : const GPtrDiff_t *bufferStride)
4658 : {
4659 120 : std::vector<size_t> anStackCount(nDims);
4660 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4661 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4662 : #if defined(__GNUC__)
4663 : #pragma GCC diagnostic push
4664 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4665 : #endif
4666 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4667 : #if defined(__GNUC__)
4668 : #pragma GCC diagnostic pop
4669 : #endif
4670 60 : size_t iDim = 0;
4671 :
4672 749 : lbl_next_depth:
4673 749 : if (iDim == nDims - 1)
4674 : {
4675 661 : size_t n = count[iDim];
4676 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4677 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4678 29186 : while (n > 0)
4679 : {
4680 28525 : --n;
4681 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4682 28525 : pabyDstBuffer += bufferStrideLastDim;
4683 28525 : pabySrcBuffer += N;
4684 : }
4685 : }
4686 : else
4687 : {
4688 88 : anStackCount[iDim] = count[iDim];
4689 : while (true)
4690 : {
4691 689 : ++iDim;
4692 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4693 689 : goto lbl_next_depth;
4694 689 : lbl_return_to_caller_in_loop:
4695 689 : --iDim;
4696 689 : --anStackCount[iDim];
4697 689 : if (anStackCount[iDim] == 0)
4698 88 : break;
4699 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4700 : }
4701 : }
4702 749 : if (iDim > 0)
4703 689 : goto lbl_return_to_caller_in_loop;
4704 60 : }
4705 :
4706 : /************************************************************************/
4707 : /* CopyToFinalBuffer() */
4708 : /************************************************************************/
4709 :
4710 166 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4711 : const GDALExtendedDataType &eSrcDataType,
4712 : void *pDstBuffer,
4713 : const GDALExtendedDataType &eDstDataType,
4714 : size_t nDims, const size_t *count,
4715 : const GPtrDiff_t *bufferStride)
4716 : {
4717 166 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4718 : // Use specialized implementation for well-known data types when no
4719 : // type conversion is needed
4720 166 : if (eSrcDataType == eDstDataType)
4721 : {
4722 110 : if (nSrcDataTypeSize == 1)
4723 : {
4724 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4725 : count, bufferStride);
4726 60 : return;
4727 : }
4728 69 : else if (nSrcDataTypeSize == 2)
4729 : {
4730 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4731 : count, bufferStride);
4732 1 : return;
4733 : }
4734 68 : else if (nSrcDataTypeSize == 4)
4735 : {
4736 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4737 : count, bufferStride);
4738 14 : return;
4739 : }
4740 54 : else if (nSrcDataTypeSize == 8)
4741 : {
4742 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4743 : count, bufferStride);
4744 4 : return;
4745 : }
4746 : }
4747 :
4748 106 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4749 212 : std::vector<size_t> anStackCount(nDims);
4750 212 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4751 106 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4752 106 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4753 106 : size_t iDim = 0;
4754 :
4755 375 : lbl_next_depth:
4756 375 : if (iDim == nDims - 1)
4757 : {
4758 365 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4759 365 : pabyDstBufferStack[iDim], eDstDataType,
4760 365 : bufferStride[iDim], count[iDim]);
4761 365 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4762 : }
4763 : else
4764 : {
4765 10 : anStackCount[iDim] = count[iDim];
4766 : while (true)
4767 : {
4768 269 : ++iDim;
4769 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4770 269 : goto lbl_next_depth;
4771 269 : lbl_return_to_caller_in_loop:
4772 269 : --iDim;
4773 269 : --anStackCount[iDim];
4774 269 : if (anStackCount[iDim] == 0)
4775 10 : break;
4776 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4777 : }
4778 : }
4779 375 : if (iDim > 0)
4780 269 : goto lbl_return_to_caller_in_loop;
4781 : }
4782 :
4783 : /************************************************************************/
4784 : /* TransposeLast2Dims() */
4785 : /************************************************************************/
4786 :
4787 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4788 : const GDALExtendedDataType &eDT,
4789 : const size_t nDims, const size_t *count,
4790 : const size_t nEltsNonLast2Dims)
4791 : {
4792 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4793 19 : const auto nDTSize = eDT.GetSize();
4794 : void *pTempBufferForLast2DimsTranspose =
4795 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4796 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4797 0 : return false;
4798 :
4799 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4800 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4801 : {
4802 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4803 : pTempBufferForLast2DimsTranspose,
4804 39 : eDT.GetNumericDataType(), count[nDims - 1],
4805 39 : count[nDims - 2]);
4806 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4807 : nDTSize * nEltsLast2Dims);
4808 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4809 : }
4810 :
4811 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4812 :
4813 19 : return true;
4814 : }
4815 :
4816 : /************************************************************************/
4817 : /* ReadForTransposedRequest() */
4818 : /************************************************************************/
4819 :
4820 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4821 : // transposed view yield to extremely poor/unusable performance. This fixes
4822 : // this by using temporary memory to read in a contiguous buffer in a
4823 : // row-major order, and then do the transposition to the final buffer.
4824 :
4825 185 : bool GDALMDArray::ReadForTransposedRequest(
4826 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4827 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4828 : void *pDstBuffer) const
4829 : {
4830 185 : const size_t nDims(GetDimensionCount());
4831 185 : if (nDims == 0)
4832 : {
4833 0 : CPLAssert(false);
4834 : return false; // shouldn't happen
4835 : }
4836 185 : size_t nElts = 1;
4837 492 : for (size_t i = 0; i < nDims; ++i)
4838 307 : nElts *= count[i];
4839 :
4840 370 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4841 185 : tmpBufferStrides.back() = 1;
4842 307 : for (size_t i = nDims - 1; i > 0;)
4843 : {
4844 122 : --i;
4845 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4846 : }
4847 :
4848 185 : const auto &eDT = GetDataType();
4849 185 : const auto nDTSize = eDT.GetSize();
4850 314 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4851 330 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4852 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4853 : {
4854 : // Optimization of the optimization if only the last 2 dims are
4855 : // transposed that saves on temporary buffer allocation
4856 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4857 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4858 23 : bool bRowMajorStridesForNonLast2Dims = true;
4859 23 : size_t nEltsNonLast2Dims = 1;
4860 40 : for (size_t i = nDims - 2; i > 0;)
4861 : {
4862 17 : --i;
4863 17 : if (static_cast<size_t>(bufferStride[i]) !=
4864 : nCurStrideForRowMajorStrides)
4865 : {
4866 4 : bRowMajorStridesForNonLast2Dims = false;
4867 : }
4868 : // Integer overflows have already been checked in
4869 : // CheckReadWriteParams()
4870 17 : nCurStrideForRowMajorStrides *= count[i];
4871 17 : nEltsNonLast2Dims *= count[i];
4872 : }
4873 23 : if (bRowMajorStridesForNonLast2Dims)
4874 : {
4875 : // We read in the final buffer!
4876 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4877 19 : eDT, pDstBuffer))
4878 : {
4879 0 : return false;
4880 : }
4881 :
4882 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4883 19 : nEltsNonLast2Dims);
4884 : }
4885 : }
4886 :
4887 166 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4888 166 : if (pTempBuffer == nullptr)
4889 0 : return false;
4890 :
4891 166 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4892 166 : pTempBuffer))
4893 : {
4894 0 : VSIFree(pTempBuffer);
4895 0 : return false;
4896 : }
4897 166 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4898 : count, bufferStride);
4899 :
4900 166 : if (eDT.NeedsFreeDynamicMemory())
4901 : {
4902 95 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4903 190 : for (size_t i = 0; i < nElts; ++i)
4904 : {
4905 95 : eDT.FreeDynamicMemory(pabyPtr);
4906 95 : pabyPtr += nDTSize;
4907 : }
4908 : }
4909 :
4910 166 : VSIFree(pTempBuffer);
4911 166 : return true;
4912 : }
4913 :
4914 : /************************************************************************/
4915 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4916 : /************************************************************************/
4917 :
4918 : // Returns true if at all following conditions are met:
4919 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4920 : // defines a row-major ordered contiguous buffer.
4921 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4922 : const size_t *count, const GInt64 *arrayStep,
4923 : const GPtrDiff_t *bufferStride,
4924 : const GDALExtendedDataType &bufferDataType) const
4925 : {
4926 78 : if (bufferDataType != GetDataType())
4927 5 : return false;
4928 73 : size_t nExpectedStride = 1;
4929 166 : for (size_t i = GetDimensionCount(); i > 0;)
4930 : {
4931 96 : --i;
4932 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4933 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4934 : {
4935 3 : return false;
4936 : }
4937 93 : nExpectedStride *= count[i];
4938 : }
4939 70 : return true;
4940 : }
4941 :
4942 : /************************************************************************/
4943 : /* ReadUsingContiguousIRead() */
4944 : /************************************************************************/
4945 :
4946 : // Used for example by the TileDB driver when requesting it with
4947 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4948 : // not defining a row-major ordered contiguous buffer.
4949 : // Should only be called when at least one of the above conditions are true,
4950 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4951 : // returning none.
4952 : // This method will call IRead() again with arrayStep[] == 1,
4953 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4954 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4955 : // content of that temporary buffer onto pDstBuffer.
4956 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4957 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4958 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4959 : void *pDstBuffer) const
4960 : {
4961 7 : const size_t nDims(GetDimensionCount());
4962 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4963 14 : std::vector<size_t> anTmpCount(nDims);
4964 7 : const auto &oType = GetDataType();
4965 7 : size_t nMemArraySize = oType.GetSize();
4966 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4967 7 : GPtrDiff_t nStride = 1;
4968 18 : for (size_t i = nDims; i > 0;)
4969 : {
4970 11 : --i;
4971 11 : if (arrayStep[i] > 0)
4972 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4973 : else
4974 2 : anTmpStartIdx[i] =
4975 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4976 : const uint64_t nCount =
4977 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4978 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4979 : {
4980 0 : CPLError(CE_Failure, CPLE_AppDefined,
4981 : "Read() failed due to too large memory requirement");
4982 0 : return false;
4983 : }
4984 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4985 11 : nMemArraySize *= anTmpCount[i];
4986 11 : anTmpStride[i] = nStride;
4987 11 : nStride *= anTmpCount[i];
4988 : }
4989 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4990 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4991 7 : if (!pTmpBuffer)
4992 0 : return false;
4993 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4994 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4995 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4996 : {
4997 0 : return false;
4998 : }
4999 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5000 18 : for (size_t i = 0; i < nDims; ++i)
5001 : {
5002 11 : if (arrayStep[i] > 0)
5003 9 : anTmpStartIdx[i] = 0;
5004 : else
5005 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5006 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5007 22 : std::string(), std::string(), std::string(), std::string(),
5008 22 : anTmpCount[i]);
5009 : }
5010 : auto poMEMArray =
5011 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5012 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5013 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5014 7 : bufferStride, bufferDataType, pDstBuffer);
5015 : }
5016 :
5017 : //! @endcond
5018 :
5019 : /************************************************************************/
5020 : /* GDALSlicedMDArray */
5021 : /************************************************************************/
5022 :
5023 : class GDALSlicedMDArray final : public GDALPamMDArray
5024 : {
5025 : private:
5026 : std::shared_ptr<GDALMDArray> m_poParent{};
5027 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5028 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5029 : std::vector<Range>
5030 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5031 :
5032 : mutable std::vector<GUInt64> m_parentStart;
5033 : mutable std::vector<size_t> m_parentCount;
5034 : mutable std::vector<GInt64> m_parentStep;
5035 : mutable std::vector<GPtrDiff_t> m_parentStride;
5036 :
5037 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5038 : const GInt64 *arrayStep,
5039 : const GPtrDiff_t *bufferStride) const;
5040 :
5041 : protected:
5042 582 : explicit GDALSlicedMDArray(
5043 : const std::shared_ptr<GDALMDArray> &poParent,
5044 : const std::string &viewExpr,
5045 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5046 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5047 : std::vector<Range> &&parentRanges)
5048 1746 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5049 1746 : poParent->GetFullName() +
5050 1164 : " (" + viewExpr + ")"),
5051 1164 : GDALPamMDArray(std::string(),
5052 1164 : "Sliced view of " + poParent->GetFullName() + " (" +
5053 1164 : viewExpr + ")",
5054 1164 : GDALPamMultiDim::GetPAM(poParent),
5055 : poParent->GetContext()),
5056 1164 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5057 582 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5058 582 : m_parentRanges(std::move(parentRanges)),
5059 582 : m_parentStart(m_poParent->GetDimensionCount()),
5060 582 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5061 582 : m_parentStep(m_poParent->GetDimensionCount()),
5062 4656 : m_parentStride(m_poParent->GetDimensionCount())
5063 : {
5064 582 : }
5065 :
5066 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5067 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5068 : const GDALExtendedDataType &bufferDataType,
5069 : void *pDstBuffer) const override;
5070 :
5071 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5072 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5073 : const GDALExtendedDataType &bufferDataType,
5074 : const void *pSrcBuffer) override;
5075 :
5076 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5077 : CSLConstList papszOptions) const override;
5078 :
5079 : public:
5080 : static std::shared_ptr<GDALSlicedMDArray>
5081 582 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5082 : const std::string &viewExpr,
5083 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5084 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5085 : std::vector<Range> &&parentRanges)
5086 : {
5087 582 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5088 582 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5089 :
5090 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5091 582 : poParent, viewExpr, std::move(dims),
5092 582 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5093 582 : newAr->SetSelf(newAr);
5094 582 : return newAr;
5095 : }
5096 :
5097 56 : bool IsWritable() const override
5098 : {
5099 56 : return m_poParent->IsWritable();
5100 : }
5101 :
5102 990 : const std::string &GetFilename() const override
5103 : {
5104 990 : return m_poParent->GetFilename();
5105 : }
5106 :
5107 : const std::vector<std::shared_ptr<GDALDimension>> &
5108 3687 : GetDimensions() const override
5109 : {
5110 3687 : return m_dims;
5111 : }
5112 :
5113 1402 : const GDALExtendedDataType &GetDataType() const override
5114 : {
5115 1402 : return m_poParent->GetDataType();
5116 : }
5117 :
5118 2 : const std::string &GetUnit() const override
5119 : {
5120 2 : return m_poParent->GetUnit();
5121 : }
5122 :
5123 : // bool SetUnit(const std::string& osUnit) override { return
5124 : // m_poParent->SetUnit(osUnit); }
5125 :
5126 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5127 : {
5128 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5129 2 : if (!poSrcSRS)
5130 1 : return nullptr;
5131 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5132 2 : std::vector<int> dstMapping;
5133 3 : for (int srcAxis : srcMapping)
5134 : {
5135 2 : bool bFound = false;
5136 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5137 : {
5138 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5139 3 : srcAxis - 1)
5140 : {
5141 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5142 2 : bFound = true;
5143 2 : break;
5144 : }
5145 : }
5146 2 : if (!bFound)
5147 : {
5148 0 : dstMapping.push_back(0);
5149 : }
5150 : }
5151 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5152 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5153 1 : return poClone;
5154 : }
5155 :
5156 59 : const void *GetRawNoDataValue() const override
5157 : {
5158 59 : return m_poParent->GetRawNoDataValue();
5159 : }
5160 :
5161 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5162 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5163 :
5164 2 : double GetOffset(bool *pbHasOffset,
5165 : GDALDataType *peStorageType) const override
5166 : {
5167 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5168 : }
5169 :
5170 2 : double GetScale(bool *pbHasScale,
5171 : GDALDataType *peStorageType) const override
5172 : {
5173 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5174 : }
5175 :
5176 : // bool SetOffset(double dfOffset) override { return
5177 : // m_poParent->SetOffset(dfOffset); }
5178 :
5179 : // bool SetScale(double dfScale) override { return
5180 : // m_poParent->SetScale(dfScale); }
5181 :
5182 198 : std::vector<GUInt64> GetBlockSize() const override
5183 : {
5184 198 : std::vector<GUInt64> ret(GetDimensionCount());
5185 396 : const auto parentBlockSize(m_poParent->GetBlockSize());
5186 598 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5187 : {
5188 400 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5189 400 : if (iOldAxis != static_cast<size_t>(-1))
5190 : {
5191 400 : ret[i] = parentBlockSize[iOldAxis];
5192 : }
5193 : }
5194 396 : return ret;
5195 : }
5196 :
5197 : std::shared_ptr<GDALAttribute>
5198 6 : GetAttribute(const std::string &osName) const override
5199 : {
5200 6 : return m_poParent->GetAttribute(osName);
5201 : }
5202 :
5203 : std::vector<std::shared_ptr<GDALAttribute>>
5204 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5205 : {
5206 24 : return m_poParent->GetAttributes(papszOptions);
5207 : }
5208 : };
5209 :
5210 : /************************************************************************/
5211 : /* PrepareParentArrays() */
5212 : /************************************************************************/
5213 :
5214 480 : void GDALSlicedMDArray::PrepareParentArrays(
5215 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5216 : const GPtrDiff_t *bufferStride) const
5217 : {
5218 480 : const size_t nParentDimCount = m_parentRanges.size();
5219 1496 : for (size_t i = 0; i < nParentDimCount; i++)
5220 : {
5221 : // For dimensions in parent that have no existence in sliced array
5222 1016 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5223 : }
5224 :
5225 1265 : for (size_t i = 0; i < m_dims.size(); i++)
5226 : {
5227 785 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5228 785 : if (iParent != static_cast<size_t>(-1))
5229 : {
5230 783 : m_parentStart[iParent] =
5231 783 : m_parentRanges[iParent].m_nIncr >= 0
5232 783 : ? m_parentRanges[iParent].m_nStartIdx +
5233 749 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5234 34 : : m_parentRanges[iParent].m_nStartIdx -
5235 68 : arrayStartIdx[i] *
5236 34 : static_cast<GUInt64>(
5237 34 : -m_parentRanges[iParent].m_nIncr);
5238 783 : m_parentCount[iParent] = count[i];
5239 783 : if (arrayStep)
5240 : {
5241 782 : m_parentStep[iParent] =
5242 782 : count[i] == 1 ? 1 :
5243 : // other checks should have ensured this does
5244 : // not overflow
5245 592 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5246 : }
5247 783 : if (bufferStride)
5248 : {
5249 782 : m_parentStride[iParent] = bufferStride[i];
5250 : }
5251 : }
5252 : }
5253 480 : }
5254 :
5255 : /************************************************************************/
5256 : /* IRead() */
5257 : /************************************************************************/
5258 :
5259 447 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5260 : const GInt64 *arrayStep,
5261 : const GPtrDiff_t *bufferStride,
5262 : const GDALExtendedDataType &bufferDataType,
5263 : void *pDstBuffer) const
5264 : {
5265 447 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5266 894 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5267 447 : m_parentStep.data(), m_parentStride.data(),
5268 447 : bufferDataType, pDstBuffer);
5269 : }
5270 :
5271 : /************************************************************************/
5272 : /* IWrite() */
5273 : /************************************************************************/
5274 :
5275 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5276 : const size_t *count, const GInt64 *arrayStep,
5277 : const GPtrDiff_t *bufferStride,
5278 : const GDALExtendedDataType &bufferDataType,
5279 : const void *pSrcBuffer)
5280 : {
5281 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5282 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5283 32 : m_parentStep.data(), m_parentStride.data(),
5284 32 : bufferDataType, pSrcBuffer);
5285 : }
5286 :
5287 : /************************************************************************/
5288 : /* IAdviseRead() */
5289 : /************************************************************************/
5290 :
5291 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5292 : const size_t *count,
5293 : CSLConstList papszOptions) const
5294 : {
5295 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5296 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5297 1 : papszOptions);
5298 : }
5299 :
5300 : /************************************************************************/
5301 : /* CreateSlicedArray() */
5302 : /************************************************************************/
5303 :
5304 : static std::shared_ptr<GDALMDArray>
5305 600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5306 : const std::string &viewExpr, const std::string &activeSlice,
5307 : bool bRenameDimensions,
5308 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5309 : {
5310 600 : const auto &srcDims(self->GetDimensions());
5311 600 : if (srcDims.empty())
5312 : {
5313 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5314 2 : return nullptr;
5315 : }
5316 :
5317 1196 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5318 598 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5319 :
5320 1196 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5321 1196 : std::vector<size_t> mapDimIdxToParentDimIdx;
5322 1196 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5323 598 : newDims.reserve(nTokens);
5324 598 : mapDimIdxToParentDimIdx.reserve(nTokens);
5325 598 : parentRanges.reserve(nTokens);
5326 :
5327 598 : bool bGotEllipsis = false;
5328 598 : size_t nCurSrcDim = 0;
5329 1768 : for (size_t i = 0; i < nTokens; i++)
5330 : {
5331 1186 : const char *pszIdxSpec = aosTokens[i];
5332 1186 : if (EQUAL(pszIdxSpec, "..."))
5333 : {
5334 38 : if (bGotEllipsis)
5335 : {
5336 2 : CPLError(CE_Failure, CPLE_AppDefined,
5337 : "Only one single ellipsis is supported");
5338 2 : return nullptr;
5339 : }
5340 36 : bGotEllipsis = true;
5341 36 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5342 79 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5343 : {
5344 43 : parentRanges.emplace_back(0, 1);
5345 43 : newDims.push_back(srcDims[nCurSrcDim]);
5346 43 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5347 : }
5348 36 : continue;
5349 : }
5350 1148 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5351 1145 : EQUAL(pszIdxSpec, "np.newaxis"))
5352 : {
5353 3 : newDims.push_back(std::make_shared<GDALDimension>(
5354 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5355 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5356 3 : continue;
5357 : }
5358 1145 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5359 : {
5360 325 : if (nCurSrcDim >= srcDims.size())
5361 : {
5362 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5363 : activeSlice.c_str());
5364 7 : return nullptr;
5365 : }
5366 :
5367 323 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5368 323 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5369 323 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5370 319 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5371 : {
5372 5 : CPLError(CE_Failure, CPLE_AppDefined,
5373 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5374 5 : return nullptr;
5375 : }
5376 318 : if (nVal < 0)
5377 0 : nVal += nDimSize;
5378 318 : parentRanges.emplace_back(nVal, 0);
5379 : }
5380 : else
5381 : {
5382 820 : if (nCurSrcDim >= srcDims.size())
5383 : {
5384 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5385 : activeSlice.c_str());
5386 7 : return nullptr;
5387 : }
5388 :
5389 : CPLStringList aosRangeTokens(
5390 819 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5391 819 : int nRangeTokens = aosRangeTokens.size();
5392 819 : if (nRangeTokens > 3)
5393 : {
5394 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5395 : pszIdxSpec);
5396 1 : return nullptr;
5397 : }
5398 818 : if (nRangeTokens <= 1)
5399 : {
5400 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5401 : pszIdxSpec);
5402 1 : return nullptr;
5403 : }
5404 817 : const char *pszStart = aosRangeTokens[0];
5405 817 : const char *pszEnd = aosRangeTokens[1];
5406 817 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5407 817 : GDALSlicedMDArray::Range range;
5408 817 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5409 817 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5410 1633 : if (range.m_nIncr == 0 ||
5411 816 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5412 : {
5413 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5414 1 : return nullptr;
5415 : }
5416 816 : auto startIdx(CPLAtoGIntBig(pszStart));
5417 816 : if (startIdx < 0)
5418 : {
5419 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5420 0 : startIdx = 0;
5421 : else
5422 0 : startIdx = nDimSize + startIdx;
5423 : }
5424 816 : const bool bPosIncr = range.m_nIncr > 0;
5425 816 : range.m_nStartIdx = startIdx;
5426 1632 : range.m_nStartIdx = EQUAL(pszStart, "")
5427 816 : ? (bPosIncr ? 0 : nDimSize - 1)
5428 : : range.m_nStartIdx;
5429 816 : if (range.m_nStartIdx >= nDimSize - 1)
5430 186 : range.m_nStartIdx = nDimSize - 1;
5431 816 : auto endIdx(CPLAtoGIntBig(pszEnd));
5432 816 : if (endIdx < 0)
5433 : {
5434 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5435 1 : if (nDimSize < positiveEndIdx)
5436 0 : endIdx = 0;
5437 : else
5438 1 : endIdx = nDimSize - positiveEndIdx;
5439 : }
5440 816 : GUInt64 nEndIdx = endIdx;
5441 816 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5442 816 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5443 814 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5444 : {
5445 3 : CPLError(CE_Failure, CPLE_AppDefined,
5446 : "Output dimension of size 0 is not allowed");
5447 3 : return nullptr;
5448 : }
5449 813 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5450 813 : const auto nAbsIncr = std::abs(range.m_nIncr);
5451 813 : const GUInt64 newSize =
5452 : bPosIncr
5453 847 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5454 34 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5455 1341 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5456 528 : newSize == srcDims[nCurSrcDim]->GetSize())
5457 : {
5458 159 : newDims.push_back(srcDims[nCurSrcDim]);
5459 : }
5460 : else
5461 : {
5462 654 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5463 654 : if (bRenameDimensions)
5464 : {
5465 : osNewDimName =
5466 1212 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5467 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5468 : "_" CPL_FRMT_GUIB,
5469 606 : static_cast<GUIntBig>(range.m_nStartIdx),
5470 606 : static_cast<GIntBig>(range.m_nIncr),
5471 606 : static_cast<GUIntBig>(newSize));
5472 : }
5473 654 : newDims.push_back(std::make_shared<GDALDimension>(
5474 1308 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5475 1308 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5476 : : std::string(),
5477 : newSize));
5478 : }
5479 813 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5480 813 : parentRanges.emplace_back(range);
5481 : }
5482 :
5483 1131 : nCurSrcDim++;
5484 : }
5485 655 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5486 : {
5487 73 : parentRanges.emplace_back(0, 1);
5488 73 : newDims.push_back(srcDims[nCurSrcDim]);
5489 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5490 : }
5491 :
5492 582 : GDALMDArray::ViewSpec viewSpec;
5493 582 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5494 582 : viewSpec.m_parentRanges = parentRanges;
5495 582 : viewSpecs.emplace_back(std::move(viewSpec));
5496 :
5497 1164 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5498 582 : std::move(mapDimIdxToParentDimIdx),
5499 1164 : std::move(parentRanges));
5500 : }
5501 :
5502 : /************************************************************************/
5503 : /* GDALExtractFieldMDArray */
5504 : /************************************************************************/
5505 :
5506 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5507 : {
5508 : private:
5509 : std::shared_ptr<GDALMDArray> m_poParent{};
5510 : GDALExtendedDataType m_dt;
5511 : std::string m_srcCompName;
5512 : mutable std::vector<GByte> m_pabyNoData{};
5513 :
5514 : protected:
5515 82 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5516 : const std::string &fieldName,
5517 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5518 328 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5519 164 : " of " +
5520 82 : poParent->GetFullName()),
5521 : GDALPamMDArray(
5522 164 : std::string(),
5523 164 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5524 164 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5525 : m_poParent(poParent), m_dt(srcComp->GetType()),
5526 410 : m_srcCompName(srcComp->GetName())
5527 : {
5528 82 : m_pabyNoData.resize(m_dt.GetSize());
5529 82 : }
5530 :
5531 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5532 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5533 : const GDALExtendedDataType &bufferDataType,
5534 : void *pDstBuffer) const override;
5535 :
5536 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5537 : CSLConstList papszOptions) const override
5538 : {
5539 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5540 : }
5541 :
5542 : public:
5543 : static std::shared_ptr<GDALExtractFieldMDArray>
5544 82 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5545 : const std::string &fieldName,
5546 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5547 : {
5548 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5549 82 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5550 82 : newAr->SetSelf(newAr);
5551 82 : return newAr;
5552 : }
5553 :
5554 164 : ~GDALExtractFieldMDArray()
5555 82 : {
5556 82 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5557 164 : }
5558 :
5559 41 : bool IsWritable() const override
5560 : {
5561 41 : return m_poParent->IsWritable();
5562 : }
5563 :
5564 247 : const std::string &GetFilename() const override
5565 : {
5566 247 : return m_poParent->GetFilename();
5567 : }
5568 :
5569 : const std::vector<std::shared_ptr<GDALDimension>> &
5570 351 : GetDimensions() const override
5571 : {
5572 351 : return m_poParent->GetDimensions();
5573 : }
5574 :
5575 295 : const GDALExtendedDataType &GetDataType() const override
5576 : {
5577 295 : return m_dt;
5578 : }
5579 :
5580 2 : const std::string &GetUnit() const override
5581 : {
5582 2 : return m_poParent->GetUnit();
5583 : }
5584 :
5585 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5586 : {
5587 2 : return m_poParent->GetSpatialRef();
5588 : }
5589 :
5590 60 : const void *GetRawNoDataValue() const override
5591 : {
5592 60 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5593 60 : if (parentNoData == nullptr)
5594 1 : return nullptr;
5595 :
5596 59 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5597 59 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5598 :
5599 118 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5600 118 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5601 118 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5602 59 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5603 177 : std::move(comps)));
5604 :
5605 59 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5606 59 : &m_pabyNoData[0], tmpDT);
5607 :
5608 59 : return &m_pabyNoData[0];
5609 : }
5610 :
5611 2 : double GetOffset(bool *pbHasOffset,
5612 : GDALDataType *peStorageType) const override
5613 : {
5614 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5615 : }
5616 :
5617 2 : double GetScale(bool *pbHasScale,
5618 : GDALDataType *peStorageType) const override
5619 : {
5620 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5621 : }
5622 :
5623 42 : std::vector<GUInt64> GetBlockSize() const override
5624 : {
5625 42 : return m_poParent->GetBlockSize();
5626 : }
5627 : };
5628 :
5629 : /************************************************************************/
5630 : /* IRead() */
5631 : /************************************************************************/
5632 :
5633 88 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5634 : const size_t *count,
5635 : const GInt64 *arrayStep,
5636 : const GPtrDiff_t *bufferStride,
5637 : const GDALExtendedDataType &bufferDataType,
5638 : void *pDstBuffer) const
5639 : {
5640 176 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5641 176 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5642 176 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5643 : auto tmpDT(GDALExtendedDataType::Create(
5644 176 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5645 :
5646 88 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5647 176 : tmpDT, pDstBuffer);
5648 : }
5649 :
5650 : /************************************************************************/
5651 : /* CreateFieldNameExtractArray() */
5652 : /************************************************************************/
5653 :
5654 : static std::shared_ptr<GDALMDArray>
5655 83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5656 : const std::string &fieldName)
5657 : {
5658 83 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5659 83 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5660 202 : for (const auto &comp : self->GetDataType().GetComponents())
5661 : {
5662 201 : if (comp->GetName() == fieldName)
5663 : {
5664 82 : srcComp = ∁
5665 82 : break;
5666 : }
5667 : }
5668 83 : if (srcComp == nullptr)
5669 : {
5670 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5671 : fieldName.c_str());
5672 1 : return nullptr;
5673 : }
5674 82 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5675 : }
5676 :
5677 : /************************************************************************/
5678 : /* GetView() */
5679 : /************************************************************************/
5680 :
5681 : // clang-format off
5682 : /** Return a view of the array using slicing or field access.
5683 : *
5684 : * The slice expression uses the same syntax as NumPy basic slicing and
5685 : * indexing. See
5686 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5687 : * Or it can use field access by name. See
5688 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5689 : *
5690 : * Multiple [] bracket elements can be concatenated, with a slice expression
5691 : * or field name inside each.
5692 : *
5693 : * For basic slicing and indexing, inside each [] bracket element, a list of
5694 : * indexes that apply to successive source dimensions, can be specified, using
5695 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5696 : * or newaxis, using a comma separator.
5697 : *
5698 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5699 : * <ul>
5700 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5701 : * at index 1 in the first dimension, and index 2 in the second dimension
5702 : * from the source array. That is 5</li>
5703 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5704 : * implemented internally doing this intermediate slicing approach.</li>
5705 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5706 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5707 : * first dimension. That is [4,5,6,7].</li>
5708 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5709 : * second dimension. That is [2,6].</li>
5710 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5711 : * the second dimension. That is [[2],[6]].</li>
5712 : * <li>GetView("[::,2]"): Same as
5713 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5714 : * ellipsis only expands to one dimension here.</li>
5715 : * <li>GetView("[:,::2]"):
5716 : * returns a 2-dimensional array, with even-indexed elements of the second
5717 : * dimension. That is [[0,2],[4,6]].</li>
5718 : * <li>GetView("[:,1::2]"): returns a
5719 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5720 : * is [[1,3],[5,7]].</li>
5721 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5722 : * array, with elements of the second dimension with index in the range [1,3[.
5723 : * That is [[1,2],[5,6]].</li>
5724 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5725 : * array, with the values in first dimension reversed. That is
5726 : * [[4,5,6,7],[0,1,2,3]].</li>
5727 : * <li>GetView("[newaxis,...]"): returns a
5728 : * 3-dimensional array, with an additional dimension of size 1 put at the
5729 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5730 : * </ul>
5731 : *
5732 : * One difference with NumPy behavior is that ranges that would result in
5733 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5734 : * GDAL multidimensional model).
5735 : *
5736 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5737 : * Multiple field specification is not supported currently.
5738 : *
5739 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5740 : *
5741 : * \note When using the GDAL Python bindings, natural Python syntax can be
5742 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5743 : * ar.GetView("[0,::,1]['foo']")
5744 : * \note When using the C++ API and integer indexing only, you may use the
5745 : * at(idx0, idx1, ...) method.
5746 : *
5747 : * The returned array holds a reference to the original one, and thus is
5748 : * a view of it (not a copy). If the content of the original array changes,
5749 : * the content of the view array too. When using basic slicing and indexing,
5750 : * the view can be written if the underlying array is writable.
5751 : *
5752 : * This is the same as the C function GDALMDArrayGetView()
5753 : *
5754 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5755 : * access.
5756 : * @return a new array, that holds a reference to the original one, and thus is
5757 : * a view of it (not a copy), or nullptr in case of error.
5758 : */
5759 : // clang-format on
5760 :
5761 : std::shared_ptr<GDALMDArray>
5762 619 : GDALMDArray::GetView(const std::string &viewExpr) const
5763 : {
5764 1238 : std::vector<ViewSpec> viewSpecs;
5765 1238 : return GetView(viewExpr, true, viewSpecs);
5766 : }
5767 :
5768 : //! @cond Doxygen_Suppress
5769 : std::shared_ptr<GDALMDArray>
5770 689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5771 : std::vector<ViewSpec> &viewSpecs) const
5772 : {
5773 1378 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5774 689 : if (!self)
5775 : {
5776 1 : CPLError(CE_Failure, CPLE_AppDefined,
5777 : "Driver implementation issue: m_pSelf not set !");
5778 1 : return nullptr;
5779 : }
5780 688 : std::string curExpr(viewExpr);
5781 : while (true)
5782 : {
5783 691 : if (curExpr.empty() || curExpr[0] != '[')
5784 : {
5785 2 : CPLError(CE_Failure, CPLE_AppDefined,
5786 : "Slice string should start with ['");
5787 688 : return nullptr;
5788 : }
5789 :
5790 689 : std::string fieldName;
5791 : size_t endExpr;
5792 689 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5793 : {
5794 87 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5795 : {
5796 2 : CPLError(CE_Failure, CPLE_AppDefined,
5797 : "Field access not allowed on non-compound data type");
5798 2 : return nullptr;
5799 : }
5800 85 : size_t idx = 2;
5801 768 : for (; idx < curExpr.size(); idx++)
5802 : {
5803 767 : const char ch = curExpr[idx];
5804 767 : if (ch == curExpr[1])
5805 84 : break;
5806 683 : if (ch == '\\' && idx + 1 < curExpr.size())
5807 : {
5808 1 : fieldName += curExpr[idx + 1];
5809 1 : idx++;
5810 : }
5811 : else
5812 : {
5813 682 : fieldName += ch;
5814 : }
5815 : }
5816 85 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5817 : {
5818 2 : CPLError(CE_Failure, CPLE_AppDefined,
5819 : "Invalid field access specification");
5820 2 : return nullptr;
5821 : }
5822 83 : endExpr = idx + 1;
5823 : }
5824 : else
5825 : {
5826 602 : endExpr = curExpr.find(']');
5827 : }
5828 685 : if (endExpr == std::string::npos)
5829 : {
5830 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5831 1 : return nullptr;
5832 : }
5833 684 : if (endExpr == 1)
5834 : {
5835 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5836 1 : return nullptr;
5837 : }
5838 683 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5839 :
5840 683 : if (!fieldName.empty())
5841 : {
5842 166 : ViewSpec viewSpec;
5843 83 : viewSpec.m_osFieldName = fieldName;
5844 83 : viewSpecs.emplace_back(std::move(viewSpec));
5845 : }
5846 :
5847 683 : auto newArray = !fieldName.empty()
5848 : ? CreateFieldNameExtractArray(self, fieldName)
5849 : : CreateSlicedArray(self, viewExpr, activeSlice,
5850 683 : bRenameDimensions, viewSpecs);
5851 :
5852 683 : if (endExpr == curExpr.size() - 1)
5853 : {
5854 680 : return newArray;
5855 : }
5856 3 : self = std::move(newArray);
5857 3 : curExpr = curExpr.substr(endExpr + 1);
5858 3 : }
5859 : }
5860 :
5861 : //! @endcond
5862 :
5863 : std::shared_ptr<GDALMDArray>
5864 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5865 : {
5866 19 : std::string osExpr("[");
5867 19 : bool bFirst = true;
5868 45 : for (const auto &idx : indices)
5869 : {
5870 26 : if (!bFirst)
5871 7 : osExpr += ',';
5872 26 : bFirst = false;
5873 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5874 : }
5875 57 : return GetView(osExpr + ']');
5876 : }
5877 :
5878 : /************************************************************************/
5879 : /* operator[] */
5880 : /************************************************************************/
5881 :
5882 : /** Return a view of the array using field access
5883 : *
5884 : * Equivalent of GetView("['fieldName']")
5885 : *
5886 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5887 : */
5888 : std::shared_ptr<GDALMDArray>
5889 2 : GDALMDArray::operator[](const std::string &fieldName) const
5890 : {
5891 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5892 4 : .replaceAll('\\', "\\\\")
5893 4 : .replaceAll('\'', "\\\'")
5894 6 : .c_str()));
5895 : }
5896 :
5897 : /************************************************************************/
5898 : /* GDALMDArrayTransposed */
5899 : /************************************************************************/
5900 :
5901 : class GDALMDArrayTransposed final : public GDALPamMDArray
5902 : {
5903 : private:
5904 : std::shared_ptr<GDALMDArray> m_poParent{};
5905 : std::vector<int> m_anMapNewAxisToOldAxis{};
5906 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5907 :
5908 : mutable std::vector<GUInt64> m_parentStart;
5909 : mutable std::vector<size_t> m_parentCount;
5910 : mutable std::vector<GInt64> m_parentStep;
5911 : mutable std::vector<GPtrDiff_t> m_parentStride;
5912 :
5913 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5914 : const GInt64 *arrayStep,
5915 : const GPtrDiff_t *bufferStride) const;
5916 :
5917 : static std::string
5918 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5919 : {
5920 84 : std::string ret;
5921 84 : ret += '[';
5922 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5923 : {
5924 228 : if (i > 0)
5925 144 : ret += ',';
5926 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5927 : }
5928 84 : ret += ']';
5929 84 : return ret;
5930 : }
5931 :
5932 : protected:
5933 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5934 : const std::vector<int> &anMapNewAxisToOldAxis,
5935 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5936 84 : : GDALAbstractMDArray(std::string(),
5937 84 : "Transposed view of " + poParent->GetFullName() +
5938 84 : " along " +
5939 42 : MappingToStr(anMapNewAxisToOldAxis)),
5940 84 : GDALPamMDArray(std::string(),
5941 84 : "Transposed view of " + poParent->GetFullName() +
5942 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5943 84 : GDALPamMultiDim::GetPAM(poParent),
5944 : poParent->GetContext()),
5945 42 : m_poParent(std::move(poParent)),
5946 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5947 42 : m_dims(std::move(dims)),
5948 42 : m_parentStart(m_poParent->GetDimensionCount()),
5949 42 : m_parentCount(m_poParent->GetDimensionCount()),
5950 42 : m_parentStep(m_poParent->GetDimensionCount()),
5951 336 : m_parentStride(m_poParent->GetDimensionCount())
5952 : {
5953 42 : }
5954 :
5955 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5956 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5957 : const GDALExtendedDataType &bufferDataType,
5958 : void *pDstBuffer) const override;
5959 :
5960 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5961 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5962 : const GDALExtendedDataType &bufferDataType,
5963 : const void *pSrcBuffer) override;
5964 :
5965 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5966 : CSLConstList papszOptions) const override;
5967 :
5968 : public:
5969 : static std::shared_ptr<GDALMDArrayTransposed>
5970 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5971 : const std::vector<int> &anMapNewAxisToOldAxis)
5972 : {
5973 42 : const auto &parentDims(poParent->GetDimensions());
5974 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5975 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5976 : {
5977 114 : if (iOldAxis < 0)
5978 : {
5979 1 : dims.push_back(std::make_shared<GDALDimension>(
5980 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5981 : }
5982 : else
5983 : {
5984 113 : dims.emplace_back(parentDims[iOldAxis]);
5985 : }
5986 : }
5987 :
5988 : auto newAr(
5989 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5990 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5991 42 : newAr->SetSelf(newAr);
5992 84 : return newAr;
5993 : }
5994 :
5995 1 : bool IsWritable() const override
5996 : {
5997 1 : return m_poParent->IsWritable();
5998 : }
5999 :
6000 84 : const std::string &GetFilename() const override
6001 : {
6002 84 : return m_poParent->GetFilename();
6003 : }
6004 :
6005 : const std::vector<std::shared_ptr<GDALDimension>> &
6006 358 : GetDimensions() const override
6007 : {
6008 358 : return m_dims;
6009 : }
6010 :
6011 141 : const GDALExtendedDataType &GetDataType() const override
6012 : {
6013 141 : return m_poParent->GetDataType();
6014 : }
6015 :
6016 4 : const std::string &GetUnit() const override
6017 : {
6018 4 : return m_poParent->GetUnit();
6019 : }
6020 :
6021 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6022 : {
6023 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6024 5 : if (!poSrcSRS)
6025 2 : return nullptr;
6026 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6027 6 : std::vector<int> dstMapping;
6028 9 : for (int srcAxis : srcMapping)
6029 : {
6030 6 : bool bFound = false;
6031 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6032 : {
6033 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6034 : {
6035 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6036 6 : bFound = true;
6037 6 : break;
6038 : }
6039 : }
6040 6 : if (!bFound)
6041 : {
6042 0 : dstMapping.push_back(0);
6043 : }
6044 : }
6045 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6046 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6047 3 : return poClone;
6048 : }
6049 :
6050 4 : const void *GetRawNoDataValue() const override
6051 : {
6052 4 : return m_poParent->GetRawNoDataValue();
6053 : }
6054 :
6055 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6056 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6057 :
6058 4 : double GetOffset(bool *pbHasOffset,
6059 : GDALDataType *peStorageType) const override
6060 : {
6061 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6062 : }
6063 :
6064 4 : double GetScale(bool *pbHasScale,
6065 : GDALDataType *peStorageType) const override
6066 : {
6067 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6068 : }
6069 :
6070 : // bool SetOffset(double dfOffset) override { return
6071 : // m_poParent->SetOffset(dfOffset); }
6072 :
6073 : // bool SetScale(double dfScale) override { return
6074 : // m_poParent->SetScale(dfScale); }
6075 :
6076 3 : std::vector<GUInt64> GetBlockSize() const override
6077 : {
6078 3 : std::vector<GUInt64> ret(GetDimensionCount());
6079 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6080 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6081 : {
6082 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6083 8 : if (iOldAxis >= 0)
6084 : {
6085 7 : ret[i] = parentBlockSize[iOldAxis];
6086 : }
6087 : }
6088 6 : return ret;
6089 : }
6090 :
6091 : std::shared_ptr<GDALAttribute>
6092 1 : GetAttribute(const std::string &osName) const override
6093 : {
6094 1 : return m_poParent->GetAttribute(osName);
6095 : }
6096 :
6097 : std::vector<std::shared_ptr<GDALAttribute>>
6098 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6099 : {
6100 6 : return m_poParent->GetAttributes(papszOptions);
6101 : }
6102 : };
6103 :
6104 : /************************************************************************/
6105 : /* PrepareParentArrays() */
6106 : /************************************************************************/
6107 :
6108 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6109 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6110 : const GPtrDiff_t *bufferStride) const
6111 : {
6112 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6113 : {
6114 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6115 129 : if (iOldAxis >= 0)
6116 : {
6117 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6118 128 : m_parentCount[iOldAxis] = count[i];
6119 128 : if (arrayStep) // only null when called from IAdviseRead()
6120 : {
6121 126 : m_parentStep[iOldAxis] = arrayStep[i];
6122 : }
6123 128 : if (bufferStride) // only null when called from IAdviseRead()
6124 : {
6125 126 : m_parentStride[iOldAxis] = bufferStride[i];
6126 : }
6127 : }
6128 : }
6129 47 : }
6130 :
6131 : /************************************************************************/
6132 : /* IRead() */
6133 : /************************************************************************/
6134 :
6135 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6136 : const size_t *count, const GInt64 *arrayStep,
6137 : const GPtrDiff_t *bufferStride,
6138 : const GDALExtendedDataType &bufferDataType,
6139 : void *pDstBuffer) const
6140 : {
6141 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6142 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6143 44 : m_parentStep.data(), m_parentStride.data(),
6144 44 : bufferDataType, pDstBuffer);
6145 : }
6146 :
6147 : /************************************************************************/
6148 : /* IWrite() */
6149 : /************************************************************************/
6150 :
6151 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6152 : const size_t *count, const GInt64 *arrayStep,
6153 : const GPtrDiff_t *bufferStride,
6154 : const GDALExtendedDataType &bufferDataType,
6155 : const void *pSrcBuffer)
6156 : {
6157 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6158 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6159 2 : m_parentStep.data(), m_parentStride.data(),
6160 2 : bufferDataType, pSrcBuffer);
6161 : }
6162 :
6163 : /************************************************************************/
6164 : /* IAdviseRead() */
6165 : /************************************************************************/
6166 :
6167 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6168 : const size_t *count,
6169 : CSLConstList papszOptions) const
6170 : {
6171 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6172 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6173 1 : papszOptions);
6174 : }
6175 :
6176 : /************************************************************************/
6177 : /* Transpose() */
6178 : /************************************************************************/
6179 :
6180 : /** Return a view of the array whose axis have been reordered.
6181 : *
6182 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6183 : * and GetDimensionCount() - 1, and each only once.
6184 : * -1 can be used as a special index value to ask for the insertion of a new
6185 : * axis of size 1.
6186 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6187 : * index of one of its dimension, it corresponds to the axis of index
6188 : * anMapNewAxisToOldAxis[i] from the current array.
6189 : *
6190 : * This is similar to the numpy.transpose() method
6191 : *
6192 : * The returned array holds a reference to the original one, and thus is
6193 : * a view of it (not a copy). If the content of the original array changes,
6194 : * the content of the view array too. The view can be written if the underlying
6195 : * array is writable.
6196 : *
6197 : * Note that I/O performance in such a transposed view might be poor.
6198 : *
6199 : * This is the same as the C function GDALMDArrayTranspose().
6200 : *
6201 : * @return a new array, that holds a reference to the original one, and thus is
6202 : * a view of it (not a copy), or nullptr in case of error.
6203 : */
6204 : std::shared_ptr<GDALMDArray>
6205 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6206 : {
6207 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6208 50 : if (!self)
6209 : {
6210 0 : CPLError(CE_Failure, CPLE_AppDefined,
6211 : "Driver implementation issue: m_pSelf not set !");
6212 0 : return nullptr;
6213 : }
6214 50 : const int nDims = static_cast<int>(GetDimensionCount());
6215 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6216 50 : int nCountOldAxis = 0;
6217 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6218 : {
6219 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6220 : {
6221 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6222 4 : return nullptr;
6223 : }
6224 130 : if (iOldAxis >= 0)
6225 : {
6226 128 : if (alreadyUsedOldAxis[iOldAxis])
6227 : {
6228 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6229 : iOldAxis);
6230 1 : return nullptr;
6231 : }
6232 127 : alreadyUsedOldAxis[iOldAxis] = true;
6233 127 : nCountOldAxis++;
6234 : }
6235 : }
6236 46 : if (nCountOldAxis != nDims)
6237 : {
6238 4 : CPLError(CE_Failure, CPLE_AppDefined,
6239 : "One or several original axis missing");
6240 4 : return nullptr;
6241 : }
6242 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6243 : }
6244 :
6245 : /************************************************************************/
6246 : /* IRead() */
6247 : /************************************************************************/
6248 :
6249 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6250 : const size_t *count, const GInt64 *arrayStep,
6251 : const GPtrDiff_t *bufferStride,
6252 : const GDALExtendedDataType &bufferDataType,
6253 : void *pDstBuffer) const
6254 : {
6255 16 : const double dfScale = m_dfScale;
6256 16 : const double dfOffset = m_dfOffset;
6257 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6258 : const auto dtDouble =
6259 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6260 16 : const size_t nDTSize = dtDouble.GetSize();
6261 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6262 :
6263 16 : double adfSrcNoData[2] = {0, 0};
6264 16 : if (m_bHasNoData)
6265 : {
6266 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6267 9 : m_poParent->GetDataType(),
6268 : &adfSrcNoData[0], dtDouble);
6269 : }
6270 :
6271 16 : const auto nDims = GetDimensions().size();
6272 16 : if (nDims == 0)
6273 : {
6274 : double adfVal[2];
6275 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6276 : dtDouble, &adfVal[0]))
6277 : {
6278 0 : return false;
6279 : }
6280 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6281 : {
6282 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6283 6 : if (bDTIsComplex)
6284 : {
6285 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6286 : }
6287 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6288 : bufferDataType);
6289 : }
6290 : else
6291 : {
6292 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6293 : pDstBuffer, bufferDataType);
6294 : }
6295 9 : return true;
6296 : }
6297 :
6298 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6299 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6300 7 : void *pTempBuffer = pDstBuffer;
6301 7 : if (bTempBufferNeeded)
6302 : {
6303 2 : size_t nElts = 1;
6304 2 : actualBufferStrideVector.resize(nDims);
6305 7 : for (size_t i = 0; i < nDims; i++)
6306 5 : nElts *= count[i];
6307 2 : actualBufferStrideVector.back() = 1;
6308 5 : for (size_t i = nDims - 1; i > 0;)
6309 : {
6310 3 : --i;
6311 3 : actualBufferStrideVector[i] =
6312 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6313 : }
6314 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6315 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6316 2 : if (!pTempBuffer)
6317 0 : return false;
6318 : }
6319 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6320 : actualBufferStridePtr, dtDouble, pTempBuffer))
6321 : {
6322 0 : if (bTempBufferNeeded)
6323 0 : VSIFree(pTempBuffer);
6324 0 : return false;
6325 : }
6326 :
6327 : struct Stack
6328 : {
6329 : size_t nIters = 0;
6330 : double *src_ptr = nullptr;
6331 : GByte *dst_ptr = nullptr;
6332 : GPtrDiff_t src_inc_offset = 0;
6333 : GPtrDiff_t dst_inc_offset = 0;
6334 : };
6335 :
6336 7 : std::vector<Stack> stack(nDims);
6337 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6338 23 : for (size_t i = 0; i < nDims; i++)
6339 : {
6340 32 : stack[i].src_inc_offset =
6341 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6342 16 : stack[i].dst_inc_offset =
6343 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6344 : }
6345 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6346 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6347 :
6348 7 : size_t dimIdx = 0;
6349 7 : const size_t nDimsMinus1 = nDims - 1;
6350 : GByte abyDstNoData[16];
6351 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6352 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6353 : bufferDataType);
6354 :
6355 37 : lbl_next_depth:
6356 37 : if (dimIdx == nDimsMinus1)
6357 : {
6358 25 : auto nIters = count[dimIdx];
6359 25 : double *padfVal = stack[dimIdx].src_ptr;
6360 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6361 : while (true)
6362 : {
6363 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6364 : {
6365 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6366 88 : if (bDTIsComplex)
6367 : {
6368 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6369 : }
6370 88 : if (bTempBufferNeeded)
6371 : {
6372 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6373 : dst_ptr, bufferDataType);
6374 : }
6375 : }
6376 : else
6377 : {
6378 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6379 : }
6380 :
6381 92 : if ((--nIters) == 0)
6382 25 : break;
6383 67 : padfVal += stack[dimIdx].src_inc_offset;
6384 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6385 : }
6386 : }
6387 : else
6388 : {
6389 12 : stack[dimIdx].nIters = count[dimIdx];
6390 : while (true)
6391 : {
6392 30 : dimIdx++;
6393 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6394 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6395 30 : goto lbl_next_depth;
6396 30 : lbl_return_to_caller:
6397 30 : dimIdx--;
6398 30 : if ((--stack[dimIdx].nIters) == 0)
6399 12 : break;
6400 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6401 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6402 : }
6403 : }
6404 37 : if (dimIdx > 0)
6405 30 : goto lbl_return_to_caller;
6406 :
6407 7 : if (bTempBufferNeeded)
6408 2 : VSIFree(pTempBuffer);
6409 7 : return true;
6410 : }
6411 :
6412 : /************************************************************************/
6413 : /* IWrite() */
6414 : /************************************************************************/
6415 :
6416 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6417 : const size_t *count, const GInt64 *arrayStep,
6418 : const GPtrDiff_t *bufferStride,
6419 : const GDALExtendedDataType &bufferDataType,
6420 : const void *pSrcBuffer)
6421 : {
6422 16 : const double dfScale = m_dfScale;
6423 16 : const double dfOffset = m_dfOffset;
6424 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6425 : const auto dtDouble =
6426 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6427 16 : const size_t nDTSize = dtDouble.GetSize();
6428 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6429 : const bool bSelfAndParentHaveNoData =
6430 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6431 16 : double dfNoData = 0;
6432 16 : if (m_bHasNoData)
6433 : {
6434 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6435 : &dfNoData, GDT_Float64, 0, 1);
6436 : }
6437 :
6438 16 : double adfSrcNoData[2] = {0, 0};
6439 16 : if (bSelfAndParentHaveNoData)
6440 : {
6441 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6442 7 : m_poParent->GetDataType(),
6443 : &adfSrcNoData[0], dtDouble);
6444 : }
6445 :
6446 16 : const auto nDims = GetDimensions().size();
6447 16 : if (nDims == 0)
6448 : {
6449 : double adfVal[2];
6450 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6451 : dtDouble);
6452 16 : if (bSelfAndParentHaveNoData &&
6453 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6454 : {
6455 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6456 2 : bufferStride, m_poParent->GetDataType(),
6457 4 : m_poParent->GetRawNoDataValue());
6458 : }
6459 : else
6460 : {
6461 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6462 8 : if (bDTIsComplex)
6463 : {
6464 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6465 : }
6466 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6467 8 : bufferStride, dtDouble, &adfVal[0]);
6468 : }
6469 : }
6470 :
6471 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6472 6 : size_t nElts = 1;
6473 6 : tmpBufferStrideVector.resize(nDims);
6474 20 : for (size_t i = 0; i < nDims; i++)
6475 14 : nElts *= count[i];
6476 6 : tmpBufferStrideVector.back() = 1;
6477 14 : for (size_t i = nDims - 1; i > 0;)
6478 : {
6479 8 : --i;
6480 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6481 : }
6482 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6483 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6484 6 : if (!pTempBuffer)
6485 0 : return false;
6486 :
6487 : struct Stack
6488 : {
6489 : size_t nIters = 0;
6490 : double *dst_ptr = nullptr;
6491 : const GByte *src_ptr = nullptr;
6492 : GPtrDiff_t src_inc_offset = 0;
6493 : GPtrDiff_t dst_inc_offset = 0;
6494 : };
6495 :
6496 6 : std::vector<Stack> stack(nDims);
6497 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6498 20 : for (size_t i = 0; i < nDims; i++)
6499 : {
6500 28 : stack[i].dst_inc_offset =
6501 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6502 14 : stack[i].src_inc_offset =
6503 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6504 : }
6505 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6506 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6507 :
6508 6 : size_t dimIdx = 0;
6509 6 : const size_t nDimsMinus1 = nDims - 1;
6510 :
6511 34 : lbl_next_depth:
6512 34 : if (dimIdx == nDimsMinus1)
6513 : {
6514 23 : auto nIters = count[dimIdx];
6515 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6516 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6517 : while (true)
6518 : {
6519 : double adfVal[2];
6520 : const double *padfSrcVal;
6521 86 : if (bIsBufferDataTypeNativeDataType)
6522 : {
6523 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6524 : }
6525 : else
6526 : {
6527 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6528 : &adfVal[0], dtDouble);
6529 36 : padfSrcVal = adfVal;
6530 : }
6531 :
6532 148 : if (bSelfAndParentHaveNoData &&
6533 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6534 : {
6535 3 : dst_ptr[0] = adfSrcNoData[0];
6536 3 : if (bDTIsComplex)
6537 : {
6538 1 : dst_ptr[1] = adfSrcNoData[1];
6539 : }
6540 : }
6541 : else
6542 : {
6543 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6544 83 : if (bDTIsComplex)
6545 : {
6546 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6547 : }
6548 : }
6549 :
6550 86 : if ((--nIters) == 0)
6551 23 : break;
6552 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6553 63 : src_ptr += stack[dimIdx].src_inc_offset;
6554 63 : }
6555 : }
6556 : else
6557 : {
6558 11 : stack[dimIdx].nIters = count[dimIdx];
6559 : while (true)
6560 : {
6561 28 : dimIdx++;
6562 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6563 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6564 28 : goto lbl_next_depth;
6565 28 : lbl_return_to_caller:
6566 28 : dimIdx--;
6567 28 : if ((--stack[dimIdx].nIters) == 0)
6568 11 : break;
6569 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6570 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6571 : }
6572 : }
6573 34 : if (dimIdx > 0)
6574 28 : goto lbl_return_to_caller;
6575 :
6576 : // If the parent array is not double/complex-double, then convert the
6577 : // values to it, before calling Write(), as some implementations can be
6578 : // very slow when doing the type conversion.
6579 6 : const auto &eParentDT = m_poParent->GetDataType();
6580 6 : const size_t nParentDTSize = eParentDT.GetSize();
6581 6 : if (nParentDTSize <= nDTSize / 2)
6582 : {
6583 : // Copy in-place by making sure that source and target do not overlap
6584 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6585 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6586 :
6587 : // Copy first element
6588 : {
6589 6 : std::vector<GByte> abyTemp(nParentDTSize);
6590 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6591 6 : static_cast<int>(nDTSize), &abyTemp[0],
6592 : eParentNumericDT, static_cast<int>(nParentDTSize),
6593 : 1);
6594 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6595 : }
6596 : // Remaining elements
6597 86 : for (size_t i = 1; i < nElts; ++i)
6598 : {
6599 80 : GDALCopyWords64(
6600 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6601 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6602 : eParentNumericDT, 0, 1);
6603 : }
6604 : }
6605 :
6606 : const bool ret =
6607 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6608 : eParentDT, pTempBuffer);
6609 :
6610 6 : VSIFree(pTempBuffer);
6611 6 : return ret;
6612 : }
6613 :
6614 : /************************************************************************/
6615 : /* GetUnscaled() */
6616 : /************************************************************************/
6617 :
6618 : /** Return an array that is the unscaled version of the current one.
6619 : *
6620 : * That is each value of the unscaled array will be
6621 : * unscaled_value = raw_value * GetScale() + GetOffset()
6622 : *
6623 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6624 : * from unscaled values to raw values.
6625 : *
6626 : * This is the same as the C function GDALMDArrayGetUnscaled().
6627 : *
6628 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6629 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6630 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6631 : * @return a new array, that holds a reference to the original one, and thus is
6632 : * a view of it (not a copy), or nullptr in case of error.
6633 : */
6634 : std::shared_ptr<GDALMDArray>
6635 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6636 : double dfOverriddenDstNodata) const
6637 : {
6638 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6639 17 : if (!self)
6640 : {
6641 0 : CPLError(CE_Failure, CPLE_AppDefined,
6642 : "Driver implementation issue: m_pSelf not set !");
6643 0 : return nullptr;
6644 : }
6645 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6646 : {
6647 0 : CPLError(CE_Failure, CPLE_AppDefined,
6648 : "GetUnscaled() only supports numeric data type");
6649 0 : return nullptr;
6650 : }
6651 : const double dfScale =
6652 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6653 : const double dfOffset =
6654 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6655 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6656 4 : return self;
6657 :
6658 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6659 13 : ? GDT_CFloat64
6660 13 : : GDT_Float64;
6661 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6662 : {
6663 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6664 0 : eDT = GDT_Float16;
6665 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6666 1 : eDT = GDT_Float32;
6667 : }
6668 :
6669 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6670 13 : dfOverriddenDstNodata, eDT);
6671 : }
6672 :
6673 : /************************************************************************/
6674 : /* GDALMDArrayMask */
6675 : /************************************************************************/
6676 :
6677 : class GDALMDArrayMask final : public GDALPamMDArray
6678 : {
6679 : private:
6680 : std::shared_ptr<GDALMDArray> m_poParent{};
6681 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6682 : double m_dfMissingValue = 0.0;
6683 : bool m_bHasMissingValue = false;
6684 : double m_dfFillValue = 0.0;
6685 : bool m_bHasFillValue = false;
6686 : double m_dfValidMin = 0.0;
6687 : bool m_bHasValidMin = false;
6688 : double m_dfValidMax = 0.0;
6689 : bool m_bHasValidMax = false;
6690 : std::vector<uint32_t> m_anValidFlagMasks{};
6691 : std::vector<uint32_t> m_anValidFlagValues{};
6692 :
6693 : bool Init(CSLConstList papszOptions);
6694 :
6695 : template <typename Type>
6696 : void
6697 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6698 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6699 : const void *pTempBuffer,
6700 : const GDALExtendedDataType &oTmpBufferDT,
6701 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6702 :
6703 : protected:
6704 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6705 96 : : GDALAbstractMDArray(std::string(),
6706 96 : "Mask of " + poParent->GetFullName()),
6707 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6708 96 : GDALPamMultiDim::GetPAM(poParent),
6709 : poParent->GetContext()),
6710 240 : m_poParent(std::move(poParent))
6711 : {
6712 48 : }
6713 :
6714 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6715 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6716 : const GDALExtendedDataType &bufferDataType,
6717 : void *pDstBuffer) const override;
6718 :
6719 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6720 : CSLConstList papszOptions) const override
6721 : {
6722 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6723 : }
6724 :
6725 : public:
6726 : static std::shared_ptr<GDALMDArrayMask>
6727 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6728 : CSLConstList papszOptions);
6729 :
6730 1 : bool IsWritable() const override
6731 : {
6732 1 : return false;
6733 : }
6734 :
6735 54 : const std::string &GetFilename() const override
6736 : {
6737 54 : return m_poParent->GetFilename();
6738 : }
6739 :
6740 : const std::vector<std::shared_ptr<GDALDimension>> &
6741 382 : GetDimensions() const override
6742 : {
6743 382 : return m_poParent->GetDimensions();
6744 : }
6745 :
6746 138 : const GDALExtendedDataType &GetDataType() const override
6747 : {
6748 138 : return m_dt;
6749 : }
6750 :
6751 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6752 : {
6753 1 : return m_poParent->GetSpatialRef();
6754 : }
6755 :
6756 2 : std::vector<GUInt64> GetBlockSize() const override
6757 : {
6758 2 : return m_poParent->GetBlockSize();
6759 : }
6760 : };
6761 :
6762 : /************************************************************************/
6763 : /* GDALMDArrayMask::Create() */
6764 : /************************************************************************/
6765 :
6766 : /* static */ std::shared_ptr<GDALMDArrayMask>
6767 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6768 : CSLConstList papszOptions)
6769 : {
6770 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6771 48 : newAr->SetSelf(newAr);
6772 48 : if (!newAr->Init(papszOptions))
6773 6 : return nullptr;
6774 42 : return newAr;
6775 : }
6776 :
6777 : /************************************************************************/
6778 : /* GDALMDArrayMask::Init() */
6779 : /************************************************************************/
6780 :
6781 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6782 : {
6783 : const auto GetSingleValNumericAttr =
6784 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6785 : {
6786 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6787 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6788 : {
6789 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6790 21 : if (anDimSizes.empty() ||
6791 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6792 : {
6793 11 : bHasVal = true;
6794 11 : dfVal = poAttr->ReadAsDouble();
6795 : }
6796 : }
6797 192 : };
6798 :
6799 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6800 48 : m_dfMissingValue);
6801 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6802 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6803 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6804 :
6805 : {
6806 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6807 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6808 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6809 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6810 : {
6811 6 : m_bHasValidMin = true;
6812 6 : m_bHasValidMax = true;
6813 6 : auto vals = poValidRange->ReadAsDoubleArray();
6814 6 : CPLAssert(vals.size() == 2);
6815 6 : m_dfValidMin = vals[0];
6816 6 : m_dfValidMax = vals[1];
6817 : }
6818 : }
6819 :
6820 : // Take into account
6821 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6822 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6823 : const char *pszUnmaskFlags =
6824 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6825 48 : if (pszUnmaskFlags)
6826 : {
6827 : const auto IsScalarStringAttr =
6828 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6829 : {
6830 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6831 26 : (poAttr->GetDimensionsSize().empty() ||
6832 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6833 26 : poAttr->GetDimensionsSize()[0] == 1));
6834 : };
6835 :
6836 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6837 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6838 : {
6839 1 : CPLError(CE_Failure, CPLE_AppDefined,
6840 : "UNMASK_FLAGS option specified but array has no "
6841 : "flag_meanings attribute");
6842 1 : return false;
6843 : }
6844 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6845 13 : if (!pszFlagMeanings)
6846 : {
6847 1 : CPLError(CE_Failure, CPLE_AppDefined,
6848 : "Cannot read flag_meanings attribute");
6849 1 : return false;
6850 : }
6851 :
6852 : const auto IsSingleDimNumericAttr =
6853 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6854 : {
6855 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6856 26 : poAttr->GetDimensionsSize().size() == 1;
6857 : };
6858 :
6859 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6860 : const bool bHasFlagValues =
6861 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6862 :
6863 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6864 : const bool bHasFlagMasks =
6865 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6866 :
6867 12 : if (!bHasFlagValues && !bHasFlagMasks)
6868 : {
6869 1 : CPLError(CE_Failure, CPLE_AppDefined,
6870 : "Cannot find flag_values and/or flag_masks attribute");
6871 1 : return false;
6872 : }
6873 :
6874 : const CPLStringList aosUnmaskFlags(
6875 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6876 : const CPLStringList aosFlagMeanings(
6877 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6878 :
6879 11 : if (bHasFlagValues)
6880 : {
6881 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6882 : // We could support Int64 or UInt64, but more work...
6883 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6884 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6885 : {
6886 0 : CPLError(CE_Failure, CPLE_NotSupported,
6887 : "Unsupported data type for flag_values attribute: %s",
6888 : GDALGetDataTypeName(eType));
6889 0 : return false;
6890 : }
6891 : }
6892 :
6893 11 : if (bHasFlagMasks)
6894 : {
6895 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6896 : // We could support Int64 or UInt64, but more work...
6897 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6898 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6899 : {
6900 0 : CPLError(CE_Failure, CPLE_NotSupported,
6901 : "Unsupported data type for flag_masks attribute: %s",
6902 : GDALGetDataTypeName(eType));
6903 0 : return false;
6904 : }
6905 : }
6906 :
6907 : const std::vector<double> adfValues(
6908 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6909 11 : : std::vector<double>());
6910 : const std::vector<double> adfMasks(
6911 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6912 11 : : std::vector<double>());
6913 :
6914 18 : if (bHasFlagValues &&
6915 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6916 : {
6917 1 : CPLError(CE_Failure, CPLE_AppDefined,
6918 : "Number of values in flag_values attribute is different "
6919 : "from the one in flag_meanings");
6920 1 : return false;
6921 : }
6922 :
6923 16 : if (bHasFlagMasks &&
6924 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6925 : {
6926 1 : CPLError(CE_Failure, CPLE_AppDefined,
6927 : "Number of values in flag_masks attribute is different "
6928 : "from the one in flag_meanings");
6929 1 : return false;
6930 : }
6931 :
6932 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6933 : {
6934 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6935 11 : if (nIdxFlag < 0)
6936 : {
6937 1 : CPLError(
6938 : CE_Failure, CPLE_AppDefined,
6939 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6940 : aosUnmaskFlags[i], pszFlagMeanings);
6941 1 : return false;
6942 : }
6943 :
6944 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6945 : {
6946 0 : CPLError(CE_Failure, CPLE_AppDefined,
6947 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6948 0 : adfValues[nIdxFlag]);
6949 0 : return false;
6950 : }
6951 :
6952 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6953 : {
6954 0 : CPLError(CE_Failure, CPLE_AppDefined,
6955 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6956 0 : adfMasks[nIdxFlag]);
6957 0 : return false;
6958 : }
6959 :
6960 10 : if (bHasFlagValues)
6961 : {
6962 12 : m_anValidFlagValues.push_back(
6963 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6964 : }
6965 :
6966 10 : if (bHasFlagMasks)
6967 : {
6968 12 : m_anValidFlagMasks.push_back(
6969 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6970 : }
6971 : }
6972 : }
6973 :
6974 42 : return true;
6975 : }
6976 :
6977 : /************************************************************************/
6978 : /* IRead() */
6979 : /************************************************************************/
6980 :
6981 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6982 : const GInt64 *arrayStep,
6983 : const GPtrDiff_t *bufferStride,
6984 : const GDALExtendedDataType &bufferDataType,
6985 : void *pDstBuffer) const
6986 : {
6987 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
6988 : {
6989 0 : CPLError(CE_Failure, CPLE_AppDefined,
6990 : "%s: only reading to a numeric data type is supported",
6991 : __func__);
6992 0 : return false;
6993 : }
6994 51 : size_t nElts = 1;
6995 51 : const size_t nDims = GetDimensionCount();
6996 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6997 139 : for (size_t i = 0; i < nDims; i++)
6998 88 : nElts *= count[i];
6999 51 : if (nDims > 0)
7000 : {
7001 46 : tmpBufferStrideVector.back() = 1;
7002 88 : for (size_t i = nDims - 1; i > 0;)
7003 : {
7004 42 : --i;
7005 42 : tmpBufferStrideVector[i] =
7006 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7007 : }
7008 : }
7009 :
7010 : /* Optimized case: if we are an integer data type and that there is no */
7011 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7012 : /* directly */
7013 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7014 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7015 34 : m_anValidFlagMasks.empty() &&
7016 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7017 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7018 : {
7019 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7020 7 : if (bBufferDataTypeIsByte) // Byte case
7021 : {
7022 4 : bool bContiguous = true;
7023 10 : for (size_t i = 0; i < nDims; i++)
7024 : {
7025 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7026 : {
7027 1 : bContiguous = false;
7028 1 : break;
7029 : }
7030 : }
7031 4 : if (bContiguous)
7032 : {
7033 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7034 3 : memset(pDstBuffer, 1, nElts);
7035 3 : return true;
7036 : }
7037 : }
7038 :
7039 : struct Stack
7040 : {
7041 : size_t nIters = 0;
7042 : GByte *dst_ptr = nullptr;
7043 : GPtrDiff_t dst_inc_offset = 0;
7044 : };
7045 :
7046 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7047 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7048 13 : for (size_t i = 0; i < nDims; i++)
7049 : {
7050 9 : stack[i].dst_inc_offset =
7051 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7052 : }
7053 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7054 :
7055 4 : size_t dimIdx = 0;
7056 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7057 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7058 4 : CPLAssert(nBufferDTSize <= 16);
7059 4 : const GByte flag = 1;
7060 4 : GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7061 : bufferDataType.GetNumericDataType(), 0, 1);
7062 :
7063 28 : lbl_next_depth:
7064 28 : if (dimIdx == nDimsMinus1)
7065 : {
7066 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7067 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7068 :
7069 : while (true)
7070 : {
7071 : // cppcheck-suppress knownConditionTrueFalse
7072 73 : if (bBufferDataTypeIsByte)
7073 : {
7074 24 : *dst_ptr = flag;
7075 : }
7076 : else
7077 : {
7078 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7079 : }
7080 :
7081 73 : if ((--nIters) == 0)
7082 19 : break;
7083 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7084 : }
7085 : }
7086 : else
7087 : {
7088 9 : stack[dimIdx].nIters = count[dimIdx];
7089 : while (true)
7090 : {
7091 24 : dimIdx++;
7092 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7093 24 : goto lbl_next_depth;
7094 24 : lbl_return_to_caller:
7095 24 : dimIdx--;
7096 24 : if ((--stack[dimIdx].nIters) == 0)
7097 9 : break;
7098 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7099 : }
7100 : }
7101 28 : if (dimIdx > 0)
7102 24 : goto lbl_return_to_caller;
7103 :
7104 4 : return true;
7105 : }
7106 :
7107 : const auto oTmpBufferDT =
7108 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7109 : ? GDALExtendedDataType::Create(GDT_Float64)
7110 88 : : m_poParent->GetDataType();
7111 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7112 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7113 44 : if (!pTempBuffer)
7114 0 : return false;
7115 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7116 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7117 : pTempBuffer))
7118 : {
7119 0 : VSIFree(pTempBuffer);
7120 0 : return false;
7121 : }
7122 :
7123 44 : switch (oTmpBufferDT.GetNumericDataType())
7124 : {
7125 7 : case GDT_Byte:
7126 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7127 : pTempBuffer, oTmpBufferDT,
7128 : tmpBufferStrideVector);
7129 7 : break;
7130 :
7131 0 : case GDT_Int8:
7132 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7133 : pTempBuffer, oTmpBufferDT,
7134 : tmpBufferStrideVector);
7135 0 : break;
7136 :
7137 1 : case GDT_UInt16:
7138 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7139 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7140 : tmpBufferStrideVector);
7141 1 : break;
7142 :
7143 14 : case GDT_Int16:
7144 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7145 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7146 : tmpBufferStrideVector);
7147 14 : break;
7148 :
7149 1 : case GDT_UInt32:
7150 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7151 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7152 : tmpBufferStrideVector);
7153 1 : break;
7154 :
7155 5 : case GDT_Int32:
7156 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7157 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7158 : tmpBufferStrideVector);
7159 5 : break;
7160 :
7161 0 : case GDT_UInt64:
7162 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7163 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7164 : tmpBufferStrideVector);
7165 0 : break;
7166 :
7167 0 : case GDT_Int64:
7168 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7169 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7170 : tmpBufferStrideVector);
7171 0 : break;
7172 :
7173 0 : case GDT_Float16:
7174 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7175 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7176 : tmpBufferStrideVector);
7177 0 : break;
7178 :
7179 7 : case GDT_Float32:
7180 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7181 : pTempBuffer, oTmpBufferDT,
7182 : tmpBufferStrideVector);
7183 7 : break;
7184 :
7185 9 : case GDT_Float64:
7186 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7187 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7188 : tmpBufferStrideVector);
7189 9 : break;
7190 0 : case GDT_Unknown:
7191 : case GDT_CInt16:
7192 : case GDT_CInt32:
7193 : case GDT_CFloat16:
7194 : case GDT_CFloat32:
7195 : case GDT_CFloat64:
7196 : case GDT_TypeCount:
7197 0 : CPLAssert(false);
7198 : break;
7199 : }
7200 :
7201 44 : VSIFree(pTempBuffer);
7202 :
7203 44 : return true;
7204 : }
7205 :
7206 : /************************************************************************/
7207 : /* IsValidForDT() */
7208 : /************************************************************************/
7209 :
7210 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7211 : {
7212 40 : if (std::isnan(dfVal))
7213 0 : return false;
7214 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7215 0 : return false;
7216 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7217 0 : return false;
7218 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7219 : }
7220 :
7221 9 : template <> bool IsValidForDT<double>(double)
7222 : {
7223 9 : return true;
7224 : }
7225 :
7226 : /************************************************************************/
7227 : /* IsNan() */
7228 : /************************************************************************/
7229 :
7230 1438 : template <typename Type> inline bool IsNan(Type)
7231 : {
7232 1438 : return false;
7233 : }
7234 :
7235 65 : template <> bool IsNan<double>(double val)
7236 : {
7237 65 : return std::isnan(val);
7238 : }
7239 :
7240 26 : template <> bool IsNan<float>(float val)
7241 : {
7242 26 : return std::isnan(val);
7243 : }
7244 :
7245 : /************************************************************************/
7246 : /* ReadInternal() */
7247 : /************************************************************************/
7248 :
7249 : template <typename Type>
7250 44 : void GDALMDArrayMask::ReadInternal(
7251 : const size_t *count, const GPtrDiff_t *bufferStride,
7252 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7253 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7254 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7255 : {
7256 44 : const size_t nDims = GetDimensionCount();
7257 :
7258 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7259 : {
7260 220 : if (bHasVal)
7261 : {
7262 49 : if (IsValidForDT<Type>(dfVal))
7263 : {
7264 49 : return static_cast<Type>(dfVal);
7265 : }
7266 : else
7267 : {
7268 0 : bHasVal = false;
7269 : }
7270 : }
7271 171 : return 0;
7272 : };
7273 :
7274 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7275 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7276 : const Type nNoDataValue =
7277 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7278 44 : bool bHasMissingValue = m_bHasMissingValue;
7279 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7280 44 : bool bHasFillValue = m_bHasFillValue;
7281 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7282 44 : bool bHasValidMin = m_bHasValidMin;
7283 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7284 44 : bool bHasValidMax = m_bHasValidMax;
7285 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7286 44 : const bool bHasValidFlags =
7287 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7288 :
7289 351 : const auto IsValidFlag = [this](Type v)
7290 : {
7291 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7292 : {
7293 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7294 : {
7295 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7296 : m_anValidFlagValues[i])
7297 : {
7298 4 : return true;
7299 : }
7300 : }
7301 : }
7302 42 : else if (!m_anValidFlagValues.empty())
7303 : {
7304 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7305 : {
7306 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7307 : {
7308 4 : return true;
7309 : }
7310 : }
7311 : }
7312 : else /* if( !m_anValidFlagMasks.empty() ) */
7313 : {
7314 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7315 : {
7316 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7317 : {
7318 9 : return true;
7319 : }
7320 : }
7321 : }
7322 37 : return false;
7323 : };
7324 :
7325 : #define GET_MASK_FOR_SAMPLE(v) \
7326 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7327 : !(bHasMissingValue && v == nMissingValue) && \
7328 : !(bHasFillValue && v == nFillValue) && \
7329 : !(bHasValidMin && v < nValidMin) && \
7330 : !(bHasValidMax && v > nValidMax) && \
7331 : (!bHasValidFlags || IsValidFlag(v)));
7332 :
7333 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7334 : /* Optimized case: Byte output and output buffer is contiguous */
7335 44 : if (bBufferDataTypeIsByte)
7336 : {
7337 40 : bool bContiguous = true;
7338 103 : for (size_t i = 0; i < nDims; i++)
7339 : {
7340 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7341 : {
7342 1 : bContiguous = false;
7343 1 : break;
7344 : }
7345 : }
7346 40 : if (bContiguous)
7347 : {
7348 39 : size_t nElts = 1;
7349 102 : for (size_t i = 0; i < nDims; i++)
7350 63 : nElts *= count[i];
7351 :
7352 1113 : for (size_t i = 0; i < nElts; i++)
7353 : {
7354 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7355 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7356 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7357 : }
7358 39 : return;
7359 : }
7360 : }
7361 :
7362 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7363 :
7364 : struct Stack
7365 : {
7366 : size_t nIters = 0;
7367 : const GByte *src_ptr = nullptr;
7368 : GByte *dst_ptr = nullptr;
7369 : GPtrDiff_t src_inc_offset = 0;
7370 : GPtrDiff_t dst_inc_offset = 0;
7371 : };
7372 :
7373 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7374 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7375 15 : for (size_t i = 0; i < nDims; i++)
7376 : {
7377 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7378 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7379 10 : stack[i].dst_inc_offset =
7380 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7381 : }
7382 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7383 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7384 :
7385 5 : size_t dimIdx = 0;
7386 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7387 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7388 5 : CPLAssert(nBufferDTSize <= 16);
7389 15 : for (GByte flag = 0; flag <= 1; flag++)
7390 : {
7391 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7392 : bufferDataType.GetNumericDataType(), 0, 1);
7393 : }
7394 :
7395 43 : lbl_next_depth:
7396 43 : if (dimIdx == nDimsMinus1)
7397 : {
7398 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7399 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7400 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7401 :
7402 420 : while (true)
7403 : {
7404 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7405 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7406 :
7407 455 : if (bBufferDataTypeIsByte)
7408 : {
7409 24 : *dst_ptr = flag;
7410 : }
7411 : else
7412 : {
7413 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7414 : }
7415 :
7416 455 : if ((--nIters) == 0)
7417 35 : break;
7418 420 : src_ptr += stack[dimIdx].src_inc_offset;
7419 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7420 : }
7421 : }
7422 : else
7423 : {
7424 8 : stack[dimIdx].nIters = count[dimIdx];
7425 : while (true)
7426 : {
7427 38 : dimIdx++;
7428 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7429 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7430 38 : goto lbl_next_depth;
7431 38 : lbl_return_to_caller:
7432 38 : dimIdx--;
7433 38 : if ((--stack[dimIdx].nIters) == 0)
7434 8 : break;
7435 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7436 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7437 : }
7438 : }
7439 43 : if (dimIdx > 0)
7440 38 : goto lbl_return_to_caller;
7441 : }
7442 :
7443 : /************************************************************************/
7444 : /* GetMask() */
7445 : /************************************************************************/
7446 :
7447 : /** Return an array that is a mask for the current array
7448 :
7449 : This array will be of type Byte, with values set to 0 to indicate invalid
7450 : pixels of the current array, and values set to 1 to indicate valid pixels.
7451 :
7452 : The generic implementation honours the NoDataValue, as well as various
7453 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7454 : and valid_range.
7455 :
7456 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7457 : can be used to specify strings of the "flag_meanings" attribute
7458 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7459 : for which pixels matching any of those flags will be set at 1 in the mask array,
7460 : and pixels matching none of those flags will be set at 0.
7461 : For example, let's consider the following netCDF variable defined with:
7462 : \verbatim
7463 : l2p_flags:valid_min = 0s ;
7464 : l2p_flags:valid_max = 256s ;
7465 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7466 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7467 : \endverbatim
7468 :
7469 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7470 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7471 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7472 : will be 1.
7473 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7474 : will be 0.
7475 :
7476 : This is the same as the C function GDALMDArrayGetMask().
7477 :
7478 : @param papszOptions NULL-terminated list of options, or NULL.
7479 :
7480 : @return a new array, that holds a reference to the original one, and thus is
7481 : a view of it (not a copy), or nullptr in case of error.
7482 : */
7483 : std::shared_ptr<GDALMDArray>
7484 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7485 : {
7486 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7487 49 : if (!self)
7488 : {
7489 0 : CPLError(CE_Failure, CPLE_AppDefined,
7490 : "Driver implementation issue: m_pSelf not set !");
7491 0 : return nullptr;
7492 : }
7493 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7494 : {
7495 1 : CPLError(CE_Failure, CPLE_AppDefined,
7496 : "GetMask() only supports numeric data type");
7497 1 : return nullptr;
7498 : }
7499 48 : return GDALMDArrayMask::Create(self, papszOptions);
7500 : }
7501 :
7502 : /************************************************************************/
7503 : /* IsRegularlySpaced() */
7504 : /************************************************************************/
7505 :
7506 : /** Returns whether an array is a 1D regularly spaced array.
7507 : *
7508 : * @param[out] dfStart First value in the array
7509 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7510 : * @return true if the array is regularly spaced.
7511 : */
7512 189 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7513 : {
7514 189 : dfStart = 0;
7515 189 : dfIncrement = 0;
7516 189 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7517 0 : return false;
7518 189 : const auto nSize = GetDimensions()[0]->GetSize();
7519 189 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7520 2 : return false;
7521 :
7522 187 : size_t nCount = static_cast<size_t>(nSize);
7523 374 : std::vector<double> adfTmp;
7524 : try
7525 : {
7526 187 : adfTmp.resize(nCount);
7527 : }
7528 0 : catch (const std::exception &)
7529 : {
7530 0 : return false;
7531 : }
7532 :
7533 187 : GUInt64 anStart[1] = {0};
7534 187 : size_t anCount[1] = {nCount};
7535 :
7536 : const auto IsRegularlySpacedInternal =
7537 83950 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7538 : {
7539 261 : dfStart = adfTmp[0];
7540 261 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7541 261 : if (dfIncrement == 0)
7542 : {
7543 3 : return false;
7544 : }
7545 20920 : for (size_t i = 1; i < anCount[0]; i++)
7546 : {
7547 20662 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7548 20662 : 1e-3 * fabs(dfIncrement))
7549 : {
7550 0 : return false;
7551 : }
7552 : }
7553 258 : return true;
7554 187 : };
7555 :
7556 : // First try with the first block(s). This can avoid excessive processing
7557 : // time, for example with Zarr datasets.
7558 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7559 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7560 187 : const auto nBlockSize = GetBlockSize()[0];
7561 187 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7562 : {
7563 : size_t nReducedCount =
7564 77 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7565 440 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7566 363 : nReducedCount *= 2;
7567 77 : anCount[0] = nReducedCount;
7568 77 : if (!Read(anStart, anCount, nullptr, nullptr,
7569 154 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7570 : {
7571 0 : return false;
7572 : }
7573 77 : if (!IsRegularlySpacedInternal())
7574 : {
7575 3 : return false;
7576 : }
7577 :
7578 : // Get next values
7579 74 : anStart[0] = nReducedCount;
7580 74 : anCount[0] = nCount - nReducedCount;
7581 : }
7582 :
7583 184 : if (!Read(anStart, anCount, nullptr, nullptr,
7584 368 : GDALExtendedDataType::Create(GDT_Float64),
7585 184 : &adfTmp[static_cast<size_t>(anStart[0])]))
7586 : {
7587 0 : return false;
7588 : }
7589 :
7590 184 : return IsRegularlySpacedInternal();
7591 : }
7592 :
7593 : /************************************************************************/
7594 : /* GuessGeoTransform() */
7595 : /************************************************************************/
7596 :
7597 : /** Returns whether 2 specified dimensions form a geotransform
7598 : *
7599 : * @param nDimX Index of the X axis.
7600 : * @param nDimY Index of the Y axis.
7601 : * @param bPixelIsPoint Whether the geotransform should be returned
7602 : * with the pixel-is-point (pixel-center) convention
7603 : * (bPixelIsPoint = true), or with the pixel-is-area
7604 : * (top left corner convention)
7605 : * (bPixelIsPoint = false)
7606 : * @param[out] gt Computed geotransform
7607 : * @return true if a geotransform could be computed.
7608 : */
7609 229 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7610 : bool bPixelIsPoint,
7611 : GDALGeoTransform >) const
7612 : {
7613 229 : const auto &dims(GetDimensions());
7614 458 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7615 458 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7616 229 : double dfXStart = 0.0;
7617 229 : double dfXSpacing = 0.0;
7618 229 : double dfYStart = 0.0;
7619 229 : double dfYSpacing = 0.0;
7620 517 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7621 288 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7622 334 : poVarY && poVarY->GetDimensionCount() == 1 &&
7623 95 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7624 463 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7625 90 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7626 : {
7627 90 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7628 90 : gt[1] = dfXSpacing;
7629 90 : gt[2] = 0;
7630 90 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7631 90 : gt[4] = 0;
7632 90 : gt[5] = dfYSpacing;
7633 90 : return true;
7634 : }
7635 139 : return false;
7636 : }
7637 :
7638 : /** Returns whether 2 specified dimensions form a geotransform
7639 : *
7640 : * @param nDimX Index of the X axis.
7641 : * @param nDimY Index of the Y axis.
7642 : * @param bPixelIsPoint Whether the geotransform should be returned
7643 : * with the pixel-is-point (pixel-center) convention
7644 : * (bPixelIsPoint = true), or with the pixel-is-area
7645 : * (top left corner convention)
7646 : * (bPixelIsPoint = false)
7647 : * @param[out] adfGeoTransform Computed geotransform
7648 : * @return true if a geotransform could be computed.
7649 : */
7650 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7651 : bool bPixelIsPoint,
7652 : double adfGeoTransform[6]) const
7653 : {
7654 0 : GDALGeoTransform *gt =
7655 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7656 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7657 : }
7658 :
7659 : /************************************************************************/
7660 : /* GDALMDArrayResampled */
7661 : /************************************************************************/
7662 :
7663 : class GDALMDArrayResampledDataset;
7664 :
7665 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7666 : {
7667 : protected:
7668 : CPLErr IReadBlock(int, int, void *) override;
7669 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7670 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7671 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7672 : GSpacing nLineSpaceBuf,
7673 : GDALRasterIOExtraArg *psExtraArg) override;
7674 :
7675 : public:
7676 : explicit GDALMDArrayResampledDatasetRasterBand(
7677 : GDALMDArrayResampledDataset *poDSIn);
7678 :
7679 : double GetNoDataValue(int *pbHasNoData) override;
7680 : };
7681 :
7682 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7683 : {
7684 : friend class GDALMDArrayResampled;
7685 : friend class GDALMDArrayResampledDatasetRasterBand;
7686 :
7687 : std::shared_ptr<GDALMDArray> m_poArray;
7688 : const size_t m_iXDim;
7689 : const size_t m_iYDim;
7690 : GDALGeoTransform m_gt{};
7691 : bool m_bHasGT = false;
7692 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7693 :
7694 : std::vector<GUInt64> m_anOffset{};
7695 : std::vector<size_t> m_anCount{};
7696 : std::vector<GPtrDiff_t> m_anStride{};
7697 :
7698 : std::string m_osFilenameLong{};
7699 : std::string m_osFilenameLat{};
7700 :
7701 : public:
7702 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7703 : size_t iXDim, size_t iYDim)
7704 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7705 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7706 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7707 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7708 : {
7709 24 : const auto &dims(m_poArray->GetDimensions());
7710 :
7711 24 : nRasterYSize = static_cast<int>(
7712 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7713 24 : nRasterXSize = static_cast<int>(
7714 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7715 :
7716 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7717 :
7718 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7719 24 : }
7720 :
7721 : ~GDALMDArrayResampledDataset() override;
7722 :
7723 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7724 : {
7725 43 : gt = m_gt;
7726 43 : return m_bHasGT ? CE_None : CE_Failure;
7727 : }
7728 :
7729 105 : const OGRSpatialReference *GetSpatialRef() const override
7730 : {
7731 105 : m_poSRS = m_poArray->GetSpatialRef();
7732 105 : if (m_poSRS)
7733 : {
7734 79 : m_poSRS.reset(m_poSRS->Clone());
7735 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7736 237 : for (auto &m : axisMapping)
7737 : {
7738 158 : if (m == static_cast<int>(m_iXDim) + 1)
7739 79 : m = 1;
7740 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7741 79 : m = 2;
7742 : }
7743 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7744 : }
7745 105 : return m_poSRS.get();
7746 : }
7747 :
7748 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7749 : const std::string &osFilenameLat)
7750 : {
7751 5 : m_osFilenameLong = osFilenameLong;
7752 5 : m_osFilenameLat = osFilenameLat;
7753 10 : CPLStringList aosGeoLoc;
7754 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7755 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7756 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7757 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7758 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7759 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7760 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7761 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7762 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7763 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7764 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7765 5 : }
7766 : };
7767 :
7768 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7769 : {
7770 24 : if (!m_osFilenameLong.empty())
7771 5 : VSIUnlink(m_osFilenameLong.c_str());
7772 24 : if (!m_osFilenameLat.empty())
7773 5 : VSIUnlink(m_osFilenameLat.c_str());
7774 48 : }
7775 :
7776 : /************************************************************************/
7777 : /* GDALMDArrayResampledDatasetRasterBand() */
7778 : /************************************************************************/
7779 :
7780 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7781 24 : GDALMDArrayResampledDataset *poDSIn)
7782 : {
7783 24 : const auto &poArray(poDSIn->m_poArray);
7784 24 : const auto blockSize(poArray->GetBlockSize());
7785 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7786 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7787 13 : blockSize[poDSIn->m_iYDim]))
7788 24 : : 1;
7789 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7790 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7791 13 : blockSize[poDSIn->m_iXDim]))
7792 24 : : poDSIn->GetRasterXSize();
7793 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7794 24 : eAccess = poDSIn->eAccess;
7795 24 : }
7796 :
7797 : /************************************************************************/
7798 : /* GetNoDataValue() */
7799 : /************************************************************************/
7800 :
7801 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7802 : {
7803 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7804 50 : const auto &poArray(l_poDS->m_poArray);
7805 50 : bool bHasNodata = false;
7806 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7807 50 : if (pbHasNoData)
7808 46 : *pbHasNoData = bHasNodata;
7809 50 : return dfRes;
7810 : }
7811 :
7812 : /************************************************************************/
7813 : /* IReadBlock() */
7814 : /************************************************************************/
7815 :
7816 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7817 : int nBlockYOff,
7818 : void *pImage)
7819 : {
7820 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7821 0 : const int nXOff = nBlockXOff * nBlockXSize;
7822 0 : const int nYOff = nBlockYOff * nBlockYSize;
7823 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7824 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7825 : GDALRasterIOExtraArg sExtraArg;
7826 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7827 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7828 : nReqXSize, nReqYSize, eDataType, nDTSize,
7829 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7830 : }
7831 :
7832 : /************************************************************************/
7833 : /* IRasterIO() */
7834 : /************************************************************************/
7835 :
7836 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7837 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7838 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7839 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7840 : GDALRasterIOExtraArg *psExtraArg)
7841 : {
7842 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7843 32 : const auto &poArray(l_poDS->m_poArray);
7844 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7845 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7846 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7847 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7848 : {
7849 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7850 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7851 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7852 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7853 :
7854 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7855 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7856 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7857 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7858 :
7859 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7860 32 : l_poDS->m_anCount.data(), nullptr,
7861 32 : l_poDS->m_anStride.data(),
7862 64 : GDALExtendedDataType::Create(eBufType), pData)
7863 32 : ? CE_None
7864 32 : : CE_Failure;
7865 : }
7866 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7867 : pData, nBufXSize, nBufYSize, eBufType,
7868 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7869 : }
7870 :
7871 : class GDALMDArrayResampled final : public GDALPamMDArray
7872 : {
7873 : private:
7874 : std::shared_ptr<GDALMDArray> m_poParent{};
7875 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7876 : std::vector<GUInt64> m_anBlockSize;
7877 : GDALExtendedDataType m_dt;
7878 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7879 : std::shared_ptr<GDALMDArray> m_poVarX{};
7880 : std::shared_ptr<GDALMDArray> m_poVarY{};
7881 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7882 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7883 :
7884 : protected:
7885 21 : GDALMDArrayResampled(
7886 : const std::shared_ptr<GDALMDArray> &poParent,
7887 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7888 : const std::vector<GUInt64> &anBlockSize)
7889 42 : : GDALAbstractMDArray(std::string(),
7890 42 : "Resampled view of " + poParent->GetFullName()),
7891 : GDALPamMDArray(
7892 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7893 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7894 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7895 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7896 : {
7897 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7898 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7899 21 : }
7900 :
7901 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7902 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7903 : const GDALExtendedDataType &bufferDataType,
7904 : void *pDstBuffer) const override;
7905 :
7906 : public:
7907 : static std::shared_ptr<GDALMDArray>
7908 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7909 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7910 : GDALRIOResampleAlg resampleAlg,
7911 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7912 :
7913 42 : ~GDALMDArrayResampled()
7914 21 : {
7915 : // First close the warped VRT
7916 21 : m_poReprojectedDS.reset();
7917 21 : m_poParentDS.reset();
7918 42 : }
7919 :
7920 11 : bool IsWritable() const override
7921 : {
7922 11 : return false;
7923 : }
7924 :
7925 74 : const std::string &GetFilename() const override
7926 : {
7927 74 : return m_poParent->GetFilename();
7928 : }
7929 :
7930 : const std::vector<std::shared_ptr<GDALDimension>> &
7931 257 : GetDimensions() const override
7932 : {
7933 257 : return m_apoDims;
7934 : }
7935 :
7936 109 : const GDALExtendedDataType &GetDataType() const override
7937 : {
7938 109 : return m_dt;
7939 : }
7940 :
7941 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7942 : {
7943 21 : return m_poSRS;
7944 : }
7945 :
7946 12 : std::vector<GUInt64> GetBlockSize() const override
7947 : {
7948 12 : return m_anBlockSize;
7949 : }
7950 :
7951 : std::shared_ptr<GDALAttribute>
7952 1 : GetAttribute(const std::string &osName) const override
7953 : {
7954 1 : return m_poParent->GetAttribute(osName);
7955 : }
7956 :
7957 : std::vector<std::shared_ptr<GDALAttribute>>
7958 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7959 : {
7960 12 : return m_poParent->GetAttributes(papszOptions);
7961 : }
7962 :
7963 1 : const std::string &GetUnit() const override
7964 : {
7965 1 : return m_poParent->GetUnit();
7966 : }
7967 :
7968 1 : const void *GetRawNoDataValue() const override
7969 : {
7970 1 : return m_poParent->GetRawNoDataValue();
7971 : }
7972 :
7973 1 : double GetOffset(bool *pbHasOffset,
7974 : GDALDataType *peStorageType) const override
7975 : {
7976 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7977 : }
7978 :
7979 1 : double GetScale(bool *pbHasScale,
7980 : GDALDataType *peStorageType) const override
7981 : {
7982 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7983 : }
7984 : };
7985 :
7986 : /************************************************************************/
7987 : /* GDALMDArrayResampled::Create() */
7988 : /************************************************************************/
7989 :
7990 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7991 : const std::shared_ptr<GDALMDArray> &poParent,
7992 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7993 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7994 : CSLConstList /* papszOptions */)
7995 : {
7996 29 : const char *pszResampleAlg = "nearest";
7997 29 : bool unsupported = false;
7998 29 : switch (resampleAlg)
7999 : {
8000 16 : case GRIORA_NearestNeighbour:
8001 16 : pszResampleAlg = "nearest";
8002 16 : break;
8003 2 : case GRIORA_Bilinear:
8004 2 : pszResampleAlg = "bilinear";
8005 2 : break;
8006 5 : case GRIORA_Cubic:
8007 5 : pszResampleAlg = "cubic";
8008 5 : break;
8009 1 : case GRIORA_CubicSpline:
8010 1 : pszResampleAlg = "cubicspline";
8011 1 : break;
8012 1 : case GRIORA_Lanczos:
8013 1 : pszResampleAlg = "lanczos";
8014 1 : break;
8015 1 : case GRIORA_Average:
8016 1 : pszResampleAlg = "average";
8017 1 : break;
8018 1 : case GRIORA_Mode:
8019 1 : pszResampleAlg = "mode";
8020 1 : break;
8021 1 : case GRIORA_Gauss:
8022 1 : unsupported = true;
8023 1 : break;
8024 0 : case GRIORA_RESERVED_START:
8025 0 : unsupported = true;
8026 0 : break;
8027 0 : case GRIORA_RESERVED_END:
8028 0 : unsupported = true;
8029 0 : break;
8030 1 : case GRIORA_RMS:
8031 1 : pszResampleAlg = "rms";
8032 1 : break;
8033 : }
8034 29 : if (unsupported)
8035 : {
8036 1 : CPLError(CE_Failure, CPLE_NotSupported,
8037 : "Unsupported resample method for GetResampled()");
8038 1 : return nullptr;
8039 : }
8040 :
8041 28 : if (poParent->GetDimensionCount() < 2)
8042 : {
8043 1 : CPLError(CE_Failure, CPLE_NotSupported,
8044 : "GetResampled() only supports 2 dimensions or more");
8045 1 : return nullptr;
8046 : }
8047 :
8048 27 : const auto &aoParentDims = poParent->GetDimensions();
8049 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8050 : {
8051 2 : CPLError(CE_Failure, CPLE_AppDefined,
8052 : "GetResampled(): apoNewDims size should be the same as "
8053 : "GetDimensionCount()");
8054 2 : return nullptr;
8055 : }
8056 :
8057 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8058 25 : apoNewDims.reserve(apoNewDimsIn.size());
8059 :
8060 50 : std::vector<GUInt64> anBlockSize;
8061 25 : anBlockSize.reserve(apoNewDimsIn.size());
8062 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8063 :
8064 50 : auto apoParentDims = poParent->GetDimensions();
8065 : // Special case for NASA EMIT datasets
8066 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8067 7 : apoParentDims[0]->GetName() == "downtrack" &&
8068 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8069 2 : apoParentDims[2]->GetName() == "bands");
8070 :
8071 : const size_t iYDimParent =
8072 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8073 : const size_t iXDimParent =
8074 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8075 :
8076 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8077 : {
8078 53 : if (i == iYDimParent || i == iXDimParent)
8079 48 : continue;
8080 5 : if (apoNewDimsIn[i] == nullptr)
8081 : {
8082 3 : apoNewDims.emplace_back(aoParentDims[i]);
8083 : }
8084 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8085 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8086 : {
8087 1 : CPLError(CE_Failure, CPLE_AppDefined,
8088 : "GetResampled(): apoNewDims[%u] should be the same "
8089 : "as its parent",
8090 : i);
8091 1 : return nullptr;
8092 : }
8093 : else
8094 : {
8095 1 : apoNewDims.emplace_back(aoParentDims[i]);
8096 : }
8097 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8098 : }
8099 :
8100 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8101 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8102 :
8103 24 : double dfXStart = 0.0;
8104 24 : double dfXSpacing = 0.0;
8105 24 : bool gotXSpacing = false;
8106 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8107 24 : if (poNewDimX)
8108 : {
8109 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8110 : {
8111 0 : CPLError(CE_Failure, CPLE_NotSupported,
8112 : "Too big size for X dimension");
8113 0 : return nullptr;
8114 : }
8115 4 : auto var = poNewDimX->GetIndexingVariable();
8116 4 : if (var)
8117 : {
8118 2 : if (var->GetDimensionCount() != 1 ||
8119 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8120 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8121 : {
8122 0 : CPLError(CE_Failure, CPLE_NotSupported,
8123 : "New X dimension should be indexed by a regularly "
8124 : "spaced variable");
8125 0 : return nullptr;
8126 : }
8127 1 : gotXSpacing = true;
8128 : }
8129 : }
8130 :
8131 24 : double dfYStart = 0.0;
8132 24 : double dfYSpacing = 0.0;
8133 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8134 24 : bool gotYSpacing = false;
8135 24 : if (poNewDimY)
8136 : {
8137 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8138 : {
8139 0 : CPLError(CE_Failure, CPLE_NotSupported,
8140 : "Too big size for Y dimension");
8141 0 : return nullptr;
8142 : }
8143 4 : auto var = poNewDimY->GetIndexingVariable();
8144 4 : if (var)
8145 : {
8146 2 : if (var->GetDimensionCount() != 1 ||
8147 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8148 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8149 : {
8150 0 : CPLError(CE_Failure, CPLE_NotSupported,
8151 : "New Y dimension should be indexed by a regularly "
8152 : "spaced variable");
8153 0 : return nullptr;
8154 : }
8155 1 : gotYSpacing = true;
8156 : }
8157 : }
8158 :
8159 : // This limitation could probably be removed
8160 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8161 : {
8162 0 : CPLError(CE_Failure, CPLE_NotSupported,
8163 : "Either none of new X or Y dimension should have an indexing "
8164 : "variable, or both should both should have one.");
8165 0 : return nullptr;
8166 : }
8167 :
8168 48 : std::string osDstWKT;
8169 24 : if (poTargetSRS)
8170 : {
8171 2 : char *pszDstWKT = nullptr;
8172 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8173 : {
8174 0 : CPLFree(pszDstWKT);
8175 0 : return nullptr;
8176 : }
8177 2 : osDstWKT = pszDstWKT;
8178 2 : CPLFree(pszDstWKT);
8179 : }
8180 :
8181 : // Use coordinate variables for geolocation array
8182 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8183 24 : bool useGeolocationArray = false;
8184 24 : if (apoCoordinateVars.size() >= 2)
8185 : {
8186 0 : std::shared_ptr<GDALMDArray> poLongVar;
8187 0 : std::shared_ptr<GDALMDArray> poLatVar;
8188 15 : for (const auto &poCoordVar : apoCoordinateVars)
8189 : {
8190 10 : const auto &osName = poCoordVar->GetName();
8191 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8192 20 : std::string osStandardName;
8193 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8194 2 : poAttr->GetDimensionCount() == 0)
8195 : {
8196 2 : const char *pszStandardName = poAttr->ReadAsString();
8197 2 : if (pszStandardName)
8198 2 : osStandardName = pszStandardName;
8199 : }
8200 21 : if (osName == "lon" || osName == "longitude" ||
8201 21 : osName == "Longitude" || osStandardName == "longitude")
8202 : {
8203 5 : poLongVar = poCoordVar;
8204 : }
8205 6 : else if (osName == "lat" || osName == "latitude" ||
8206 6 : osName == "Latitude" || osStandardName == "latitude")
8207 : {
8208 5 : poLatVar = poCoordVar;
8209 : }
8210 : }
8211 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8212 : {
8213 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8214 5 : const auto &longDims = poLongVar->GetDimensions();
8215 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8216 5 : const auto &latDims = poLatVar->GetDimensions();
8217 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8218 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8219 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8220 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8221 : {
8222 : // Geolocation arrays are 1D, and of consistent size with
8223 : // the variable
8224 0 : useGeolocationArray = true;
8225 : }
8226 1 : else if ((longDimCount == 2 ||
8227 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8228 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8229 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8230 1 : (latDimCount == 2 ||
8231 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8232 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8233 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8234 :
8235 : {
8236 : // Geolocation arrays are 2D (or 3D with first dimension of
8237 : // size 1, as found in Sentinel 5P products), and of consistent
8238 : // size with the variable
8239 5 : useGeolocationArray = true;
8240 : }
8241 : else
8242 : {
8243 0 : CPLDebug(
8244 : "GDAL",
8245 : "Longitude and latitude coordinate variables found, "
8246 : "but their characteristics are not compatible of using "
8247 : "them as geolocation arrays");
8248 : }
8249 5 : if (useGeolocationArray)
8250 : {
8251 10 : CPLDebug("GDAL",
8252 : "Setting geolocation array from variables %s and %s",
8253 5 : poLongVar->GetName().c_str(),
8254 5 : poLatVar->GetName().c_str());
8255 : const std::string osFilenameLong =
8256 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8257 : const std::string osFilenameLat =
8258 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8259 : std::unique_ptr<GDALDataset> poTmpLongDS(
8260 : longDimCount == 1
8261 0 : ? poLongVar->AsClassicDataset(0, 0)
8262 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8263 15 : longDimCount - 2));
8264 5 : auto hTIFFLongDS = GDALTranslate(
8265 : osFilenameLong.c_str(),
8266 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8267 : std::unique_ptr<GDALDataset> poTmpLatDS(
8268 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8269 20 : : poLatVar->AsClassicDataset(
8270 15 : latDimCount - 1, latDimCount - 2));
8271 5 : auto hTIFFLatDS = GDALTranslate(
8272 : osFilenameLat.c_str(),
8273 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8274 5 : const bool bError =
8275 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8276 5 : GDALClose(hTIFFLongDS);
8277 5 : GDALClose(hTIFFLatDS);
8278 5 : if (bError)
8279 : {
8280 0 : VSIUnlink(osFilenameLong.c_str());
8281 0 : VSIUnlink(osFilenameLat.c_str());
8282 0 : return nullptr;
8283 : }
8284 :
8285 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8286 : }
8287 : }
8288 : else
8289 : {
8290 0 : CPLDebug("GDAL",
8291 : "Coordinate variables available for %s, but "
8292 : "longitude and/or latitude variables were not identified",
8293 0 : poParent->GetName().c_str());
8294 : }
8295 : }
8296 :
8297 : // Build gdalwarp arguments
8298 48 : CPLStringList aosArgv;
8299 :
8300 24 : aosArgv.AddString("-of");
8301 24 : aosArgv.AddString("VRT");
8302 :
8303 24 : aosArgv.AddString("-r");
8304 24 : aosArgv.AddString(pszResampleAlg);
8305 :
8306 24 : if (!osDstWKT.empty())
8307 : {
8308 2 : aosArgv.AddString("-t_srs");
8309 2 : aosArgv.AddString(osDstWKT.c_str());
8310 : }
8311 :
8312 24 : if (useGeolocationArray)
8313 5 : aosArgv.AddString("-geoloc");
8314 :
8315 24 : if (gotXSpacing && gotYSpacing)
8316 : {
8317 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8318 : const double dfXMax =
8319 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8320 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8321 : const double dfYMin =
8322 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8323 1 : aosArgv.AddString("-te");
8324 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8325 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8326 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8327 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8328 : }
8329 :
8330 24 : if (poNewDimX && poNewDimY)
8331 : {
8332 3 : aosArgv.AddString("-ts");
8333 : aosArgv.AddString(
8334 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8335 : aosArgv.AddString(
8336 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8337 : }
8338 21 : else if (poNewDimX)
8339 : {
8340 1 : aosArgv.AddString("-ts");
8341 : aosArgv.AddString(
8342 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8343 1 : aosArgv.AddString("0");
8344 : }
8345 20 : else if (poNewDimY)
8346 : {
8347 1 : aosArgv.AddString("-ts");
8348 1 : aosArgv.AddString("0");
8349 : aosArgv.AddString(
8350 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8351 : }
8352 :
8353 : // Create a warped VRT dataset
8354 : GDALWarpAppOptions *psOptions =
8355 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8356 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8357 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8358 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8359 24 : GDALWarpAppOptionsFree(psOptions);
8360 24 : if (poReprojectedDS == nullptr)
8361 3 : return nullptr;
8362 :
8363 : int nBlockXSize;
8364 : int nBlockYSize;
8365 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8366 21 : anBlockSize.emplace_back(nBlockYSize);
8367 21 : anBlockSize.emplace_back(nBlockXSize);
8368 :
8369 21 : GDALGeoTransform gt;
8370 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8371 21 : CPLAssert(eErr == CE_None);
8372 21 : CPL_IGNORE_RET_VAL(eErr);
8373 :
8374 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8375 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8376 42 : poReprojectedDS->GetRasterYSize());
8377 : auto varY = GDALMDArrayRegularlySpaced::Create(
8378 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8379 21 : poDimY->SetIndexingVariable(varY);
8380 :
8381 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8382 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8383 42 : poReprojectedDS->GetRasterXSize());
8384 : auto varX = GDALMDArrayRegularlySpaced::Create(
8385 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8386 21 : poDimX->SetIndexingVariable(varX);
8387 :
8388 21 : apoNewDims.emplace_back(poDimY);
8389 21 : apoNewDims.emplace_back(poDimX);
8390 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8391 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8392 21 : newAr->SetSelf(newAr);
8393 21 : if (poTargetSRS)
8394 : {
8395 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8396 : }
8397 : else
8398 : {
8399 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8400 : }
8401 21 : newAr->m_poVarX = varX;
8402 21 : newAr->m_poVarY = varY;
8403 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8404 21 : newAr->m_poParentDS = std::move(poParentDS);
8405 :
8406 : // If the input array is y,x,band ordered, the above newAr is
8407 : // actually band,y,x ordered as it is more convenient for
8408 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8409 : // array to the order of the input array
8410 21 : if (bYXBandOrder)
8411 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8412 :
8413 19 : return newAr;
8414 : }
8415 :
8416 : /************************************************************************/
8417 : /* GDALMDArrayResampled::IRead() */
8418 : /************************************************************************/
8419 :
8420 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8421 : const size_t *count, const GInt64 *arrayStep,
8422 : const GPtrDiff_t *bufferStride,
8423 : const GDALExtendedDataType &bufferDataType,
8424 : void *pDstBuffer) const
8425 : {
8426 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8427 0 : return false;
8428 :
8429 : struct Stack
8430 : {
8431 : size_t nIters = 0;
8432 : GByte *dst_ptr = nullptr;
8433 : GPtrDiff_t dst_inc_offset = 0;
8434 : };
8435 :
8436 29 : const auto nDims = GetDimensionCount();
8437 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8438 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8439 92 : for (size_t i = 0; i < nDims; i++)
8440 : {
8441 63 : stack[i].dst_inc_offset =
8442 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8443 : }
8444 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8445 :
8446 29 : size_t dimIdx = 0;
8447 29 : const size_t iDimY = nDims - 2;
8448 29 : const size_t iDimX = nDims - 1;
8449 : // Use an array to avoid a false positive warning from CLang Static
8450 : // Analyzer about flushCaches being never read
8451 29 : bool flushCaches[] = {false};
8452 : const bool bYXBandOrder =
8453 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8454 :
8455 38 : lbl_next_depth:
8456 38 : if (dimIdx == iDimY)
8457 : {
8458 33 : if (flushCaches[0])
8459 : {
8460 5 : flushCaches[0] = false;
8461 : // When changing of 2D slice, flush GDAL 2D buffers
8462 5 : m_poParentDS->FlushCache(false);
8463 5 : m_poReprojectedDS->FlushCache(false);
8464 : }
8465 :
8466 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8467 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8468 : arrayStep, bufferStride, bufferDataType,
8469 33 : stack[dimIdx].dst_ptr))
8470 : {
8471 0 : return false;
8472 : }
8473 : }
8474 : else
8475 : {
8476 5 : stack[dimIdx].nIters = count[dimIdx];
8477 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8478 5 : arrayStartIdx[dimIdx])
8479 : {
8480 1 : flushCaches[0] = true;
8481 : }
8482 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8483 5 : arrayStartIdx[dimIdx];
8484 : while (true)
8485 : {
8486 9 : dimIdx++;
8487 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8488 9 : goto lbl_next_depth;
8489 9 : lbl_return_to_caller:
8490 9 : dimIdx--;
8491 9 : if ((--stack[dimIdx].nIters) == 0)
8492 5 : break;
8493 4 : flushCaches[0] = true;
8494 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8495 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8496 : }
8497 : }
8498 38 : if (dimIdx > 0)
8499 9 : goto lbl_return_to_caller;
8500 :
8501 29 : return true;
8502 : }
8503 :
8504 : /************************************************************************/
8505 : /* GetResampled() */
8506 : /************************************************************************/
8507 :
8508 : /** Return an array that is a resampled / reprojected view of the current array
8509 : *
8510 : * This is the same as the C function GDALMDArrayGetResampled().
8511 : *
8512 : * Currently this method can only resample along the last 2 dimensions, unless
8513 : * orthorectifying a NASA EMIT dataset.
8514 : *
8515 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8516 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8517 : *
8518 : * Options available are:
8519 : * <ul>
8520 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8521 : * Can be set to NO to use generic reprojection method.
8522 : * </li>
8523 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8524 : * orthorectification to take into account the value of the
8525 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8526 : * current array along the band dimension are valid.</li>
8527 : * </ul>
8528 : *
8529 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8530 : * apoNewDims[i] can be NULL to let the method automatically
8531 : * determine it.
8532 : * @param resampleAlg Resampling algorithm
8533 : * @param poTargetSRS Target SRS, or nullptr
8534 : * @param papszOptions NULL-terminated list of options, or NULL.
8535 : *
8536 : * @return a new array, that holds a reference to the original one, and thus is
8537 : * a view of it (not a copy), or nullptr in case of error.
8538 : *
8539 : * @since 3.4
8540 : */
8541 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8542 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8543 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8544 : CSLConstList papszOptions) const
8545 : {
8546 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8547 38 : if (!self)
8548 : {
8549 0 : CPLError(CE_Failure, CPLE_AppDefined,
8550 : "Driver implementation issue: m_pSelf not set !");
8551 0 : return nullptr;
8552 : }
8553 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8554 : {
8555 0 : CPLError(CE_Failure, CPLE_AppDefined,
8556 : "GetResampled() only supports numeric data type");
8557 0 : return nullptr;
8558 : }
8559 :
8560 : // Special case for NASA EMIT datasets
8561 76 : auto apoDims = GetDimensions();
8562 36 : if (poTargetSRS == nullptr &&
8563 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8564 20 : apoDims[1]->GetName() == "crosstrack" &&
8565 10 : apoDims[2]->GetName() == "bands" &&
8566 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8567 1 : apoNewDims ==
8568 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8569 30 : apoDims[2]})) ||
8570 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8571 3 : apoDims[1]->GetName() == "crosstrack" &&
8572 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8573 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8574 : "EMIT_ORTHORECTIFICATION", "YES")))
8575 : {
8576 9 : auto poRootGroup = GetRootGroup();
8577 9 : if (poRootGroup)
8578 : {
8579 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8580 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8581 9 : if (poAttrGeotransform &&
8582 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8583 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8584 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8585 9 : poLocationGroup)
8586 : {
8587 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8588 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8589 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8590 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8591 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8592 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8593 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8594 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8595 : {
8596 : return CreateGLTOrthorectified(
8597 : self, poRootGroup, poGLT_X, poGLT_Y,
8598 : /* nGLTIndexOffset = */ -1,
8599 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8600 : }
8601 : }
8602 : }
8603 : }
8604 :
8605 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8606 : "EMIT_ORTHORECTIFICATION", "NO")))
8607 : {
8608 0 : CPLError(CE_Failure, CPLE_AppDefined,
8609 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8610 : "parameters are not compatible with it");
8611 0 : return nullptr;
8612 : }
8613 :
8614 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8615 29 : poTargetSRS, papszOptions);
8616 : }
8617 :
8618 : /************************************************************************/
8619 : /* GDALDatasetFromArray() */
8620 : /************************************************************************/
8621 :
8622 : class GDALDatasetFromArray;
8623 :
8624 : namespace
8625 : {
8626 : struct MetadataItem
8627 : {
8628 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8629 : std::string osName{};
8630 : std::string osDefinition{};
8631 : bool bDefinitionUsesPctForG = false;
8632 : };
8633 :
8634 : struct BandImageryMetadata
8635 : {
8636 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8637 : double dfCentralWavelengthToMicrometer = 1.0;
8638 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8639 : double dfFWHMToMicrometer = 1.0;
8640 : };
8641 :
8642 : } // namespace
8643 :
8644 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8645 : {
8646 : std::vector<GUInt64> m_anOffset{};
8647 : std::vector<size_t> m_anCount{};
8648 : std::vector<GPtrDiff_t> m_anStride{};
8649 :
8650 : protected:
8651 : CPLErr IReadBlock(int, int, void *) override;
8652 : CPLErr IWriteBlock(int, int, void *) override;
8653 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8654 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8655 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8656 : GSpacing nLineSpaceBuf,
8657 : GDALRasterIOExtraArg *psExtraArg) override;
8658 :
8659 : public:
8660 : explicit GDALRasterBandFromArray(
8661 : GDALDatasetFromArray *poDSIn,
8662 : const std::vector<GUInt64> &anOtherDimCoord,
8663 : const std::vector<std::vector<MetadataItem>>
8664 : &aoBandParameterMetadataItems,
8665 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8666 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8667 :
8668 : double GetNoDataValue(int *pbHasNoData) override;
8669 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8670 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8671 : double GetOffset(int *pbHasOffset) override;
8672 : double GetScale(int *pbHasScale) override;
8673 : const char *GetUnitType() override;
8674 : GDALColorInterp GetColorInterpretation() override;
8675 : };
8676 :
8677 : class GDALDatasetFromArray final : public GDALPamDataset
8678 : {
8679 : friend class GDALRasterBandFromArray;
8680 :
8681 : std::shared_ptr<GDALMDArray> m_poArray;
8682 : size_t m_iXDim;
8683 : size_t m_iYDim;
8684 : GDALGeoTransform m_gt{};
8685 : bool m_bHasGT = false;
8686 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8687 : GDALMultiDomainMetadata m_oMDD{};
8688 : std::string m_osOvrFilename{};
8689 :
8690 : public:
8691 205 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8692 : size_t iXDim, size_t iYDim)
8693 205 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8694 : {
8695 : // Initialize an overview filename from the filename of the array
8696 : // and its name.
8697 205 : const std::string &osFilename = m_poArray->GetFilename();
8698 205 : if (!osFilename.empty())
8699 : {
8700 181 : m_osOvrFilename = osFilename;
8701 181 : m_osOvrFilename += '.';
8702 6630 : for (char ch : m_poArray->GetName())
8703 : {
8704 6449 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8705 5715 : (ch >= 'a' && ch <= 'z') || ch == '_')
8706 : {
8707 5188 : m_osOvrFilename += ch;
8708 : }
8709 : else
8710 : {
8711 1261 : m_osOvrFilename += '_';
8712 : }
8713 : }
8714 181 : m_osOvrFilename += ".ovr";
8715 181 : oOvManager.Initialize(this);
8716 : }
8717 205 : }
8718 :
8719 : static GDALDatasetFromArray *
8720 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8721 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8722 : CSLConstList papszOptions);
8723 :
8724 : ~GDALDatasetFromArray() override;
8725 :
8726 336 : CPLErr Close() override
8727 : {
8728 336 : CPLErr eErr = CE_None;
8729 336 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8730 : {
8731 336 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8732 : CE_None)
8733 0 : eErr = CE_Failure;
8734 336 : m_poArray.reset();
8735 : }
8736 336 : return eErr;
8737 : }
8738 :
8739 54 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8740 : {
8741 54 : gt = m_gt;
8742 54 : return m_bHasGT ? CE_None : CE_Failure;
8743 : }
8744 :
8745 62 : const OGRSpatialReference *GetSpatialRef() const override
8746 : {
8747 62 : if (m_poArray->GetDimensionCount() < 2)
8748 3 : return nullptr;
8749 59 : m_poSRS = m_poArray->GetSpatialRef();
8750 59 : if (m_poSRS)
8751 : {
8752 20 : m_poSRS.reset(m_poSRS->Clone());
8753 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8754 60 : for (auto &m : axisMapping)
8755 : {
8756 40 : if (m == static_cast<int>(m_iXDim) + 1)
8757 20 : m = 1;
8758 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8759 20 : m = 2;
8760 : }
8761 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8762 : }
8763 59 : return m_poSRS.get();
8764 : }
8765 :
8766 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8767 : {
8768 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8769 : }
8770 :
8771 163 : char **GetMetadata(const char *pszDomain) override
8772 : {
8773 163 : return m_oMDD.GetMetadata(pszDomain);
8774 : }
8775 :
8776 233 : const char *GetMetadataItem(const char *pszName,
8777 : const char *pszDomain) override
8778 : {
8779 421 : if (!m_osOvrFilename.empty() && pszName &&
8780 435 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8781 14 : EQUAL(pszDomain, "OVERVIEWS"))
8782 : {
8783 14 : return m_osOvrFilename.c_str();
8784 : }
8785 219 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8786 : }
8787 : };
8788 :
8789 410 : GDALDatasetFromArray::~GDALDatasetFromArray()
8790 : {
8791 205 : GDALDatasetFromArray::Close();
8792 410 : }
8793 :
8794 : /************************************************************************/
8795 : /* GDALRasterBandFromArray() */
8796 : /************************************************************************/
8797 :
8798 279 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8799 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8800 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8801 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8802 279 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8803 : {
8804 279 : const auto &poArray(poDSIn->m_poArray);
8805 279 : const auto &dims(poArray->GetDimensions());
8806 279 : const auto nDimCount(dims.size());
8807 558 : const auto blockSize(poArray->GetBlockSize());
8808 267 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8809 546 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8810 138 : blockSize[poDSIn->m_iYDim]))
8811 : : 1;
8812 279 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8813 150 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8814 150 : blockSize[poDSIn->m_iXDim]))
8815 279 : : poDSIn->GetRasterXSize();
8816 279 : eDataType = poArray->GetDataType().GetNumericDataType();
8817 279 : eAccess = poDSIn->eAccess;
8818 279 : m_anOffset.resize(nDimCount);
8819 279 : m_anCount.resize(nDimCount, 1);
8820 279 : m_anStride.resize(nDimCount);
8821 951 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8822 : {
8823 672 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8824 : {
8825 252 : std::string dimName(dims[i]->GetName());
8826 126 : GUInt64 nIndex = anOtherDimCoord[j];
8827 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8828 : // subsetted dimensions as generated by GetView()
8829 126 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8830 : {
8831 : CPLStringList aosTokens(
8832 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8833 6 : if (aosTokens.size() == 5)
8834 : {
8835 6 : dimName = aosTokens[1];
8836 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8837 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8838 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8839 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8840 0 : : nStartDim - (nIndex * -nIncrDim);
8841 : }
8842 : }
8843 126 : if (nDimCount != 3 || dimName != "Band")
8844 : {
8845 70 : SetMetadataItem(
8846 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8847 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8848 : }
8849 :
8850 126 : auto indexingVar = dims[i]->GetIndexingVariable();
8851 :
8852 : // If the indexing variable is also listed in band parameter arrays,
8853 : // then don't use our default formatting
8854 126 : if (indexingVar)
8855 : {
8856 42 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8857 : {
8858 14 : if (oItem.poArray->GetFullName() ==
8859 14 : indexingVar->GetFullName())
8860 : {
8861 12 : indexingVar.reset();
8862 12 : break;
8863 : }
8864 : }
8865 : }
8866 :
8867 154 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8868 28 : indexingVar->GetDimensions()[0]->GetSize() ==
8869 28 : dims[i]->GetSize())
8870 : {
8871 28 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8872 : {
8873 0 : if (!bHasWarned)
8874 : {
8875 0 : CPLError(
8876 : CE_Warning, CPLE_AppDefined,
8877 : "Maximum delay to load band metadata from "
8878 : "dimension indexing variables has expired. "
8879 : "Increase the value of the "
8880 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8881 : "option of GDALMDArray::AsClassicDataset() "
8882 : "(also accessible as the "
8883 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8884 : "configuration option), "
8885 : "or set it to 'unlimited' for unlimited delay. ");
8886 0 : bHasWarned = true;
8887 : }
8888 : }
8889 : else
8890 : {
8891 28 : size_t nCount = 1;
8892 28 : const auto &dt(indexingVar->GetDataType());
8893 56 : std::vector<GByte> abyTmp(dt.GetSize());
8894 56 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8895 28 : nullptr, nullptr, dt, &abyTmp[0]))
8896 : {
8897 28 : char *pszTmp = nullptr;
8898 28 : GDALExtendedDataType::CopyValue(
8899 28 : &abyTmp[0], dt, &pszTmp,
8900 56 : GDALExtendedDataType::CreateString());
8901 28 : if (pszTmp)
8902 : {
8903 28 : SetMetadataItem(
8904 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8905 : pszTmp);
8906 28 : CPLFree(pszTmp);
8907 : }
8908 :
8909 28 : const auto &unit(indexingVar->GetUnit());
8910 28 : if (!unit.empty())
8911 : {
8912 12 : SetMetadataItem(
8913 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8914 : unit.c_str());
8915 : }
8916 : }
8917 : }
8918 : }
8919 :
8920 144 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8921 : {
8922 36 : CPLString osVal;
8923 :
8924 18 : size_t nCount = 1;
8925 18 : const auto &dt(oItem.poArray->GetDataType());
8926 18 : if (oItem.bDefinitionUsesPctForG)
8927 : {
8928 : // There is one and only one %[x][.y]f|g in osDefinition
8929 16 : std::vector<GByte> abyTmp(dt.GetSize());
8930 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8931 8 : nullptr, nullptr, dt, &abyTmp[0]))
8932 : {
8933 8 : double dfVal = 0;
8934 8 : GDALExtendedDataType::CopyValue(
8935 8 : &abyTmp[0], dt, &dfVal,
8936 16 : GDALExtendedDataType::Create(GDT_Float64));
8937 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8938 : }
8939 : }
8940 : else
8941 : {
8942 : // There should be zero or one %s in osDefinition
8943 10 : char *pszValue = nullptr;
8944 10 : if (dt.GetClass() == GEDTC_STRING)
8945 : {
8946 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8947 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8948 2 : dt, &pszValue));
8949 : }
8950 : else
8951 : {
8952 16 : std::vector<GByte> abyTmp(dt.GetSize());
8953 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8954 : nullptr, nullptr, dt,
8955 8 : &abyTmp[0]))
8956 : {
8957 8 : GDALExtendedDataType::CopyValue(
8958 8 : &abyTmp[0], dt, &pszValue,
8959 16 : GDALExtendedDataType::CreateString());
8960 : }
8961 : }
8962 :
8963 10 : if (pszValue)
8964 : {
8965 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8966 10 : CPLFree(pszValue);
8967 : }
8968 : }
8969 18 : if (!osVal.empty())
8970 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
8971 : }
8972 :
8973 126 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8974 : {
8975 : auto &poCentralWavelengthArray =
8976 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8977 4 : size_t nCount = 1;
8978 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
8979 8 : std::vector<GByte> abyTmp(dt.GetSize());
8980 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8981 : &nCount, nullptr, nullptr,
8982 4 : dt, &abyTmp[0]))
8983 : {
8984 4 : double dfVal = 0;
8985 4 : GDALExtendedDataType::CopyValue(
8986 4 : &abyTmp[0], dt, &dfVal,
8987 8 : GDALExtendedDataType::Create(GDT_Float64));
8988 4 : SetMetadataItem(
8989 : "CENTRAL_WAVELENGTH_UM",
8990 : CPLSPrintf(
8991 4 : "%g", dfVal * aoBandImageryMetadata[j]
8992 4 : .dfCentralWavelengthToMicrometer),
8993 : "IMAGERY");
8994 : }
8995 : }
8996 :
8997 126 : if (aoBandImageryMetadata[j].poFWHMArray)
8998 : {
8999 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9000 2 : size_t nCount = 1;
9001 2 : const auto &dt(poFWHMArray->GetDataType());
9002 4 : std::vector<GByte> abyTmp(dt.GetSize());
9003 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9004 2 : nullptr, dt, &abyTmp[0]))
9005 : {
9006 2 : double dfVal = 0;
9007 2 : GDALExtendedDataType::CopyValue(
9008 2 : &abyTmp[0], dt, &dfVal,
9009 4 : GDALExtendedDataType::Create(GDT_Float64));
9010 2 : SetMetadataItem(
9011 : "FWHM_UM",
9012 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9013 2 : .dfFWHMToMicrometer),
9014 : "IMAGERY");
9015 : }
9016 : }
9017 :
9018 126 : m_anOffset[i] = anOtherDimCoord[j];
9019 126 : j++;
9020 : }
9021 : }
9022 279 : }
9023 :
9024 : /************************************************************************/
9025 : /* GetNoDataValue() */
9026 : /************************************************************************/
9027 :
9028 111 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9029 : {
9030 111 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9031 111 : const auto &poArray(l_poDS->m_poArray);
9032 111 : bool bHasNodata = false;
9033 111 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9034 111 : if (pbHasNoData)
9035 99 : *pbHasNoData = bHasNodata;
9036 111 : return res;
9037 : }
9038 :
9039 : /************************************************************************/
9040 : /* GetNoDataValueAsInt64() */
9041 : /************************************************************************/
9042 :
9043 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9044 : {
9045 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9046 1 : const auto &poArray(l_poDS->m_poArray);
9047 1 : bool bHasNodata = false;
9048 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9049 1 : if (pbHasNoData)
9050 1 : *pbHasNoData = bHasNodata;
9051 1 : return nodata;
9052 : }
9053 :
9054 : /************************************************************************/
9055 : /* GetNoDataValueAsUInt64() */
9056 : /************************************************************************/
9057 :
9058 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9059 : {
9060 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9061 1 : const auto &poArray(l_poDS->m_poArray);
9062 1 : bool bHasNodata = false;
9063 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9064 1 : if (pbHasNoData)
9065 1 : *pbHasNoData = bHasNodata;
9066 1 : return nodata;
9067 : }
9068 :
9069 : /************************************************************************/
9070 : /* GetOffset() */
9071 : /************************************************************************/
9072 :
9073 38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9074 : {
9075 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9076 38 : const auto &poArray(l_poDS->m_poArray);
9077 38 : bool bHasValue = false;
9078 38 : double dfRes = poArray->GetOffset(&bHasValue);
9079 38 : if (pbHasOffset)
9080 19 : *pbHasOffset = bHasValue;
9081 38 : return dfRes;
9082 : }
9083 :
9084 : /************************************************************************/
9085 : /* GetUnitType() */
9086 : /************************************************************************/
9087 :
9088 44 : const char *GDALRasterBandFromArray::GetUnitType()
9089 : {
9090 44 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9091 44 : const auto &poArray(l_poDS->m_poArray);
9092 44 : return poArray->GetUnit().c_str();
9093 : }
9094 :
9095 : /************************************************************************/
9096 : /* GetScale() */
9097 : /************************************************************************/
9098 :
9099 36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9100 : {
9101 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9102 36 : const auto &poArray(l_poDS->m_poArray);
9103 36 : bool bHasValue = false;
9104 36 : double dfRes = poArray->GetScale(&bHasValue);
9105 36 : if (pbHasScale)
9106 17 : *pbHasScale = bHasValue;
9107 36 : return dfRes;
9108 : }
9109 :
9110 : /************************************************************************/
9111 : /* IReadBlock() */
9112 : /************************************************************************/
9113 :
9114 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9115 : void *pImage)
9116 : {
9117 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9118 68 : const int nXOff = nBlockXOff * nBlockXSize;
9119 68 : const int nYOff = nBlockYOff * nBlockYSize;
9120 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9121 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9122 : GDALRasterIOExtraArg sExtraArg;
9123 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9124 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9125 : nReqXSize, nReqYSize, eDataType, nDTSize,
9126 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9127 : }
9128 :
9129 : /************************************************************************/
9130 : /* IWriteBlock() */
9131 : /************************************************************************/
9132 :
9133 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9134 : void *pImage)
9135 : {
9136 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9137 1 : const int nXOff = nBlockXOff * nBlockXSize;
9138 1 : const int nYOff = nBlockYOff * nBlockYSize;
9139 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9140 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9141 : GDALRasterIOExtraArg sExtraArg;
9142 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9143 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9144 : nReqXSize, nReqYSize, eDataType, nDTSize,
9145 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9146 : }
9147 :
9148 : /************************************************************************/
9149 : /* IRasterIO() */
9150 : /************************************************************************/
9151 :
9152 332 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9153 : int nYOff, int nXSize, int nYSize,
9154 : void *pData, int nBufXSize,
9155 : int nBufYSize, GDALDataType eBufType,
9156 : GSpacing nPixelSpaceBuf,
9157 : GSpacing nLineSpaceBuf,
9158 : GDALRasterIOExtraArg *psExtraArg)
9159 : {
9160 332 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9161 332 : const auto &poArray(l_poDS->m_poArray);
9162 332 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9163 332 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9164 332 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9165 332 : (nLineSpaceBuf % nBufferDTSize) == 0)
9166 : {
9167 332 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9168 332 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9169 664 : m_anStride[l_poDS->m_iXDim] =
9170 332 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9171 332 : if (poArray->GetDimensionCount() >= 2)
9172 : {
9173 323 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9174 323 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9175 323 : m_anStride[l_poDS->m_iYDim] =
9176 323 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9177 : }
9178 332 : if (eRWFlag == GF_Read)
9179 : {
9180 652 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9181 326 : m_anStride.data(),
9182 652 : GDALExtendedDataType::Create(eBufType), pData)
9183 326 : ? CE_None
9184 326 : : CE_Failure;
9185 : }
9186 : else
9187 : {
9188 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9189 6 : m_anStride.data(),
9190 12 : GDALExtendedDataType::Create(eBufType), pData)
9191 6 : ? CE_None
9192 6 : : CE_Failure;
9193 : }
9194 : }
9195 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9196 : pData, nBufXSize, nBufYSize, eBufType,
9197 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9198 : }
9199 :
9200 : /************************************************************************/
9201 : /* GetColorInterpretation() */
9202 : /************************************************************************/
9203 :
9204 60 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9205 : {
9206 60 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9207 60 : const auto &poArray(l_poDS->m_poArray);
9208 180 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9209 60 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9210 : {
9211 6 : bool bOK = false;
9212 6 : GUInt64 nStartIndex = 0;
9213 6 : if (poArray->GetDimensionCount() == 2 &&
9214 0 : poAttr->GetDimensionCount() == 0)
9215 : {
9216 0 : bOK = true;
9217 : }
9218 6 : else if (poArray->GetDimensionCount() == 3)
9219 : {
9220 6 : uint64_t nExtraDimSamples = 1;
9221 6 : const auto &apoDims = poArray->GetDimensions();
9222 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9223 : {
9224 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9225 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9226 : }
9227 6 : if (poAttr->GetDimensionsSize() ==
9228 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9229 : {
9230 6 : bOK = true;
9231 : }
9232 6 : nStartIndex = nBand - 1;
9233 : }
9234 6 : if (bOK)
9235 : {
9236 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9237 6 : const size_t nCount = 1;
9238 6 : const GInt64 arrayStep = 1;
9239 6 : const GPtrDiff_t bufferStride = 1;
9240 6 : char *pszValue = nullptr;
9241 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9242 6 : oStringDT, &pszValue);
9243 6 : if (pszValue)
9244 : {
9245 : const auto eColorInterp =
9246 6 : GDALGetColorInterpretationByName(pszValue);
9247 6 : CPLFree(pszValue);
9248 6 : return eColorInterp;
9249 : }
9250 : }
9251 : }
9252 54 : return GCI_Undefined;
9253 : }
9254 :
9255 : /************************************************************************/
9256 : /* GDALDatasetFromArray::Create() */
9257 : /************************************************************************/
9258 :
9259 255 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9260 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9261 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9262 :
9263 : {
9264 255 : const auto nDimCount(array->GetDimensionCount());
9265 255 : if (nDimCount == 0)
9266 : {
9267 1 : CPLError(CE_Failure, CPLE_NotSupported,
9268 : "Unsupported number of dimensions");
9269 1 : return nullptr;
9270 : }
9271 507 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9272 253 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9273 : {
9274 1 : CPLError(CE_Failure, CPLE_NotSupported,
9275 : "Only arrays with numeric data types "
9276 : "can be exposed as classic GDALDataset");
9277 1 : return nullptr;
9278 : }
9279 253 : if (iXDim >= nDimCount ||
9280 238 : (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
9281 : {
9282 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9283 6 : return nullptr;
9284 : }
9285 247 : GUInt64 nTotalBands = 1;
9286 247 : const auto &dims(array->GetDimensions());
9287 817 : for (size_t i = 0; i < nDimCount; ++i)
9288 : {
9289 571 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9290 : {
9291 91 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9292 : {
9293 1 : CPLError(CE_Failure, CPLE_AppDefined,
9294 : "Too many bands. Operate on a sliced view");
9295 1 : return nullptr;
9296 : }
9297 90 : nTotalBands *= dims[i]->GetSize();
9298 : }
9299 : }
9300 :
9301 492 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9302 492 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9303 816 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9304 : {
9305 570 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9306 : {
9307 90 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9308 90 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9309 90 : ++j;
9310 : }
9311 : }
9312 :
9313 246 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9314 :
9315 : const char *pszBandMetadata =
9316 246 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9317 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9318 492 : nNewDimCount);
9319 246 : if (pszBandMetadata)
9320 : {
9321 32 : if (!poRootGroup)
9322 : {
9323 1 : CPLError(CE_Failure, CPLE_AppDefined,
9324 : "Root group should be provided when BAND_METADATA is set");
9325 24 : return nullptr;
9326 : }
9327 31 : CPLJSONDocument oDoc;
9328 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9329 : {
9330 1 : CPLError(CE_Failure, CPLE_AppDefined,
9331 : "Invalid JSON content for BAND_METADATA");
9332 1 : return nullptr;
9333 : }
9334 30 : auto oRoot = oDoc.GetRoot();
9335 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9336 : {
9337 1 : CPLError(CE_Failure, CPLE_AppDefined,
9338 : "Value of BAND_METADATA should be an array");
9339 1 : return nullptr;
9340 : }
9341 :
9342 29 : auto oArray = oRoot.ToArray();
9343 38 : for (int j = 0; j < oArray.Size(); ++j)
9344 : {
9345 30 : const auto oJsonItem = oArray[j];
9346 30 : MetadataItem oItem;
9347 30 : size_t iExtraDimIdx = 0;
9348 :
9349 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9350 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9351 0 : std::shared_ptr<GDALMDArray> poArray;
9352 0 : std::shared_ptr<GDALAttribute> poAttribute;
9353 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9354 : {
9355 1 : CPLError(CE_Failure, CPLE_AppDefined,
9356 : "BAND_METADATA[%d][\"array\"] or "
9357 : "BAND_METADATA[%d][\"attribute\"] is missing",
9358 : j, j);
9359 1 : return nullptr;
9360 : }
9361 48 : else if (!osBandArrayFullname.empty() &&
9362 19 : !osBandAttributeName.empty())
9363 : {
9364 1 : CPLError(
9365 : CE_Failure, CPLE_AppDefined,
9366 : "BAND_METADATA[%d][\"array\"] and "
9367 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9368 : j, j);
9369 1 : return nullptr;
9370 : }
9371 28 : else if (!osBandArrayFullname.empty())
9372 : {
9373 : poArray =
9374 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9375 18 : if (!poArray)
9376 : {
9377 1 : CPLError(CE_Failure, CPLE_AppDefined,
9378 : "Array %s cannot be found",
9379 : osBandArrayFullname.c_str());
9380 3 : return nullptr;
9381 : }
9382 17 : if (poArray->GetDimensionCount() != 1)
9383 : {
9384 1 : CPLError(CE_Failure, CPLE_AppDefined,
9385 : "Array %s is not a 1D array",
9386 : osBandArrayFullname.c_str());
9387 1 : return nullptr;
9388 : }
9389 : const auto &osAuxArrayDimName =
9390 16 : poArray->GetDimensions()[0]->GetName();
9391 : auto oIter =
9392 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9393 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9394 : {
9395 1 : CPLError(
9396 : CE_Failure, CPLE_AppDefined,
9397 : "Dimension %s of array %s is not a non-X/Y dimension "
9398 : "of array %s",
9399 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9400 1 : array->GetName().c_str());
9401 1 : return nullptr;
9402 : }
9403 15 : iExtraDimIdx = oIter->second;
9404 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9405 : }
9406 : else
9407 : {
9408 10 : CPLAssert(!osBandAttributeName.empty());
9409 10 : poAttribute = !osBandAttributeName.empty() &&
9410 10 : osBandAttributeName[0] == '/'
9411 24 : ? poRootGroup->OpenAttributeFromFullname(
9412 : osBandAttributeName)
9413 10 : : array->GetAttribute(osBandAttributeName);
9414 10 : if (!poAttribute)
9415 : {
9416 2 : CPLError(CE_Failure, CPLE_AppDefined,
9417 : "Attribute %s cannot be found",
9418 : osBandAttributeName.c_str());
9419 8 : return nullptr;
9420 : }
9421 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9422 8 : if (aoAttrDims.size() != 1)
9423 : {
9424 4 : CPLError(CE_Failure, CPLE_AppDefined,
9425 : "Attribute %s is not a 1D array",
9426 : osBandAttributeName.c_str());
9427 4 : return nullptr;
9428 : }
9429 4 : bool found = false;
9430 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9431 : {
9432 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9433 5 : ->GetSize() == aoAttrDims[0])
9434 : {
9435 4 : if (found)
9436 : {
9437 2 : CPLError(CE_Failure, CPLE_AppDefined,
9438 : "Several dimensions of %s have the same "
9439 : "size as attribute %s. Cannot infer which "
9440 : "one to bind to!",
9441 1 : array->GetName().c_str(),
9442 : osBandAttributeName.c_str());
9443 1 : return nullptr;
9444 : }
9445 3 : found = true;
9446 3 : iExtraDimIdx = iter.second;
9447 : }
9448 : }
9449 3 : if (!found)
9450 : {
9451 2 : CPLError(
9452 : CE_Failure, CPLE_AppDefined,
9453 : "No dimension of %s has the same size as attribute %s",
9454 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9455 1 : return nullptr;
9456 : }
9457 : }
9458 :
9459 17 : oItem.osName = oJsonItem.GetString("item_name");
9460 17 : if (oItem.osName.empty())
9461 : {
9462 1 : CPLError(CE_Failure, CPLE_AppDefined,
9463 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9464 1 : return nullptr;
9465 : }
9466 :
9467 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9468 :
9469 : // Check correctness of definition
9470 16 : bool bFirstNumericFormatter = true;
9471 16 : std::string osModDefinition;
9472 16 : bool bDefinitionUsesPctForG = false;
9473 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9474 : {
9475 70 : if (osDefinition[k] == '%')
9476 : {
9477 15 : osModDefinition += osDefinition[k];
9478 15 : if (k + 1 == osDefinition.size())
9479 : {
9480 1 : CPLError(CE_Failure, CPLE_AppDefined,
9481 : "Value of "
9482 : "BAND_METADATA[%d][\"item_value\"] = "
9483 : "%s is invalid at offset %d",
9484 : j, osDefinition.c_str(), int(k));
9485 1 : return nullptr;
9486 : }
9487 14 : ++k;
9488 14 : if (osDefinition[k] == '%')
9489 : {
9490 1 : osModDefinition += osDefinition[k];
9491 1 : continue;
9492 : }
9493 13 : if (!bFirstNumericFormatter)
9494 : {
9495 1 : CPLError(CE_Failure, CPLE_AppDefined,
9496 : "Value of "
9497 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9498 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9499 : "formatters should be specified at most once",
9500 : j, osDefinition.c_str(), int(k));
9501 1 : return nullptr;
9502 : }
9503 12 : bFirstNumericFormatter = false;
9504 19 : for (; k < osDefinition.size(); ++k)
9505 : {
9506 19 : osModDefinition += osDefinition[k];
9507 38 : if (!((osDefinition[k] >= '0' &&
9508 16 : osDefinition[k] <= '9') ||
9509 15 : osDefinition[k] == '.'))
9510 12 : break;
9511 : }
9512 24 : if (k == osDefinition.size() ||
9513 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9514 5 : osDefinition[k] != 's'))
9515 : {
9516 1 : CPLError(CE_Failure, CPLE_AppDefined,
9517 : "Value of "
9518 : "BAND_METADATA[%d][\"item_value\"] = "
9519 : "%s is invalid at offset %d: only "
9520 : "%%[x][.y]f|g or %%s formatters are accepted",
9521 : j, osDefinition.c_str(), int(k));
9522 1 : return nullptr;
9523 : }
9524 11 : bDefinitionUsesPctForG =
9525 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9526 11 : if (bDefinitionUsesPctForG)
9527 : {
9528 12 : if (poArray &&
9529 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9530 : {
9531 1 : CPLError(CE_Failure, CPLE_AppDefined,
9532 : "Data type of %s array is not numeric",
9533 1 : poArray->GetName().c_str());
9534 1 : return nullptr;
9535 : }
9536 8 : else if (poAttribute &&
9537 2 : poAttribute->GetDataType().GetClass() !=
9538 6 : GEDTC_NUMERIC)
9539 : {
9540 0 : CPLError(CE_Failure, CPLE_AppDefined,
9541 : "Data type of %s attribute is not numeric",
9542 0 : poAttribute->GetFullName().c_str());
9543 0 : return nullptr;
9544 : }
9545 : }
9546 : }
9547 62 : else if (osDefinition[k] == '$' &&
9548 62 : k + 1 < osDefinition.size() &&
9549 7 : osDefinition[k + 1] == '{')
9550 : {
9551 7 : const auto nPos = osDefinition.find('}', k);
9552 7 : if (nPos == std::string::npos)
9553 : {
9554 1 : CPLError(CE_Failure, CPLE_AppDefined,
9555 : "Value of "
9556 : "BAND_METADATA[%d][\"item_value\"] = "
9557 : "%s is invalid at offset %d",
9558 : j, osDefinition.c_str(), int(k));
9559 3 : return nullptr;
9560 : }
9561 : const auto osAttrName =
9562 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9563 0 : std::shared_ptr<GDALAttribute> poAttr;
9564 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9565 : {
9566 4 : poAttr = poArray->GetAttribute(osAttrName);
9567 4 : if (!poAttr)
9568 : {
9569 1 : CPLError(
9570 : CE_Failure, CPLE_AppDefined,
9571 : "Value of "
9572 : "BAND_METADATA[%d][\"item_value\"] = "
9573 : "%s is invalid: %s is not an attribute of %s",
9574 : j, osDefinition.c_str(), osAttrName.c_str(),
9575 1 : poArray->GetName().c_str());
9576 1 : return nullptr;
9577 : }
9578 : }
9579 : else
9580 : {
9581 : poAttr =
9582 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9583 2 : if (!poAttr)
9584 : {
9585 1 : CPLError(CE_Failure, CPLE_AppDefined,
9586 : "Value of "
9587 : "BAND_METADATA[%d][\"item_value\"] = "
9588 : "%s is invalid: %s is not an attribute",
9589 : j, osDefinition.c_str(),
9590 : osAttrName.c_str());
9591 1 : return nullptr;
9592 : }
9593 : }
9594 4 : k = nPos;
9595 4 : const char *pszValue = poAttr->ReadAsString();
9596 4 : if (!pszValue)
9597 : {
9598 0 : CPLError(CE_Failure, CPLE_AppDefined,
9599 : "Cannot get value of attribute %s as a "
9600 : "string",
9601 : osAttrName.c_str());
9602 0 : return nullptr;
9603 : }
9604 4 : osModDefinition += pszValue;
9605 : }
9606 : else
9607 : {
9608 48 : osModDefinition += osDefinition[k];
9609 : }
9610 : }
9611 :
9612 9 : if (poArray)
9613 8 : oItem.poArray = std::move(poArray);
9614 : else
9615 1 : oItem.poArray = std::move(poAttribute);
9616 9 : oItem.osDefinition = std::move(osModDefinition);
9617 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9618 :
9619 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9620 9 : std::move(oItem));
9621 : }
9622 : }
9623 :
9624 444 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9625 : const char *pszBandImageryMetadata =
9626 222 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9627 222 : if (pszBandImageryMetadata)
9628 : {
9629 20 : if (!poRootGroup)
9630 : {
9631 1 : CPLError(CE_Failure, CPLE_AppDefined,
9632 : "Root group should be provided when BAND_IMAGERY_METADATA "
9633 : "is set");
9634 17 : return nullptr;
9635 : }
9636 19 : CPLJSONDocument oDoc;
9637 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9638 : {
9639 1 : CPLError(CE_Failure, CPLE_AppDefined,
9640 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9641 1 : return nullptr;
9642 : }
9643 18 : auto oRoot = oDoc.GetRoot();
9644 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9645 : {
9646 1 : CPLError(CE_Failure, CPLE_AppDefined,
9647 : "Value of BAND_IMAGERY_METADATA should be an object");
9648 1 : return nullptr;
9649 : }
9650 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9651 : {
9652 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9653 20 : oJsonItem.GetName() == "FWHM_UM")
9654 : {
9655 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9656 : const auto osBandAttributeName =
9657 34 : oJsonItem.GetString("attribute");
9658 0 : std::shared_ptr<GDALMDArray> poArray;
9659 0 : std::shared_ptr<GDALAttribute> poAttribute;
9660 17 : size_t iExtraDimIdx = 0;
9661 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9662 : {
9663 2 : CPLError(CE_Failure, CPLE_AppDefined,
9664 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9665 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9666 : "missing",
9667 2 : oJsonItem.GetName().c_str(),
9668 2 : oJsonItem.GetName().c_str());
9669 1 : return nullptr;
9670 : }
9671 25 : else if (!osBandArrayFullname.empty() &&
9672 9 : !osBandAttributeName.empty())
9673 : {
9674 2 : CPLError(CE_Failure, CPLE_AppDefined,
9675 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9676 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9677 : "mutually exclusive",
9678 2 : oJsonItem.GetName().c_str(),
9679 2 : oJsonItem.GetName().c_str());
9680 1 : return nullptr;
9681 : }
9682 15 : else if (!osBandArrayFullname.empty())
9683 : {
9684 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9685 8 : osBandArrayFullname);
9686 8 : if (!poArray)
9687 : {
9688 1 : CPLError(CE_Failure, CPLE_AppDefined,
9689 : "Array %s cannot be found",
9690 : osBandArrayFullname.c_str());
9691 3 : return nullptr;
9692 : }
9693 7 : if (poArray->GetDimensionCount() != 1)
9694 : {
9695 1 : CPLError(CE_Failure, CPLE_AppDefined,
9696 : "Array %s is not a 1D array",
9697 : osBandArrayFullname.c_str());
9698 1 : return nullptr;
9699 : }
9700 : const auto &osAuxArrayDimName =
9701 6 : poArray->GetDimensions()[0]->GetName();
9702 : auto oIter =
9703 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9704 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9705 : {
9706 1 : CPLError(CE_Failure, CPLE_AppDefined,
9707 : "Dimension \"%s\" of array \"%s\" is not a "
9708 : "non-X/Y dimension of array \"%s\"",
9709 : osAuxArrayDimName.c_str(),
9710 : osBandArrayFullname.c_str(),
9711 1 : array->GetName().c_str());
9712 1 : return nullptr;
9713 : }
9714 5 : iExtraDimIdx = oIter->second;
9715 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9716 : }
9717 : else
9718 : {
9719 : poAttribute =
9720 7 : !osBandAttributeName.empty() &&
9721 7 : osBandAttributeName[0] == '/'
9722 16 : ? poRootGroup->OpenAttributeFromFullname(
9723 : osBandAttributeName)
9724 7 : : array->GetAttribute(osBandAttributeName);
9725 7 : if (!poAttribute)
9726 : {
9727 2 : CPLError(CE_Failure, CPLE_AppDefined,
9728 : "Attribute %s cannot be found",
9729 : osBandAttributeName.c_str());
9730 6 : return nullptr;
9731 : }
9732 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9733 5 : if (aoAttrDims.size() != 1)
9734 : {
9735 2 : CPLError(CE_Failure, CPLE_AppDefined,
9736 : "Attribute %s is not a 1D array",
9737 : osBandAttributeName.c_str());
9738 2 : return nullptr;
9739 : }
9740 3 : bool found = false;
9741 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9742 : {
9743 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9744 4 : ->GetSize() == aoAttrDims[0])
9745 : {
9746 3 : if (found)
9747 : {
9748 2 : CPLError(CE_Failure, CPLE_AppDefined,
9749 : "Several dimensions of %s have the "
9750 : "same size as attribute %s. Cannot "
9751 : "infer which one to bind to!",
9752 1 : array->GetName().c_str(),
9753 : osBandAttributeName.c_str());
9754 1 : return nullptr;
9755 : }
9756 2 : found = true;
9757 2 : iExtraDimIdx = iter.second;
9758 : }
9759 : }
9760 2 : if (!found)
9761 : {
9762 2 : CPLError(CE_Failure, CPLE_AppDefined,
9763 : "No dimension of %s has the same size as "
9764 : "attribute %s",
9765 1 : array->GetName().c_str(),
9766 : osBandAttributeName.c_str());
9767 1 : return nullptr;
9768 : }
9769 : }
9770 :
9771 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9772 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9773 : {
9774 4 : if (osUnit.back() != '}')
9775 : {
9776 2 : CPLError(CE_Failure, CPLE_AppDefined,
9777 : "Value of "
9778 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9779 : "%s is invalid",
9780 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9781 2 : return nullptr;
9782 : }
9783 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9784 0 : std::shared_ptr<GDALAttribute> poAttr;
9785 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9786 : {
9787 2 : poAttr = poArray->GetAttribute(osAttrName);
9788 2 : if (!poAttr)
9789 : {
9790 2 : CPLError(
9791 : CE_Failure, CPLE_AppDefined,
9792 : "Value of "
9793 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9794 : "%s is invalid: %s is not an attribute of %s",
9795 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9796 : osAttrName.c_str(),
9797 : osBandArrayFullname.c_str());
9798 1 : return nullptr;
9799 : }
9800 : }
9801 : else
9802 : {
9803 : poAttr =
9804 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9805 1 : if (!poAttr)
9806 : {
9807 0 : CPLError(
9808 : CE_Failure, CPLE_AppDefined,
9809 : "Value of "
9810 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9811 : "%s is invalid: %s is not an attribute",
9812 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9813 : osAttrName.c_str());
9814 0 : return nullptr;
9815 : }
9816 : }
9817 :
9818 2 : const char *pszValue = poAttr->ReadAsString();
9819 2 : if (!pszValue)
9820 : {
9821 0 : CPLError(CE_Failure, CPLE_AppDefined,
9822 : "Cannot get value of attribute %s of %s as a "
9823 : "string",
9824 : osAttrName.c_str(),
9825 : osBandArrayFullname.c_str());
9826 0 : return nullptr;
9827 : }
9828 2 : osUnit = pszValue;
9829 : }
9830 4 : double dfConvToUM = 1.0;
9831 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9832 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9833 3 : osUnit == "nanometers")
9834 : {
9835 1 : dfConvToUM = 1e-3;
9836 : }
9837 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9838 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9839 1 : osUnit == "micrometers"))
9840 : {
9841 2 : CPLError(CE_Failure, CPLE_AppDefined,
9842 : "Unhandled value for "
9843 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9844 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9845 1 : return nullptr;
9846 : }
9847 :
9848 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9849 :
9850 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9851 3 : if (poArray)
9852 2 : abstractArray = std::move(poArray);
9853 : else
9854 1 : abstractArray = std::move(poAttribute);
9855 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9856 : {
9857 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9858 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9859 : }
9860 : else
9861 : {
9862 1 : item.poFWHMArray = std::move(abstractArray);
9863 1 : item.dfFWHMToMicrometer = dfConvToUM;
9864 : }
9865 : }
9866 : else
9867 : {
9868 1 : CPLError(CE_Warning, CPLE_AppDefined,
9869 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9870 2 : oJsonItem.GetName().c_str());
9871 : }
9872 : }
9873 : }
9874 :
9875 410 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9876 :
9877 205 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9878 :
9879 205 : poDS->nRasterYSize =
9880 205 : nDimCount < 2 ? 1
9881 193 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9882 193 : dims[iYDim]->GetSize()));
9883 410 : poDS->nRasterXSize = static_cast<int>(
9884 205 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9885 :
9886 410 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9887 410 : std::vector<GUInt64> anStackIters(nDimCount);
9888 410 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9889 650 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9890 : {
9891 445 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9892 : {
9893 47 : anMapNewToOld[j] = i;
9894 47 : j++;
9895 : }
9896 : }
9897 :
9898 205 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9899 :
9900 410 : const auto attrs(array->GetAttributes());
9901 314 : for (const auto &attr : attrs)
9902 : {
9903 109 : if (attr->GetName() != "COLOR_INTERPRETATION")
9904 : {
9905 200 : auto stringArray = attr->ReadAsStringArray();
9906 200 : std::string val;
9907 100 : if (stringArray.size() > 1)
9908 : {
9909 44 : val += '{';
9910 : }
9911 448 : for (int i = 0; i < stringArray.size(); ++i)
9912 : {
9913 348 : if (i > 0)
9914 248 : val += ',';
9915 348 : val += stringArray[i];
9916 : }
9917 100 : if (stringArray.size() > 1)
9918 : {
9919 44 : val += '}';
9920 : }
9921 100 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9922 : }
9923 : }
9924 :
9925 205 : const char *pszDelay = CSLFetchNameValueDef(
9926 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9927 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9928 : const double dfDelay =
9929 205 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9930 205 : const auto nStartTime = time(nullptr);
9931 205 : bool bHasWarned = false;
9932 : // Instantiate bands by iterating over non-XY variables
9933 205 : size_t iDim = 0;
9934 205 : int nCurBand = 1;
9935 328 : lbl_next_depth:
9936 328 : if (iDim < nNewDimCount)
9937 : {
9938 49 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9939 49 : anOtherDimCoord[iDim] = 0;
9940 : while (true)
9941 : {
9942 123 : ++iDim;
9943 123 : goto lbl_next_depth;
9944 123 : lbl_return_to_caller:
9945 123 : --iDim;
9946 123 : --anStackIters[iDim];
9947 123 : if (anStackIters[iDim] == 0)
9948 49 : break;
9949 74 : ++anOtherDimCoord[iDim];
9950 : }
9951 : }
9952 : else
9953 : {
9954 558 : poDS->SetBand(nCurBand,
9955 : new GDALRasterBandFromArray(
9956 279 : poDS.get(), anOtherDimCoord,
9957 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9958 279 : dfDelay, nStartTime, bHasWarned));
9959 279 : ++nCurBand;
9960 : }
9961 328 : if (iDim > 0)
9962 123 : goto lbl_return_to_caller;
9963 :
9964 205 : if (!array->GetFilename().empty())
9965 : {
9966 181 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9967 : std::string osDerivedDatasetName(
9968 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9969 362 : int(iYDim), array->GetFullName().c_str()));
9970 181 : if (!array->GetContext().empty())
9971 : {
9972 2 : osDerivedDatasetName += " with context ";
9973 2 : osDerivedDatasetName += array->GetContext();
9974 : }
9975 181 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9976 181 : poDS->TryLoadXML();
9977 :
9978 2 : for (const auto &[pszKey, pszValue] :
9979 : cpl::IterateNameValue(static_cast<CSLConstList>(
9980 183 : poDS->GDALPamDataset::GetMetadata())))
9981 : {
9982 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9983 : }
9984 : }
9985 :
9986 205 : return poDS.release();
9987 : }
9988 :
9989 : /************************************************************************/
9990 : /* AsClassicDataset() */
9991 : /************************************************************************/
9992 :
9993 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9994 : *
9995 : * In the case of > 2D arrays, additional dimensions will be represented as
9996 : * raster bands.
9997 : *
9998 : * The "reverse" method is GDALRasterBand::AsMDArray().
9999 : *
10000 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10001 : *
10002 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10003 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10004 : * Ignored if the dimension count is 1.
10005 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10006 : * and BAND_IMAGERY_METADATA option.
10007 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10008 : * nullptr. Current supported options are:
10009 : * <ul>
10010 : * <li>BAND_METADATA: JSON serialized array defining which
10011 : * arrays of the poRootGroup, indexed by non-X and Y
10012 : * dimensions, should be mapped as band metadata items.
10013 : * Each array item should be an object with the
10014 : * following members:
10015 : * - "array": full name of a band parameter array.
10016 : * Such array must be a one
10017 : * dimensional array, and its dimension must be one of
10018 : * the dimensions of the array on which the method is
10019 : * called (excluding the X and Y dimensons).
10020 : * - "attribute": name relative to *this array or full
10021 : * name of a single dimension numeric array whose size
10022 : * must be one of the dimensions of *this array
10023 : * (excluding the X and Y dimensons).
10024 : * "array" and "attribute" are mutually exclusive.
10025 : * - "item_name": band metadata item name
10026 : * - "item_value": (optional) String, where "%[x][.y]f",
10027 : * "%[x][.y]g" or "%s" printf-like formatting can be
10028 : * used to format the corresponding value of the
10029 : * parameter array. The percentage character should be
10030 : * repeated: "%%"
10031 : * "${attribute_name}" can also be used to include the
10032 : * value of an attribute for "array" when set and if
10033 : * not starting with '/'. Otherwise if starting with
10034 : * '/', it is the full path to the attribute.
10035 : *
10036 : * If "item_value" is not provided, a default formatting
10037 : * of the value will be applied.
10038 : *
10039 : * Example:
10040 : * [
10041 : * {
10042 : * "array": "/sensor_band_parameters/wavelengths",
10043 : * "item_name": "WAVELENGTH",
10044 : * "item_value": "%.1f ${units}"
10045 : * },
10046 : * {
10047 : * "array": "/sensor_band_parameters/fwhm",
10048 : * "item_name": "FWHM"
10049 : * },
10050 : * {
10051 : * "array": "/sensor_band_parameters/fwhm",
10052 : * "item_name": "FWHM_UNIT",
10053 : * "item_value": "${units}"
10054 : * }
10055 : * ]
10056 : *
10057 : * Example for Planet Labs Tanager radiance products:
10058 : * [
10059 : * {
10060 : * "attribute": "center_wavelengths",
10061 : * "item_name": "WAVELENGTH",
10062 : * "item_value": "%.1f ${center_wavelengths_units}"
10063 : * }
10064 : * ]
10065 : *
10066 : * </li>
10067 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10068 : * JSON serialized object defining which arrays of the
10069 : * poRootGroup, indexed by non-X and Y dimensions,
10070 : * should be mapped as band metadata items in the
10071 : * band IMAGERY domain.
10072 : * The object currently accepts 2 members:
10073 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10074 : * micrometers.
10075 : * - "FWHM_UM": Full-width half-maximum
10076 : * in micrometers.
10077 : * The value of each member should be an object with the
10078 : * following members:
10079 : * - "array": full name of a band parameter array.
10080 : * Such array must be a one dimensional array, and its
10081 : * dimension must be one of the dimensions of the
10082 : * array on which the method is called
10083 : * (excluding the X and Y dimensons).
10084 : * - "attribute": name relative to *this array or full
10085 : * name of a single dimension numeric array whose size
10086 : * must be one of the dimensions of *this array
10087 : * (excluding the X and Y dimensons).
10088 : * "array" and "attribute" are mutually exclusive,
10089 : * and one of them is required.
10090 : * - "unit": (optional) unit of the values pointed in
10091 : * the above array.
10092 : * Can be a literal string or a string of the form
10093 : * "${attribute_name}" to point to an attribute for
10094 : * "array" when set and if no starting
10095 : * with '/'. Otherwise if starting with '/', it is
10096 : * the full path to the attribute.
10097 : * Accepted values are "um", "micrometer"
10098 : * (with UK vs US spelling, singular or plural), "nm",
10099 : * "nanometer" (with UK vs US spelling, singular or
10100 : * plural)
10101 : * If not provided, micrometer is assumed.
10102 : *
10103 : * Example for EMIT datasets:
10104 : * {
10105 : * "CENTRAL_WAVELENGTH_UM": {
10106 : * "array": "/sensor_band_parameters/wavelengths",
10107 : * "unit": "${units}"
10108 : * },
10109 : * "FWHM_UM": {
10110 : * "array": "/sensor_band_parameters/fwhm",
10111 : * "unit": "${units}"
10112 : * }
10113 : * }
10114 : *
10115 : * Example for Planet Labs Tanager radiance products:
10116 : * {
10117 : * "CENTRAL_WAVELENGTH_UM": {
10118 : * "attribute": "center_wavelengths",
10119 : * "unit": "${center_wavelengths_units}"
10120 : * },
10121 : * "FWHM_UM": {
10122 : * "attribute": "fwhm",
10123 : * "unit": "${fwhm_units}"
10124 : * }
10125 : * }
10126 : *
10127 : * </li>
10128 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10129 : * seconds allowed to set the DIM_{dimname}_VALUE band
10130 : * metadata items from the indexing variable of the
10131 : * dimensions.
10132 : * Default value is 5. 'unlimited' can be used to mean
10133 : * unlimited delay. Can also be defined globally with
10134 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10135 : * option.</li>
10136 : * </ul>
10137 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10138 : */
10139 : GDALDataset *
10140 255 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10141 : const std::shared_ptr<GDALGroup> &poRootGroup,
10142 : CSLConstList papszOptions) const
10143 : {
10144 510 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10145 255 : if (!self)
10146 : {
10147 0 : CPLError(CE_Failure, CPLE_AppDefined,
10148 : "Driver implementation issue: m_pSelf not set !");
10149 0 : return nullptr;
10150 : }
10151 255 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10152 255 : papszOptions);
10153 : }
10154 :
10155 : /************************************************************************/
10156 : /* GetStatistics() */
10157 : /************************************************************************/
10158 :
10159 : /**
10160 : * \brief Fetch statistics.
10161 : *
10162 : * Returns the minimum, maximum, mean and standard deviation of all
10163 : * pixel values in this array.
10164 : *
10165 : * If bForce is FALSE results will only be returned if it can be done
10166 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10167 : * results cannot be returned efficiently, the method will return CE_Warning
10168 : * but no warning will have been issued. This is a non-standard use of
10169 : * the CE_Warning return value to indicate "nothing done".
10170 : *
10171 : * When cached statistics are not available, and bForce is TRUE,
10172 : * ComputeStatistics() is called.
10173 : *
10174 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10175 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10176 : * after the first request.
10177 : *
10178 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10179 : *
10180 : * This method is the same as the C function GDALMDArrayGetStatistics().
10181 : *
10182 : * @param bApproxOK Currently ignored. In the future, should be set to true
10183 : * if statistics on the whole array are wished, or to false if a subset of it
10184 : * may be used.
10185 : *
10186 : * @param bForce If false statistics will only be returned if it can
10187 : * be done without rescanning the image.
10188 : *
10189 : * @param pdfMin Location into which to load image minimum (may be NULL).
10190 : *
10191 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10192 : *
10193 : * @param pdfMean Location into which to load image mean (may be NULL).
10194 : *
10195 : * @param pdfStdDev Location into which to load image standard deviation
10196 : * (may be NULL).
10197 : *
10198 : * @param pnValidCount Number of samples whose value is different from the
10199 : * nodata value. (may be NULL)
10200 : *
10201 : * @param pfnProgress a function to call to report progress, or NULL.
10202 : *
10203 : * @param pProgressData application data to pass to the progress function.
10204 : *
10205 : * @return CE_None on success, CE_Warning if no values returned,
10206 : * CE_Failure if an error occurs.
10207 : *
10208 : * @since GDAL 3.2
10209 : */
10210 :
10211 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10212 : double *pdfMax, double *pdfMean,
10213 : double *pdfStdDev, GUInt64 *pnValidCount,
10214 : GDALProgressFunc pfnProgress,
10215 : void *pProgressData)
10216 : {
10217 10 : if (!bForce)
10218 1 : return CE_Warning;
10219 :
10220 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10221 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10222 9 : ? CE_None
10223 9 : : CE_Failure;
10224 : }
10225 :
10226 : /************************************************************************/
10227 : /* ComputeStatistics() */
10228 : /************************************************************************/
10229 :
10230 : /**
10231 : * \brief Compute statistics.
10232 : *
10233 : * Returns the minimum, maximum, mean and standard deviation of all
10234 : * pixel values in this array.
10235 : *
10236 : * Pixels taken into account in statistics are those whose mask value
10237 : * (as determined by GetMask()) is non-zero.
10238 : *
10239 : * Once computed, the statistics will generally be "set" back on the
10240 : * owing dataset.
10241 : *
10242 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10243 : *
10244 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10245 : * and GDALMDArrayComputeStatisticsEx().
10246 : *
10247 : * @param bApproxOK Currently ignored. In the future, should be set to true
10248 : * if statistics on the whole array are wished, or to false if a subset of it
10249 : * may be used.
10250 : *
10251 : * @param pdfMin Location into which to load image minimum (may be NULL).
10252 : *
10253 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10254 : *
10255 : * @param pdfMean Location into which to load image mean (may be NULL).
10256 : *
10257 : * @param pdfStdDev Location into which to load image standard deviation
10258 : * (may be NULL).
10259 : *
10260 : * @param pnValidCount Number of samples whose value is different from the
10261 : * nodata value. (may be NULL)
10262 : *
10263 : * @param pfnProgress a function to call to report progress, or NULL.
10264 : *
10265 : * @param pProgressData application data to pass to the progress function.
10266 : *
10267 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10268 : * Options are driver specific. For now the netCDF and Zarr
10269 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10270 : * to add or update the actual_range attribute with the
10271 : * computed min/max, only if done on the full array, in non
10272 : * approximate mode, and the dataset is opened in update
10273 : * mode.
10274 : *
10275 : * @return true on success
10276 : *
10277 : * @since GDAL 3.2
10278 : */
10279 :
10280 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10281 : double *pdfMax, double *pdfMean,
10282 : double *pdfStdDev, GUInt64 *pnValidCount,
10283 : GDALProgressFunc pfnProgress,
10284 : void *pProgressData,
10285 : CSLConstList papszOptions)
10286 : {
10287 : struct StatsPerChunkType
10288 : {
10289 : const GDALMDArray *array = nullptr;
10290 : std::shared_ptr<GDALMDArray> poMask{};
10291 : double dfMin = cpl::NumericLimits<double>::max();
10292 : double dfMax = -cpl::NumericLimits<double>::max();
10293 : double dfMean = 0.0;
10294 : double dfM2 = 0.0;
10295 : GUInt64 nValidCount = 0;
10296 : std::vector<GByte> abyData{};
10297 : std::vector<double> adfData{};
10298 : std::vector<GByte> abyMaskData{};
10299 : GDALProgressFunc pfnProgress = nullptr;
10300 : void *pProgressData = nullptr;
10301 : };
10302 :
10303 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10304 : const GUInt64 *chunkArrayStartIdx,
10305 : const size_t *chunkCount, GUInt64 iCurChunk,
10306 : GUInt64 nChunkCount, void *pUserData)
10307 : {
10308 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10309 13 : const GDALMDArray *array = data->array;
10310 13 : const GDALMDArray *poMask = data->poMask.get();
10311 13 : const size_t nDims = array->GetDimensionCount();
10312 13 : size_t nVals = 1;
10313 34 : for (size_t i = 0; i < nDims; i++)
10314 21 : nVals *= chunkCount[i];
10315 :
10316 : // Get mask
10317 13 : data->abyMaskData.resize(nVals);
10318 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10319 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10320 : {
10321 0 : return false;
10322 : }
10323 :
10324 : // Get data
10325 13 : const auto &oType = array->GetDataType();
10326 13 : if (oType.GetNumericDataType() == GDT_Float64)
10327 : {
10328 6 : data->adfData.resize(nVals);
10329 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10330 6 : oType, &data->adfData[0]))
10331 : {
10332 0 : return false;
10333 : }
10334 : }
10335 : else
10336 : {
10337 7 : data->abyData.resize(nVals * oType.GetSize());
10338 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10339 7 : oType, &data->abyData[0]))
10340 : {
10341 0 : return false;
10342 : }
10343 7 : data->adfData.resize(nVals);
10344 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10345 7 : static_cast<int>(oType.GetSize()),
10346 7 : &data->adfData[0], GDT_Float64,
10347 : static_cast<int>(sizeof(double)),
10348 : static_cast<GPtrDiff_t>(nVals));
10349 : }
10350 912 : for (size_t i = 0; i < nVals; i++)
10351 : {
10352 899 : if (data->abyMaskData[i])
10353 : {
10354 894 : const double dfValue = data->adfData[i];
10355 894 : data->dfMin = std::min(data->dfMin, dfValue);
10356 894 : data->dfMax = std::max(data->dfMax, dfValue);
10357 894 : data->nValidCount++;
10358 894 : const double dfDelta = dfValue - data->dfMean;
10359 894 : data->dfMean += dfDelta / data->nValidCount;
10360 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10361 : }
10362 : }
10363 13 : if (data->pfnProgress &&
10364 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10365 : "", data->pProgressData))
10366 : {
10367 0 : return false;
10368 : }
10369 13 : return true;
10370 : };
10371 :
10372 13 : const auto &oType = GetDataType();
10373 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10374 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10375 : {
10376 0 : CPLError(
10377 : CE_Failure, CPLE_NotSupported,
10378 : "Statistics can only be computed on non-complex numeric data type");
10379 0 : return false;
10380 : }
10381 :
10382 13 : const size_t nDims = GetDimensionCount();
10383 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10384 26 : std::vector<GUInt64> count(nDims);
10385 13 : const auto &poDims = GetDimensions();
10386 34 : for (size_t i = 0; i < nDims; i++)
10387 : {
10388 21 : count[i] = poDims[i]->GetSize();
10389 : }
10390 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10391 : const size_t nMaxChunkSize =
10392 : pszSwathSize
10393 13 : ? static_cast<size_t>(
10394 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10395 0 : CPLAtoGIntBig(pszSwathSize)))
10396 : : static_cast<size_t>(
10397 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10398 13 : GDALGetCacheMax64() / 4));
10399 26 : StatsPerChunkType sData;
10400 13 : sData.array = this;
10401 13 : sData.poMask = GetMask(nullptr);
10402 13 : if (sData.poMask == nullptr)
10403 : {
10404 0 : return false;
10405 : }
10406 13 : sData.pfnProgress = pfnProgress;
10407 13 : sData.pProgressData = pProgressData;
10408 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10409 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10410 13 : PerChunkFunc, &sData))
10411 : {
10412 0 : return false;
10413 : }
10414 :
10415 13 : if (pdfMin)
10416 13 : *pdfMin = sData.dfMin;
10417 :
10418 13 : if (pdfMax)
10419 13 : *pdfMax = sData.dfMax;
10420 :
10421 13 : if (pdfMean)
10422 11 : *pdfMean = sData.dfMean;
10423 :
10424 : const double dfStdDev =
10425 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10426 13 : if (pdfStdDev)
10427 11 : *pdfStdDev = dfStdDev;
10428 :
10429 13 : if (pnValidCount)
10430 11 : *pnValidCount = sData.nValidCount;
10431 :
10432 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10433 13 : sData.nValidCount, papszOptions);
10434 :
10435 13 : return true;
10436 : }
10437 :
10438 : /************************************************************************/
10439 : /* SetStatistics() */
10440 : /************************************************************************/
10441 : //! @cond Doxygen_Suppress
10442 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10443 : double /* dfMax */, double /* dfMean */,
10444 : double /* dfStdDev */,
10445 : GUInt64 /* nValidCount */,
10446 : CSLConstList /* papszOptions */)
10447 : {
10448 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10449 5 : return false;
10450 : }
10451 :
10452 : //! @endcond
10453 :
10454 : /************************************************************************/
10455 : /* ClearStatistics() */
10456 : /************************************************************************/
10457 :
10458 : /**
10459 : * \brief Clear statistics.
10460 : *
10461 : * @since GDAL 3.4
10462 : */
10463 0 : void GDALMDArray::ClearStatistics()
10464 : {
10465 0 : }
10466 :
10467 : /************************************************************************/
10468 : /* GetCoordinateVariables() */
10469 : /************************************************************************/
10470 :
10471 : /**
10472 : * \brief Return coordinate variables.
10473 : *
10474 : * Coordinate variables are an alternate way of indexing an array that can
10475 : * be sometimes used. For example, an array collected through remote sensing
10476 : * might be indexed by (scanline, pixel). But there can be
10477 : * a longitude and latitude arrays alongside that are also both indexed by
10478 : * (scanline, pixel), and are referenced from operational arrays for
10479 : * reprojection purposes.
10480 : *
10481 : * For netCDF, this will return the arrays referenced by the "coordinates"
10482 : * attribute.
10483 : *
10484 : * This method is the same as the C function
10485 : * GDALMDArrayGetCoordinateVariables().
10486 : *
10487 : * @return a vector of arrays
10488 : *
10489 : * @since GDAL 3.4
10490 : */
10491 :
10492 : std::vector<std::shared_ptr<GDALMDArray>>
10493 13 : GDALMDArray::GetCoordinateVariables() const
10494 : {
10495 13 : return {};
10496 : }
10497 :
10498 : /************************************************************************/
10499 : /* ~GDALExtendedDataType() */
10500 : /************************************************************************/
10501 :
10502 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10503 :
10504 : /************************************************************************/
10505 : /* GDALExtendedDataType() */
10506 : /************************************************************************/
10507 :
10508 9534 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10509 9534 : GDALExtendedDataTypeSubType eSubType)
10510 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10511 9534 : m_nMaxStringLength(nMaxStringLength)
10512 : {
10513 9534 : }
10514 :
10515 : /************************************************************************/
10516 : /* GDALExtendedDataType() */
10517 : /************************************************************************/
10518 :
10519 37242 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10520 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10521 37242 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10522 : {
10523 37242 : }
10524 :
10525 : /************************************************************************/
10526 : /* GDALExtendedDataType() */
10527 : /************************************************************************/
10528 :
10529 63 : GDALExtendedDataType::GDALExtendedDataType(
10530 : const std::string &osName, GDALDataType eBaseType,
10531 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10532 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10533 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10534 : {
10535 63 : }
10536 :
10537 : /************************************************************************/
10538 : /* GDALExtendedDataType() */
10539 : /************************************************************************/
10540 :
10541 855 : GDALExtendedDataType::GDALExtendedDataType(
10542 : const std::string &osName, size_t nTotalSize,
10543 855 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10544 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10545 855 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10546 : {
10547 855 : }
10548 :
10549 : /************************************************************************/
10550 : /* GDALExtendedDataType() */
10551 : /************************************************************************/
10552 :
10553 : /** Copy constructor. */
10554 17299 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10555 34598 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10556 17299 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10557 17299 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10558 17299 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10559 : {
10560 17299 : if (m_eClass == GEDTC_COMPOUND)
10561 : {
10562 521 : for (const auto &elt : other.m_aoComponents)
10563 : {
10564 341 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10565 : }
10566 : }
10567 17299 : }
10568 :
10569 : /************************************************************************/
10570 : /* operator= () */
10571 : /************************************************************************/
10572 :
10573 : /** Copy assignment. */
10574 : GDALExtendedDataType &
10575 1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10576 : {
10577 1045 : if (this != &other)
10578 : {
10579 1045 : m_osName = other.m_osName;
10580 1045 : m_eClass = other.m_eClass;
10581 1045 : m_eSubType = other.m_eSubType;
10582 1045 : m_eNumericDT = other.m_eNumericDT;
10583 1045 : m_nSize = other.m_nSize;
10584 1045 : m_nMaxStringLength = other.m_nMaxStringLength;
10585 1045 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10586 1045 : m_aoComponents.clear();
10587 1045 : if (m_eClass == GEDTC_COMPOUND)
10588 : {
10589 0 : for (const auto &elt : other.m_aoComponents)
10590 : {
10591 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10592 : }
10593 : }
10594 : }
10595 1045 : return *this;
10596 : }
10597 :
10598 : /************************************************************************/
10599 : /* operator= () */
10600 : /************************************************************************/
10601 :
10602 : /** Move assignment. */
10603 : GDALExtendedDataType &
10604 15239 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10605 : {
10606 15239 : m_osName = std::move(other.m_osName);
10607 15239 : m_eClass = other.m_eClass;
10608 15239 : m_eSubType = other.m_eSubType;
10609 15239 : m_eNumericDT = other.m_eNumericDT;
10610 15239 : m_nSize = other.m_nSize;
10611 15239 : m_nMaxStringLength = other.m_nMaxStringLength;
10612 15239 : m_aoComponents = std::move(other.m_aoComponents);
10613 15239 : m_poRAT = std::move(other.m_poRAT);
10614 15239 : other.m_eClass = GEDTC_NUMERIC;
10615 15239 : other.m_eNumericDT = GDT_Unknown;
10616 15239 : other.m_nSize = 0;
10617 15239 : other.m_nMaxStringLength = 0;
10618 15239 : return *this;
10619 : }
10620 :
10621 : /************************************************************************/
10622 : /* Create() */
10623 : /************************************************************************/
10624 :
10625 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10626 : *
10627 : * This is the same as the C function GDALExtendedDataTypeCreate()
10628 : *
10629 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10630 : * GDT_TypeCount
10631 : */
10632 37235 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10633 : {
10634 37235 : return GDALExtendedDataType(eType);
10635 : }
10636 :
10637 : /************************************************************************/
10638 : /* Create() */
10639 : /************************************************************************/
10640 :
10641 : /** Return a new GDALExtendedDataType from a raster attribute table.
10642 : *
10643 : * @param osName Type name
10644 : * @param eBaseType Base integer data type.
10645 : * @param poRAT Raster attribute table. Must not be NULL.
10646 : * @since 3.12
10647 : */
10648 : GDALExtendedDataType
10649 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10650 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10651 : {
10652 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10653 : }
10654 :
10655 : /************************************************************************/
10656 : /* Create() */
10657 : /************************************************************************/
10658 :
10659 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10660 : *
10661 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10662 : *
10663 : * @param osName Type name.
10664 : * @param nTotalSize Total size of the type in bytes.
10665 : * Should be large enough to store all components.
10666 : * @param components Components of the compound type.
10667 : */
10668 862 : GDALExtendedDataType GDALExtendedDataType::Create(
10669 : const std::string &osName, size_t nTotalSize,
10670 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10671 : {
10672 862 : size_t nLastOffset = 0;
10673 : // Some arbitrary threshold to avoid potential integer overflows
10674 862 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10675 : {
10676 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10677 2 : return GDALExtendedDataType(GDT_Unknown);
10678 : }
10679 4088 : for (const auto &comp : components)
10680 : {
10681 : // Check alignment too ?
10682 3229 : if (comp->GetOffset() < nLastOffset)
10683 : {
10684 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10685 1 : return GDALExtendedDataType(GDT_Unknown);
10686 : }
10687 3228 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10688 : }
10689 859 : if (nTotalSize < nLastOffset)
10690 : {
10691 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10692 1 : return GDALExtendedDataType(GDT_Unknown);
10693 : }
10694 858 : if (nTotalSize == 0 || components.empty())
10695 : {
10696 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10697 3 : return GDALExtendedDataType(GDT_Unknown);
10698 : }
10699 855 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10700 : }
10701 :
10702 : /************************************************************************/
10703 : /* Create() */
10704 : /************************************************************************/
10705 :
10706 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10707 : *
10708 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10709 : *
10710 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10711 : * unknown/unlimited
10712 : * @param eSubType Subtype.
10713 : */
10714 : GDALExtendedDataType
10715 9534 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10716 : GDALExtendedDataTypeSubType eSubType)
10717 : {
10718 9534 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10719 : }
10720 :
10721 : /************************************************************************/
10722 : /* operator==() */
10723 : /************************************************************************/
10724 :
10725 : /** Equality operator.
10726 : *
10727 : * This is the same as the C function GDALExtendedDataTypeEquals().
10728 : */
10729 2718 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10730 : {
10731 2691 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10732 5409 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10733 : {
10734 258 : return false;
10735 : }
10736 2460 : if (m_eClass == GEDTC_NUMERIC)
10737 : {
10738 873 : return m_eNumericDT == other.m_eNumericDT;
10739 : }
10740 1587 : if (m_eClass == GEDTC_STRING)
10741 : {
10742 1369 : return true;
10743 : }
10744 218 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10745 218 : if (m_aoComponents.size() != other.m_aoComponents.size())
10746 : {
10747 2 : return false;
10748 : }
10749 1139 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10750 : {
10751 923 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10752 : {
10753 0 : return false;
10754 : }
10755 : }
10756 216 : return true;
10757 : }
10758 :
10759 : /************************************************************************/
10760 : /* CanConvertTo() */
10761 : /************************************************************************/
10762 :
10763 : /** Return whether this data type can be converted to the other one.
10764 : *
10765 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10766 : *
10767 : * @param other Target data type for the conversion being considered.
10768 : */
10769 8527 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10770 : {
10771 8527 : if (m_eClass == GEDTC_NUMERIC)
10772 : {
10773 5952 : if (m_eNumericDT == GDT_Unknown)
10774 0 : return false;
10775 5952 : if (other.m_eClass == GEDTC_NUMERIC &&
10776 5843 : other.m_eNumericDT == GDT_Unknown)
10777 0 : return false;
10778 6061 : return other.m_eClass == GEDTC_NUMERIC ||
10779 6061 : other.m_eClass == GEDTC_STRING;
10780 : }
10781 2575 : if (m_eClass == GEDTC_STRING)
10782 : {
10783 2389 : return other.m_eClass == m_eClass;
10784 : }
10785 186 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10786 186 : if (other.m_eClass != GEDTC_COMPOUND)
10787 0 : return false;
10788 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10789 372 : srcComponents;
10790 929 : for (const auto &srcComp : m_aoComponents)
10791 : {
10792 743 : srcComponents[srcComp->GetName()] = &srcComp;
10793 : }
10794 513 : for (const auto &dstComp : other.m_aoComponents)
10795 : {
10796 328 : auto oIter = srcComponents.find(dstComp->GetName());
10797 328 : if (oIter == srcComponents.end())
10798 1 : return false;
10799 327 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10800 0 : return false;
10801 : }
10802 185 : return true;
10803 : }
10804 :
10805 : /************************************************************************/
10806 : /* NeedsFreeDynamicMemory() */
10807 : /************************************************************************/
10808 :
10809 : /** Return whether the data type holds dynamically allocated memory, that
10810 : * needs to be freed with FreeDynamicMemory().
10811 : *
10812 : */
10813 3742 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10814 : {
10815 3742 : switch (m_eClass)
10816 : {
10817 936 : case GEDTC_STRING:
10818 936 : return true;
10819 :
10820 2696 : case GEDTC_NUMERIC:
10821 2696 : return false;
10822 :
10823 110 : case GEDTC_COMPOUND:
10824 : {
10825 223 : for (const auto &comp : m_aoComponents)
10826 : {
10827 209 : if (comp->GetType().NeedsFreeDynamicMemory())
10828 96 : return true;
10829 : }
10830 : }
10831 : }
10832 14 : return false;
10833 : }
10834 :
10835 : /************************************************************************/
10836 : /* FreeDynamicMemory() */
10837 : /************************************************************************/
10838 :
10839 : /** Release the dynamic memory (strings typically) from a raw value.
10840 : *
10841 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10842 : *
10843 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10844 : */
10845 3865 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10846 : {
10847 3865 : switch (m_eClass)
10848 : {
10849 2788 : case GEDTC_STRING:
10850 : {
10851 : char *pszStr;
10852 2788 : memcpy(&pszStr, pBuffer, sizeof(char *));
10853 2788 : if (pszStr)
10854 : {
10855 2243 : VSIFree(pszStr);
10856 : }
10857 2788 : break;
10858 : }
10859 :
10860 901 : case GEDTC_NUMERIC:
10861 : {
10862 901 : break;
10863 : }
10864 :
10865 176 : case GEDTC_COMPOUND:
10866 : {
10867 176 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10868 938 : for (const auto &comp : m_aoComponents)
10869 : {
10870 1524 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10871 762 : comp->GetOffset());
10872 : }
10873 176 : break;
10874 : }
10875 : }
10876 3865 : }
10877 :
10878 : /************************************************************************/
10879 : /* ~GDALEDTComponent() */
10880 : /************************************************************************/
10881 :
10882 : GDALEDTComponent::~GDALEDTComponent() = default;
10883 :
10884 : /************************************************************************/
10885 : /* GDALEDTComponent() */
10886 : /************************************************************************/
10887 :
10888 : /** constructor of a GDALEDTComponent
10889 : *
10890 : * This is the same as the C function GDALEDTComponendCreate()
10891 : *
10892 : * @param name Component name
10893 : * @param offset Offset in byte of the component in the compound data type.
10894 : * In case of nesting of compound data type, this should be
10895 : * the offset to the immediate belonging data type, not to the
10896 : * higher level one.
10897 : * @param type Component data type.
10898 : */
10899 3220 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10900 3220 : const GDALExtendedDataType &type)
10901 3220 : : m_osName(name), m_nOffset(offset), m_oType(type)
10902 : {
10903 3220 : }
10904 :
10905 : /************************************************************************/
10906 : /* GDALEDTComponent() */
10907 : /************************************************************************/
10908 :
10909 : /** Copy constructor. */
10910 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10911 :
10912 : /************************************************************************/
10913 : /* operator==() */
10914 : /************************************************************************/
10915 :
10916 : /** Equality operator.
10917 : */
10918 923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10919 : {
10920 1846 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10921 1846 : m_oType == other.m_oType;
10922 : }
10923 :
10924 : /************************************************************************/
10925 : /* ~GDALDimension() */
10926 : /************************************************************************/
10927 :
10928 : GDALDimension::~GDALDimension() = default;
10929 :
10930 : /************************************************************************/
10931 : /* GDALDimension() */
10932 : /************************************************************************/
10933 :
10934 : //! @cond Doxygen_Suppress
10935 : /** Constructor.
10936 : *
10937 : * @param osParentName Parent name
10938 : * @param osName name
10939 : * @param osType type. See GetType().
10940 : * @param osDirection direction. See GetDirection().
10941 : * @param nSize size.
10942 : */
10943 8502 : GDALDimension::GDALDimension(const std::string &osParentName,
10944 : const std::string &osName,
10945 : const std::string &osType,
10946 8502 : const std::string &osDirection, GUInt64 nSize)
10947 : : m_osName(osName),
10948 : m_osFullName(
10949 8502 : !osParentName.empty()
10950 12521 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10951 : : osName),
10952 29525 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10953 : {
10954 8502 : }
10955 :
10956 : //! @endcond
10957 :
10958 : /************************************************************************/
10959 : /* GetIndexingVariable() */
10960 : /************************************************************************/
10961 :
10962 : /** Return the variable that is used to index the dimension (if there is one).
10963 : *
10964 : * This is the array, typically one-dimensional, describing the values taken
10965 : * by the dimension.
10966 : */
10967 73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10968 : {
10969 73 : return nullptr;
10970 : }
10971 :
10972 : /************************************************************************/
10973 : /* SetIndexingVariable() */
10974 : /************************************************************************/
10975 :
10976 : /** Set the variable that is used to index the dimension.
10977 : *
10978 : * This is the array, typically one-dimensional, describing the values taken
10979 : * by the dimension.
10980 : *
10981 : * Optionally implemented by drivers.
10982 : *
10983 : * Drivers known to implement it: MEM.
10984 : *
10985 : * @param poArray Variable to use to index the dimension.
10986 : * @return true in case of success.
10987 : */
10988 3 : bool GDALDimension::SetIndexingVariable(
10989 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10990 : {
10991 3 : CPLError(CE_Failure, CPLE_NotSupported,
10992 : "SetIndexingVariable() not implemented");
10993 3 : return false;
10994 : }
10995 :
10996 : /************************************************************************/
10997 : /* Rename() */
10998 : /************************************************************************/
10999 :
11000 : /** Rename the dimension.
11001 : *
11002 : * This is not implemented by all drivers.
11003 : *
11004 : * Drivers known to implement it: MEM, netCDF, ZARR.
11005 : *
11006 : * This is the same as the C function GDALDimensionRename().
11007 : *
11008 : * @param osNewName New name.
11009 : *
11010 : * @return true in case of success
11011 : * @since GDAL 3.8
11012 : */
11013 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11014 : {
11015 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11016 0 : return false;
11017 : }
11018 :
11019 : /************************************************************************/
11020 : /* BaseRename() */
11021 : /************************************************************************/
11022 :
11023 : //! @cond Doxygen_Suppress
11024 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11025 : {
11026 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11027 8 : m_osFullName += osNewName;
11028 8 : m_osName = osNewName;
11029 8 : }
11030 :
11031 : //! @endcond
11032 :
11033 : //! @cond Doxygen_Suppress
11034 : /************************************************************************/
11035 : /* ParentRenamed() */
11036 : /************************************************************************/
11037 :
11038 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11039 : {
11040 8 : m_osFullName = osNewParentFullName;
11041 8 : m_osFullName += "/";
11042 8 : m_osFullName += m_osName;
11043 8 : }
11044 :
11045 : //! @endcond
11046 :
11047 : //! @cond Doxygen_Suppress
11048 : /************************************************************************/
11049 : /* ParentDeleted() */
11050 : /************************************************************************/
11051 :
11052 4 : void GDALDimension::ParentDeleted()
11053 : {
11054 4 : }
11055 :
11056 : //! @endcond
11057 :
11058 : /************************************************************************/
11059 : /************************************************************************/
11060 : /************************************************************************/
11061 : /* C API */
11062 : /************************************************************************/
11063 : /************************************************************************/
11064 : /************************************************************************/
11065 :
11066 : /************************************************************************/
11067 : /* GDALExtendedDataTypeCreate() */
11068 : /************************************************************************/
11069 :
11070 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11071 : *
11072 : * This is the same as the C++ method GDALExtendedDataType::Create()
11073 : *
11074 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11075 : *
11076 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11077 : * GDT_TypeCount
11078 : *
11079 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11080 : */
11081 2008 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11082 : {
11083 2008 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11084 : {
11085 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11086 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11087 0 : return nullptr;
11088 : }
11089 : return new GDALExtendedDataTypeHS(
11090 2008 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11091 : }
11092 :
11093 : /************************************************************************/
11094 : /* GDALExtendedDataTypeCreateString() */
11095 : /************************************************************************/
11096 :
11097 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11098 : *
11099 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11100 : *
11101 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11102 : *
11103 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11104 : */
11105 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11106 : {
11107 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11108 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11109 : }
11110 :
11111 : /************************************************************************/
11112 : /* GDALExtendedDataTypeCreateStringEx() */
11113 : /************************************************************************/
11114 :
11115 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11116 : *
11117 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11118 : *
11119 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11120 : *
11121 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11122 : * @since GDAL 3.4
11123 : */
11124 : GDALExtendedDataTypeH
11125 190 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11126 : GDALExtendedDataTypeSubType eSubType)
11127 : {
11128 190 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11129 190 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11130 : }
11131 :
11132 : /************************************************************************/
11133 : /* GDALExtendedDataTypeCreateCompound() */
11134 : /************************************************************************/
11135 :
11136 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11137 : *
11138 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11139 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11140 : *
11141 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11142 : *
11143 : * @param pszName Type name.
11144 : * @param nTotalSize Total size of the type in bytes.
11145 : * Should be large enough to store all components.
11146 : * @param nComponents Number of components in comps array.
11147 : * @param comps Components.
11148 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11149 : */
11150 : GDALExtendedDataTypeH
11151 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11152 : size_t nComponents,
11153 : const GDALEDTComponentH *comps)
11154 : {
11155 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11156 54 : for (size_t i = 0; i < nComponents; i++)
11157 : {
11158 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
11159 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
11160 : }
11161 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11162 66 : std::move(compsCpp));
11163 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11164 6 : return nullptr;
11165 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
11166 : }
11167 :
11168 : /************************************************************************/
11169 : /* GDALExtendedDataTypeRelease() */
11170 : /************************************************************************/
11171 :
11172 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11173 : *
11174 : * Note: when applied on a object coming from a driver, this does not
11175 : * destroy the object in the file, database, etc...
11176 : */
11177 6621 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11178 : {
11179 6621 : delete hEDT;
11180 6621 : }
11181 :
11182 : /************************************************************************/
11183 : /* GDALExtendedDataTypeGetName() */
11184 : /************************************************************************/
11185 :
11186 : /** Return type name.
11187 : *
11188 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11189 : */
11190 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11191 : {
11192 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11193 8 : return hEDT->m_poImpl->GetName().c_str();
11194 : }
11195 :
11196 : /************************************************************************/
11197 : /* GDALExtendedDataTypeGetClass() */
11198 : /************************************************************************/
11199 :
11200 : /** Return type class.
11201 : *
11202 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11203 : */
11204 : GDALExtendedDataTypeClass
11205 9177 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11206 : {
11207 9177 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11208 9177 : return hEDT->m_poImpl->GetClass();
11209 : }
11210 :
11211 : /************************************************************************/
11212 : /* GDALExtendedDataTypeGetNumericDataType() */
11213 : /************************************************************************/
11214 :
11215 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11216 : *
11217 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11218 : */
11219 2069 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11220 : {
11221 2069 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11222 2069 : return hEDT->m_poImpl->GetNumericDataType();
11223 : }
11224 :
11225 : /************************************************************************/
11226 : /* GDALExtendedDataTypeGetSize() */
11227 : /************************************************************************/
11228 :
11229 : /** Return data type size in bytes.
11230 : *
11231 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11232 : */
11233 2538 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11234 : {
11235 2538 : VALIDATE_POINTER1(hEDT, __func__, 0);
11236 2538 : return hEDT->m_poImpl->GetSize();
11237 : }
11238 :
11239 : /************************************************************************/
11240 : /* GDALExtendedDataTypeGetMaxStringLength() */
11241 : /************************************************************************/
11242 :
11243 : /** Return the maximum length of a string in bytes.
11244 : *
11245 : * 0 indicates unknown/unlimited string.
11246 : *
11247 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11248 : */
11249 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11250 : {
11251 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11252 3 : return hEDT->m_poImpl->GetMaxStringLength();
11253 : }
11254 :
11255 : /************************************************************************/
11256 : /* GDALExtendedDataTypeCanConvertTo() */
11257 : /************************************************************************/
11258 :
11259 : /** Return whether this data type can be converted to the other one.
11260 : *
11261 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11262 : *
11263 : * @param hSourceEDT Source data type for the conversion being considered.
11264 : * @param hTargetEDT Target data type for the conversion being considered.
11265 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11266 : */
11267 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11268 : GDALExtendedDataTypeH hTargetEDT)
11269 : {
11270 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11271 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11272 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11273 : }
11274 :
11275 : /************************************************************************/
11276 : /* GDALExtendedDataTypeEquals() */
11277 : /************************************************************************/
11278 :
11279 : /** Return whether this data type is equal to another one.
11280 : *
11281 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11282 : *
11283 : * @param hFirstEDT First data type.
11284 : * @param hSecondEDT Second data type.
11285 : * @return TRUE if they are equal. FALSE otherwise.
11286 : */
11287 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11288 : GDALExtendedDataTypeH hSecondEDT)
11289 : {
11290 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11291 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11292 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11293 : }
11294 :
11295 : /************************************************************************/
11296 : /* GDALExtendedDataTypeGetSubType() */
11297 : /************************************************************************/
11298 :
11299 : /** Return the subtype of a type.
11300 : *
11301 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11302 : *
11303 : * @param hEDT Data type.
11304 : * @return subtype.
11305 : * @since 3.4
11306 : */
11307 : GDALExtendedDataTypeSubType
11308 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11309 : {
11310 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11311 104 : return hEDT->m_poImpl->GetSubType();
11312 : }
11313 :
11314 : /************************************************************************/
11315 : /* GDALExtendedDataTypeGetRAT() */
11316 : /************************************************************************/
11317 :
11318 : /** Return associated raster attribute table, when there is one.
11319 : *
11320 : * * For the netCDF driver, the RAT will capture enumerated types, with
11321 : * a "value" column with an integer value and a "name" column with the
11322 : * associated name.
11323 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11324 : *
11325 : * @param hEDT Data type.
11326 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11327 : * @since 3.12
11328 : */
11329 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11330 : {
11331 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11332 1 : return GDALRasterAttributeTable::ToHandle(
11333 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11334 : }
11335 :
11336 : /************************************************************************/
11337 : /* GDALExtendedDataTypeGetComponents() */
11338 : /************************************************************************/
11339 :
11340 : /** Return the components of the data type (only valid when GetClass() ==
11341 : * GEDTC_COMPOUND)
11342 : *
11343 : * The returned array and its content must be freed with
11344 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11345 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11346 : * individual array members).
11347 : *
11348 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11349 : *
11350 : * @param hEDT Data type
11351 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11352 : * @return an array of *pnCount components.
11353 : */
11354 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11355 : size_t *pnCount)
11356 : {
11357 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11358 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11359 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11360 : auto ret = static_cast<GDALEDTComponentH *>(
11361 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11362 131 : for (size_t i = 0; i < components.size(); i++)
11363 : {
11364 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11365 : }
11366 44 : *pnCount = components.size();
11367 44 : return ret;
11368 : }
11369 :
11370 : /************************************************************************/
11371 : /* GDALExtendedDataTypeFreeComponents() */
11372 : /************************************************************************/
11373 :
11374 : /** Free the return of GDALExtendedDataTypeGetComponents().
11375 : *
11376 : * @param components return value of GDALExtendedDataTypeGetComponents()
11377 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11378 : */
11379 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11380 : size_t nCount)
11381 : {
11382 131 : for (size_t i = 0; i < nCount; i++)
11383 : {
11384 87 : delete components[i];
11385 : }
11386 44 : CPLFree(components);
11387 44 : }
11388 :
11389 : /************************************************************************/
11390 : /* GDALEDTComponentCreate() */
11391 : /************************************************************************/
11392 :
11393 : /** Create a new GDALEDTComponent.
11394 : *
11395 : * The returned value must be freed with GDALEDTComponentRelease().
11396 : *
11397 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11398 : */
11399 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11400 : GDALExtendedDataTypeH hType)
11401 : {
11402 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11403 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11404 : return new GDALEDTComponentHS(
11405 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11406 : }
11407 :
11408 : /************************************************************************/
11409 : /* GDALEDTComponentRelease() */
11410 : /************************************************************************/
11411 :
11412 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11413 : *
11414 : * Note: when applied on a object coming from a driver, this does not
11415 : * destroy the object in the file, database, etc...
11416 : */
11417 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11418 : {
11419 61 : delete hComp;
11420 61 : }
11421 :
11422 : /************************************************************************/
11423 : /* GDALEDTComponentGetName() */
11424 : /************************************************************************/
11425 :
11426 : /** Return the name.
11427 : *
11428 : * The returned pointer is valid until hComp is released.
11429 : *
11430 : * This is the same as the C++ method GDALEDTComponent::GetName().
11431 : */
11432 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11433 : {
11434 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11435 33 : return hComp->m_poImpl->GetName().c_str();
11436 : }
11437 :
11438 : /************************************************************************/
11439 : /* GDALEDTComponentGetOffset() */
11440 : /************************************************************************/
11441 :
11442 : /** Return the offset (in bytes) of the component in the compound data type.
11443 : *
11444 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11445 : */
11446 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11447 : {
11448 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11449 31 : return hComp->m_poImpl->GetOffset();
11450 : }
11451 :
11452 : /************************************************************************/
11453 : /* GDALEDTComponentGetType() */
11454 : /************************************************************************/
11455 :
11456 : /** Return the data type of the component.
11457 : *
11458 : * This is the same as the C++ method GDALEDTComponent::GetType().
11459 : */
11460 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11461 : {
11462 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11463 : return new GDALExtendedDataTypeHS(
11464 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11465 : }
11466 :
11467 : /************************************************************************/
11468 : /* GDALGroupRelease() */
11469 : /************************************************************************/
11470 :
11471 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11472 : *
11473 : * Note: when applied on a object coming from a driver, this does not
11474 : * destroy the object in the file, database, etc...
11475 : */
11476 1440 : void GDALGroupRelease(GDALGroupH hGroup)
11477 : {
11478 1440 : delete hGroup;
11479 1440 : }
11480 :
11481 : /************************************************************************/
11482 : /* GDALGroupGetName() */
11483 : /************************************************************************/
11484 :
11485 : /** Return the name of the group.
11486 : *
11487 : * The returned pointer is valid until hGroup is released.
11488 : *
11489 : * This is the same as the C++ method GDALGroup::GetName().
11490 : */
11491 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11492 : {
11493 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11494 95 : return hGroup->m_poImpl->GetName().c_str();
11495 : }
11496 :
11497 : /************************************************************************/
11498 : /* GDALGroupGetFullName() */
11499 : /************************************************************************/
11500 :
11501 : /** Return the full name of the group.
11502 : *
11503 : * The returned pointer is valid until hGroup is released.
11504 : *
11505 : * This is the same as the C++ method GDALGroup::GetFullName().
11506 : */
11507 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11508 : {
11509 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11510 47 : return hGroup->m_poImpl->GetFullName().c_str();
11511 : }
11512 :
11513 : /************************************************************************/
11514 : /* GDALGroupGetMDArrayNames() */
11515 : /************************************************************************/
11516 :
11517 : /** Return the list of multidimensional array names contained in this group.
11518 : *
11519 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11520 : *
11521 : * @return the array names, to be freed with CSLDestroy()
11522 : */
11523 329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11524 : {
11525 329 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11526 658 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11527 658 : CPLStringList res;
11528 829 : for (const auto &name : names)
11529 : {
11530 500 : res.AddString(name.c_str());
11531 : }
11532 329 : return res.StealList();
11533 : }
11534 :
11535 : /************************************************************************/
11536 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11537 : /************************************************************************/
11538 :
11539 : /** Return the list of multidimensional array full names contained in this
11540 : * group and its subgroups.
11541 : *
11542 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11543 : *
11544 : * @return the array names, to be freed with CSLDestroy()
11545 : *
11546 : * @since 3.11
11547 : */
11548 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11549 : CSLConstList papszGroupOptions,
11550 : CSLConstList papszArrayOptions)
11551 : {
11552 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11553 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11554 2 : papszGroupOptions, papszArrayOptions);
11555 2 : CPLStringList res;
11556 5 : for (const auto &name : names)
11557 : {
11558 4 : res.AddString(name.c_str());
11559 : }
11560 1 : return res.StealList();
11561 : }
11562 :
11563 : /************************************************************************/
11564 : /* GDALGroupOpenMDArray() */
11565 : /************************************************************************/
11566 :
11567 : /** Open and return a multidimensional array.
11568 : *
11569 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11570 : *
11571 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11572 : */
11573 806 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11574 : CSLConstList papszOptions)
11575 : {
11576 806 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11577 806 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11578 2418 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11579 2418 : papszOptions);
11580 806 : if (!array)
11581 30 : return nullptr;
11582 776 : return new GDALMDArrayHS(array);
11583 : }
11584 :
11585 : /************************************************************************/
11586 : /* GDALGroupOpenMDArrayFromFullname() */
11587 : /************************************************************************/
11588 :
11589 : /** Open and return a multidimensional array from its fully qualified name.
11590 : *
11591 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11592 : *
11593 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11594 : *
11595 : * @since GDAL 3.2
11596 : */
11597 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11598 : const char *pszFullname,
11599 : CSLConstList papszOptions)
11600 : {
11601 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11602 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11603 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11604 48 : std::string(pszFullname), papszOptions);
11605 16 : if (!array)
11606 2 : return nullptr;
11607 14 : return new GDALMDArrayHS(array);
11608 : }
11609 :
11610 : /************************************************************************/
11611 : /* GDALGroupResolveMDArray() */
11612 : /************************************************************************/
11613 :
11614 : /** Locate an array in a group and its subgroups by name.
11615 : *
11616 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11617 : * @since GDAL 3.2
11618 : */
11619 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11620 : const char *pszStartingPoint,
11621 : CSLConstList papszOptions)
11622 : {
11623 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11624 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11625 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11626 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11627 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11628 19 : if (!array)
11629 2 : return nullptr;
11630 17 : return new GDALMDArrayHS(array);
11631 : }
11632 :
11633 : /************************************************************************/
11634 : /* GDALGroupGetGroupNames() */
11635 : /************************************************************************/
11636 :
11637 : /** Return the list of sub-groups contained in this group.
11638 : *
11639 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11640 : *
11641 : * @return the group names, to be freed with CSLDestroy()
11642 : */
11643 97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11644 : {
11645 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11646 194 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11647 194 : CPLStringList res;
11648 219 : for (const auto &name : names)
11649 : {
11650 122 : res.AddString(name.c_str());
11651 : }
11652 97 : return res.StealList();
11653 : }
11654 :
11655 : /************************************************************************/
11656 : /* GDALGroupOpenGroup() */
11657 : /************************************************************************/
11658 :
11659 : /** Open and return a sub-group.
11660 : *
11661 : * This is the same as the C++ method GDALGroup::OpenGroup().
11662 : *
11663 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11664 : */
11665 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11666 : CSLConstList papszOptions)
11667 : {
11668 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11669 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11670 : auto subGroup =
11671 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11672 163 : if (!subGroup)
11673 30 : return nullptr;
11674 133 : return new GDALGroupHS(subGroup);
11675 : }
11676 :
11677 : /************************************************************************/
11678 : /* GDALGroupGetVectorLayerNames() */
11679 : /************************************************************************/
11680 :
11681 : /** Return the list of layer names contained in this group.
11682 : *
11683 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11684 : *
11685 : * @return the group names, to be freed with CSLDestroy()
11686 : * @since 3.4
11687 : */
11688 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11689 : CSLConstList papszOptions)
11690 : {
11691 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11692 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11693 16 : CPLStringList res;
11694 18 : for (const auto &name : names)
11695 : {
11696 10 : res.AddString(name.c_str());
11697 : }
11698 8 : return res.StealList();
11699 : }
11700 :
11701 : /************************************************************************/
11702 : /* GDALGroupOpenVectorLayer() */
11703 : /************************************************************************/
11704 :
11705 : /** Open and return a vector layer.
11706 : *
11707 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11708 : *
11709 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11710 : * the returned handled if only valid while the parent GDALDatasetH is kept
11711 : * opened.
11712 : *
11713 : * @return the vector layer, or nullptr.
11714 : * @since 3.4
11715 : */
11716 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11717 : const char *pszVectorLayerName,
11718 : CSLConstList papszOptions)
11719 : {
11720 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11721 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11722 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11723 24 : std::string(pszVectorLayerName), papszOptions));
11724 : }
11725 :
11726 : /************************************************************************/
11727 : /* GDALGroupOpenMDArrayFromFullname() */
11728 : /************************************************************************/
11729 :
11730 : /** Open and return a sub-group from its fully qualified name.
11731 : *
11732 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11733 : *
11734 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11735 : *
11736 : * @since GDAL 3.2
11737 : */
11738 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11739 : const char *pszFullname,
11740 : CSLConstList papszOptions)
11741 : {
11742 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11743 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11744 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11745 9 : std::string(pszFullname), papszOptions);
11746 3 : if (!subGroup)
11747 2 : return nullptr;
11748 1 : return new GDALGroupHS(subGroup);
11749 : }
11750 :
11751 : /************************************************************************/
11752 : /* GDALGroupGetDimensions() */
11753 : /************************************************************************/
11754 :
11755 : /** Return the list of dimensions contained in this group and used by its
11756 : * arrays.
11757 : *
11758 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11759 : * array itself needs to be freed, CPLFree() should be called (and
11760 : * GDALDimensionRelease() on individual array members).
11761 : *
11762 : * This is the same as the C++ method GDALGroup::GetDimensions().
11763 : *
11764 : * @param hGroup Group.
11765 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11766 : * @param papszOptions Driver specific options determining how dimensions
11767 : * should be retrieved. Pass nullptr for default behavior.
11768 : *
11769 : * @return an array of *pnCount dimensions.
11770 : */
11771 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11772 : CSLConstList papszOptions)
11773 : {
11774 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11775 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11776 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11777 : auto ret = static_cast<GDALDimensionH *>(
11778 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11779 230 : for (size_t i = 0; i < dims.size(); i++)
11780 : {
11781 157 : ret[i] = new GDALDimensionHS(dims[i]);
11782 : }
11783 73 : *pnCount = dims.size();
11784 73 : return ret;
11785 : }
11786 :
11787 : /************************************************************************/
11788 : /* GDALGroupGetAttribute() */
11789 : /************************************************************************/
11790 :
11791 : /** Return an attribute by its name.
11792 : *
11793 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11794 : *
11795 : * The returned attribute must be freed with GDALAttributeRelease().
11796 : */
11797 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11798 : {
11799 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11800 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11801 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11802 80 : if (attr)
11803 76 : return new GDALAttributeHS(attr);
11804 4 : return nullptr;
11805 : }
11806 :
11807 : /************************************************************************/
11808 : /* GDALGroupGetAttributes() */
11809 : /************************************************************************/
11810 :
11811 : /** Return the list of attributes contained in this group.
11812 : *
11813 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11814 : * array itself needs to be freed, CPLFree() should be called (and
11815 : * GDALAttributeRelease() on individual array members).
11816 : *
11817 : * This is the same as the C++ method GDALGroup::GetAttributes().
11818 : *
11819 : * @param hGroup Group.
11820 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11821 : * @param papszOptions Driver specific options determining how attributes
11822 : * should be retrieved. Pass nullptr for default behavior.
11823 : *
11824 : * @return an array of *pnCount attributes.
11825 : */
11826 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11827 : CSLConstList papszOptions)
11828 : {
11829 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11830 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11831 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11832 : auto ret = static_cast<GDALAttributeH *>(
11833 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11834 229 : for (size_t i = 0; i < attrs.size(); i++)
11835 : {
11836 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11837 : }
11838 71 : *pnCount = attrs.size();
11839 71 : return ret;
11840 : }
11841 :
11842 : /************************************************************************/
11843 : /* GDALGroupGetStructuralInfo() */
11844 : /************************************************************************/
11845 :
11846 : /** Return structural information on the group.
11847 : *
11848 : * This may be the compression, etc..
11849 : *
11850 : * The return value should not be freed and is valid until GDALGroup is
11851 : * released or this function called again.
11852 : *
11853 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11854 : */
11855 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11856 : {
11857 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11858 4 : return hGroup->m_poImpl->GetStructuralInfo();
11859 : }
11860 :
11861 : /************************************************************************/
11862 : /* GDALGroupGetDataTypeCount() */
11863 : /************************************************************************/
11864 :
11865 : /** Return the number of data types associated with the group
11866 : * (typically enumerations).
11867 : *
11868 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11869 : *
11870 : * @since 3.12
11871 : */
11872 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11873 : {
11874 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11875 4 : return hGroup->m_poImpl->GetDataTypes().size();
11876 : }
11877 :
11878 : /************************************************************************/
11879 : /* GDALGroupGetDataType() */
11880 : /************************************************************************/
11881 :
11882 : /** Return one of the data types associated with the group.
11883 : *
11884 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11885 : *
11886 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11887 : * or nullptr in case of error.
11888 : * @since 3.12
11889 : */
11890 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11891 : {
11892 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11893 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11894 0 : return nullptr;
11895 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11896 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11897 : }
11898 :
11899 : /************************************************************************/
11900 : /* GDALReleaseAttributes() */
11901 : /************************************************************************/
11902 :
11903 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11904 : *
11905 : * @param attributes return pointer of above methods
11906 : * @param nCount *pnCount value returned by above methods
11907 : */
11908 129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11909 : {
11910 416 : for (size_t i = 0; i < nCount; i++)
11911 : {
11912 287 : delete attributes[i];
11913 : }
11914 129 : CPLFree(attributes);
11915 129 : }
11916 :
11917 : /************************************************************************/
11918 : /* GDALGroupCreateGroup() */
11919 : /************************************************************************/
11920 :
11921 : /** Create a sub-group within a group.
11922 : *
11923 : * This is the same as the C++ method GDALGroup::CreateGroup().
11924 : *
11925 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11926 : */
11927 177 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11928 : CSLConstList papszOptions)
11929 : {
11930 177 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11931 177 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11932 531 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11933 531 : papszOptions);
11934 177 : if (!ret)
11935 49 : return nullptr;
11936 128 : return new GDALGroupHS(ret);
11937 : }
11938 :
11939 : /************************************************************************/
11940 : /* GDALGroupDeleteGroup() */
11941 : /************************************************************************/
11942 :
11943 : /** Delete a sub-group from a group.
11944 : *
11945 : * After this call, if a previously obtained instance of the deleted object
11946 : * is still alive, no method other than for freeing it should be invoked.
11947 : *
11948 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11949 : *
11950 : * @return true in case of success.
11951 : * @since GDAL 3.8
11952 : */
11953 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11954 : CSLConstList papszOptions)
11955 : {
11956 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11957 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11958 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11959 20 : papszOptions);
11960 : }
11961 :
11962 : /************************************************************************/
11963 : /* GDALGroupCreateDimension() */
11964 : /************************************************************************/
11965 :
11966 : /** Create a dimension within a group.
11967 : *
11968 : * This is the same as the C++ method GDALGroup::CreateDimension().
11969 : *
11970 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11971 : */
11972 666 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11973 : const char *pszType,
11974 : const char *pszDirection, GUInt64 nSize,
11975 : CSLConstList papszOptions)
11976 : {
11977 666 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11978 666 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11979 666 : auto ret = hGroup->m_poImpl->CreateDimension(
11980 1332 : std::string(pszName), std::string(pszType ? pszType : ""),
11981 2664 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11982 666 : if (!ret)
11983 9 : return nullptr;
11984 657 : return new GDALDimensionHS(ret);
11985 : }
11986 :
11987 : /************************************************************************/
11988 : /* GDALGroupCreateMDArray() */
11989 : /************************************************************************/
11990 :
11991 : /** Create a multidimensional array within a group.
11992 : *
11993 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11994 : *
11995 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11996 : */
11997 609 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11998 : size_t nDimensions,
11999 : GDALDimensionH *pahDimensions,
12000 : GDALExtendedDataTypeH hEDT,
12001 : CSLConstList papszOptions)
12002 : {
12003 609 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12004 609 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12005 609 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12006 1218 : std::vector<std::shared_ptr<GDALDimension>> dims;
12007 609 : dims.reserve(nDimensions);
12008 1437 : for (size_t i = 0; i < nDimensions; i++)
12009 828 : dims.push_back(pahDimensions[i]->m_poImpl);
12010 1827 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12011 1827 : *(hEDT->m_poImpl), papszOptions);
12012 609 : if (!ret)
12013 65 : return nullptr;
12014 544 : return new GDALMDArrayHS(ret);
12015 : }
12016 :
12017 : /************************************************************************/
12018 : /* GDALGroupDeleteMDArray() */
12019 : /************************************************************************/
12020 :
12021 : /** Delete an array from a group.
12022 : *
12023 : * After this call, if a previously obtained instance of the deleted object
12024 : * is still alive, no method other than for freeing it should be invoked.
12025 : *
12026 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12027 : *
12028 : * @return true in case of success.
12029 : * @since GDAL 3.8
12030 : */
12031 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12032 : CSLConstList papszOptions)
12033 : {
12034 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12035 20 : VALIDATE_POINTER1(pszName, __func__, false);
12036 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12037 : }
12038 :
12039 : /************************************************************************/
12040 : /* GDALGroupCreateAttribute() */
12041 : /************************************************************************/
12042 :
12043 : /** Create a attribute within a group.
12044 : *
12045 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12046 : *
12047 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12048 : */
12049 122 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12050 : size_t nDimensions,
12051 : const GUInt64 *panDimensions,
12052 : GDALExtendedDataTypeH hEDT,
12053 : CSLConstList papszOptions)
12054 : {
12055 122 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12056 122 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12057 244 : std::vector<GUInt64> dims;
12058 122 : dims.reserve(nDimensions);
12059 172 : for (size_t i = 0; i < nDimensions; i++)
12060 50 : dims.push_back(panDimensions[i]);
12061 122 : auto ret = hGroup->m_poImpl->CreateAttribute(
12062 366 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12063 122 : if (!ret)
12064 14 : return nullptr;
12065 108 : return new GDALAttributeHS(ret);
12066 : }
12067 :
12068 : /************************************************************************/
12069 : /* GDALGroupDeleteAttribute() */
12070 : /************************************************************************/
12071 :
12072 : /** Delete an attribute from a group.
12073 : *
12074 : * After this call, if a previously obtained instance of the deleted object
12075 : * is still alive, no method other than for freeing it should be invoked.
12076 : *
12077 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12078 : *
12079 : * @return true in case of success.
12080 : * @since GDAL 3.8
12081 : */
12082 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12083 : CSLConstList papszOptions)
12084 : {
12085 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12086 25 : VALIDATE_POINTER1(pszName, __func__, false);
12087 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12088 25 : papszOptions);
12089 : }
12090 :
12091 : /************************************************************************/
12092 : /* GDALGroupRename() */
12093 : /************************************************************************/
12094 :
12095 : /** Rename the group.
12096 : *
12097 : * This is not implemented by all drivers.
12098 : *
12099 : * Drivers known to implement it: MEM, netCDF.
12100 : *
12101 : * This is the same as the C++ method GDALGroup::Rename()
12102 : *
12103 : * @return true in case of success
12104 : * @since GDAL 3.8
12105 : */
12106 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12107 : {
12108 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12109 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12110 45 : return hGroup->m_poImpl->Rename(pszNewName);
12111 : }
12112 :
12113 : /************************************************************************/
12114 : /* GDALGroupSubsetDimensionFromSelection() */
12115 : /************************************************************************/
12116 :
12117 : /** Return a virtual group whose one dimension has been subset according to a
12118 : * selection.
12119 : *
12120 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12121 : *
12122 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12123 : */
12124 : GDALGroupH
12125 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12126 : const char *pszSelection,
12127 : CPL_UNUSED CSLConstList papszOptions)
12128 : {
12129 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12130 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12131 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12132 42 : std::string(pszSelection));
12133 14 : if (!hNewGroup)
12134 8 : return nullptr;
12135 6 : return new GDALGroupHS(hNewGroup);
12136 : }
12137 :
12138 : /************************************************************************/
12139 : /* GDALMDArrayRelease() */
12140 : /************************************************************************/
12141 :
12142 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12143 : *
12144 : * Note: when applied on a object coming from a driver, this does not
12145 : * destroy the object in the file, database, etc...
12146 : */
12147 2017 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12148 : {
12149 2017 : delete hMDArray;
12150 2017 : }
12151 :
12152 : /************************************************************************/
12153 : /* GDALMDArrayGetName() */
12154 : /************************************************************************/
12155 :
12156 : /** Return array name.
12157 : *
12158 : * This is the same as the C++ method GDALMDArray::GetName()
12159 : */
12160 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12161 : {
12162 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12163 83 : return hArray->m_poImpl->GetName().c_str();
12164 : }
12165 :
12166 : /************************************************************************/
12167 : /* GDALMDArrayGetFullName() */
12168 : /************************************************************************/
12169 :
12170 : /** Return array full name.
12171 : *
12172 : * This is the same as the C++ method GDALMDArray::GetFullName()
12173 : */
12174 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12175 : {
12176 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12177 50 : return hArray->m_poImpl->GetFullName().c_str();
12178 : }
12179 :
12180 : /************************************************************************/
12181 : /* GDALMDArrayGetName() */
12182 : /************************************************************************/
12183 :
12184 : /** Return the total number of values in the array.
12185 : *
12186 : * This is the same as the C++ method
12187 : * GDALAbstractMDArray::GetTotalElementsCount()
12188 : */
12189 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12190 : {
12191 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12192 6 : return hArray->m_poImpl->GetTotalElementsCount();
12193 : }
12194 :
12195 : /************************************************************************/
12196 : /* GDALMDArrayGetDimensionCount() */
12197 : /************************************************************************/
12198 :
12199 : /** Return the number of dimensions.
12200 : *
12201 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12202 : */
12203 10368 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12204 : {
12205 10368 : VALIDATE_POINTER1(hArray, __func__, 0);
12206 10368 : return hArray->m_poImpl->GetDimensionCount();
12207 : }
12208 :
12209 : /************************************************************************/
12210 : /* GDALMDArrayGetDimensions() */
12211 : /************************************************************************/
12212 :
12213 : /** Return the dimensions of the array
12214 : *
12215 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12216 : * array itself needs to be freed, CPLFree() should be called (and
12217 : * GDALDimensionRelease() on individual array members).
12218 : *
12219 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12220 : *
12221 : * @param hArray Array.
12222 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12223 : *
12224 : * @return an array of *pnCount dimensions.
12225 : */
12226 2299 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12227 : {
12228 2299 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12229 2299 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12230 2299 : const auto &dims(hArray->m_poImpl->GetDimensions());
12231 : auto ret = static_cast<GDALDimensionH *>(
12232 2299 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12233 6465 : for (size_t i = 0; i < dims.size(); i++)
12234 : {
12235 4166 : ret[i] = new GDALDimensionHS(dims[i]);
12236 : }
12237 2299 : *pnCount = dims.size();
12238 2299 : return ret;
12239 : }
12240 :
12241 : /************************************************************************/
12242 : /* GDALReleaseDimensions() */
12243 : /************************************************************************/
12244 :
12245 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12246 : *
12247 : * @param dims return pointer of above methods
12248 : * @param nCount *pnCount value returned by above methods
12249 : */
12250 2372 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12251 : {
12252 6695 : for (size_t i = 0; i < nCount; i++)
12253 : {
12254 4323 : delete dims[i];
12255 : }
12256 2372 : CPLFree(dims);
12257 2372 : }
12258 :
12259 : /************************************************************************/
12260 : /* GDALMDArrayGetDataType() */
12261 : /************************************************************************/
12262 :
12263 : /** Return the data type
12264 : *
12265 : * The return must be freed with GDALExtendedDataTypeRelease().
12266 : */
12267 3909 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12268 : {
12269 3909 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12270 : return new GDALExtendedDataTypeHS(
12271 3909 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12272 : }
12273 :
12274 : /************************************************************************/
12275 : /* GDALMDArrayRead() */
12276 : /************************************************************************/
12277 :
12278 : /** Read part or totality of a multidimensional array.
12279 : *
12280 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12281 : *
12282 : * @return TRUE in case of success.
12283 : */
12284 1956 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12285 : const size_t *count, const GInt64 *arrayStep,
12286 : const GPtrDiff_t *bufferStride,
12287 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12288 : const void *pDstBufferAllocStart,
12289 : size_t nDstBufferAllocSize)
12290 : {
12291 1956 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12292 1956 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12293 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12294 : {
12295 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12296 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12297 : }
12298 1956 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12299 1956 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12300 3912 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12301 1956 : *(bufferDataType->m_poImpl), pDstBuffer,
12302 1956 : pDstBufferAllocStart, nDstBufferAllocSize);
12303 : }
12304 :
12305 : /************************************************************************/
12306 : /* GDALMDArrayWrite() */
12307 : /************************************************************************/
12308 :
12309 : /** Write part or totality of a multidimensional array.
12310 : *
12311 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12312 : *
12313 : * @return TRUE in case of success.
12314 : */
12315 558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12316 : const size_t *count, const GInt64 *arrayStep,
12317 : const GPtrDiff_t *bufferStride,
12318 : GDALExtendedDataTypeH bufferDataType,
12319 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12320 : size_t nSrcBufferAllocSize)
12321 : {
12322 558 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12323 558 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12324 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12325 : {
12326 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12327 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12328 : }
12329 558 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12330 558 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12331 1116 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12332 558 : bufferStride, *(bufferDataType->m_poImpl),
12333 : pSrcBuffer, pSrcBufferAllocStart,
12334 558 : nSrcBufferAllocSize);
12335 : }
12336 :
12337 : /************************************************************************/
12338 : /* GDALMDArrayAdviseRead() */
12339 : /************************************************************************/
12340 :
12341 : /** Advise driver of upcoming read requests.
12342 : *
12343 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12344 : *
12345 : * @return TRUE in case of success.
12346 : *
12347 : * @since GDAL 3.2
12348 : */
12349 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12350 : const size_t *count)
12351 : {
12352 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12353 : }
12354 :
12355 : /************************************************************************/
12356 : /* GDALMDArrayAdviseReadEx() */
12357 : /************************************************************************/
12358 :
12359 : /** Advise driver of upcoming read requests.
12360 : *
12361 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12362 : *
12363 : * @return TRUE in case of success.
12364 : *
12365 : * @since GDAL 3.4
12366 : */
12367 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12368 : const size_t *count, CSLConstList papszOptions)
12369 : {
12370 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12371 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12372 : }
12373 :
12374 : /************************************************************************/
12375 : /* GDALMDArrayGetAttribute() */
12376 : /************************************************************************/
12377 :
12378 : /** Return an attribute by its name.
12379 : *
12380 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12381 : *
12382 : * The returned attribute must be freed with GDALAttributeRelease().
12383 : */
12384 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12385 : {
12386 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12387 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12388 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12389 119 : if (attr)
12390 110 : return new GDALAttributeHS(attr);
12391 9 : return nullptr;
12392 : }
12393 :
12394 : /************************************************************************/
12395 : /* GDALMDArrayGetAttributes() */
12396 : /************************************************************************/
12397 :
12398 : /** Return the list of attributes contained in this array.
12399 : *
12400 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12401 : * array itself needs to be freed, CPLFree() should be called (and
12402 : * GDALAttributeRelease() on individual array members).
12403 : *
12404 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12405 : *
12406 : * @param hArray Array.
12407 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12408 : * @param papszOptions Driver specific options determining how attributes
12409 : * should be retrieved. Pass nullptr for default behavior.
12410 : *
12411 : * @return an array of *pnCount attributes.
12412 : */
12413 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12414 : CSLConstList papszOptions)
12415 : {
12416 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12417 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12418 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12419 : auto ret = static_cast<GDALAttributeH *>(
12420 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12421 187 : for (size_t i = 0; i < attrs.size(); i++)
12422 : {
12423 129 : ret[i] = new GDALAttributeHS(attrs[i]);
12424 : }
12425 58 : *pnCount = attrs.size();
12426 58 : return ret;
12427 : }
12428 :
12429 : /************************************************************************/
12430 : /* GDALMDArrayCreateAttribute() */
12431 : /************************************************************************/
12432 :
12433 : /** Create a attribute within an array.
12434 : *
12435 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12436 : *
12437 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12438 : */
12439 160 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12440 : const char *pszName,
12441 : size_t nDimensions,
12442 : const GUInt64 *panDimensions,
12443 : GDALExtendedDataTypeH hEDT,
12444 : CSLConstList papszOptions)
12445 : {
12446 160 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12447 160 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12448 160 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12449 320 : std::vector<GUInt64> dims;
12450 160 : dims.reserve(nDimensions);
12451 195 : for (size_t i = 0; i < nDimensions; i++)
12452 35 : dims.push_back(panDimensions[i]);
12453 160 : auto ret = hArray->m_poImpl->CreateAttribute(
12454 480 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12455 160 : if (!ret)
12456 9 : return nullptr;
12457 151 : return new GDALAttributeHS(ret);
12458 : }
12459 :
12460 : /************************************************************************/
12461 : /* GDALMDArrayDeleteAttribute() */
12462 : /************************************************************************/
12463 :
12464 : /** Delete an attribute from an array.
12465 : *
12466 : * After this call, if a previously obtained instance of the deleted object
12467 : * is still alive, no method other than for freeing it should be invoked.
12468 : *
12469 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12470 : *
12471 : * @return true in case of success.
12472 : * @since GDAL 3.8
12473 : */
12474 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12475 : CSLConstList papszOptions)
12476 : {
12477 24 : VALIDATE_POINTER1(hArray, __func__, false);
12478 24 : VALIDATE_POINTER1(pszName, __func__, false);
12479 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12480 24 : papszOptions);
12481 : }
12482 :
12483 : /************************************************************************/
12484 : /* GDALMDArrayGetRawNoDataValue() */
12485 : /************************************************************************/
12486 :
12487 : /** Return the nodata value as a "raw" value.
12488 : *
12489 : * The value returned might be nullptr in case of no nodata value. When
12490 : * a nodata value is registered, a non-nullptr will be returned whose size in
12491 : * bytes is GetDataType().GetSize().
12492 : *
12493 : * The returned value should not be modified or freed.
12494 : *
12495 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12496 : *
12497 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12498 : */
12499 76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12500 : {
12501 76 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12502 76 : return hArray->m_poImpl->GetRawNoDataValue();
12503 : }
12504 :
12505 : /************************************************************************/
12506 : /* GDALMDArrayGetNoDataValueAsDouble() */
12507 : /************************************************************************/
12508 :
12509 : /** Return the nodata value as a double.
12510 : *
12511 : * The value returned might be nullptr in case of no nodata value. When
12512 : * a nodata value is registered, a non-nullptr will be returned whose size in
12513 : * bytes is GetDataType().GetSize().
12514 : *
12515 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12516 : *
12517 : * @param hArray Array handle.
12518 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12519 : * if a nodata value exists and can be converted to double. Might be nullptr.
12520 : *
12521 : * @return the nodata value as a double. A 0.0 value might also indicate the
12522 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12523 : * will be set to false then).
12524 : */
12525 120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12526 : int *pbHasNoDataValue)
12527 : {
12528 120 : VALIDATE_POINTER1(hArray, __func__, 0);
12529 120 : bool bHasNodataValue = false;
12530 120 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12531 120 : if (pbHasNoDataValue)
12532 120 : *pbHasNoDataValue = bHasNodataValue;
12533 120 : return ret;
12534 : }
12535 :
12536 : /************************************************************************/
12537 : /* GDALMDArrayGetNoDataValueAsInt64() */
12538 : /************************************************************************/
12539 :
12540 : /** Return the nodata value as a Int64.
12541 : *
12542 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12543 : *
12544 : * @param hArray Array handle.
12545 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12546 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12547 : *
12548 : * @return the nodata value as a Int64.
12549 : * @since GDAL 3.5
12550 : */
12551 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12552 : int *pbHasNoDataValue)
12553 : {
12554 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12555 11 : bool bHasNodataValue = false;
12556 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12557 11 : if (pbHasNoDataValue)
12558 11 : *pbHasNoDataValue = bHasNodataValue;
12559 11 : return ret;
12560 : }
12561 :
12562 : /************************************************************************/
12563 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12564 : /************************************************************************/
12565 :
12566 : /** Return the nodata value as a UInt64.
12567 : *
12568 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12569 : *
12570 : * @param hArray Array handle.
12571 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12572 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12573 : *
12574 : * @return the nodata value as a UInt64.
12575 : * @since GDAL 3.5
12576 : */
12577 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12578 : int *pbHasNoDataValue)
12579 : {
12580 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12581 7 : bool bHasNodataValue = false;
12582 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12583 7 : if (pbHasNoDataValue)
12584 7 : *pbHasNoDataValue = bHasNodataValue;
12585 7 : return ret;
12586 : }
12587 :
12588 : /************************************************************************/
12589 : /* GDALMDArraySetRawNoDataValue() */
12590 : /************************************************************************/
12591 :
12592 : /** Set the nodata value as a "raw" value.
12593 : *
12594 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12595 : * void*).
12596 : *
12597 : * @return TRUE in case of success.
12598 : */
12599 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12600 : {
12601 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12602 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12603 : }
12604 :
12605 : /************************************************************************/
12606 : /* GDALMDArraySetNoDataValueAsDouble() */
12607 : /************************************************************************/
12608 :
12609 : /** Set the nodata value as a double.
12610 : *
12611 : * If the natural data type of the attribute/array is not double, type
12612 : * conversion will occur to the type returned by GetDataType().
12613 : *
12614 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12615 : *
12616 : * @return TRUE in case of success.
12617 : */
12618 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12619 : {
12620 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12621 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12622 : }
12623 :
12624 : /************************************************************************/
12625 : /* GDALMDArraySetNoDataValueAsInt64() */
12626 : /************************************************************************/
12627 :
12628 : /** Set the nodata value as a Int64.
12629 : *
12630 : * If the natural data type of the attribute/array is not Int64, type conversion
12631 : * will occur to the type returned by GetDataType().
12632 : *
12633 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12634 : *
12635 : * @return TRUE in case of success.
12636 : * @since GDAL 3.5
12637 : */
12638 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12639 : {
12640 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12641 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12642 : }
12643 :
12644 : /************************************************************************/
12645 : /* GDALMDArraySetNoDataValueAsUInt64() */
12646 : /************************************************************************/
12647 :
12648 : /** Set the nodata value as a UInt64.
12649 : *
12650 : * If the natural data type of the attribute/array is not UInt64, type
12651 : * conversion will occur to the type returned by GetDataType().
12652 : *
12653 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12654 : *
12655 : * @return TRUE in case of success.
12656 : * @since GDAL 3.5
12657 : */
12658 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12659 : uint64_t nNoDataValue)
12660 : {
12661 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12662 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12663 : }
12664 :
12665 : /************************************************************************/
12666 : /* GDALMDArrayResize() */
12667 : /************************************************************************/
12668 :
12669 : /** Resize an array to new dimensions.
12670 : *
12671 : * Not all drivers may allow this operation, and with restrictions (e.g.
12672 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12673 : *
12674 : * Resizing a dimension used in other arrays will cause those other arrays
12675 : * to be resized.
12676 : *
12677 : * This is the same as the C++ method GDALMDArray::Resize().
12678 : *
12679 : * @param hArray Array.
12680 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12681 : * new size of each indexing dimension.
12682 : * @param papszOptions Options. (Driver specific)
12683 : * @return true in case of success.
12684 : * @since GDAL 3.7
12685 : */
12686 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12687 : CSLConstList papszOptions)
12688 : {
12689 42 : VALIDATE_POINTER1(hArray, __func__, false);
12690 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12691 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12692 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12693 : {
12694 83 : anNewDimSizes[i] = panNewDimSizes[i];
12695 : }
12696 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12697 : }
12698 :
12699 : /************************************************************************/
12700 : /* GDALMDArraySetScale() */
12701 : /************************************************************************/
12702 :
12703 : /** Set the scale value to apply to raw values.
12704 : *
12705 : * unscaled_value = raw_value * GetScale() + GetOffset()
12706 : *
12707 : * This is the same as the C++ method GDALMDArray::SetScale().
12708 : *
12709 : * @return TRUE in case of success.
12710 : */
12711 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12712 : {
12713 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12714 0 : return hArray->m_poImpl->SetScale(dfScale);
12715 : }
12716 :
12717 : /************************************************************************/
12718 : /* GDALMDArraySetScaleEx() */
12719 : /************************************************************************/
12720 :
12721 : /** Set the scale value to apply to raw values.
12722 : *
12723 : * unscaled_value = raw_value * GetScale() + GetOffset()
12724 : *
12725 : * This is the same as the C++ method GDALMDArray::SetScale().
12726 : *
12727 : * @return TRUE in case of success.
12728 : * @since GDAL 3.3
12729 : */
12730 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12731 : GDALDataType eStorageType)
12732 : {
12733 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12734 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12735 : }
12736 :
12737 : /************************************************************************/
12738 : /* GDALMDArraySetOffset() */
12739 : /************************************************************************/
12740 :
12741 : /** Set the scale value to apply to raw values.
12742 : *
12743 : * unscaled_value = raw_value * GetScale() + GetOffset()
12744 : *
12745 : * This is the same as the C++ method GDALMDArray::SetOffset().
12746 : *
12747 : * @return TRUE in case of success.
12748 : */
12749 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12750 : {
12751 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12752 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12753 : }
12754 :
12755 : /************************************************************************/
12756 : /* GDALMDArraySetOffsetEx() */
12757 : /************************************************************************/
12758 :
12759 : /** Set the scale value to apply to raw values.
12760 : *
12761 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12762 : *
12763 : * This is the same as the C++ method GDALMDArray::SetOffset().
12764 : *
12765 : * @return TRUE in case of success.
12766 : * @since GDAL 3.3
12767 : */
12768 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12769 : GDALDataType eStorageType)
12770 : {
12771 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12772 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12773 : }
12774 :
12775 : /************************************************************************/
12776 : /* GDALMDArrayGetScale() */
12777 : /************************************************************************/
12778 :
12779 : /** Get the scale value to apply to raw values.
12780 : *
12781 : * unscaled_value = raw_value * GetScale() + GetOffset()
12782 : *
12783 : * This is the same as the C++ method GDALMDArray::GetScale().
12784 : *
12785 : * @return the scale value
12786 : */
12787 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12788 : {
12789 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12790 103 : bool bHasValue = false;
12791 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12792 103 : if (pbHasValue)
12793 103 : *pbHasValue = bHasValue;
12794 103 : return dfRet;
12795 : }
12796 :
12797 : /************************************************************************/
12798 : /* GDALMDArrayGetScaleEx() */
12799 : /************************************************************************/
12800 :
12801 : /** Get the scale value to apply to raw values.
12802 : *
12803 : * unscaled_value = raw_value * GetScale() + GetScale()
12804 : *
12805 : * This is the same as the C++ method GDALMDArray::GetScale().
12806 : *
12807 : * @return the scale value
12808 : * @since GDAL 3.3
12809 : */
12810 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12811 : GDALDataType *peStorageType)
12812 : {
12813 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12814 5 : bool bHasValue = false;
12815 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12816 5 : if (pbHasValue)
12817 5 : *pbHasValue = bHasValue;
12818 5 : return dfRet;
12819 : }
12820 :
12821 : /************************************************************************/
12822 : /* GDALMDArrayGetOffset() */
12823 : /************************************************************************/
12824 :
12825 : /** Get the scale value to apply to raw values.
12826 : *
12827 : * unscaled_value = raw_value * GetScale() + GetOffset()
12828 : *
12829 : * This is the same as the C++ method GDALMDArray::GetOffset().
12830 : *
12831 : * @return the scale value
12832 : */
12833 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12834 : {
12835 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12836 100 : bool bHasValue = false;
12837 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12838 100 : if (pbHasValue)
12839 100 : *pbHasValue = bHasValue;
12840 100 : return dfRet;
12841 : }
12842 :
12843 : /************************************************************************/
12844 : /* GDALMDArrayGetOffsetEx() */
12845 : /************************************************************************/
12846 :
12847 : /** Get the scale value to apply to raw values.
12848 : *
12849 : * unscaled_value = raw_value * GetScale() + GetOffset()
12850 : *
12851 : * This is the same as the C++ method GDALMDArray::GetOffset().
12852 : *
12853 : * @return the scale value
12854 : * @since GDAL 3.3
12855 : */
12856 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12857 : GDALDataType *peStorageType)
12858 : {
12859 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12860 5 : bool bHasValue = false;
12861 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12862 5 : if (pbHasValue)
12863 5 : *pbHasValue = bHasValue;
12864 5 : return dfRet;
12865 : }
12866 :
12867 : /************************************************************************/
12868 : /* GDALMDArrayGetBlockSize() */
12869 : /************************************************************************/
12870 :
12871 : /** Return the "natural" block size of the array along all dimensions.
12872 : *
12873 : * Some drivers might organize the array in tiles/blocks and reading/writing
12874 : * aligned on those tile/block boundaries will be more efficient.
12875 : *
12876 : * The returned number of elements in the vector is the same as
12877 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12878 : * the natural block size along the considered dimension.
12879 : * "Flat" arrays will typically return a vector of values set to 0.
12880 : *
12881 : * The default implementation will return a vector of values set to 0.
12882 : *
12883 : * This method is used by GetProcessingChunkSize().
12884 : *
12885 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12886 : * theoretical case of a 32-bit platform, this might exceed its size_t
12887 : * allocation capabilities.
12888 : *
12889 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12890 : *
12891 : * @return the block size, in number of elements along each dimension.
12892 : */
12893 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12894 : {
12895 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12896 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12897 93 : auto res = hArray->m_poImpl->GetBlockSize();
12898 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12899 285 : for (size_t i = 0; i < res.size(); i++)
12900 : {
12901 192 : ret[i] = res[i];
12902 : }
12903 93 : *pnCount = res.size();
12904 93 : return ret;
12905 : }
12906 :
12907 : /***********************************************************************/
12908 : /* GDALMDArrayGetProcessingChunkSize() */
12909 : /************************************************************************/
12910 :
12911 : /** \brief Return an optimal chunk size for read/write operations, given the
12912 : * natural block size and memory constraints specified.
12913 : *
12914 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12915 : * multiple of those returned by GetBlockSize() (unless the block define by
12916 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12917 : * returned by this method).
12918 : *
12919 : * This is the same as the C++ method
12920 : * GDALAbstractMDArray::GetProcessingChunkSize().
12921 : *
12922 : * @param hArray Array.
12923 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12924 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12925 : * chunk.
12926 : *
12927 : * @return the chunk size, in number of elements along each dimension.
12928 : */
12929 :
12930 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12931 : size_t nMaxChunkMemory)
12932 : {
12933 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12934 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12935 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12936 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12937 3 : for (size_t i = 0; i < res.size(); i++)
12938 : {
12939 2 : ret[i] = res[i];
12940 : }
12941 1 : *pnCount = res.size();
12942 1 : return ret;
12943 : }
12944 :
12945 : /************************************************************************/
12946 : /* GDALMDArrayGetStructuralInfo() */
12947 : /************************************************************************/
12948 :
12949 : /** Return structural information on the array.
12950 : *
12951 : * This may be the compression, etc..
12952 : *
12953 : * The return value should not be freed and is valid until GDALMDArray is
12954 : * released or this function called again.
12955 : *
12956 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12957 : */
12958 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12959 : {
12960 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12961 15 : return hArray->m_poImpl->GetStructuralInfo();
12962 : }
12963 :
12964 : /************************************************************************/
12965 : /* GDALMDArrayGetView() */
12966 : /************************************************************************/
12967 :
12968 : /** Return a view of the array using slicing or field access.
12969 : *
12970 : * The returned object should be released with GDALMDArrayRelease().
12971 : *
12972 : * This is the same as the C++ method GDALMDArray::GetView().
12973 : */
12974 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12975 : {
12976 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12977 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12978 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12979 430 : if (!sliced)
12980 22 : return nullptr;
12981 408 : return new GDALMDArrayHS(sliced);
12982 : }
12983 :
12984 : /************************************************************************/
12985 : /* GDALMDArrayTranspose() */
12986 : /************************************************************************/
12987 :
12988 : /** Return a view of the array whose axis have been reordered.
12989 : *
12990 : * The returned object should be released with GDALMDArrayRelease().
12991 : *
12992 : * This is the same as the C++ method GDALMDArray::Transpose().
12993 : */
12994 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12995 : const int *panMapNewAxisToOldAxis)
12996 : {
12997 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12998 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12999 44 : if (nNewAxisCount)
13000 : {
13001 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13002 : nNewAxisCount * sizeof(int));
13003 : }
13004 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13005 44 : if (!reordered)
13006 7 : return nullptr;
13007 37 : return new GDALMDArrayHS(reordered);
13008 : }
13009 :
13010 : /************************************************************************/
13011 : /* GDALMDArrayGetUnscaled() */
13012 : /************************************************************************/
13013 :
13014 : /** Return an array that is the unscaled version of the current one.
13015 : *
13016 : * That is each value of the unscaled array will be
13017 : * unscaled_value = raw_value * GetScale() + GetOffset()
13018 : *
13019 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13020 : * from unscaled values to raw values.
13021 : *
13022 : * The returned object should be released with GDALMDArrayRelease().
13023 : *
13024 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13025 : */
13026 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13027 : {
13028 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13029 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13030 13 : if (!unscaled)
13031 0 : return nullptr;
13032 13 : return new GDALMDArrayHS(unscaled);
13033 : }
13034 :
13035 : /************************************************************************/
13036 : /* GDALMDArrayGetMask() */
13037 : /************************************************************************/
13038 :
13039 : /** Return an array that is a mask for the current array
13040 : *
13041 : * This array will be of type Byte, with values set to 0 to indicate invalid
13042 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13043 : *
13044 : * The returned object should be released with GDALMDArrayRelease().
13045 : *
13046 : * This is the same as the C++ method GDALMDArray::GetMask().
13047 : */
13048 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13049 : {
13050 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13051 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13052 35 : if (!unscaled)
13053 7 : return nullptr;
13054 28 : return new GDALMDArrayHS(unscaled);
13055 : }
13056 :
13057 : /************************************************************************/
13058 : /* GDALMDArrayGetResampled() */
13059 : /************************************************************************/
13060 :
13061 : /** Return an array that is a resampled / reprojected view of the current array
13062 : *
13063 : * This is the same as the C++ method GDALMDArray::GetResampled().
13064 : *
13065 : * Currently this method can only resample along the last 2 dimensions, unless
13066 : * orthorectifying a NASA EMIT dataset.
13067 : *
13068 : * The returned object should be released with GDALMDArrayRelease().
13069 : *
13070 : * @since 3.4
13071 : */
13072 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13073 : const GDALDimensionH *pahNewDims,
13074 : GDALRIOResampleAlg resampleAlg,
13075 : OGRSpatialReferenceH hTargetSRS,
13076 : CSLConstList papszOptions)
13077 : {
13078 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13079 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13080 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13081 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13082 : {
13083 78 : if (pahNewDims[i])
13084 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13085 : }
13086 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13087 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13088 68 : papszOptions);
13089 34 : if (!poNewArray)
13090 8 : return nullptr;
13091 26 : return new GDALMDArrayHS(poNewArray);
13092 : }
13093 :
13094 : /************************************************************************/
13095 : /* GDALMDArraySetUnit() */
13096 : /************************************************************************/
13097 :
13098 : /** Set the variable unit.
13099 : *
13100 : * Values should conform as much as possible with those allowed by
13101 : * the NetCDF CF conventions:
13102 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13103 : * but others might be returned.
13104 : *
13105 : * Few examples are "meter", "degrees", "second", ...
13106 : * Empty value means unknown.
13107 : *
13108 : * This is the same as the C function GDALMDArraySetUnit()
13109 : *
13110 : * @param hArray array.
13111 : * @param pszUnit unit name.
13112 : * @return TRUE in case of success.
13113 : */
13114 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13115 : {
13116 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13117 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13118 : }
13119 :
13120 : /************************************************************************/
13121 : /* GDALMDArrayGetUnit() */
13122 : /************************************************************************/
13123 :
13124 : /** Return the array unit.
13125 : *
13126 : * Values should conform as much as possible with those allowed by
13127 : * the NetCDF CF conventions:
13128 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13129 : * but others might be returned.
13130 : *
13131 : * Few examples are "meter", "degrees", "second", ...
13132 : * Empty value means unknown.
13133 : *
13134 : * The return value should not be freed and is valid until GDALMDArray is
13135 : * released or this function called again.
13136 : *
13137 : * This is the same as the C++ method GDALMDArray::GetUnit().
13138 : */
13139 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13140 : {
13141 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13142 111 : return hArray->m_poImpl->GetUnit().c_str();
13143 : }
13144 :
13145 : /************************************************************************/
13146 : /* GDALMDArrayGetSpatialRef() */
13147 : /************************************************************************/
13148 :
13149 : /** Assign a spatial reference system object to the array.
13150 : *
13151 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13152 : * @return TRUE in case of success.
13153 : */
13154 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13155 : {
13156 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13157 60 : return hArray->m_poImpl->SetSpatialRef(
13158 60 : OGRSpatialReference::FromHandle(hSRS));
13159 : }
13160 :
13161 : /************************************************************************/
13162 : /* GDALMDArrayGetSpatialRef() */
13163 : /************************************************************************/
13164 :
13165 : /** Return the spatial reference system object associated with the array.
13166 : *
13167 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13168 : *
13169 : * The returned object must be freed with OSRDestroySpatialReference().
13170 : */
13171 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13172 : {
13173 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13174 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13175 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13176 : }
13177 :
13178 : /************************************************************************/
13179 : /* GDALMDArrayGetStatistics() */
13180 : /************************************************************************/
13181 :
13182 : /**
13183 : * \brief Fetch statistics.
13184 : *
13185 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13186 : *
13187 : * @since GDAL 3.2
13188 : */
13189 :
13190 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13191 : int bApproxOK, int bForce, double *pdfMin,
13192 : double *pdfMax, double *pdfMean,
13193 : double *pdfStdDev, GUInt64 *pnValidCount,
13194 : GDALProgressFunc pfnProgress,
13195 : void *pProgressData)
13196 : {
13197 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13198 30 : return hArray->m_poImpl->GetStatistics(
13199 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13200 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13201 : }
13202 :
13203 : /************************************************************************/
13204 : /* GDALMDArrayComputeStatistics() */
13205 : /************************************************************************/
13206 :
13207 : /**
13208 : * \brief Compute statistics.
13209 : *
13210 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13211 : *
13212 : * @since GDAL 3.2
13213 : * @see GDALMDArrayComputeStatisticsEx()
13214 : */
13215 :
13216 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13217 : int bApproxOK, double *pdfMin, double *pdfMax,
13218 : double *pdfMean, double *pdfStdDev,
13219 : GUInt64 *pnValidCount,
13220 : GDALProgressFunc pfnProgress,
13221 : void *pProgressData)
13222 : {
13223 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13224 0 : return hArray->m_poImpl->ComputeStatistics(
13225 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13226 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13227 : }
13228 :
13229 : /************************************************************************/
13230 : /* GDALMDArrayComputeStatisticsEx() */
13231 : /************************************************************************/
13232 :
13233 : /**
13234 : * \brief Compute statistics.
13235 : *
13236 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13237 : *
13238 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13239 : *
13240 : * @since GDAL 3.8
13241 : */
13242 :
13243 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13244 : int bApproxOK, double *pdfMin,
13245 : double *pdfMax, double *pdfMean,
13246 : double *pdfStdDev, GUInt64 *pnValidCount,
13247 : GDALProgressFunc pfnProgress,
13248 : void *pProgressData,
13249 : CSLConstList papszOptions)
13250 : {
13251 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13252 8 : return hArray->m_poImpl->ComputeStatistics(
13253 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13254 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13255 : }
13256 :
13257 : /************************************************************************/
13258 : /* GDALMDArrayGetCoordinateVariables() */
13259 : /************************************************************************/
13260 :
13261 : /** Return coordinate variables.
13262 : *
13263 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13264 : * itself needs to be freed, CPLFree() should be called (and
13265 : * GDALMDArrayRelease() on individual array members).
13266 : *
13267 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13268 : *
13269 : * @param hArray Array.
13270 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13271 : *
13272 : * @return an array of *pnCount arrays.
13273 : * @since 3.4
13274 : */
13275 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13276 : size_t *pnCount)
13277 : {
13278 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13279 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13280 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13281 : auto ret = static_cast<GDALMDArrayH *>(
13282 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13283 29 : for (size_t i = 0; i < coordinates.size(); i++)
13284 : {
13285 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13286 : }
13287 13 : *pnCount = coordinates.size();
13288 13 : return ret;
13289 : }
13290 :
13291 : /************************************************************************/
13292 : /* GDALMDArrayGetGridded() */
13293 : /************************************************************************/
13294 :
13295 : /** Return a gridded array from scattered point data, that is from an array
13296 : * whose last dimension is the indexing variable of X and Y arrays.
13297 : *
13298 : * The returned object should be released with GDALMDArrayRelease().
13299 : *
13300 : * This is the same as the C++ method GDALMDArray::GetGridded().
13301 : *
13302 : * @since GDAL 3.7
13303 : */
13304 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13305 : const char *pszGridOptions,
13306 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13307 : CSLConstList papszOptions)
13308 : {
13309 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13310 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13311 22 : auto gridded = hArray->m_poImpl->GetGridded(
13312 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13313 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13314 22 : if (!gridded)
13315 19 : return nullptr;
13316 3 : return new GDALMDArrayHS(gridded);
13317 : }
13318 :
13319 : /************************************************************************/
13320 : /* GDALMDArrayGetMeshGrid() */
13321 : /************************************************************************/
13322 :
13323 : /** Return a list of multidimensional arrays from a list of one-dimensional
13324 : * arrays.
13325 : *
13326 : * This is typically used to transform one-dimensional longitude, latitude
13327 : * arrays into 2D ones.
13328 : *
13329 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13330 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13331 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13332 : * repeated to fill the matrix along the first dimension for x1, the second
13333 : * for x2 and so on.
13334 : *
13335 : * For example, if x = [1, 2], and y = [3, 4, 5],
13336 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13337 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13338 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13339 : *
13340 : * and
13341 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13342 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13343 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13344 : *
13345 : * The currently supported options are:
13346 : * <ul>
13347 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13348 : * output.
13349 : * </li>
13350 : * </ul>
13351 : *
13352 : * This is the same as
13353 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13354 : * function.
13355 : *
13356 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13357 : * If only the array itself needs to be freed, CPLFree() should be called
13358 : * (and GDALMDArrayRelease() on individual array members).
13359 : *
13360 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13361 : *
13362 : * @param pahInputArrays Input arrays
13363 : * @param nCountInputArrays Number of input arrays
13364 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13365 : * @param papszOptions NULL, or NULL terminated list of options.
13366 : *
13367 : * @return an array of *pnCountOutputArrays arrays.
13368 : * @since 3.10
13369 : */
13370 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13371 : size_t nCountInputArrays,
13372 : size_t *pnCountOutputArrays,
13373 : CSLConstList papszOptions)
13374 : {
13375 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13376 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13377 :
13378 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13379 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13380 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13381 :
13382 : const auto apoOutputArrays =
13383 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13384 : auto ret = static_cast<GDALMDArrayH *>(
13385 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13386 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13387 : {
13388 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13389 : }
13390 7 : *pnCountOutputArrays = apoOutputArrays.size();
13391 7 : return ret;
13392 : }
13393 :
13394 : /************************************************************************/
13395 : /* GDALReleaseArrays() */
13396 : /************************************************************************/
13397 :
13398 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13399 : *
13400 : * @param arrays return pointer of above methods
13401 : * @param nCount *pnCount value returned by above methods
13402 : */
13403 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13404 : {
13405 46 : for (size_t i = 0; i < nCount; i++)
13406 : {
13407 26 : delete arrays[i];
13408 : }
13409 20 : CPLFree(arrays);
13410 20 : }
13411 :
13412 : /************************************************************************/
13413 : /* GDALMDArrayCache() */
13414 : /************************************************************************/
13415 :
13416 : /**
13417 : * \brief Cache the content of the array into an auxiliary filename.
13418 : *
13419 : * This is the same as the C++ method GDALMDArray::Cache().
13420 : *
13421 : * @since GDAL 3.4
13422 : */
13423 :
13424 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13425 : {
13426 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13427 7 : return hArray->m_poImpl->Cache(papszOptions);
13428 : }
13429 :
13430 : /************************************************************************/
13431 : /* GDALMDArrayRename() */
13432 : /************************************************************************/
13433 :
13434 : /** Rename the array.
13435 : *
13436 : * This is not implemented by all drivers.
13437 : *
13438 : * Drivers known to implement it: MEM, netCDF, Zarr.
13439 : *
13440 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13441 : *
13442 : * @return true in case of success
13443 : * @since GDAL 3.8
13444 : */
13445 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13446 : {
13447 28 : VALIDATE_POINTER1(hArray, __func__, false);
13448 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13449 28 : return hArray->m_poImpl->Rename(pszNewName);
13450 : }
13451 :
13452 : /************************************************************************/
13453 : /* GDALAttributeRelease() */
13454 : /************************************************************************/
13455 :
13456 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13457 : *
13458 : * Note: when applied on a object coming from a driver, this does not
13459 : * destroy the object in the file, database, etc...
13460 : */
13461 732 : void GDALAttributeRelease(GDALAttributeH hAttr)
13462 : {
13463 732 : delete hAttr;
13464 732 : }
13465 :
13466 : /************************************************************************/
13467 : /* GDALAttributeGetName() */
13468 : /************************************************************************/
13469 :
13470 : /** Return the name of the attribute.
13471 : *
13472 : * The returned pointer is valid until hAttr is released.
13473 : *
13474 : * This is the same as the C++ method GDALAttribute::GetName().
13475 : */
13476 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13477 : {
13478 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13479 361 : return hAttr->m_poImpl->GetName().c_str();
13480 : }
13481 :
13482 : /************************************************************************/
13483 : /* GDALAttributeGetFullName() */
13484 : /************************************************************************/
13485 :
13486 : /** Return the full name of the attribute.
13487 : *
13488 : * The returned pointer is valid until hAttr is released.
13489 : *
13490 : * This is the same as the C++ method GDALAttribute::GetFullName().
13491 : */
13492 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13493 : {
13494 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13495 49 : return hAttr->m_poImpl->GetFullName().c_str();
13496 : }
13497 :
13498 : /************************************************************************/
13499 : /* GDALAttributeGetTotalElementsCount() */
13500 : /************************************************************************/
13501 :
13502 : /** Return the total number of values in the attribute.
13503 : *
13504 : * This is the same as the C++ method
13505 : * GDALAbstractMDArray::GetTotalElementsCount()
13506 : */
13507 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13508 : {
13509 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13510 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13511 : }
13512 :
13513 : /************************************************************************/
13514 : /* GDALAttributeGetDimensionCount() */
13515 : /************************************************************************/
13516 :
13517 : /** Return the number of dimensions.
13518 : *
13519 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13520 : */
13521 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13522 : {
13523 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13524 12 : return hAttr->m_poImpl->GetDimensionCount();
13525 : }
13526 :
13527 : /************************************************************************/
13528 : /* GDALAttributeGetDimensionsSize() */
13529 : /************************************************************************/
13530 :
13531 : /** Return the dimension sizes of the attribute.
13532 : *
13533 : * The returned array must be freed with CPLFree()
13534 : *
13535 : * @param hAttr Attribute.
13536 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13537 : *
13538 : * @return an array of *pnCount values.
13539 : */
13540 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13541 : {
13542 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13543 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13544 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13545 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13546 22 : for (size_t i = 0; i < dims.size(); i++)
13547 : {
13548 11 : ret[i] = dims[i]->GetSize();
13549 : }
13550 11 : *pnCount = dims.size();
13551 11 : return ret;
13552 : }
13553 :
13554 : /************************************************************************/
13555 : /* GDALAttributeGetDataType() */
13556 : /************************************************************************/
13557 :
13558 : /** Return the data type
13559 : *
13560 : * The return must be freed with GDALExtendedDataTypeRelease().
13561 : */
13562 429 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13563 : {
13564 429 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13565 : return new GDALExtendedDataTypeHS(
13566 429 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13567 : }
13568 :
13569 : /************************************************************************/
13570 : /* GDALAttributeReadAsRaw() */
13571 : /************************************************************************/
13572 :
13573 : /** Return the raw value of an attribute.
13574 : *
13575 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13576 : *
13577 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13578 : *
13579 : * @param hAttr Attribute.
13580 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13581 : *
13582 : * @return a buffer of *pnSize bytes.
13583 : */
13584 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13585 : {
13586 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13587 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13588 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13589 6 : *pnSize = res.size();
13590 6 : auto ret = res.StealData();
13591 6 : if (!ret)
13592 : {
13593 0 : *pnSize = 0;
13594 0 : return nullptr;
13595 : }
13596 6 : return ret;
13597 : }
13598 :
13599 : /************************************************************************/
13600 : /* GDALAttributeFreeRawResult() */
13601 : /************************************************************************/
13602 :
13603 : /** Free the return of GDALAttributeAsRaw()
13604 : */
13605 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13606 : CPL_UNUSED size_t nSize)
13607 : {
13608 6 : VALIDATE_POINTER0(hAttr, __func__);
13609 6 : if (raw)
13610 : {
13611 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13612 6 : const auto nDTSize(dt.GetSize());
13613 6 : GByte *pabyPtr = raw;
13614 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13615 6 : CPLAssert(nSize == nDTSize * nEltCount);
13616 12 : for (size_t i = 0; i < nEltCount; ++i)
13617 : {
13618 6 : dt.FreeDynamicMemory(pabyPtr);
13619 6 : pabyPtr += nDTSize;
13620 : }
13621 6 : CPLFree(raw);
13622 : }
13623 : }
13624 :
13625 : /************************************************************************/
13626 : /* GDALAttributeReadAsString() */
13627 : /************************************************************************/
13628 :
13629 : /** Return the value of an attribute as a string.
13630 : *
13631 : * The returned string should not be freed, and its lifetime does not
13632 : * excess a next call to ReadAsString() on the same object, or the deletion
13633 : * of the object itself.
13634 : *
13635 : * This function will only return the first element if there are several.
13636 : *
13637 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13638 : *
13639 : * @return a string, or nullptr.
13640 : */
13641 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13642 : {
13643 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13644 107 : return hAttr->m_poImpl->ReadAsString();
13645 : }
13646 :
13647 : /************************************************************************/
13648 : /* GDALAttributeReadAsInt() */
13649 : /************************************************************************/
13650 :
13651 : /** Return the value of an attribute as a integer.
13652 : *
13653 : * This function will only return the first element if there are several.
13654 : *
13655 : * It can fail if its value can not be converted to integer.
13656 : *
13657 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13658 : *
13659 : * @return a integer, or INT_MIN in case of error.
13660 : */
13661 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13662 : {
13663 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13664 22 : return hAttr->m_poImpl->ReadAsInt();
13665 : }
13666 :
13667 : /************************************************************************/
13668 : /* GDALAttributeReadAsInt64() */
13669 : /************************************************************************/
13670 :
13671 : /** Return the value of an attribute as a int64_t.
13672 : *
13673 : * This function will only return the first element if there are several.
13674 : *
13675 : * It can fail if its value can not be converted to integer.
13676 : *
13677 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13678 : *
13679 : * @return an int64_t, or INT64_MIN in case of error.
13680 : */
13681 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13682 : {
13683 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13684 15 : return hAttr->m_poImpl->ReadAsInt64();
13685 : }
13686 :
13687 : /************************************************************************/
13688 : /* GDALAttributeReadAsDouble() */
13689 : /************************************************************************/
13690 :
13691 : /** Return the value of an attribute as a double.
13692 : *
13693 : * This function will only return the first element if there are several.
13694 : *
13695 : * It can fail if its value can not be converted to double.
13696 : *
13697 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13698 : *
13699 : * @return a double value.
13700 : */
13701 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13702 : {
13703 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13704 40 : return hAttr->m_poImpl->ReadAsDouble();
13705 : }
13706 :
13707 : /************************************************************************/
13708 : /* GDALAttributeReadAsStringArray() */
13709 : /************************************************************************/
13710 :
13711 : /** Return the value of an attribute as an array of strings.
13712 : *
13713 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13714 : *
13715 : * The return value must be freed with CSLDestroy().
13716 : */
13717 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13718 : {
13719 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13720 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13721 : }
13722 :
13723 : /************************************************************************/
13724 : /* GDALAttributeReadAsIntArray() */
13725 : /************************************************************************/
13726 :
13727 : /** Return the value of an attribute as an array of integers.
13728 : *
13729 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13730 : *
13731 : * @param hAttr Attribute
13732 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13733 : * @return array to be freed with CPLFree(), or nullptr.
13734 : */
13735 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13736 : {
13737 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13738 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13739 15 : *pnCount = 0;
13740 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13741 15 : if (tmp.empty())
13742 0 : return nullptr;
13743 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13744 15 : if (!ret)
13745 0 : return nullptr;
13746 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13747 15 : *pnCount = tmp.size();
13748 15 : return ret;
13749 : }
13750 :
13751 : /************************************************************************/
13752 : /* GDALAttributeReadAsInt64Array() */
13753 : /************************************************************************/
13754 :
13755 : /** Return the value of an attribute as an array of int64_t.
13756 : *
13757 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
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 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13764 : {
13765 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13766 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13767 14 : *pnCount = 0;
13768 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13769 14 : if (tmp.empty())
13770 0 : return nullptr;
13771 : auto ret = static_cast<int64_t *>(
13772 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13773 14 : if (!ret)
13774 0 : return nullptr;
13775 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13776 14 : *pnCount = tmp.size();
13777 14 : return ret;
13778 : }
13779 :
13780 : /************************************************************************/
13781 : /* GDALAttributeReadAsDoubleArray() */
13782 : /************************************************************************/
13783 :
13784 : /** Return the value of an attribute as an array of doubles.
13785 : *
13786 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13787 : *
13788 : * @param hAttr Attribute
13789 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13790 : * @return array to be freed with CPLFree(), or nullptr.
13791 : */
13792 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13793 : {
13794 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13795 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13796 29 : *pnCount = 0;
13797 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13798 29 : if (tmp.empty())
13799 0 : return nullptr;
13800 : auto ret =
13801 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13802 29 : if (!ret)
13803 0 : return nullptr;
13804 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13805 29 : *pnCount = tmp.size();
13806 29 : return ret;
13807 : }
13808 :
13809 : /************************************************************************/
13810 : /* GDALAttributeWriteRaw() */
13811 : /************************************************************************/
13812 :
13813 : /** Write an attribute from raw values expressed in GetDataType()
13814 : *
13815 : * The values should be provided in the type of GetDataType() and there should
13816 : * be exactly GetTotalElementsCount() of them.
13817 : * If GetDataType() is a string, each value should be a char* pointer.
13818 : *
13819 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13820 : *
13821 : * @param hAttr Attribute
13822 : * @param pabyValue Buffer of nLen bytes.
13823 : * @param nLength Size of pabyValue in bytes. Should be equal to
13824 : * GetTotalElementsCount() * GetDataType().GetSize()
13825 : * @return TRUE in case of success.
13826 : */
13827 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13828 : size_t nLength)
13829 : {
13830 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13831 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13832 : }
13833 :
13834 : /************************************************************************/
13835 : /* GDALAttributeWriteString() */
13836 : /************************************************************************/
13837 :
13838 : /** Write an attribute from a string value.
13839 : *
13840 : * Type conversion will be performed if needed. If the attribute contains
13841 : * multiple values, only the first one will be updated.
13842 : *
13843 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13844 : *
13845 : * @param hAttr Attribute
13846 : * @param pszVal Pointer to a string.
13847 : * @return TRUE in case of success.
13848 : */
13849 177 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13850 : {
13851 177 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13852 177 : return hAttr->m_poImpl->Write(pszVal);
13853 : }
13854 :
13855 : /************************************************************************/
13856 : /* GDALAttributeWriteInt() */
13857 : /************************************************************************/
13858 :
13859 : /** Write an attribute from a integer value.
13860 : *
13861 : * Type conversion will be performed if needed. If the attribute contains
13862 : * multiple values, only the first one will be updated.
13863 : *
13864 : * This is the same as the C++ method GDALAttribute::WriteInt()
13865 : *
13866 : * @param hAttr Attribute
13867 : * @param nVal Value.
13868 : * @return TRUE in case of success.
13869 : */
13870 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13871 : {
13872 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13873 22 : return hAttr->m_poImpl->WriteInt(nVal);
13874 : }
13875 :
13876 : /************************************************************************/
13877 : /* GDALAttributeWriteInt64() */
13878 : /************************************************************************/
13879 :
13880 : /** Write an attribute from an int64_t value.
13881 : *
13882 : * Type conversion will be performed if needed. If the attribute contains
13883 : * multiple values, only the first one will be updated.
13884 : *
13885 : * This is the same as the C++ method GDALAttribute::WriteLong()
13886 : *
13887 : * @param hAttr Attribute
13888 : * @param nVal Value.
13889 : * @return TRUE in case of success.
13890 : */
13891 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13892 : {
13893 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13894 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13895 : }
13896 :
13897 : /************************************************************************/
13898 : /* GDALAttributeWriteDouble() */
13899 : /************************************************************************/
13900 :
13901 : /** Write an attribute from a double value.
13902 : *
13903 : * Type conversion will be performed if needed. If the attribute contains
13904 : * multiple values, only the first one will be updated.
13905 : *
13906 : * This is the same as the C++ method GDALAttribute::Write(double);
13907 : *
13908 : * @param hAttr Attribute
13909 : * @param dfVal Value.
13910 : *
13911 : * @return TRUE in case of success.
13912 : */
13913 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13914 : {
13915 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13916 11 : return hAttr->m_poImpl->Write(dfVal);
13917 : }
13918 :
13919 : /************************************************************************/
13920 : /* GDALAttributeWriteStringArray() */
13921 : /************************************************************************/
13922 :
13923 : /** Write an attribute from an array of strings.
13924 : *
13925 : * Type conversion will be performed if needed.
13926 : *
13927 : * Exactly GetTotalElementsCount() strings must be provided
13928 : *
13929 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13930 : *
13931 : * @param hAttr Attribute
13932 : * @param papszValues Array of strings.
13933 : * @return TRUE in case of success.
13934 : */
13935 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13936 : CSLConstList papszValues)
13937 : {
13938 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13939 8 : return hAttr->m_poImpl->Write(papszValues);
13940 : }
13941 :
13942 : /************************************************************************/
13943 : /* GDALAttributeWriteIntArray() */
13944 : /************************************************************************/
13945 :
13946 : /** Write an attribute from an array of int.
13947 : *
13948 : * Type conversion will be performed if needed.
13949 : *
13950 : * Exactly GetTotalElementsCount() strings must be provided
13951 : *
13952 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13953 : * size_t)
13954 : *
13955 : * @param hAttr Attribute
13956 : * @param panValues Array of int.
13957 : * @param nCount Should be equal to GetTotalElementsCount().
13958 : * @return TRUE in case of success.
13959 : */
13960 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13961 : size_t nCount)
13962 : {
13963 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13964 11 : return hAttr->m_poImpl->Write(panValues, nCount);
13965 : }
13966 :
13967 : /************************************************************************/
13968 : /* GDALAttributeWriteInt64Array() */
13969 : /************************************************************************/
13970 :
13971 : /** Write an attribute from an array of int64_t.
13972 : *
13973 : * Type conversion will be performed if needed.
13974 : *
13975 : * Exactly GetTotalElementsCount() strings must be provided
13976 : *
13977 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13978 : * size_t)
13979 : *
13980 : * @param hAttr Attribute
13981 : * @param panValues Array of int64_t.
13982 : * @param nCount Should be equal to GetTotalElementsCount().
13983 : * @return TRUE in case of success.
13984 : */
13985 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13986 : size_t nCount)
13987 : {
13988 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13989 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13990 : }
13991 :
13992 : /************************************************************************/
13993 : /* GDALAttributeWriteDoubleArray() */
13994 : /************************************************************************/
13995 :
13996 : /** Write an attribute from an array of double.
13997 : *
13998 : * Type conversion will be performed if needed.
13999 : *
14000 : * Exactly GetTotalElementsCount() strings must be provided
14001 : *
14002 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14003 : * size_t)
14004 : *
14005 : * @param hAttr Attribute
14006 : * @param padfValues Array of double.
14007 : * @param nCount Should be equal to GetTotalElementsCount().
14008 : * @return TRUE in case of success.
14009 : */
14010 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14011 : const double *padfValues, size_t nCount)
14012 : {
14013 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14014 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14015 : }
14016 :
14017 : /************************************************************************/
14018 : /* GDALAttributeRename() */
14019 : /************************************************************************/
14020 :
14021 : /** Rename the attribute.
14022 : *
14023 : * This is not implemented by all drivers.
14024 : *
14025 : * Drivers known to implement it: MEM, netCDF.
14026 : *
14027 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14028 : *
14029 : * @return true in case of success
14030 : * @since GDAL 3.8
14031 : */
14032 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14033 : {
14034 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14035 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14036 27 : return hAttr->m_poImpl->Rename(pszNewName);
14037 : }
14038 :
14039 : /************************************************************************/
14040 : /* GDALDimensionRelease() */
14041 : /************************************************************************/
14042 :
14043 : /** Release the GDAL in-memory object associated with a GDALDimension.
14044 : *
14045 : * Note: when applied on a object coming from a driver, this does not
14046 : * destroy the object in the file, database, etc...
14047 : */
14048 4910 : void GDALDimensionRelease(GDALDimensionH hDim)
14049 : {
14050 4910 : delete hDim;
14051 4910 : }
14052 :
14053 : /************************************************************************/
14054 : /* GDALDimensionGetName() */
14055 : /************************************************************************/
14056 :
14057 : /** Return dimension name.
14058 : *
14059 : * This is the same as the C++ method GDALDimension::GetName()
14060 : */
14061 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14062 : {
14063 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14064 284 : return hDim->m_poImpl->GetName().c_str();
14065 : }
14066 :
14067 : /************************************************************************/
14068 : /* GDALDimensionGetFullName() */
14069 : /************************************************************************/
14070 :
14071 : /** Return dimension full name.
14072 : *
14073 : * This is the same as the C++ method GDALDimension::GetFullName()
14074 : */
14075 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14076 : {
14077 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14078 80 : return hDim->m_poImpl->GetFullName().c_str();
14079 : }
14080 :
14081 : /************************************************************************/
14082 : /* GDALDimensionGetType() */
14083 : /************************************************************************/
14084 :
14085 : /** Return dimension type.
14086 : *
14087 : * This is the same as the C++ method GDALDimension::GetType()
14088 : */
14089 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14090 : {
14091 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14092 62 : return hDim->m_poImpl->GetType().c_str();
14093 : }
14094 :
14095 : /************************************************************************/
14096 : /* GDALDimensionGetDirection() */
14097 : /************************************************************************/
14098 :
14099 : /** Return dimension direction.
14100 : *
14101 : * This is the same as the C++ method GDALDimension::GetDirection()
14102 : */
14103 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14104 : {
14105 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14106 32 : return hDim->m_poImpl->GetDirection().c_str();
14107 : }
14108 :
14109 : /************************************************************************/
14110 : /* GDALDimensionGetSize() */
14111 : /************************************************************************/
14112 :
14113 : /** Return the size, that is the number of values along the dimension.
14114 : *
14115 : * This is the same as the C++ method GDALDimension::GetSize()
14116 : */
14117 3685 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14118 : {
14119 3685 : VALIDATE_POINTER1(hDim, __func__, 0);
14120 3685 : return hDim->m_poImpl->GetSize();
14121 : }
14122 :
14123 : /************************************************************************/
14124 : /* GDALDimensionGetIndexingVariable() */
14125 : /************************************************************************/
14126 :
14127 : /** Return the variable that is used to index the dimension (if there is one).
14128 : *
14129 : * This is the array, typically one-dimensional, describing the values taken
14130 : * by the dimension.
14131 : *
14132 : * The returned value should be freed with GDALMDArrayRelease().
14133 : *
14134 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14135 : */
14136 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14137 : {
14138 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14139 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
14140 118 : if (!var)
14141 10 : return nullptr;
14142 108 : return new GDALMDArrayHS(var);
14143 : }
14144 :
14145 : /************************************************************************/
14146 : /* GDALDimensionSetIndexingVariable() */
14147 : /************************************************************************/
14148 :
14149 : /** Set the variable that is used to index the dimension.
14150 : *
14151 : * This is the array, typically one-dimensional, describing the values taken
14152 : * by the dimension.
14153 : *
14154 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14155 : *
14156 : * @return TRUE in case of success.
14157 : */
14158 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14159 : {
14160 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14161 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14162 46 : : nullptr);
14163 : }
14164 :
14165 : /************************************************************************/
14166 : /* GDALDimensionRename() */
14167 : /************************************************************************/
14168 :
14169 : /** Rename the dimension.
14170 : *
14171 : * This is not implemented by all drivers.
14172 : *
14173 : * Drivers known to implement it: MEM, netCDF.
14174 : *
14175 : * This is the same as the C++ method GDALDimension::Rename()
14176 : *
14177 : * @return true in case of success
14178 : * @since GDAL 3.8
14179 : */
14180 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14181 : {
14182 31 : VALIDATE_POINTER1(hDim, __func__, false);
14183 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14184 31 : return hDim->m_poImpl->Rename(pszNewName);
14185 : }
14186 :
14187 : /************************************************************************/
14188 : /* GDALDatasetGetRootGroup() */
14189 : /************************************************************************/
14190 :
14191 : /** Return the root GDALGroup of this dataset.
14192 : *
14193 : * Only valid for multidimensional datasets.
14194 : *
14195 : * The returned value must be freed with GDALGroupRelease().
14196 : *
14197 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14198 : *
14199 : * @since GDAL 3.1
14200 : */
14201 1176 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14202 : {
14203 1176 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14204 1176 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14205 1176 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14206 : }
14207 :
14208 : /************************************************************************/
14209 : /* GDALRasterBandAsMDArray() */
14210 : /************************************************************************/
14211 :
14212 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14213 : *
14214 : * The band must be linked to a GDALDataset. If this dataset is not already
14215 : * marked as shared, it will be, so that the returned array holds a reference
14216 : * to it.
14217 : *
14218 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14219 : * returned array will have an associated indexing variable.
14220 : *
14221 : * The returned pointer must be released with GDALMDArrayRelease().
14222 : *
14223 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14224 : *
14225 : * @return a new array, or NULL.
14226 : *
14227 : * @since GDAL 3.1
14228 : */
14229 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14230 : {
14231 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14232 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14233 21 : if (!poArray)
14234 0 : return nullptr;
14235 21 : return new GDALMDArrayHS(poArray);
14236 : }
14237 :
14238 : /************************************************************************/
14239 : /* GDALMDArrayAsClassicDataset() */
14240 : /************************************************************************/
14241 :
14242 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14243 : *
14244 : * Only 2D or more arrays are supported.
14245 : *
14246 : * In the case of > 2D arrays, additional dimensions will be represented as
14247 : * raster bands.
14248 : *
14249 : * The "reverse" method is GDALRasterBand::AsMDArray().
14250 : *
14251 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14252 : *
14253 : * @param hArray Array.
14254 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14255 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14256 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14257 : */
14258 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14259 : size_t iYDim)
14260 : {
14261 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14262 0 : return GDALDataset::ToHandle(
14263 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14264 : }
14265 :
14266 : /************************************************************************/
14267 : /* GDALMDArrayAsClassicDatasetEx() */
14268 : /************************************************************************/
14269 :
14270 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14271 : *
14272 : * Only 2D or more arrays are supported.
14273 : *
14274 : * In the case of > 2D arrays, additional dimensions will be represented as
14275 : * raster bands.
14276 : *
14277 : * The "reverse" method is GDALRasterBand::AsMDArray().
14278 : *
14279 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14280 : * @param hArray Array.
14281 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14282 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14283 : * Ignored if the dimension count is 1.
14284 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14285 : * BAND_IMAGERY_METADATA option.
14286 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14287 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14288 : * @since GDAL 3.8
14289 : */
14290 90 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14291 : size_t iYDim, GDALGroupH hRootGroup,
14292 : CSLConstList papszOptions)
14293 : {
14294 90 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14295 180 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14296 180 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14297 180 : papszOptions));
14298 : }
14299 :
14300 : //! @cond Doxygen_Suppress
14301 :
14302 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14303 : const std::string &osName,
14304 : const std::string &osValue,
14305 180 : GDALExtendedDataTypeSubType eSubType)
14306 : : GDALAbstractMDArray(osParentName, osName),
14307 : GDALAttribute(osParentName, osName),
14308 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14309 : {
14310 180 : }
14311 :
14312 : const std::vector<std::shared_ptr<GDALDimension>> &
14313 30 : GDALAttributeString::GetDimensions() const
14314 : {
14315 30 : return m_dims;
14316 : }
14317 :
14318 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14319 : {
14320 21 : return m_dt;
14321 : }
14322 :
14323 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14324 : const GPtrDiff_t *,
14325 : const GDALExtendedDataType &bufferDataType,
14326 : void *pDstBuffer) const
14327 : {
14328 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14329 0 : return false;
14330 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14331 10 : if (!pszStr)
14332 0 : return false;
14333 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14334 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14335 10 : return true;
14336 : }
14337 :
14338 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14339 : const std::string &osName,
14340 66 : double dfValue)
14341 : : GDALAbstractMDArray(osParentName, osName),
14342 : GDALAttribute(osParentName, osName),
14343 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14344 : {
14345 66 : }
14346 :
14347 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14348 : const std::string &osName,
14349 27 : int nValue)
14350 : : GDALAbstractMDArray(osParentName, osName),
14351 : GDALAttribute(osParentName, osName),
14352 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14353 : {
14354 27 : }
14355 :
14356 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14357 : const std::string &osName,
14358 7 : const std::vector<GUInt32> &anValues)
14359 : : GDALAbstractMDArray(osParentName, osName),
14360 : GDALAttribute(osParentName, osName),
14361 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14362 : {
14363 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14364 14 : std::string(), "dim0", std::string(), std::string(),
14365 7 : m_anValuesUInt32.size()));
14366 7 : }
14367 :
14368 : const std::vector<std::shared_ptr<GDALDimension>> &
14369 14 : GDALAttributeNumeric::GetDimensions() const
14370 : {
14371 14 : return m_dims;
14372 : }
14373 :
14374 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14375 : {
14376 8 : return m_dt;
14377 : }
14378 :
14379 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14380 : const size_t *count, const GInt64 *arrayStep,
14381 : const GPtrDiff_t *bufferStride,
14382 : const GDALExtendedDataType &bufferDataType,
14383 : void *pDstBuffer) const
14384 : {
14385 4 : if (m_dims.empty())
14386 : {
14387 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14388 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14389 : bufferDataType);
14390 : else
14391 : {
14392 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14393 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14394 : bufferDataType);
14395 : }
14396 : }
14397 : else
14398 : {
14399 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14400 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14401 30 : for (size_t i = 0; i < count[0]; ++i)
14402 : {
14403 29 : GDALExtendedDataType::CopyValue(
14404 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14405 29 : i * arrayStep[0])],
14406 29 : m_dt, pabyDstBuffer, bufferDataType);
14407 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14408 : }
14409 : }
14410 4 : return true;
14411 : }
14412 :
14413 194 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14414 : const std::string &osParentName, const std::string &osName,
14415 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14416 194 : double dfIncrement, double dfOffsetInIncrement)
14417 : : GDALAbstractMDArray(osParentName, osName),
14418 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14419 : m_dfIncrement(dfIncrement),
14420 388 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14421 : {
14422 194 : }
14423 :
14424 194 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14425 : const std::string &osParentName, const std::string &osName,
14426 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14427 : double dfIncrement, double dfOffsetInIncrement)
14428 : {
14429 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14430 194 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14431 194 : poArray->SetSelf(poArray);
14432 194 : return poArray;
14433 : }
14434 :
14435 : const std::vector<std::shared_ptr<GDALDimension>> &
14436 788 : GDALMDArrayRegularlySpaced::GetDimensions() const
14437 : {
14438 788 : return m_dims;
14439 : }
14440 :
14441 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14442 : {
14443 316 : return m_dt;
14444 : }
14445 :
14446 : std::vector<std::shared_ptr<GDALAttribute>>
14447 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14448 : {
14449 4 : return m_attributes;
14450 : }
14451 :
14452 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14453 : const std::shared_ptr<GDALAttribute> &poAttr)
14454 : {
14455 0 : m_attributes.emplace_back(poAttr);
14456 0 : }
14457 :
14458 188 : bool GDALMDArrayRegularlySpaced::IRead(
14459 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14460 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14461 : void *pDstBuffer) const
14462 : {
14463 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14464 14719 : for (size_t i = 0; i < count[0]; i++)
14465 : {
14466 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
14467 14531 : m_dfOffsetInIncrement) *
14468 14531 : m_dfIncrement;
14469 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14470 : bufferDataType);
14471 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14472 : }
14473 188 : return true;
14474 : }
14475 :
14476 3025 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14477 : const std::string &osParentName, const std::string &osName,
14478 3025 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14479 3025 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14480 : {
14481 3025 : }
14482 :
14483 : std::shared_ptr<GDALMDArray>
14484 731 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14485 : {
14486 731 : return m_poIndexingVariable.lock();
14487 : }
14488 :
14489 : // cppcheck-suppress passedByValue
14490 500 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14491 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14492 : {
14493 500 : m_poIndexingVariable = poIndexingVariable;
14494 500 : return true;
14495 : }
14496 :
14497 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14498 : {
14499 33 : m_nSize = nNewSize;
14500 33 : }
14501 :
14502 : /************************************************************************/
14503 : /* GDALPamMultiDim::Private */
14504 : /************************************************************************/
14505 :
14506 : struct GDALPamMultiDim::Private
14507 : {
14508 : std::string m_osFilename{};
14509 : std::string m_osPamFilename{};
14510 :
14511 : struct Statistics
14512 : {
14513 : bool bHasStats = false;
14514 : bool bApproxStats = false;
14515 : double dfMin = 0;
14516 : double dfMax = 0;
14517 : double dfMean = 0;
14518 : double dfStdDev = 0;
14519 : GUInt64 nValidCount = 0;
14520 : };
14521 :
14522 : struct ArrayInfo
14523 : {
14524 : std::shared_ptr<OGRSpatialReference> poSRS{};
14525 : // cppcheck-suppress unusedStructMember
14526 : Statistics stats{};
14527 : };
14528 :
14529 : typedef std::pair<std::string, std::string> NameContext;
14530 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14531 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14532 : bool m_bDirty = false;
14533 : bool m_bLoaded = false;
14534 : };
14535 :
14536 : /************************************************************************/
14537 : /* GDALPamMultiDim */
14538 : /************************************************************************/
14539 :
14540 1449 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14541 1449 : : d(new Private())
14542 : {
14543 1449 : d->m_osFilename = osFilename;
14544 1449 : }
14545 :
14546 : /************************************************************************/
14547 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14548 : /************************************************************************/
14549 :
14550 1449 : GDALPamMultiDim::~GDALPamMultiDim()
14551 : {
14552 1449 : if (d->m_bDirty)
14553 30 : Save();
14554 1449 : }
14555 :
14556 : /************************************************************************/
14557 : /* GDALPamMultiDim::Load() */
14558 : /************************************************************************/
14559 :
14560 107 : void GDALPamMultiDim::Load()
14561 : {
14562 107 : if (d->m_bLoaded)
14563 96 : return;
14564 45 : d->m_bLoaded = true;
14565 :
14566 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14567 45 : d->m_osPamFilename =
14568 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14569 45 : CPLXMLTreeCloser oTree(nullptr);
14570 : {
14571 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14572 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14573 : }
14574 45 : if (!oTree)
14575 : {
14576 34 : return;
14577 : }
14578 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14579 11 : if (!poPAMMultiDim)
14580 0 : return;
14581 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14582 24 : psIter = psIter->psNext)
14583 : {
14584 24 : if (psIter->eType == CXT_Element &&
14585 24 : strcmp(psIter->pszValue, "Array") == 0)
14586 : {
14587 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14588 13 : if (!pszName)
14589 0 : continue;
14590 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14591 : const auto oKey =
14592 26 : std::pair<std::string, std::string>(pszName, pszContext);
14593 :
14594 : /* --------------------------------------------------------------------
14595 : */
14596 : /* Check for an SRS node. */
14597 : /* --------------------------------------------------------------------
14598 : */
14599 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14600 13 : if (psSRSNode)
14601 : {
14602 : std::shared_ptr<OGRSpatialReference> poSRS =
14603 6 : std::make_shared<OGRSpatialReference>();
14604 3 : poSRS->SetFromUserInput(
14605 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14606 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14607 3 : const char *pszMapping = CPLGetXMLValue(
14608 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14609 3 : if (pszMapping)
14610 : {
14611 : char **papszTokens =
14612 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14613 6 : std::vector<int> anMapping;
14614 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14615 : {
14616 6 : anMapping.push_back(atoi(papszTokens[i]));
14617 : }
14618 3 : CSLDestroy(papszTokens);
14619 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14620 : }
14621 : else
14622 : {
14623 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14624 : }
14625 :
14626 : const char *pszCoordinateEpoch =
14627 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14628 3 : if (pszCoordinateEpoch)
14629 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14630 :
14631 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14632 : }
14633 :
14634 : const CPLXMLNode *psStatistics =
14635 13 : CPLGetXMLNode(psIter, "Statistics");
14636 13 : if (psStatistics)
14637 : {
14638 7 : Private::Statistics sStats;
14639 7 : sStats.bHasStats = true;
14640 7 : sStats.bApproxStats = CPLTestBool(
14641 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14642 7 : sStats.dfMin =
14643 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14644 7 : sStats.dfMax =
14645 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14646 7 : sStats.dfMean =
14647 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14648 7 : sStats.dfStdDev =
14649 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14650 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14651 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14652 7 : d->m_oMapArray[oKey].stats = sStats;
14653 13 : }
14654 : }
14655 : else
14656 : {
14657 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14658 11 : psIter->psNext = nullptr;
14659 11 : d->m_apoOtherNodes.emplace_back(
14660 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14661 11 : psIter->psNext = psNextBackup;
14662 : }
14663 : }
14664 : }
14665 :
14666 : /************************************************************************/
14667 : /* GDALPamMultiDim::Save() */
14668 : /************************************************************************/
14669 :
14670 30 : void GDALPamMultiDim::Save()
14671 : {
14672 : CPLXMLTreeCloser oTree(
14673 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14674 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14675 : {
14676 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14677 : }
14678 112 : for (const auto &kv : d->m_oMapArray)
14679 : {
14680 : CPLXMLNode *psArrayNode =
14681 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14682 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14683 82 : if (!kv.first.second.empty())
14684 : {
14685 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14686 : kv.first.second.c_str());
14687 : }
14688 82 : if (kv.second.poSRS)
14689 : {
14690 71 : char *pszWKT = nullptr;
14691 : {
14692 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14693 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14694 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14695 : }
14696 : CPLXMLNode *psSRSNode =
14697 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14698 71 : CPLFree(pszWKT);
14699 : const auto &mapping =
14700 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14701 142 : CPLString osMapping;
14702 213 : for (size_t i = 0; i < mapping.size(); ++i)
14703 : {
14704 142 : if (!osMapping.empty())
14705 71 : osMapping += ",";
14706 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14707 : }
14708 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14709 : osMapping.c_str());
14710 :
14711 : const double dfCoordinateEpoch =
14712 71 : kv.second.poSRS->GetCoordinateEpoch();
14713 71 : if (dfCoordinateEpoch > 0)
14714 : {
14715 : std::string osCoordinateEpoch =
14716 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14717 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14718 : {
14719 6 : while (osCoordinateEpoch.back() == '0')
14720 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14721 : }
14722 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14723 : osCoordinateEpoch.c_str());
14724 : }
14725 : }
14726 :
14727 82 : if (kv.second.stats.bHasStats)
14728 : {
14729 : CPLXMLNode *psMDArray =
14730 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14731 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14732 8 : kv.second.stats.bApproxStats ? "1"
14733 : : "0");
14734 8 : CPLCreateXMLElementAndValue(
14735 : psMDArray, "Minimum",
14736 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14737 8 : CPLCreateXMLElementAndValue(
14738 : psMDArray, "Maximum",
14739 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14740 8 : CPLCreateXMLElementAndValue(
14741 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14742 8 : CPLCreateXMLElementAndValue(
14743 : psMDArray, "StdDev",
14744 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14745 8 : CPLCreateXMLElementAndValue(
14746 : psMDArray, "ValidSampleCount",
14747 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14748 : }
14749 : }
14750 :
14751 : int bSaved;
14752 60 : CPLErrorAccumulator oErrorAccumulator;
14753 : {
14754 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14755 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14756 : bSaved =
14757 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14758 : }
14759 :
14760 30 : const char *pszNewPam = nullptr;
14761 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14762 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14763 : {
14764 0 : CPLErrorReset();
14765 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14766 : }
14767 : else
14768 : {
14769 30 : oErrorAccumulator.ReplayErrors();
14770 : }
14771 30 : }
14772 :
14773 : /************************************************************************/
14774 : /* GDALPamMultiDim::GetSpatialRef() */
14775 : /************************************************************************/
14776 :
14777 : std::shared_ptr<OGRSpatialReference>
14778 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14779 : const std::string &osContext)
14780 : {
14781 10 : Load();
14782 : auto oIter =
14783 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14784 10 : if (oIter != d->m_oMapArray.end())
14785 2 : return oIter->second.poSRS;
14786 8 : return nullptr;
14787 : }
14788 :
14789 : /************************************************************************/
14790 : /* GDALPamMultiDim::SetSpatialRef() */
14791 : /************************************************************************/
14792 :
14793 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14794 : const std::string &osContext,
14795 : const OGRSpatialReference *poSRS)
14796 : {
14797 72 : Load();
14798 72 : d->m_bDirty = true;
14799 72 : if (poSRS && !poSRS->IsEmpty())
14800 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14801 : poSRS->Clone());
14802 : else
14803 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14804 1 : .poSRS.reset();
14805 72 : }
14806 :
14807 : /************************************************************************/
14808 : /* GetStatistics() */
14809 : /************************************************************************/
14810 :
14811 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14812 : const std::string &osContext,
14813 : bool bApproxOK, double *pdfMin,
14814 : double *pdfMax, double *pdfMean,
14815 : double *pdfStdDev, GUInt64 *pnValidCount)
14816 : {
14817 16 : Load();
14818 : auto oIter =
14819 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14820 16 : if (oIter == d->m_oMapArray.end())
14821 9 : return CE_Failure;
14822 7 : const auto &stats = oIter->second.stats;
14823 7 : if (!stats.bHasStats)
14824 1 : return CE_Failure;
14825 6 : if (!bApproxOK && stats.bApproxStats)
14826 0 : return CE_Failure;
14827 6 : if (pdfMin)
14828 6 : *pdfMin = stats.dfMin;
14829 6 : if (pdfMax)
14830 6 : *pdfMax = stats.dfMax;
14831 6 : if (pdfMean)
14832 6 : *pdfMean = stats.dfMean;
14833 6 : if (pdfStdDev)
14834 6 : *pdfStdDev = stats.dfStdDev;
14835 6 : if (pnValidCount)
14836 6 : *pnValidCount = stats.nValidCount;
14837 6 : return CE_None;
14838 : }
14839 :
14840 : /************************************************************************/
14841 : /* SetStatistics() */
14842 : /************************************************************************/
14843 :
14844 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14845 : const std::string &osContext,
14846 : bool bApproxStats, double dfMin,
14847 : double dfMax, double dfMean,
14848 : double dfStdDev, GUInt64 nValidCount)
14849 : {
14850 8 : Load();
14851 8 : d->m_bDirty = true;
14852 : auto &stats =
14853 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14854 8 : stats.bHasStats = true;
14855 8 : stats.bApproxStats = bApproxStats;
14856 8 : stats.dfMin = dfMin;
14857 8 : stats.dfMax = dfMax;
14858 8 : stats.dfMean = dfMean;
14859 8 : stats.dfStdDev = dfStdDev;
14860 8 : stats.nValidCount = nValidCount;
14861 8 : }
14862 :
14863 : /************************************************************************/
14864 : /* ClearStatistics() */
14865 : /************************************************************************/
14866 :
14867 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14868 : const std::string &osContext)
14869 : {
14870 0 : Load();
14871 0 : d->m_bDirty = true;
14872 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14873 : false;
14874 0 : }
14875 :
14876 : /************************************************************************/
14877 : /* ClearStatistics() */
14878 : /************************************************************************/
14879 :
14880 1 : void GDALPamMultiDim::ClearStatistics()
14881 : {
14882 1 : Load();
14883 1 : d->m_bDirty = true;
14884 3 : for (auto &kv : d->m_oMapArray)
14885 2 : kv.second.stats.bHasStats = false;
14886 1 : }
14887 :
14888 : /************************************************************************/
14889 : /* GetPAM() */
14890 : /************************************************************************/
14891 :
14892 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14893 815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14894 : {
14895 815 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14896 815 : if (poPamArray)
14897 567 : return poPamArray->GetPAM();
14898 248 : return nullptr;
14899 : }
14900 :
14901 : /************************************************************************/
14902 : /* GDALPamMDArray */
14903 : /************************************************************************/
14904 :
14905 3755 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14906 : const std::string &osName,
14907 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14908 0 : const std::string &osContext)
14909 : :
14910 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14911 : GDALAbstractMDArray(osParentName, osName),
14912 : #endif
14913 3755 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14914 : {
14915 3755 : }
14916 :
14917 : /************************************************************************/
14918 : /* GDALPamMDArray::SetSpatialRef() */
14919 : /************************************************************************/
14920 :
14921 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14922 : {
14923 72 : if (!m_poPam)
14924 0 : return false;
14925 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14926 72 : return true;
14927 : }
14928 :
14929 : /************************************************************************/
14930 : /* GDALPamMDArray::GetSpatialRef() */
14931 : /************************************************************************/
14932 :
14933 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14934 : {
14935 10 : if (!m_poPam)
14936 0 : return nullptr;
14937 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14938 : }
14939 :
14940 : /************************************************************************/
14941 : /* GetStatistics() */
14942 : /************************************************************************/
14943 :
14944 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14945 : double *pdfMin, double *pdfMax,
14946 : double *pdfMean, double *pdfStdDev,
14947 : GUInt64 *pnValidCount,
14948 : GDALProgressFunc pfnProgress,
14949 : void *pProgressData)
14950 : {
14951 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14952 : bApproxOK, pdfMin, pdfMax, pdfMean,
14953 16 : pdfStdDev, pnValidCount) == CE_None)
14954 : {
14955 6 : return CE_None;
14956 : }
14957 10 : if (!bForce)
14958 4 : return CE_Warning;
14959 :
14960 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14961 : pdfMean, pdfStdDev, pnValidCount,
14962 6 : pfnProgress, pProgressData);
14963 : }
14964 :
14965 : /************************************************************************/
14966 : /* SetStatistics() */
14967 : /************************************************************************/
14968 :
14969 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14970 : double dfMax, double dfMean, double dfStdDev,
14971 : GUInt64 nValidCount,
14972 : CSLConstList /* papszOptions */)
14973 : {
14974 8 : if (!m_poPam)
14975 0 : return false;
14976 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14977 : dfMax, dfMean, dfStdDev, nValidCount);
14978 8 : return true;
14979 : }
14980 :
14981 : /************************************************************************/
14982 : /* ClearStatistics() */
14983 : /************************************************************************/
14984 :
14985 0 : void GDALPamMDArray::ClearStatistics()
14986 : {
14987 0 : if (!m_poPam)
14988 0 : return;
14989 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14990 : }
14991 :
14992 : //! @endcond
|