Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_pam_multidim.h"
31 : #include "gdal_rat.h"
32 : #include "gdal_utils.h"
33 : #include "cpl_safemaths.hpp"
34 : #include "memmultidim.h"
35 : #include "ogrsf_frmts.h"
36 : #include "gdalmultidim_priv.h"
37 :
38 : #if defined(__clang__) || defined(_MSC_VER)
39 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40 : #endif
41 :
42 : /************************************************************************/
43 : /* GDALMDArrayUnscaled */
44 : /************************************************************************/
45 :
46 : class GDALMDArrayUnscaled final : public GDALPamMDArray
47 : {
48 : private:
49 : std::shared_ptr<GDALMDArray> m_poParent{};
50 : const GDALExtendedDataType m_dt;
51 : bool m_bHasNoData;
52 : const double m_dfScale;
53 : const double m_dfOffset;
54 : std::vector<GByte> m_abyRawNoData{};
55 :
56 : protected:
57 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58 : double dfScale, double dfOffset,
59 : double dfOverriddenDstNodata, GDALDataType eDT)
60 26 : : GDALAbstractMDArray(std::string(),
61 26 : "Unscaled view of " + poParent->GetFullName()),
62 : GDALPamMDArray(
63 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
64 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65 13 : m_poParent(std::move(poParent)),
66 : m_dt(GDALExtendedDataType::Create(eDT)),
67 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
69 : {
70 13 : m_abyRawNoData.resize(m_dt.GetSize());
71 : const auto eNonComplexDT =
72 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73 26 : GDALCopyWords64(
74 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77 13 : }
78 :
79 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81 : const GDALExtendedDataType &bufferDataType,
82 : void *pDstBuffer) const override;
83 :
84 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86 : const GDALExtendedDataType &bufferDataType,
87 : const void *pSrcBuffer) override;
88 :
89 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90 : CSLConstList papszOptions) const override
91 : {
92 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93 : }
94 :
95 : public:
96 : static std::shared_ptr<GDALMDArrayUnscaled>
97 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98 : double dfOffset, double dfDstNodata, GDALDataType eDT)
99 : {
100 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102 13 : newAr->SetSelf(newAr);
103 13 : return newAr;
104 : }
105 :
106 1 : bool IsWritable() const override
107 : {
108 1 : return m_poParent->IsWritable();
109 : }
110 :
111 15 : const std::string &GetFilename() const override
112 : {
113 15 : return m_poParent->GetFilename();
114 : }
115 :
116 : const std::vector<std::shared_ptr<GDALDimension>> &
117 220 : GetDimensions() const override
118 : {
119 220 : return m_poParent->GetDimensions();
120 : }
121 :
122 103 : const GDALExtendedDataType &GetDataType() const override
123 : {
124 103 : return m_dt;
125 : }
126 :
127 1 : const std::string &GetUnit() const override
128 : {
129 1 : return m_poParent->GetUnit();
130 : }
131 :
132 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133 : {
134 1 : return m_poParent->GetSpatialRef();
135 : }
136 :
137 6 : const void *GetRawNoDataValue() const override
138 : {
139 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140 : }
141 :
142 1 : bool SetRawNoDataValue(const void *pRawNoData) override
143 : {
144 1 : m_bHasNoData = true;
145 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146 1 : return true;
147 : }
148 :
149 4 : std::vector<GUInt64> GetBlockSize() const override
150 : {
151 4 : return m_poParent->GetBlockSize();
152 : }
153 :
154 : std::shared_ptr<GDALAttribute>
155 0 : GetAttribute(const std::string &osName) const override
156 : {
157 0 : return m_poParent->GetAttribute(osName);
158 : }
159 :
160 : std::vector<std::shared_ptr<GDALAttribute>>
161 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
162 : {
163 1 : return m_poParent->GetAttributes(papszOptions);
164 : }
165 :
166 0 : bool SetUnit(const std::string &osUnit) override
167 : {
168 0 : return m_poParent->SetUnit(osUnit);
169 : }
170 :
171 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172 : {
173 0 : return m_poParent->SetSpatialRef(poSRS);
174 : }
175 :
176 : std::shared_ptr<GDALAttribute>
177 1 : CreateAttribute(const std::string &osName,
178 : const std::vector<GUInt64> &anDimensions,
179 : const GDALExtendedDataType &oDataType,
180 : CSLConstList papszOptions = nullptr) override
181 : {
182 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183 1 : papszOptions);
184 : }
185 : };
186 :
187 : /************************************************************************/
188 : /* ~GDALIHasAttribute() */
189 : /************************************************************************/
190 :
191 : GDALIHasAttribute::~GDALIHasAttribute() = default;
192 :
193 : /************************************************************************/
194 : /* GetAttribute() */
195 : /************************************************************************/
196 :
197 : /** Return an attribute by its name.
198 : *
199 : * If the attribute does not exist, nullptr should be silently returned.
200 : *
201 : * @note Driver implementation: this method will fallback to
202 : * GetAttributeFromAttributes() is not explicitly implemented
203 : *
204 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
205 : *
206 : * This is the same as the C function GDALGroupGetAttribute() or
207 : * GDALMDArrayGetAttribute().
208 : *
209 : * @param osName Attribute name
210 : * @return the attribute, or nullptr if it does not exist or an error occurred.
211 : */
212 : std::shared_ptr<GDALAttribute>
213 2172 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
214 : {
215 2172 : return GetAttributeFromAttributes(osName);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetAttributeFromAttributes() */
220 : /************************************************************************/
221 :
222 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
223 : */
224 : std::shared_ptr<GDALAttribute>
225 2172 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226 : {
227 4344 : auto attrs(GetAttributes());
228 14975 : for (const auto &attr : attrs)
229 : {
230 14287 : if (attr->GetName() == osName)
231 1484 : return attr;
232 : }
233 688 : return nullptr;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetAttributes() */
238 : /************************************************************************/
239 :
240 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241 : *
242 : * If the attribute does not exist, nullptr should be silently returned.
243 : *
244 : * @note Driver implementation: optionally implemented. If implemented,
245 : * GetAttribute() should also be implemented.
246 : *
247 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
248 : *
249 : * This is the same as the C function GDALGroupGetAttributes() or
250 : * GDALMDArrayGetAttributes().
251 :
252 : * @param papszOptions Driver specific options determining how attributes
253 : * should be retrieved. Pass nullptr for default behavior.
254 : *
255 : * @return the attributes.
256 : */
257 : std::vector<std::shared_ptr<GDALAttribute>>
258 54 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259 : {
260 54 : return {};
261 : }
262 :
263 : /************************************************************************/
264 : /* CreateAttribute() */
265 : /************************************************************************/
266 :
267 : /** Create an attribute within a GDALMDArray or GDALGroup.
268 : *
269 : * The attribute might not be "physically" created until a value is written
270 : * into it.
271 : *
272 : * Optionally implemented.
273 : *
274 : * Drivers known to implement it: MEM, netCDF
275 : *
276 : * This is the same as the C function GDALGroupCreateAttribute() or
277 : * GDALMDArrayCreateAttribute()
278 : *
279 : * @param osName Attribute name.
280 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
281 : * dimension first to the fastest varying dimension last.
282 : * Empty for a scalar attribute (common case)
283 : * @param oDataType Attribute data type.
284 : * @param papszOptions Driver specific options determining how the attribute.
285 : * should be created.
286 : *
287 : * @return the new attribute, or nullptr if case of error
288 : */
289 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290 : CPL_UNUSED const std::string &osName,
291 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292 : CPL_UNUSED const GDALExtendedDataType &oDataType,
293 : CPL_UNUSED CSLConstList papszOptions)
294 : {
295 0 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "CreateAttribute() not implemented");
297 0 : return nullptr;
298 : }
299 :
300 : /************************************************************************/
301 : /* DeleteAttribute() */
302 : /************************************************************************/
303 :
304 : /** Delete an attribute from a GDALMDArray or GDALGroup.
305 : *
306 : * Optionally implemented.
307 : *
308 : * After this call, if a previously obtained instance of the deleted object
309 : * is still alive, no method other than for freeing it should be invoked.
310 : *
311 : * Drivers known to implement it: MEM, netCDF
312 : *
313 : * This is the same as the C function GDALGroupDeleteAttribute() or
314 : * GDALMDArrayDeleteAttribute()
315 : *
316 : * @param osName Attribute name.
317 : * @param papszOptions Driver specific options determining how the attribute.
318 : * should be deleted.
319 : *
320 : * @return true in case of success
321 : * @since GDAL 3.8
322 : */
323 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324 : CPL_UNUSED CSLConstList papszOptions)
325 : {
326 0 : CPLError(CE_Failure, CPLE_NotSupported,
327 : "DeleteAttribute() not implemented");
328 0 : return false;
329 : }
330 :
331 : /************************************************************************/
332 : /* GDALGroup() */
333 : /************************************************************************/
334 :
335 : //! @cond Doxygen_Suppress
336 7360 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 7360 : const std::string &osContext)
338 7360 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 14720 : !osParentName.empty()
341 11335 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 18695 : m_osContext(osContext)
344 : {
345 7360 : }
346 :
347 : //! @endcond
348 :
349 : /************************************************************************/
350 : /* ~GDALGroup() */
351 : /************************************************************************/
352 :
353 : GDALGroup::~GDALGroup() = default;
354 :
355 : /************************************************************************/
356 : /* GetMDArrayNames() */
357 : /************************************************************************/
358 :
359 : /** Return the list of multidimensional array names contained in this group.
360 : *
361 : * @note Driver implementation: optionally implemented. If implemented,
362 : * OpenMDArray() should also be implemented.
363 : *
364 : * Drivers known to implement it: MEM, netCDF.
365 : *
366 : * This is the same as the C function GDALGroupGetMDArrayNames().
367 : *
368 : * @param papszOptions Driver specific options determining how arrays
369 : * should be retrieved. Pass nullptr for default behavior.
370 : *
371 : * @return the array names.
372 : */
373 : std::vector<std::string>
374 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375 : {
376 0 : return {};
377 : }
378 :
379 : /************************************************************************/
380 : /* GetMDArrayFullNamesRecursive() */
381 : /************************************************************************/
382 :
383 : /** Return the list of multidimensional array full names contained in this
384 : * group and its subgroups.
385 : *
386 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387 : *
388 : * @param papszGroupOptions Driver specific options determining how groups
389 : * should be retrieved. Pass nullptr for default behavior.
390 : * @param papszArrayOptions Driver specific options determining how arrays
391 : * should be retrieved. Pass nullptr for default behavior.
392 : *
393 : * @return the array full names.
394 : *
395 : * @since 3.11
396 : */
397 : std::vector<std::string>
398 10 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399 : CSLConstList papszArrayOptions) const
400 : {
401 10 : std::vector<std::string> ret;
402 20 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
403 10 : stackGroups.push_back(nullptr); // nullptr means this
404 23 : while (!stackGroups.empty())
405 : {
406 26 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407 13 : stackGroups.erase(stackGroups.begin());
408 13 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409 31 : for (const std::string &arrayName :
410 75 : poCurGroup->GetMDArrayNames(papszArrayOptions))
411 : {
412 62 : std::string osFullName = poCurGroup->GetFullName();
413 31 : if (!osFullName.empty() && osFullName.back() != '/')
414 3 : osFullName += '/';
415 31 : osFullName += arrayName;
416 31 : ret.push_back(std::move(osFullName));
417 : }
418 13 : auto insertionPoint = stackGroups.begin();
419 3 : for (const auto &osSubGroup :
420 19 : poCurGroup->GetGroupNames(papszGroupOptions))
421 : {
422 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423 3 : if (poSubGroup)
424 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
425 : }
426 : }
427 :
428 20 : return ret;
429 : }
430 :
431 : /************************************************************************/
432 : /* OpenMDArray() */
433 : /************************************************************************/
434 :
435 : /** Open and return a multidimensional array.
436 : *
437 : * @note Driver implementation: optionally implemented. If implemented,
438 : * GetMDArrayNames() should also be implemented.
439 : *
440 : * Drivers known to implement it: MEM, netCDF.
441 : *
442 : * This is the same as the C function GDALGroupOpenMDArray().
443 : *
444 : * @param osName Array name.
445 : * @param papszOptions Driver specific options determining how the array should
446 : * be opened. Pass nullptr for default behavior.
447 : *
448 : * @return the array, or nullptr.
449 : */
450 : std::shared_ptr<GDALMDArray>
451 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452 : CPL_UNUSED CSLConstList papszOptions) const
453 : {
454 0 : return nullptr;
455 : }
456 :
457 : /************************************************************************/
458 : /* GetGroupNames() */
459 : /************************************************************************/
460 :
461 : /** Return the list of sub-groups contained in this group.
462 : *
463 : * @note Driver implementation: optionally implemented. If implemented,
464 : * OpenGroup() should also be implemented.
465 : *
466 : * Drivers known to implement it: MEM, netCDF.
467 : *
468 : * This is the same as the C function GDALGroupGetGroupNames().
469 : *
470 : * @param papszOptions Driver specific options determining how groups
471 : * should be retrieved. Pass nullptr for default behavior.
472 : *
473 : * @return the group names.
474 : */
475 : std::vector<std::string>
476 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477 : {
478 4 : return {};
479 : }
480 :
481 : /************************************************************************/
482 : /* OpenGroup() */
483 : /************************************************************************/
484 :
485 : /** Open and return a sub-group.
486 : *
487 : * @note Driver implementation: optionally implemented. If implemented,
488 : * GetGroupNames() should also be implemented.
489 : *
490 : * Drivers known to implement it: MEM, netCDF.
491 : *
492 : * This is the same as the C function GDALGroupOpenGroup().
493 : *
494 : * @param osName Sub-group name.
495 : * @param papszOptions Driver specific options determining how the sub-group
496 : * should be opened. Pass nullptr for default behavior.
497 : *
498 : * @return the group, or nullptr.
499 : */
500 : std::shared_ptr<GDALGroup>
501 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502 : CPL_UNUSED CSLConstList papszOptions) const
503 : {
504 4 : return nullptr;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetVectorLayerNames() */
509 : /************************************************************************/
510 :
511 : /** Return the list of layer names contained in this group.
512 : *
513 : * @note Driver implementation: optionally implemented. If implemented,
514 : * OpenVectorLayer() should also be implemented.
515 : *
516 : * Drivers known to implement it: OpenFileGDB, FileGDB
517 : *
518 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519 : * GDALDataset::GetLayer() should then be used.
520 : *
521 : * This is the same as the C function GDALGroupGetVectorLayerNames().
522 : *
523 : * @param papszOptions Driver specific options determining how layers
524 : * should be retrieved. Pass nullptr for default behavior.
525 : *
526 : * @return the vector layer names.
527 : * @since GDAL 3.4
528 : */
529 : std::vector<std::string>
530 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 1 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* OpenVectorLayer() */
537 : /************************************************************************/
538 :
539 : /** Open and return a vector layer.
540 : *
541 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542 : * lifetime of the returned OGRLayer* is linked to the one of the owner
543 : * dataset (contrary to the general design of this class where objects can be
544 : * used independently of the object that returned them)
545 : *
546 : * @note Driver implementation: optionally implemented. If implemented,
547 : * GetVectorLayerNames() should also be implemented.
548 : *
549 : * Drivers known to implement it: MEM, netCDF.
550 : *
551 : * This is the same as the C function GDALGroupOpenVectorLayer().
552 : *
553 : * @param osName Vector layer name.
554 : * @param papszOptions Driver specific options determining how the layer should
555 : * be opened. Pass nullptr for default behavior.
556 : *
557 : * @return the group, or nullptr.
558 : */
559 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560 : CPL_UNUSED CSLConstList papszOptions) const
561 : {
562 2 : return nullptr;
563 : }
564 :
565 : /************************************************************************/
566 : /* GetDimensions() */
567 : /************************************************************************/
568 :
569 : /** Return the list of dimensions contained in this group and used by its
570 : * arrays.
571 : *
572 : * This is for dimensions that can potentially be used by several arrays.
573 : * Not all drivers might implement this. To retrieve the dimensions used by
574 : * a specific array, use GDALMDArray::GetDimensions().
575 : *
576 : * Drivers known to implement it: MEM, netCDF
577 : *
578 : * This is the same as the C function GDALGroupGetDimensions().
579 : *
580 : * @param papszOptions Driver specific options determining how groups
581 : * should be retrieved. Pass nullptr for default behavior.
582 : *
583 : * @return the dimensions.
584 : */
585 : std::vector<std::shared_ptr<GDALDimension>>
586 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587 : {
588 11 : return {};
589 : }
590 :
591 : /************************************************************************/
592 : /* GetStructuralInfo() */
593 : /************************************************************************/
594 :
595 : /** Return structural information on the group.
596 : *
597 : * This may be the compression, etc..
598 : *
599 : * The return value should not be freed and is valid until GDALGroup is
600 : * released or this function called again.
601 : *
602 : * This is the same as the C function GDALGroupGetStructuralInfo().
603 : */
604 42 : CSLConstList GDALGroup::GetStructuralInfo() const
605 : {
606 42 : return nullptr;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateGroup() */
611 : /************************************************************************/
612 :
613 : /** Create a sub-group within a group.
614 : *
615 : * Optionally implemented by drivers.
616 : *
617 : * Drivers known to implement it: MEM, netCDF
618 : *
619 : * This is the same as the C function GDALGroupCreateGroup().
620 : *
621 : * @param osName Sub-group name.
622 : * @param papszOptions Driver specific options determining how the sub-group
623 : * should be created.
624 : *
625 : * @return the new sub-group, or nullptr in case of error.
626 : */
627 : std::shared_ptr<GDALGroup>
628 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629 : CPL_UNUSED CSLConstList papszOptions)
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632 0 : return nullptr;
633 : }
634 :
635 : /************************************************************************/
636 : /* DeleteGroup() */
637 : /************************************************************************/
638 :
639 : /** Delete a sub-group from a group.
640 : *
641 : * Optionally implemented.
642 : *
643 : * After this call, if a previously obtained instance of the deleted object
644 : * is still alive, no method other than for freeing it should be invoked.
645 : *
646 : * Drivers known to implement it: MEM, Zarr
647 : *
648 : * This is the same as the C function GDALGroupDeleteGroup().
649 : *
650 : * @param osName Sub-group name.
651 : * @param papszOptions Driver specific options determining how the group.
652 : * should be deleted.
653 : *
654 : * @return true in case of success
655 : * @since GDAL 3.8
656 : */
657 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658 : CPL_UNUSED CSLConstList papszOptions)
659 : {
660 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661 0 : return false;
662 : }
663 :
664 : /************************************************************************/
665 : /* CreateDimension() */
666 : /************************************************************************/
667 :
668 : /** Create a dimension within a group.
669 : *
670 : * @note Driver implementation: drivers supporting CreateDimension() should
671 : * implement this method, but do not have necessarily to implement
672 : * GDALGroup::GetDimensions().
673 : *
674 : * Drivers known to implement it: MEM, netCDF
675 : *
676 : * This is the same as the C function GDALGroupCreateDimension().
677 : *
678 : * @param osName Dimension name.
679 : * @param osType Dimension type (might be empty, and ignored by drivers)
680 : * @param osDirection Dimension direction (might be empty, and ignored by
681 : * drivers)
682 : * @param nSize Number of values indexed by this dimension. Should be > 0.
683 : * @param papszOptions Driver specific options determining how the dimension
684 : * should be created.
685 : *
686 : * @return the new dimension, or nullptr if case of error
687 : */
688 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691 : CPL_UNUSED CSLConstList papszOptions)
692 : {
693 0 : CPLError(CE_Failure, CPLE_NotSupported,
694 : "CreateDimension() not implemented");
695 0 : return nullptr;
696 : }
697 :
698 : /************************************************************************/
699 : /* CreateMDArray() */
700 : /************************************************************************/
701 :
702 : /** Create a multidimensional array within a group.
703 : *
704 : * It is recommended that the GDALDimension objects passed in aoDimensions
705 : * belong to this group, either by retrieving them with GetDimensions()
706 : * or creating a new one with CreateDimension().
707 : *
708 : * Optionally implemented.
709 : *
710 : * Drivers known to implement it: MEM, netCDF
711 : *
712 : * This is the same as the C function GDALGroupCreateMDArray().
713 : *
714 : * @note Driver implementation: drivers should take into account the possibility
715 : * that GDALDimension object passed in aoDimensions might belong to a different
716 : * group / dataset / driver and act accordingly.
717 : *
718 : * @param osName Array name.
719 : * @param aoDimensions List of dimensions, ordered from the slowest varying
720 : * dimension first to the fastest varying dimension last.
721 : * Might be empty for a scalar array (if supported by
722 : * driver)
723 : * @param oDataType Array data type.
724 : * @param papszOptions Driver specific options determining how the array
725 : * should be created.
726 : *
727 : * @return the new array, or nullptr in case of error
728 : */
729 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730 : CPL_UNUSED const std::string &osName,
731 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732 : CPL_UNUSED const GDALExtendedDataType &oDataType,
733 : CPL_UNUSED CSLConstList papszOptions)
734 : {
735 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736 0 : return nullptr;
737 : }
738 :
739 : /************************************************************************/
740 : /* DeleteMDArray() */
741 : /************************************************************************/
742 :
743 : /** Delete an array from a group.
744 : *
745 : * Optionally implemented.
746 : *
747 : * After this call, if a previously obtained instance of the deleted object
748 : * is still alive, no method other than for freeing it should be invoked.
749 : *
750 : * Drivers known to implement it: MEM, Zarr
751 : *
752 : * This is the same as the C function GDALGroupDeleteMDArray().
753 : *
754 : * @param osName Arrayname.
755 : * @param papszOptions Driver specific options determining how the array.
756 : * should be deleted.
757 : *
758 : * @return true in case of success
759 : * @since GDAL 3.8
760 : */
761 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762 : CPL_UNUSED CSLConstList papszOptions)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765 0 : return false;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetTotalCopyCost() */
770 : /************************************************************************/
771 :
772 : /** Return a total "cost" to copy the group.
773 : *
774 : * Used as a parameter for CopFrom()
775 : */
776 33 : GUInt64 GDALGroup::GetTotalCopyCost() const
777 : {
778 33 : GUInt64 nCost = COPY_COST;
779 33 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780 :
781 66 : auto groupNames = GetGroupNames();
782 39 : for (const auto &name : groupNames)
783 : {
784 12 : auto subGroup = OpenGroup(name);
785 6 : if (subGroup)
786 : {
787 6 : nCost += subGroup->GetTotalCopyCost();
788 : }
789 : }
790 :
791 33 : auto arrayNames = GetMDArrayNames();
792 102 : for (const auto &name : arrayNames)
793 : {
794 138 : auto array = OpenMDArray(name);
795 69 : if (array)
796 : {
797 69 : nCost += array->GetTotalCopyCost();
798 : }
799 : }
800 66 : return nCost;
801 : }
802 :
803 : /************************************************************************/
804 : /* CopyFrom() */
805 : /************************************************************************/
806 :
807 : /** Copy the content of a group into a new (generally empty) group.
808 : *
809 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
811 : * of some output drivers this is not recommended)
812 : * @param poSrcGroup Source group. Must NOT be nullptr.
813 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
814 : * stop the copy. In relaxed mode, the copy will be attempted to
815 : * be pursued.
816 : * @param nCurCost Should be provided as a variable initially set to 0.
817 : * @param nTotalCost Total cost from GetTotalCopyCost().
818 : * @param pfnProgress Progress callback, or nullptr.
819 : * @param pProgressData Progress user data, or nulptr.
820 : * @param papszOptions Creation options. Currently, only array creation
821 : * options are supported. They must be prefixed with
822 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
823 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
824 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
825 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
826 : * Restriction to arrays of a given name is done with adding
827 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
828 : * a full qualified name.
829 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
830 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
831 : * scaled to UInt16 with scale and offset values being computed from the minimum
832 : * and maximum of the source array. The integer data type used can be set with
833 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
834 : *
835 : * @return true in case of success (or partial success if bStrict == false).
836 : */
837 33 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
838 : GDALDataset *poSrcDS,
839 : const std::shared_ptr<GDALGroup> &poSrcGroup,
840 : bool bStrict, GUInt64 &nCurCost,
841 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
842 : void *pProgressData, CSLConstList papszOptions)
843 : {
844 33 : if (pfnProgress == nullptr)
845 0 : pfnProgress = GDALDummyProgress;
846 :
847 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
848 : if (!(x)) \
849 : { \
850 : if (bStrict) \
851 : return false; \
852 : continue; \
853 : } \
854 : (void)0
855 :
856 : try
857 : {
858 33 : nCurCost += GDALGroup::COPY_COST;
859 :
860 66 : const auto srcDims = poSrcGroup->GetDimensions();
861 : std::map<std::string, std::shared_ptr<GDALDimension>>
862 66 : mapExistingDstDims;
863 66 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
864 87 : for (const auto &dim : srcDims)
865 : {
866 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
867 54 : dim->GetDirection(), dim->GetSize());
868 54 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
869 54 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
870 108 : auto poIndexingVarSrc(dim->GetIndexingVariable());
871 54 : if (poIndexingVarSrc)
872 : {
873 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
874 35 : ->GetName()] =
875 70 : dim->GetName();
876 : }
877 : }
878 :
879 66 : auto attrs = poSrcGroup->GetAttributes();
880 51 : for (const auto &attr : attrs)
881 : {
882 : auto dstAttr =
883 18 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
884 36 : attr->GetDataType());
885 18 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
886 18 : auto raw(attr->ReadAsRaw());
887 18 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
888 0 : return false;
889 : }
890 33 : if (!attrs.empty())
891 : {
892 8 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
893 8 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
894 0 : return false;
895 : }
896 :
897 : const auto CopyArray =
898 69 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
899 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
900 : papszOptions, bStrict, &nCurCost,
901 616 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
902 : {
903 : // Map source dimensions to target dimensions
904 138 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
905 69 : const auto &srcArrayDims(srcArray->GetDimensions());
906 172 : for (const auto &dim : srcArrayDims)
907 : {
908 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
909 103 : dim->GetFullName());
910 103 : if (dstDim && dstDim->GetSize() == dim->GetSize())
911 : {
912 93 : dstArrayDims.emplace_back(dstDim);
913 : }
914 : else
915 : {
916 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
917 19 : if (oIter != mapExistingDstDims.end() &&
918 9 : oIter->second->GetSize() == dim->GetSize())
919 : {
920 8 : dstArrayDims.emplace_back(oIter->second);
921 : }
922 : else
923 : {
924 2 : std::string newDimName;
925 2 : if (oIter == mapExistingDstDims.end())
926 : {
927 1 : newDimName = dim->GetName();
928 : }
929 : else
930 : {
931 1 : std::string newDimNamePrefix(srcArray->GetName() +
932 3 : '_' + dim->GetName());
933 1 : newDimName = newDimNamePrefix;
934 1 : int nIterCount = 2;
935 0 : while (
936 1 : cpl::contains(mapExistingDstDims, newDimName))
937 : {
938 0 : newDimName = newDimNamePrefix +
939 0 : CPLSPrintf("_%d", nIterCount);
940 0 : nIterCount++;
941 : }
942 : }
943 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
944 : dim->GetDirection(),
945 4 : dim->GetSize());
946 2 : if (!dstDim)
947 0 : return false;
948 2 : mapExistingDstDims[newDimName] = dstDim;
949 2 : dstArrayDims.emplace_back(dstDim);
950 : }
951 : }
952 : }
953 :
954 138 : CPLStringList aosArrayCO;
955 69 : bool bAutoScale = false;
956 69 : GDALDataType eAutoScaleType = GDT_UInt16;
957 76 : for (const char *pszItem : cpl::Iterate(papszOptions))
958 : {
959 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
960 : {
961 7 : const char *pszOption = pszItem + strlen("ARRAY:");
962 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
963 : {
964 1 : const char *pszNext = strchr(pszOption, ':');
965 1 : if (pszNext != nullptr)
966 : {
967 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
968 1 : if (static_cast<size_t>(nDim) ==
969 1 : dstArrayDims.size())
970 : {
971 1 : pszOption = pszNext + 1;
972 : }
973 : else
974 : {
975 0 : pszOption = nullptr;
976 : }
977 : }
978 : }
979 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
980 : {
981 2 : const char *pszName = pszOption + strlen("IF(NAME=");
982 2 : const char *pszNext = strchr(pszName, ':');
983 2 : if (pszNext != nullptr && pszNext > pszName &&
984 2 : pszNext[-1] == ')')
985 : {
986 4 : CPLString osName;
987 2 : osName.assign(pszName, pszNext - pszName - 1);
988 3 : if (osName == srcArray->GetName() ||
989 1 : osName == srcArray->GetFullName())
990 : {
991 2 : pszOption = pszNext + 1;
992 : }
993 : else
994 : {
995 0 : pszOption = nullptr;
996 : }
997 : }
998 : }
999 7 : if (pszOption)
1000 : {
1001 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1002 : {
1003 : bAutoScale =
1004 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1005 : }
1006 5 : else if (STARTS_WITH_CI(pszOption,
1007 : "AUTOSCALE_DATA_TYPE="))
1008 : {
1009 1 : const char *pszDataType =
1010 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1011 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1012 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1013 1 : GDALDataTypeIsFloating(eAutoScaleType))
1014 : {
1015 0 : CPLError(CE_Failure, CPLE_NotSupported,
1016 : "Unsupported value for "
1017 : "AUTOSCALE_DATA_TYPE");
1018 0 : return false;
1019 : }
1020 : }
1021 : else
1022 : {
1023 4 : aosArrayCO.AddString(pszOption);
1024 : }
1025 : }
1026 : }
1027 : }
1028 :
1029 69 : if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
1030 : {
1031 136 : const auto anBlockSize = srcArray->GetBlockSize();
1032 136 : std::string osBlockSize;
1033 74 : for (auto v : anBlockSize)
1034 : {
1035 69 : if (v == 0)
1036 : {
1037 63 : osBlockSize.clear();
1038 63 : break;
1039 : }
1040 6 : if (!osBlockSize.empty())
1041 2 : osBlockSize += ',';
1042 6 : osBlockSize += std::to_string(v);
1043 : }
1044 68 : if (!osBlockSize.empty())
1045 3 : aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
1046 : }
1047 :
1048 : auto oIterDimName =
1049 69 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1050 69 : const auto &srcArrayType = srcArray->GetDataType();
1051 :
1052 69 : std::shared_ptr<GDALMDArray> dstArray;
1053 :
1054 : // Only autoscale non-indexing variables
1055 69 : bool bHasOffset = false;
1056 69 : bool bHasScale = false;
1057 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1058 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1059 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1060 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1061 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1062 73 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1063 71 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1064 : {
1065 2 : constexpr bool bApproxOK = false;
1066 2 : constexpr bool bForce = true;
1067 2 : double dfMin = 0.0;
1068 2 : double dfMax = 0.0;
1069 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1070 : nullptr, nullptr, nullptr, nullptr,
1071 2 : nullptr) != CE_None)
1072 : {
1073 0 : CPLError(CE_Failure, CPLE_AppDefined,
1074 : "Could not retrieve statistics for array %s",
1075 0 : srcArray->GetName().c_str());
1076 0 : return false;
1077 : }
1078 2 : double dfDTMin = 0;
1079 2 : double dfDTMax = 0;
1080 : #define setDTMinMax(ctype) \
1081 : do \
1082 : { \
1083 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1084 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1085 : } while (0)
1086 :
1087 2 : switch (eAutoScaleType)
1088 : {
1089 0 : case GDT_UInt8:
1090 0 : setDTMinMax(GByte);
1091 0 : break;
1092 0 : case GDT_Int8:
1093 0 : setDTMinMax(GInt8);
1094 0 : break;
1095 1 : case GDT_UInt16:
1096 1 : setDTMinMax(GUInt16);
1097 1 : break;
1098 1 : case GDT_Int16:
1099 1 : setDTMinMax(GInt16);
1100 1 : break;
1101 0 : case GDT_UInt32:
1102 0 : setDTMinMax(GUInt32);
1103 0 : break;
1104 0 : case GDT_Int32:
1105 0 : setDTMinMax(GInt32);
1106 0 : break;
1107 0 : case GDT_UInt64:
1108 0 : setDTMinMax(std::uint64_t);
1109 0 : break;
1110 0 : case GDT_Int64:
1111 0 : setDTMinMax(std::int64_t);
1112 0 : break;
1113 0 : case GDT_Float16:
1114 : case GDT_Float32:
1115 : case GDT_Float64:
1116 : case GDT_Unknown:
1117 : case GDT_CInt16:
1118 : case GDT_CInt32:
1119 : case GDT_CFloat16:
1120 : case GDT_CFloat32:
1121 : case GDT_CFloat64:
1122 : case GDT_TypeCount:
1123 0 : CPLAssert(false);
1124 : }
1125 :
1126 : dstArray =
1127 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1128 4 : GDALExtendedDataType::Create(eAutoScaleType),
1129 4 : aosArrayCO.List());
1130 2 : if (!dstArray)
1131 0 : return !bStrict;
1132 :
1133 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1134 : {
1135 : // If there's a nodata value in the source array, reserve
1136 : // DTMax for that purpose in the target scaled array
1137 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1138 : {
1139 0 : CPLError(CE_Failure, CPLE_AppDefined,
1140 : "Cannot set nodata value");
1141 0 : return false;
1142 : }
1143 1 : dfDTMax--;
1144 : }
1145 2 : const double dfScale =
1146 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1147 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1148 :
1149 4 : if (!dstArray->SetOffset(dfOffset) ||
1150 2 : !dstArray->SetScale(dfScale))
1151 : {
1152 0 : CPLError(CE_Failure, CPLE_AppDefined,
1153 : "Cannot set scale/offset");
1154 0 : return false;
1155 : }
1156 :
1157 2 : auto poUnscaled = dstArray->GetUnscaled();
1158 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1159 : {
1160 1 : poUnscaled->SetNoDataValue(
1161 : srcArray->GetNoDataValueAsDouble());
1162 : }
1163 :
1164 : // Copy source array into unscaled array
1165 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1166 : nCurCost, nTotalCost, pfnProgress,
1167 2 : pProgressData))
1168 : {
1169 0 : return false;
1170 : }
1171 : }
1172 : else
1173 : {
1174 134 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1175 134 : srcArrayType, aosArrayCO.List());
1176 67 : if (!dstArray)
1177 0 : return !bStrict;
1178 :
1179 134 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1180 : nCurCost, nTotalCost, pfnProgress,
1181 67 : pProgressData))
1182 : {
1183 0 : return false;
1184 : }
1185 : }
1186 :
1187 : // If this array is the indexing variable of a dimension, link them
1188 : // together.
1189 69 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1190 : {
1191 : auto oCorrespondingDimIter =
1192 35 : mapExistingDstDims.find(oIterDimName->second);
1193 35 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1194 : {
1195 : CPLErrorStateBackuper oErrorStateBackuper(
1196 35 : CPLQuietErrorHandler);
1197 70 : oCorrespondingDimIter->second->SetIndexingVariable(
1198 35 : std::move(dstArray));
1199 : }
1200 : }
1201 :
1202 69 : return true;
1203 33 : };
1204 :
1205 66 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1206 :
1207 : // Start by copying arrays that are indexing variables of dimensions
1208 102 : for (const auto &name : arrayNames)
1209 : {
1210 69 : auto srcArray = poSrcGroup->OpenMDArray(name);
1211 69 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1212 :
1213 69 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1214 69 : srcArray->GetName()))
1215 : {
1216 35 : if (!CopyArray(srcArray))
1217 0 : return false;
1218 : }
1219 : }
1220 :
1221 : // Then copy regular arrays
1222 102 : for (const auto &name : arrayNames)
1223 : {
1224 69 : auto srcArray = poSrcGroup->OpenMDArray(name);
1225 69 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1226 :
1227 69 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1228 69 : srcArray->GetName()))
1229 : {
1230 34 : if (!CopyArray(srcArray))
1231 0 : return false;
1232 : }
1233 : }
1234 :
1235 66 : const auto groupNames = poSrcGroup->GetGroupNames();
1236 39 : for (const auto &name : groupNames)
1237 : {
1238 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1239 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1240 6 : auto dstSubGroup = CreateGroup(name);
1241 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1242 12 : if (!dstSubGroup->CopyFrom(
1243 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1244 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1245 0 : return false;
1246 : }
1247 :
1248 33 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1249 0 : return false;
1250 :
1251 33 : return true;
1252 : }
1253 0 : catch (const std::exception &e)
1254 : {
1255 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1256 0 : return false;
1257 : }
1258 : }
1259 :
1260 : /************************************************************************/
1261 : /* GetInnerMostGroup() */
1262 : /************************************************************************/
1263 :
1264 : //! @cond Doxygen_Suppress
1265 : const GDALGroup *
1266 1511 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1267 : std::shared_ptr<GDALGroup> &curGroupHolder,
1268 : std::string &osLastPart) const
1269 : {
1270 1511 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1271 6 : return nullptr;
1272 1505 : const GDALGroup *poCurGroup = this;
1273 : CPLStringList aosTokens(
1274 3010 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1275 1505 : if (aosTokens.size() == 0)
1276 : {
1277 0 : return nullptr;
1278 : }
1279 :
1280 1843 : for (int i = 0; i < aosTokens.size() - 1; i++)
1281 : {
1282 350 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1283 350 : if (!curGroupHolder)
1284 : {
1285 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1286 : aosTokens[i]);
1287 12 : return nullptr;
1288 : }
1289 338 : poCurGroup = curGroupHolder.get();
1290 : }
1291 1493 : osLastPart = aosTokens[aosTokens.size() - 1];
1292 1493 : return poCurGroup;
1293 : }
1294 :
1295 : //! @endcond
1296 :
1297 : /************************************************************************/
1298 : /* OpenMDArrayFromFullname() */
1299 : /************************************************************************/
1300 :
1301 : /** Get an array from its fully qualified name */
1302 : std::shared_ptr<GDALMDArray>
1303 731 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1304 : CSLConstList papszOptions) const
1305 : {
1306 1462 : std::string osName;
1307 731 : std::shared_ptr<GDALGroup> curGroupHolder;
1308 731 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1309 731 : if (poGroup == nullptr)
1310 12 : return nullptr;
1311 719 : return poGroup->OpenMDArray(osName, papszOptions);
1312 : }
1313 :
1314 : /************************************************************************/
1315 : /* OpenAttributeFromFullname() */
1316 : /************************************************************************/
1317 :
1318 : /** Get an attribute from its fully qualified name */
1319 : std::shared_ptr<GDALAttribute>
1320 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1321 : CSLConstList papszOptions) const
1322 : {
1323 9 : const auto pos = osFullName.rfind('/');
1324 9 : if (pos == std::string::npos)
1325 0 : return nullptr;
1326 18 : const std::string attrName = osFullName.substr(pos + 1);
1327 9 : if (pos == 0)
1328 2 : return GetAttribute(attrName);
1329 14 : const std::string container = osFullName.substr(0, pos);
1330 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1331 7 : if (poArray)
1332 4 : return poArray->GetAttribute(attrName);
1333 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1334 3 : if (poGroup)
1335 1 : return poGroup->GetAttribute(attrName);
1336 2 : return nullptr;
1337 : }
1338 :
1339 : /************************************************************************/
1340 : /* ResolveMDArray() */
1341 : /************************************************************************/
1342 :
1343 : /** Locate an array in a group and its subgroups by name.
1344 : *
1345 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1346 : * used
1347 : * Otherwise the search will start from the group identified by osStartingPath,
1348 : * and an array whose name is osName will be looked for in this group (if
1349 : * osStartingPath is empty or "/", then the current group is used). If there
1350 : * is no match, then a recursive descendant search will be made in its
1351 : * subgroups. If there is no match in the subgroups, then the parent (if
1352 : * existing) of the group pointed by osStartingPath will be used as the new
1353 : * starting point for the search.
1354 : *
1355 : * @param osName name, qualified or not
1356 : * @param osStartingPath fully qualified name of the (sub-)group from which
1357 : * the search should be started. If this is a non-empty
1358 : * string, the group on which this method is called should
1359 : * nominally be the root group (otherwise the path will
1360 : * be interpreted as from the current group)
1361 : * @param papszOptions options to pass to OpenMDArray()
1362 : * @since GDAL 3.2
1363 : */
1364 : std::shared_ptr<GDALMDArray>
1365 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1366 : const std::string &osStartingPath,
1367 : CSLConstList papszOptions) const
1368 : {
1369 19 : if (!osName.empty() && osName[0] == '/')
1370 : {
1371 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1372 1 : if (poArray)
1373 1 : return poArray;
1374 : }
1375 36 : std::string osPath(osStartingPath);
1376 36 : std::set<std::string> oSetAlreadyVisited;
1377 :
1378 : while (true)
1379 : {
1380 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1381 0 : std::shared_ptr<GDALGroup> poGroup;
1382 :
1383 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1384 22 : bool goOn = false;
1385 22 : if (osPath.empty() || osPath == "/")
1386 : {
1387 11 : goOn = true;
1388 : }
1389 : else
1390 : {
1391 22 : std::string osLastPart;
1392 : const GDALGroup *poGroupPtr =
1393 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1394 11 : if (poGroupPtr)
1395 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1396 22 : if (poGroup &&
1397 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1398 : {
1399 11 : oQueue.push(poGroup);
1400 11 : goOn = true;
1401 : }
1402 : }
1403 :
1404 22 : if (goOn)
1405 : {
1406 17 : do
1407 : {
1408 : const GDALGroup *groupPtr;
1409 39 : if (!oQueue.empty())
1410 : {
1411 28 : poGroup = oQueue.front();
1412 28 : oQueue.pop();
1413 28 : groupPtr = poGroup.get();
1414 : }
1415 : else
1416 : {
1417 11 : groupPtr = this;
1418 : }
1419 :
1420 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1421 39 : if (poArray)
1422 16 : return poArray;
1423 :
1424 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1425 47 : for (const auto &osGroupName : aosGroupNames)
1426 : {
1427 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1428 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1429 48 : poSubGroup->GetFullName()))
1430 : {
1431 24 : oQueue.push(poSubGroup);
1432 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1433 : }
1434 : }
1435 23 : } while (!oQueue.empty());
1436 : }
1437 :
1438 6 : if (osPath.empty() || osPath == "/")
1439 2 : break;
1440 :
1441 4 : const auto nPos = osPath.rfind('/');
1442 4 : if (nPos == 0)
1443 1 : osPath = "/";
1444 : else
1445 : {
1446 3 : if (nPos == std::string::npos)
1447 0 : break;
1448 3 : osPath.resize(nPos);
1449 : }
1450 4 : }
1451 2 : return nullptr;
1452 : }
1453 :
1454 : /************************************************************************/
1455 : /* OpenGroupFromFullname() */
1456 : /************************************************************************/
1457 :
1458 : /** Get a group from its fully qualified name.
1459 : * @since GDAL 3.2
1460 : */
1461 : std::shared_ptr<GDALGroup>
1462 564 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1463 : CSLConstList papszOptions) const
1464 : {
1465 1128 : std::string osName;
1466 564 : std::shared_ptr<GDALGroup> curGroupHolder;
1467 564 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1468 564 : if (poGroup == nullptr)
1469 4 : return nullptr;
1470 560 : return poGroup->OpenGroup(osName, papszOptions);
1471 : }
1472 :
1473 : /************************************************************************/
1474 : /* OpenDimensionFromFullname() */
1475 : /************************************************************************/
1476 :
1477 : /** Get a dimension from its fully qualified name */
1478 : std::shared_ptr<GDALDimension>
1479 205 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1480 : {
1481 410 : std::string osName;
1482 205 : std::shared_ptr<GDALGroup> curGroupHolder;
1483 205 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1484 205 : if (poGroup == nullptr)
1485 2 : return nullptr;
1486 406 : auto dims(poGroup->GetDimensions());
1487 348 : for (auto &dim : dims)
1488 : {
1489 296 : if (dim->GetName() == osName)
1490 151 : return dim;
1491 : }
1492 52 : return nullptr;
1493 : }
1494 :
1495 : /************************************************************************/
1496 : /* ClearStatistics() */
1497 : /************************************************************************/
1498 :
1499 : /**
1500 : * \brief Clear statistics.
1501 : *
1502 : * @since GDAL 3.4
1503 : */
1504 0 : void GDALGroup::ClearStatistics()
1505 : {
1506 0 : auto groupNames = GetGroupNames();
1507 0 : for (const auto &name : groupNames)
1508 : {
1509 0 : auto subGroup = OpenGroup(name);
1510 0 : if (subGroup)
1511 : {
1512 0 : subGroup->ClearStatistics();
1513 : }
1514 : }
1515 :
1516 0 : auto arrayNames = GetMDArrayNames();
1517 0 : for (const auto &name : arrayNames)
1518 : {
1519 0 : auto array = OpenMDArray(name);
1520 0 : if (array)
1521 : {
1522 0 : array->ClearStatistics();
1523 : }
1524 : }
1525 0 : }
1526 :
1527 : /************************************************************************/
1528 : /* Rename() */
1529 : /************************************************************************/
1530 :
1531 : /** Rename the group.
1532 : *
1533 : * This is not implemented by all drivers.
1534 : *
1535 : * Drivers known to implement it: MEM, netCDF, ZARR.
1536 : *
1537 : * This is the same as the C function GDALGroupRename().
1538 : *
1539 : * @param osNewName New name.
1540 : *
1541 : * @return true in case of success
1542 : * @since GDAL 3.8
1543 : */
1544 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1545 : {
1546 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1547 0 : return false;
1548 : }
1549 :
1550 : /************************************************************************/
1551 : /* BaseRename() */
1552 : /************************************************************************/
1553 :
1554 : //! @cond Doxygen_Suppress
1555 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1556 : {
1557 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1558 8 : m_osFullName += osNewName;
1559 8 : m_osName = osNewName;
1560 :
1561 8 : NotifyChildrenOfRenaming();
1562 8 : }
1563 :
1564 : //! @endcond
1565 :
1566 : /************************************************************************/
1567 : /* ParentRenamed() */
1568 : /************************************************************************/
1569 :
1570 : //! @cond Doxygen_Suppress
1571 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1572 : {
1573 7 : m_osFullName = osNewParentFullName;
1574 7 : m_osFullName += "/";
1575 7 : m_osFullName += m_osName;
1576 :
1577 7 : NotifyChildrenOfRenaming();
1578 7 : }
1579 :
1580 : //! @endcond
1581 :
1582 : /************************************************************************/
1583 : /* Deleted() */
1584 : /************************************************************************/
1585 :
1586 : //! @cond Doxygen_Suppress
1587 22 : void GDALGroup::Deleted()
1588 : {
1589 22 : m_bValid = false;
1590 :
1591 22 : NotifyChildrenOfDeletion();
1592 22 : }
1593 :
1594 : //! @endcond
1595 :
1596 : /************************************************************************/
1597 : /* ParentDeleted() */
1598 : /************************************************************************/
1599 :
1600 : //! @cond Doxygen_Suppress
1601 3 : void GDALGroup::ParentDeleted()
1602 : {
1603 3 : Deleted();
1604 3 : }
1605 :
1606 : //! @endcond
1607 :
1608 : /************************************************************************/
1609 : /* CheckValidAndErrorOutIfNot() */
1610 : /************************************************************************/
1611 :
1612 : //! @cond Doxygen_Suppress
1613 17292 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1614 : {
1615 17292 : if (!m_bValid)
1616 : {
1617 14 : CPLError(CE_Failure, CPLE_AppDefined,
1618 : "This object has been deleted. No action on it is possible");
1619 : }
1620 17292 : return m_bValid;
1621 : }
1622 :
1623 : //! @endcond
1624 :
1625 : /************************************************************************/
1626 : /* ~GDALAbstractMDArray() */
1627 : /************************************************************************/
1628 :
1629 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1630 :
1631 : /************************************************************************/
1632 : /* GDALAbstractMDArray() */
1633 : /************************************************************************/
1634 :
1635 : //! @cond Doxygen_Suppress
1636 36772 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1637 36772 : const std::string &osName)
1638 : : m_osName(osName),
1639 : m_osFullName(
1640 36772 : !osParentName.empty()
1641 71615 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1642 145159 : : osName)
1643 : {
1644 36772 : }
1645 :
1646 : //! @endcond
1647 :
1648 : /************************************************************************/
1649 : /* GetDimensions() */
1650 : /************************************************************************/
1651 :
1652 : /** \fn GDALAbstractMDArray::GetDimensions() const
1653 : * \brief Return the dimensions of an attribute/array.
1654 : *
1655 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1656 : * similar to GDALAttributeGetDimensionsSize().
1657 : */
1658 :
1659 : /************************************************************************/
1660 : /* GetDataType() */
1661 : /************************************************************************/
1662 :
1663 : /** \fn GDALAbstractMDArray::GetDataType() const
1664 : * \brief Return the data type of an attribute/array.
1665 : *
1666 : * This is the same as the C functions GDALMDArrayGetDataType() and
1667 : * GDALAttributeGetDataType()
1668 : */
1669 :
1670 : /************************************************************************/
1671 : /* GetDimensionCount() */
1672 : /************************************************************************/
1673 :
1674 : /** Return the number of dimensions.
1675 : *
1676 : * Default implementation is GetDimensions().size(), and may be overridden by
1677 : * drivers if they have a faster / less expensive implementations.
1678 : *
1679 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1680 : * GDALAttributeGetDimensionCount().
1681 : *
1682 : */
1683 27913 : size_t GDALAbstractMDArray::GetDimensionCount() const
1684 : {
1685 27913 : return GetDimensions().size();
1686 : }
1687 :
1688 : /************************************************************************/
1689 : /* Rename() */
1690 : /************************************************************************/
1691 :
1692 : /** Rename the attribute/array.
1693 : *
1694 : * This is not implemented by all drivers.
1695 : *
1696 : * Drivers known to implement it: MEM, netCDF, Zarr.
1697 : *
1698 : * This is the same as the C functions GDALMDArrayRename() or
1699 : * GDALAttributeRename().
1700 : *
1701 : * @param osNewName New name.
1702 : *
1703 : * @return true in case of success
1704 : * @since GDAL 3.8
1705 : */
1706 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1707 : {
1708 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1709 0 : return false;
1710 : }
1711 :
1712 : /************************************************************************/
1713 : /* CopyValue() */
1714 : /************************************************************************/
1715 :
1716 : /** Convert a value from a source type to a destination type.
1717 : *
1718 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1719 : * that must be freed with CPLFree().
1720 : */
1721 85917 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1722 : const GDALExtendedDataType &srcType,
1723 : void *pDst,
1724 : const GDALExtendedDataType &dstType)
1725 : {
1726 166499 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1727 80582 : dstType.GetClass() == GEDTC_NUMERIC)
1728 : {
1729 79568 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1730 : dstType.GetNumericDataType(), 0, 1);
1731 79568 : return true;
1732 : }
1733 11462 : if (srcType.GetClass() == GEDTC_STRING &&
1734 5113 : dstType.GetClass() == GEDTC_STRING)
1735 : {
1736 : const char *srcStrPtr;
1737 3865 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1738 3865 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1739 3865 : *reinterpret_cast<void **>(pDst) = pszDup;
1740 3865 : return true;
1741 : }
1742 3498 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1743 1014 : dstType.GetClass() == GEDTC_STRING)
1744 : {
1745 1014 : const char *str = nullptr;
1746 1014 : switch (srcType.GetNumericDataType())
1747 : {
1748 0 : case GDT_Unknown:
1749 0 : break;
1750 0 : case GDT_UInt8:
1751 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1752 0 : break;
1753 3 : case GDT_Int8:
1754 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1755 3 : break;
1756 72 : case GDT_UInt16:
1757 72 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1758 72 : break;
1759 0 : case GDT_Int16:
1760 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1761 0 : break;
1762 26 : case GDT_UInt32:
1763 26 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1764 26 : break;
1765 69 : case GDT_Int32:
1766 69 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1767 69 : break;
1768 0 : case GDT_UInt64:
1769 : str =
1770 0 : CPLSPrintf(CPL_FRMT_GUIB,
1771 : static_cast<GUIntBig>(
1772 : *static_cast<const std::uint64_t *>(pSrc)));
1773 0 : break;
1774 30 : case GDT_Int64:
1775 30 : str = CPLSPrintf(CPL_FRMT_GIB,
1776 : static_cast<GIntBig>(
1777 : *static_cast<const std::int64_t *>(pSrc)));
1778 30 : break;
1779 0 : case GDT_Float16:
1780 0 : str = CPLSPrintf("%.5g",
1781 : double(*static_cast<const GFloat16 *>(pSrc)));
1782 0 : break;
1783 60 : case GDT_Float32:
1784 120 : str = CPLSPrintf(
1785 : "%.9g",
1786 60 : static_cast<double>(*static_cast<const float *>(pSrc)));
1787 60 : break;
1788 752 : case GDT_Float64:
1789 752 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1790 752 : break;
1791 2 : case GDT_CInt16:
1792 : {
1793 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1794 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1795 2 : break;
1796 : }
1797 0 : case GDT_CInt32:
1798 : {
1799 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1800 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1801 0 : break;
1802 : }
1803 0 : case GDT_CFloat16:
1804 : {
1805 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1806 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1807 0 : break;
1808 : }
1809 0 : case GDT_CFloat32:
1810 : {
1811 0 : const float *src = static_cast<const float *>(pSrc);
1812 0 : str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1813 0 : break;
1814 : }
1815 0 : case GDT_CFloat64:
1816 : {
1817 0 : const double *src = static_cast<const double *>(pSrc);
1818 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1819 0 : break;
1820 : }
1821 0 : case GDT_TypeCount:
1822 0 : CPLAssert(false);
1823 : break;
1824 : }
1825 1014 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1826 1014 : *reinterpret_cast<void **>(pDst) = pszDup;
1827 1014 : return true;
1828 : }
1829 2718 : if (srcType.GetClass() == GEDTC_STRING &&
1830 1248 : dstType.GetClass() == GEDTC_NUMERIC)
1831 : {
1832 : const char *srcStrPtr;
1833 1248 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1834 1248 : if (dstType.GetNumericDataType() == GDT_Int64)
1835 : {
1836 2 : *(static_cast<int64_t *>(pDst)) =
1837 2 : srcStrPtr == nullptr ? 0
1838 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1839 : }
1840 1246 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1841 : {
1842 2 : *(static_cast<uint64_t *>(pDst)) =
1843 2 : srcStrPtr == nullptr
1844 2 : ? 0
1845 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1846 : }
1847 : else
1848 : {
1849 1244 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1850 1244 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1851 : dstType.GetNumericDataType(), 0, 1);
1852 : }
1853 1248 : return true;
1854 : }
1855 444 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1856 222 : dstType.GetClass() == GEDTC_COMPOUND)
1857 : {
1858 222 : const auto &srcComponents = srcType.GetComponents();
1859 222 : const auto &dstComponents = dstType.GetComponents();
1860 222 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1861 222 : GByte *pabyDst = static_cast<GByte *>(pDst);
1862 :
1863 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1864 444 : srcComponentMap;
1865 1078 : for (const auto &srcComp : srcComponents)
1866 : {
1867 856 : srcComponentMap[srcComp->GetName()] = &srcComp;
1868 : }
1869 598 : for (const auto &dstComp : dstComponents)
1870 : {
1871 376 : auto oIter = srcComponentMap.find(dstComp->GetName());
1872 376 : if (oIter == srcComponentMap.end())
1873 0 : return false;
1874 376 : const auto &srcComp = *(oIter->second);
1875 1128 : if (!GDALExtendedDataType::CopyValue(
1876 376 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1877 376 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1878 : {
1879 0 : return false;
1880 : }
1881 : }
1882 222 : return true;
1883 : }
1884 :
1885 0 : return false;
1886 : }
1887 :
1888 : /************************************************************************/
1889 : /* CopyValues() */
1890 : /************************************************************************/
1891 :
1892 : /** Convert severals value from a source type to a destination type.
1893 : *
1894 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1895 : * that must be freed with CPLFree().
1896 : */
1897 370 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1898 : const GDALExtendedDataType &srcType,
1899 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1900 : const GDALExtendedDataType &dstType,
1901 : GPtrDiff_t nDstStrideInElts,
1902 : size_t nValues)
1903 : {
1904 : const auto nSrcStrideInBytes =
1905 370 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1906 : const auto nDstStrideInBytes =
1907 370 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1908 636 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1909 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1910 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1911 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1912 902 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1913 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1914 : {
1915 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1916 : static_cast<int>(nSrcStrideInBytes), pDst,
1917 : dstType.GetNumericDataType(),
1918 : static_cast<int>(nDstStrideInBytes), nValues);
1919 : }
1920 : else
1921 : {
1922 104 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1923 104 : GByte *pabyDst = static_cast<GByte *>(pDst);
1924 208 : for (size_t i = 0; i < nValues; ++i)
1925 : {
1926 104 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1927 0 : return false;
1928 104 : pabySrc += nSrcStrideInBytes;
1929 104 : pabyDst += nDstStrideInBytes;
1930 : }
1931 : }
1932 370 : return true;
1933 : }
1934 :
1935 : /************************************************************************/
1936 : /* CheckReadWriteParams() */
1937 : /************************************************************************/
1938 : //! @cond Doxygen_Suppress
1939 10592 : bool GDALAbstractMDArray::CheckReadWriteParams(
1940 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1941 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1942 : const void *buffer, const void *buffer_alloc_start,
1943 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1944 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1945 : {
1946 0 : const auto lamda_error = []()
1947 : {
1948 0 : CPLError(CE_Failure, CPLE_AppDefined,
1949 : "Not all elements pointed by buffer will fit in "
1950 : "[buffer_alloc_start, "
1951 : "buffer_alloc_start + buffer_alloc_size]");
1952 0 : };
1953 :
1954 10592 : const auto &dims = GetDimensions();
1955 10592 : if (dims.empty())
1956 : {
1957 4715 : if (buffer_alloc_start)
1958 : {
1959 4317 : const size_t elementSize = bufferDataType.GetSize();
1960 4317 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1961 4317 : const GByte *paby_buffer_alloc_start =
1962 : static_cast<const GByte *>(buffer_alloc_start);
1963 4317 : const GByte *paby_buffer_alloc_end =
1964 : paby_buffer_alloc_start + buffer_alloc_size;
1965 :
1966 4317 : if (paby_buffer < paby_buffer_alloc_start ||
1967 4317 : paby_buffer + elementSize > paby_buffer_alloc_end)
1968 : {
1969 0 : lamda_error();
1970 0 : return false;
1971 : }
1972 : }
1973 4715 : return true;
1974 : }
1975 :
1976 5877 : if (arrayStep == nullptr)
1977 : {
1978 1744 : tmp_arrayStep.resize(dims.size(), 1);
1979 1744 : arrayStep = tmp_arrayStep.data();
1980 : }
1981 16130 : for (size_t i = 0; i < dims.size(); i++)
1982 : {
1983 10253 : assert(count);
1984 10253 : if (count[i] == 0)
1985 : {
1986 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1987 : static_cast<unsigned>(i));
1988 0 : return false;
1989 : }
1990 : }
1991 5877 : bool bufferStride_all_positive = true;
1992 5877 : if (bufferStride == nullptr)
1993 : {
1994 1384 : GPtrDiff_t stride = 1;
1995 1384 : assert(dims.empty() || count != nullptr);
1996 : // To compute strides we must proceed from the fastest varying dimension
1997 : // (the last one), and then reverse the result
1998 3076 : for (size_t i = dims.size(); i != 0;)
1999 : {
2000 1692 : --i;
2001 1692 : tmp_bufferStride.push_back(stride);
2002 1692 : GUInt64 newStride = 0;
2003 : bool bOK;
2004 : try
2005 : {
2006 1692 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2007 3384 : CPLSM(static_cast<uint64_t>(count[i])))
2008 1692 : .v();
2009 1692 : bOK = static_cast<size_t>(newStride) == newStride &&
2010 1692 : newStride < std::numeric_limits<size_t>::max() / 2;
2011 : }
2012 0 : catch (...)
2013 : {
2014 0 : bOK = false;
2015 : }
2016 1692 : if (!bOK)
2017 : {
2018 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2019 0 : return false;
2020 : }
2021 1692 : stride = static_cast<GPtrDiff_t>(newStride);
2022 : }
2023 1384 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2024 1384 : bufferStride = tmp_bufferStride.data();
2025 : }
2026 : else
2027 : {
2028 13052 : for (size_t i = 0; i < dims.size(); i++)
2029 : {
2030 8560 : if (bufferStride[i] < 0)
2031 : {
2032 1 : bufferStride_all_positive = false;
2033 1 : break;
2034 : }
2035 : }
2036 : }
2037 16101 : for (size_t i = 0; i < dims.size(); i++)
2038 : {
2039 10234 : assert(arrayStartIdx);
2040 10234 : assert(count);
2041 10234 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2042 : {
2043 2 : CPLError(CE_Failure, CPLE_AppDefined,
2044 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2045 : static_cast<unsigned>(i),
2046 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2047 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2048 2 : return false;
2049 : }
2050 : bool bOverflow;
2051 10232 : if (arrayStep[i] >= 0)
2052 : {
2053 : try
2054 : {
2055 9577 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2056 9579 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2057 38311 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2058 9577 : .v() >= dims[i]->GetSize();
2059 : }
2060 1 : catch (...)
2061 : {
2062 1 : bOverflow = true;
2063 : }
2064 9578 : if (bOverflow)
2065 : {
2066 5 : CPLError(CE_Failure, CPLE_AppDefined,
2067 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2068 : ">= " CPL_FRMT_GUIB,
2069 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2070 : static_cast<unsigned>(i),
2071 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2072 5 : return false;
2073 : }
2074 : }
2075 : else
2076 : {
2077 : try
2078 : {
2079 654 : bOverflow =
2080 654 : arrayStartIdx[i] <
2081 654 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2082 1308 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2083 : ? (static_cast<uint64_t>(1) << 63)
2084 1308 : : static_cast<uint64_t>(-arrayStep[i])))
2085 654 : .v();
2086 : }
2087 0 : catch (...)
2088 : {
2089 0 : bOverflow = true;
2090 : }
2091 654 : if (bOverflow)
2092 : {
2093 3 : CPLError(
2094 : CE_Failure, CPLE_AppDefined,
2095 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2096 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2097 : static_cast<unsigned>(i));
2098 3 : return false;
2099 : }
2100 : }
2101 : }
2102 :
2103 5867 : if (buffer_alloc_start)
2104 : {
2105 2810 : const size_t elementSize = bufferDataType.GetSize();
2106 2810 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2107 2810 : const GByte *paby_buffer_alloc_start =
2108 : static_cast<const GByte *>(buffer_alloc_start);
2109 2810 : const GByte *paby_buffer_alloc_end =
2110 : paby_buffer_alloc_start + buffer_alloc_size;
2111 2810 : if (bufferStride_all_positive)
2112 : {
2113 2810 : if (paby_buffer < paby_buffer_alloc_start)
2114 : {
2115 0 : lamda_error();
2116 0 : return false;
2117 : }
2118 2810 : GUInt64 nOffset = elementSize;
2119 7996 : for (size_t i = 0; i < dims.size(); i++)
2120 : {
2121 : try
2122 : {
2123 5186 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2124 5186 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2125 10372 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2126 20744 : CPLSM(static_cast<uint64_t>(elementSize)))
2127 5186 : .v();
2128 : }
2129 0 : catch (...)
2130 : {
2131 0 : lamda_error();
2132 0 : return false;
2133 : }
2134 : }
2135 : #if SIZEOF_VOIDP == 4
2136 : if (static_cast<size_t>(nOffset) != nOffset)
2137 : {
2138 : lamda_error();
2139 : return false;
2140 : }
2141 : #endif
2142 2810 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2143 : {
2144 0 : lamda_error();
2145 0 : return false;
2146 : }
2147 : }
2148 0 : else if (dims.size() < 31)
2149 : {
2150 : // Check all corners of the hypercube
2151 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2152 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2153 : {
2154 0 : const GByte *paby = paby_buffer;
2155 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2156 : i++)
2157 : {
2158 0 : if (iCornerCode & (1U << i))
2159 : {
2160 : // We should check for integer overflows
2161 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2162 : }
2163 : }
2164 0 : if (paby < paby_buffer_alloc_start ||
2165 0 : paby + elementSize > paby_buffer_alloc_end)
2166 : {
2167 0 : lamda_error();
2168 0 : return false;
2169 : }
2170 : }
2171 : }
2172 : }
2173 :
2174 5867 : return true;
2175 : }
2176 :
2177 : //! @endcond
2178 :
2179 : /************************************************************************/
2180 : /* Read() */
2181 : /************************************************************************/
2182 :
2183 : /** Read part or totality of a multidimensional array or attribute.
2184 : *
2185 : * This will extract the content of a hyper-rectangle from the array into
2186 : * a user supplied buffer.
2187 : *
2188 : * If bufferDataType is of type string, the values written in pDstBuffer
2189 : * will be char* pointers and the strings should be freed with CPLFree().
2190 : *
2191 : * This is the same as the C function GDALMDArrayRead().
2192 : *
2193 : * @param arrayStartIdx Values representing the starting index to read
2194 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2195 : * Array of GetDimensionCount() values. Must not be
2196 : * nullptr, unless for a zero-dimensional array.
2197 : *
2198 : * @param count Values representing the number of values to extract in
2199 : * each dimension.
2200 : * Array of GetDimensionCount() values. Must not be
2201 : * nullptr, unless for a zero-dimensional array.
2202 : *
2203 : * @param arrayStep Spacing between values to extract in each dimension.
2204 : * The spacing is in number of array elements, not bytes.
2205 : * If provided, must contain GetDimensionCount() values.
2206 : * If set to nullptr, [1, 1, ... 1] will be used as a
2207 : * default to indicate consecutive elements.
2208 : *
2209 : * @param bufferStride Spacing between values to store in pDstBuffer.
2210 : * The spacing is in number of array elements, not bytes.
2211 : * If provided, must contain GetDimensionCount() values.
2212 : * Negative values are possible (for example to reorder
2213 : * from bottom-to-top to top-to-bottom).
2214 : * If set to nullptr, will be set so that pDstBuffer is
2215 : * written in a compact way, with elements of the last /
2216 : * fastest varying dimension being consecutive.
2217 : *
2218 : * @param bufferDataType Data type of values in pDstBuffer.
2219 : *
2220 : * @param pDstBuffer User buffer to store the values read. Should be big
2221 : * enough to store the number of values indicated by
2222 : * count[] and with the spacing of bufferStride[].
2223 : *
2224 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2225 : * validity of pDstBuffer. pDstBufferAllocStart
2226 : * should be the pointer returned by the malloc() or equivalent call used to
2227 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2228 : * bufferStride[] values are all positive), but not necessarily. If specified,
2229 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2230 : * validation is needed, nullptr can be passed.
2231 : *
2232 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2233 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2234 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2235 : * set to the appropriate value.
2236 : * If no validation is needed, 0 can be passed.
2237 : *
2238 : * @return true in case of success.
2239 : */
2240 3930 : bool GDALAbstractMDArray::Read(
2241 : const GUInt64 *arrayStartIdx, const size_t *count,
2242 : const GInt64 *arrayStep, // step in elements
2243 : const GPtrDiff_t *bufferStride, // stride in elements
2244 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2245 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2246 : {
2247 3930 : if (!GetDataType().CanConvertTo(bufferDataType))
2248 : {
2249 0 : CPLError(CE_Failure, CPLE_AppDefined,
2250 : "Array data type is not convertible to buffer data type");
2251 0 : return false;
2252 : }
2253 :
2254 7860 : std::vector<GInt64> tmp_arrayStep;
2255 7860 : std::vector<GPtrDiff_t> tmp_bufferStride;
2256 3930 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2257 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2258 : nDstBufferAllocSize, tmp_arrayStep,
2259 : tmp_bufferStride))
2260 : {
2261 0 : return false;
2262 : }
2263 :
2264 3930 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2265 3930 : pDstBuffer);
2266 : }
2267 :
2268 : /************************************************************************/
2269 : /* IWrite() */
2270 : /************************************************************************/
2271 :
2272 : //! @cond Doxygen_Suppress
2273 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2274 : const GInt64 *, const GPtrDiff_t *,
2275 : const GDALExtendedDataType &, const void *)
2276 : {
2277 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2278 1 : return false;
2279 : }
2280 :
2281 : //! @endcond
2282 :
2283 : /************************************************************************/
2284 : /* Write() */
2285 : /************************************************************************/
2286 :
2287 : /** Write part or totality of a multidimensional array or attribute.
2288 : *
2289 : * This will set the content of a hyper-rectangle into the array from
2290 : * a user supplied buffer.
2291 : *
2292 : * If bufferDataType is of type string, the values read from pSrcBuffer
2293 : * will be char* pointers.
2294 : *
2295 : * This is the same as the C function GDALMDArrayWrite().
2296 : *
2297 : * @param arrayStartIdx Values representing the starting index to write
2298 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2299 : * Array of GetDimensionCount() values. Must not be
2300 : * nullptr, unless for a zero-dimensional array.
2301 : *
2302 : * @param count Values representing the number of values to write in
2303 : * each dimension.
2304 : * Array of GetDimensionCount() values. Must not be
2305 : * nullptr, unless for a zero-dimensional array.
2306 : *
2307 : * @param arrayStep Spacing between values to write in each dimension.
2308 : * The spacing is in number of array elements, not bytes.
2309 : * If provided, must contain GetDimensionCount() values.
2310 : * If set to nullptr, [1, 1, ... 1] will be used as a
2311 : * default to indicate consecutive elements.
2312 : *
2313 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2314 : * The spacing is in number of array elements, not bytes.
2315 : * If provided, must contain GetDimensionCount() values.
2316 : * Negative values are possible (for example to reorder
2317 : * from bottom-to-top to top-to-bottom).
2318 : * If set to nullptr, will be set so that pSrcBuffer is
2319 : * written in a compact way, with elements of the last /
2320 : * fastest varying dimension being consecutive.
2321 : *
2322 : * @param bufferDataType Data type of values in pSrcBuffer.
2323 : *
2324 : * @param pSrcBuffer User buffer to read the values from. Should be big
2325 : * enough to store the number of values indicated by
2326 : * count[] and with the spacing of bufferStride[].
2327 : *
2328 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2329 : * validity of pSrcBuffer. pSrcBufferAllocStart
2330 : * should be the pointer returned by the malloc() or equivalent call used to
2331 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2332 : * bufferStride[] values are all positive), but not necessarily. If specified,
2333 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2334 : * validation is needed, nullptr can be passed.
2335 : *
2336 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2337 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2338 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2339 : * set to the appropriate value.
2340 : * If no validation is needed, 0 can be passed.
2341 : *
2342 : * @return true in case of success.
2343 : */
2344 2185 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2345 : const size_t *count, const GInt64 *arrayStep,
2346 : const GPtrDiff_t *bufferStride,
2347 : const GDALExtendedDataType &bufferDataType,
2348 : const void *pSrcBuffer,
2349 : const void *pSrcBufferAllocStart,
2350 : size_t nSrcBufferAllocSize)
2351 : {
2352 2185 : if (!bufferDataType.CanConvertTo(GetDataType()))
2353 : {
2354 0 : CPLError(CE_Failure, CPLE_AppDefined,
2355 : "Buffer data type is not convertible to array data type");
2356 0 : return false;
2357 : }
2358 :
2359 4370 : std::vector<GInt64> tmp_arrayStep;
2360 4370 : std::vector<GPtrDiff_t> tmp_bufferStride;
2361 2185 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2362 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2363 : nSrcBufferAllocSize, tmp_arrayStep,
2364 : tmp_bufferStride))
2365 : {
2366 0 : return false;
2367 : }
2368 :
2369 2185 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2370 2185 : pSrcBuffer);
2371 : }
2372 :
2373 : /************************************************************************/
2374 : /* GetTotalElementsCount() */
2375 : /************************************************************************/
2376 :
2377 : /** Return the total number of values in the array.
2378 : *
2379 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2380 : * and GDALAttributeGetTotalElementsCount().
2381 : *
2382 : */
2383 1462 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2384 : {
2385 1462 : const auto &dims = GetDimensions();
2386 1462 : if (dims.empty())
2387 792 : return 1;
2388 670 : GUInt64 nElts = 1;
2389 1478 : for (const auto &dim : dims)
2390 : {
2391 : try
2392 : {
2393 808 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2394 2424 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2395 808 : .v();
2396 : }
2397 0 : catch (...)
2398 : {
2399 0 : return 0;
2400 : }
2401 : }
2402 670 : return nElts;
2403 : }
2404 :
2405 : /************************************************************************/
2406 : /* GetBlockSize() */
2407 : /************************************************************************/
2408 :
2409 : /** Return the "natural" block size of the array along all dimensions.
2410 : *
2411 : * Some drivers might organize the array in tiles/blocks and reading/writing
2412 : * aligned on those tile/block boundaries will be more efficient.
2413 : *
2414 : * The returned number of elements in the vector is the same as
2415 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2416 : * the natural block size along the considered dimension.
2417 : * "Flat" arrays will typically return a vector of values set to 0.
2418 : *
2419 : * The default implementation will return a vector of values set to 0.
2420 : *
2421 : * This method is used by GetProcessingChunkSize().
2422 : *
2423 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
2424 : * theoretical case of a 32-bit platform, this might exceed its size_t
2425 : * allocation capabilities.
2426 : *
2427 : * This is the same as the C function GDALMDArrayGetBlockSize().
2428 : *
2429 : * @return the block size, in number of elements along each dimension.
2430 : */
2431 297 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2432 : {
2433 297 : return std::vector<GUInt64>(GetDimensionCount());
2434 : }
2435 :
2436 : /************************************************************************/
2437 : /* GetProcessingChunkSize() */
2438 : /************************************************************************/
2439 :
2440 : /** \brief Return an optimal chunk size for read/write operations, given the
2441 : * natural block size and memory constraints specified.
2442 : *
2443 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2444 : * multiple of those returned by GetBlockSize() (unless the block define by
2445 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2446 : * returned by this method).
2447 : *
2448 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2449 : *
2450 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2451 : * chunk.
2452 : *
2453 : * @return the chunk size, in number of elements along each dimension.
2454 : */
2455 : std::vector<size_t>
2456 90 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2457 : {
2458 90 : const auto &dims = GetDimensions();
2459 90 : const auto &nDTSize = GetDataType().GetSize();
2460 90 : std::vector<size_t> anChunkSize;
2461 180 : auto blockSize = GetBlockSize();
2462 90 : CPLAssert(blockSize.size() == dims.size());
2463 90 : size_t nChunkSize = nDTSize;
2464 90 : bool bOverflow = false;
2465 90 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2466 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2467 : // [1, min(sizet_max, dim_size[i])]
2468 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2469 246 : for (size_t i = 0; i < dims.size(); i++)
2470 : {
2471 : const auto sizeDimI =
2472 312 : std::max(static_cast<size_t>(1),
2473 312 : static_cast<size_t>(
2474 312 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2475 156 : std::min(blockSize[i], dims[i]->GetSize()))));
2476 156 : anChunkSize.push_back(sizeDimI);
2477 156 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2478 : {
2479 4 : bOverflow = true;
2480 : }
2481 : else
2482 : {
2483 152 : nChunkSize *= sizeDimI;
2484 : }
2485 : }
2486 90 : if (nChunkSize == 0)
2487 0 : return anChunkSize;
2488 :
2489 : // If the product of all anChunkSize[i] does not fit on size_t, then
2490 : // set lowest anChunkSize[i] to 1.
2491 90 : if (bOverflow)
2492 : {
2493 2 : nChunkSize = nDTSize;
2494 2 : bOverflow = false;
2495 8 : for (size_t i = dims.size(); i > 0;)
2496 : {
2497 6 : --i;
2498 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2499 : {
2500 4 : bOverflow = true;
2501 4 : anChunkSize[i] = 1;
2502 : }
2503 : else
2504 : {
2505 2 : nChunkSize *= anChunkSize[i];
2506 : }
2507 : }
2508 : }
2509 :
2510 90 : nChunkSize = nDTSize;
2511 180 : std::vector<size_t> anAccBlockSizeFromStart;
2512 246 : for (size_t i = 0; i < dims.size(); i++)
2513 : {
2514 156 : nChunkSize *= anChunkSize[i];
2515 156 : anAccBlockSizeFromStart.push_back(nChunkSize);
2516 : }
2517 90 : if (nChunkSize <= nMaxChunkMemory / 2)
2518 : {
2519 86 : size_t nVoxelsFromEnd = 1;
2520 234 : for (size_t i = dims.size(); i > 0;)
2521 : {
2522 148 : --i;
2523 : const auto nCurBlockSize =
2524 148 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2525 148 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2526 148 : if (nMul >= 2)
2527 : {
2528 140 : const auto nSizeThisDim(dims[i]->GetSize());
2529 : const auto nBlocksThisDim =
2530 140 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2531 140 : anChunkSize[i] = static_cast<size_t>(std::min(
2532 140 : anChunkSize[i] *
2533 280 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2534 140 : nSizeThisDim));
2535 : }
2536 148 : nVoxelsFromEnd *= anChunkSize[i];
2537 : }
2538 : }
2539 90 : return anChunkSize;
2540 : }
2541 :
2542 : /************************************************************************/
2543 : /* BaseRename() */
2544 : /************************************************************************/
2545 :
2546 : //! @cond Doxygen_Suppress
2547 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2548 : {
2549 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2550 18 : m_osFullName += osNewName;
2551 18 : m_osName = osNewName;
2552 :
2553 18 : NotifyChildrenOfRenaming();
2554 18 : }
2555 :
2556 : //! @endcond
2557 :
2558 : //! @cond Doxygen_Suppress
2559 : /************************************************************************/
2560 : /* ParentRenamed() */
2561 : /************************************************************************/
2562 :
2563 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2564 : {
2565 50 : m_osFullName = osNewParentFullName;
2566 50 : m_osFullName += "/";
2567 50 : m_osFullName += m_osName;
2568 :
2569 50 : NotifyChildrenOfRenaming();
2570 50 : }
2571 :
2572 : //! @endcond
2573 :
2574 : /************************************************************************/
2575 : /* Deleted() */
2576 : /************************************************************************/
2577 :
2578 : //! @cond Doxygen_Suppress
2579 52 : void GDALAbstractMDArray::Deleted()
2580 : {
2581 52 : m_bValid = false;
2582 :
2583 52 : NotifyChildrenOfDeletion();
2584 52 : }
2585 :
2586 : //! @endcond
2587 :
2588 : /************************************************************************/
2589 : /* ParentDeleted() */
2590 : /************************************************************************/
2591 :
2592 : //! @cond Doxygen_Suppress
2593 28 : void GDALAbstractMDArray::ParentDeleted()
2594 : {
2595 28 : Deleted();
2596 28 : }
2597 :
2598 : //! @endcond
2599 :
2600 : /************************************************************************/
2601 : /* CheckValidAndErrorOutIfNot() */
2602 : /************************************************************************/
2603 :
2604 : //! @cond Doxygen_Suppress
2605 6793 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2606 : {
2607 6793 : if (!m_bValid)
2608 : {
2609 26 : CPLError(CE_Failure, CPLE_AppDefined,
2610 : "This object has been deleted. No action on it is possible");
2611 : }
2612 6793 : return m_bValid;
2613 : }
2614 :
2615 : //! @endcond
2616 :
2617 : /************************************************************************/
2618 : /* SetUnit() */
2619 : /************************************************************************/
2620 :
2621 : /** Set the variable unit.
2622 : *
2623 : * Values should conform as much as possible with those allowed by
2624 : * the NetCDF CF conventions:
2625 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2626 : * but others might be returned.
2627 : *
2628 : * Few examples are "meter", "degrees", "second", ...
2629 : * Empty value means unknown.
2630 : *
2631 : * This is the same as the C function GDALMDArraySetUnit()
2632 : *
2633 : * @note Driver implementation: optionally implemented.
2634 : *
2635 : * @param osUnit unit name.
2636 : * @return true in case of success.
2637 : */
2638 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2639 : {
2640 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2641 0 : return false;
2642 : }
2643 :
2644 : /************************************************************************/
2645 : /* GetUnit() */
2646 : /************************************************************************/
2647 :
2648 : /** Return the array unit.
2649 : *
2650 : * Values should conform as much as possible with those allowed by
2651 : * the NetCDF CF conventions:
2652 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2653 : * but others might be returned.
2654 : *
2655 : * Few examples are "meter", "degrees", "second", ...
2656 : * Empty value means unknown.
2657 : *
2658 : * This is the same as the C function GDALMDArrayGetUnit()
2659 : */
2660 5 : const std::string &GDALMDArray::GetUnit() const
2661 : {
2662 5 : static const std::string emptyString;
2663 5 : return emptyString;
2664 : }
2665 :
2666 : /************************************************************************/
2667 : /* SetSpatialRef() */
2668 : /************************************************************************/
2669 :
2670 : /** Assign a spatial reference system object to the array.
2671 : *
2672 : * This is the same as the C function GDALMDArraySetSpatialRef().
2673 : */
2674 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2675 : {
2676 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2677 0 : return false;
2678 : }
2679 :
2680 : /************************************************************************/
2681 : /* GetSpatialRef() */
2682 : /************************************************************************/
2683 :
2684 : /** Return the spatial reference system object associated with the array.
2685 : *
2686 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2687 : */
2688 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2689 : {
2690 4 : return nullptr;
2691 : }
2692 :
2693 : /************************************************************************/
2694 : /* GetRawNoDataValue() */
2695 : /************************************************************************/
2696 :
2697 : /** Return the nodata value as a "raw" value.
2698 : *
2699 : * The value returned might be nullptr in case of no nodata value. When
2700 : * a nodata value is registered, a non-nullptr will be returned whose size in
2701 : * bytes is GetDataType().GetSize().
2702 : *
2703 : * The returned value should not be modified or freed. It is valid until
2704 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2705 : * SetRawNoDataValue(), or any similar methods.
2706 : *
2707 : * @note Driver implementation: this method shall be implemented if nodata
2708 : * is supported.
2709 : *
2710 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2711 : *
2712 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2713 : */
2714 5 : const void *GDALMDArray::GetRawNoDataValue() const
2715 : {
2716 5 : return nullptr;
2717 : }
2718 :
2719 : /************************************************************************/
2720 : /* GetNoDataValueAsDouble() */
2721 : /************************************************************************/
2722 :
2723 : /** Return the nodata value as a double.
2724 : *
2725 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2726 : *
2727 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2728 : * a nodata value exists and can be converted to double. Might be nullptr.
2729 : *
2730 : * @return the nodata value as a double. A 0.0 value might also indicate the
2731 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2732 : * set to false then).
2733 : */
2734 22515 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2735 : {
2736 22515 : const void *pNoData = GetRawNoDataValue();
2737 22515 : double dfNoData = 0.0;
2738 22515 : const auto &eDT = GetDataType();
2739 22515 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2740 22515 : if (ok)
2741 : {
2742 22194 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2743 : GDT_Float64, 0, 1);
2744 : }
2745 22515 : if (pbHasNoData)
2746 474 : *pbHasNoData = ok;
2747 22515 : return dfNoData;
2748 : }
2749 :
2750 : /************************************************************************/
2751 : /* GetNoDataValueAsInt64() */
2752 : /************************************************************************/
2753 :
2754 : /** Return the nodata value as a Int64.
2755 : *
2756 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2757 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2758 : *
2759 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2760 : *
2761 : * @return the nodata value as a Int64
2762 : *
2763 : * @since GDAL 3.5
2764 : */
2765 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2766 : {
2767 12 : const void *pNoData = GetRawNoDataValue();
2768 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2769 12 : const auto &eDT = GetDataType();
2770 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2771 12 : if (ok)
2772 : {
2773 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2774 : GDT_Int64, 0, 1);
2775 : }
2776 12 : if (pbHasNoData)
2777 12 : *pbHasNoData = ok;
2778 12 : return nNoData;
2779 : }
2780 :
2781 : /************************************************************************/
2782 : /* GetNoDataValueAsUInt64() */
2783 : /************************************************************************/
2784 :
2785 : /** Return the nodata value as a UInt64.
2786 : *
2787 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2788 :
2789 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2790 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2791 : *
2792 : * @return the nodata value as a UInt64
2793 : *
2794 : * @since GDAL 3.5
2795 : */
2796 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2797 : {
2798 8 : const void *pNoData = GetRawNoDataValue();
2799 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2800 8 : const auto &eDT = GetDataType();
2801 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2802 8 : if (ok)
2803 : {
2804 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2805 : GDT_UInt64, 0, 1);
2806 : }
2807 8 : if (pbHasNoData)
2808 8 : *pbHasNoData = ok;
2809 8 : return nNoData;
2810 : }
2811 :
2812 : /************************************************************************/
2813 : /* SetRawNoDataValue() */
2814 : /************************************************************************/
2815 :
2816 : /** Set the nodata value as a "raw" value.
2817 : *
2818 : * The value passed might be nullptr in case of no nodata value. When
2819 : * a nodata value is registered, a non-nullptr whose size in
2820 : * bytes is GetDataType().GetSize() must be passed.
2821 : *
2822 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2823 : *
2824 : * @note Driver implementation: this method shall be implemented if setting
2825 : nodata
2826 : * is supported.
2827 :
2828 : * @return true in case of success.
2829 : */
2830 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2831 : {
2832 0 : CPLError(CE_Failure, CPLE_NotSupported,
2833 : "SetRawNoDataValue() not implemented");
2834 0 : return false;
2835 : }
2836 :
2837 : /************************************************************************/
2838 : /* SetNoDataValue() */
2839 : /************************************************************************/
2840 :
2841 : /** Set the nodata value as a double.
2842 : *
2843 : * If the natural data type of the attribute/array is not double, type
2844 : * conversion will occur to the type returned by GetDataType().
2845 : *
2846 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2847 : *
2848 : * @return true in case of success.
2849 : */
2850 61 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2851 : {
2852 61 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2853 61 : bool bRet = false;
2854 61 : if (GDALExtendedDataType::CopyValue(
2855 122 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2856 61 : GetDataType()))
2857 : {
2858 61 : bRet = SetRawNoDataValue(pRawNoData);
2859 : }
2860 61 : CPLFree(pRawNoData);
2861 61 : return bRet;
2862 : }
2863 :
2864 : /************************************************************************/
2865 : /* SetNoDataValue() */
2866 : /************************************************************************/
2867 :
2868 : /** Set the nodata value as a Int64.
2869 : *
2870 : * If the natural data type of the attribute/array is not Int64, type conversion
2871 : * will occur to the type returned by GetDataType().
2872 : *
2873 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2874 : *
2875 : * @return true in case of success.
2876 : *
2877 : * @since GDAL 3.5
2878 : */
2879 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2880 : {
2881 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2882 3 : bool bRet = false;
2883 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2884 6 : GDALExtendedDataType::Create(GDT_Int64),
2885 3 : pRawNoData, GetDataType()))
2886 : {
2887 3 : bRet = SetRawNoDataValue(pRawNoData);
2888 : }
2889 3 : CPLFree(pRawNoData);
2890 3 : return bRet;
2891 : }
2892 :
2893 : /************************************************************************/
2894 : /* SetNoDataValue() */
2895 : /************************************************************************/
2896 :
2897 : /** Set the nodata value as a Int64.
2898 : *
2899 : * If the natural data type of the attribute/array is not Int64, type conversion
2900 : * will occur to the type returned by GetDataType().
2901 : *
2902 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2903 : *
2904 : * @return true in case of success.
2905 : *
2906 : * @since GDAL 3.5
2907 : */
2908 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2909 : {
2910 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2911 1 : bool bRet = false;
2912 1 : if (GDALExtendedDataType::CopyValue(
2913 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2914 1 : GetDataType()))
2915 : {
2916 1 : bRet = SetRawNoDataValue(pRawNoData);
2917 : }
2918 1 : CPLFree(pRawNoData);
2919 1 : return bRet;
2920 : }
2921 :
2922 : /************************************************************************/
2923 : /* Resize() */
2924 : /************************************************************************/
2925 :
2926 : /** Resize an array to new dimensions.
2927 : *
2928 : * Not all drivers may allow this operation, and with restrictions (e.g.
2929 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2930 : *
2931 : * Resizing a dimension used in other arrays will cause those other arrays
2932 : * to be resized.
2933 : *
2934 : * This is the same as the C function GDALMDArrayResize().
2935 : *
2936 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2937 : * new size of each indexing dimension.
2938 : * @param papszOptions Options. (Driver specific)
2939 : * @return true in case of success.
2940 : * @since GDAL 3.7
2941 : */
2942 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2943 : CPL_UNUSED CSLConstList papszOptions)
2944 : {
2945 0 : CPLError(CE_Failure, CPLE_NotSupported,
2946 : "Resize() is not supported for this array");
2947 0 : return false;
2948 : }
2949 :
2950 : /************************************************************************/
2951 : /* SetScale() */
2952 : /************************************************************************/
2953 :
2954 : /** Set the scale value to apply to raw values.
2955 : *
2956 : * unscaled_value = raw_value * GetScale() + GetOffset()
2957 : *
2958 : * This is the same as the C function GDALMDArraySetScale() /
2959 : * GDALMDArraySetScaleEx().
2960 : *
2961 : * @note Driver implementation: this method shall be implemented if setting
2962 : * scale is supported.
2963 : *
2964 : * @param dfScale scale
2965 : * @param eStorageType Data type to which create the potential attribute that
2966 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2967 : * implementation will decide automatically the data type. Note that changing
2968 : * the data type after initial setting might not be supported.
2969 : * @return true in case of success.
2970 : */
2971 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2972 : CPL_UNUSED GDALDataType eStorageType)
2973 : {
2974 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2975 0 : return false;
2976 : }
2977 :
2978 : /************************************************************************/
2979 : /* SetOffset) */
2980 : /************************************************************************/
2981 :
2982 : /** Set the offset value to apply to raw values.
2983 : *
2984 : * unscaled_value = raw_value * GetScale() + GetOffset()
2985 : *
2986 : * This is the same as the C function GDALMDArraySetOffset() /
2987 : * GDALMDArraySetOffsetEx().
2988 : *
2989 : * @note Driver implementation: this method shall be implemented if setting
2990 : * offset is supported.
2991 : *
2992 : * @param dfOffset Offset
2993 : * @param eStorageType Data type to which create the potential attribute that
2994 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2995 : * implementation will decide automatically the data type. Note that changing
2996 : * the data type after initial setting might not be supported.
2997 : * @return true in case of success.
2998 : */
2999 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
3000 : CPL_UNUSED GDALDataType eStorageType)
3001 : {
3002 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
3003 0 : return false;
3004 : }
3005 :
3006 : /************************************************************************/
3007 : /* GetScale() */
3008 : /************************************************************************/
3009 :
3010 : /** Get the scale value to apply to raw values.
3011 : *
3012 : * unscaled_value = raw_value * GetScale() + GetOffset()
3013 : *
3014 : * This is the same as the C function GDALMDArrayGetScale().
3015 : *
3016 : * @note Driver implementation: this method shall be implemented if getting
3017 : * scale is supported.
3018 : *
3019 : * @param pbHasScale Pointer to a output boolean that will be set to true if
3020 : * a scale value exists. Might be nullptr.
3021 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3022 : * the storage type of the scale value, when known/relevant. Otherwise will be
3023 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3024 : *
3025 : * @return the scale value. A 1.0 value might also indicate the
3026 : * absence of a scale value.
3027 : */
3028 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3029 : CPL_UNUSED GDALDataType *peStorageType) const
3030 : {
3031 20 : if (pbHasScale)
3032 20 : *pbHasScale = false;
3033 20 : return 1.0;
3034 : }
3035 :
3036 : /************************************************************************/
3037 : /* GetOffset() */
3038 : /************************************************************************/
3039 :
3040 : /** Get the offset value to apply to raw values.
3041 : *
3042 : * unscaled_value = raw_value * GetScale() + GetOffset()
3043 : *
3044 : * This is the same as the C function GDALMDArrayGetOffset().
3045 : *
3046 : * @note Driver implementation: this method shall be implemented if getting
3047 : * offset is supported.
3048 : *
3049 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3050 : * a offset value exists. Might be nullptr.
3051 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3052 : * the storage type of the offset value, when known/relevant. Otherwise will be
3053 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3054 : *
3055 : * @return the offset value. A 0.0 value might also indicate the
3056 : * absence of a offset value.
3057 : */
3058 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3059 : CPL_UNUSED GDALDataType *peStorageType) const
3060 : {
3061 20 : if (pbHasOffset)
3062 20 : *pbHasOffset = false;
3063 20 : return 0.0;
3064 : }
3065 :
3066 : /************************************************************************/
3067 : /* ProcessPerChunk() */
3068 : /************************************************************************/
3069 :
3070 : namespace
3071 : {
3072 : enum class Caller
3073 : {
3074 : CALLER_END_OF_LOOP,
3075 : CALLER_IN_LOOP,
3076 : };
3077 : }
3078 :
3079 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3080 : *
3081 : * This method is to be used when doing operations on an array, or a subset of
3082 : * it, in a chunk by chunk way.
3083 : *
3084 : * @param arrayStartIdx Values representing the starting index to use
3085 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3086 : * Array of GetDimensionCount() values. Must not be
3087 : * nullptr, unless for a zero-dimensional array.
3088 : *
3089 : * @param count Values representing the number of values to use in
3090 : * each dimension.
3091 : * Array of GetDimensionCount() values. Must not be
3092 : * nullptr, unless for a zero-dimensional array.
3093 : *
3094 : * @param chunkSize Values representing the chunk size in each dimension.
3095 : * Might typically the output of GetProcessingChunkSize().
3096 : * Array of GetDimensionCount() values. Must not be
3097 : * nullptr, unless for a zero-dimensional array.
3098 : *
3099 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3100 : * Must NOT be nullptr.
3101 : *
3102 : * @param pUserData Pointer to pass as the value of the pUserData argument
3103 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3104 : *
3105 : * @return true in case of success.
3106 : */
3107 88 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3108 : const GUInt64 *count,
3109 : const size_t *chunkSize,
3110 : FuncProcessPerChunkType pfnFunc,
3111 : void *pUserData)
3112 : {
3113 88 : const auto &dims = GetDimensions();
3114 88 : if (dims.empty())
3115 : {
3116 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3117 : }
3118 :
3119 : // Sanity check
3120 86 : size_t nTotalChunkSize = 1;
3121 219 : for (size_t i = 0; i < dims.size(); i++)
3122 : {
3123 140 : const auto nSizeThisDim(dims[i]->GetSize());
3124 140 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3125 138 : arrayStartIdx[i] > nSizeThisDim - count[i])
3126 : {
3127 4 : CPLError(CE_Failure, CPLE_AppDefined,
3128 : "Inconsistent arrayStartIdx[] / count[] values "
3129 : "regarding array size");
3130 4 : return false;
3131 : }
3132 270 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3133 134 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3134 : {
3135 3 : CPLError(CE_Failure, CPLE_AppDefined,
3136 : "Inconsistent chunkSize[] values");
3137 3 : return false;
3138 : }
3139 133 : nTotalChunkSize *= chunkSize[i];
3140 : }
3141 :
3142 79 : size_t dimIdx = 0;
3143 158 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3144 158 : std::vector<size_t> chunkCount(dims.size());
3145 :
3146 : struct Stack
3147 : {
3148 : GUInt64 nBlockCounter = 0;
3149 : GUInt64 nBlocksMinusOne = 0;
3150 : size_t first_count = 0; // only used if nBlocks > 1
3151 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3152 : };
3153 :
3154 158 : std::vector<Stack> stack(dims.size());
3155 79 : GUInt64 iCurChunk = 0;
3156 79 : GUInt64 nChunkCount = 1;
3157 211 : for (size_t i = 0; i < dims.size(); i++)
3158 : {
3159 132 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3160 132 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3161 132 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3162 132 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3163 132 : if (stack[i].nBlocksMinusOne == 0)
3164 : {
3165 127 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3166 127 : chunkCount[i] = static_cast<size_t>(count[i]);
3167 : }
3168 : else
3169 : {
3170 5 : stack[i].first_count = static_cast<size_t>(
3171 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3172 : }
3173 : }
3174 :
3175 79 : lbl_next_depth:
3176 321 : if (dimIdx == dims.size())
3177 : {
3178 112 : ++iCurChunk;
3179 112 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3180 : iCurChunk, nChunkCount, pUserData))
3181 : {
3182 1 : return false;
3183 : }
3184 : }
3185 : else
3186 : {
3187 209 : if (stack[dimIdx].nBlocksMinusOne != 0)
3188 : {
3189 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3190 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3191 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3192 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3193 : while (true)
3194 : {
3195 33 : dimIdx++;
3196 33 : goto lbl_next_depth;
3197 33 : lbl_return_to_caller_in_loop:
3198 33 : --stack[dimIdx].nBlockCounter;
3199 33 : if (stack[dimIdx].nBlockCounter == 0)
3200 11 : break;
3201 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3202 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3203 : }
3204 :
3205 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3206 22 : chunkCount[dimIdx] =
3207 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3208 11 : chunkArrayStartIdx[dimIdx]);
3209 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3210 : }
3211 209 : dimIdx++;
3212 209 : goto lbl_next_depth;
3213 207 : lbl_return_to_caller_end_of_loop:
3214 207 : if (dimIdx == 0)
3215 78 : goto end;
3216 : }
3217 :
3218 240 : assert(dimIdx > 0);
3219 240 : dimIdx--;
3220 : // cppcheck-suppress negativeContainerIndex
3221 240 : switch (stack[dimIdx].return_point)
3222 : {
3223 207 : case Caller::CALLER_END_OF_LOOP:
3224 207 : goto lbl_return_to_caller_end_of_loop;
3225 33 : case Caller::CALLER_IN_LOOP:
3226 33 : goto lbl_return_to_caller_in_loop;
3227 : }
3228 78 : end:
3229 78 : return true;
3230 : }
3231 :
3232 : /************************************************************************/
3233 : /* GDALAttribute() */
3234 : /************************************************************************/
3235 :
3236 : //! @cond Doxygen_Suppress
3237 29182 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3238 0 : CPL_UNUSED const std::string &osName)
3239 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3240 29182 : : GDALAbstractMDArray(osParentName, osName)
3241 : #endif
3242 : {
3243 29182 : }
3244 :
3245 : GDALAttribute::~GDALAttribute() = default;
3246 :
3247 : //! @endcond
3248 :
3249 : /************************************************************************/
3250 : /* GetDimensionSize() */
3251 : /************************************************************************/
3252 :
3253 : /** Return the size of the dimensions of the attribute.
3254 : *
3255 : * This will be an empty array for a scalar (single value) attribute.
3256 : *
3257 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3258 : */
3259 677 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3260 : {
3261 677 : const auto &dims = GetDimensions();
3262 677 : std::vector<GUInt64> ret;
3263 677 : ret.reserve(dims.size());
3264 830 : for (const auto &dim : dims)
3265 153 : ret.push_back(dim->GetSize());
3266 677 : return ret;
3267 : }
3268 :
3269 : /************************************************************************/
3270 : /* GDALRawResult() */
3271 : /************************************************************************/
3272 :
3273 : //! @cond Doxygen_Suppress
3274 236 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3275 236 : size_t nEltCount)
3276 472 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3277 236 : m_raw(raw)
3278 : {
3279 236 : }
3280 :
3281 : //! @endcond
3282 :
3283 : /************************************************************************/
3284 : /* GDALRawResult() */
3285 : /************************************************************************/
3286 :
3287 : /** Move constructor. */
3288 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3289 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3290 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3291 : {
3292 0 : other.m_nEltCount = 0;
3293 0 : other.m_nSize = 0;
3294 0 : other.m_raw = nullptr;
3295 0 : }
3296 :
3297 : /************************************************************************/
3298 : /* FreeMe() */
3299 : /************************************************************************/
3300 :
3301 236 : void GDALRawResult::FreeMe()
3302 : {
3303 236 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3304 : {
3305 122 : GByte *pabyPtr = m_raw;
3306 122 : const auto nDTSize(m_dt.GetSize());
3307 244 : for (size_t i = 0; i < m_nEltCount; ++i)
3308 : {
3309 122 : m_dt.FreeDynamicMemory(pabyPtr);
3310 122 : pabyPtr += nDTSize;
3311 : }
3312 : }
3313 236 : VSIFree(m_raw);
3314 236 : }
3315 :
3316 : /************************************************************************/
3317 : /* operator=() */
3318 : /************************************************************************/
3319 :
3320 : /** Move assignment. */
3321 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3322 : {
3323 0 : FreeMe();
3324 0 : m_dt = std::move(other.m_dt);
3325 0 : m_nEltCount = other.m_nEltCount;
3326 0 : m_nSize = other.m_nSize;
3327 0 : m_raw = other.m_raw;
3328 0 : other.m_nEltCount = 0;
3329 0 : other.m_nSize = 0;
3330 0 : other.m_raw = nullptr;
3331 0 : return *this;
3332 : }
3333 :
3334 : /************************************************************************/
3335 : /* ~GDALRawResult() */
3336 : /************************************************************************/
3337 :
3338 : /** Destructor. */
3339 236 : GDALRawResult::~GDALRawResult()
3340 : {
3341 236 : FreeMe();
3342 236 : }
3343 :
3344 : /************************************************************************/
3345 : /* StealData() */
3346 : /************************************************************************/
3347 :
3348 : //! @cond Doxygen_Suppress
3349 : /** Return buffer to caller which becomes owner of it.
3350 : * Only to be used by GDALAttributeReadAsRaw().
3351 : */
3352 6 : GByte *GDALRawResult::StealData()
3353 : {
3354 6 : GByte *ret = m_raw;
3355 6 : m_raw = nullptr;
3356 6 : m_nEltCount = 0;
3357 6 : m_nSize = 0;
3358 6 : return ret;
3359 : }
3360 :
3361 : //! @endcond
3362 :
3363 : /************************************************************************/
3364 : /* ReadAsRaw() */
3365 : /************************************************************************/
3366 :
3367 : /** Return the raw value of an attribute.
3368 : *
3369 : *
3370 : * This is the same as the C function GDALAttributeReadAsRaw().
3371 : */
3372 236 : GDALRawResult GDALAttribute::ReadAsRaw() const
3373 : {
3374 236 : const auto nEltCount(GetTotalElementsCount());
3375 236 : const auto &dt(GetDataType());
3376 236 : const auto nDTSize(dt.GetSize());
3377 : GByte *res = static_cast<GByte *>(
3378 236 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3379 236 : if (!res)
3380 0 : return GDALRawResult(nullptr, dt, 0);
3381 236 : const auto &dims = GetDimensions();
3382 236 : const auto nDims = GetDimensionCount();
3383 472 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3384 472 : std::vector<size_t> count(1 + nDims);
3385 262 : for (size_t i = 0; i < nDims; i++)
3386 : {
3387 26 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3388 : }
3389 236 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3390 236 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3391 : {
3392 0 : VSIFree(res);
3393 0 : return GDALRawResult(nullptr, dt, 0);
3394 : }
3395 236 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3396 : }
3397 :
3398 : /************************************************************************/
3399 : /* ReadAsString() */
3400 : /************************************************************************/
3401 :
3402 : /** Return the value of an attribute as a string.
3403 : *
3404 : * The returned string should not be freed, and its lifetime does not
3405 : * excess a next call to ReadAsString() on the same object, or the deletion
3406 : * of the object itself.
3407 : *
3408 : * This function will only return the first element if there are several.
3409 : *
3410 : * This is the same as the C function GDALAttributeReadAsString()
3411 : *
3412 : * @return a string, or nullptr.
3413 : */
3414 2120 : const char *GDALAttribute::ReadAsString() const
3415 : {
3416 2120 : const auto nDims = GetDimensionCount();
3417 4240 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3418 4240 : std::vector<size_t> count(1 + nDims, 1);
3419 2120 : char *szRet = nullptr;
3420 2120 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3421 2120 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3422 6359 : sizeof(szRet)) ||
3423 2119 : szRet == nullptr)
3424 : {
3425 5 : return nullptr;
3426 : }
3427 2115 : m_osCachedVal = szRet;
3428 2115 : CPLFree(szRet);
3429 2115 : return m_osCachedVal.c_str();
3430 : }
3431 :
3432 : /************************************************************************/
3433 : /* ReadAsInt() */
3434 : /************************************************************************/
3435 :
3436 : /** Return the value of an attribute as a integer.
3437 : *
3438 : * This function will only return the first element if there are several.
3439 : *
3440 : * It can fail if its value can not be converted to integer.
3441 : *
3442 : * This is the same as the C function GDALAttributeReadAsInt()
3443 : *
3444 : * @return a integer, or INT_MIN in case of error.
3445 : */
3446 542 : int GDALAttribute::ReadAsInt() const
3447 : {
3448 542 : const auto nDims = GetDimensionCount();
3449 1084 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3450 542 : std::vector<size_t> count(1 + nDims, 1);
3451 542 : int nRet = INT_MIN;
3452 542 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3453 1084 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3454 1084 : return nRet;
3455 : }
3456 :
3457 : /************************************************************************/
3458 : /* ReadAsInt64() */
3459 : /************************************************************************/
3460 :
3461 : /** Return the value of an attribute as an int64_t.
3462 : *
3463 : * This function will only return the first element if there are several.
3464 : *
3465 : * It can fail if its value can not be converted to long.
3466 : *
3467 : * This is the same as the C function GDALAttributeReadAsInt64()
3468 : *
3469 : * @return an int64_t, or INT64_MIN in case of error.
3470 : */
3471 102 : int64_t GDALAttribute::ReadAsInt64() const
3472 : {
3473 102 : const auto nDims = GetDimensionCount();
3474 204 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3475 102 : std::vector<size_t> count(1 + nDims, 1);
3476 102 : int64_t nRet = INT64_MIN;
3477 102 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3478 204 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3479 204 : return nRet;
3480 : }
3481 :
3482 : /************************************************************************/
3483 : /* ReadAsDouble() */
3484 : /************************************************************************/
3485 :
3486 : /** Return the value of an attribute as a double.
3487 : *
3488 : * This function will only return the first element if there are several.
3489 : *
3490 : * It can fail if its value can not be converted to double.
3491 : *
3492 : * This is the same as the C function GDALAttributeReadAsInt()
3493 : *
3494 : * @return a double value.
3495 : */
3496 544 : double GDALAttribute::ReadAsDouble() const
3497 : {
3498 544 : const auto nDims = GetDimensionCount();
3499 1088 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3500 544 : std::vector<size_t> count(1 + nDims, 1);
3501 544 : double dfRet = 0;
3502 544 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3503 544 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3504 544 : sizeof(dfRet));
3505 1088 : return dfRet;
3506 : }
3507 :
3508 : /************************************************************************/
3509 : /* ReadAsStringArray() */
3510 : /************************************************************************/
3511 :
3512 : /** Return the value of an attribute as an array of strings.
3513 : *
3514 : * This is the same as the C function GDALAttributeReadAsStringArray()
3515 : */
3516 173 : CPLStringList GDALAttribute::ReadAsStringArray() const
3517 : {
3518 173 : const auto nElts = GetTotalElementsCount();
3519 173 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3520 0 : return CPLStringList();
3521 : char **papszList = static_cast<char **>(
3522 173 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3523 173 : const auto &dims = GetDimensions();
3524 173 : const auto nDims = GetDimensionCount();
3525 346 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3526 346 : std::vector<size_t> count(1 + nDims);
3527 270 : for (size_t i = 0; i < nDims; i++)
3528 : {
3529 97 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3530 : }
3531 173 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3532 173 : GDALExtendedDataType::CreateString(), papszList, papszList,
3533 173 : sizeof(char *) * static_cast<int>(nElts));
3534 672 : for (int i = 0; i < static_cast<int>(nElts); i++)
3535 : {
3536 499 : if (papszList[i] == nullptr)
3537 13 : papszList[i] = CPLStrdup("");
3538 : }
3539 173 : return CPLStringList(papszList);
3540 : }
3541 :
3542 : /************************************************************************/
3543 : /* ReadAsIntArray() */
3544 : /************************************************************************/
3545 :
3546 : /** Return the value of an attribute as an array of integers.
3547 : *
3548 : * This is the same as the C function GDALAttributeReadAsIntArray().
3549 : */
3550 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3551 : {
3552 15 : const auto nElts = GetTotalElementsCount();
3553 : #if SIZEOF_VOIDP == 4
3554 : if (nElts > static_cast<size_t>(nElts))
3555 : return {};
3556 : #endif
3557 15 : std::vector<int> res(static_cast<size_t>(nElts));
3558 15 : const auto &dims = GetDimensions();
3559 15 : const auto nDims = GetDimensionCount();
3560 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3561 30 : std::vector<size_t> count(1 + nDims);
3562 32 : for (size_t i = 0; i < nDims; i++)
3563 : {
3564 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3565 : }
3566 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3567 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3568 15 : res.size() * sizeof(res[0]));
3569 30 : return res;
3570 : }
3571 :
3572 : /************************************************************************/
3573 : /* ReadAsInt64Array() */
3574 : /************************************************************************/
3575 :
3576 : /** Return the value of an attribute as an array of int64_t.
3577 : *
3578 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3579 : */
3580 62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3581 : {
3582 62 : const auto nElts = GetTotalElementsCount();
3583 : #if SIZEOF_VOIDP == 4
3584 : if (nElts > static_cast<size_t>(nElts))
3585 : return {};
3586 : #endif
3587 62 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3588 62 : const auto &dims = GetDimensions();
3589 62 : const auto nDims = GetDimensionCount();
3590 124 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3591 124 : std::vector<size_t> count(1 + nDims);
3592 124 : for (size_t i = 0; i < nDims; i++)
3593 : {
3594 62 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3595 : }
3596 62 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3597 124 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3598 62 : res.size() * sizeof(res[0]));
3599 124 : return res;
3600 : }
3601 :
3602 : /************************************************************************/
3603 : /* ReadAsDoubleArray() */
3604 : /************************************************************************/
3605 :
3606 : /** Return the value of an attribute as an array of double.
3607 : *
3608 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3609 : */
3610 94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3611 : {
3612 94 : const auto nElts = GetTotalElementsCount();
3613 : #if SIZEOF_VOIDP == 4
3614 : if (nElts > static_cast<size_t>(nElts))
3615 : return {};
3616 : #endif
3617 94 : std::vector<double> res(static_cast<size_t>(nElts));
3618 94 : const auto &dims = GetDimensions();
3619 94 : const auto nDims = GetDimensionCount();
3620 188 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3621 188 : std::vector<size_t> count(1 + nDims);
3622 172 : for (size_t i = 0; i < nDims; i++)
3623 : {
3624 78 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3625 : }
3626 94 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3627 188 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3628 94 : res.size() * sizeof(res[0]));
3629 188 : return res;
3630 : }
3631 :
3632 : /************************************************************************/
3633 : /* Write() */
3634 : /************************************************************************/
3635 :
3636 : /** Write an attribute from raw values expressed in GetDataType()
3637 : *
3638 : * The values should be provided in the type of GetDataType() and there should
3639 : * be exactly GetTotalElementsCount() of them.
3640 : * If GetDataType() is a string, each value should be a char* pointer.
3641 : *
3642 : * This is the same as the C function GDALAttributeWriteRaw().
3643 : *
3644 : * @param pabyValue Buffer of nLen bytes.
3645 : * @param nLen Size of pabyValue in bytes. Should be equal to
3646 : * GetTotalElementsCount() * GetDataType().GetSize()
3647 : * @return true in case of success.
3648 : */
3649 173 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3650 : {
3651 173 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3652 : {
3653 0 : CPLError(CE_Failure, CPLE_AppDefined,
3654 : "Length is not of expected value");
3655 0 : return false;
3656 : }
3657 173 : const auto &dims = GetDimensions();
3658 173 : const auto nDims = GetDimensionCount();
3659 346 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3660 346 : std::vector<size_t> count(1 + nDims);
3661 199 : for (size_t i = 0; i < nDims; i++)
3662 : {
3663 26 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3664 : }
3665 173 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3666 173 : pabyValue, pabyValue, nLen);
3667 : }
3668 :
3669 : /************************************************************************/
3670 : /* Write() */
3671 : /************************************************************************/
3672 :
3673 : /** Write an attribute from a string value.
3674 : *
3675 : * Type conversion will be performed if needed. If the attribute contains
3676 : * multiple values, only the first one will be updated.
3677 : *
3678 : * This is the same as the C function GDALAttributeWriteString().
3679 : *
3680 : * @param pszValue Pointer to a string.
3681 : * @return true in case of success.
3682 : */
3683 391 : bool GDALAttribute::Write(const char *pszValue)
3684 : {
3685 391 : const auto nDims = GetDimensionCount();
3686 782 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3687 391 : std::vector<size_t> count(1 + nDims, 1);
3688 391 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3689 782 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3690 782 : sizeof(pszValue));
3691 : }
3692 :
3693 : /************************************************************************/
3694 : /* WriteInt() */
3695 : /************************************************************************/
3696 :
3697 : /** Write an attribute from a integer value.
3698 : *
3699 : * Type conversion will be performed if needed. If the attribute contains
3700 : * multiple values, only the first one will be updated.
3701 : *
3702 : * This is the same as the C function GDALAttributeWriteInt().
3703 : *
3704 : * @param nVal Value.
3705 : * @return true in case of success.
3706 : */
3707 22 : bool GDALAttribute::WriteInt(int nVal)
3708 : {
3709 22 : const auto nDims = GetDimensionCount();
3710 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3711 22 : std::vector<size_t> count(1 + nDims, 1);
3712 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3713 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3714 44 : sizeof(nVal));
3715 : }
3716 :
3717 : /************************************************************************/
3718 : /* WriteInt64() */
3719 : /************************************************************************/
3720 :
3721 : /** Write an attribute from an int64_t value.
3722 : *
3723 : * Type conversion will be performed if needed. If the attribute contains
3724 : * multiple values, only the first one will be updated.
3725 : *
3726 : * This is the same as the C function GDALAttributeWriteInt().
3727 : *
3728 : * @param nVal Value.
3729 : * @return true in case of success.
3730 : */
3731 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3732 : {
3733 11 : const auto nDims = GetDimensionCount();
3734 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3735 11 : std::vector<size_t> count(1 + nDims, 1);
3736 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3737 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3738 22 : sizeof(nVal));
3739 : }
3740 :
3741 : /************************************************************************/
3742 : /* Write() */
3743 : /************************************************************************/
3744 :
3745 : /** Write an attribute from a double value.
3746 : *
3747 : * Type conversion will be performed if needed. If the attribute contains
3748 : * multiple values, only the first one will be updated.
3749 : *
3750 : * This is the same as the C function GDALAttributeWriteDouble().
3751 : *
3752 : * @param dfVal Value.
3753 : * @return true in case of success.
3754 : */
3755 39 : bool GDALAttribute::Write(double dfVal)
3756 : {
3757 39 : const auto nDims = GetDimensionCount();
3758 78 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3759 39 : std::vector<size_t> count(1 + nDims, 1);
3760 39 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3761 78 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3762 78 : sizeof(dfVal));
3763 : }
3764 :
3765 : /************************************************************************/
3766 : /* Write() */
3767 : /************************************************************************/
3768 :
3769 : /** Write an attribute from an array of strings.
3770 : *
3771 : * Type conversion will be performed if needed.
3772 : *
3773 : * Exactly GetTotalElementsCount() strings must be provided
3774 : *
3775 : * This is the same as the C function GDALAttributeWriteStringArray().
3776 : *
3777 : * @param vals Array of strings.
3778 : * @return true in case of success.
3779 : */
3780 8 : bool GDALAttribute::Write(CSLConstList vals)
3781 : {
3782 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3783 : {
3784 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3785 1 : return false;
3786 : }
3787 7 : const auto nDims = GetDimensionCount();
3788 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3789 7 : std::vector<size_t> count(1 + nDims);
3790 7 : const auto &dims = GetDimensions();
3791 15 : for (size_t i = 0; i < nDims; i++)
3792 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3793 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3794 7 : GDALExtendedDataType::CreateString(), vals, vals,
3795 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3796 : }
3797 :
3798 : /************************************************************************/
3799 : /* Write() */
3800 : /************************************************************************/
3801 :
3802 : /** Write an attribute from an array of int.
3803 : *
3804 : * Type conversion will be performed if needed.
3805 : *
3806 : * Exactly GetTotalElementsCount() strings must be provided
3807 : *
3808 : * This is the same as the C function GDALAttributeWriteIntArray()
3809 : *
3810 : * @param vals Array of int.
3811 : * @param nVals Should be equal to GetTotalElementsCount().
3812 : * @return true in case of success.
3813 : */
3814 11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3815 : {
3816 11 : if (nVals != GetTotalElementsCount())
3817 : {
3818 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3819 1 : return false;
3820 : }
3821 10 : const auto nDims = GetDimensionCount();
3822 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3823 10 : std::vector<size_t> count(1 + nDims);
3824 10 : const auto &dims = GetDimensions();
3825 20 : for (size_t i = 0; i < nDims; i++)
3826 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3827 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3828 10 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3829 20 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3830 : }
3831 :
3832 : /************************************************************************/
3833 : /* Write() */
3834 : /************************************************************************/
3835 :
3836 : /** Write an attribute from an array of int64_t.
3837 : *
3838 : * Type conversion will be performed if needed.
3839 : *
3840 : * Exactly GetTotalElementsCount() strings must be provided
3841 : *
3842 : * This is the same as the C function GDALAttributeWriteLongArray()
3843 : *
3844 : * @param vals Array of int64_t.
3845 : * @param nVals Should be equal to GetTotalElementsCount().
3846 : * @return true in case of success.
3847 : */
3848 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3849 : {
3850 10 : if (nVals != GetTotalElementsCount())
3851 : {
3852 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3853 0 : return false;
3854 : }
3855 10 : const auto nDims = GetDimensionCount();
3856 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3857 10 : std::vector<size_t> count(1 + nDims);
3858 10 : const auto &dims = GetDimensions();
3859 20 : for (size_t i = 0; i < nDims; i++)
3860 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3861 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3862 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3863 10 : static_cast<size_t>(GetTotalElementsCount()) *
3864 10 : sizeof(int64_t));
3865 : }
3866 :
3867 : /************************************************************************/
3868 : /* Write() */
3869 : /************************************************************************/
3870 :
3871 : /** Write an attribute from an array of double.
3872 : *
3873 : * Type conversion will be performed if needed.
3874 : *
3875 : * Exactly GetTotalElementsCount() strings must be provided
3876 : *
3877 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3878 : *
3879 : * @param vals Array of double.
3880 : * @param nVals Should be equal to GetTotalElementsCount().
3881 : * @return true in case of success.
3882 : */
3883 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3884 : {
3885 7 : if (nVals != GetTotalElementsCount())
3886 : {
3887 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3888 1 : return false;
3889 : }
3890 6 : const auto nDims = GetDimensionCount();
3891 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3892 6 : std::vector<size_t> count(1 + nDims);
3893 6 : const auto &dims = GetDimensions();
3894 13 : for (size_t i = 0; i < nDims; i++)
3895 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3896 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3897 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3898 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3899 : }
3900 :
3901 : /************************************************************************/
3902 : /* GDALMDArray() */
3903 : /************************************************************************/
3904 :
3905 : //! @cond Doxygen_Suppress
3906 7590 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3907 : CPL_UNUSED const std::string &osName,
3908 0 : const std::string &osContext)
3909 : :
3910 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3911 : GDALAbstractMDArray(osParentName, osName),
3912 : #endif
3913 7590 : m_osContext(osContext)
3914 : {
3915 7590 : }
3916 :
3917 : //! @endcond
3918 :
3919 : /************************************************************************/
3920 : /* GetTotalCopyCost() */
3921 : /************************************************************************/
3922 :
3923 : /** Return a total "cost" to copy the array.
3924 : *
3925 : * Used as a parameter for CopyFrom()
3926 : */
3927 73 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3928 : {
3929 146 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3930 146 : GetTotalElementsCount() * GetDataType().GetSize();
3931 : }
3932 :
3933 : /************************************************************************/
3934 : /* CopyFromAllExceptValues() */
3935 : /************************************************************************/
3936 :
3937 : //! @cond Doxygen_Suppress
3938 :
3939 214 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3940 : bool bStrict, GUInt64 &nCurCost,
3941 : const GUInt64 nTotalCost,
3942 : GDALProgressFunc pfnProgress,
3943 : void *pProgressData)
3944 : {
3945 : // Nodata setting must be one of the first things done for TileDB
3946 214 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3947 214 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3948 : {
3949 13 : SetRawNoDataValue(pNoData);
3950 : }
3951 :
3952 214 : const bool bThisIsUnscaledArray =
3953 214 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3954 428 : auto attrs = poSrcArray->GetAttributes();
3955 297 : for (const auto &attr : attrs)
3956 : {
3957 83 : const auto &osAttrName = attr->GetName();
3958 83 : if (bThisIsUnscaledArray)
3959 : {
3960 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3961 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3962 1 : osAttrName == "valid_range")
3963 : {
3964 1 : continue;
3965 : }
3966 : }
3967 :
3968 82 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3969 164 : attr->GetDataType());
3970 82 : if (!dstAttr)
3971 : {
3972 0 : if (bStrict)
3973 0 : return false;
3974 0 : continue;
3975 : }
3976 82 : auto raw = attr->ReadAsRaw();
3977 82 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3978 0 : return false;
3979 : }
3980 214 : if (!attrs.empty())
3981 : {
3982 47 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3983 81 : if (pfnProgress &&
3984 34 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3985 0 : return false;
3986 : }
3987 :
3988 214 : auto srcSRS = poSrcArray->GetSpatialRef();
3989 214 : if (srcSRS)
3990 : {
3991 22 : SetSpatialRef(srcSRS.get());
3992 : }
3993 :
3994 214 : const std::string &osUnit(poSrcArray->GetUnit());
3995 214 : if (!osUnit.empty())
3996 : {
3997 24 : SetUnit(osUnit);
3998 : }
3999 :
4000 214 : bool bGotValue = false;
4001 214 : GDALDataType eOffsetStorageType = GDT_Unknown;
4002 : const double dfOffset =
4003 214 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
4004 214 : if (bGotValue)
4005 : {
4006 3 : SetOffset(dfOffset, eOffsetStorageType);
4007 : }
4008 :
4009 214 : bGotValue = false;
4010 214 : GDALDataType eScaleStorageType = GDT_Unknown;
4011 214 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
4012 214 : if (bGotValue)
4013 : {
4014 3 : SetScale(dfScale, eScaleStorageType);
4015 : }
4016 :
4017 214 : return true;
4018 : }
4019 :
4020 : //! @endcond
4021 :
4022 : /************************************************************************/
4023 : /* CopyFrom() */
4024 : /************************************************************************/
4025 :
4026 : /** Copy the content of an array into a new (generally empty) array.
4027 : *
4028 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4029 : * of some output drivers this is not recommended)
4030 : * @param poSrcArray Source array. Should NOT be nullptr.
4031 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4032 : * stop the copy. In relaxed mode, the copy will be attempted to
4033 : * be pursued.
4034 : * @param nCurCost Should be provided as a variable initially set to 0.
4035 : * @param nTotalCost Total cost from GetTotalCopyCost().
4036 : * @param pfnProgress Progress callback, or nullptr.
4037 : * @param pProgressData Progress user data, or nulptr.
4038 : *
4039 : * @return true in case of success (or partial success if bStrict == false).
4040 : */
4041 68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4042 : const GDALMDArray *poSrcArray, bool bStrict,
4043 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4044 : GDALProgressFunc pfnProgress, void *pProgressData)
4045 : {
4046 68 : if (pfnProgress == nullptr)
4047 4 : pfnProgress = GDALDummyProgress;
4048 :
4049 68 : nCurCost += GDALMDArray::COPY_COST;
4050 :
4051 68 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4052 : pfnProgress, pProgressData))
4053 : {
4054 0 : return false;
4055 : }
4056 :
4057 68 : const auto &dims = poSrcArray->GetDimensions();
4058 68 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4059 68 : if (dims.empty())
4060 : {
4061 2 : std::vector<GByte> abyTmp(nDTSize);
4062 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4063 2 : GetDataType(), &abyTmp[0]) &&
4064 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4065 4 : &abyTmp[0])) &&
4066 : bStrict)
4067 : {
4068 0 : return false;
4069 : }
4070 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4071 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4072 0 : return false;
4073 : }
4074 : else
4075 : {
4076 66 : std::vector<GUInt64> arrayStartIdx(dims.size());
4077 66 : std::vector<GUInt64> count(dims.size());
4078 172 : for (size_t i = 0; i < dims.size(); i++)
4079 : {
4080 106 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4081 : }
4082 :
4083 : struct CopyFunc
4084 : {
4085 : GDALMDArray *poDstArray = nullptr;
4086 : std::vector<GByte> abyTmp{};
4087 : GDALProgressFunc pfnProgress = nullptr;
4088 : void *pProgressData = nullptr;
4089 : GUInt64 nCurCost = 0;
4090 : GUInt64 nTotalCost = 0;
4091 : GUInt64 nTotalBytesThisArray = 0;
4092 : bool bStop = false;
4093 :
4094 84 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4095 : const GUInt64 *chunkArrayStartIdx,
4096 : const size_t *chunkCount, GUInt64 iCurChunk,
4097 : GUInt64 nChunkCount, void *pUserData)
4098 : {
4099 84 : const auto &dt(l_poSrcArray->GetDataType());
4100 84 : auto data = static_cast<CopyFunc *>(pUserData);
4101 84 : auto poDstArray = data->poDstArray;
4102 84 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4103 84 : nullptr, dt, &data->abyTmp[0]))
4104 : {
4105 1 : return false;
4106 : }
4107 : bool bRet =
4108 83 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4109 83 : nullptr, dt, &data->abyTmp[0]);
4110 83 : if (dt.NeedsFreeDynamicMemory())
4111 : {
4112 5 : const auto l_nDTSize = dt.GetSize();
4113 5 : GByte *ptr = &data->abyTmp[0];
4114 5 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4115 5 : size_t nEltCount = 1;
4116 10 : for (size_t i = 0; i < l_nDims; ++i)
4117 : {
4118 5 : nEltCount *= chunkCount[i];
4119 : }
4120 22 : for (size_t i = 0; i < nEltCount; i++)
4121 : {
4122 17 : dt.FreeDynamicMemory(ptr);
4123 17 : ptr += l_nDTSize;
4124 : }
4125 : }
4126 83 : if (!bRet)
4127 : {
4128 0 : return false;
4129 : }
4130 :
4131 83 : double dfCurCost =
4132 83 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4133 83 : data->nTotalBytesThisArray;
4134 83 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4135 : data->pProgressData))
4136 : {
4137 0 : data->bStop = true;
4138 0 : return false;
4139 : }
4140 :
4141 83 : return true;
4142 : }
4143 : };
4144 :
4145 66 : CopyFunc copyFunc;
4146 66 : copyFunc.poDstArray = this;
4147 66 : copyFunc.nCurCost = nCurCost;
4148 66 : copyFunc.nTotalCost = nTotalCost;
4149 66 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4150 66 : copyFunc.pfnProgress = pfnProgress;
4151 66 : copyFunc.pProgressData = pProgressData;
4152 : const char *pszSwathSize =
4153 66 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4154 : const size_t nMaxChunkSize =
4155 : pszSwathSize
4156 66 : ? static_cast<size_t>(
4157 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4158 1 : CPLAtoGIntBig(pszSwathSize)))
4159 : : static_cast<size_t>(
4160 65 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4161 65 : GDALGetCacheMax64() / 4));
4162 66 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4163 66 : size_t nRealChunkSize = nDTSize;
4164 172 : for (const auto &nChunkSize : anChunkSizes)
4165 : {
4166 106 : nRealChunkSize *= nChunkSize;
4167 : }
4168 : try
4169 : {
4170 66 : copyFunc.abyTmp.resize(nRealChunkSize);
4171 : }
4172 0 : catch (const std::exception &)
4173 : {
4174 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4175 : "Cannot allocate temporary buffer");
4176 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4177 0 : return false;
4178 : }
4179 197 : if (copyFunc.nTotalBytesThisArray != 0 &&
4180 65 : !const_cast<GDALMDArray *>(poSrcArray)
4181 65 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4182 : anChunkSizes.data(), CopyFunc::f,
4183 132 : ©Func) &&
4184 1 : (bStrict || copyFunc.bStop))
4185 : {
4186 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4187 0 : return false;
4188 : }
4189 66 : nCurCost += copyFunc.nTotalBytesThisArray;
4190 : }
4191 :
4192 68 : return true;
4193 : }
4194 :
4195 : /************************************************************************/
4196 : /* GetStructuralInfo() */
4197 : /************************************************************************/
4198 :
4199 : /** Return structural information on the array.
4200 : *
4201 : * This may be the compression, etc..
4202 : *
4203 : * The return value should not be freed and is valid until GDALMDArray is
4204 : * released or this function called again.
4205 : *
4206 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4207 : */
4208 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4209 : {
4210 95 : return nullptr;
4211 : }
4212 :
4213 : /************************************************************************/
4214 : /* AdviseRead() */
4215 : /************************************************************************/
4216 :
4217 : /** Advise driver of upcoming read requests.
4218 : *
4219 : * Some GDAL drivers operate more efficiently if they know in advance what
4220 : * set of upcoming read requests will be made. The AdviseRead() method allows
4221 : * an application to notify the driver of the region of interest.
4222 : *
4223 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4224 : * accelerate access via some drivers. One such case is when reading through
4225 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4226 : * with the region of interest defined by AdviseRead())
4227 : *
4228 : * This is the same as the C function GDALMDArrayAdviseRead().
4229 : *
4230 : * @param arrayStartIdx Values representing the starting index to read
4231 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4232 : * Array of GetDimensionCount() values.
4233 : * Can be nullptr as a synonymous for [0 for i in
4234 : * range(GetDimensionCount() ]
4235 : *
4236 : * @param count Values representing the number of values to extract in
4237 : * each dimension.
4238 : * Array of GetDimensionCount() values.
4239 : * Can be nullptr as a synonymous for
4240 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4241 : * range(GetDimensionCount() ]
4242 : *
4243 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4244 : * documentation.
4245 : *
4246 : * @return true in case of success (ignoring the advice is a success)
4247 : *
4248 : * @since GDAL 3.2
4249 : */
4250 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4251 : CSLConstList papszOptions) const
4252 : {
4253 25 : const auto nDimCount = GetDimensionCount();
4254 25 : if (nDimCount == 0)
4255 2 : return true;
4256 :
4257 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4258 23 : if (arrayStartIdx == nullptr)
4259 : {
4260 0 : tmp_arrayStartIdx.resize(nDimCount);
4261 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4262 : }
4263 :
4264 46 : std::vector<size_t> tmp_count;
4265 23 : if (count == nullptr)
4266 : {
4267 0 : tmp_count.resize(nDimCount);
4268 0 : const auto &dims = GetDimensions();
4269 0 : for (size_t i = 0; i < nDimCount; i++)
4270 : {
4271 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4272 : #if SIZEOF_VOIDP < 8
4273 : if (nSize != static_cast<size_t>(nSize))
4274 : {
4275 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4276 : return false;
4277 : }
4278 : #endif
4279 0 : tmp_count[i] = static_cast<size_t>(nSize);
4280 : }
4281 0 : count = tmp_count.data();
4282 : }
4283 :
4284 46 : std::vector<GInt64> tmp_arrayStep;
4285 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4286 23 : const GInt64 *arrayStep = nullptr;
4287 23 : const GPtrDiff_t *bufferStride = nullptr;
4288 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4289 46 : GDALExtendedDataType::Create(GDT_Unknown),
4290 : nullptr, nullptr, 0, tmp_arrayStep,
4291 : tmp_bufferStride))
4292 : {
4293 1 : return false;
4294 : }
4295 :
4296 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4297 : }
4298 :
4299 : /************************************************************************/
4300 : /* IAdviseRead() */
4301 : /************************************************************************/
4302 :
4303 : //! @cond Doxygen_Suppress
4304 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4305 : CSLConstList /* papszOptions*/) const
4306 : {
4307 3 : return true;
4308 : }
4309 :
4310 : //! @endcond
4311 :
4312 : /************************************************************************/
4313 : /* MassageName() */
4314 : /************************************************************************/
4315 :
4316 : //! @cond Doxygen_Suppress
4317 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4318 : {
4319 32 : std::string ret;
4320 604 : for (const char ch : inputName)
4321 : {
4322 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4323 138 : ret += '_';
4324 : else
4325 434 : ret += ch;
4326 : }
4327 32 : return ret;
4328 : }
4329 :
4330 : //! @endcond
4331 :
4332 : /************************************************************************/
4333 : /* GetCacheRootGroup() */
4334 : /************************************************************************/
4335 :
4336 : //! @cond Doxygen_Suppress
4337 : std::shared_ptr<GDALGroup>
4338 1651 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4339 : std::string &osCacheFilenameOut) const
4340 : {
4341 1651 : const auto &osFilename = GetFilename();
4342 1651 : if (osFilename.empty())
4343 : {
4344 1 : CPLError(CE_Failure, CPLE_AppDefined,
4345 : "Cannot cache an array with an empty filename");
4346 1 : return nullptr;
4347 : }
4348 :
4349 1650 : osCacheFilenameOut = osFilename + ".gmac";
4350 1650 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4351 : {
4352 0 : const auto nPosQuestionMark = osFilename.find('?');
4353 0 : if (nPosQuestionMark != std::string::npos)
4354 : {
4355 : osCacheFilenameOut =
4356 0 : osFilename.substr(0, nPosQuestionMark)
4357 0 : .append(".gmac")
4358 0 : .append(osFilename.substr(nPosQuestionMark));
4359 : }
4360 : }
4361 1650 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4362 1650 : if (pszProxy != nullptr)
4363 7 : osCacheFilenameOut = pszProxy;
4364 :
4365 1650 : std::unique_ptr<GDALDataset> poDS;
4366 : VSIStatBufL sStat;
4367 1650 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4368 : {
4369 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4370 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4371 : nullptr, nullptr, nullptr));
4372 : }
4373 1650 : if (poDS)
4374 : {
4375 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4376 28 : return poDS->GetRootGroup();
4377 : }
4378 :
4379 1622 : if (bCanCreate)
4380 : {
4381 4 : const char *pszDrvName = "netCDF";
4382 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4383 4 : if (poDrv == nullptr)
4384 : {
4385 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4386 : pszDrvName);
4387 0 : return nullptr;
4388 : }
4389 : {
4390 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4391 8 : CPLErrorStateBackuper oErrorStateBackuper;
4392 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4393 : nullptr, nullptr));
4394 : }
4395 4 : if (!poDS)
4396 : {
4397 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4398 1 : if (pszProxy)
4399 : {
4400 1 : osCacheFilenameOut = pszProxy;
4401 1 : poDS.reset(poDrv->CreateMultiDimensional(
4402 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4403 : }
4404 : }
4405 4 : if (poDS)
4406 : {
4407 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4408 4 : return poDS->GetRootGroup();
4409 : }
4410 : else
4411 : {
4412 0 : CPLError(CE_Failure, CPLE_AppDefined,
4413 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4414 : "configuration option to write the cache in "
4415 : "another directory",
4416 : osCacheFilenameOut.c_str());
4417 : }
4418 : }
4419 :
4420 1618 : return nullptr;
4421 : }
4422 :
4423 : //! @endcond
4424 :
4425 : /************************************************************************/
4426 : /* Cache() */
4427 : /************************************************************************/
4428 :
4429 : /** Cache the content of the array into an auxiliary filename.
4430 : *
4431 : * The main purpose of this method is to be able to cache views that are
4432 : * expensive to compute, such as transposed arrays.
4433 : *
4434 : * The array will be stored in a file whose name is the one of
4435 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4436 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4437 : *
4438 : * If the .gmac file cannot be written next to the dataset, the
4439 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4440 : * directory.
4441 : *
4442 : * The GDALMDArray::Read() method will automatically use the cache when it
4443 : * exists. There is no timestamp checks between the source array and the cached
4444 : * array. If the source arrays changes, the cache must be manually deleted.
4445 : *
4446 : * This is the same as the C function GDALMDArrayCache()
4447 : *
4448 : * @note Driver implementation: optionally implemented.
4449 : *
4450 : * @param papszOptions List of options, null terminated, or NULL. Currently
4451 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4452 : * to specify the block size of the cached array.
4453 : * @return true in case of success.
4454 : */
4455 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4456 : {
4457 14 : std::string osCacheFilename;
4458 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4459 7 : if (!poRG)
4460 1 : return false;
4461 :
4462 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4463 6 : if (poRG->OpenMDArray(osCachedArrayName))
4464 : {
4465 2 : CPLError(CE_Failure, CPLE_NotSupported,
4466 : "An array with same name %s already exists in %s",
4467 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4468 2 : return false;
4469 : }
4470 :
4471 8 : CPLStringList aosOptions;
4472 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4473 4 : const auto &aoDims = GetDimensions();
4474 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4475 4 : if (!aoDims.empty())
4476 : {
4477 : std::string osBlockSize(
4478 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4479 4 : if (osBlockSize.empty())
4480 : {
4481 6 : const auto anBlockSize = GetBlockSize();
4482 3 : int idxDim = 0;
4483 10 : for (auto nBlockSize : anBlockSize)
4484 : {
4485 7 : if (idxDim > 0)
4486 4 : osBlockSize += ',';
4487 7 : if (nBlockSize == 0)
4488 7 : nBlockSize = 256;
4489 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4490 : osBlockSize +=
4491 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4492 7 : idxDim++;
4493 : }
4494 : }
4495 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4496 :
4497 4 : int idxDim = 0;
4498 13 : for (const auto &poDim : aoDims)
4499 : {
4500 9 : auto poNewDim = poRG->CreateDimension(
4501 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4502 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4503 9 : if (!poNewDim)
4504 0 : return false;
4505 9 : aoNewDims.emplace_back(poNewDim);
4506 9 : idxDim++;
4507 : }
4508 : }
4509 :
4510 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4511 8 : GetDataType(), aosOptions.List());
4512 4 : if (!poCachedArray)
4513 : {
4514 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4515 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4516 0 : return false;
4517 : }
4518 :
4519 4 : GUInt64 nCost = 0;
4520 8 : return poCachedArray->CopyFrom(nullptr, this,
4521 : false, // strict
4522 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4523 : }
4524 :
4525 : /************************************************************************/
4526 : /* Read() */
4527 : /************************************************************************/
4528 :
4529 4454 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4530 : const GInt64 *arrayStep, // step in elements
4531 : const GPtrDiff_t *bufferStride, // stride in elements
4532 : const GDALExtendedDataType &bufferDataType,
4533 : void *pDstBuffer, const void *pDstBufferAllocStart,
4534 : size_t nDstBufferAllocSize) const
4535 : {
4536 4454 : if (!m_bHasTriedCachedArray)
4537 : {
4538 2108 : m_bHasTriedCachedArray = true;
4539 2108 : if (IsCacheable())
4540 : {
4541 2108 : const auto &osFilename = GetFilename();
4542 3553 : if (!osFilename.empty() &&
4543 3553 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4544 : {
4545 2870 : std::string osCacheFilename;
4546 2870 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4547 1435 : if (poRG)
4548 : {
4549 : const std::string osCachedArrayName(
4550 32 : MassageName(GetFullName()));
4551 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4552 16 : if (m_poCachedArray)
4553 : {
4554 6 : const auto &dims = GetDimensions();
4555 : const auto &cachedDims =
4556 6 : m_poCachedArray->GetDimensions();
4557 6 : const size_t nDims = dims.size();
4558 : bool ok =
4559 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4560 6 : cachedDims.size() == nDims;
4561 19 : for (size_t i = 0; ok && i < nDims; ++i)
4562 : {
4563 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4564 : }
4565 6 : if (ok)
4566 : {
4567 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4568 : osCachedArrayName.c_str(),
4569 : osCacheFilename.c_str());
4570 : }
4571 : else
4572 : {
4573 0 : CPLError(CE_Warning, CPLE_AppDefined,
4574 : "Cached array %s in %s has incompatible "
4575 : "characteristics with current array.",
4576 : osCachedArrayName.c_str(),
4577 : osCacheFilename.c_str());
4578 0 : m_poCachedArray.reset();
4579 : }
4580 : }
4581 : }
4582 : }
4583 : }
4584 : }
4585 :
4586 4454 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4587 4454 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4588 : {
4589 0 : CPLError(CE_Failure, CPLE_AppDefined,
4590 : "Array data type is not convertible to buffer data type");
4591 0 : return false;
4592 : }
4593 :
4594 8908 : std::vector<GInt64> tmp_arrayStep;
4595 8908 : std::vector<GPtrDiff_t> tmp_bufferStride;
4596 4454 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4597 : bufferStride, bufferDataType, pDstBuffer,
4598 : pDstBufferAllocStart, nDstBufferAllocSize,
4599 : tmp_arrayStep, tmp_bufferStride))
4600 : {
4601 9 : return false;
4602 : }
4603 :
4604 4445 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4605 4445 : bufferDataType, pDstBuffer);
4606 : }
4607 :
4608 : /************************************************************************/
4609 : /* GetRootGroup() */
4610 : /************************************************************************/
4611 :
4612 : /** Return the root group to which this arrays belongs too.
4613 : *
4614 : * Note that arrays may be free standing and some drivers may not implement
4615 : * this method, hence nullptr may be returned.
4616 : *
4617 : * It is used internally by the GetResampled() method to detect if GLT
4618 : * orthorectification is available.
4619 : *
4620 : * @return the root group, or nullptr.
4621 : * @since GDAL 3.8
4622 : */
4623 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4624 : {
4625 0 : return nullptr;
4626 : }
4627 :
4628 : //! @cond Doxygen_Suppress
4629 :
4630 : /************************************************************************/
4631 : /* IsTransposedRequest() */
4632 : /************************************************************************/
4633 :
4634 1005 : bool GDALMDArray::IsTransposedRequest(
4635 : const size_t *count,
4636 : const GPtrDiff_t *bufferStride) const // stride in elements
4637 : {
4638 : /*
4639 : For example:
4640 : count = [2,3,4]
4641 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4642 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4643 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4644 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4645 : */
4646 1005 : const size_t nDims(GetDimensionCount());
4647 1005 : size_t nCurStrideForRowMajorStrides = 1;
4648 1005 : bool bRowMajorStrides = true;
4649 1005 : size_t nElts = 1;
4650 1005 : size_t nLastIdx = 0;
4651 2669 : for (size_t i = nDims; i > 0;)
4652 : {
4653 1664 : --i;
4654 1664 : if (bufferStride[i] < 0)
4655 0 : return false;
4656 1664 : if (static_cast<size_t>(bufferStride[i]) !=
4657 : nCurStrideForRowMajorStrides)
4658 : {
4659 278 : bRowMajorStrides = false;
4660 : }
4661 : // Integer overflows have already been checked in CheckReadWriteParams()
4662 1664 : nCurStrideForRowMajorStrides *= count[i];
4663 1664 : nElts *= count[i];
4664 1664 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4665 : }
4666 1005 : if (bRowMajorStrides)
4667 801 : return false;
4668 204 : return nLastIdx == nElts - 1;
4669 : }
4670 :
4671 : /************************************************************************/
4672 : /* CopyToFinalBufferSameDataType() */
4673 : /************************************************************************/
4674 :
4675 : template <size_t N>
4676 72 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4677 : size_t nDims, const size_t *count,
4678 : const GPtrDiff_t *bufferStride)
4679 : {
4680 144 : std::vector<size_t> anStackCount(nDims);
4681 144 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4682 72 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4683 : #if defined(__GNUC__)
4684 : #pragma GCC diagnostic push
4685 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4686 : #endif
4687 72 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4688 : #if defined(__GNUC__)
4689 : #pragma GCC diagnostic pop
4690 : #endif
4691 72 : size_t iDim = 0;
4692 :
4693 761 : lbl_next_depth:
4694 761 : if (iDim == nDims - 1)
4695 : {
4696 673 : size_t n = count[iDim];
4697 673 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4698 673 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4699 29210 : while (n > 0)
4700 : {
4701 28537 : --n;
4702 28537 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4703 28537 : pabyDstBuffer += bufferStrideLastDim;
4704 28537 : pabySrcBuffer += N;
4705 : }
4706 : }
4707 : else
4708 : {
4709 88 : anStackCount[iDim] = count[iDim];
4710 : while (true)
4711 : {
4712 689 : ++iDim;
4713 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4714 689 : goto lbl_next_depth;
4715 689 : lbl_return_to_caller_in_loop:
4716 689 : --iDim;
4717 689 : --anStackCount[iDim];
4718 689 : if (anStackCount[iDim] == 0)
4719 88 : break;
4720 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4721 : }
4722 : }
4723 761 : if (iDim > 0)
4724 689 : goto lbl_return_to_caller_in_loop;
4725 72 : }
4726 :
4727 : /************************************************************************/
4728 : /* CopyToFinalBuffer() */
4729 : /************************************************************************/
4730 :
4731 183 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4732 : const GDALExtendedDataType &eSrcDataType,
4733 : void *pDstBuffer,
4734 : const GDALExtendedDataType &eDstDataType,
4735 : size_t nDims, const size_t *count,
4736 : const GPtrDiff_t *bufferStride)
4737 : {
4738 183 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4739 : // Use specialized implementation for well-known data types when no
4740 : // type conversion is needed
4741 183 : if (eSrcDataType == eDstDataType)
4742 : {
4743 122 : if (nSrcDataTypeSize == 1)
4744 : {
4745 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4746 : count, bufferStride);
4747 72 : return;
4748 : }
4749 81 : else if (nSrcDataTypeSize == 2)
4750 : {
4751 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4752 : count, bufferStride);
4753 1 : return;
4754 : }
4755 80 : else if (nSrcDataTypeSize == 4)
4756 : {
4757 22 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4758 : count, bufferStride);
4759 22 : return;
4760 : }
4761 58 : else if (nSrcDataTypeSize == 8)
4762 : {
4763 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4764 : count, bufferStride);
4765 8 : return;
4766 : }
4767 : }
4768 :
4769 111 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4770 222 : std::vector<size_t> anStackCount(nDims);
4771 222 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4772 111 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4773 111 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4774 111 : size_t iDim = 0;
4775 :
4776 380 : lbl_next_depth:
4777 380 : if (iDim == nDims - 1)
4778 : {
4779 370 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4780 370 : pabyDstBufferStack[iDim], eDstDataType,
4781 370 : bufferStride[iDim], count[iDim]);
4782 370 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4783 : }
4784 : else
4785 : {
4786 10 : anStackCount[iDim] = count[iDim];
4787 : while (true)
4788 : {
4789 269 : ++iDim;
4790 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4791 269 : goto lbl_next_depth;
4792 269 : lbl_return_to_caller_in_loop:
4793 269 : --iDim;
4794 269 : --anStackCount[iDim];
4795 269 : if (anStackCount[iDim] == 0)
4796 10 : break;
4797 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4798 : }
4799 : }
4800 380 : if (iDim > 0)
4801 269 : goto lbl_return_to_caller_in_loop;
4802 : }
4803 :
4804 : /************************************************************************/
4805 : /* TransposeLast2Dims() */
4806 : /************************************************************************/
4807 :
4808 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4809 : const GDALExtendedDataType &eDT,
4810 : const size_t nDims, const size_t *count,
4811 : const size_t nEltsNonLast2Dims)
4812 : {
4813 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4814 19 : const auto nDTSize = eDT.GetSize();
4815 : void *pTempBufferForLast2DimsTranspose =
4816 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4817 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4818 0 : return false;
4819 :
4820 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4821 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4822 : {
4823 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4824 : pTempBufferForLast2DimsTranspose,
4825 39 : eDT.GetNumericDataType(), count[nDims - 1],
4826 39 : count[nDims - 2]);
4827 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4828 : nDTSize * nEltsLast2Dims);
4829 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4830 : }
4831 :
4832 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4833 :
4834 19 : return true;
4835 : }
4836 :
4837 : /************************************************************************/
4838 : /* ReadForTransposedRequest() */
4839 : /************************************************************************/
4840 :
4841 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4842 : // transposed view yield to extremely poor/unusable performance. This fixes
4843 : // this by using temporary memory to read in a contiguous buffer in a
4844 : // row-major order, and then do the transposition to the final buffer.
4845 :
4846 202 : bool GDALMDArray::ReadForTransposedRequest(
4847 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4848 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4849 : void *pDstBuffer) const
4850 : {
4851 202 : const size_t nDims(GetDimensionCount());
4852 202 : if (nDims == 0)
4853 : {
4854 0 : CPLAssert(false);
4855 : return false; // shouldn't happen
4856 : }
4857 202 : size_t nElts = 1;
4858 526 : for (size_t i = 0; i < nDims; ++i)
4859 324 : nElts *= count[i];
4860 :
4861 404 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4862 202 : tmpBufferStrides.back() = 1;
4863 324 : for (size_t i = nDims - 1; i > 0;)
4864 : {
4865 122 : --i;
4866 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4867 : }
4868 :
4869 202 : const auto &eDT = GetDataType();
4870 202 : const auto nDTSize = eDT.GetSize();
4871 343 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4872 359 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4873 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4874 : {
4875 : // Optimization of the optimization if only the last 2 dims are
4876 : // transposed that saves on temporary buffer allocation
4877 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4878 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4879 23 : bool bRowMajorStridesForNonLast2Dims = true;
4880 23 : size_t nEltsNonLast2Dims = 1;
4881 40 : for (size_t i = nDims - 2; i > 0;)
4882 : {
4883 17 : --i;
4884 17 : if (static_cast<size_t>(bufferStride[i]) !=
4885 : nCurStrideForRowMajorStrides)
4886 : {
4887 4 : bRowMajorStridesForNonLast2Dims = false;
4888 : }
4889 : // Integer overflows have already been checked in
4890 : // CheckReadWriteParams()
4891 17 : nCurStrideForRowMajorStrides *= count[i];
4892 17 : nEltsNonLast2Dims *= count[i];
4893 : }
4894 23 : if (bRowMajorStridesForNonLast2Dims)
4895 : {
4896 : // We read in the final buffer!
4897 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4898 19 : eDT, pDstBuffer))
4899 : {
4900 0 : return false;
4901 : }
4902 :
4903 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4904 19 : nEltsNonLast2Dims);
4905 : }
4906 : }
4907 :
4908 183 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4909 183 : if (pTempBuffer == nullptr)
4910 0 : return false;
4911 :
4912 183 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4913 183 : pTempBuffer))
4914 : {
4915 0 : VSIFree(pTempBuffer);
4916 0 : return false;
4917 : }
4918 183 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4919 : count, bufferStride);
4920 :
4921 183 : if (eDT.NeedsFreeDynamicMemory())
4922 : {
4923 100 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4924 200 : for (size_t i = 0; i < nElts; ++i)
4925 : {
4926 100 : eDT.FreeDynamicMemory(pabyPtr);
4927 100 : pabyPtr += nDTSize;
4928 : }
4929 : }
4930 :
4931 183 : VSIFree(pTempBuffer);
4932 183 : return true;
4933 : }
4934 :
4935 : /************************************************************************/
4936 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4937 : /************************************************************************/
4938 :
4939 : // Returns true if at all following conditions are met:
4940 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4941 : // defines a row-major ordered contiguous buffer.
4942 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4943 : const size_t *count, const GInt64 *arrayStep,
4944 : const GPtrDiff_t *bufferStride,
4945 : const GDALExtendedDataType &bufferDataType) const
4946 : {
4947 78 : if (bufferDataType != GetDataType())
4948 5 : return false;
4949 73 : size_t nExpectedStride = 1;
4950 166 : for (size_t i = GetDimensionCount(); i > 0;)
4951 : {
4952 96 : --i;
4953 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4954 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4955 : {
4956 3 : return false;
4957 : }
4958 93 : nExpectedStride *= count[i];
4959 : }
4960 70 : return true;
4961 : }
4962 :
4963 : /************************************************************************/
4964 : /* ReadUsingContiguousIRead() */
4965 : /************************************************************************/
4966 :
4967 : // Used for example by the TileDB driver when requesting it with
4968 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4969 : // not defining a row-major ordered contiguous buffer.
4970 : // Should only be called when at least one of the above conditions are true,
4971 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4972 : // returning none.
4973 : // This method will call IRead() again with arrayStep[] == 1,
4974 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4975 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4976 : // content of that temporary buffer onto pDstBuffer.
4977 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4978 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4979 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4980 : void *pDstBuffer) const
4981 : {
4982 7 : const size_t nDims(GetDimensionCount());
4983 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4984 14 : std::vector<size_t> anTmpCount(nDims);
4985 7 : const auto &oType = GetDataType();
4986 7 : size_t nMemArraySize = oType.GetSize();
4987 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4988 7 : GPtrDiff_t nStride = 1;
4989 18 : for (size_t i = nDims; i > 0;)
4990 : {
4991 11 : --i;
4992 11 : if (arrayStep[i] > 0)
4993 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4994 : else
4995 2 : anTmpStartIdx[i] =
4996 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4997 : const uint64_t nCount =
4998 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4999 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
5000 : {
5001 0 : CPLError(CE_Failure, CPLE_AppDefined,
5002 : "Read() failed due to too large memory requirement");
5003 0 : return false;
5004 : }
5005 11 : anTmpCount[i] = static_cast<size_t>(nCount);
5006 11 : nMemArraySize *= anTmpCount[i];
5007 11 : anTmpStride[i] = nStride;
5008 11 : nStride *= anTmpCount[i];
5009 : }
5010 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
5011 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
5012 7 : if (!pTmpBuffer)
5013 0 : return false;
5014 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
5015 14 : std::vector<GInt64>(nDims, 1).data(), // steps
5016 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
5017 : {
5018 0 : return false;
5019 : }
5020 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5021 18 : for (size_t i = 0; i < nDims; ++i)
5022 : {
5023 11 : if (arrayStep[i] > 0)
5024 9 : anTmpStartIdx[i] = 0;
5025 : else
5026 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5027 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5028 22 : std::string(), std::string(), std::string(), std::string(),
5029 22 : anTmpCount[i]);
5030 : }
5031 : auto poMEMArray =
5032 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5033 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5034 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5035 7 : bufferStride, bufferDataType, pDstBuffer);
5036 : }
5037 :
5038 : //! @endcond
5039 :
5040 : /************************************************************************/
5041 : /* GDALSlicedMDArray */
5042 : /************************************************************************/
5043 :
5044 : class GDALSlicedMDArray final : public GDALPamMDArray
5045 : {
5046 : private:
5047 : std::shared_ptr<GDALMDArray> m_poParent{};
5048 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5049 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5050 : std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5051 : std::vector<Range>
5052 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5053 :
5054 : mutable std::vector<GUInt64> m_parentStart;
5055 : mutable std::vector<size_t> m_parentCount;
5056 : mutable std::vector<GInt64> m_parentStep;
5057 : mutable std::vector<GPtrDiff_t> m_parentStride;
5058 :
5059 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5060 : const GInt64 *arrayStep,
5061 : const GPtrDiff_t *bufferStride) const;
5062 :
5063 : protected:
5064 690 : explicit GDALSlicedMDArray(
5065 : const std::shared_ptr<GDALMDArray> &poParent,
5066 : const std::string &viewExpr,
5067 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5068 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5069 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5070 : std::vector<Range> &&parentRanges)
5071 2070 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5072 2070 : poParent->GetFullName() +
5073 1380 : " (" + viewExpr + ")"),
5074 1380 : GDALPamMDArray(std::string(),
5075 1380 : "Sliced view of " + poParent->GetFullName() + " (" +
5076 1380 : viewExpr + ")",
5077 1380 : GDALPamMultiDim::GetPAM(poParent),
5078 : poParent->GetContext()),
5079 1380 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5080 690 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5081 690 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5082 690 : m_parentRanges(std::move(parentRanges)),
5083 690 : m_parentStart(m_poParent->GetDimensionCount()),
5084 690 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5085 690 : m_parentStep(m_poParent->GetDimensionCount()),
5086 5520 : m_parentStride(m_poParent->GetDimensionCount())
5087 : {
5088 690 : }
5089 :
5090 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5091 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5092 : const GDALExtendedDataType &bufferDataType,
5093 : void *pDstBuffer) const override;
5094 :
5095 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5096 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5097 : const GDALExtendedDataType &bufferDataType,
5098 : const void *pSrcBuffer) override;
5099 :
5100 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5101 : CSLConstList papszOptions) const override;
5102 :
5103 : public:
5104 : static std::shared_ptr<GDALSlicedMDArray>
5105 690 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5106 : const std::string &viewExpr,
5107 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5108 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5109 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5110 : std::vector<Range> &&parentRanges)
5111 : {
5112 690 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5113 690 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5114 :
5115 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5116 690 : poParent, viewExpr, std::move(dims),
5117 690 : std::move(mapDimIdxToParentDimIdx),
5118 690 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5119 690 : newAr->SetSelf(newAr);
5120 690 : return newAr;
5121 : }
5122 :
5123 77 : bool IsWritable() const override
5124 : {
5125 77 : return m_poParent->IsWritable();
5126 : }
5127 :
5128 1123 : const std::string &GetFilename() const override
5129 : {
5130 1123 : return m_poParent->GetFilename();
5131 : }
5132 :
5133 : const std::vector<std::shared_ptr<GDALDimension>> &
5134 4152 : GetDimensions() const override
5135 : {
5136 4152 : return m_dims;
5137 : }
5138 :
5139 1593 : const GDALExtendedDataType &GetDataType() const override
5140 : {
5141 1593 : return m_poParent->GetDataType();
5142 : }
5143 :
5144 2 : const std::string &GetUnit() const override
5145 : {
5146 2 : return m_poParent->GetUnit();
5147 : }
5148 :
5149 : // bool SetUnit(const std::string& osUnit) override { return
5150 : // m_poParent->SetUnit(osUnit); }
5151 :
5152 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5153 : {
5154 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5155 2 : if (!poSrcSRS)
5156 1 : return nullptr;
5157 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5158 2 : std::vector<int> dstMapping;
5159 3 : for (int srcAxis : srcMapping)
5160 : {
5161 2 : bool bFound = false;
5162 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5163 : {
5164 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5165 3 : srcAxis - 1)
5166 : {
5167 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5168 2 : bFound = true;
5169 2 : break;
5170 : }
5171 : }
5172 2 : if (!bFound)
5173 : {
5174 0 : dstMapping.push_back(0);
5175 : }
5176 : }
5177 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5178 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5179 1 : return poClone;
5180 : }
5181 :
5182 61 : const void *GetRawNoDataValue() const override
5183 : {
5184 61 : return m_poParent->GetRawNoDataValue();
5185 : }
5186 :
5187 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5188 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5189 :
5190 2 : double GetOffset(bool *pbHasOffset,
5191 : GDALDataType *peStorageType) const override
5192 : {
5193 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5194 : }
5195 :
5196 2 : double GetScale(bool *pbHasScale,
5197 : GDALDataType *peStorageType) const override
5198 : {
5199 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5200 : }
5201 :
5202 : // bool SetOffset(double dfOffset) override { return
5203 : // m_poParent->SetOffset(dfOffset); }
5204 :
5205 : // bool SetScale(double dfScale) override { return
5206 : // m_poParent->SetScale(dfScale); }
5207 :
5208 263 : std::vector<GUInt64> GetBlockSize() const override
5209 : {
5210 263 : std::vector<GUInt64> ret(GetDimensionCount());
5211 526 : const auto parentBlockSize(m_poParent->GetBlockSize());
5212 749 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5213 : {
5214 486 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5215 486 : if (iOldAxis != static_cast<size_t>(-1))
5216 : {
5217 486 : ret[i] = parentBlockSize[iOldAxis];
5218 : }
5219 : }
5220 526 : return ret;
5221 : }
5222 :
5223 : std::shared_ptr<GDALAttribute>
5224 6 : GetAttribute(const std::string &osName) const override
5225 : {
5226 6 : return m_poParent->GetAttribute(osName);
5227 : }
5228 :
5229 : std::vector<std::shared_ptr<GDALAttribute>>
5230 35 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5231 : {
5232 35 : return m_poParent->GetAttributes(papszOptions);
5233 : }
5234 : };
5235 :
5236 : /************************************************************************/
5237 : /* PrepareParentArrays() */
5238 : /************************************************************************/
5239 :
5240 553 : void GDALSlicedMDArray::PrepareParentArrays(
5241 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5242 : const GPtrDiff_t *bufferStride) const
5243 : {
5244 553 : const size_t nParentDimCount = m_parentRanges.size();
5245 1663 : for (size_t i = 0; i < nParentDimCount; i++)
5246 : {
5247 : // For dimensions in parent that have no existence in sliced array
5248 1110 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5249 : }
5250 :
5251 1431 : for (size_t i = 0; i < m_dims.size(); i++)
5252 : {
5253 878 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5254 878 : if (iParent != static_cast<size_t>(-1))
5255 : {
5256 876 : m_parentStart[iParent] =
5257 876 : m_parentRanges[iParent].m_nIncr >= 0
5258 876 : ? m_parentRanges[iParent].m_nStartIdx +
5259 787 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5260 89 : : m_parentRanges[iParent].m_nStartIdx -
5261 178 : arrayStartIdx[i] *
5262 89 : static_cast<GUInt64>(
5263 89 : -m_parentRanges[iParent].m_nIncr);
5264 876 : m_parentCount[iParent] = count[i];
5265 876 : if (arrayStep)
5266 : {
5267 875 : m_parentStep[iParent] =
5268 875 : count[i] == 1 ? 1 :
5269 : // other checks should have ensured this does
5270 : // not overflow
5271 681 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5272 : }
5273 876 : if (bufferStride)
5274 : {
5275 875 : m_parentStride[iParent] = bufferStride[i];
5276 : }
5277 : }
5278 : }
5279 553 : }
5280 :
5281 : /************************************************************************/
5282 : /* IRead() */
5283 : /************************************************************************/
5284 :
5285 520 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5286 : const GInt64 *arrayStep,
5287 : const GPtrDiff_t *bufferStride,
5288 : const GDALExtendedDataType &bufferDataType,
5289 : void *pDstBuffer) const
5290 : {
5291 520 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5292 1040 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5293 520 : m_parentStep.data(), m_parentStride.data(),
5294 520 : bufferDataType, pDstBuffer);
5295 : }
5296 :
5297 : /************************************************************************/
5298 : /* IWrite() */
5299 : /************************************************************************/
5300 :
5301 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5302 : const size_t *count, const GInt64 *arrayStep,
5303 : const GPtrDiff_t *bufferStride,
5304 : const GDALExtendedDataType &bufferDataType,
5305 : const void *pSrcBuffer)
5306 : {
5307 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5308 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5309 32 : m_parentStep.data(), m_parentStride.data(),
5310 32 : bufferDataType, pSrcBuffer);
5311 : }
5312 :
5313 : /************************************************************************/
5314 : /* IAdviseRead() */
5315 : /************************************************************************/
5316 :
5317 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5318 : const size_t *count,
5319 : CSLConstList papszOptions) const
5320 : {
5321 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5322 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5323 1 : papszOptions);
5324 : }
5325 :
5326 : /************************************************************************/
5327 : /* CreateSlicedArray() */
5328 : /************************************************************************/
5329 :
5330 : static std::shared_ptr<GDALMDArray>
5331 626 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5332 : const std::string &viewExpr, const std::string &activeSlice,
5333 : bool bRenameDimensions,
5334 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5335 : {
5336 626 : const auto &srcDims(self->GetDimensions());
5337 626 : if (srcDims.empty())
5338 : {
5339 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5340 2 : return nullptr;
5341 : }
5342 :
5343 1248 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5344 624 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5345 :
5346 1248 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5347 1248 : std::vector<size_t> mapDimIdxToParentDimIdx;
5348 1248 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5349 624 : newDims.reserve(nTokens);
5350 624 : mapDimIdxToParentDimIdx.reserve(nTokens);
5351 624 : parentRanges.reserve(nTokens);
5352 :
5353 624 : bool bGotEllipsis = false;
5354 624 : size_t nCurSrcDim = 0;
5355 1248 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5356 1842 : for (size_t i = 0; i < nTokens; i++)
5357 : {
5358 1235 : const char *pszIdxSpec = aosTokens[i];
5359 1235 : if (EQUAL(pszIdxSpec, "..."))
5360 : {
5361 46 : if (bGotEllipsis)
5362 : {
5363 2 : CPLError(CE_Failure, CPLE_AppDefined,
5364 : "Only one single ellipsis is supported");
5365 2 : return nullptr;
5366 : }
5367 44 : bGotEllipsis = true;
5368 44 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5369 97 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5370 : {
5371 53 : parentRanges.emplace_back(0, 1);
5372 53 : newDims.push_back(srcDims[nCurSrcDim]);
5373 53 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5374 : }
5375 44 : continue;
5376 : }
5377 1189 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5378 1186 : EQUAL(pszIdxSpec, "np.newaxis"))
5379 : {
5380 3 : newDims.push_back(std::make_shared<GDALDimension>(
5381 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5382 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5383 3 : continue;
5384 : }
5385 1186 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5386 : {
5387 327 : if (nCurSrcDim >= srcDims.size())
5388 : {
5389 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5390 : activeSlice.c_str());
5391 7 : return nullptr;
5392 : }
5393 :
5394 325 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5395 325 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5396 325 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5397 321 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5398 : {
5399 5 : CPLError(CE_Failure, CPLE_AppDefined,
5400 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5401 5 : return nullptr;
5402 : }
5403 320 : if (nVal < 0)
5404 0 : nVal += nDimSize;
5405 320 : parentRanges.emplace_back(nVal, 0);
5406 : }
5407 : else
5408 : {
5409 859 : if (nCurSrcDim >= srcDims.size())
5410 : {
5411 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5412 : activeSlice.c_str());
5413 8 : return nullptr;
5414 : }
5415 :
5416 : CPLStringList aosRangeTokens(
5417 858 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5418 858 : int nRangeTokens = aosRangeTokens.size();
5419 858 : if (nRangeTokens > 3)
5420 : {
5421 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5422 : pszIdxSpec);
5423 1 : return nullptr;
5424 : }
5425 857 : if (nRangeTokens <= 1)
5426 : {
5427 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5428 : pszIdxSpec);
5429 1 : return nullptr;
5430 : }
5431 856 : const char *pszStart = aosRangeTokens[0];
5432 856 : const char *pszEnd = aosRangeTokens[1];
5433 856 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5434 856 : GDALSlicedMDArray::Range range;
5435 856 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5436 856 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5437 1711 : if (range.m_nIncr == 0 ||
5438 855 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5439 : {
5440 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5441 1 : return nullptr;
5442 : }
5443 855 : auto startIdx(CPLAtoGIntBig(pszStart));
5444 855 : if (startIdx < 0)
5445 : {
5446 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5447 0 : startIdx = 0;
5448 : else
5449 0 : startIdx = nDimSize + startIdx;
5450 : }
5451 855 : const bool bPosIncr = range.m_nIncr > 0;
5452 855 : range.m_nStartIdx = startIdx;
5453 1710 : range.m_nStartIdx = EQUAL(pszStart, "")
5454 855 : ? (bPosIncr ? 0 : nDimSize - 1)
5455 : : range.m_nStartIdx;
5456 855 : if (range.m_nStartIdx >= nDimSize - 1)
5457 203 : range.m_nStartIdx = nDimSize - 1;
5458 855 : auto endIdx(CPLAtoGIntBig(pszEnd));
5459 855 : if (endIdx < 0)
5460 : {
5461 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5462 1 : if (nDimSize < positiveEndIdx)
5463 0 : endIdx = 0;
5464 : else
5465 1 : endIdx = nDimSize - positiveEndIdx;
5466 : }
5467 855 : GUInt64 nEndIdx = endIdx;
5468 855 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5469 855 : if (pszStart[0] || pszEnd[0])
5470 : {
5471 636 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5472 633 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5473 : {
5474 4 : CPLError(CE_Failure, CPLE_AppDefined,
5475 : "Output dimension of size 0 is not allowed");
5476 4 : return nullptr;
5477 : }
5478 : }
5479 851 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5480 851 : const auto nAbsIncr = std::abs(range.m_nIncr);
5481 851 : const GUInt64 newSize =
5482 219 : (pszStart[0] == 0 && pszEnd[0] == 0 &&
5483 219 : range.m_nStartIdx == nEndIdx)
5484 1702 : ? 1
5485 : : bPosIncr
5486 895 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5487 45 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5488 851 : const auto &poSrcDim = srcDims[nCurSrcDim];
5489 1401 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5490 550 : newSize == poSrcDim->GetSize())
5491 : {
5492 181 : newDims.push_back(poSrcDim);
5493 : }
5494 : else
5495 : {
5496 1340 : std::string osNewDimName(poSrcDim->GetName());
5497 670 : if (bRenameDimensions)
5498 : {
5499 : osNewDimName =
5500 1244 : "subset_" + poSrcDim->GetName() +
5501 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5502 : "_" CPL_FRMT_GUIB,
5503 622 : static_cast<GUIntBig>(range.m_nStartIdx),
5504 622 : static_cast<GIntBig>(range.m_nIncr),
5505 622 : static_cast<GUIntBig>(newSize));
5506 : }
5507 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5508 1340 : std::string(), osNewDimName, poSrcDim->GetType(),
5509 670 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5510 : : std::string(),
5511 1340 : newSize);
5512 670 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5513 753 : if (poSrcIndexingVar &&
5514 753 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5515 83 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5516 : {
5517 : std::vector<std::shared_ptr<GDALDimension>>
5518 332 : indexingVarNewDims{poNewDim};
5519 166 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5520 : std::vector<std::shared_ptr<GDALMDArray>>
5521 166 : indexingVarNewIndexingVar;
5522 : std::vector<GDALSlicedMDArray::Range>
5523 166 : indexingVarParentRanges{range};
5524 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5525 : poSrcIndexingVar, pszIdxSpec,
5526 83 : std::move(indexingVarNewDims),
5527 83 : std::move(indexingVarMapDimIdxToParentDimIdx),
5528 83 : std::move(indexingVarNewIndexingVar),
5529 249 : std::move(indexingVarParentRanges));
5530 83 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5531 83 : apoNewIndexingVariables.push_back(
5532 83 : std::move(poNewIndexingVar));
5533 : }
5534 670 : newDims.push_back(std::move(poNewDim));
5535 : }
5536 851 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5537 851 : parentRanges.emplace_back(range);
5538 : }
5539 :
5540 1171 : nCurSrcDim++;
5541 : }
5542 680 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5543 : {
5544 73 : parentRanges.emplace_back(0, 1);
5545 73 : newDims.push_back(srcDims[nCurSrcDim]);
5546 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5547 : }
5548 :
5549 607 : GDALMDArray::ViewSpec viewSpec;
5550 607 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5551 607 : viewSpec.m_parentRanges = parentRanges;
5552 607 : viewSpecs.emplace_back(std::move(viewSpec));
5553 :
5554 1214 : return GDALSlicedMDArray::Create(
5555 607 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5556 1214 : std::move(apoNewIndexingVariables), std::move(parentRanges));
5557 : }
5558 :
5559 : /************************************************************************/
5560 : /* GDALExtractFieldMDArray */
5561 : /************************************************************************/
5562 :
5563 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5564 : {
5565 : private:
5566 : std::shared_ptr<GDALMDArray> m_poParent{};
5567 : GDALExtendedDataType m_dt;
5568 : std::string m_srcCompName;
5569 : mutable std::vector<GByte> m_pabyNoData{};
5570 :
5571 : protected:
5572 98 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5573 : const std::string &fieldName,
5574 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5575 392 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5576 196 : " of " +
5577 98 : poParent->GetFullName()),
5578 : GDALPamMDArray(
5579 196 : std::string(),
5580 196 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5581 196 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5582 : m_poParent(poParent), m_dt(srcComp->GetType()),
5583 490 : m_srcCompName(srcComp->GetName())
5584 : {
5585 98 : m_pabyNoData.resize(m_dt.GetSize());
5586 98 : }
5587 :
5588 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5589 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5590 : const GDALExtendedDataType &bufferDataType,
5591 : void *pDstBuffer) const override;
5592 :
5593 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5594 : CSLConstList papszOptions) const override
5595 : {
5596 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5597 : }
5598 :
5599 : public:
5600 : static std::shared_ptr<GDALExtractFieldMDArray>
5601 98 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5602 : const std::string &fieldName,
5603 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5604 : {
5605 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5606 98 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5607 98 : newAr->SetSelf(newAr);
5608 98 : return newAr;
5609 : }
5610 :
5611 196 : ~GDALExtractFieldMDArray() override
5612 98 : {
5613 98 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5614 196 : }
5615 :
5616 51 : bool IsWritable() const override
5617 : {
5618 51 : return m_poParent->IsWritable();
5619 : }
5620 :
5621 309 : const std::string &GetFilename() const override
5622 : {
5623 309 : return m_poParent->GetFilename();
5624 : }
5625 :
5626 : const std::vector<std::shared_ptr<GDALDimension>> &
5627 418 : GetDimensions() const override
5628 : {
5629 418 : return m_poParent->GetDimensions();
5630 : }
5631 :
5632 344 : const GDALExtendedDataType &GetDataType() const override
5633 : {
5634 344 : return m_dt;
5635 : }
5636 :
5637 2 : const std::string &GetUnit() const override
5638 : {
5639 2 : return m_poParent->GetUnit();
5640 : }
5641 :
5642 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5643 : {
5644 2 : return m_poParent->GetSpatialRef();
5645 : }
5646 :
5647 62 : const void *GetRawNoDataValue() const override
5648 : {
5649 62 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5650 62 : if (parentNoData == nullptr)
5651 2 : return nullptr;
5652 :
5653 60 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5654 60 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5655 :
5656 120 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5657 120 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5658 120 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5659 60 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5660 180 : std::move(comps)));
5661 :
5662 60 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5663 60 : &m_pabyNoData[0], tmpDT);
5664 :
5665 60 : return &m_pabyNoData[0];
5666 : }
5667 :
5668 2 : double GetOffset(bool *pbHasOffset,
5669 : GDALDataType *peStorageType) const override
5670 : {
5671 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5672 : }
5673 :
5674 2 : double GetScale(bool *pbHasScale,
5675 : GDALDataType *peStorageType) const override
5676 : {
5677 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5678 : }
5679 :
5680 52 : std::vector<GUInt64> GetBlockSize() const override
5681 : {
5682 52 : return m_poParent->GetBlockSize();
5683 : }
5684 : };
5685 :
5686 : /************************************************************************/
5687 : /* IRead() */
5688 : /************************************************************************/
5689 :
5690 105 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5691 : const size_t *count,
5692 : const GInt64 *arrayStep,
5693 : const GPtrDiff_t *bufferStride,
5694 : const GDALExtendedDataType &bufferDataType,
5695 : void *pDstBuffer) const
5696 : {
5697 210 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5698 210 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5699 210 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5700 : auto tmpDT(GDALExtendedDataType::Create(
5701 210 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5702 :
5703 105 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5704 210 : tmpDT, pDstBuffer);
5705 : }
5706 :
5707 : /************************************************************************/
5708 : /* CreateFieldNameExtractArray() */
5709 : /************************************************************************/
5710 :
5711 : static std::shared_ptr<GDALMDArray>
5712 99 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5713 : const std::string &fieldName)
5714 : {
5715 99 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5716 99 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5717 235 : for (const auto &comp : self->GetDataType().GetComponents())
5718 : {
5719 234 : if (comp->GetName() == fieldName)
5720 : {
5721 98 : srcComp = ∁
5722 98 : break;
5723 : }
5724 : }
5725 99 : if (srcComp == nullptr)
5726 : {
5727 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5728 : fieldName.c_str());
5729 1 : return nullptr;
5730 : }
5731 98 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5732 : }
5733 :
5734 : /************************************************************************/
5735 : /* GetView() */
5736 : /************************************************************************/
5737 :
5738 : // clang-format off
5739 : /** Return a view of the array using slicing or field access.
5740 : *
5741 : * The slice expression uses the same syntax as NumPy basic slicing and
5742 : * indexing. See
5743 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5744 : * Or it can use field access by name. See
5745 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5746 : *
5747 : * Multiple [] bracket elements can be concatenated, with a slice expression
5748 : * or field name inside each.
5749 : *
5750 : * For basic slicing and indexing, inside each [] bracket element, a list of
5751 : * indexes that apply to successive source dimensions, can be specified, using
5752 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5753 : * or newaxis, using a comma separator.
5754 : *
5755 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5756 : * <ul>
5757 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5758 : * at index 1 in the first dimension, and index 2 in the second dimension
5759 : * from the source array. That is 5</li>
5760 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5761 : * implemented internally doing this intermediate slicing approach.</li>
5762 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5763 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5764 : * first dimension. That is [4,5,6,7].</li>
5765 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5766 : * second dimension. That is [2,6].</li>
5767 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5768 : * the second dimension. That is [[2],[6]].</li>
5769 : * <li>GetView("[::,2]"): Same as
5770 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5771 : * ellipsis only expands to one dimension here.</li>
5772 : * <li>GetView("[:,::2]"):
5773 : * returns a 2-dimensional array, with even-indexed elements of the second
5774 : * dimension. That is [[0,2],[4,6]].</li>
5775 : * <li>GetView("[:,1::2]"): returns a
5776 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5777 : * is [[1,3],[5,7]].</li>
5778 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5779 : * array, with elements of the second dimension with index in the range [1,3[.
5780 : * That is [[1,2],[5,6]].</li>
5781 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5782 : * array, with the values in first dimension reversed. That is
5783 : * [[4,5,6,7],[0,1,2,3]].</li>
5784 : * <li>GetView("[newaxis,...]"): returns a
5785 : * 3-dimensional array, with an additional dimension of size 1 put at the
5786 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5787 : * </ul>
5788 : *
5789 : * One difference with NumPy behavior is that ranges that would result in
5790 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5791 : * GDAL multidimensional model).
5792 : *
5793 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5794 : * Multiple field specification is not supported currently.
5795 : *
5796 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5797 : *
5798 : * \note When using the GDAL Python bindings, natural Python syntax can be
5799 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5800 : * ar.GetView("[0,::,1]['foo']")
5801 : * \note When using the C++ API and integer indexing only, you may use the
5802 : * at(idx0, idx1, ...) method.
5803 : *
5804 : * The returned array holds a reference to the original one, and thus is
5805 : * a view of it (not a copy). If the content of the original array changes,
5806 : * the content of the view array too. When using basic slicing and indexing,
5807 : * the view can be written if the underlying array is writable.
5808 : *
5809 : * This is the same as the C function GDALMDArrayGetView()
5810 : *
5811 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5812 : * access.
5813 : * @return a new array, that holds a reference to the original one, and thus is
5814 : * a view of it (not a copy), or nullptr in case of error.
5815 : */
5816 : // clang-format on
5817 :
5818 : std::shared_ptr<GDALMDArray>
5819 659 : GDALMDArray::GetView(const std::string &viewExpr) const
5820 : {
5821 1318 : std::vector<ViewSpec> viewSpecs;
5822 1318 : return GetView(viewExpr, true, viewSpecs);
5823 : }
5824 :
5825 : //! @cond Doxygen_Suppress
5826 : std::shared_ptr<GDALMDArray>
5827 731 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5828 : std::vector<ViewSpec> &viewSpecs) const
5829 : {
5830 1462 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5831 731 : if (!self)
5832 : {
5833 1 : CPLError(CE_Failure, CPLE_AppDefined,
5834 : "Driver implementation issue: m_pSelf not set !");
5835 1 : return nullptr;
5836 : }
5837 730 : std::string curExpr(viewExpr);
5838 : while (true)
5839 : {
5840 733 : if (curExpr.empty() || curExpr[0] != '[')
5841 : {
5842 2 : CPLError(CE_Failure, CPLE_AppDefined,
5843 : "Slice string should start with ['");
5844 730 : return nullptr;
5845 : }
5846 :
5847 731 : std::string fieldName;
5848 : size_t endExpr;
5849 731 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5850 : {
5851 103 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5852 : {
5853 2 : CPLError(CE_Failure, CPLE_AppDefined,
5854 : "Field access not allowed on non-compound data type");
5855 2 : return nullptr;
5856 : }
5857 101 : size_t idx = 2;
5858 985 : for (; idx < curExpr.size(); idx++)
5859 : {
5860 984 : const char ch = curExpr[idx];
5861 984 : if (ch == curExpr[1])
5862 100 : break;
5863 884 : if (ch == '\\' && idx + 1 < curExpr.size())
5864 : {
5865 1 : fieldName += curExpr[idx + 1];
5866 1 : idx++;
5867 : }
5868 : else
5869 : {
5870 883 : fieldName += ch;
5871 : }
5872 : }
5873 101 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5874 : {
5875 2 : CPLError(CE_Failure, CPLE_AppDefined,
5876 : "Invalid field access specification");
5877 2 : return nullptr;
5878 : }
5879 99 : endExpr = idx + 1;
5880 : }
5881 : else
5882 : {
5883 628 : endExpr = curExpr.find(']');
5884 : }
5885 727 : if (endExpr == std::string::npos)
5886 : {
5887 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5888 1 : return nullptr;
5889 : }
5890 726 : if (endExpr == 1)
5891 : {
5892 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5893 1 : return nullptr;
5894 : }
5895 725 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5896 :
5897 725 : if (!fieldName.empty())
5898 : {
5899 198 : ViewSpec viewSpec;
5900 99 : viewSpec.m_osFieldName = fieldName;
5901 99 : viewSpecs.emplace_back(std::move(viewSpec));
5902 : }
5903 :
5904 725 : auto newArray = !fieldName.empty()
5905 : ? CreateFieldNameExtractArray(self, fieldName)
5906 : : CreateSlicedArray(self, viewExpr, activeSlice,
5907 725 : bRenameDimensions, viewSpecs);
5908 :
5909 725 : if (endExpr == curExpr.size() - 1)
5910 : {
5911 722 : return newArray;
5912 : }
5913 3 : self = std::move(newArray);
5914 3 : curExpr = curExpr.substr(endExpr + 1);
5915 3 : }
5916 : }
5917 :
5918 : //! @endcond
5919 :
5920 : std::shared_ptr<GDALMDArray>
5921 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5922 : {
5923 19 : std::string osExpr("[");
5924 19 : bool bFirst = true;
5925 45 : for (const auto &idx : indices)
5926 : {
5927 26 : if (!bFirst)
5928 7 : osExpr += ',';
5929 26 : bFirst = false;
5930 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5931 : }
5932 57 : return GetView(osExpr + ']');
5933 : }
5934 :
5935 : /************************************************************************/
5936 : /* operator[] */
5937 : /************************************************************************/
5938 :
5939 : /** Return a view of the array using field access
5940 : *
5941 : * Equivalent of GetView("['fieldName']")
5942 : *
5943 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5944 : */
5945 : std::shared_ptr<GDALMDArray>
5946 2 : GDALMDArray::operator[](const std::string &fieldName) const
5947 : {
5948 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5949 4 : .replaceAll('\\', "\\\\")
5950 4 : .replaceAll('\'', "\\\'")
5951 6 : .c_str()));
5952 : }
5953 :
5954 : /************************************************************************/
5955 : /* GDALMDArrayTransposed */
5956 : /************************************************************************/
5957 :
5958 : class GDALMDArrayTransposed final : public GDALPamMDArray
5959 : {
5960 : private:
5961 : std::shared_ptr<GDALMDArray> m_poParent{};
5962 : std::vector<int> m_anMapNewAxisToOldAxis{};
5963 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5964 :
5965 : mutable std::vector<GUInt64> m_parentStart;
5966 : mutable std::vector<size_t> m_parentCount;
5967 : mutable std::vector<GInt64> m_parentStep;
5968 : mutable std::vector<GPtrDiff_t> m_parentStride;
5969 :
5970 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5971 : const GInt64 *arrayStep,
5972 : const GPtrDiff_t *bufferStride) const;
5973 :
5974 : static std::string
5975 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5976 : {
5977 84 : std::string ret;
5978 84 : ret += '[';
5979 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5980 : {
5981 228 : if (i > 0)
5982 144 : ret += ',';
5983 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5984 : }
5985 84 : ret += ']';
5986 84 : return ret;
5987 : }
5988 :
5989 : protected:
5990 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5991 : const std::vector<int> &anMapNewAxisToOldAxis,
5992 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5993 84 : : GDALAbstractMDArray(std::string(),
5994 84 : "Transposed view of " + poParent->GetFullName() +
5995 84 : " along " +
5996 42 : MappingToStr(anMapNewAxisToOldAxis)),
5997 84 : GDALPamMDArray(std::string(),
5998 84 : "Transposed view of " + poParent->GetFullName() +
5999 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
6000 84 : GDALPamMultiDim::GetPAM(poParent),
6001 : poParent->GetContext()),
6002 42 : m_poParent(std::move(poParent)),
6003 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
6004 42 : m_dims(std::move(dims)),
6005 42 : m_parentStart(m_poParent->GetDimensionCount()),
6006 42 : m_parentCount(m_poParent->GetDimensionCount()),
6007 42 : m_parentStep(m_poParent->GetDimensionCount()),
6008 336 : m_parentStride(m_poParent->GetDimensionCount())
6009 : {
6010 42 : }
6011 :
6012 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6013 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6014 : const GDALExtendedDataType &bufferDataType,
6015 : void *pDstBuffer) const override;
6016 :
6017 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
6018 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6019 : const GDALExtendedDataType &bufferDataType,
6020 : const void *pSrcBuffer) override;
6021 :
6022 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6023 : CSLConstList papszOptions) const override;
6024 :
6025 : public:
6026 : static std::shared_ptr<GDALMDArrayTransposed>
6027 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6028 : const std::vector<int> &anMapNewAxisToOldAxis)
6029 : {
6030 42 : const auto &parentDims(poParent->GetDimensions());
6031 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
6032 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6033 : {
6034 114 : if (iOldAxis < 0)
6035 : {
6036 1 : dims.push_back(std::make_shared<GDALDimension>(
6037 2 : std::string(), "newaxis", std::string(), std::string(), 1));
6038 : }
6039 : else
6040 : {
6041 113 : dims.emplace_back(parentDims[iOldAxis]);
6042 : }
6043 : }
6044 :
6045 : auto newAr(
6046 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6047 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
6048 42 : newAr->SetSelf(newAr);
6049 84 : return newAr;
6050 : }
6051 :
6052 1 : bool IsWritable() const override
6053 : {
6054 1 : return m_poParent->IsWritable();
6055 : }
6056 :
6057 84 : const std::string &GetFilename() const override
6058 : {
6059 84 : return m_poParent->GetFilename();
6060 : }
6061 :
6062 : const std::vector<std::shared_ptr<GDALDimension>> &
6063 358 : GetDimensions() const override
6064 : {
6065 358 : return m_dims;
6066 : }
6067 :
6068 141 : const GDALExtendedDataType &GetDataType() const override
6069 : {
6070 141 : return m_poParent->GetDataType();
6071 : }
6072 :
6073 4 : const std::string &GetUnit() const override
6074 : {
6075 4 : return m_poParent->GetUnit();
6076 : }
6077 :
6078 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6079 : {
6080 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6081 5 : if (!poSrcSRS)
6082 2 : return nullptr;
6083 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6084 6 : std::vector<int> dstMapping;
6085 9 : for (int srcAxis : srcMapping)
6086 : {
6087 6 : bool bFound = false;
6088 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6089 : {
6090 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6091 : {
6092 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6093 6 : bFound = true;
6094 6 : break;
6095 : }
6096 : }
6097 6 : if (!bFound)
6098 : {
6099 0 : dstMapping.push_back(0);
6100 : }
6101 : }
6102 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6103 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6104 3 : return poClone;
6105 : }
6106 :
6107 4 : const void *GetRawNoDataValue() const override
6108 : {
6109 4 : return m_poParent->GetRawNoDataValue();
6110 : }
6111 :
6112 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6113 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6114 :
6115 4 : double GetOffset(bool *pbHasOffset,
6116 : GDALDataType *peStorageType) const override
6117 : {
6118 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6119 : }
6120 :
6121 4 : double GetScale(bool *pbHasScale,
6122 : GDALDataType *peStorageType) const override
6123 : {
6124 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6125 : }
6126 :
6127 : // bool SetOffset(double dfOffset) override { return
6128 : // m_poParent->SetOffset(dfOffset); }
6129 :
6130 : // bool SetScale(double dfScale) override { return
6131 : // m_poParent->SetScale(dfScale); }
6132 :
6133 3 : std::vector<GUInt64> GetBlockSize() const override
6134 : {
6135 3 : std::vector<GUInt64> ret(GetDimensionCount());
6136 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6137 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6138 : {
6139 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6140 8 : if (iOldAxis >= 0)
6141 : {
6142 7 : ret[i] = parentBlockSize[iOldAxis];
6143 : }
6144 : }
6145 6 : return ret;
6146 : }
6147 :
6148 : std::shared_ptr<GDALAttribute>
6149 1 : GetAttribute(const std::string &osName) const override
6150 : {
6151 1 : return m_poParent->GetAttribute(osName);
6152 : }
6153 :
6154 : std::vector<std::shared_ptr<GDALAttribute>>
6155 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6156 : {
6157 6 : return m_poParent->GetAttributes(papszOptions);
6158 : }
6159 : };
6160 :
6161 : /************************************************************************/
6162 : /* PrepareParentArrays() */
6163 : /************************************************************************/
6164 :
6165 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6166 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6167 : const GPtrDiff_t *bufferStride) const
6168 : {
6169 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6170 : {
6171 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6172 129 : if (iOldAxis >= 0)
6173 : {
6174 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6175 128 : m_parentCount[iOldAxis] = count[i];
6176 128 : if (arrayStep) // only null when called from IAdviseRead()
6177 : {
6178 126 : m_parentStep[iOldAxis] = arrayStep[i];
6179 : }
6180 128 : if (bufferStride) // only null when called from IAdviseRead()
6181 : {
6182 126 : m_parentStride[iOldAxis] = bufferStride[i];
6183 : }
6184 : }
6185 : }
6186 47 : }
6187 :
6188 : /************************************************************************/
6189 : /* IRead() */
6190 : /************************************************************************/
6191 :
6192 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6193 : const size_t *count, const GInt64 *arrayStep,
6194 : const GPtrDiff_t *bufferStride,
6195 : const GDALExtendedDataType &bufferDataType,
6196 : void *pDstBuffer) const
6197 : {
6198 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6199 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6200 44 : m_parentStep.data(), m_parentStride.data(),
6201 44 : bufferDataType, pDstBuffer);
6202 : }
6203 :
6204 : /************************************************************************/
6205 : /* IWrite() */
6206 : /************************************************************************/
6207 :
6208 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6209 : const size_t *count, const GInt64 *arrayStep,
6210 : const GPtrDiff_t *bufferStride,
6211 : const GDALExtendedDataType &bufferDataType,
6212 : const void *pSrcBuffer)
6213 : {
6214 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6215 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6216 2 : m_parentStep.data(), m_parentStride.data(),
6217 2 : bufferDataType, pSrcBuffer);
6218 : }
6219 :
6220 : /************************************************************************/
6221 : /* IAdviseRead() */
6222 : /************************************************************************/
6223 :
6224 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6225 : const size_t *count,
6226 : CSLConstList papszOptions) const
6227 : {
6228 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6229 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6230 1 : papszOptions);
6231 : }
6232 :
6233 : /************************************************************************/
6234 : /* Transpose() */
6235 : /************************************************************************/
6236 :
6237 : /** Return a view of the array whose axis have been reordered.
6238 : *
6239 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6240 : * and GetDimensionCount() - 1, and each only once.
6241 : * -1 can be used as a special index value to ask for the insertion of a new
6242 : * axis of size 1.
6243 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6244 : * index of one of its dimension, it corresponds to the axis of index
6245 : * anMapNewAxisToOldAxis[i] from the current array.
6246 : *
6247 : * This is similar to the numpy.transpose() method
6248 : *
6249 : * The returned array holds a reference to the original one, and thus is
6250 : * a view of it (not a copy). If the content of the original array changes,
6251 : * the content of the view array too. The view can be written if the underlying
6252 : * array is writable.
6253 : *
6254 : * Note that I/O performance in such a transposed view might be poor.
6255 : *
6256 : * This is the same as the C function GDALMDArrayTranspose().
6257 : *
6258 : * @return a new array, that holds a reference to the original one, and thus is
6259 : * a view of it (not a copy), or nullptr in case of error.
6260 : */
6261 : std::shared_ptr<GDALMDArray>
6262 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6263 : {
6264 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6265 50 : if (!self)
6266 : {
6267 0 : CPLError(CE_Failure, CPLE_AppDefined,
6268 : "Driver implementation issue: m_pSelf not set !");
6269 0 : return nullptr;
6270 : }
6271 50 : const int nDims = static_cast<int>(GetDimensionCount());
6272 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6273 50 : int nCountOldAxis = 0;
6274 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6275 : {
6276 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6277 : {
6278 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6279 4 : return nullptr;
6280 : }
6281 130 : if (iOldAxis >= 0)
6282 : {
6283 128 : if (alreadyUsedOldAxis[iOldAxis])
6284 : {
6285 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6286 : iOldAxis);
6287 1 : return nullptr;
6288 : }
6289 127 : alreadyUsedOldAxis[iOldAxis] = true;
6290 127 : nCountOldAxis++;
6291 : }
6292 : }
6293 46 : if (nCountOldAxis != nDims)
6294 : {
6295 4 : CPLError(CE_Failure, CPLE_AppDefined,
6296 : "One or several original axis missing");
6297 4 : return nullptr;
6298 : }
6299 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6300 : }
6301 :
6302 : /************************************************************************/
6303 : /* IRead() */
6304 : /************************************************************************/
6305 :
6306 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6307 : const size_t *count, const GInt64 *arrayStep,
6308 : const GPtrDiff_t *bufferStride,
6309 : const GDALExtendedDataType &bufferDataType,
6310 : void *pDstBuffer) const
6311 : {
6312 16 : const double dfScale = m_dfScale;
6313 16 : const double dfOffset = m_dfOffset;
6314 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6315 : const auto dtDouble =
6316 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6317 16 : const size_t nDTSize = dtDouble.GetSize();
6318 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6319 :
6320 16 : double adfSrcNoData[2] = {0, 0};
6321 16 : if (m_bHasNoData)
6322 : {
6323 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6324 9 : m_poParent->GetDataType(),
6325 : &adfSrcNoData[0], dtDouble);
6326 : }
6327 :
6328 16 : const auto nDims = GetDimensions().size();
6329 16 : if (nDims == 0)
6330 : {
6331 : double adfVal[2];
6332 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6333 : dtDouble, &adfVal[0]))
6334 : {
6335 0 : return false;
6336 : }
6337 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6338 : {
6339 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6340 6 : if (bDTIsComplex)
6341 : {
6342 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6343 : }
6344 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6345 : bufferDataType);
6346 : }
6347 : else
6348 : {
6349 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6350 : pDstBuffer, bufferDataType);
6351 : }
6352 9 : return true;
6353 : }
6354 :
6355 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6356 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6357 7 : void *pTempBuffer = pDstBuffer;
6358 7 : if (bTempBufferNeeded)
6359 : {
6360 2 : size_t nElts = 1;
6361 2 : actualBufferStrideVector.resize(nDims);
6362 7 : for (size_t i = 0; i < nDims; i++)
6363 5 : nElts *= count[i];
6364 2 : actualBufferStrideVector.back() = 1;
6365 5 : for (size_t i = nDims - 1; i > 0;)
6366 : {
6367 3 : --i;
6368 3 : actualBufferStrideVector[i] =
6369 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6370 : }
6371 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6372 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6373 2 : if (!pTempBuffer)
6374 0 : return false;
6375 : }
6376 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6377 : actualBufferStridePtr, dtDouble, pTempBuffer))
6378 : {
6379 0 : if (bTempBufferNeeded)
6380 0 : VSIFree(pTempBuffer);
6381 0 : return false;
6382 : }
6383 :
6384 : struct Stack
6385 : {
6386 : size_t nIters = 0;
6387 : double *src_ptr = nullptr;
6388 : GByte *dst_ptr = nullptr;
6389 : GPtrDiff_t src_inc_offset = 0;
6390 : GPtrDiff_t dst_inc_offset = 0;
6391 : };
6392 :
6393 7 : std::vector<Stack> stack(nDims);
6394 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6395 23 : for (size_t i = 0; i < nDims; i++)
6396 : {
6397 32 : stack[i].src_inc_offset =
6398 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6399 16 : stack[i].dst_inc_offset =
6400 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6401 : }
6402 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6403 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6404 :
6405 7 : size_t dimIdx = 0;
6406 7 : const size_t nDimsMinus1 = nDims - 1;
6407 : GByte abyDstNoData[16];
6408 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6409 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6410 : bufferDataType);
6411 :
6412 37 : lbl_next_depth:
6413 37 : if (dimIdx == nDimsMinus1)
6414 : {
6415 25 : auto nIters = count[dimIdx];
6416 25 : double *padfVal = stack[dimIdx].src_ptr;
6417 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6418 : while (true)
6419 : {
6420 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6421 : {
6422 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6423 88 : if (bDTIsComplex)
6424 : {
6425 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6426 : }
6427 88 : if (bTempBufferNeeded)
6428 : {
6429 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6430 : dst_ptr, bufferDataType);
6431 : }
6432 : }
6433 : else
6434 : {
6435 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6436 : }
6437 :
6438 92 : if ((--nIters) == 0)
6439 25 : break;
6440 67 : padfVal += stack[dimIdx].src_inc_offset;
6441 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6442 : }
6443 : }
6444 : else
6445 : {
6446 12 : stack[dimIdx].nIters = count[dimIdx];
6447 : while (true)
6448 : {
6449 30 : dimIdx++;
6450 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6451 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6452 30 : goto lbl_next_depth;
6453 30 : lbl_return_to_caller:
6454 30 : dimIdx--;
6455 30 : if ((--stack[dimIdx].nIters) == 0)
6456 12 : break;
6457 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6458 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6459 : }
6460 : }
6461 37 : if (dimIdx > 0)
6462 30 : goto lbl_return_to_caller;
6463 :
6464 7 : if (bTempBufferNeeded)
6465 2 : VSIFree(pTempBuffer);
6466 7 : return true;
6467 : }
6468 :
6469 : /************************************************************************/
6470 : /* IWrite() */
6471 : /************************************************************************/
6472 :
6473 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6474 : const size_t *count, const GInt64 *arrayStep,
6475 : const GPtrDiff_t *bufferStride,
6476 : const GDALExtendedDataType &bufferDataType,
6477 : const void *pSrcBuffer)
6478 : {
6479 16 : const double dfScale = m_dfScale;
6480 16 : const double dfOffset = m_dfOffset;
6481 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6482 : const auto dtDouble =
6483 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6484 16 : const size_t nDTSize = dtDouble.GetSize();
6485 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6486 : const bool bSelfAndParentHaveNoData =
6487 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6488 16 : double dfNoData = 0;
6489 16 : if (m_bHasNoData)
6490 : {
6491 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6492 : &dfNoData, GDT_Float64, 0, 1);
6493 : }
6494 :
6495 16 : double adfSrcNoData[2] = {0, 0};
6496 16 : if (bSelfAndParentHaveNoData)
6497 : {
6498 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6499 7 : m_poParent->GetDataType(),
6500 : &adfSrcNoData[0], dtDouble);
6501 : }
6502 :
6503 16 : const auto nDims = GetDimensions().size();
6504 16 : if (nDims == 0)
6505 : {
6506 : double adfVal[2];
6507 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6508 : dtDouble);
6509 16 : if (bSelfAndParentHaveNoData &&
6510 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6511 : {
6512 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6513 2 : bufferStride, m_poParent->GetDataType(),
6514 4 : m_poParent->GetRawNoDataValue());
6515 : }
6516 : else
6517 : {
6518 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6519 8 : if (bDTIsComplex)
6520 : {
6521 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6522 : }
6523 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6524 8 : bufferStride, dtDouble, &adfVal[0]);
6525 : }
6526 : }
6527 :
6528 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6529 6 : size_t nElts = 1;
6530 6 : tmpBufferStrideVector.resize(nDims);
6531 20 : for (size_t i = 0; i < nDims; i++)
6532 14 : nElts *= count[i];
6533 6 : tmpBufferStrideVector.back() = 1;
6534 14 : for (size_t i = nDims - 1; i > 0;)
6535 : {
6536 8 : --i;
6537 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6538 : }
6539 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6540 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6541 6 : if (!pTempBuffer)
6542 0 : return false;
6543 :
6544 : struct Stack
6545 : {
6546 : size_t nIters = 0;
6547 : double *dst_ptr = nullptr;
6548 : const GByte *src_ptr = nullptr;
6549 : GPtrDiff_t src_inc_offset = 0;
6550 : GPtrDiff_t dst_inc_offset = 0;
6551 : };
6552 :
6553 6 : std::vector<Stack> stack(nDims);
6554 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6555 20 : for (size_t i = 0; i < nDims; i++)
6556 : {
6557 28 : stack[i].dst_inc_offset =
6558 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6559 14 : stack[i].src_inc_offset =
6560 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6561 : }
6562 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6563 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6564 :
6565 6 : size_t dimIdx = 0;
6566 6 : const size_t nDimsMinus1 = nDims - 1;
6567 :
6568 34 : lbl_next_depth:
6569 34 : if (dimIdx == nDimsMinus1)
6570 : {
6571 23 : auto nIters = count[dimIdx];
6572 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6573 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6574 : while (true)
6575 : {
6576 : double adfVal[2];
6577 : const double *padfSrcVal;
6578 86 : if (bIsBufferDataTypeNativeDataType)
6579 : {
6580 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6581 : }
6582 : else
6583 : {
6584 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6585 : &adfVal[0], dtDouble);
6586 36 : padfSrcVal = adfVal;
6587 : }
6588 :
6589 148 : if (bSelfAndParentHaveNoData &&
6590 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6591 : {
6592 3 : dst_ptr[0] = adfSrcNoData[0];
6593 3 : if (bDTIsComplex)
6594 : {
6595 1 : dst_ptr[1] = adfSrcNoData[1];
6596 : }
6597 : }
6598 : else
6599 : {
6600 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6601 83 : if (bDTIsComplex)
6602 : {
6603 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6604 : }
6605 : }
6606 :
6607 86 : if ((--nIters) == 0)
6608 23 : break;
6609 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6610 63 : src_ptr += stack[dimIdx].src_inc_offset;
6611 63 : }
6612 : }
6613 : else
6614 : {
6615 11 : stack[dimIdx].nIters = count[dimIdx];
6616 : while (true)
6617 : {
6618 28 : dimIdx++;
6619 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6620 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6621 28 : goto lbl_next_depth;
6622 28 : lbl_return_to_caller:
6623 28 : dimIdx--;
6624 28 : if ((--stack[dimIdx].nIters) == 0)
6625 11 : break;
6626 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6627 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6628 : }
6629 : }
6630 34 : if (dimIdx > 0)
6631 28 : goto lbl_return_to_caller;
6632 :
6633 : // If the parent array is not double/complex-double, then convert the
6634 : // values to it, before calling Write(), as some implementations can be
6635 : // very slow when doing the type conversion.
6636 6 : const auto &eParentDT = m_poParent->GetDataType();
6637 6 : const size_t nParentDTSize = eParentDT.GetSize();
6638 6 : if (nParentDTSize <= nDTSize / 2)
6639 : {
6640 : // Copy in-place by making sure that source and target do not overlap
6641 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6642 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6643 :
6644 : // Copy first element
6645 : {
6646 6 : std::vector<GByte> abyTemp(nParentDTSize);
6647 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6648 6 : static_cast<int>(nDTSize), &abyTemp[0],
6649 : eParentNumericDT, static_cast<int>(nParentDTSize),
6650 : 1);
6651 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6652 : }
6653 : // Remaining elements
6654 86 : for (size_t i = 1; i < nElts; ++i)
6655 : {
6656 80 : GDALCopyWords64(
6657 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6658 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6659 : eParentNumericDT, 0, 1);
6660 : }
6661 : }
6662 :
6663 : const bool ret =
6664 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6665 : eParentDT, pTempBuffer);
6666 :
6667 6 : VSIFree(pTempBuffer);
6668 6 : return ret;
6669 : }
6670 :
6671 : /************************************************************************/
6672 : /* GetUnscaled() */
6673 : /************************************************************************/
6674 :
6675 : /** Return an array that is the unscaled version of the current one.
6676 : *
6677 : * That is each value of the unscaled array will be
6678 : * unscaled_value = raw_value * GetScale() + GetOffset()
6679 : *
6680 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6681 : * from unscaled values to raw values.
6682 : *
6683 : * This is the same as the C function GDALMDArrayGetUnscaled().
6684 : *
6685 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6686 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6687 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6688 : * @return a new array, that holds a reference to the original one, and thus is
6689 : * a view of it (not a copy), or nullptr in case of error.
6690 : */
6691 : std::shared_ptr<GDALMDArray>
6692 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6693 : double dfOverriddenDstNodata) const
6694 : {
6695 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6696 17 : if (!self)
6697 : {
6698 0 : CPLError(CE_Failure, CPLE_AppDefined,
6699 : "Driver implementation issue: m_pSelf not set !");
6700 0 : return nullptr;
6701 : }
6702 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6703 : {
6704 0 : CPLError(CE_Failure, CPLE_AppDefined,
6705 : "GetUnscaled() only supports numeric data type");
6706 0 : return nullptr;
6707 : }
6708 : const double dfScale =
6709 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6710 : const double dfOffset =
6711 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6712 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6713 4 : return self;
6714 :
6715 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6716 13 : ? GDT_CFloat64
6717 13 : : GDT_Float64;
6718 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6719 : {
6720 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6721 0 : eDT = GDT_Float16;
6722 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6723 1 : eDT = GDT_Float32;
6724 : }
6725 :
6726 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6727 13 : dfOverriddenDstNodata, eDT);
6728 : }
6729 :
6730 : /************************************************************************/
6731 : /* GDALMDArrayMask */
6732 : /************************************************************************/
6733 :
6734 : class GDALMDArrayMask final : public GDALPamMDArray
6735 : {
6736 : private:
6737 : std::shared_ptr<GDALMDArray> m_poParent{};
6738 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
6739 : double m_dfMissingValue = 0.0;
6740 : bool m_bHasMissingValue = false;
6741 : double m_dfFillValue = 0.0;
6742 : bool m_bHasFillValue = false;
6743 : double m_dfValidMin = 0.0;
6744 : bool m_bHasValidMin = false;
6745 : double m_dfValidMax = 0.0;
6746 : bool m_bHasValidMax = false;
6747 : std::vector<uint32_t> m_anValidFlagMasks{};
6748 : std::vector<uint32_t> m_anValidFlagValues{};
6749 :
6750 : bool Init(CSLConstList papszOptions);
6751 :
6752 : template <typename Type>
6753 : void
6754 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6755 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6756 : const void *pTempBuffer,
6757 : const GDALExtendedDataType &oTmpBufferDT,
6758 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6759 :
6760 : protected:
6761 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6762 96 : : GDALAbstractMDArray(std::string(),
6763 96 : "Mask of " + poParent->GetFullName()),
6764 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6765 96 : GDALPamMultiDim::GetPAM(poParent),
6766 : poParent->GetContext()),
6767 240 : m_poParent(std::move(poParent))
6768 : {
6769 48 : }
6770 :
6771 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6772 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6773 : const GDALExtendedDataType &bufferDataType,
6774 : void *pDstBuffer) const override;
6775 :
6776 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6777 : CSLConstList papszOptions) const override
6778 : {
6779 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6780 : }
6781 :
6782 : public:
6783 : static std::shared_ptr<GDALMDArrayMask>
6784 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6785 : CSLConstList papszOptions);
6786 :
6787 1 : bool IsWritable() const override
6788 : {
6789 1 : return false;
6790 : }
6791 :
6792 54 : const std::string &GetFilename() const override
6793 : {
6794 54 : return m_poParent->GetFilename();
6795 : }
6796 :
6797 : const std::vector<std::shared_ptr<GDALDimension>> &
6798 382 : GetDimensions() const override
6799 : {
6800 382 : return m_poParent->GetDimensions();
6801 : }
6802 :
6803 138 : const GDALExtendedDataType &GetDataType() const override
6804 : {
6805 138 : return m_dt;
6806 : }
6807 :
6808 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6809 : {
6810 1 : return m_poParent->GetSpatialRef();
6811 : }
6812 :
6813 2 : std::vector<GUInt64> GetBlockSize() const override
6814 : {
6815 2 : return m_poParent->GetBlockSize();
6816 : }
6817 : };
6818 :
6819 : /************************************************************************/
6820 : /* GDALMDArrayMask::Create() */
6821 : /************************************************************************/
6822 :
6823 : /* static */ std::shared_ptr<GDALMDArrayMask>
6824 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6825 : CSLConstList papszOptions)
6826 : {
6827 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6828 48 : newAr->SetSelf(newAr);
6829 48 : if (!newAr->Init(papszOptions))
6830 6 : return nullptr;
6831 42 : return newAr;
6832 : }
6833 :
6834 : /************************************************************************/
6835 : /* GDALMDArrayMask::Init() */
6836 : /************************************************************************/
6837 :
6838 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6839 : {
6840 : const auto GetSingleValNumericAttr =
6841 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6842 : {
6843 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6844 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6845 : {
6846 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6847 21 : if (anDimSizes.empty() ||
6848 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6849 : {
6850 11 : bHasVal = true;
6851 11 : dfVal = poAttr->ReadAsDouble();
6852 : }
6853 : }
6854 192 : };
6855 :
6856 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6857 48 : m_dfMissingValue);
6858 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6859 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6860 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6861 :
6862 : {
6863 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6864 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6865 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6866 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6867 : {
6868 6 : m_bHasValidMin = true;
6869 6 : m_bHasValidMax = true;
6870 6 : auto vals = poValidRange->ReadAsDoubleArray();
6871 6 : CPLAssert(vals.size() == 2);
6872 6 : m_dfValidMin = vals[0];
6873 6 : m_dfValidMax = vals[1];
6874 : }
6875 : }
6876 :
6877 : // Take into account
6878 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6879 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6880 : const char *pszUnmaskFlags =
6881 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6882 48 : if (pszUnmaskFlags)
6883 : {
6884 : const auto IsScalarStringAttr =
6885 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6886 : {
6887 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6888 26 : (poAttr->GetDimensionsSize().empty() ||
6889 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6890 26 : poAttr->GetDimensionsSize()[0] == 1));
6891 : };
6892 :
6893 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6894 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6895 : {
6896 1 : CPLError(CE_Failure, CPLE_AppDefined,
6897 : "UNMASK_FLAGS option specified but array has no "
6898 : "flag_meanings attribute");
6899 1 : return false;
6900 : }
6901 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6902 13 : if (!pszFlagMeanings)
6903 : {
6904 1 : CPLError(CE_Failure, CPLE_AppDefined,
6905 : "Cannot read flag_meanings attribute");
6906 1 : return false;
6907 : }
6908 :
6909 : const auto IsSingleDimNumericAttr =
6910 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6911 : {
6912 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6913 26 : poAttr->GetDimensionsSize().size() == 1;
6914 : };
6915 :
6916 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6917 : const bool bHasFlagValues =
6918 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6919 :
6920 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6921 : const bool bHasFlagMasks =
6922 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6923 :
6924 12 : if (!bHasFlagValues && !bHasFlagMasks)
6925 : {
6926 1 : CPLError(CE_Failure, CPLE_AppDefined,
6927 : "Cannot find flag_values and/or flag_masks attribute");
6928 1 : return false;
6929 : }
6930 :
6931 : const CPLStringList aosUnmaskFlags(
6932 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6933 : const CPLStringList aosFlagMeanings(
6934 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6935 :
6936 11 : if (bHasFlagValues)
6937 : {
6938 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6939 : // We could support Int64 or UInt64, but more work...
6940 7 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6941 7 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6942 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6943 : {
6944 0 : CPLError(CE_Failure, CPLE_NotSupported,
6945 : "Unsupported data type for flag_values attribute: %s",
6946 : GDALGetDataTypeName(eType));
6947 0 : return false;
6948 : }
6949 : }
6950 :
6951 11 : if (bHasFlagMasks)
6952 : {
6953 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6954 : // We could support Int64 or UInt64, but more work...
6955 6 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6956 6 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6957 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6958 : {
6959 0 : CPLError(CE_Failure, CPLE_NotSupported,
6960 : "Unsupported data type for flag_masks attribute: %s",
6961 : GDALGetDataTypeName(eType));
6962 0 : return false;
6963 : }
6964 : }
6965 :
6966 : const std::vector<double> adfValues(
6967 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6968 11 : : std::vector<double>());
6969 : const std::vector<double> adfMasks(
6970 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6971 11 : : std::vector<double>());
6972 :
6973 18 : if (bHasFlagValues &&
6974 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6975 : {
6976 1 : CPLError(CE_Failure, CPLE_AppDefined,
6977 : "Number of values in flag_values attribute is different "
6978 : "from the one in flag_meanings");
6979 1 : return false;
6980 : }
6981 :
6982 16 : if (bHasFlagMasks &&
6983 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6984 : {
6985 1 : CPLError(CE_Failure, CPLE_AppDefined,
6986 : "Number of values in flag_masks attribute is different "
6987 : "from the one in flag_meanings");
6988 1 : return false;
6989 : }
6990 :
6991 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6992 : {
6993 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6994 11 : if (nIdxFlag < 0)
6995 : {
6996 1 : CPLError(
6997 : CE_Failure, CPLE_AppDefined,
6998 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6999 : aosUnmaskFlags[i], pszFlagMeanings);
7000 1 : return false;
7001 : }
7002 :
7003 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
7004 : {
7005 0 : CPLError(CE_Failure, CPLE_AppDefined,
7006 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
7007 0 : adfValues[nIdxFlag]);
7008 0 : return false;
7009 : }
7010 :
7011 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
7012 : {
7013 0 : CPLError(CE_Failure, CPLE_AppDefined,
7014 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
7015 0 : adfMasks[nIdxFlag]);
7016 0 : return false;
7017 : }
7018 :
7019 10 : if (bHasFlagValues)
7020 : {
7021 12 : m_anValidFlagValues.push_back(
7022 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
7023 : }
7024 :
7025 10 : if (bHasFlagMasks)
7026 : {
7027 12 : m_anValidFlagMasks.push_back(
7028 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
7029 : }
7030 : }
7031 : }
7032 :
7033 42 : return true;
7034 : }
7035 :
7036 : /************************************************************************/
7037 : /* IRead() */
7038 : /************************************************************************/
7039 :
7040 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7041 : const GInt64 *arrayStep,
7042 : const GPtrDiff_t *bufferStride,
7043 : const GDALExtendedDataType &bufferDataType,
7044 : void *pDstBuffer) const
7045 : {
7046 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7047 : {
7048 0 : CPLError(CE_Failure, CPLE_AppDefined,
7049 : "%s: only reading to a numeric data type is supported",
7050 : __func__);
7051 0 : return false;
7052 : }
7053 51 : size_t nElts = 1;
7054 51 : const size_t nDims = GetDimensionCount();
7055 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7056 139 : for (size_t i = 0; i < nDims; i++)
7057 88 : nElts *= count[i];
7058 51 : if (nDims > 0)
7059 : {
7060 46 : tmpBufferStrideVector.back() = 1;
7061 88 : for (size_t i = nDims - 1; i > 0;)
7062 : {
7063 42 : --i;
7064 42 : tmpBufferStrideVector[i] =
7065 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7066 : }
7067 : }
7068 :
7069 : /* Optimized case: if we are an integer data type and that there is no */
7070 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7071 : /* directly */
7072 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7073 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7074 34 : m_anValidFlagMasks.empty() &&
7075 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7076 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7077 : {
7078 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7079 7 : if (bBufferDataTypeIsByte) // Byte case
7080 : {
7081 4 : bool bContiguous = true;
7082 10 : for (size_t i = 0; i < nDims; i++)
7083 : {
7084 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7085 : {
7086 1 : bContiguous = false;
7087 1 : break;
7088 : }
7089 : }
7090 4 : if (bContiguous)
7091 : {
7092 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7093 3 : memset(pDstBuffer, 1, nElts);
7094 3 : return true;
7095 : }
7096 : }
7097 :
7098 : struct Stack
7099 : {
7100 : size_t nIters = 0;
7101 : GByte *dst_ptr = nullptr;
7102 : GPtrDiff_t dst_inc_offset = 0;
7103 : };
7104 :
7105 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7106 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7107 13 : for (size_t i = 0; i < nDims; i++)
7108 : {
7109 9 : stack[i].dst_inc_offset =
7110 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7111 : }
7112 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7113 :
7114 4 : size_t dimIdx = 0;
7115 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7116 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7117 4 : CPLAssert(nBufferDTSize <= 16);
7118 4 : const GByte flag = 1;
7119 4 : GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
7120 : bufferDataType.GetNumericDataType(), 0, 1);
7121 :
7122 28 : lbl_next_depth:
7123 28 : if (dimIdx == nDimsMinus1)
7124 : {
7125 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7126 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7127 :
7128 : while (true)
7129 : {
7130 : // cppcheck-suppress knownConditionTrueFalse
7131 73 : if (bBufferDataTypeIsByte)
7132 : {
7133 24 : *dst_ptr = flag;
7134 : }
7135 : else
7136 : {
7137 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7138 : }
7139 :
7140 73 : if ((--nIters) == 0)
7141 19 : break;
7142 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7143 : }
7144 : }
7145 : else
7146 : {
7147 9 : stack[dimIdx].nIters = count[dimIdx];
7148 : while (true)
7149 : {
7150 24 : dimIdx++;
7151 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7152 24 : goto lbl_next_depth;
7153 24 : lbl_return_to_caller:
7154 24 : dimIdx--;
7155 24 : if ((--stack[dimIdx].nIters) == 0)
7156 9 : break;
7157 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7158 : }
7159 : }
7160 28 : if (dimIdx > 0)
7161 24 : goto lbl_return_to_caller;
7162 :
7163 4 : return true;
7164 : }
7165 :
7166 : const auto oTmpBufferDT =
7167 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7168 : ? GDALExtendedDataType::Create(GDT_Float64)
7169 88 : : m_poParent->GetDataType();
7170 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7171 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7172 44 : if (!pTempBuffer)
7173 0 : return false;
7174 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7175 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7176 : pTempBuffer))
7177 : {
7178 0 : VSIFree(pTempBuffer);
7179 0 : return false;
7180 : }
7181 :
7182 44 : switch (oTmpBufferDT.GetNumericDataType())
7183 : {
7184 7 : case GDT_UInt8:
7185 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7186 : pTempBuffer, oTmpBufferDT,
7187 : tmpBufferStrideVector);
7188 7 : break;
7189 :
7190 0 : case GDT_Int8:
7191 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7192 : pTempBuffer, oTmpBufferDT,
7193 : tmpBufferStrideVector);
7194 0 : break;
7195 :
7196 1 : case GDT_UInt16:
7197 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7198 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7199 : tmpBufferStrideVector);
7200 1 : break;
7201 :
7202 14 : case GDT_Int16:
7203 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7204 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7205 : tmpBufferStrideVector);
7206 14 : break;
7207 :
7208 1 : case GDT_UInt32:
7209 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7210 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7211 : tmpBufferStrideVector);
7212 1 : break;
7213 :
7214 5 : case GDT_Int32:
7215 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7216 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7217 : tmpBufferStrideVector);
7218 5 : break;
7219 :
7220 0 : case GDT_UInt64:
7221 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7222 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7223 : tmpBufferStrideVector);
7224 0 : break;
7225 :
7226 0 : case GDT_Int64:
7227 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7228 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7229 : tmpBufferStrideVector);
7230 0 : break;
7231 :
7232 0 : case GDT_Float16:
7233 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7234 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7235 : tmpBufferStrideVector);
7236 0 : break;
7237 :
7238 7 : case GDT_Float32:
7239 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7240 : pTempBuffer, oTmpBufferDT,
7241 : tmpBufferStrideVector);
7242 7 : break;
7243 :
7244 9 : case GDT_Float64:
7245 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7246 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7247 : tmpBufferStrideVector);
7248 9 : break;
7249 0 : case GDT_Unknown:
7250 : case GDT_CInt16:
7251 : case GDT_CInt32:
7252 : case GDT_CFloat16:
7253 : case GDT_CFloat32:
7254 : case GDT_CFloat64:
7255 : case GDT_TypeCount:
7256 0 : CPLAssert(false);
7257 : break;
7258 : }
7259 :
7260 44 : VSIFree(pTempBuffer);
7261 :
7262 44 : return true;
7263 : }
7264 :
7265 : /************************************************************************/
7266 : /* IsValidForDT() */
7267 : /************************************************************************/
7268 :
7269 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7270 : {
7271 40 : if (std::isnan(dfVal))
7272 0 : return false;
7273 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7274 0 : return false;
7275 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7276 0 : return false;
7277 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7278 : }
7279 :
7280 9 : template <> bool IsValidForDT<double>(double)
7281 : {
7282 9 : return true;
7283 : }
7284 :
7285 : /************************************************************************/
7286 : /* IsNan() */
7287 : /************************************************************************/
7288 :
7289 1438 : template <typename Type> inline bool IsNan(Type)
7290 : {
7291 1438 : return false;
7292 : }
7293 :
7294 65 : template <> bool IsNan<double>(double val)
7295 : {
7296 65 : return std::isnan(val);
7297 : }
7298 :
7299 26 : template <> bool IsNan<float>(float val)
7300 : {
7301 26 : return std::isnan(val);
7302 : }
7303 :
7304 : /************************************************************************/
7305 : /* ReadInternal() */
7306 : /************************************************************************/
7307 :
7308 : template <typename Type>
7309 44 : void GDALMDArrayMask::ReadInternal(
7310 : const size_t *count, const GPtrDiff_t *bufferStride,
7311 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7312 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7313 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7314 : {
7315 44 : const size_t nDims = GetDimensionCount();
7316 :
7317 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7318 : {
7319 220 : if (bHasVal)
7320 : {
7321 49 : if (IsValidForDT<Type>(dfVal))
7322 : {
7323 49 : return static_cast<Type>(dfVal);
7324 : }
7325 : else
7326 : {
7327 0 : bHasVal = false;
7328 : }
7329 : }
7330 171 : return 0;
7331 : };
7332 :
7333 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7334 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7335 : const Type nNoDataValue =
7336 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7337 44 : bool bHasMissingValue = m_bHasMissingValue;
7338 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7339 44 : bool bHasFillValue = m_bHasFillValue;
7340 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7341 44 : bool bHasValidMin = m_bHasValidMin;
7342 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7343 44 : bool bHasValidMax = m_bHasValidMax;
7344 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7345 44 : const bool bHasValidFlags =
7346 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7347 :
7348 351 : const auto IsValidFlag = [this](Type v)
7349 : {
7350 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7351 : {
7352 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7353 : {
7354 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7355 : m_anValidFlagValues[i])
7356 : {
7357 4 : return true;
7358 : }
7359 : }
7360 : }
7361 42 : else if (!m_anValidFlagValues.empty())
7362 : {
7363 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7364 : {
7365 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7366 : {
7367 4 : return true;
7368 : }
7369 : }
7370 : }
7371 : else /* if( !m_anValidFlagMasks.empty() ) */
7372 : {
7373 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7374 : {
7375 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7376 : {
7377 9 : return true;
7378 : }
7379 : }
7380 : }
7381 37 : return false;
7382 : };
7383 :
7384 : #define GET_MASK_FOR_SAMPLE(v) \
7385 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7386 : !(bHasMissingValue && v == nMissingValue) && \
7387 : !(bHasFillValue && v == nFillValue) && \
7388 : !(bHasValidMin && v < nValidMin) && \
7389 : !(bHasValidMax && v > nValidMax) && \
7390 : (!bHasValidFlags || IsValidFlag(v)));
7391 :
7392 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7393 : /* Optimized case: Byte output and output buffer is contiguous */
7394 44 : if (bBufferDataTypeIsByte)
7395 : {
7396 40 : bool bContiguous = true;
7397 103 : for (size_t i = 0; i < nDims; i++)
7398 : {
7399 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7400 : {
7401 1 : bContiguous = false;
7402 1 : break;
7403 : }
7404 : }
7405 40 : if (bContiguous)
7406 : {
7407 39 : size_t nElts = 1;
7408 102 : for (size_t i = 0; i < nDims; i++)
7409 63 : nElts *= count[i];
7410 :
7411 1113 : for (size_t i = 0; i < nElts; i++)
7412 : {
7413 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7414 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7415 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7416 : }
7417 39 : return;
7418 : }
7419 : }
7420 :
7421 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7422 :
7423 : struct Stack
7424 : {
7425 : size_t nIters = 0;
7426 : const GByte *src_ptr = nullptr;
7427 : GByte *dst_ptr = nullptr;
7428 : GPtrDiff_t src_inc_offset = 0;
7429 : GPtrDiff_t dst_inc_offset = 0;
7430 : };
7431 :
7432 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7433 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7434 15 : for (size_t i = 0; i < nDims; i++)
7435 : {
7436 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7437 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7438 10 : stack[i].dst_inc_offset =
7439 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7440 : }
7441 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7442 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7443 :
7444 5 : size_t dimIdx = 0;
7445 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7446 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7447 5 : CPLAssert(nBufferDTSize <= 16);
7448 15 : for (GByte flag = 0; flag <= 1; flag++)
7449 : {
7450 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7451 : bufferDataType.GetNumericDataType(), 0, 1);
7452 : }
7453 :
7454 43 : lbl_next_depth:
7455 43 : if (dimIdx == nDimsMinus1)
7456 : {
7457 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7458 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7459 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7460 :
7461 420 : while (true)
7462 : {
7463 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7464 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7465 :
7466 455 : if (bBufferDataTypeIsByte)
7467 : {
7468 24 : *dst_ptr = flag;
7469 : }
7470 : else
7471 : {
7472 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7473 : }
7474 :
7475 455 : if ((--nIters) == 0)
7476 35 : break;
7477 420 : src_ptr += stack[dimIdx].src_inc_offset;
7478 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7479 : }
7480 : }
7481 : else
7482 : {
7483 8 : stack[dimIdx].nIters = count[dimIdx];
7484 : while (true)
7485 : {
7486 38 : dimIdx++;
7487 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7488 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7489 38 : goto lbl_next_depth;
7490 38 : lbl_return_to_caller:
7491 38 : dimIdx--;
7492 38 : if ((--stack[dimIdx].nIters) == 0)
7493 8 : break;
7494 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7495 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7496 : }
7497 : }
7498 43 : if (dimIdx > 0)
7499 38 : goto lbl_return_to_caller;
7500 : }
7501 :
7502 : /************************************************************************/
7503 : /* GetMask() */
7504 : /************************************************************************/
7505 :
7506 : /** Return an array that is a mask for the current array
7507 :
7508 : This array will be of type Byte, with values set to 0 to indicate invalid
7509 : pixels of the current array, and values set to 1 to indicate valid pixels.
7510 :
7511 : The generic implementation honours the NoDataValue, as well as various
7512 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7513 : and valid_range.
7514 :
7515 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7516 : can be used to specify strings of the "flag_meanings" attribute
7517 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7518 : for which pixels matching any of those flags will be set at 1 in the mask array,
7519 : and pixels matching none of those flags will be set at 0.
7520 : For example, let's consider the following netCDF variable defined with:
7521 : \verbatim
7522 : l2p_flags:valid_min = 0s ;
7523 : l2p_flags:valid_max = 256s ;
7524 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7525 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7526 : \endverbatim
7527 :
7528 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7529 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7530 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7531 : will be 1.
7532 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7533 : will be 0.
7534 :
7535 : This is the same as the C function GDALMDArrayGetMask().
7536 :
7537 : @param papszOptions NULL-terminated list of options, or NULL.
7538 :
7539 : @return a new array, that holds a reference to the original one, and thus is
7540 : a view of it (not a copy), or nullptr in case of error.
7541 : */
7542 : std::shared_ptr<GDALMDArray>
7543 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7544 : {
7545 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7546 49 : if (!self)
7547 : {
7548 0 : CPLError(CE_Failure, CPLE_AppDefined,
7549 : "Driver implementation issue: m_pSelf not set !");
7550 0 : return nullptr;
7551 : }
7552 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7553 : {
7554 1 : CPLError(CE_Failure, CPLE_AppDefined,
7555 : "GetMask() only supports numeric data type");
7556 1 : return nullptr;
7557 : }
7558 48 : return GDALMDArrayMask::Create(self, papszOptions);
7559 : }
7560 :
7561 : /************************************************************************/
7562 : /* IsRegularlySpaced() */
7563 : /************************************************************************/
7564 :
7565 : /** Returns whether an array is a 1D regularly spaced array.
7566 : *
7567 : * @param[out] dfStart First value in the array
7568 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7569 : * @return true if the array is regularly spaced.
7570 : */
7571 335 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7572 : {
7573 335 : dfStart = 0;
7574 335 : dfIncrement = 0;
7575 335 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7576 0 : return false;
7577 335 : const auto nSize = GetDimensions()[0]->GetSize();
7578 335 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7579 2 : return false;
7580 :
7581 333 : size_t nCount = static_cast<size_t>(nSize);
7582 666 : std::vector<double> adfTmp;
7583 : try
7584 : {
7585 333 : adfTmp.resize(nCount);
7586 : }
7587 0 : catch (const std::exception &)
7588 : {
7589 0 : return false;
7590 : }
7591 :
7592 333 : GUInt64 anStart[1] = {0};
7593 333 : size_t anCount[1] = {nCount};
7594 :
7595 : const auto IsRegularlySpacedInternal =
7596 90468 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7597 : {
7598 443 : dfStart = adfTmp[0];
7599 443 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7600 443 : if (dfIncrement == 0)
7601 : {
7602 3 : return false;
7603 : }
7604 22495 : for (size_t i = 1; i < anCount[0]; i++)
7605 : {
7606 22067 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7607 22067 : 1e-3 * fabs(dfIncrement))
7608 : {
7609 12 : return false;
7610 : }
7611 : }
7612 428 : return true;
7613 333 : };
7614 :
7615 : // First try with the first block(s). This can avoid excessive processing
7616 : // time, for example with Zarr datasets.
7617 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7618 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7619 333 : const auto nBlockSize = GetBlockSize()[0];
7620 333 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7621 : {
7622 : size_t nReducedCount =
7623 113 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7624 546 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7625 433 : nReducedCount *= 2;
7626 113 : anCount[0] = nReducedCount;
7627 113 : if (!Read(anStart, anCount, nullptr, nullptr,
7628 226 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7629 : {
7630 0 : return false;
7631 : }
7632 113 : if (!IsRegularlySpacedInternal())
7633 : {
7634 3 : return false;
7635 : }
7636 :
7637 : // Get next values
7638 110 : anStart[0] = nReducedCount;
7639 110 : anCount[0] = nCount - nReducedCount;
7640 : }
7641 :
7642 330 : if (!Read(anStart, anCount, nullptr, nullptr,
7643 660 : GDALExtendedDataType::Create(GDT_Float64),
7644 330 : &adfTmp[static_cast<size_t>(anStart[0])]))
7645 : {
7646 0 : return false;
7647 : }
7648 :
7649 330 : return IsRegularlySpacedInternal();
7650 : }
7651 :
7652 : /************************************************************************/
7653 : /* GuessGeoTransform() */
7654 : /************************************************************************/
7655 :
7656 : /** Returns whether 2 specified dimensions form a geotransform
7657 : *
7658 : * @param nDimX Index of the X axis.
7659 : * @param nDimY Index of the Y axis.
7660 : * @param bPixelIsPoint Whether the geotransform should be returned
7661 : * with the pixel-is-point (pixel-center) convention
7662 : * (bPixelIsPoint = true), or with the pixel-is-area
7663 : * (top left corner convention)
7664 : * (bPixelIsPoint = false)
7665 : * @param[out] gt Computed geotransform
7666 : * @return true if a geotransform could be computed.
7667 : */
7668 266 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7669 : bool bPixelIsPoint,
7670 : GDALGeoTransform >) const
7671 : {
7672 266 : const auto &dims(GetDimensions());
7673 532 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7674 532 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7675 266 : double dfXStart = 0.0;
7676 266 : double dfXSpacing = 0.0;
7677 266 : double dfYStart = 0.0;
7678 266 : double dfYSpacing = 0.0;
7679 590 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7680 324 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7681 464 : poVarY && poVarY->GetDimensionCount() == 1 &&
7682 151 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7683 574 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7684 146 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7685 : {
7686 146 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7687 146 : gt[1] = dfXSpacing;
7688 146 : gt[2] = 0;
7689 146 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7690 146 : gt[4] = 0;
7691 146 : gt[5] = dfYSpacing;
7692 146 : return true;
7693 : }
7694 120 : return false;
7695 : }
7696 :
7697 : /** Returns whether 2 specified dimensions form a geotransform
7698 : *
7699 : * @param nDimX Index of the X axis.
7700 : * @param nDimY Index of the Y axis.
7701 : * @param bPixelIsPoint Whether the geotransform should be returned
7702 : * with the pixel-is-point (pixel-center) convention
7703 : * (bPixelIsPoint = true), or with the pixel-is-area
7704 : * (top left corner convention)
7705 : * (bPixelIsPoint = false)
7706 : * @param[out] adfGeoTransform Computed geotransform
7707 : * @return true if a geotransform could be computed.
7708 : */
7709 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7710 : bool bPixelIsPoint,
7711 : double adfGeoTransform[6]) const
7712 : {
7713 0 : GDALGeoTransform *gt =
7714 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7715 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7716 : }
7717 :
7718 : /************************************************************************/
7719 : /* GDALMDArrayResampled */
7720 : /************************************************************************/
7721 :
7722 : class GDALMDArrayResampledDataset;
7723 :
7724 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7725 : {
7726 : protected:
7727 : CPLErr IReadBlock(int, int, void *) override;
7728 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7729 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7730 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7731 : GSpacing nLineSpaceBuf,
7732 : GDALRasterIOExtraArg *psExtraArg) override;
7733 :
7734 : public:
7735 : explicit GDALMDArrayResampledDatasetRasterBand(
7736 : GDALMDArrayResampledDataset *poDSIn);
7737 :
7738 : double GetNoDataValue(int *pbHasNoData) override;
7739 : };
7740 :
7741 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7742 : {
7743 : friend class GDALMDArrayResampled;
7744 : friend class GDALMDArrayResampledDatasetRasterBand;
7745 :
7746 : std::shared_ptr<GDALMDArray> m_poArray;
7747 : const size_t m_iXDim;
7748 : const size_t m_iYDim;
7749 : GDALGeoTransform m_gt{};
7750 : bool m_bHasGT = false;
7751 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7752 :
7753 : std::vector<GUInt64> m_anOffset{};
7754 : std::vector<size_t> m_anCount{};
7755 : std::vector<GPtrDiff_t> m_anStride{};
7756 :
7757 : std::string m_osFilenameLong{};
7758 : std::string m_osFilenameLat{};
7759 :
7760 : public:
7761 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7762 : size_t iXDim, size_t iYDim)
7763 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7764 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7765 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7766 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7767 : {
7768 24 : const auto &dims(m_poArray->GetDimensions());
7769 :
7770 24 : nRasterYSize = static_cast<int>(
7771 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7772 24 : nRasterXSize = static_cast<int>(
7773 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7774 :
7775 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7776 :
7777 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7778 24 : }
7779 :
7780 : ~GDALMDArrayResampledDataset() override;
7781 :
7782 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7783 : {
7784 43 : gt = m_gt;
7785 43 : return m_bHasGT ? CE_None : CE_Failure;
7786 : }
7787 :
7788 105 : const OGRSpatialReference *GetSpatialRef() const override
7789 : {
7790 105 : m_poSRS = m_poArray->GetSpatialRef();
7791 105 : if (m_poSRS)
7792 : {
7793 79 : m_poSRS.reset(m_poSRS->Clone());
7794 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7795 237 : for (auto &m : axisMapping)
7796 : {
7797 158 : if (m == static_cast<int>(m_iXDim) + 1)
7798 79 : m = 1;
7799 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7800 79 : m = 2;
7801 : }
7802 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7803 : }
7804 105 : return m_poSRS.get();
7805 : }
7806 :
7807 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7808 : const std::string &osFilenameLat)
7809 : {
7810 5 : m_osFilenameLong = osFilenameLong;
7811 5 : m_osFilenameLat = osFilenameLat;
7812 10 : CPLStringList aosGeoLoc;
7813 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7814 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7815 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7816 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7817 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7818 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7819 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7820 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7821 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7822 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7823 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7824 5 : }
7825 : };
7826 :
7827 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7828 : {
7829 24 : if (!m_osFilenameLong.empty())
7830 5 : VSIUnlink(m_osFilenameLong.c_str());
7831 24 : if (!m_osFilenameLat.empty())
7832 5 : VSIUnlink(m_osFilenameLat.c_str());
7833 48 : }
7834 :
7835 : /************************************************************************/
7836 : /* GDALMDArrayResampledDatasetRasterBand() */
7837 : /************************************************************************/
7838 :
7839 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7840 24 : GDALMDArrayResampledDataset *poDSIn)
7841 : {
7842 24 : const auto &poArray(poDSIn->m_poArray);
7843 24 : const auto blockSize(poArray->GetBlockSize());
7844 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7845 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7846 13 : blockSize[poDSIn->m_iYDim]))
7847 24 : : 1;
7848 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7849 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7850 13 : blockSize[poDSIn->m_iXDim]))
7851 24 : : poDSIn->GetRasterXSize();
7852 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7853 24 : eAccess = poDSIn->eAccess;
7854 24 : }
7855 :
7856 : /************************************************************************/
7857 : /* GetNoDataValue() */
7858 : /************************************************************************/
7859 :
7860 54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7861 : {
7862 54 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7863 54 : const auto &poArray(l_poDS->m_poArray);
7864 54 : bool bHasNodata = false;
7865 54 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7866 54 : if (pbHasNoData)
7867 48 : *pbHasNoData = bHasNodata;
7868 54 : return dfRes;
7869 : }
7870 :
7871 : /************************************************************************/
7872 : /* IReadBlock() */
7873 : /************************************************************************/
7874 :
7875 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7876 : int nBlockYOff,
7877 : void *pImage)
7878 : {
7879 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7880 0 : const int nXOff = nBlockXOff * nBlockXSize;
7881 0 : const int nYOff = nBlockYOff * nBlockYSize;
7882 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7883 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7884 : GDALRasterIOExtraArg sExtraArg;
7885 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7886 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7887 : nReqXSize, nReqYSize, eDataType, nDTSize,
7888 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7889 : }
7890 :
7891 : /************************************************************************/
7892 : /* IRasterIO() */
7893 : /************************************************************************/
7894 :
7895 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7896 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7897 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7898 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7899 : GDALRasterIOExtraArg *psExtraArg)
7900 : {
7901 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7902 32 : const auto &poArray(l_poDS->m_poArray);
7903 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7904 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7905 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7906 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7907 : {
7908 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7909 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7910 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7911 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7912 :
7913 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7914 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7915 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7916 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7917 :
7918 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7919 32 : l_poDS->m_anCount.data(), nullptr,
7920 32 : l_poDS->m_anStride.data(),
7921 64 : GDALExtendedDataType::Create(eBufType), pData)
7922 32 : ? CE_None
7923 32 : : CE_Failure;
7924 : }
7925 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7926 : pData, nBufXSize, nBufYSize, eBufType,
7927 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7928 : }
7929 :
7930 : class GDALMDArrayResampled final : public GDALPamMDArray
7931 : {
7932 : private:
7933 : std::shared_ptr<GDALMDArray> m_poParent{};
7934 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7935 : std::vector<GUInt64> m_anBlockSize;
7936 : GDALExtendedDataType m_dt;
7937 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7938 : std::shared_ptr<GDALMDArray> m_poVarX{};
7939 : std::shared_ptr<GDALMDArray> m_poVarY{};
7940 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7941 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7942 :
7943 : protected:
7944 21 : GDALMDArrayResampled(
7945 : const std::shared_ptr<GDALMDArray> &poParent,
7946 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7947 : const std::vector<GUInt64> &anBlockSize)
7948 42 : : GDALAbstractMDArray(std::string(),
7949 42 : "Resampled view of " + poParent->GetFullName()),
7950 : GDALPamMDArray(
7951 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7952 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7953 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7954 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7955 : {
7956 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7957 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7958 21 : }
7959 :
7960 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7961 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7962 : const GDALExtendedDataType &bufferDataType,
7963 : void *pDstBuffer) const override;
7964 :
7965 : public:
7966 : static std::shared_ptr<GDALMDArray>
7967 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7968 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7969 : GDALRIOResampleAlg resampleAlg,
7970 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7971 :
7972 42 : ~GDALMDArrayResampled() override
7973 21 : {
7974 : // First close the warped VRT
7975 21 : m_poReprojectedDS.reset();
7976 21 : m_poParentDS.reset();
7977 42 : }
7978 :
7979 11 : bool IsWritable() const override
7980 : {
7981 11 : return false;
7982 : }
7983 :
7984 74 : const std::string &GetFilename() const override
7985 : {
7986 74 : return m_poParent->GetFilename();
7987 : }
7988 :
7989 : const std::vector<std::shared_ptr<GDALDimension>> &
7990 257 : GetDimensions() const override
7991 : {
7992 257 : return m_apoDims;
7993 : }
7994 :
7995 109 : const GDALExtendedDataType &GetDataType() const override
7996 : {
7997 109 : return m_dt;
7998 : }
7999 :
8000 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
8001 : {
8002 21 : return m_poSRS;
8003 : }
8004 :
8005 12 : std::vector<GUInt64> GetBlockSize() const override
8006 : {
8007 12 : return m_anBlockSize;
8008 : }
8009 :
8010 : std::shared_ptr<GDALAttribute>
8011 1 : GetAttribute(const std::string &osName) const override
8012 : {
8013 1 : return m_poParent->GetAttribute(osName);
8014 : }
8015 :
8016 : std::vector<std::shared_ptr<GDALAttribute>>
8017 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
8018 : {
8019 12 : return m_poParent->GetAttributes(papszOptions);
8020 : }
8021 :
8022 1 : const std::string &GetUnit() const override
8023 : {
8024 1 : return m_poParent->GetUnit();
8025 : }
8026 :
8027 1 : const void *GetRawNoDataValue() const override
8028 : {
8029 1 : return m_poParent->GetRawNoDataValue();
8030 : }
8031 :
8032 1 : double GetOffset(bool *pbHasOffset,
8033 : GDALDataType *peStorageType) const override
8034 : {
8035 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
8036 : }
8037 :
8038 1 : double GetScale(bool *pbHasScale,
8039 : GDALDataType *peStorageType) const override
8040 : {
8041 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
8042 : }
8043 : };
8044 :
8045 : /************************************************************************/
8046 : /* GDALMDArrayResampled::Create() */
8047 : /************************************************************************/
8048 :
8049 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8050 : const std::shared_ptr<GDALMDArray> &poParent,
8051 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8052 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8053 : CSLConstList /* papszOptions */)
8054 : {
8055 29 : const char *pszResampleAlg = "nearest";
8056 29 : bool unsupported = false;
8057 29 : switch (resampleAlg)
8058 : {
8059 16 : case GRIORA_NearestNeighbour:
8060 16 : pszResampleAlg = "nearest";
8061 16 : break;
8062 2 : case GRIORA_Bilinear:
8063 2 : pszResampleAlg = "bilinear";
8064 2 : break;
8065 5 : case GRIORA_Cubic:
8066 5 : pszResampleAlg = "cubic";
8067 5 : break;
8068 1 : case GRIORA_CubicSpline:
8069 1 : pszResampleAlg = "cubicspline";
8070 1 : break;
8071 1 : case GRIORA_Lanczos:
8072 1 : pszResampleAlg = "lanczos";
8073 1 : break;
8074 1 : case GRIORA_Average:
8075 1 : pszResampleAlg = "average";
8076 1 : break;
8077 1 : case GRIORA_Mode:
8078 1 : pszResampleAlg = "mode";
8079 1 : break;
8080 1 : case GRIORA_Gauss:
8081 1 : unsupported = true;
8082 1 : break;
8083 0 : case GRIORA_RESERVED_START:
8084 0 : unsupported = true;
8085 0 : break;
8086 0 : case GRIORA_RESERVED_END:
8087 0 : unsupported = true;
8088 0 : break;
8089 1 : case GRIORA_RMS:
8090 1 : pszResampleAlg = "rms";
8091 1 : break;
8092 : }
8093 29 : if (unsupported)
8094 : {
8095 1 : CPLError(CE_Failure, CPLE_NotSupported,
8096 : "Unsupported resample method for GetResampled()");
8097 1 : return nullptr;
8098 : }
8099 :
8100 28 : if (poParent->GetDimensionCount() < 2)
8101 : {
8102 1 : CPLError(CE_Failure, CPLE_NotSupported,
8103 : "GetResampled() only supports 2 dimensions or more");
8104 1 : return nullptr;
8105 : }
8106 :
8107 27 : const auto &aoParentDims = poParent->GetDimensions();
8108 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8109 : {
8110 2 : CPLError(CE_Failure, CPLE_AppDefined,
8111 : "GetResampled(): apoNewDims size should be the same as "
8112 : "GetDimensionCount()");
8113 2 : return nullptr;
8114 : }
8115 :
8116 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8117 25 : apoNewDims.reserve(apoNewDimsIn.size());
8118 :
8119 50 : std::vector<GUInt64> anBlockSize;
8120 25 : anBlockSize.reserve(apoNewDimsIn.size());
8121 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8122 :
8123 50 : auto apoParentDims = poParent->GetDimensions();
8124 : // Special case for NASA EMIT datasets
8125 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8126 7 : apoParentDims[0]->GetName() == "downtrack" &&
8127 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8128 2 : apoParentDims[2]->GetName() == "bands");
8129 :
8130 : const size_t iYDimParent =
8131 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8132 : const size_t iXDimParent =
8133 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8134 :
8135 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8136 : {
8137 53 : if (i == iYDimParent || i == iXDimParent)
8138 48 : continue;
8139 5 : if (apoNewDimsIn[i] == nullptr)
8140 : {
8141 3 : apoNewDims.emplace_back(aoParentDims[i]);
8142 : }
8143 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8144 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8145 : {
8146 1 : CPLError(CE_Failure, CPLE_AppDefined,
8147 : "GetResampled(): apoNewDims[%u] should be the same "
8148 : "as its parent",
8149 : i);
8150 1 : return nullptr;
8151 : }
8152 : else
8153 : {
8154 1 : apoNewDims.emplace_back(aoParentDims[i]);
8155 : }
8156 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8157 : }
8158 :
8159 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8160 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8161 :
8162 24 : double dfXStart = 0.0;
8163 24 : double dfXSpacing = 0.0;
8164 24 : bool gotXSpacing = false;
8165 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8166 24 : if (poNewDimX)
8167 : {
8168 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8169 : {
8170 0 : CPLError(CE_Failure, CPLE_NotSupported,
8171 : "Too big size for X dimension");
8172 0 : return nullptr;
8173 : }
8174 4 : auto var = poNewDimX->GetIndexingVariable();
8175 4 : if (var)
8176 : {
8177 2 : if (var->GetDimensionCount() != 1 ||
8178 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8179 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8180 : {
8181 0 : CPLError(CE_Failure, CPLE_NotSupported,
8182 : "New X dimension should be indexed by a regularly "
8183 : "spaced variable");
8184 0 : return nullptr;
8185 : }
8186 1 : gotXSpacing = true;
8187 : }
8188 : }
8189 :
8190 24 : double dfYStart = 0.0;
8191 24 : double dfYSpacing = 0.0;
8192 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8193 24 : bool gotYSpacing = false;
8194 24 : if (poNewDimY)
8195 : {
8196 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8197 : {
8198 0 : CPLError(CE_Failure, CPLE_NotSupported,
8199 : "Too big size for Y dimension");
8200 0 : return nullptr;
8201 : }
8202 4 : auto var = poNewDimY->GetIndexingVariable();
8203 4 : if (var)
8204 : {
8205 2 : if (var->GetDimensionCount() != 1 ||
8206 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8207 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8208 : {
8209 0 : CPLError(CE_Failure, CPLE_NotSupported,
8210 : "New Y dimension should be indexed by a regularly "
8211 : "spaced variable");
8212 0 : return nullptr;
8213 : }
8214 1 : gotYSpacing = true;
8215 : }
8216 : }
8217 :
8218 : // This limitation could probably be removed
8219 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8220 : {
8221 0 : CPLError(CE_Failure, CPLE_NotSupported,
8222 : "Either none of new X or Y dimension should have an indexing "
8223 : "variable, or both should both should have one.");
8224 0 : return nullptr;
8225 : }
8226 :
8227 48 : std::string osDstWKT;
8228 24 : if (poTargetSRS)
8229 : {
8230 2 : char *pszDstWKT = nullptr;
8231 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8232 : {
8233 0 : CPLFree(pszDstWKT);
8234 0 : return nullptr;
8235 : }
8236 2 : osDstWKT = pszDstWKT;
8237 2 : CPLFree(pszDstWKT);
8238 : }
8239 :
8240 : // Use coordinate variables for geolocation array
8241 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8242 24 : bool useGeolocationArray = false;
8243 24 : if (apoCoordinateVars.size() >= 2)
8244 : {
8245 0 : std::shared_ptr<GDALMDArray> poLongVar;
8246 0 : std::shared_ptr<GDALMDArray> poLatVar;
8247 15 : for (const auto &poCoordVar : apoCoordinateVars)
8248 : {
8249 10 : const auto &osName = poCoordVar->GetName();
8250 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8251 20 : std::string osStandardName;
8252 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8253 2 : poAttr->GetDimensionCount() == 0)
8254 : {
8255 2 : const char *pszStandardName = poAttr->ReadAsString();
8256 2 : if (pszStandardName)
8257 2 : osStandardName = pszStandardName;
8258 : }
8259 21 : if (osName == "lon" || osName == "longitude" ||
8260 21 : osName == "Longitude" || osStandardName == "longitude")
8261 : {
8262 5 : poLongVar = poCoordVar;
8263 : }
8264 6 : else if (osName == "lat" || osName == "latitude" ||
8265 6 : osName == "Latitude" || osStandardName == "latitude")
8266 : {
8267 5 : poLatVar = poCoordVar;
8268 : }
8269 : }
8270 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8271 : {
8272 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8273 5 : const auto &longDims = poLongVar->GetDimensions();
8274 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8275 5 : const auto &latDims = poLatVar->GetDimensions();
8276 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8277 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8278 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8279 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8280 : {
8281 : // Geolocation arrays are 1D, and of consistent size with
8282 : // the variable
8283 0 : useGeolocationArray = true;
8284 : }
8285 1 : else if ((longDimCount == 2 ||
8286 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8287 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8288 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8289 1 : (latDimCount == 2 ||
8290 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8291 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8292 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8293 :
8294 : {
8295 : // Geolocation arrays are 2D (or 3D with first dimension of
8296 : // size 1, as found in Sentinel 5P products), and of consistent
8297 : // size with the variable
8298 5 : useGeolocationArray = true;
8299 : }
8300 : else
8301 : {
8302 0 : CPLDebug(
8303 : "GDAL",
8304 : "Longitude and latitude coordinate variables found, "
8305 : "but their characteristics are not compatible of using "
8306 : "them as geolocation arrays");
8307 : }
8308 5 : if (useGeolocationArray)
8309 : {
8310 10 : CPLDebug("GDAL",
8311 : "Setting geolocation array from variables %s and %s",
8312 5 : poLongVar->GetName().c_str(),
8313 5 : poLatVar->GetName().c_str());
8314 : const std::string osFilenameLong =
8315 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8316 : const std::string osFilenameLat =
8317 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8318 : std::unique_ptr<GDALDataset> poTmpLongDS(
8319 : longDimCount == 1
8320 0 : ? poLongVar->AsClassicDataset(0, 0)
8321 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8322 15 : longDimCount - 2));
8323 5 : auto hTIFFLongDS = GDALTranslate(
8324 : osFilenameLong.c_str(),
8325 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8326 : std::unique_ptr<GDALDataset> poTmpLatDS(
8327 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8328 20 : : poLatVar->AsClassicDataset(
8329 15 : latDimCount - 1, latDimCount - 2));
8330 5 : auto hTIFFLatDS = GDALTranslate(
8331 : osFilenameLat.c_str(),
8332 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8333 5 : const bool bError =
8334 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8335 5 : GDALClose(hTIFFLongDS);
8336 5 : GDALClose(hTIFFLatDS);
8337 5 : if (bError)
8338 : {
8339 0 : VSIUnlink(osFilenameLong.c_str());
8340 0 : VSIUnlink(osFilenameLat.c_str());
8341 0 : return nullptr;
8342 : }
8343 :
8344 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8345 : }
8346 : }
8347 : else
8348 : {
8349 0 : CPLDebug("GDAL",
8350 : "Coordinate variables available for %s, but "
8351 : "longitude and/or latitude variables were not identified",
8352 0 : poParent->GetName().c_str());
8353 : }
8354 : }
8355 :
8356 : // Build gdalwarp arguments
8357 48 : CPLStringList aosArgv;
8358 :
8359 24 : aosArgv.AddString("-of");
8360 24 : aosArgv.AddString("VRT");
8361 :
8362 24 : aosArgv.AddString("-r");
8363 24 : aosArgv.AddString(pszResampleAlg);
8364 :
8365 24 : if (!osDstWKT.empty())
8366 : {
8367 2 : aosArgv.AddString("-t_srs");
8368 2 : aosArgv.AddString(osDstWKT.c_str());
8369 : }
8370 :
8371 24 : if (useGeolocationArray)
8372 5 : aosArgv.AddString("-geoloc");
8373 :
8374 24 : if (gotXSpacing && gotYSpacing)
8375 : {
8376 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8377 : const double dfXMax =
8378 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8379 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8380 : const double dfYMin =
8381 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8382 1 : aosArgv.AddString("-te");
8383 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8384 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8385 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8386 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8387 : }
8388 :
8389 24 : if (poNewDimX && poNewDimY)
8390 : {
8391 3 : aosArgv.AddString("-ts");
8392 : aosArgv.AddString(
8393 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8394 : aosArgv.AddString(
8395 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8396 : }
8397 21 : else if (poNewDimX)
8398 : {
8399 1 : aosArgv.AddString("-ts");
8400 : aosArgv.AddString(
8401 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8402 1 : aosArgv.AddString("0");
8403 : }
8404 20 : else if (poNewDimY)
8405 : {
8406 1 : aosArgv.AddString("-ts");
8407 1 : aosArgv.AddString("0");
8408 : aosArgv.AddString(
8409 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8410 : }
8411 :
8412 : // Create a warped VRT dataset
8413 : GDALWarpAppOptions *psOptions =
8414 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8415 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8416 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8417 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8418 24 : GDALWarpAppOptionsFree(psOptions);
8419 24 : if (poReprojectedDS == nullptr)
8420 3 : return nullptr;
8421 :
8422 : int nBlockXSize;
8423 : int nBlockYSize;
8424 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8425 21 : anBlockSize.emplace_back(nBlockYSize);
8426 21 : anBlockSize.emplace_back(nBlockXSize);
8427 :
8428 21 : GDALGeoTransform gt;
8429 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8430 21 : CPLAssert(eErr == CE_None);
8431 21 : CPL_IGNORE_RET_VAL(eErr);
8432 :
8433 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8434 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8435 42 : poReprojectedDS->GetRasterYSize());
8436 : auto varY = GDALMDArrayRegularlySpaced::Create(
8437 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8438 21 : poDimY->SetIndexingVariable(varY);
8439 :
8440 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8441 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8442 42 : poReprojectedDS->GetRasterXSize());
8443 : auto varX = GDALMDArrayRegularlySpaced::Create(
8444 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8445 21 : poDimX->SetIndexingVariable(varX);
8446 :
8447 21 : apoNewDims.emplace_back(poDimY);
8448 21 : apoNewDims.emplace_back(poDimX);
8449 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8450 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8451 21 : newAr->SetSelf(newAr);
8452 21 : if (poTargetSRS)
8453 : {
8454 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8455 : }
8456 : else
8457 : {
8458 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8459 : }
8460 21 : newAr->m_poVarX = varX;
8461 21 : newAr->m_poVarY = varY;
8462 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8463 21 : newAr->m_poParentDS = std::move(poParentDS);
8464 :
8465 : // If the input array is y,x,band ordered, the above newAr is
8466 : // actually band,y,x ordered as it is more convenient for
8467 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8468 : // array to the order of the input array
8469 21 : if (bYXBandOrder)
8470 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8471 :
8472 19 : return newAr;
8473 : }
8474 :
8475 : /************************************************************************/
8476 : /* GDALMDArrayResampled::IRead() */
8477 : /************************************************************************/
8478 :
8479 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8480 : const size_t *count, const GInt64 *arrayStep,
8481 : const GPtrDiff_t *bufferStride,
8482 : const GDALExtendedDataType &bufferDataType,
8483 : void *pDstBuffer) const
8484 : {
8485 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8486 0 : return false;
8487 :
8488 : struct Stack
8489 : {
8490 : size_t nIters = 0;
8491 : GByte *dst_ptr = nullptr;
8492 : GPtrDiff_t dst_inc_offset = 0;
8493 : };
8494 :
8495 29 : const auto nDims = GetDimensionCount();
8496 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8497 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8498 92 : for (size_t i = 0; i < nDims; i++)
8499 : {
8500 63 : stack[i].dst_inc_offset =
8501 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8502 : }
8503 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8504 :
8505 29 : size_t dimIdx = 0;
8506 29 : const size_t iDimY = nDims - 2;
8507 29 : const size_t iDimX = nDims - 1;
8508 : // Use an array to avoid a false positive warning from CLang Static
8509 : // Analyzer about flushCaches being never read
8510 29 : bool flushCaches[] = {false};
8511 : const bool bYXBandOrder =
8512 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8513 :
8514 38 : lbl_next_depth:
8515 38 : if (dimIdx == iDimY)
8516 : {
8517 33 : if (flushCaches[0])
8518 : {
8519 5 : flushCaches[0] = false;
8520 : // When changing of 2D slice, flush GDAL 2D buffers
8521 5 : m_poParentDS->FlushCache(false);
8522 5 : m_poReprojectedDS->FlushCache(false);
8523 : }
8524 :
8525 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8526 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8527 : arrayStep, bufferStride, bufferDataType,
8528 33 : stack[dimIdx].dst_ptr))
8529 : {
8530 0 : return false;
8531 : }
8532 : }
8533 : else
8534 : {
8535 5 : stack[dimIdx].nIters = count[dimIdx];
8536 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8537 5 : arrayStartIdx[dimIdx])
8538 : {
8539 1 : flushCaches[0] = true;
8540 : }
8541 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8542 5 : arrayStartIdx[dimIdx];
8543 : while (true)
8544 : {
8545 9 : dimIdx++;
8546 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8547 9 : goto lbl_next_depth;
8548 9 : lbl_return_to_caller:
8549 9 : dimIdx--;
8550 9 : if ((--stack[dimIdx].nIters) == 0)
8551 5 : break;
8552 4 : flushCaches[0] = true;
8553 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8554 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8555 : }
8556 : }
8557 38 : if (dimIdx > 0)
8558 9 : goto lbl_return_to_caller;
8559 :
8560 29 : return true;
8561 : }
8562 :
8563 : /************************************************************************/
8564 : /* GetResampled() */
8565 : /************************************************************************/
8566 :
8567 : /** Return an array that is a resampled / reprojected view of the current array
8568 : *
8569 : * This is the same as the C function GDALMDArrayGetResampled().
8570 : *
8571 : * Currently this method can only resample along the last 2 dimensions, unless
8572 : * orthorectifying a NASA EMIT dataset.
8573 : *
8574 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8575 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8576 : *
8577 : * Options available are:
8578 : * <ul>
8579 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8580 : * Can be set to NO to use generic reprojection method.
8581 : * </li>
8582 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8583 : * orthorectification to take into account the value of the
8584 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8585 : * current array along the band dimension are valid.</li>
8586 : * </ul>
8587 : *
8588 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8589 : * apoNewDims[i] can be NULL to let the method automatically
8590 : * determine it.
8591 : * @param resampleAlg Resampling algorithm
8592 : * @param poTargetSRS Target SRS, or nullptr
8593 : * @param papszOptions NULL-terminated list of options, or NULL.
8594 : *
8595 : * @return a new array, that holds a reference to the original one, and thus is
8596 : * a view of it (not a copy), or nullptr in case of error.
8597 : *
8598 : * @since 3.4
8599 : */
8600 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8601 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8602 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8603 : CSLConstList papszOptions) const
8604 : {
8605 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8606 38 : if (!self)
8607 : {
8608 0 : CPLError(CE_Failure, CPLE_AppDefined,
8609 : "Driver implementation issue: m_pSelf not set !");
8610 0 : return nullptr;
8611 : }
8612 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8613 : {
8614 0 : CPLError(CE_Failure, CPLE_AppDefined,
8615 : "GetResampled() only supports numeric data type");
8616 0 : return nullptr;
8617 : }
8618 :
8619 : // Special case for NASA EMIT datasets
8620 76 : auto apoDims = GetDimensions();
8621 36 : if (poTargetSRS == nullptr &&
8622 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8623 20 : apoDims[1]->GetName() == "crosstrack" &&
8624 10 : apoDims[2]->GetName() == "bands" &&
8625 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8626 1 : apoNewDims ==
8627 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8628 30 : apoDims[2]})) ||
8629 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8630 3 : apoDims[1]->GetName() == "crosstrack" &&
8631 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8632 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8633 : "EMIT_ORTHORECTIFICATION", "YES")))
8634 : {
8635 9 : auto poRootGroup = GetRootGroup();
8636 9 : if (poRootGroup)
8637 : {
8638 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8639 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8640 9 : if (poAttrGeotransform &&
8641 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8642 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8643 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8644 9 : poLocationGroup)
8645 : {
8646 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8647 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8648 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8649 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8650 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8651 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8652 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8653 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8654 : {
8655 : return CreateGLTOrthorectified(
8656 : self, poRootGroup, poGLT_X, poGLT_Y,
8657 : /* nGLTIndexOffset = */ -1,
8658 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8659 : }
8660 : }
8661 : }
8662 : }
8663 :
8664 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8665 : "EMIT_ORTHORECTIFICATION", "NO")))
8666 : {
8667 0 : CPLError(CE_Failure, CPLE_AppDefined,
8668 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8669 : "parameters are not compatible with it");
8670 0 : return nullptr;
8671 : }
8672 :
8673 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8674 29 : poTargetSRS, papszOptions);
8675 : }
8676 :
8677 : /************************************************************************/
8678 : /* GDALDatasetFromArray() */
8679 : /************************************************************************/
8680 :
8681 : class GDALDatasetFromArray;
8682 :
8683 : namespace
8684 : {
8685 : struct MetadataItem
8686 : {
8687 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8688 : std::string osName{};
8689 : std::string osDefinition{};
8690 : bool bDefinitionUsesPctForG = false;
8691 : };
8692 :
8693 : struct BandImageryMetadata
8694 : {
8695 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8696 : double dfCentralWavelengthToMicrometer = 1.0;
8697 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8698 : double dfFWHMToMicrometer = 1.0;
8699 : };
8700 :
8701 : } // namespace
8702 :
8703 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8704 : {
8705 : std::vector<GUInt64> m_anOffset{};
8706 : std::vector<size_t> m_anCount{};
8707 : std::vector<GPtrDiff_t> m_anStride{};
8708 :
8709 : protected:
8710 : CPLErr IReadBlock(int, int, void *) override;
8711 : CPLErr IWriteBlock(int, int, void *) override;
8712 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8713 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8714 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8715 : GSpacing nLineSpaceBuf,
8716 : GDALRasterIOExtraArg *psExtraArg) override;
8717 :
8718 : public:
8719 : explicit GDALRasterBandFromArray(
8720 : GDALDatasetFromArray *poDSIn,
8721 : const std::vector<GUInt64> &anOtherDimCoord,
8722 : const std::vector<std::vector<MetadataItem>>
8723 : &aoBandParameterMetadataItems,
8724 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8725 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8726 :
8727 : double GetNoDataValue(int *pbHasNoData) override;
8728 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8729 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8730 : double GetOffset(int *pbHasOffset) override;
8731 : double GetScale(int *pbHasScale) override;
8732 : const char *GetUnitType() override;
8733 : GDALColorInterp GetColorInterpretation() override;
8734 : };
8735 :
8736 : class GDALDatasetFromArray final : public GDALPamDataset
8737 : {
8738 : friend class GDALRasterBandFromArray;
8739 :
8740 : std::shared_ptr<GDALMDArray> m_poArray;
8741 : size_t m_iXDim;
8742 : size_t m_iYDim;
8743 : GDALGeoTransform m_gt{};
8744 : bool m_bHasGT = false;
8745 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8746 : GDALMultiDomainMetadata m_oMDD{};
8747 : std::string m_osOvrFilename{};
8748 :
8749 : public:
8750 242 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8751 : size_t iXDim, size_t iYDim)
8752 242 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8753 : {
8754 : // Initialize an overview filename from the filename of the array
8755 : // and its name.
8756 242 : const std::string &osFilename = m_poArray->GetFilename();
8757 242 : if (!osFilename.empty())
8758 : {
8759 205 : m_osOvrFilename = osFilename;
8760 205 : m_osOvrFilename += '.';
8761 8066 : for (char ch : m_poArray->GetName())
8762 : {
8763 7861 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8764 6956 : (ch >= 'a' && ch <= 'z') || ch == '_')
8765 : {
8766 6290 : m_osOvrFilename += ch;
8767 : }
8768 : else
8769 : {
8770 1571 : m_osOvrFilename += '_';
8771 : }
8772 : }
8773 205 : m_osOvrFilename += ".ovr";
8774 205 : oOvManager.Initialize(this);
8775 : }
8776 242 : }
8777 :
8778 : static GDALDatasetFromArray *
8779 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8780 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8781 : CSLConstList papszOptions);
8782 :
8783 : ~GDALDatasetFromArray() override;
8784 :
8785 391 : CPLErr Close() override
8786 : {
8787 391 : CPLErr eErr = CE_None;
8788 391 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8789 : {
8790 391 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8791 : CE_None)
8792 0 : eErr = CE_Failure;
8793 391 : m_poArray.reset();
8794 : }
8795 391 : return eErr;
8796 : }
8797 :
8798 73 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8799 : {
8800 73 : gt = m_gt;
8801 73 : return m_bHasGT ? CE_None : CE_Failure;
8802 : }
8803 :
8804 70 : const OGRSpatialReference *GetSpatialRef() const override
8805 : {
8806 70 : if (m_poArray->GetDimensionCount() < 2)
8807 3 : return nullptr;
8808 67 : m_poSRS = m_poArray->GetSpatialRef();
8809 67 : if (m_poSRS)
8810 : {
8811 28 : m_poSRS.reset(m_poSRS->Clone());
8812 56 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8813 84 : for (auto &m : axisMapping)
8814 : {
8815 56 : if (m == static_cast<int>(m_iXDim) + 1)
8816 28 : m = 1;
8817 28 : else if (m == static_cast<int>(m_iYDim) + 1)
8818 28 : m = 2;
8819 : }
8820 28 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8821 : }
8822 67 : return m_poSRS.get();
8823 : }
8824 :
8825 6 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8826 : {
8827 6 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8828 : }
8829 :
8830 179 : char **GetMetadata(const char *pszDomain) override
8831 : {
8832 179 : return m_oMDD.GetMetadata(pszDomain);
8833 : }
8834 :
8835 237 : const char *GetMetadataItem(const char *pszName,
8836 : const char *pszDomain) override
8837 : {
8838 429 : if (!m_osOvrFilename.empty() && pszName &&
8839 444 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8840 15 : EQUAL(pszDomain, "OVERVIEWS"))
8841 : {
8842 15 : return m_osOvrFilename.c_str();
8843 : }
8844 222 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8845 : }
8846 : };
8847 :
8848 484 : GDALDatasetFromArray::~GDALDatasetFromArray()
8849 : {
8850 242 : GDALDatasetFromArray::Close();
8851 484 : }
8852 :
8853 : /************************************************************************/
8854 : /* GDALRasterBandFromArray() */
8855 : /************************************************************************/
8856 :
8857 318 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8858 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8859 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8860 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8861 318 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8862 : {
8863 318 : const auto &poArray(poDSIn->m_poArray);
8864 318 : const auto &dims(poArray->GetDimensions());
8865 318 : const auto nDimCount(dims.size());
8866 636 : const auto blockSize(poArray->GetBlockSize());
8867 302 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8868 620 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8869 150 : blockSize[poDSIn->m_iYDim]))
8870 : : 1;
8871 318 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8872 165 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8873 165 : blockSize[poDSIn->m_iXDim]))
8874 318 : : poDSIn->GetRasterXSize();
8875 318 : eDataType = poArray->GetDataType().GetNumericDataType();
8876 318 : eAccess = poDSIn->eAccess;
8877 318 : m_anOffset.resize(nDimCount);
8878 318 : m_anCount.resize(nDimCount, 1);
8879 318 : m_anStride.resize(nDimCount);
8880 1069 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8881 : {
8882 751 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8883 : {
8884 262 : std::string dimName(dims[i]->GetName());
8885 131 : GUInt64 nIndex = anOtherDimCoord[j];
8886 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8887 : // subsetted dimensions as generated by GetView()
8888 131 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8889 : {
8890 : CPLStringList aosTokens(
8891 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8892 6 : if (aosTokens.size() == 5)
8893 : {
8894 6 : dimName = aosTokens[1];
8895 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8896 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8897 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8898 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8899 0 : : nStartDim - (nIndex * -nIncrDim);
8900 : }
8901 : }
8902 131 : if (nDimCount != 3 || dimName != "Band")
8903 : {
8904 70 : SetMetadataItem(
8905 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8906 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8907 : }
8908 :
8909 131 : auto indexingVar = dims[i]->GetIndexingVariable();
8910 :
8911 : // If the indexing variable is also listed in band parameter arrays,
8912 : // then don't use our default formatting
8913 131 : if (indexingVar)
8914 : {
8915 47 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8916 : {
8917 14 : if (oItem.poArray->GetFullName() ==
8918 14 : indexingVar->GetFullName())
8919 : {
8920 12 : indexingVar.reset();
8921 12 : break;
8922 : }
8923 : }
8924 : }
8925 :
8926 164 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8927 33 : indexingVar->GetDimensions()[0]->GetSize() ==
8928 33 : dims[i]->GetSize())
8929 : {
8930 33 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8931 : {
8932 0 : if (!bHasWarned)
8933 : {
8934 0 : CPLError(
8935 : CE_Warning, CPLE_AppDefined,
8936 : "Maximum delay to load band metadata from "
8937 : "dimension indexing variables has expired. "
8938 : "Increase the value of the "
8939 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8940 : "option of GDALMDArray::AsClassicDataset() "
8941 : "(also accessible as the "
8942 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8943 : "configuration option), "
8944 : "or set it to 'unlimited' for unlimited delay. ");
8945 0 : bHasWarned = true;
8946 : }
8947 : }
8948 : else
8949 : {
8950 33 : size_t nCount = 1;
8951 33 : const auto &dt(indexingVar->GetDataType());
8952 66 : std::vector<GByte> abyTmp(dt.GetSize());
8953 66 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8954 33 : nullptr, nullptr, dt, &abyTmp[0]))
8955 : {
8956 33 : char *pszTmp = nullptr;
8957 33 : GDALExtendedDataType::CopyValue(
8958 33 : &abyTmp[0], dt, &pszTmp,
8959 66 : GDALExtendedDataType::CreateString());
8960 33 : dt.FreeDynamicMemory(abyTmp.data());
8961 33 : if (pszTmp)
8962 : {
8963 33 : SetMetadataItem(
8964 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8965 : pszTmp);
8966 33 : CPLFree(pszTmp);
8967 : }
8968 :
8969 33 : const auto &unit(indexingVar->GetUnit());
8970 33 : if (!unit.empty())
8971 : {
8972 12 : SetMetadataItem(
8973 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8974 : unit.c_str());
8975 : }
8976 : }
8977 : }
8978 : }
8979 :
8980 149 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8981 : {
8982 36 : CPLString osVal;
8983 :
8984 18 : size_t nCount = 1;
8985 18 : const auto &dt(oItem.poArray->GetDataType());
8986 18 : if (oItem.bDefinitionUsesPctForG)
8987 : {
8988 : // There is one and only one %[x][.y]f|g in osDefinition
8989 16 : std::vector<GByte> abyTmp(dt.GetSize());
8990 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8991 8 : nullptr, nullptr, dt, &abyTmp[0]))
8992 : {
8993 8 : double dfVal = 0;
8994 8 : GDALExtendedDataType::CopyValue(
8995 8 : &abyTmp[0], dt, &dfVal,
8996 16 : GDALExtendedDataType::Create(GDT_Float64));
8997 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8998 8 : dt.FreeDynamicMemory(abyTmp.data());
8999 : }
9000 : }
9001 : else
9002 : {
9003 : // There should be zero or one %s in osDefinition
9004 10 : char *pszValue = nullptr;
9005 10 : if (dt.GetClass() == GEDTC_STRING)
9006 : {
9007 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
9008 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
9009 2 : dt, &pszValue));
9010 : }
9011 : else
9012 : {
9013 16 : std::vector<GByte> abyTmp(dt.GetSize());
9014 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9015 : nullptr, nullptr, dt,
9016 8 : &abyTmp[0]))
9017 : {
9018 8 : GDALExtendedDataType::CopyValue(
9019 8 : &abyTmp[0], dt, &pszValue,
9020 16 : GDALExtendedDataType::CreateString());
9021 : }
9022 : }
9023 :
9024 10 : if (pszValue)
9025 : {
9026 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9027 10 : CPLFree(pszValue);
9028 : }
9029 : }
9030 18 : if (!osVal.empty())
9031 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9032 : }
9033 :
9034 131 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9035 : {
9036 : auto &poCentralWavelengthArray =
9037 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9038 4 : size_t nCount = 1;
9039 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9040 8 : std::vector<GByte> abyTmp(dt.GetSize());
9041 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9042 : &nCount, nullptr, nullptr,
9043 4 : dt, &abyTmp[0]))
9044 : {
9045 4 : double dfVal = 0;
9046 4 : GDALExtendedDataType::CopyValue(
9047 4 : &abyTmp[0], dt, &dfVal,
9048 8 : GDALExtendedDataType::Create(GDT_Float64));
9049 4 : dt.FreeDynamicMemory(abyTmp.data());
9050 4 : SetMetadataItem(
9051 : "CENTRAL_WAVELENGTH_UM",
9052 : CPLSPrintf(
9053 4 : "%g", dfVal * aoBandImageryMetadata[j]
9054 4 : .dfCentralWavelengthToMicrometer),
9055 : "IMAGERY");
9056 : }
9057 : }
9058 :
9059 131 : if (aoBandImageryMetadata[j].poFWHMArray)
9060 : {
9061 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9062 2 : size_t nCount = 1;
9063 2 : const auto &dt(poFWHMArray->GetDataType());
9064 4 : std::vector<GByte> abyTmp(dt.GetSize());
9065 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9066 2 : nullptr, dt, &abyTmp[0]))
9067 : {
9068 2 : double dfVal = 0;
9069 2 : GDALExtendedDataType::CopyValue(
9070 2 : &abyTmp[0], dt, &dfVal,
9071 4 : GDALExtendedDataType::Create(GDT_Float64));
9072 2 : dt.FreeDynamicMemory(abyTmp.data());
9073 2 : SetMetadataItem(
9074 : "FWHM_UM",
9075 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9076 2 : .dfFWHMToMicrometer),
9077 : "IMAGERY");
9078 : }
9079 : }
9080 :
9081 131 : m_anOffset[i] = anOtherDimCoord[j];
9082 131 : j++;
9083 : }
9084 : }
9085 318 : }
9086 :
9087 : /************************************************************************/
9088 : /* GetNoDataValue() */
9089 : /************************************************************************/
9090 :
9091 114 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9092 : {
9093 114 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9094 114 : const auto &poArray(l_poDS->m_poArray);
9095 114 : bool bHasNodata = false;
9096 114 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9097 114 : if (pbHasNoData)
9098 102 : *pbHasNoData = bHasNodata;
9099 114 : return res;
9100 : }
9101 :
9102 : /************************************************************************/
9103 : /* GetNoDataValueAsInt64() */
9104 : /************************************************************************/
9105 :
9106 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9107 : {
9108 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9109 1 : const auto &poArray(l_poDS->m_poArray);
9110 1 : bool bHasNodata = false;
9111 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9112 1 : if (pbHasNoData)
9113 1 : *pbHasNoData = bHasNodata;
9114 1 : return nodata;
9115 : }
9116 :
9117 : /************************************************************************/
9118 : /* GetNoDataValueAsUInt64() */
9119 : /************************************************************************/
9120 :
9121 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9122 : {
9123 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9124 1 : const auto &poArray(l_poDS->m_poArray);
9125 1 : bool bHasNodata = false;
9126 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9127 1 : if (pbHasNoData)
9128 1 : *pbHasNoData = bHasNodata;
9129 1 : return nodata;
9130 : }
9131 :
9132 : /************************************************************************/
9133 : /* GetOffset() */
9134 : /************************************************************************/
9135 :
9136 40 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9137 : {
9138 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9139 40 : const auto &poArray(l_poDS->m_poArray);
9140 40 : bool bHasValue = false;
9141 40 : double dfRes = poArray->GetOffset(&bHasValue);
9142 40 : if (pbHasOffset)
9143 21 : *pbHasOffset = bHasValue;
9144 40 : return dfRes;
9145 : }
9146 :
9147 : /************************************************************************/
9148 : /* GetUnitType() */
9149 : /************************************************************************/
9150 :
9151 46 : const char *GDALRasterBandFromArray::GetUnitType()
9152 : {
9153 46 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9154 46 : const auto &poArray(l_poDS->m_poArray);
9155 46 : return poArray->GetUnit().c_str();
9156 : }
9157 :
9158 : /************************************************************************/
9159 : /* GetScale() */
9160 : /************************************************************************/
9161 :
9162 38 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9163 : {
9164 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9165 38 : const auto &poArray(l_poDS->m_poArray);
9166 38 : bool bHasValue = false;
9167 38 : double dfRes = poArray->GetScale(&bHasValue);
9168 38 : if (pbHasScale)
9169 19 : *pbHasScale = bHasValue;
9170 38 : return dfRes;
9171 : }
9172 :
9173 : /************************************************************************/
9174 : /* IReadBlock() */
9175 : /************************************************************************/
9176 :
9177 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9178 : void *pImage)
9179 : {
9180 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9181 68 : const int nXOff = nBlockXOff * nBlockXSize;
9182 68 : const int nYOff = nBlockYOff * nBlockYSize;
9183 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9184 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9185 : GDALRasterIOExtraArg sExtraArg;
9186 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9187 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9188 : nReqXSize, nReqYSize, eDataType, nDTSize,
9189 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9190 : }
9191 :
9192 : /************************************************************************/
9193 : /* IWriteBlock() */
9194 : /************************************************************************/
9195 :
9196 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9197 : void *pImage)
9198 : {
9199 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9200 1 : const int nXOff = nBlockXOff * nBlockXSize;
9201 1 : const int nYOff = nBlockYOff * nBlockYSize;
9202 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9203 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9204 : GDALRasterIOExtraArg sExtraArg;
9205 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9206 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9207 : nReqXSize, nReqYSize, eDataType, nDTSize,
9208 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9209 : }
9210 :
9211 : /************************************************************************/
9212 : /* IRasterIO() */
9213 : /************************************************************************/
9214 :
9215 400 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9216 : int nYOff, int nXSize, int nYSize,
9217 : void *pData, int nBufXSize,
9218 : int nBufYSize, GDALDataType eBufType,
9219 : GSpacing nPixelSpaceBuf,
9220 : GSpacing nLineSpaceBuf,
9221 : GDALRasterIOExtraArg *psExtraArg)
9222 : {
9223 400 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9224 400 : const auto &poArray(l_poDS->m_poArray);
9225 400 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9226 400 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9227 400 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9228 400 : (nLineSpaceBuf % nBufferDTSize) == 0)
9229 : {
9230 400 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9231 400 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9232 800 : m_anStride[l_poDS->m_iXDim] =
9233 400 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9234 400 : if (poArray->GetDimensionCount() >= 2)
9235 : {
9236 387 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9237 387 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9238 387 : m_anStride[l_poDS->m_iYDim] =
9239 387 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9240 : }
9241 400 : if (eRWFlag == GF_Read)
9242 : {
9243 788 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9244 394 : m_anStride.data(),
9245 788 : GDALExtendedDataType::Create(eBufType), pData)
9246 394 : ? CE_None
9247 394 : : CE_Failure;
9248 : }
9249 : else
9250 : {
9251 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9252 6 : m_anStride.data(),
9253 12 : GDALExtendedDataType::Create(eBufType), pData)
9254 6 : ? CE_None
9255 6 : : CE_Failure;
9256 : }
9257 : }
9258 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9259 : pData, nBufXSize, nBufYSize, eBufType,
9260 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9261 : }
9262 :
9263 : /************************************************************************/
9264 : /* GetColorInterpretation() */
9265 : /************************************************************************/
9266 :
9267 66 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9268 : {
9269 66 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9270 66 : const auto &poArray(l_poDS->m_poArray);
9271 198 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9272 66 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9273 : {
9274 6 : bool bOK = false;
9275 6 : GUInt64 nStartIndex = 0;
9276 6 : if (poArray->GetDimensionCount() == 2 &&
9277 0 : poAttr->GetDimensionCount() == 0)
9278 : {
9279 0 : bOK = true;
9280 : }
9281 6 : else if (poArray->GetDimensionCount() == 3)
9282 : {
9283 6 : uint64_t nExtraDimSamples = 1;
9284 6 : const auto &apoDims = poArray->GetDimensions();
9285 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9286 : {
9287 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9288 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9289 : }
9290 6 : if (poAttr->GetDimensionsSize() ==
9291 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9292 : {
9293 6 : bOK = true;
9294 : }
9295 6 : nStartIndex = nBand - 1;
9296 : }
9297 6 : if (bOK)
9298 : {
9299 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9300 6 : const size_t nCount = 1;
9301 6 : const GInt64 arrayStep = 1;
9302 6 : const GPtrDiff_t bufferStride = 1;
9303 6 : char *pszValue = nullptr;
9304 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9305 6 : oStringDT, &pszValue);
9306 6 : if (pszValue)
9307 : {
9308 : const auto eColorInterp =
9309 6 : GDALGetColorInterpretationByName(pszValue);
9310 6 : CPLFree(pszValue);
9311 6 : return eColorInterp;
9312 : }
9313 : }
9314 : }
9315 60 : return GCI_Undefined;
9316 : }
9317 :
9318 : /************************************************************************/
9319 : /* GDALDatasetFromArray::Create() */
9320 : /************************************************************************/
9321 :
9322 294 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9323 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9324 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9325 :
9326 : {
9327 294 : const auto nDimCount(array->GetDimensionCount());
9328 294 : if (nDimCount == 0)
9329 : {
9330 1 : CPLError(CE_Failure, CPLE_NotSupported,
9331 : "Unsupported number of dimensions");
9332 1 : return nullptr;
9333 : }
9334 585 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9335 292 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9336 : {
9337 1 : CPLError(CE_Failure, CPLE_NotSupported,
9338 : "Only arrays with numeric data types "
9339 : "can be exposed as classic GDALDataset");
9340 1 : return nullptr;
9341 : }
9342 292 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9343 269 : (nDimCount >= 2 && iXDim == iYDim))
9344 : {
9345 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9346 8 : return nullptr;
9347 : }
9348 284 : GUInt64 nTotalBands = 1;
9349 284 : const auto &dims(array->GetDimensions());
9350 927 : for (size_t i = 0; i < nDimCount; ++i)
9351 : {
9352 644 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9353 : {
9354 94 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9355 : {
9356 1 : CPLError(CE_Failure, CPLE_AppDefined,
9357 : "Too many bands. Operate on a sliced view");
9358 1 : return nullptr;
9359 : }
9360 93 : nTotalBands *= dims[i]->GetSize();
9361 : }
9362 : }
9363 :
9364 566 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9365 566 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9366 926 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9367 : {
9368 643 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9369 : {
9370 93 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9371 93 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9372 93 : ++j;
9373 : }
9374 : }
9375 :
9376 283 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9377 :
9378 : const char *pszBandMetadata =
9379 283 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9380 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9381 566 : nNewDimCount);
9382 283 : if (pszBandMetadata)
9383 : {
9384 32 : if (!poRootGroup)
9385 : {
9386 1 : CPLError(CE_Failure, CPLE_AppDefined,
9387 : "Root group should be provided when BAND_METADATA is set");
9388 24 : return nullptr;
9389 : }
9390 31 : CPLJSONDocument oDoc;
9391 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9392 : {
9393 1 : CPLError(CE_Failure, CPLE_AppDefined,
9394 : "Invalid JSON content for BAND_METADATA");
9395 1 : return nullptr;
9396 : }
9397 30 : auto oRoot = oDoc.GetRoot();
9398 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9399 : {
9400 1 : CPLError(CE_Failure, CPLE_AppDefined,
9401 : "Value of BAND_METADATA should be an array");
9402 1 : return nullptr;
9403 : }
9404 :
9405 29 : auto oArray = oRoot.ToArray();
9406 38 : for (int j = 0; j < oArray.Size(); ++j)
9407 : {
9408 30 : const auto oJsonItem = oArray[j];
9409 30 : MetadataItem oItem;
9410 30 : size_t iExtraDimIdx = 0;
9411 :
9412 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9413 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9414 0 : std::shared_ptr<GDALMDArray> poArray;
9415 0 : std::shared_ptr<GDALAttribute> poAttribute;
9416 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9417 : {
9418 1 : CPLError(CE_Failure, CPLE_AppDefined,
9419 : "BAND_METADATA[%d][\"array\"] or "
9420 : "BAND_METADATA[%d][\"attribute\"] is missing",
9421 : j, j);
9422 1 : return nullptr;
9423 : }
9424 48 : else if (!osBandArrayFullname.empty() &&
9425 19 : !osBandAttributeName.empty())
9426 : {
9427 1 : CPLError(
9428 : CE_Failure, CPLE_AppDefined,
9429 : "BAND_METADATA[%d][\"array\"] and "
9430 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9431 : j, j);
9432 1 : return nullptr;
9433 : }
9434 28 : else if (!osBandArrayFullname.empty())
9435 : {
9436 : poArray =
9437 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9438 18 : if (!poArray)
9439 : {
9440 1 : CPLError(CE_Failure, CPLE_AppDefined,
9441 : "Array %s cannot be found",
9442 : osBandArrayFullname.c_str());
9443 3 : return nullptr;
9444 : }
9445 17 : if (poArray->GetDimensionCount() != 1)
9446 : {
9447 1 : CPLError(CE_Failure, CPLE_AppDefined,
9448 : "Array %s is not a 1D array",
9449 : osBandArrayFullname.c_str());
9450 1 : return nullptr;
9451 : }
9452 : const auto &osAuxArrayDimName =
9453 16 : poArray->GetDimensions()[0]->GetName();
9454 : auto oIter =
9455 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9456 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9457 : {
9458 1 : CPLError(
9459 : CE_Failure, CPLE_AppDefined,
9460 : "Dimension %s of array %s is not a non-X/Y dimension "
9461 : "of array %s",
9462 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9463 1 : array->GetName().c_str());
9464 1 : return nullptr;
9465 : }
9466 15 : iExtraDimIdx = oIter->second;
9467 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9468 : }
9469 : else
9470 : {
9471 10 : CPLAssert(!osBandAttributeName.empty());
9472 10 : poAttribute = !osBandAttributeName.empty() &&
9473 10 : osBandAttributeName[0] == '/'
9474 24 : ? poRootGroup->OpenAttributeFromFullname(
9475 : osBandAttributeName)
9476 10 : : array->GetAttribute(osBandAttributeName);
9477 10 : if (!poAttribute)
9478 : {
9479 2 : CPLError(CE_Failure, CPLE_AppDefined,
9480 : "Attribute %s cannot be found",
9481 : osBandAttributeName.c_str());
9482 8 : return nullptr;
9483 : }
9484 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9485 8 : if (aoAttrDims.size() != 1)
9486 : {
9487 4 : CPLError(CE_Failure, CPLE_AppDefined,
9488 : "Attribute %s is not a 1D array",
9489 : osBandAttributeName.c_str());
9490 4 : return nullptr;
9491 : }
9492 4 : bool found = false;
9493 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9494 : {
9495 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9496 5 : ->GetSize() == aoAttrDims[0])
9497 : {
9498 4 : if (found)
9499 : {
9500 2 : CPLError(CE_Failure, CPLE_AppDefined,
9501 : "Several dimensions of %s have the same "
9502 : "size as attribute %s. Cannot infer which "
9503 : "one to bind to!",
9504 1 : array->GetName().c_str(),
9505 : osBandAttributeName.c_str());
9506 1 : return nullptr;
9507 : }
9508 3 : found = true;
9509 3 : iExtraDimIdx = iter.second;
9510 : }
9511 : }
9512 3 : if (!found)
9513 : {
9514 2 : CPLError(
9515 : CE_Failure, CPLE_AppDefined,
9516 : "No dimension of %s has the same size as attribute %s",
9517 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9518 1 : return nullptr;
9519 : }
9520 : }
9521 :
9522 17 : oItem.osName = oJsonItem.GetString("item_name");
9523 17 : if (oItem.osName.empty())
9524 : {
9525 1 : CPLError(CE_Failure, CPLE_AppDefined,
9526 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9527 1 : return nullptr;
9528 : }
9529 :
9530 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9531 :
9532 : // Check correctness of definition
9533 16 : bool bFirstNumericFormatter = true;
9534 16 : std::string osModDefinition;
9535 16 : bool bDefinitionUsesPctForG = false;
9536 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9537 : {
9538 70 : if (osDefinition[k] == '%')
9539 : {
9540 15 : osModDefinition += osDefinition[k];
9541 15 : if (k + 1 == osDefinition.size())
9542 : {
9543 1 : CPLError(CE_Failure, CPLE_AppDefined,
9544 : "Value of "
9545 : "BAND_METADATA[%d][\"item_value\"] = "
9546 : "%s is invalid at offset %d",
9547 : j, osDefinition.c_str(), int(k));
9548 1 : return nullptr;
9549 : }
9550 14 : ++k;
9551 14 : if (osDefinition[k] == '%')
9552 : {
9553 1 : osModDefinition += osDefinition[k];
9554 1 : continue;
9555 : }
9556 13 : if (!bFirstNumericFormatter)
9557 : {
9558 1 : CPLError(CE_Failure, CPLE_AppDefined,
9559 : "Value of "
9560 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9561 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9562 : "formatters should be specified at most once",
9563 : j, osDefinition.c_str(), int(k));
9564 1 : return nullptr;
9565 : }
9566 12 : bFirstNumericFormatter = false;
9567 19 : for (; k < osDefinition.size(); ++k)
9568 : {
9569 19 : osModDefinition += osDefinition[k];
9570 38 : if (!((osDefinition[k] >= '0' &&
9571 16 : osDefinition[k] <= '9') ||
9572 15 : osDefinition[k] == '.'))
9573 12 : break;
9574 : }
9575 24 : if (k == osDefinition.size() ||
9576 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9577 5 : osDefinition[k] != 's'))
9578 : {
9579 1 : CPLError(CE_Failure, CPLE_AppDefined,
9580 : "Value of "
9581 : "BAND_METADATA[%d][\"item_value\"] = "
9582 : "%s is invalid at offset %d: only "
9583 : "%%[x][.y]f|g or %%s formatters are accepted",
9584 : j, osDefinition.c_str(), int(k));
9585 1 : return nullptr;
9586 : }
9587 11 : bDefinitionUsesPctForG =
9588 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9589 11 : if (bDefinitionUsesPctForG)
9590 : {
9591 12 : if (poArray &&
9592 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9593 : {
9594 1 : CPLError(CE_Failure, CPLE_AppDefined,
9595 : "Data type of %s array is not numeric",
9596 1 : poArray->GetName().c_str());
9597 1 : return nullptr;
9598 : }
9599 8 : else if (poAttribute &&
9600 2 : poAttribute->GetDataType().GetClass() !=
9601 6 : GEDTC_NUMERIC)
9602 : {
9603 0 : CPLError(CE_Failure, CPLE_AppDefined,
9604 : "Data type of %s attribute is not numeric",
9605 0 : poAttribute->GetFullName().c_str());
9606 0 : return nullptr;
9607 : }
9608 : }
9609 : }
9610 62 : else if (osDefinition[k] == '$' &&
9611 62 : k + 1 < osDefinition.size() &&
9612 7 : osDefinition[k + 1] == '{')
9613 : {
9614 7 : const auto nPos = osDefinition.find('}', k);
9615 7 : if (nPos == std::string::npos)
9616 : {
9617 1 : CPLError(CE_Failure, CPLE_AppDefined,
9618 : "Value of "
9619 : "BAND_METADATA[%d][\"item_value\"] = "
9620 : "%s is invalid at offset %d",
9621 : j, osDefinition.c_str(), int(k));
9622 3 : return nullptr;
9623 : }
9624 : const auto osAttrName =
9625 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9626 0 : std::shared_ptr<GDALAttribute> poAttr;
9627 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9628 : {
9629 4 : poAttr = poArray->GetAttribute(osAttrName);
9630 4 : if (!poAttr)
9631 : {
9632 1 : CPLError(
9633 : CE_Failure, CPLE_AppDefined,
9634 : "Value of "
9635 : "BAND_METADATA[%d][\"item_value\"] = "
9636 : "%s is invalid: %s is not an attribute of %s",
9637 : j, osDefinition.c_str(), osAttrName.c_str(),
9638 1 : poArray->GetName().c_str());
9639 1 : return nullptr;
9640 : }
9641 : }
9642 : else
9643 : {
9644 : poAttr =
9645 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9646 2 : if (!poAttr)
9647 : {
9648 1 : CPLError(CE_Failure, CPLE_AppDefined,
9649 : "Value of "
9650 : "BAND_METADATA[%d][\"item_value\"] = "
9651 : "%s is invalid: %s is not an attribute",
9652 : j, osDefinition.c_str(),
9653 : osAttrName.c_str());
9654 1 : return nullptr;
9655 : }
9656 : }
9657 4 : k = nPos;
9658 4 : const char *pszValue = poAttr->ReadAsString();
9659 4 : if (!pszValue)
9660 : {
9661 0 : CPLError(CE_Failure, CPLE_AppDefined,
9662 : "Cannot get value of attribute %s as a "
9663 : "string",
9664 : osAttrName.c_str());
9665 0 : return nullptr;
9666 : }
9667 4 : osModDefinition += pszValue;
9668 : }
9669 : else
9670 : {
9671 48 : osModDefinition += osDefinition[k];
9672 : }
9673 : }
9674 :
9675 9 : if (poArray)
9676 8 : oItem.poArray = std::move(poArray);
9677 : else
9678 1 : oItem.poArray = std::move(poAttribute);
9679 9 : oItem.osDefinition = std::move(osModDefinition);
9680 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9681 :
9682 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9683 9 : std::move(oItem));
9684 : }
9685 : }
9686 :
9687 518 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9688 : const char *pszBandImageryMetadata =
9689 259 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9690 259 : if (pszBandImageryMetadata)
9691 : {
9692 20 : if (!poRootGroup)
9693 : {
9694 1 : CPLError(CE_Failure, CPLE_AppDefined,
9695 : "Root group should be provided when BAND_IMAGERY_METADATA "
9696 : "is set");
9697 17 : return nullptr;
9698 : }
9699 19 : CPLJSONDocument oDoc;
9700 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9701 : {
9702 1 : CPLError(CE_Failure, CPLE_AppDefined,
9703 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9704 1 : return nullptr;
9705 : }
9706 18 : auto oRoot = oDoc.GetRoot();
9707 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9708 : {
9709 1 : CPLError(CE_Failure, CPLE_AppDefined,
9710 : "Value of BAND_IMAGERY_METADATA should be an object");
9711 1 : return nullptr;
9712 : }
9713 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9714 : {
9715 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9716 20 : oJsonItem.GetName() == "FWHM_UM")
9717 : {
9718 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9719 : const auto osBandAttributeName =
9720 34 : oJsonItem.GetString("attribute");
9721 0 : std::shared_ptr<GDALMDArray> poArray;
9722 0 : std::shared_ptr<GDALAttribute> poAttribute;
9723 17 : size_t iExtraDimIdx = 0;
9724 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9725 : {
9726 2 : CPLError(CE_Failure, CPLE_AppDefined,
9727 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9728 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9729 : "missing",
9730 2 : oJsonItem.GetName().c_str(),
9731 2 : oJsonItem.GetName().c_str());
9732 1 : return nullptr;
9733 : }
9734 25 : else if (!osBandArrayFullname.empty() &&
9735 9 : !osBandAttributeName.empty())
9736 : {
9737 2 : CPLError(CE_Failure, CPLE_AppDefined,
9738 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9739 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9740 : "mutually exclusive",
9741 2 : oJsonItem.GetName().c_str(),
9742 2 : oJsonItem.GetName().c_str());
9743 1 : return nullptr;
9744 : }
9745 15 : else if (!osBandArrayFullname.empty())
9746 : {
9747 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9748 8 : osBandArrayFullname);
9749 8 : if (!poArray)
9750 : {
9751 1 : CPLError(CE_Failure, CPLE_AppDefined,
9752 : "Array %s cannot be found",
9753 : osBandArrayFullname.c_str());
9754 3 : return nullptr;
9755 : }
9756 7 : if (poArray->GetDimensionCount() != 1)
9757 : {
9758 1 : CPLError(CE_Failure, CPLE_AppDefined,
9759 : "Array %s is not a 1D array",
9760 : osBandArrayFullname.c_str());
9761 1 : return nullptr;
9762 : }
9763 : const auto &osAuxArrayDimName =
9764 6 : poArray->GetDimensions()[0]->GetName();
9765 : auto oIter =
9766 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9767 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9768 : {
9769 1 : CPLError(CE_Failure, CPLE_AppDefined,
9770 : "Dimension \"%s\" of array \"%s\" is not a "
9771 : "non-X/Y dimension of array \"%s\"",
9772 : osAuxArrayDimName.c_str(),
9773 : osBandArrayFullname.c_str(),
9774 1 : array->GetName().c_str());
9775 1 : return nullptr;
9776 : }
9777 5 : iExtraDimIdx = oIter->second;
9778 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9779 : }
9780 : else
9781 : {
9782 : poAttribute =
9783 7 : !osBandAttributeName.empty() &&
9784 7 : osBandAttributeName[0] == '/'
9785 16 : ? poRootGroup->OpenAttributeFromFullname(
9786 : osBandAttributeName)
9787 7 : : array->GetAttribute(osBandAttributeName);
9788 7 : if (!poAttribute)
9789 : {
9790 2 : CPLError(CE_Failure, CPLE_AppDefined,
9791 : "Attribute %s cannot be found",
9792 : osBandAttributeName.c_str());
9793 6 : return nullptr;
9794 : }
9795 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9796 5 : if (aoAttrDims.size() != 1)
9797 : {
9798 2 : CPLError(CE_Failure, CPLE_AppDefined,
9799 : "Attribute %s is not a 1D array",
9800 : osBandAttributeName.c_str());
9801 2 : return nullptr;
9802 : }
9803 3 : bool found = false;
9804 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9805 : {
9806 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9807 4 : ->GetSize() == aoAttrDims[0])
9808 : {
9809 3 : if (found)
9810 : {
9811 2 : CPLError(CE_Failure, CPLE_AppDefined,
9812 : "Several dimensions of %s have the "
9813 : "same size as attribute %s. Cannot "
9814 : "infer which one to bind to!",
9815 1 : array->GetName().c_str(),
9816 : osBandAttributeName.c_str());
9817 1 : return nullptr;
9818 : }
9819 2 : found = true;
9820 2 : iExtraDimIdx = iter.second;
9821 : }
9822 : }
9823 2 : if (!found)
9824 : {
9825 2 : CPLError(CE_Failure, CPLE_AppDefined,
9826 : "No dimension of %s has the same size as "
9827 : "attribute %s",
9828 1 : array->GetName().c_str(),
9829 : osBandAttributeName.c_str());
9830 1 : return nullptr;
9831 : }
9832 : }
9833 :
9834 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9835 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9836 : {
9837 4 : if (osUnit.back() != '}')
9838 : {
9839 2 : CPLError(CE_Failure, CPLE_AppDefined,
9840 : "Value of "
9841 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9842 : "%s is invalid",
9843 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9844 2 : return nullptr;
9845 : }
9846 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9847 0 : std::shared_ptr<GDALAttribute> poAttr;
9848 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9849 : {
9850 2 : poAttr = poArray->GetAttribute(osAttrName);
9851 2 : if (!poAttr)
9852 : {
9853 2 : CPLError(
9854 : CE_Failure, CPLE_AppDefined,
9855 : "Value of "
9856 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9857 : "%s is invalid: %s is not an attribute of %s",
9858 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9859 : osAttrName.c_str(),
9860 : osBandArrayFullname.c_str());
9861 1 : return nullptr;
9862 : }
9863 : }
9864 : else
9865 : {
9866 : poAttr =
9867 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9868 1 : if (!poAttr)
9869 : {
9870 0 : CPLError(
9871 : CE_Failure, CPLE_AppDefined,
9872 : "Value of "
9873 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9874 : "%s is invalid: %s is not an attribute",
9875 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9876 : osAttrName.c_str());
9877 0 : return nullptr;
9878 : }
9879 : }
9880 :
9881 2 : const char *pszValue = poAttr->ReadAsString();
9882 2 : if (!pszValue)
9883 : {
9884 0 : CPLError(CE_Failure, CPLE_AppDefined,
9885 : "Cannot get value of attribute %s of %s as a "
9886 : "string",
9887 : osAttrName.c_str(),
9888 : osBandArrayFullname.c_str());
9889 0 : return nullptr;
9890 : }
9891 2 : osUnit = pszValue;
9892 : }
9893 4 : double dfConvToUM = 1.0;
9894 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9895 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9896 3 : osUnit == "nanometers")
9897 : {
9898 1 : dfConvToUM = 1e-3;
9899 : }
9900 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9901 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9902 1 : osUnit == "micrometers"))
9903 : {
9904 2 : CPLError(CE_Failure, CPLE_AppDefined,
9905 : "Unhandled value for "
9906 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9907 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9908 1 : return nullptr;
9909 : }
9910 :
9911 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9912 :
9913 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9914 3 : if (poArray)
9915 2 : abstractArray = std::move(poArray);
9916 : else
9917 1 : abstractArray = std::move(poAttribute);
9918 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9919 : {
9920 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9921 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9922 : }
9923 : else
9924 : {
9925 1 : item.poFWHMArray = std::move(abstractArray);
9926 1 : item.dfFWHMToMicrometer = dfConvToUM;
9927 : }
9928 : }
9929 : else
9930 : {
9931 1 : CPLError(CE_Warning, CPLE_AppDefined,
9932 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9933 2 : oJsonItem.GetName().c_str());
9934 : }
9935 : }
9936 : }
9937 :
9938 484 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9939 :
9940 242 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9941 :
9942 242 : poDS->nRasterYSize =
9943 242 : nDimCount < 2 ? 1
9944 226 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9945 226 : dims[iYDim]->GetSize()));
9946 484 : poDS->nRasterXSize = static_cast<int>(
9947 242 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9948 :
9949 484 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9950 484 : std::vector<GUInt64> anStackIters(nDimCount);
9951 484 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9952 760 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9953 : {
9954 518 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9955 : {
9956 50 : anMapNewToOld[j] = i;
9957 50 : j++;
9958 : }
9959 : }
9960 :
9961 242 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9962 :
9963 484 : const auto attrs(array->GetAttributes());
9964 387 : for (const auto &attr : attrs)
9965 : {
9966 145 : if (attr->GetName() != "COLOR_INTERPRETATION")
9967 : {
9968 268 : auto stringArray = attr->ReadAsStringArray();
9969 268 : std::string val;
9970 134 : if (stringArray.size() > 1)
9971 : {
9972 57 : val += '{';
9973 : }
9974 547 : for (int i = 0; i < stringArray.size(); ++i)
9975 : {
9976 413 : if (i > 0)
9977 279 : val += ',';
9978 413 : val += stringArray[i];
9979 : }
9980 134 : if (stringArray.size() > 1)
9981 : {
9982 57 : val += '}';
9983 : }
9984 134 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9985 : }
9986 : }
9987 :
9988 242 : const char *pszDelay = CSLFetchNameValueDef(
9989 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9990 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9991 : const double dfDelay =
9992 242 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9993 242 : const auto nStartTime = time(nullptr);
9994 242 : bool bHasWarned = false;
9995 : // Instantiate bands by iterating over non-XY variables
9996 242 : size_t iDim = 0;
9997 242 : int nCurBand = 1;
9998 370 : lbl_next_depth:
9999 370 : if (iDim < nNewDimCount)
10000 : {
10001 52 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
10002 52 : anOtherDimCoord[iDim] = 0;
10003 : while (true)
10004 : {
10005 128 : ++iDim;
10006 128 : goto lbl_next_depth;
10007 128 : lbl_return_to_caller:
10008 128 : --iDim;
10009 128 : --anStackIters[iDim];
10010 128 : if (anStackIters[iDim] == 0)
10011 52 : break;
10012 76 : ++anOtherDimCoord[iDim];
10013 : }
10014 : }
10015 : else
10016 : {
10017 636 : poDS->SetBand(nCurBand,
10018 : new GDALRasterBandFromArray(
10019 318 : poDS.get(), anOtherDimCoord,
10020 : aoBandParameterMetadataItems, aoBandImageryMetadata,
10021 318 : dfDelay, nStartTime, bHasWarned));
10022 318 : ++nCurBand;
10023 : }
10024 370 : if (iDim > 0)
10025 128 : goto lbl_return_to_caller;
10026 :
10027 242 : if (!array->GetFilename().empty())
10028 : {
10029 205 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10030 : std::string osDerivedDatasetName(
10031 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10032 410 : int(iYDim), array->GetFullName().c_str()));
10033 205 : if (!array->GetContext().empty())
10034 : {
10035 2 : osDerivedDatasetName += " with context ";
10036 2 : osDerivedDatasetName += array->GetContext();
10037 : }
10038 205 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10039 205 : poDS->TryLoadXML();
10040 :
10041 2 : for (const auto &[pszKey, pszValue] :
10042 : cpl::IterateNameValue(static_cast<CSLConstList>(
10043 207 : poDS->GDALPamDataset::GetMetadata())))
10044 : {
10045 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10046 : }
10047 : }
10048 :
10049 242 : return poDS.release();
10050 : }
10051 :
10052 : /************************************************************************/
10053 : /* AsClassicDataset() */
10054 : /************************************************************************/
10055 :
10056 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10057 : *
10058 : * In the case of > 2D arrays, additional dimensions will be represented as
10059 : * raster bands.
10060 : *
10061 : * The "reverse" method is GDALRasterBand::AsMDArray().
10062 : *
10063 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10064 : *
10065 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10066 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10067 : * Ignored if the dimension count is 1.
10068 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10069 : * and BAND_IMAGERY_METADATA option.
10070 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10071 : * nullptr. Current supported options are:
10072 : * <ul>
10073 : * <li>BAND_METADATA: JSON serialized array defining which
10074 : * arrays of the poRootGroup, indexed by non-X and Y
10075 : * dimensions, should be mapped as band metadata items.
10076 : * Each array item should be an object with the
10077 : * following members:
10078 : * - "array": full name of a band parameter array.
10079 : * Such array must be a one
10080 : * dimensional array, and its dimension must be one of
10081 : * the dimensions of the array on which the method is
10082 : * called (excluding the X and Y dimensions).
10083 : * - "attribute": name relative to *this array or full
10084 : * name of a single dimension numeric array whose size
10085 : * must be one of the dimensions of *this array
10086 : * (excluding the X and Y dimensions).
10087 : * "array" and "attribute" are mutually exclusive.
10088 : * - "item_name": band metadata item name
10089 : * - "item_value": (optional) String, where "%[x][.y]f",
10090 : * "%[x][.y]g" or "%s" printf-like formatting can be
10091 : * used to format the corresponding value of the
10092 : * parameter array. The percentage character should be
10093 : * repeated: "%%"
10094 : * "${attribute_name}" can also be used to include the
10095 : * value of an attribute for "array" when set and if
10096 : * not starting with '/'. Otherwise if starting with
10097 : * '/', it is the full path to the attribute.
10098 : *
10099 : * If "item_value" is not provided, a default formatting
10100 : * of the value will be applied.
10101 : *
10102 : * Example:
10103 : * [
10104 : * {
10105 : * "array": "/sensor_band_parameters/wavelengths",
10106 : * "item_name": "WAVELENGTH",
10107 : * "item_value": "%.1f ${units}"
10108 : * },
10109 : * {
10110 : * "array": "/sensor_band_parameters/fwhm",
10111 : * "item_name": "FWHM"
10112 : * },
10113 : * {
10114 : * "array": "/sensor_band_parameters/fwhm",
10115 : * "item_name": "FWHM_UNIT",
10116 : * "item_value": "${units}"
10117 : * }
10118 : * ]
10119 : *
10120 : * Example for Planet Labs Tanager radiance products:
10121 : * [
10122 : * {
10123 : * "attribute": "center_wavelengths",
10124 : * "item_name": "WAVELENGTH",
10125 : * "item_value": "%.1f ${center_wavelengths_units}"
10126 : * }
10127 : * ]
10128 : *
10129 : * </li>
10130 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10131 : * JSON serialized object defining which arrays of the
10132 : * poRootGroup, indexed by non-X and Y dimensions,
10133 : * should be mapped as band metadata items in the
10134 : * band IMAGERY domain.
10135 : * The object currently accepts 2 members:
10136 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10137 : * micrometers.
10138 : * - "FWHM_UM": Full-width half-maximum
10139 : * in micrometers.
10140 : * The value of each member should be an object with the
10141 : * following members:
10142 : * - "array": full name of a band parameter array.
10143 : * Such array must be a one dimensional array, and its
10144 : * dimension must be one of the dimensions of the
10145 : * array on which the method is called
10146 : * (excluding the X and Y dimensions).
10147 : * - "attribute": name relative to *this array or full
10148 : * name of a single dimension numeric array whose size
10149 : * must be one of the dimensions of *this array
10150 : * (excluding the X and Y dimensions).
10151 : * "array" and "attribute" are mutually exclusive,
10152 : * and one of them is required.
10153 : * - "unit": (optional) unit of the values pointed in
10154 : * the above array.
10155 : * Can be a literal string or a string of the form
10156 : * "${attribute_name}" to point to an attribute for
10157 : * "array" when set and if no starting
10158 : * with '/'. Otherwise if starting with '/', it is
10159 : * the full path to the attribute.
10160 : * Accepted values are "um", "micrometer"
10161 : * (with UK vs US spelling, singular or plural), "nm",
10162 : * "nanometer" (with UK vs US spelling, singular or
10163 : * plural)
10164 : * If not provided, micrometer is assumed.
10165 : *
10166 : * Example for EMIT datasets:
10167 : * {
10168 : * "CENTRAL_WAVELENGTH_UM": {
10169 : * "array": "/sensor_band_parameters/wavelengths",
10170 : * "unit": "${units}"
10171 : * },
10172 : * "FWHM_UM": {
10173 : * "array": "/sensor_band_parameters/fwhm",
10174 : * "unit": "${units}"
10175 : * }
10176 : * }
10177 : *
10178 : * Example for Planet Labs Tanager radiance products:
10179 : * {
10180 : * "CENTRAL_WAVELENGTH_UM": {
10181 : * "attribute": "center_wavelengths",
10182 : * "unit": "${center_wavelengths_units}"
10183 : * },
10184 : * "FWHM_UM": {
10185 : * "attribute": "fwhm",
10186 : * "unit": "${fwhm_units}"
10187 : * }
10188 : * }
10189 : *
10190 : * </li>
10191 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10192 : * seconds allowed to set the DIM_{dimname}_VALUE band
10193 : * metadata items from the indexing variable of the
10194 : * dimensions.
10195 : * Default value is 5. 'unlimited' can be used to mean
10196 : * unlimited delay. Can also be defined globally with
10197 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10198 : * option.</li>
10199 : * </ul>
10200 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10201 : */
10202 : GDALDataset *
10203 294 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10204 : const std::shared_ptr<GDALGroup> &poRootGroup,
10205 : CSLConstList papszOptions) const
10206 : {
10207 588 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10208 294 : if (!self)
10209 : {
10210 0 : CPLError(CE_Failure, CPLE_AppDefined,
10211 : "Driver implementation issue: m_pSelf not set !");
10212 0 : return nullptr;
10213 : }
10214 294 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10215 294 : papszOptions);
10216 : }
10217 :
10218 : /************************************************************************/
10219 : /* GetStatistics() */
10220 : /************************************************************************/
10221 :
10222 : /**
10223 : * \brief Fetch statistics.
10224 : *
10225 : * Returns the minimum, maximum, mean and standard deviation of all
10226 : * pixel values in this array.
10227 : *
10228 : * If bForce is FALSE results will only be returned if it can be done
10229 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10230 : * results cannot be returned efficiently, the method will return CE_Warning
10231 : * but no warning will have been issued. This is a non-standard use of
10232 : * the CE_Warning return value to indicate "nothing done".
10233 : *
10234 : * When cached statistics are not available, and bForce is TRUE,
10235 : * ComputeStatistics() is called.
10236 : *
10237 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10238 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10239 : * after the first request.
10240 : *
10241 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10242 : *
10243 : * This method is the same as the C function GDALMDArrayGetStatistics().
10244 : *
10245 : * @param bApproxOK Currently ignored. In the future, should be set to true
10246 : * if statistics on the whole array are wished, or to false if a subset of it
10247 : * may be used.
10248 : *
10249 : * @param bForce If false statistics will only be returned if it can
10250 : * be done without rescanning the image.
10251 : *
10252 : * @param pdfMin Location into which to load image minimum (may be NULL).
10253 : *
10254 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10255 : *
10256 : * @param pdfMean Location into which to load image mean (may be NULL).
10257 : *
10258 : * @param pdfStdDev Location into which to load image standard deviation
10259 : * (may be NULL).
10260 : *
10261 : * @param pnValidCount Number of samples whose value is different from the
10262 : * nodata value. (may be NULL)
10263 : *
10264 : * @param pfnProgress a function to call to report progress, or NULL.
10265 : *
10266 : * @param pProgressData application data to pass to the progress function.
10267 : *
10268 : * @return CE_None on success, CE_Warning if no values returned,
10269 : * CE_Failure if an error occurs.
10270 : *
10271 : * @since GDAL 3.2
10272 : */
10273 :
10274 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10275 : double *pdfMax, double *pdfMean,
10276 : double *pdfStdDev, GUInt64 *pnValidCount,
10277 : GDALProgressFunc pfnProgress,
10278 : void *pProgressData)
10279 : {
10280 10 : if (!bForce)
10281 1 : return CE_Warning;
10282 :
10283 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10284 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10285 9 : ? CE_None
10286 9 : : CE_Failure;
10287 : }
10288 :
10289 : /************************************************************************/
10290 : /* ComputeStatistics() */
10291 : /************************************************************************/
10292 :
10293 : /**
10294 : * \brief Compute statistics.
10295 : *
10296 : * Returns the minimum, maximum, mean and standard deviation of all
10297 : * pixel values in this array.
10298 : *
10299 : * Pixels taken into account in statistics are those whose mask value
10300 : * (as determined by GetMask()) is non-zero.
10301 : *
10302 : * Once computed, the statistics will generally be "set" back on the
10303 : * owing dataset.
10304 : *
10305 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10306 : *
10307 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10308 : * and GDALMDArrayComputeStatisticsEx().
10309 : *
10310 : * @param bApproxOK Currently ignored. In the future, should be set to true
10311 : * if statistics on the whole array are wished, or to false if a subset of it
10312 : * may be used.
10313 : *
10314 : * @param pdfMin Location into which to load image minimum (may be NULL).
10315 : *
10316 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10317 : *
10318 : * @param pdfMean Location into which to load image mean (may be NULL).
10319 : *
10320 : * @param pdfStdDev Location into which to load image standard deviation
10321 : * (may be NULL).
10322 : *
10323 : * @param pnValidCount Number of samples whose value is different from the
10324 : * nodata value. (may be NULL)
10325 : *
10326 : * @param pfnProgress a function to call to report progress, or NULL.
10327 : *
10328 : * @param pProgressData application data to pass to the progress function.
10329 : *
10330 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10331 : * Options are driver specific. For now the netCDF and Zarr
10332 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10333 : * to add or update the actual_range attribute with the
10334 : * computed min/max, only if done on the full array, in non
10335 : * approximate mode, and the dataset is opened in update
10336 : * mode.
10337 : *
10338 : * @return true on success
10339 : *
10340 : * @since GDAL 3.2
10341 : */
10342 :
10343 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10344 : double *pdfMax, double *pdfMean,
10345 : double *pdfStdDev, GUInt64 *pnValidCount,
10346 : GDALProgressFunc pfnProgress,
10347 : void *pProgressData,
10348 : CSLConstList papszOptions)
10349 : {
10350 : struct StatsPerChunkType
10351 : {
10352 : const GDALMDArray *array = nullptr;
10353 : std::shared_ptr<GDALMDArray> poMask{};
10354 : double dfMin = cpl::NumericLimits<double>::max();
10355 : double dfMax = -cpl::NumericLimits<double>::max();
10356 : double dfMean = 0.0;
10357 : double dfM2 = 0.0;
10358 : GUInt64 nValidCount = 0;
10359 : std::vector<GByte> abyData{};
10360 : std::vector<double> adfData{};
10361 : std::vector<GByte> abyMaskData{};
10362 : GDALProgressFunc pfnProgress = nullptr;
10363 : void *pProgressData = nullptr;
10364 : };
10365 :
10366 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10367 : const GUInt64 *chunkArrayStartIdx,
10368 : const size_t *chunkCount, GUInt64 iCurChunk,
10369 : GUInt64 nChunkCount, void *pUserData)
10370 : {
10371 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10372 13 : const GDALMDArray *array = data->array;
10373 13 : const GDALMDArray *poMask = data->poMask.get();
10374 13 : const size_t nDims = array->GetDimensionCount();
10375 13 : size_t nVals = 1;
10376 34 : for (size_t i = 0; i < nDims; i++)
10377 21 : nVals *= chunkCount[i];
10378 :
10379 : // Get mask
10380 13 : data->abyMaskData.resize(nVals);
10381 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10382 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10383 : {
10384 0 : return false;
10385 : }
10386 :
10387 : // Get data
10388 13 : const auto &oType = array->GetDataType();
10389 13 : if (oType.GetNumericDataType() == GDT_Float64)
10390 : {
10391 6 : data->adfData.resize(nVals);
10392 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10393 6 : oType, &data->adfData[0]))
10394 : {
10395 0 : return false;
10396 : }
10397 : }
10398 : else
10399 : {
10400 7 : data->abyData.resize(nVals * oType.GetSize());
10401 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10402 7 : oType, &data->abyData[0]))
10403 : {
10404 0 : return false;
10405 : }
10406 7 : data->adfData.resize(nVals);
10407 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10408 7 : static_cast<int>(oType.GetSize()),
10409 7 : &data->adfData[0], GDT_Float64,
10410 : static_cast<int>(sizeof(double)),
10411 : static_cast<GPtrDiff_t>(nVals));
10412 : }
10413 912 : for (size_t i = 0; i < nVals; i++)
10414 : {
10415 899 : if (data->abyMaskData[i])
10416 : {
10417 894 : const double dfValue = data->adfData[i];
10418 894 : data->dfMin = std::min(data->dfMin, dfValue);
10419 894 : data->dfMax = std::max(data->dfMax, dfValue);
10420 894 : data->nValidCount++;
10421 894 : const double dfDelta = dfValue - data->dfMean;
10422 894 : data->dfMean += dfDelta / data->nValidCount;
10423 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10424 : }
10425 : }
10426 13 : if (data->pfnProgress &&
10427 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10428 : "", data->pProgressData))
10429 : {
10430 0 : return false;
10431 : }
10432 13 : return true;
10433 : };
10434 :
10435 13 : const auto &oType = GetDataType();
10436 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10437 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10438 : {
10439 0 : CPLError(
10440 : CE_Failure, CPLE_NotSupported,
10441 : "Statistics can only be computed on non-complex numeric data type");
10442 0 : return false;
10443 : }
10444 :
10445 13 : const size_t nDims = GetDimensionCount();
10446 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10447 26 : std::vector<GUInt64> count(nDims);
10448 13 : const auto &poDims = GetDimensions();
10449 34 : for (size_t i = 0; i < nDims; i++)
10450 : {
10451 21 : count[i] = poDims[i]->GetSize();
10452 : }
10453 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10454 : const size_t nMaxChunkSize =
10455 : pszSwathSize
10456 13 : ? static_cast<size_t>(
10457 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10458 0 : CPLAtoGIntBig(pszSwathSize)))
10459 : : static_cast<size_t>(
10460 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10461 13 : GDALGetCacheMax64() / 4));
10462 26 : StatsPerChunkType sData;
10463 13 : sData.array = this;
10464 13 : sData.poMask = GetMask(nullptr);
10465 13 : if (sData.poMask == nullptr)
10466 : {
10467 0 : return false;
10468 : }
10469 13 : sData.pfnProgress = pfnProgress;
10470 13 : sData.pProgressData = pProgressData;
10471 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10472 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10473 13 : PerChunkFunc, &sData))
10474 : {
10475 0 : return false;
10476 : }
10477 :
10478 13 : if (pdfMin)
10479 13 : *pdfMin = sData.dfMin;
10480 :
10481 13 : if (pdfMax)
10482 13 : *pdfMax = sData.dfMax;
10483 :
10484 13 : if (pdfMean)
10485 11 : *pdfMean = sData.dfMean;
10486 :
10487 : const double dfStdDev =
10488 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10489 13 : if (pdfStdDev)
10490 11 : *pdfStdDev = dfStdDev;
10491 :
10492 13 : if (pnValidCount)
10493 11 : *pnValidCount = sData.nValidCount;
10494 :
10495 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10496 13 : sData.nValidCount, papszOptions);
10497 :
10498 13 : return true;
10499 : }
10500 :
10501 : /************************************************************************/
10502 : /* SetStatistics() */
10503 : /************************************************************************/
10504 : //! @cond Doxygen_Suppress
10505 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10506 : double /* dfMax */, double /* dfMean */,
10507 : double /* dfStdDev */,
10508 : GUInt64 /* nValidCount */,
10509 : CSLConstList /* papszOptions */)
10510 : {
10511 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10512 5 : return false;
10513 : }
10514 :
10515 : //! @endcond
10516 :
10517 : /************************************************************************/
10518 : /* ClearStatistics() */
10519 : /************************************************************************/
10520 :
10521 : /**
10522 : * \brief Clear statistics.
10523 : *
10524 : * @since GDAL 3.4
10525 : */
10526 0 : void GDALMDArray::ClearStatistics()
10527 : {
10528 0 : }
10529 :
10530 : /************************************************************************/
10531 : /* GetCoordinateVariables() */
10532 : /************************************************************************/
10533 :
10534 : /**
10535 : * \brief Return coordinate variables.
10536 : *
10537 : * Coordinate variables are an alternate way of indexing an array that can
10538 : * be sometimes used. For example, an array collected through remote sensing
10539 : * might be indexed by (scanline, pixel). But there can be
10540 : * a longitude and latitude arrays alongside that are also both indexed by
10541 : * (scanline, pixel), and are referenced from operational arrays for
10542 : * reprojection purposes.
10543 : *
10544 : * For netCDF, this will return the arrays referenced by the "coordinates"
10545 : * attribute.
10546 : *
10547 : * This method is the same as the C function
10548 : * GDALMDArrayGetCoordinateVariables().
10549 : *
10550 : * @return a vector of arrays
10551 : *
10552 : * @since GDAL 3.4
10553 : */
10554 :
10555 : std::vector<std::shared_ptr<GDALMDArray>>
10556 13 : GDALMDArray::GetCoordinateVariables() const
10557 : {
10558 13 : return {};
10559 : }
10560 :
10561 : /************************************************************************/
10562 : /* ~GDALExtendedDataType() */
10563 : /************************************************************************/
10564 :
10565 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10566 :
10567 : /************************************************************************/
10568 : /* GDALExtendedDataType() */
10569 : /************************************************************************/
10570 :
10571 14858 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10572 14858 : GDALExtendedDataTypeSubType eSubType)
10573 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10574 14858 : m_nMaxStringLength(nMaxStringLength)
10575 : {
10576 14858 : }
10577 :
10578 : /************************************************************************/
10579 : /* GDALExtendedDataType() */
10580 : /************************************************************************/
10581 :
10582 64947 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10583 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10584 64947 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10585 : {
10586 64947 : }
10587 :
10588 : /************************************************************************/
10589 : /* GDALExtendedDataType() */
10590 : /************************************************************************/
10591 :
10592 63 : GDALExtendedDataType::GDALExtendedDataType(
10593 : const std::string &osName, GDALDataType eBaseType,
10594 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10595 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10596 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10597 : {
10598 63 : }
10599 :
10600 : /************************************************************************/
10601 : /* GDALExtendedDataType() */
10602 : /************************************************************************/
10603 :
10604 915 : GDALExtendedDataType::GDALExtendedDataType(
10605 : const std::string &osName, size_t nTotalSize,
10606 915 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10607 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10608 915 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10609 : {
10610 915 : }
10611 :
10612 : /************************************************************************/
10613 : /* GDALExtendedDataType() */
10614 : /************************************************************************/
10615 :
10616 : /** Move constructor. */
10617 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10618 :
10619 : /************************************************************************/
10620 : /* GDALExtendedDataType() */
10621 : /************************************************************************/
10622 :
10623 : /** Copy constructor. */
10624 17102 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10625 34204 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10626 17102 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10627 17102 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10628 17102 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10629 : {
10630 17102 : if (m_eClass == GEDTC_COMPOUND)
10631 : {
10632 481 : for (const auto &elt : other.m_aoComponents)
10633 : {
10634 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10635 : }
10636 : }
10637 17102 : }
10638 :
10639 : /************************************************************************/
10640 : /* operator= () */
10641 : /************************************************************************/
10642 :
10643 : /** Copy assignment. */
10644 : GDALExtendedDataType &
10645 1088 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10646 : {
10647 1088 : if (this != &other)
10648 : {
10649 1088 : m_osName = other.m_osName;
10650 1088 : m_eClass = other.m_eClass;
10651 1088 : m_eSubType = other.m_eSubType;
10652 1088 : m_eNumericDT = other.m_eNumericDT;
10653 1088 : m_nSize = other.m_nSize;
10654 1088 : m_nMaxStringLength = other.m_nMaxStringLength;
10655 1088 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10656 1088 : m_aoComponents.clear();
10657 1088 : if (m_eClass == GEDTC_COMPOUND)
10658 : {
10659 0 : for (const auto &elt : other.m_aoComponents)
10660 : {
10661 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10662 : }
10663 : }
10664 : }
10665 1088 : return *this;
10666 : }
10667 :
10668 : /************************************************************************/
10669 : /* operator= () */
10670 : /************************************************************************/
10671 :
10672 : /** Move assignment. */
10673 : GDALExtendedDataType &
10674 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10675 :
10676 : /************************************************************************/
10677 : /* Create() */
10678 : /************************************************************************/
10679 :
10680 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10681 : *
10682 : * This is the same as the C function GDALExtendedDataTypeCreate()
10683 : *
10684 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10685 : * GDT_TypeCount
10686 : */
10687 64940 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10688 : {
10689 64940 : return GDALExtendedDataType(eType);
10690 : }
10691 :
10692 : /************************************************************************/
10693 : /* Create() */
10694 : /************************************************************************/
10695 :
10696 : /** Return a new GDALExtendedDataType from a raster attribute table.
10697 : *
10698 : * @param osName Type name
10699 : * @param eBaseType Base integer data type.
10700 : * @param poRAT Raster attribute table. Must not be NULL.
10701 : * @since 3.12
10702 : */
10703 : GDALExtendedDataType
10704 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10705 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10706 : {
10707 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10708 : }
10709 :
10710 : /************************************************************************/
10711 : /* Create() */
10712 : /************************************************************************/
10713 :
10714 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10715 : *
10716 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10717 : *
10718 : * @param osName Type name.
10719 : * @param nTotalSize Total size of the type in bytes.
10720 : * Should be large enough to store all components.
10721 : * @param components Components of the compound type.
10722 : */
10723 922 : GDALExtendedDataType GDALExtendedDataType::Create(
10724 : const std::string &osName, size_t nTotalSize,
10725 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10726 : {
10727 922 : size_t nLastOffset = 0;
10728 : // Some arbitrary threshold to avoid potential integer overflows
10729 922 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10730 : {
10731 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10732 2 : return GDALExtendedDataType(GDT_Unknown);
10733 : }
10734 4306 : for (const auto &comp : components)
10735 : {
10736 : // Check alignment too ?
10737 3387 : if (comp->GetOffset() < nLastOffset)
10738 : {
10739 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10740 1 : return GDALExtendedDataType(GDT_Unknown);
10741 : }
10742 3386 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10743 : }
10744 919 : if (nTotalSize < nLastOffset)
10745 : {
10746 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10747 1 : return GDALExtendedDataType(GDT_Unknown);
10748 : }
10749 918 : if (nTotalSize == 0 || components.empty())
10750 : {
10751 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10752 3 : return GDALExtendedDataType(GDT_Unknown);
10753 : }
10754 915 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10755 : }
10756 :
10757 : /************************************************************************/
10758 : /* Create() */
10759 : /************************************************************************/
10760 :
10761 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10762 : *
10763 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10764 : *
10765 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10766 : * unknown/unlimited
10767 : * @param eSubType Subtype.
10768 : */
10769 : GDALExtendedDataType
10770 14858 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10771 : GDALExtendedDataTypeSubType eSubType)
10772 : {
10773 14858 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10774 : }
10775 :
10776 : /************************************************************************/
10777 : /* operator==() */
10778 : /************************************************************************/
10779 :
10780 : /** Equality operator.
10781 : *
10782 : * This is the same as the C function GDALExtendedDataTypeEquals().
10783 : */
10784 3378 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10785 : {
10786 3351 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10787 6729 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10788 : {
10789 324 : return false;
10790 : }
10791 3054 : if (m_eClass == GEDTC_NUMERIC)
10792 : {
10793 940 : return m_eNumericDT == other.m_eNumericDT;
10794 : }
10795 2114 : if (m_eClass == GEDTC_STRING)
10796 : {
10797 1867 : return true;
10798 : }
10799 247 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10800 247 : if (m_aoComponents.size() != other.m_aoComponents.size())
10801 : {
10802 2 : return false;
10803 : }
10804 1232 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10805 : {
10806 987 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10807 : {
10808 0 : return false;
10809 : }
10810 : }
10811 245 : return true;
10812 : }
10813 :
10814 : /************************************************************************/
10815 : /* CanConvertTo() */
10816 : /************************************************************************/
10817 :
10818 : /** Return whether this data type can be converted to the other one.
10819 : *
10820 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10821 : *
10822 : * @param other Target data type for the conversion being considered.
10823 : */
10824 10938 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10825 : {
10826 10938 : if (m_eClass == GEDTC_NUMERIC)
10827 : {
10828 7340 : if (m_eNumericDT == GDT_Unknown)
10829 0 : return false;
10830 7340 : if (other.m_eClass == GEDTC_NUMERIC &&
10831 7129 : other.m_eNumericDT == GDT_Unknown)
10832 0 : return false;
10833 7551 : return other.m_eClass == GEDTC_NUMERIC ||
10834 7551 : other.m_eClass == GEDTC_STRING;
10835 : }
10836 3598 : if (m_eClass == GEDTC_STRING)
10837 : {
10838 3377 : return other.m_eClass == m_eClass;
10839 : }
10840 221 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10841 221 : if (other.m_eClass != GEDTC_COMPOUND)
10842 0 : return false;
10843 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10844 442 : srcComponents;
10845 1052 : for (const auto &srcComp : m_aoComponents)
10846 : {
10847 831 : srcComponents[srcComp->GetName()] = &srcComp;
10848 : }
10849 583 : for (const auto &dstComp : other.m_aoComponents)
10850 : {
10851 363 : auto oIter = srcComponents.find(dstComp->GetName());
10852 363 : if (oIter == srcComponents.end())
10853 1 : return false;
10854 362 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10855 0 : return false;
10856 : }
10857 220 : return true;
10858 : }
10859 :
10860 : /************************************************************************/
10861 : /* NeedsFreeDynamicMemory() */
10862 : /************************************************************************/
10863 :
10864 : /** Return whether the data type holds dynamically allocated memory, that
10865 : * needs to be freed with FreeDynamicMemory().
10866 : *
10867 : */
10868 4035 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10869 : {
10870 4035 : switch (m_eClass)
10871 : {
10872 1079 : case GEDTC_STRING:
10873 1079 : return true;
10874 :
10875 2833 : case GEDTC_NUMERIC:
10876 2833 : return false;
10877 :
10878 123 : case GEDTC_COMPOUND:
10879 : {
10880 244 : for (const auto &comp : m_aoComponents)
10881 : {
10882 222 : if (comp->GetType().NeedsFreeDynamicMemory())
10883 101 : return true;
10884 : }
10885 : }
10886 : }
10887 22 : return false;
10888 : }
10889 :
10890 : /************************************************************************/
10891 : /* FreeDynamicMemory() */
10892 : /************************************************************************/
10893 :
10894 : /** Release the dynamic memory (strings typically) from a raw value.
10895 : *
10896 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10897 : *
10898 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10899 : */
10900 4194 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10901 : {
10902 4194 : switch (m_eClass)
10903 : {
10904 3017 : case GEDTC_STRING:
10905 : {
10906 : char *pszStr;
10907 3017 : memcpy(&pszStr, pBuffer, sizeof(char *));
10908 3017 : if (pszStr)
10909 : {
10910 2419 : VSIFree(pszStr);
10911 : }
10912 3017 : break;
10913 : }
10914 :
10915 988 : case GEDTC_NUMERIC:
10916 : {
10917 988 : break;
10918 : }
10919 :
10920 189 : case GEDTC_COMPOUND:
10921 : {
10922 189 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10923 999 : for (const auto &comp : m_aoComponents)
10924 : {
10925 1620 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10926 810 : comp->GetOffset());
10927 : }
10928 189 : break;
10929 : }
10930 : }
10931 4194 : }
10932 :
10933 : /************************************************************************/
10934 : /* ~GDALEDTComponent() */
10935 : /************************************************************************/
10936 :
10937 : GDALEDTComponent::~GDALEDTComponent() = default;
10938 :
10939 : /************************************************************************/
10940 : /* GDALEDTComponent() */
10941 : /************************************************************************/
10942 :
10943 : /** constructor of a GDALEDTComponent
10944 : *
10945 : * This is the same as the C function GDALEDTComponendCreate()
10946 : *
10947 : * @param name Component name
10948 : * @param offset Offset in byte of the component in the compound data type.
10949 : * In case of nesting of compound data type, this should be
10950 : * the offset to the immediate belonging data type, not to the
10951 : * higher level one.
10952 : * @param type Component data type.
10953 : */
10954 3378 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10955 3378 : const GDALExtendedDataType &type)
10956 3378 : : m_osName(name), m_nOffset(offset), m_oType(type)
10957 : {
10958 3378 : }
10959 :
10960 : /************************************************************************/
10961 : /* GDALEDTComponent() */
10962 : /************************************************************************/
10963 :
10964 : /** Copy constructor. */
10965 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10966 :
10967 : /************************************************************************/
10968 : /* operator==() */
10969 : /************************************************************************/
10970 :
10971 : /** Equality operator.
10972 : */
10973 987 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10974 : {
10975 1974 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10976 1974 : m_oType == other.m_oType;
10977 : }
10978 :
10979 : /************************************************************************/
10980 : /* ~GDALDimension() */
10981 : /************************************************************************/
10982 :
10983 : GDALDimension::~GDALDimension() = default;
10984 :
10985 : /************************************************************************/
10986 : /* GDALDimension() */
10987 : /************************************************************************/
10988 :
10989 : //! @cond Doxygen_Suppress
10990 : /** Constructor.
10991 : *
10992 : * @param osParentName Parent name
10993 : * @param osName name
10994 : * @param osType type. See GetType().
10995 : * @param osDirection direction. See GetDirection().
10996 : * @param nSize size.
10997 : */
10998 9474 : GDALDimension::GDALDimension(const std::string &osParentName,
10999 : const std::string &osName,
11000 : const std::string &osType,
11001 9474 : const std::string &osDirection, GUInt64 nSize)
11002 : : m_osName(osName),
11003 : m_osFullName(
11004 9474 : !osParentName.empty()
11005 14051 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
11006 : : osName),
11007 32999 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
11008 : {
11009 9474 : }
11010 :
11011 : //! @endcond
11012 :
11013 : /************************************************************************/
11014 : /* GetIndexingVariable() */
11015 : /************************************************************************/
11016 :
11017 : /** Return the variable that is used to index the dimension (if there is one).
11018 : *
11019 : * This is the array, typically one-dimensional, describing the values taken
11020 : * by the dimension.
11021 : */
11022 43 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11023 : {
11024 43 : return nullptr;
11025 : }
11026 :
11027 : /************************************************************************/
11028 : /* SetIndexingVariable() */
11029 : /************************************************************************/
11030 :
11031 : /** Set the variable that is used to index the dimension.
11032 : *
11033 : * This is the array, typically one-dimensional, describing the values taken
11034 : * by the dimension.
11035 : *
11036 : * Optionally implemented by drivers.
11037 : *
11038 : * Drivers known to implement it: MEM.
11039 : *
11040 : * @param poArray Variable to use to index the dimension.
11041 : * @return true in case of success.
11042 : */
11043 11 : bool GDALDimension::SetIndexingVariable(
11044 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11045 : {
11046 11 : CPLError(CE_Failure, CPLE_NotSupported,
11047 : "SetIndexingVariable() not implemented");
11048 11 : return false;
11049 : }
11050 :
11051 : /************************************************************************/
11052 : /* Rename() */
11053 : /************************************************************************/
11054 :
11055 : /** Rename the dimension.
11056 : *
11057 : * This is not implemented by all drivers.
11058 : *
11059 : * Drivers known to implement it: MEM, netCDF, ZARR.
11060 : *
11061 : * This is the same as the C function GDALDimensionRename().
11062 : *
11063 : * @param osNewName New name.
11064 : *
11065 : * @return true in case of success
11066 : * @since GDAL 3.8
11067 : */
11068 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11069 : {
11070 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11071 0 : return false;
11072 : }
11073 :
11074 : /************************************************************************/
11075 : /* BaseRename() */
11076 : /************************************************************************/
11077 :
11078 : //! @cond Doxygen_Suppress
11079 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11080 : {
11081 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11082 8 : m_osFullName += osNewName;
11083 8 : m_osName = osNewName;
11084 8 : }
11085 :
11086 : //! @endcond
11087 :
11088 : //! @cond Doxygen_Suppress
11089 : /************************************************************************/
11090 : /* ParentRenamed() */
11091 : /************************************************************************/
11092 :
11093 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11094 : {
11095 8 : m_osFullName = osNewParentFullName;
11096 8 : m_osFullName += "/";
11097 8 : m_osFullName += m_osName;
11098 8 : }
11099 :
11100 : //! @endcond
11101 :
11102 : //! @cond Doxygen_Suppress
11103 : /************************************************************************/
11104 : /* ParentDeleted() */
11105 : /************************************************************************/
11106 :
11107 4 : void GDALDimension::ParentDeleted()
11108 : {
11109 4 : }
11110 :
11111 : //! @endcond
11112 :
11113 : /************************************************************************/
11114 : /************************************************************************/
11115 : /************************************************************************/
11116 : /* C API */
11117 : /************************************************************************/
11118 : /************************************************************************/
11119 : /************************************************************************/
11120 :
11121 : /************************************************************************/
11122 : /* GDALExtendedDataTypeCreate() */
11123 : /************************************************************************/
11124 :
11125 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11126 : *
11127 : * This is the same as the C++ method GDALExtendedDataType::Create()
11128 : *
11129 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11130 : *
11131 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11132 : * GDT_TypeCount
11133 : *
11134 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11135 : */
11136 2148 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11137 : {
11138 2148 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11139 : {
11140 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11141 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11142 0 : return nullptr;
11143 : }
11144 : return new GDALExtendedDataTypeHS(
11145 2148 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11146 : }
11147 :
11148 : /************************************************************************/
11149 : /* GDALExtendedDataTypeCreateString() */
11150 : /************************************************************************/
11151 :
11152 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11153 : *
11154 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11155 : *
11156 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11157 : *
11158 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11159 : */
11160 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11161 : {
11162 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11163 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11164 : }
11165 :
11166 : /************************************************************************/
11167 : /* GDALExtendedDataTypeCreateStringEx() */
11168 : /************************************************************************/
11169 :
11170 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11171 : *
11172 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11173 : *
11174 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11175 : *
11176 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11177 : * @since GDAL 3.4
11178 : */
11179 : GDALExtendedDataTypeH
11180 222 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11181 : GDALExtendedDataTypeSubType eSubType)
11182 : {
11183 222 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11184 222 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11185 : }
11186 :
11187 : /************************************************************************/
11188 : /* GDALExtendedDataTypeCreateCompound() */
11189 : /************************************************************************/
11190 :
11191 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11192 : *
11193 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11194 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11195 : *
11196 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11197 : *
11198 : * @param pszName Type name.
11199 : * @param nTotalSize Total size of the type in bytes.
11200 : * Should be large enough to store all components.
11201 : * @param nComponents Number of components in comps array.
11202 : * @param comps Components.
11203 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11204 : */
11205 : GDALExtendedDataTypeH
11206 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11207 : size_t nComponents,
11208 : const GDALEDTComponentH *comps)
11209 : {
11210 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11211 54 : for (size_t i = 0; i < nComponents; i++)
11212 : {
11213 : compsCpp.emplace_back(
11214 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11215 : }
11216 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11217 66 : std::move(compsCpp));
11218 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11219 6 : return nullptr;
11220 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11221 : }
11222 :
11223 : /************************************************************************/
11224 : /* GDALExtendedDataTypeRelease() */
11225 : /************************************************************************/
11226 :
11227 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11228 : *
11229 : * Note: when applied on a object coming from a driver, this does not
11230 : * destroy the object in the file, database, etc...
11231 : */
11232 7081 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11233 : {
11234 7081 : delete hEDT;
11235 7081 : }
11236 :
11237 : /************************************************************************/
11238 : /* GDALExtendedDataTypeGetName() */
11239 : /************************************************************************/
11240 :
11241 : /** Return type name.
11242 : *
11243 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11244 : */
11245 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11246 : {
11247 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11248 8 : return hEDT->m_poImpl->GetName().c_str();
11249 : }
11250 :
11251 : /************************************************************************/
11252 : /* GDALExtendedDataTypeGetClass() */
11253 : /************************************************************************/
11254 :
11255 : /** Return type class.
11256 : *
11257 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11258 : */
11259 : GDALExtendedDataTypeClass
11260 9613 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11261 : {
11262 9613 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11263 9613 : return hEDT->m_poImpl->GetClass();
11264 : }
11265 :
11266 : /************************************************************************/
11267 : /* GDALExtendedDataTypeGetNumericDataType() */
11268 : /************************************************************************/
11269 :
11270 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11271 : *
11272 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11273 : */
11274 2142 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11275 : {
11276 2142 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11277 2142 : return hEDT->m_poImpl->GetNumericDataType();
11278 : }
11279 :
11280 : /************************************************************************/
11281 : /* GDALExtendedDataTypeGetSize() */
11282 : /************************************************************************/
11283 :
11284 : /** Return data type size in bytes.
11285 : *
11286 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11287 : */
11288 2668 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11289 : {
11290 2668 : VALIDATE_POINTER1(hEDT, __func__, 0);
11291 2668 : return hEDT->m_poImpl->GetSize();
11292 : }
11293 :
11294 : /************************************************************************/
11295 : /* GDALExtendedDataTypeGetMaxStringLength() */
11296 : /************************************************************************/
11297 :
11298 : /** Return the maximum length of a string in bytes.
11299 : *
11300 : * 0 indicates unknown/unlimited string.
11301 : *
11302 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11303 : */
11304 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11305 : {
11306 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11307 3 : return hEDT->m_poImpl->GetMaxStringLength();
11308 : }
11309 :
11310 : /************************************************************************/
11311 : /* GDALExtendedDataTypeCanConvertTo() */
11312 : /************************************************************************/
11313 :
11314 : /** Return whether this data type can be converted to the other one.
11315 : *
11316 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11317 : *
11318 : * @param hSourceEDT Source data type for the conversion being considered.
11319 : * @param hTargetEDT Target data type for the conversion being considered.
11320 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11321 : */
11322 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11323 : GDALExtendedDataTypeH hTargetEDT)
11324 : {
11325 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11326 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11327 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11328 : }
11329 :
11330 : /************************************************************************/
11331 : /* GDALExtendedDataTypeEquals() */
11332 : /************************************************************************/
11333 :
11334 : /** Return whether this data type is equal to another one.
11335 : *
11336 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11337 : *
11338 : * @param hFirstEDT First data type.
11339 : * @param hSecondEDT Second data type.
11340 : * @return TRUE if they are equal. FALSE otherwise.
11341 : */
11342 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11343 : GDALExtendedDataTypeH hSecondEDT)
11344 : {
11345 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11346 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11347 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11348 : }
11349 :
11350 : /************************************************************************/
11351 : /* GDALExtendedDataTypeGetSubType() */
11352 : /************************************************************************/
11353 :
11354 : /** Return the subtype of a type.
11355 : *
11356 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11357 : *
11358 : * @param hEDT Data type.
11359 : * @return subtype.
11360 : * @since 3.4
11361 : */
11362 : GDALExtendedDataTypeSubType
11363 105 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11364 : {
11365 105 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11366 105 : return hEDT->m_poImpl->GetSubType();
11367 : }
11368 :
11369 : /************************************************************************/
11370 : /* GDALExtendedDataTypeGetRAT() */
11371 : /************************************************************************/
11372 :
11373 : /** Return associated raster attribute table, when there is one.
11374 : *
11375 : * * For the netCDF driver, the RAT will capture enumerated types, with
11376 : * a "value" column with an integer value and a "name" column with the
11377 : * associated name.
11378 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11379 : *
11380 : * @param hEDT Data type.
11381 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11382 : * @since 3.12
11383 : */
11384 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11385 : {
11386 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11387 1 : return GDALRasterAttributeTable::ToHandle(
11388 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11389 : }
11390 :
11391 : /************************************************************************/
11392 : /* GDALExtendedDataTypeGetComponents() */
11393 : /************************************************************************/
11394 :
11395 : /** Return the components of the data type (only valid when GetClass() ==
11396 : * GEDTC_COMPOUND)
11397 : *
11398 : * The returned array and its content must be freed with
11399 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11400 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11401 : * individual array members).
11402 : *
11403 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11404 : *
11405 : * @param hEDT Data type
11406 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11407 : * @return an array of *pnCount components.
11408 : */
11409 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11410 : size_t *pnCount)
11411 : {
11412 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11413 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11414 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11415 : auto ret = static_cast<GDALEDTComponentH *>(
11416 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11417 131 : for (size_t i = 0; i < components.size(); i++)
11418 : {
11419 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11420 : }
11421 44 : *pnCount = components.size();
11422 44 : return ret;
11423 : }
11424 :
11425 : /************************************************************************/
11426 : /* GDALExtendedDataTypeFreeComponents() */
11427 : /************************************************************************/
11428 :
11429 : /** Free the return of GDALExtendedDataTypeGetComponents().
11430 : *
11431 : * @param components return value of GDALExtendedDataTypeGetComponents()
11432 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11433 : */
11434 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11435 : size_t nCount)
11436 : {
11437 131 : for (size_t i = 0; i < nCount; i++)
11438 : {
11439 87 : delete components[i];
11440 : }
11441 44 : CPLFree(components);
11442 44 : }
11443 :
11444 : /************************************************************************/
11445 : /* GDALEDTComponentCreate() */
11446 : /************************************************************************/
11447 :
11448 : /** Create a new GDALEDTComponent.
11449 : *
11450 : * The returned value must be freed with GDALEDTComponentRelease().
11451 : *
11452 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11453 : */
11454 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11455 : GDALExtendedDataTypeH hType)
11456 : {
11457 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11458 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11459 : return new GDALEDTComponentHS(
11460 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11461 : }
11462 :
11463 : /************************************************************************/
11464 : /* GDALEDTComponentRelease() */
11465 : /************************************************************************/
11466 :
11467 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11468 : *
11469 : * Note: when applied on a object coming from a driver, this does not
11470 : * destroy the object in the file, database, etc...
11471 : */
11472 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11473 : {
11474 61 : delete hComp;
11475 61 : }
11476 :
11477 : /************************************************************************/
11478 : /* GDALEDTComponentGetName() */
11479 : /************************************************************************/
11480 :
11481 : /** Return the name.
11482 : *
11483 : * The returned pointer is valid until hComp is released.
11484 : *
11485 : * This is the same as the C++ method GDALEDTComponent::GetName().
11486 : */
11487 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11488 : {
11489 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11490 33 : return hComp->m_poImpl->GetName().c_str();
11491 : }
11492 :
11493 : /************************************************************************/
11494 : /* GDALEDTComponentGetOffset() */
11495 : /************************************************************************/
11496 :
11497 : /** Return the offset (in bytes) of the component in the compound data type.
11498 : *
11499 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11500 : */
11501 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11502 : {
11503 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11504 31 : return hComp->m_poImpl->GetOffset();
11505 : }
11506 :
11507 : /************************************************************************/
11508 : /* GDALEDTComponentGetType() */
11509 : /************************************************************************/
11510 :
11511 : /** Return the data type of the component.
11512 : *
11513 : * This is the same as the C++ method GDALEDTComponent::GetType().
11514 : */
11515 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11516 : {
11517 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11518 : return new GDALExtendedDataTypeHS(
11519 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11520 : }
11521 :
11522 : /************************************************************************/
11523 : /* GDALGroupRelease() */
11524 : /************************************************************************/
11525 :
11526 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11527 : *
11528 : * Note: when applied on a object coming from a driver, this does not
11529 : * destroy the object in the file, database, etc...
11530 : */
11531 1535 : void GDALGroupRelease(GDALGroupH hGroup)
11532 : {
11533 1535 : delete hGroup;
11534 1535 : }
11535 :
11536 : /************************************************************************/
11537 : /* GDALGroupGetName() */
11538 : /************************************************************************/
11539 :
11540 : /** Return the name of the group.
11541 : *
11542 : * The returned pointer is valid until hGroup is released.
11543 : *
11544 : * This is the same as the C++ method GDALGroup::GetName().
11545 : */
11546 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11547 : {
11548 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11549 95 : return hGroup->m_poImpl->GetName().c_str();
11550 : }
11551 :
11552 : /************************************************************************/
11553 : /* GDALGroupGetFullName() */
11554 : /************************************************************************/
11555 :
11556 : /** Return the full name of the group.
11557 : *
11558 : * The returned pointer is valid until hGroup is released.
11559 : *
11560 : * This is the same as the C++ method GDALGroup::GetFullName().
11561 : */
11562 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11563 : {
11564 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11565 47 : return hGroup->m_poImpl->GetFullName().c_str();
11566 : }
11567 :
11568 : /************************************************************************/
11569 : /* GDALGroupGetMDArrayNames() */
11570 : /************************************************************************/
11571 :
11572 : /** Return the list of multidimensional array names contained in this group.
11573 : *
11574 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11575 : *
11576 : * @return the array names, to be freed with CSLDestroy()
11577 : */
11578 337 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11579 : {
11580 337 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11581 674 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11582 674 : CPLStringList res;
11583 851 : for (const auto &name : names)
11584 : {
11585 514 : res.AddString(name.c_str());
11586 : }
11587 337 : return res.StealList();
11588 : }
11589 :
11590 : /************************************************************************/
11591 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11592 : /************************************************************************/
11593 :
11594 : /** Return the list of multidimensional array full names contained in this
11595 : * group and its subgroups.
11596 : *
11597 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11598 : *
11599 : * @return the array names, to be freed with CSLDestroy()
11600 : *
11601 : * @since 3.11
11602 : */
11603 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11604 : CSLConstList papszGroupOptions,
11605 : CSLConstList papszArrayOptions)
11606 : {
11607 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11608 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11609 2 : papszGroupOptions, papszArrayOptions);
11610 2 : CPLStringList res;
11611 5 : for (const auto &name : names)
11612 : {
11613 4 : res.AddString(name.c_str());
11614 : }
11615 1 : return res.StealList();
11616 : }
11617 :
11618 : /************************************************************************/
11619 : /* GDALGroupOpenMDArray() */
11620 : /************************************************************************/
11621 :
11622 : /** Open and return a multidimensional array.
11623 : *
11624 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11625 : *
11626 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11627 : */
11628 835 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11629 : CSLConstList papszOptions)
11630 : {
11631 835 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11632 835 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11633 2505 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11634 2505 : papszOptions);
11635 835 : if (!array)
11636 30 : return nullptr;
11637 805 : return new GDALMDArrayHS(array);
11638 : }
11639 :
11640 : /************************************************************************/
11641 : /* GDALGroupOpenMDArrayFromFullname() */
11642 : /************************************************************************/
11643 :
11644 : /** Open and return a multidimensional array from its fully qualified name.
11645 : *
11646 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11647 : *
11648 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11649 : *
11650 : * @since GDAL 3.2
11651 : */
11652 18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11653 : const char *pszFullname,
11654 : CSLConstList papszOptions)
11655 : {
11656 18 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11657 18 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11658 18 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11659 54 : std::string(pszFullname), papszOptions);
11660 18 : if (!array)
11661 2 : return nullptr;
11662 16 : return new GDALMDArrayHS(array);
11663 : }
11664 :
11665 : /************************************************************************/
11666 : /* GDALGroupResolveMDArray() */
11667 : /************************************************************************/
11668 :
11669 : /** Locate an array in a group and its subgroups by name.
11670 : *
11671 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11672 : * @since GDAL 3.2
11673 : */
11674 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11675 : const char *pszStartingPoint,
11676 : CSLConstList papszOptions)
11677 : {
11678 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11679 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11680 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11681 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11682 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11683 19 : if (!array)
11684 2 : return nullptr;
11685 17 : return new GDALMDArrayHS(array);
11686 : }
11687 :
11688 : /************************************************************************/
11689 : /* GDALGroupGetGroupNames() */
11690 : /************************************************************************/
11691 :
11692 : /** Return the list of sub-groups contained in this group.
11693 : *
11694 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11695 : *
11696 : * @return the group names, to be freed with CSLDestroy()
11697 : */
11698 98 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11699 : {
11700 98 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11701 196 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11702 196 : CPLStringList res;
11703 221 : for (const auto &name : names)
11704 : {
11705 123 : res.AddString(name.c_str());
11706 : }
11707 98 : return res.StealList();
11708 : }
11709 :
11710 : /************************************************************************/
11711 : /* GDALGroupOpenGroup() */
11712 : /************************************************************************/
11713 :
11714 : /** Open and return a sub-group.
11715 : *
11716 : * This is the same as the C++ method GDALGroup::OpenGroup().
11717 : *
11718 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11719 : */
11720 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11721 : CSLConstList papszOptions)
11722 : {
11723 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11724 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11725 : auto subGroup =
11726 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11727 163 : if (!subGroup)
11728 30 : return nullptr;
11729 133 : return new GDALGroupHS(subGroup);
11730 : }
11731 :
11732 : /************************************************************************/
11733 : /* GDALGroupGetVectorLayerNames() */
11734 : /************************************************************************/
11735 :
11736 : /** Return the list of layer names contained in this group.
11737 : *
11738 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11739 : *
11740 : * @return the group names, to be freed with CSLDestroy()
11741 : * @since 3.4
11742 : */
11743 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11744 : CSLConstList papszOptions)
11745 : {
11746 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11747 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11748 16 : CPLStringList res;
11749 18 : for (const auto &name : names)
11750 : {
11751 10 : res.AddString(name.c_str());
11752 : }
11753 8 : return res.StealList();
11754 : }
11755 :
11756 : /************************************************************************/
11757 : /* GDALGroupOpenVectorLayer() */
11758 : /************************************************************************/
11759 :
11760 : /** Open and return a vector layer.
11761 : *
11762 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11763 : *
11764 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11765 : * the returned handled if only valid while the parent GDALDatasetH is kept
11766 : * opened.
11767 : *
11768 : * @return the vector layer, or nullptr.
11769 : * @since 3.4
11770 : */
11771 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11772 : const char *pszVectorLayerName,
11773 : CSLConstList papszOptions)
11774 : {
11775 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11776 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11777 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11778 24 : std::string(pszVectorLayerName), papszOptions));
11779 : }
11780 :
11781 : /************************************************************************/
11782 : /* GDALGroupOpenMDArrayFromFullname() */
11783 : /************************************************************************/
11784 :
11785 : /** Open and return a sub-group from its fully qualified name.
11786 : *
11787 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11788 : *
11789 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11790 : *
11791 : * @since GDAL 3.2
11792 : */
11793 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11794 : const char *pszFullname,
11795 : CSLConstList papszOptions)
11796 : {
11797 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11798 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11799 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11800 9 : std::string(pszFullname), papszOptions);
11801 3 : if (!subGroup)
11802 2 : return nullptr;
11803 1 : return new GDALGroupHS(subGroup);
11804 : }
11805 :
11806 : /************************************************************************/
11807 : /* GDALGroupGetDimensions() */
11808 : /************************************************************************/
11809 :
11810 : /** Return the list of dimensions contained in this group and used by its
11811 : * arrays.
11812 : *
11813 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11814 : * array itself needs to be freed, CPLFree() should be called (and
11815 : * GDALDimensionRelease() on individual array members).
11816 : *
11817 : * This is the same as the C++ method GDALGroup::GetDimensions().
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 dimensions
11822 : * should be retrieved. Pass nullptr for default behavior.
11823 : *
11824 : * @return an array of *pnCount dimensions.
11825 : */
11826 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11827 : CSLConstList papszOptions)
11828 : {
11829 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11830 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11831 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11832 : auto ret = static_cast<GDALDimensionH *>(
11833 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11834 230 : for (size_t i = 0; i < dims.size(); i++)
11835 : {
11836 157 : ret[i] = new GDALDimensionHS(dims[i]);
11837 : }
11838 73 : *pnCount = dims.size();
11839 73 : return ret;
11840 : }
11841 :
11842 : /************************************************************************/
11843 : /* GDALGroupGetAttribute() */
11844 : /************************************************************************/
11845 :
11846 : /** Return an attribute by its name.
11847 : *
11848 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11849 : *
11850 : * The returned attribute must be freed with GDALAttributeRelease().
11851 : */
11852 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11853 : {
11854 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11855 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11856 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11857 80 : if (attr)
11858 76 : return new GDALAttributeHS(attr);
11859 4 : return nullptr;
11860 : }
11861 :
11862 : /************************************************************************/
11863 : /* GDALGroupGetAttributes() */
11864 : /************************************************************************/
11865 :
11866 : /** Return the list of attributes contained in this group.
11867 : *
11868 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11869 : * array itself needs to be freed, CPLFree() should be called (and
11870 : * GDALAttributeRelease() on individual array members).
11871 : *
11872 : * This is the same as the C++ method GDALGroup::GetAttributes().
11873 : *
11874 : * @param hGroup Group.
11875 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11876 : * @param papszOptions Driver specific options determining how attributes
11877 : * should be retrieved. Pass nullptr for default behavior.
11878 : *
11879 : * @return an array of *pnCount attributes.
11880 : */
11881 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11882 : CSLConstList papszOptions)
11883 : {
11884 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11885 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11886 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11887 : auto ret = static_cast<GDALAttributeH *>(
11888 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11889 229 : for (size_t i = 0; i < attrs.size(); i++)
11890 : {
11891 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11892 : }
11893 71 : *pnCount = attrs.size();
11894 71 : return ret;
11895 : }
11896 :
11897 : /************************************************************************/
11898 : /* GDALGroupGetStructuralInfo() */
11899 : /************************************************************************/
11900 :
11901 : /** Return structural information on the group.
11902 : *
11903 : * This may be the compression, etc..
11904 : *
11905 : * The return value should not be freed and is valid until GDALGroup is
11906 : * released or this function called again.
11907 : *
11908 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11909 : */
11910 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11911 : {
11912 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11913 4 : return hGroup->m_poImpl->GetStructuralInfo();
11914 : }
11915 :
11916 : /************************************************************************/
11917 : /* GDALGroupGetDataTypeCount() */
11918 : /************************************************************************/
11919 :
11920 : /** Return the number of data types associated with the group
11921 : * (typically enumerations).
11922 : *
11923 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11924 : *
11925 : * @since 3.12
11926 : */
11927 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11928 : {
11929 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11930 4 : return hGroup->m_poImpl->GetDataTypes().size();
11931 : }
11932 :
11933 : /************************************************************************/
11934 : /* GDALGroupGetDataType() */
11935 : /************************************************************************/
11936 :
11937 : /** Return one of the data types associated with the group.
11938 : *
11939 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11940 : *
11941 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11942 : * or nullptr in case of error.
11943 : * @since 3.12
11944 : */
11945 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11946 : {
11947 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11948 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11949 0 : return nullptr;
11950 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11951 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11952 : }
11953 :
11954 : /************************************************************************/
11955 : /* GDALReleaseAttributes() */
11956 : /************************************************************************/
11957 :
11958 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11959 : *
11960 : * @param attributes return pointer of above methods
11961 : * @param nCount *pnCount value returned by above methods
11962 : */
11963 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11964 : {
11965 418 : for (size_t i = 0; i < nCount; i++)
11966 : {
11967 288 : delete attributes[i];
11968 : }
11969 130 : CPLFree(attributes);
11970 130 : }
11971 :
11972 : /************************************************************************/
11973 : /* GDALGroupCreateGroup() */
11974 : /************************************************************************/
11975 :
11976 : /** Create a sub-group within a group.
11977 : *
11978 : * This is the same as the C++ method GDALGroup::CreateGroup().
11979 : *
11980 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11981 : */
11982 179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11983 : CSLConstList papszOptions)
11984 : {
11985 179 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11986 179 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11987 537 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11988 537 : papszOptions);
11989 179 : if (!ret)
11990 49 : return nullptr;
11991 130 : return new GDALGroupHS(ret);
11992 : }
11993 :
11994 : /************************************************************************/
11995 : /* GDALGroupDeleteGroup() */
11996 : /************************************************************************/
11997 :
11998 : /** Delete a sub-group from a group.
11999 : *
12000 : * After this call, if a previously obtained instance of the deleted object
12001 : * is still alive, no method other than for freeing it should be invoked.
12002 : *
12003 : * This is the same as the C++ method GDALGroup::DeleteGroup().
12004 : *
12005 : * @return true in case of success.
12006 : * @since GDAL 3.8
12007 : */
12008 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12009 : CSLConstList papszOptions)
12010 : {
12011 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12012 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12013 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12014 20 : papszOptions);
12015 : }
12016 :
12017 : /************************************************************************/
12018 : /* GDALGroupCreateDimension() */
12019 : /************************************************************************/
12020 :
12021 : /** Create a dimension within a group.
12022 : *
12023 : * This is the same as the C++ method GDALGroup::CreateDimension().
12024 : *
12025 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12026 : */
12027 769 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12028 : const char *pszType,
12029 : const char *pszDirection, GUInt64 nSize,
12030 : CSLConstList papszOptions)
12031 : {
12032 769 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12033 769 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12034 769 : auto ret = hGroup->m_poImpl->CreateDimension(
12035 1538 : std::string(pszName), std::string(pszType ? pszType : ""),
12036 3076 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12037 769 : if (!ret)
12038 9 : return nullptr;
12039 760 : return new GDALDimensionHS(ret);
12040 : }
12041 :
12042 : /************************************************************************/
12043 : /* GDALGroupCreateMDArray() */
12044 : /************************************************************************/
12045 :
12046 : /** Create a multidimensional array within a group.
12047 : *
12048 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12049 : *
12050 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12051 : */
12052 711 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12053 : size_t nDimensions,
12054 : GDALDimensionH *pahDimensions,
12055 : GDALExtendedDataTypeH hEDT,
12056 : CSLConstList papszOptions)
12057 : {
12058 711 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12059 711 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12060 711 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12061 1422 : std::vector<std::shared_ptr<GDALDimension>> dims;
12062 711 : dims.reserve(nDimensions);
12063 1696 : for (size_t i = 0; i < nDimensions; i++)
12064 985 : dims.push_back(pahDimensions[i]->m_poImpl);
12065 2133 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12066 2133 : *(hEDT->m_poImpl), papszOptions);
12067 711 : if (!ret)
12068 64 : return nullptr;
12069 647 : return new GDALMDArrayHS(ret);
12070 : }
12071 :
12072 : /************************************************************************/
12073 : /* GDALGroupDeleteMDArray() */
12074 : /************************************************************************/
12075 :
12076 : /** Delete an array from a group.
12077 : *
12078 : * After this call, if a previously obtained instance of the deleted object
12079 : * is still alive, no method other than for freeing it should be invoked.
12080 : *
12081 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12082 : *
12083 : * @return true in case of success.
12084 : * @since GDAL 3.8
12085 : */
12086 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12087 : CSLConstList papszOptions)
12088 : {
12089 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12090 20 : VALIDATE_POINTER1(pszName, __func__, false);
12091 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12092 : }
12093 :
12094 : /************************************************************************/
12095 : /* GDALGroupCreateAttribute() */
12096 : /************************************************************************/
12097 :
12098 : /** Create a attribute within a group.
12099 : *
12100 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12101 : *
12102 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12103 : */
12104 125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12105 : size_t nDimensions,
12106 : const GUInt64 *panDimensions,
12107 : GDALExtendedDataTypeH hEDT,
12108 : CSLConstList papszOptions)
12109 : {
12110 125 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12111 125 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12112 250 : std::vector<GUInt64> dims;
12113 125 : dims.reserve(nDimensions);
12114 175 : for (size_t i = 0; i < nDimensions; i++)
12115 50 : dims.push_back(panDimensions[i]);
12116 125 : auto ret = hGroup->m_poImpl->CreateAttribute(
12117 375 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12118 125 : if (!ret)
12119 14 : return nullptr;
12120 111 : return new GDALAttributeHS(ret);
12121 : }
12122 :
12123 : /************************************************************************/
12124 : /* GDALGroupDeleteAttribute() */
12125 : /************************************************************************/
12126 :
12127 : /** Delete an attribute from a group.
12128 : *
12129 : * After this call, if a previously obtained instance of the deleted object
12130 : * is still alive, no method other than for freeing it should be invoked.
12131 : *
12132 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12133 : *
12134 : * @return true in case of success.
12135 : * @since GDAL 3.8
12136 : */
12137 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12138 : CSLConstList papszOptions)
12139 : {
12140 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12141 25 : VALIDATE_POINTER1(pszName, __func__, false);
12142 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12143 25 : papszOptions);
12144 : }
12145 :
12146 : /************************************************************************/
12147 : /* GDALGroupRename() */
12148 : /************************************************************************/
12149 :
12150 : /** Rename the group.
12151 : *
12152 : * This is not implemented by all drivers.
12153 : *
12154 : * Drivers known to implement it: MEM, netCDF.
12155 : *
12156 : * This is the same as the C++ method GDALGroup::Rename()
12157 : *
12158 : * @return true in case of success
12159 : * @since GDAL 3.8
12160 : */
12161 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12162 : {
12163 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12164 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12165 45 : return hGroup->m_poImpl->Rename(pszNewName);
12166 : }
12167 :
12168 : /************************************************************************/
12169 : /* GDALGroupSubsetDimensionFromSelection() */
12170 : /************************************************************************/
12171 :
12172 : /** Return a virtual group whose one dimension has been subset according to a
12173 : * selection.
12174 : *
12175 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12176 : *
12177 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12178 : */
12179 : GDALGroupH
12180 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12181 : const char *pszSelection,
12182 : CPL_UNUSED CSLConstList papszOptions)
12183 : {
12184 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12185 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12186 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12187 42 : std::string(pszSelection));
12188 14 : if (!hNewGroup)
12189 8 : return nullptr;
12190 6 : return new GDALGroupHS(hNewGroup);
12191 : }
12192 :
12193 : /************************************************************************/
12194 : /* GDALMDArrayRelease() */
12195 : /************************************************************************/
12196 :
12197 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12198 : *
12199 : * Note: when applied on a object coming from a driver, this does not
12200 : * destroy the object in the file, database, etc...
12201 : */
12202 2193 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12203 : {
12204 2193 : delete hMDArray;
12205 2193 : }
12206 :
12207 : /************************************************************************/
12208 : /* GDALMDArrayGetName() */
12209 : /************************************************************************/
12210 :
12211 : /** Return array name.
12212 : *
12213 : * This is the same as the C++ method GDALMDArray::GetName()
12214 : */
12215 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12216 : {
12217 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12218 83 : return hArray->m_poImpl->GetName().c_str();
12219 : }
12220 :
12221 : /************************************************************************/
12222 : /* GDALMDArrayGetFullName() */
12223 : /************************************************************************/
12224 :
12225 : /** Return array full name.
12226 : *
12227 : * This is the same as the C++ method GDALMDArray::GetFullName()
12228 : */
12229 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12230 : {
12231 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12232 50 : return hArray->m_poImpl->GetFullName().c_str();
12233 : }
12234 :
12235 : /************************************************************************/
12236 : /* GDALMDArrayGetName() */
12237 : /************************************************************************/
12238 :
12239 : /** Return the total number of values in the array.
12240 : *
12241 : * This is the same as the C++ method
12242 : * GDALAbstractMDArray::GetTotalElementsCount()
12243 : */
12244 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12245 : {
12246 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12247 6 : return hArray->m_poImpl->GetTotalElementsCount();
12248 : }
12249 :
12250 : /************************************************************************/
12251 : /* GDALMDArrayGetDimensionCount() */
12252 : /************************************************************************/
12253 :
12254 : /** Return the number of dimensions.
12255 : *
12256 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12257 : */
12258 10868 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12259 : {
12260 10868 : VALIDATE_POINTER1(hArray, __func__, 0);
12261 10868 : return hArray->m_poImpl->GetDimensionCount();
12262 : }
12263 :
12264 : /************************************************************************/
12265 : /* GDALMDArrayGetDimensions() */
12266 : /************************************************************************/
12267 :
12268 : /** Return the dimensions of the array
12269 : *
12270 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12271 : * array itself needs to be freed, CPLFree() should be called (and
12272 : * GDALDimensionRelease() on individual array members).
12273 : *
12274 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12275 : *
12276 : * @param hArray Array.
12277 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12278 : *
12279 : * @return an array of *pnCount dimensions.
12280 : */
12281 2458 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12282 : {
12283 2458 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12284 2458 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12285 2458 : const auto &dims(hArray->m_poImpl->GetDimensions());
12286 : auto ret = static_cast<GDALDimensionH *>(
12287 2458 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12288 6935 : for (size_t i = 0; i < dims.size(); i++)
12289 : {
12290 4477 : ret[i] = new GDALDimensionHS(dims[i]);
12291 : }
12292 2458 : *pnCount = dims.size();
12293 2458 : return ret;
12294 : }
12295 :
12296 : /************************************************************************/
12297 : /* GDALReleaseDimensions() */
12298 : /************************************************************************/
12299 :
12300 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12301 : *
12302 : * @param dims return pointer of above methods
12303 : * @param nCount *pnCount value returned by above methods
12304 : */
12305 2531 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12306 : {
12307 7165 : for (size_t i = 0; i < nCount; i++)
12308 : {
12309 4634 : delete dims[i];
12310 : }
12311 2531 : CPLFree(dims);
12312 2531 : }
12313 :
12314 : /************************************************************************/
12315 : /* GDALMDArrayGetDataType() */
12316 : /************************************************************************/
12317 :
12318 : /** Return the data type
12319 : *
12320 : * The return must be freed with GDALExtendedDataTypeRelease().
12321 : */
12322 4192 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12323 : {
12324 4192 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12325 : return new GDALExtendedDataTypeHS(
12326 4192 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12327 : }
12328 :
12329 : /************************************************************************/
12330 : /* GDALMDArrayRead() */
12331 : /************************************************************************/
12332 :
12333 : /** Read part or totality of a multidimensional array.
12334 : *
12335 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12336 : *
12337 : * @return TRUE in case of success.
12338 : */
12339 1993 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12340 : const size_t *count, const GInt64 *arrayStep,
12341 : const GPtrDiff_t *bufferStride,
12342 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12343 : const void *pDstBufferAllocStart,
12344 : size_t nDstBufferAllocSize)
12345 : {
12346 1993 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12347 1993 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12348 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12349 : {
12350 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12351 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12352 : }
12353 1993 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12354 1993 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12355 3986 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12356 1993 : *(bufferDataType->m_poImpl), pDstBuffer,
12357 1993 : pDstBufferAllocStart, nDstBufferAllocSize);
12358 : }
12359 :
12360 : /************************************************************************/
12361 : /* GDALMDArrayWrite() */
12362 : /************************************************************************/
12363 :
12364 : /** Write part or totality of a multidimensional array.
12365 : *
12366 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12367 : *
12368 : * @return TRUE in case of success.
12369 : */
12370 657 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12371 : const size_t *count, const GInt64 *arrayStep,
12372 : const GPtrDiff_t *bufferStride,
12373 : GDALExtendedDataTypeH bufferDataType,
12374 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12375 : size_t nSrcBufferAllocSize)
12376 : {
12377 657 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12378 657 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12379 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12380 : {
12381 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12382 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12383 : }
12384 657 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12385 657 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12386 1314 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12387 657 : bufferStride, *(bufferDataType->m_poImpl),
12388 : pSrcBuffer, pSrcBufferAllocStart,
12389 657 : nSrcBufferAllocSize);
12390 : }
12391 :
12392 : /************************************************************************/
12393 : /* GDALMDArrayAdviseRead() */
12394 : /************************************************************************/
12395 :
12396 : /** Advise driver of upcoming read requests.
12397 : *
12398 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12399 : *
12400 : * @return TRUE in case of success.
12401 : *
12402 : * @since GDAL 3.2
12403 : */
12404 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12405 : const size_t *count)
12406 : {
12407 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12408 : }
12409 :
12410 : /************************************************************************/
12411 : /* GDALMDArrayAdviseReadEx() */
12412 : /************************************************************************/
12413 :
12414 : /** Advise driver of upcoming read requests.
12415 : *
12416 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12417 : *
12418 : * @return TRUE in case of success.
12419 : *
12420 : * @since GDAL 3.4
12421 : */
12422 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12423 : const size_t *count, CSLConstList papszOptions)
12424 : {
12425 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12426 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12427 : }
12428 :
12429 : /************************************************************************/
12430 : /* GDALMDArrayGetAttribute() */
12431 : /************************************************************************/
12432 :
12433 : /** Return an attribute by its name.
12434 : *
12435 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12436 : *
12437 : * The returned attribute must be freed with GDALAttributeRelease().
12438 : */
12439 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12440 : {
12441 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12442 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12443 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12444 120 : if (attr)
12445 111 : return new GDALAttributeHS(attr);
12446 9 : return nullptr;
12447 : }
12448 :
12449 : /************************************************************************/
12450 : /* GDALMDArrayGetAttributes() */
12451 : /************************************************************************/
12452 :
12453 : /** Return the list of attributes contained in this array.
12454 : *
12455 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12456 : * array itself needs to be freed, CPLFree() should be called (and
12457 : * GDALAttributeRelease() on individual array members).
12458 : *
12459 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12460 : *
12461 : * @param hArray Array.
12462 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12463 : * @param papszOptions Driver specific options determining how attributes
12464 : * should be retrieved. Pass nullptr for default behavior.
12465 : *
12466 : * @return an array of *pnCount attributes.
12467 : */
12468 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12469 : CSLConstList papszOptions)
12470 : {
12471 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12472 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12473 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12474 : auto ret = static_cast<GDALAttributeH *>(
12475 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12476 189 : for (size_t i = 0; i < attrs.size(); i++)
12477 : {
12478 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12479 : }
12480 59 : *pnCount = attrs.size();
12481 59 : return ret;
12482 : }
12483 :
12484 : /************************************************************************/
12485 : /* GDALMDArrayCreateAttribute() */
12486 : /************************************************************************/
12487 :
12488 : /** Create a attribute within an array.
12489 : *
12490 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12491 : *
12492 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12493 : */
12494 188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12495 : const char *pszName,
12496 : size_t nDimensions,
12497 : const GUInt64 *panDimensions,
12498 : GDALExtendedDataTypeH hEDT,
12499 : CSLConstList papszOptions)
12500 : {
12501 188 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12502 188 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12503 188 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12504 376 : std::vector<GUInt64> dims;
12505 188 : dims.reserve(nDimensions);
12506 249 : for (size_t i = 0; i < nDimensions; i++)
12507 61 : dims.push_back(panDimensions[i]);
12508 188 : auto ret = hArray->m_poImpl->CreateAttribute(
12509 564 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12510 188 : if (!ret)
12511 9 : return nullptr;
12512 179 : return new GDALAttributeHS(ret);
12513 : }
12514 :
12515 : /************************************************************************/
12516 : /* GDALMDArrayDeleteAttribute() */
12517 : /************************************************************************/
12518 :
12519 : /** Delete an attribute from an array.
12520 : *
12521 : * After this call, if a previously obtained instance of the deleted object
12522 : * is still alive, no method other than for freeing it should be invoked.
12523 : *
12524 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12525 : *
12526 : * @return true in case of success.
12527 : * @since GDAL 3.8
12528 : */
12529 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12530 : CSLConstList papszOptions)
12531 : {
12532 24 : VALIDATE_POINTER1(hArray, __func__, false);
12533 24 : VALIDATE_POINTER1(pszName, __func__, false);
12534 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12535 24 : papszOptions);
12536 : }
12537 :
12538 : /************************************************************************/
12539 : /* GDALMDArrayGetRawNoDataValue() */
12540 : /************************************************************************/
12541 :
12542 : /** Return the nodata value as a "raw" value.
12543 : *
12544 : * The value returned might be nullptr in case of no nodata value. When
12545 : * a nodata value is registered, a non-nullptr will be returned whose size in
12546 : * bytes is GetDataType().GetSize().
12547 : *
12548 : * The returned value should not be modified or freed.
12549 : *
12550 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12551 : *
12552 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12553 : */
12554 77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12555 : {
12556 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12557 77 : return hArray->m_poImpl->GetRawNoDataValue();
12558 : }
12559 :
12560 : /************************************************************************/
12561 : /* GDALMDArrayGetNoDataValueAsDouble() */
12562 : /************************************************************************/
12563 :
12564 : /** Return the nodata value as a double.
12565 : *
12566 : * The value returned might be nullptr in case of no nodata value. When
12567 : * a nodata value is registered, a non-nullptr will be returned whose size in
12568 : * bytes is GetDataType().GetSize().
12569 : *
12570 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12571 : *
12572 : * @param hArray Array handle.
12573 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12574 : * if a nodata value exists and can be converted to double. Might be nullptr.
12575 : *
12576 : * @return the nodata value as a double. A 0.0 value might also indicate the
12577 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12578 : * will be set to false then).
12579 : */
12580 121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12581 : int *pbHasNoDataValue)
12582 : {
12583 121 : VALIDATE_POINTER1(hArray, __func__, 0);
12584 121 : bool bHasNodataValue = false;
12585 121 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12586 121 : if (pbHasNoDataValue)
12587 121 : *pbHasNoDataValue = bHasNodataValue;
12588 121 : return ret;
12589 : }
12590 :
12591 : /************************************************************************/
12592 : /* GDALMDArrayGetNoDataValueAsInt64() */
12593 : /************************************************************************/
12594 :
12595 : /** Return the nodata value as a Int64.
12596 : *
12597 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12598 : *
12599 : * @param hArray Array handle.
12600 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12601 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12602 : *
12603 : * @return the nodata value as a Int64.
12604 : * @since GDAL 3.5
12605 : */
12606 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12607 : int *pbHasNoDataValue)
12608 : {
12609 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12610 11 : bool bHasNodataValue = false;
12611 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12612 11 : if (pbHasNoDataValue)
12613 11 : *pbHasNoDataValue = bHasNodataValue;
12614 11 : return ret;
12615 : }
12616 :
12617 : /************************************************************************/
12618 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12619 : /************************************************************************/
12620 :
12621 : /** Return the nodata value as a UInt64.
12622 : *
12623 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12624 : *
12625 : * @param hArray Array handle.
12626 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12627 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12628 : *
12629 : * @return the nodata value as a UInt64.
12630 : * @since GDAL 3.5
12631 : */
12632 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12633 : int *pbHasNoDataValue)
12634 : {
12635 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12636 7 : bool bHasNodataValue = false;
12637 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12638 7 : if (pbHasNoDataValue)
12639 7 : *pbHasNoDataValue = bHasNodataValue;
12640 7 : return ret;
12641 : }
12642 :
12643 : /************************************************************************/
12644 : /* GDALMDArraySetRawNoDataValue() */
12645 : /************************************************************************/
12646 :
12647 : /** Set the nodata value as a "raw" value.
12648 : *
12649 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12650 : * void*).
12651 : *
12652 : * @return TRUE in case of success.
12653 : */
12654 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12655 : {
12656 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12657 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12658 : }
12659 :
12660 : /************************************************************************/
12661 : /* GDALMDArraySetNoDataValueAsDouble() */
12662 : /************************************************************************/
12663 :
12664 : /** Set the nodata value as a double.
12665 : *
12666 : * If the natural data type of the attribute/array is not double, type
12667 : * conversion will occur to the type returned by GetDataType().
12668 : *
12669 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12670 : *
12671 : * @return TRUE in case of success.
12672 : */
12673 55 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12674 : {
12675 55 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12676 55 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12677 : }
12678 :
12679 : /************************************************************************/
12680 : /* GDALMDArraySetNoDataValueAsInt64() */
12681 : /************************************************************************/
12682 :
12683 : /** Set the nodata value as a Int64.
12684 : *
12685 : * If the natural data type of the attribute/array is not Int64, type conversion
12686 : * will occur to the type returned by GetDataType().
12687 : *
12688 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12689 : *
12690 : * @return TRUE in case of success.
12691 : * @since GDAL 3.5
12692 : */
12693 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12694 : {
12695 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12696 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12697 : }
12698 :
12699 : /************************************************************************/
12700 : /* GDALMDArraySetNoDataValueAsUInt64() */
12701 : /************************************************************************/
12702 :
12703 : /** Set the nodata value as a UInt64.
12704 : *
12705 : * If the natural data type of the attribute/array is not UInt64, type
12706 : * conversion will occur to the type returned by GetDataType().
12707 : *
12708 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12709 : *
12710 : * @return TRUE in case of success.
12711 : * @since GDAL 3.5
12712 : */
12713 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12714 : uint64_t nNoDataValue)
12715 : {
12716 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12717 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12718 : }
12719 :
12720 : /************************************************************************/
12721 : /* GDALMDArrayResize() */
12722 : /************************************************************************/
12723 :
12724 : /** Resize an array to new dimensions.
12725 : *
12726 : * Not all drivers may allow this operation, and with restrictions (e.g.
12727 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12728 : *
12729 : * Resizing a dimension used in other arrays will cause those other arrays
12730 : * to be resized.
12731 : *
12732 : * This is the same as the C++ method GDALMDArray::Resize().
12733 : *
12734 : * @param hArray Array.
12735 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12736 : * new size of each indexing dimension.
12737 : * @param papszOptions Options. (Driver specific)
12738 : * @return true in case of success.
12739 : * @since GDAL 3.7
12740 : */
12741 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12742 : CSLConstList papszOptions)
12743 : {
12744 42 : VALIDATE_POINTER1(hArray, __func__, false);
12745 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12746 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12747 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12748 : {
12749 83 : anNewDimSizes[i] = panNewDimSizes[i];
12750 : }
12751 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12752 : }
12753 :
12754 : /************************************************************************/
12755 : /* GDALMDArraySetScale() */
12756 : /************************************************************************/
12757 :
12758 : /** Set the scale value to apply to raw values.
12759 : *
12760 : * unscaled_value = raw_value * GetScale() + GetOffset()
12761 : *
12762 : * This is the same as the C++ method GDALMDArray::SetScale().
12763 : *
12764 : * @return TRUE in case of success.
12765 : */
12766 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12767 : {
12768 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12769 0 : return hArray->m_poImpl->SetScale(dfScale);
12770 : }
12771 :
12772 : /************************************************************************/
12773 : /* GDALMDArraySetScaleEx() */
12774 : /************************************************************************/
12775 :
12776 : /** Set the scale value to apply to raw values.
12777 : *
12778 : * unscaled_value = raw_value * GetScale() + GetOffset()
12779 : *
12780 : * This is the same as the C++ method GDALMDArray::SetScale().
12781 : *
12782 : * @return TRUE in case of success.
12783 : * @since GDAL 3.3
12784 : */
12785 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12786 : GDALDataType eStorageType)
12787 : {
12788 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12789 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12790 : }
12791 :
12792 : /************************************************************************/
12793 : /* GDALMDArraySetOffset() */
12794 : /************************************************************************/
12795 :
12796 : /** Set the scale value to apply to raw values.
12797 : *
12798 : * unscaled_value = raw_value * GetScale() + GetOffset()
12799 : *
12800 : * This is the same as the C++ method GDALMDArray::SetOffset().
12801 : *
12802 : * @return TRUE in case of success.
12803 : */
12804 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12805 : {
12806 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12807 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12808 : }
12809 :
12810 : /************************************************************************/
12811 : /* GDALMDArraySetOffsetEx() */
12812 : /************************************************************************/
12813 :
12814 : /** Set the scale value to apply to raw values.
12815 : *
12816 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12817 : *
12818 : * This is the same as the C++ method GDALMDArray::SetOffset().
12819 : *
12820 : * @return TRUE in case of success.
12821 : * @since GDAL 3.3
12822 : */
12823 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12824 : GDALDataType eStorageType)
12825 : {
12826 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12827 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12828 : }
12829 :
12830 : /************************************************************************/
12831 : /* GDALMDArrayGetScale() */
12832 : /************************************************************************/
12833 :
12834 : /** Get the scale value to apply to raw values.
12835 : *
12836 : * unscaled_value = raw_value * GetScale() + GetOffset()
12837 : *
12838 : * This is the same as the C++ method GDALMDArray::GetScale().
12839 : *
12840 : * @return the scale value
12841 : */
12842 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12843 : {
12844 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12845 105 : bool bHasValue = false;
12846 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12847 105 : if (pbHasValue)
12848 105 : *pbHasValue = bHasValue;
12849 105 : return dfRet;
12850 : }
12851 :
12852 : /************************************************************************/
12853 : /* GDALMDArrayGetScaleEx() */
12854 : /************************************************************************/
12855 :
12856 : /** Get the scale value to apply to raw values.
12857 : *
12858 : * unscaled_value = raw_value * GetScale() + GetScale()
12859 : *
12860 : * This is the same as the C++ method GDALMDArray::GetScale().
12861 : *
12862 : * @return the scale value
12863 : * @since GDAL 3.3
12864 : */
12865 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12866 : GDALDataType *peStorageType)
12867 : {
12868 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12869 5 : bool bHasValue = false;
12870 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12871 5 : if (pbHasValue)
12872 5 : *pbHasValue = bHasValue;
12873 5 : return dfRet;
12874 : }
12875 :
12876 : /************************************************************************/
12877 : /* GDALMDArrayGetOffset() */
12878 : /************************************************************************/
12879 :
12880 : /** Get the scale value to apply to raw values.
12881 : *
12882 : * unscaled_value = raw_value * GetScale() + GetOffset()
12883 : *
12884 : * This is the same as the C++ method GDALMDArray::GetOffset().
12885 : *
12886 : * @return the scale value
12887 : */
12888 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12889 : {
12890 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12891 102 : bool bHasValue = false;
12892 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12893 102 : if (pbHasValue)
12894 102 : *pbHasValue = bHasValue;
12895 102 : return dfRet;
12896 : }
12897 :
12898 : /************************************************************************/
12899 : /* GDALMDArrayGetOffsetEx() */
12900 : /************************************************************************/
12901 :
12902 : /** Get the scale value to apply to raw values.
12903 : *
12904 : * unscaled_value = raw_value * GetScale() + GetOffset()
12905 : *
12906 : * This is the same as the C++ method GDALMDArray::GetOffset().
12907 : *
12908 : * @return the scale value
12909 : * @since GDAL 3.3
12910 : */
12911 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12912 : GDALDataType *peStorageType)
12913 : {
12914 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12915 5 : bool bHasValue = false;
12916 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12917 5 : if (pbHasValue)
12918 5 : *pbHasValue = bHasValue;
12919 5 : return dfRet;
12920 : }
12921 :
12922 : /************************************************************************/
12923 : /* GDALMDArrayGetBlockSize() */
12924 : /************************************************************************/
12925 :
12926 : /** Return the "natural" block size of the array along all dimensions.
12927 : *
12928 : * Some drivers might organize the array in tiles/blocks and reading/writing
12929 : * aligned on those tile/block boundaries will be more efficient.
12930 : *
12931 : * The returned number of elements in the vector is the same as
12932 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12933 : * the natural block size along the considered dimension.
12934 : * "Flat" arrays will typically return a vector of values set to 0.
12935 : *
12936 : * The default implementation will return a vector of values set to 0.
12937 : *
12938 : * This method is used by GetProcessingChunkSize().
12939 : *
12940 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
12941 : * theoretical case of a 32-bit platform, this might exceed its size_t
12942 : * allocation capabilities.
12943 : *
12944 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12945 : *
12946 : * @return the block size, in number of elements along each dimension.
12947 : */
12948 98 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12949 : {
12950 98 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12951 98 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12952 98 : auto res = hArray->m_poImpl->GetBlockSize();
12953 98 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12954 301 : for (size_t i = 0; i < res.size(); i++)
12955 : {
12956 203 : ret[i] = res[i];
12957 : }
12958 98 : *pnCount = res.size();
12959 98 : return ret;
12960 : }
12961 :
12962 : /***********************************************************************/
12963 : /* GDALMDArrayGetProcessingChunkSize() */
12964 : /************************************************************************/
12965 :
12966 : /** \brief Return an optimal chunk size for read/write operations, given the
12967 : * natural block size and memory constraints specified.
12968 : *
12969 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12970 : * multiple of those returned by GetBlockSize() (unless the block define by
12971 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12972 : * returned by this method).
12973 : *
12974 : * This is the same as the C++ method
12975 : * GDALAbstractMDArray::GetProcessingChunkSize().
12976 : *
12977 : * @param hArray Array.
12978 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12979 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12980 : * chunk.
12981 : *
12982 : * @return the chunk size, in number of elements along each dimension.
12983 : */
12984 :
12985 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12986 : size_t nMaxChunkMemory)
12987 : {
12988 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12989 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12990 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12991 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12992 3 : for (size_t i = 0; i < res.size(); i++)
12993 : {
12994 2 : ret[i] = res[i];
12995 : }
12996 1 : *pnCount = res.size();
12997 1 : return ret;
12998 : }
12999 :
13000 : /************************************************************************/
13001 : /* GDALMDArrayGetStructuralInfo() */
13002 : /************************************************************************/
13003 :
13004 : /** Return structural information on the array.
13005 : *
13006 : * This may be the compression, etc..
13007 : *
13008 : * The return value should not be freed and is valid until GDALMDArray is
13009 : * released or this function called again.
13010 : *
13011 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
13012 : */
13013 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13014 : {
13015 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13016 15 : return hArray->m_poImpl->GetStructuralInfo();
13017 : }
13018 :
13019 : /************************************************************************/
13020 : /* GDALMDArrayGetView() */
13021 : /************************************************************************/
13022 :
13023 : /** Return a view of the array using slicing or field access.
13024 : *
13025 : * The returned object should be released with GDALMDArrayRelease().
13026 : *
13027 : * This is the same as the C++ method GDALMDArray::GetView().
13028 : */
13029 438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13030 : {
13031 438 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13032 438 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13033 1314 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13034 438 : if (!sliced)
13035 23 : return nullptr;
13036 415 : return new GDALMDArrayHS(sliced);
13037 : }
13038 :
13039 : /************************************************************************/
13040 : /* GDALMDArrayTranspose() */
13041 : /************************************************************************/
13042 :
13043 : /** Return a view of the array whose axis have been reordered.
13044 : *
13045 : * The returned object should be released with GDALMDArrayRelease().
13046 : *
13047 : * This is the same as the C++ method GDALMDArray::Transpose().
13048 : */
13049 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13050 : const int *panMapNewAxisToOldAxis)
13051 : {
13052 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13053 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13054 44 : if (nNewAxisCount)
13055 : {
13056 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13057 : nNewAxisCount * sizeof(int));
13058 : }
13059 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13060 44 : if (!reordered)
13061 7 : return nullptr;
13062 37 : return new GDALMDArrayHS(reordered);
13063 : }
13064 :
13065 : /************************************************************************/
13066 : /* GDALMDArrayGetUnscaled() */
13067 : /************************************************************************/
13068 :
13069 : /** Return an array that is the unscaled version of the current one.
13070 : *
13071 : * That is each value of the unscaled array will be
13072 : * unscaled_value = raw_value * GetScale() + GetOffset()
13073 : *
13074 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13075 : * from unscaled values to raw values.
13076 : *
13077 : * The returned object should be released with GDALMDArrayRelease().
13078 : *
13079 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13080 : */
13081 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13082 : {
13083 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13084 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13085 13 : if (!unscaled)
13086 0 : return nullptr;
13087 13 : return new GDALMDArrayHS(unscaled);
13088 : }
13089 :
13090 : /************************************************************************/
13091 : /* GDALMDArrayGetMask() */
13092 : /************************************************************************/
13093 :
13094 : /** Return an array that is a mask for the current array
13095 : *
13096 : * This array will be of type Byte, with values set to 0 to indicate invalid
13097 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13098 : *
13099 : * The returned object should be released with GDALMDArrayRelease().
13100 : *
13101 : * This is the same as the C++ method GDALMDArray::GetMask().
13102 : */
13103 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13104 : {
13105 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13106 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13107 35 : if (!unscaled)
13108 7 : return nullptr;
13109 28 : return new GDALMDArrayHS(unscaled);
13110 : }
13111 :
13112 : /************************************************************************/
13113 : /* GDALMDArrayGetResampled() */
13114 : /************************************************************************/
13115 :
13116 : /** Return an array that is a resampled / reprojected view of the current array
13117 : *
13118 : * This is the same as the C++ method GDALMDArray::GetResampled().
13119 : *
13120 : * Currently this method can only resample along the last 2 dimensions, unless
13121 : * orthorectifying a NASA EMIT dataset.
13122 : *
13123 : * The returned object should be released with GDALMDArrayRelease().
13124 : *
13125 : * @since 3.4
13126 : */
13127 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13128 : const GDALDimensionH *pahNewDims,
13129 : GDALRIOResampleAlg resampleAlg,
13130 : OGRSpatialReferenceH hTargetSRS,
13131 : CSLConstList papszOptions)
13132 : {
13133 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13134 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13135 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13136 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13137 : {
13138 78 : if (pahNewDims[i])
13139 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13140 : }
13141 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13142 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13143 68 : papszOptions);
13144 34 : if (!poNewArray)
13145 8 : return nullptr;
13146 26 : return new GDALMDArrayHS(poNewArray);
13147 : }
13148 :
13149 : /************************************************************************/
13150 : /* GDALMDArraySetUnit() */
13151 : /************************************************************************/
13152 :
13153 : /** Set the variable unit.
13154 : *
13155 : * Values should conform as much as possible with those allowed by
13156 : * the NetCDF CF conventions:
13157 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13158 : * but others might be returned.
13159 : *
13160 : * Few examples are "meter", "degrees", "second", ...
13161 : * Empty value means unknown.
13162 : *
13163 : * This is the same as the C function GDALMDArraySetUnit()
13164 : *
13165 : * @param hArray array.
13166 : * @param pszUnit unit name.
13167 : * @return TRUE in case of success.
13168 : */
13169 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13170 : {
13171 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13172 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13173 : }
13174 :
13175 : /************************************************************************/
13176 : /* GDALMDArrayGetUnit() */
13177 : /************************************************************************/
13178 :
13179 : /** Return the array unit.
13180 : *
13181 : * Values should conform as much as possible with those allowed by
13182 : * the NetCDF CF conventions:
13183 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13184 : * but others might be returned.
13185 : *
13186 : * Few examples are "meter", "degrees", "second", ...
13187 : * Empty value means unknown.
13188 : *
13189 : * The return value should not be freed and is valid until GDALMDArray is
13190 : * released or this function called again.
13191 : *
13192 : * This is the same as the C++ method GDALMDArray::GetUnit().
13193 : */
13194 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13195 : {
13196 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13197 113 : return hArray->m_poImpl->GetUnit().c_str();
13198 : }
13199 :
13200 : /************************************************************************/
13201 : /* GDALMDArrayGetSpatialRef() */
13202 : /************************************************************************/
13203 :
13204 : /** Assign a spatial reference system object to the array.
13205 : *
13206 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13207 : * @return TRUE in case of success.
13208 : */
13209 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13210 : {
13211 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13212 60 : return hArray->m_poImpl->SetSpatialRef(
13213 60 : OGRSpatialReference::FromHandle(hSRS));
13214 : }
13215 :
13216 : /************************************************************************/
13217 : /* GDALMDArrayGetSpatialRef() */
13218 : /************************************************************************/
13219 :
13220 : /** Return the spatial reference system object associated with the array.
13221 : *
13222 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13223 : *
13224 : * The returned object must be freed with OSRDestroySpatialReference().
13225 : */
13226 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13227 : {
13228 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13229 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13230 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13231 : }
13232 :
13233 : /************************************************************************/
13234 : /* GDALMDArrayGetStatistics() */
13235 : /************************************************************************/
13236 :
13237 : /**
13238 : * \brief Fetch statistics.
13239 : *
13240 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13241 : *
13242 : * @since GDAL 3.2
13243 : */
13244 :
13245 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13246 : int bApproxOK, int bForce, double *pdfMin,
13247 : double *pdfMax, double *pdfMean,
13248 : double *pdfStdDev, GUInt64 *pnValidCount,
13249 : GDALProgressFunc pfnProgress,
13250 : void *pProgressData)
13251 : {
13252 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13253 30 : return hArray->m_poImpl->GetStatistics(
13254 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13255 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13256 : }
13257 :
13258 : /************************************************************************/
13259 : /* GDALMDArrayComputeStatistics() */
13260 : /************************************************************************/
13261 :
13262 : /**
13263 : * \brief Compute statistics.
13264 : *
13265 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13266 : *
13267 : * @since GDAL 3.2
13268 : * @see GDALMDArrayComputeStatisticsEx()
13269 : */
13270 :
13271 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13272 : int bApproxOK, double *pdfMin, double *pdfMax,
13273 : double *pdfMean, double *pdfStdDev,
13274 : GUInt64 *pnValidCount,
13275 : GDALProgressFunc pfnProgress,
13276 : void *pProgressData)
13277 : {
13278 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13279 0 : return hArray->m_poImpl->ComputeStatistics(
13280 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13281 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13282 : }
13283 :
13284 : /************************************************************************/
13285 : /* GDALMDArrayComputeStatisticsEx() */
13286 : /************************************************************************/
13287 :
13288 : /**
13289 : * \brief Compute statistics.
13290 : *
13291 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13292 : *
13293 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13294 : *
13295 : * @since GDAL 3.8
13296 : */
13297 :
13298 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13299 : int bApproxOK, double *pdfMin,
13300 : double *pdfMax, double *pdfMean,
13301 : double *pdfStdDev, GUInt64 *pnValidCount,
13302 : GDALProgressFunc pfnProgress,
13303 : void *pProgressData,
13304 : CSLConstList papszOptions)
13305 : {
13306 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13307 8 : return hArray->m_poImpl->ComputeStatistics(
13308 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13309 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13310 : }
13311 :
13312 : /************************************************************************/
13313 : /* GDALMDArrayGetCoordinateVariables() */
13314 : /************************************************************************/
13315 :
13316 : /** Return coordinate variables.
13317 : *
13318 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13319 : * itself needs to be freed, CPLFree() should be called (and
13320 : * GDALMDArrayRelease() on individual array members).
13321 : *
13322 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13323 : *
13324 : * @param hArray Array.
13325 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13326 : *
13327 : * @return an array of *pnCount arrays.
13328 : * @since 3.4
13329 : */
13330 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13331 : size_t *pnCount)
13332 : {
13333 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13334 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13335 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13336 : auto ret = static_cast<GDALMDArrayH *>(
13337 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13338 29 : for (size_t i = 0; i < coordinates.size(); i++)
13339 : {
13340 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13341 : }
13342 13 : *pnCount = coordinates.size();
13343 13 : return ret;
13344 : }
13345 :
13346 : /************************************************************************/
13347 : /* GDALMDArrayGetGridded() */
13348 : /************************************************************************/
13349 :
13350 : /** Return a gridded array from scattered point data, that is from an array
13351 : * whose last dimension is the indexing variable of X and Y arrays.
13352 : *
13353 : * The returned object should be released with GDALMDArrayRelease().
13354 : *
13355 : * This is the same as the C++ method GDALMDArray::GetGridded().
13356 : *
13357 : * @since GDAL 3.7
13358 : */
13359 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13360 : const char *pszGridOptions,
13361 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13362 : CSLConstList papszOptions)
13363 : {
13364 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13365 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13366 22 : auto gridded = hArray->m_poImpl->GetGridded(
13367 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13368 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13369 22 : if (!gridded)
13370 19 : return nullptr;
13371 3 : return new GDALMDArrayHS(gridded);
13372 : }
13373 :
13374 : /************************************************************************/
13375 : /* GDALMDArrayGetMeshGrid() */
13376 : /************************************************************************/
13377 :
13378 : /** Return a list of multidimensional arrays from a list of one-dimensional
13379 : * arrays.
13380 : *
13381 : * This is typically used to transform one-dimensional longitude, latitude
13382 : * arrays into 2D ones.
13383 : *
13384 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13385 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13386 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13387 : * repeated to fill the matrix along the first dimension for x1, the second
13388 : * for x2 and so on.
13389 : *
13390 : * For example, if x = [1, 2], and y = [3, 4, 5],
13391 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13392 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13393 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13394 : *
13395 : * and
13396 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13397 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13398 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13399 : *
13400 : * The currently supported options are:
13401 : * <ul>
13402 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13403 : * output.
13404 : * </li>
13405 : * </ul>
13406 : *
13407 : * This is the same as
13408 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13409 : * function.
13410 : *
13411 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13412 : * If only the array itself needs to be freed, CPLFree() should be called
13413 : * (and GDALMDArrayRelease() on individual array members).
13414 : *
13415 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13416 : *
13417 : * @param pahInputArrays Input arrays
13418 : * @param nCountInputArrays Number of input arrays
13419 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13420 : * @param papszOptions NULL, or NULL terminated list of options.
13421 : *
13422 : * @return an array of *pnCountOutputArrays arrays.
13423 : * @since 3.10
13424 : */
13425 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13426 : size_t nCountInputArrays,
13427 : size_t *pnCountOutputArrays,
13428 : CSLConstList papszOptions)
13429 : {
13430 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13431 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13432 :
13433 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13434 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13435 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13436 :
13437 : const auto apoOutputArrays =
13438 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13439 : auto ret = static_cast<GDALMDArrayH *>(
13440 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13441 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13442 : {
13443 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13444 : }
13445 7 : *pnCountOutputArrays = apoOutputArrays.size();
13446 7 : return ret;
13447 : }
13448 :
13449 : /************************************************************************/
13450 : /* GDALReleaseArrays() */
13451 : /************************************************************************/
13452 :
13453 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13454 : *
13455 : * @param arrays return pointer of above methods
13456 : * @param nCount *pnCount value returned by above methods
13457 : */
13458 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13459 : {
13460 46 : for (size_t i = 0; i < nCount; i++)
13461 : {
13462 26 : delete arrays[i];
13463 : }
13464 20 : CPLFree(arrays);
13465 20 : }
13466 :
13467 : /************************************************************************/
13468 : /* GDALMDArrayCache() */
13469 : /************************************************************************/
13470 :
13471 : /**
13472 : * \brief Cache the content of the array into an auxiliary filename.
13473 : *
13474 : * This is the same as the C++ method GDALMDArray::Cache().
13475 : *
13476 : * @since GDAL 3.4
13477 : */
13478 :
13479 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13480 : {
13481 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13482 7 : return hArray->m_poImpl->Cache(papszOptions);
13483 : }
13484 :
13485 : /************************************************************************/
13486 : /* GDALMDArrayRename() */
13487 : /************************************************************************/
13488 :
13489 : /** Rename the array.
13490 : *
13491 : * This is not implemented by all drivers.
13492 : *
13493 : * Drivers known to implement it: MEM, netCDF, Zarr.
13494 : *
13495 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13496 : *
13497 : * @return true in case of success
13498 : * @since GDAL 3.8
13499 : */
13500 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13501 : {
13502 28 : VALIDATE_POINTER1(hArray, __func__, false);
13503 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13504 28 : return hArray->m_poImpl->Rename(pszNewName);
13505 : }
13506 :
13507 : /************************************************************************/
13508 : /* GDALAttributeRelease() */
13509 : /************************************************************************/
13510 :
13511 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13512 : *
13513 : * Note: when applied on a object coming from a driver, this does not
13514 : * destroy the object in the file, database, etc...
13515 : */
13516 765 : void GDALAttributeRelease(GDALAttributeH hAttr)
13517 : {
13518 765 : delete hAttr;
13519 765 : }
13520 :
13521 : /************************************************************************/
13522 : /* GDALAttributeGetName() */
13523 : /************************************************************************/
13524 :
13525 : /** Return the name of the attribute.
13526 : *
13527 : * The returned pointer is valid until hAttr is released.
13528 : *
13529 : * This is the same as the C++ method GDALAttribute::GetName().
13530 : */
13531 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13532 : {
13533 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13534 361 : return hAttr->m_poImpl->GetName().c_str();
13535 : }
13536 :
13537 : /************************************************************************/
13538 : /* GDALAttributeGetFullName() */
13539 : /************************************************************************/
13540 :
13541 : /** Return the full name of the attribute.
13542 : *
13543 : * The returned pointer is valid until hAttr is released.
13544 : *
13545 : * This is the same as the C++ method GDALAttribute::GetFullName().
13546 : */
13547 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13548 : {
13549 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13550 49 : return hAttr->m_poImpl->GetFullName().c_str();
13551 : }
13552 :
13553 : /************************************************************************/
13554 : /* GDALAttributeGetTotalElementsCount() */
13555 : /************************************************************************/
13556 :
13557 : /** Return the total number of values in the attribute.
13558 : *
13559 : * This is the same as the C++ method
13560 : * GDALAbstractMDArray::GetTotalElementsCount()
13561 : */
13562 177 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13563 : {
13564 177 : VALIDATE_POINTER1(hAttr, __func__, 0);
13565 177 : return hAttr->m_poImpl->GetTotalElementsCount();
13566 : }
13567 :
13568 : /************************************************************************/
13569 : /* GDALAttributeGetDimensionCount() */
13570 : /************************************************************************/
13571 :
13572 : /** Return the number of dimensions.
13573 : *
13574 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13575 : */
13576 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13577 : {
13578 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13579 12 : return hAttr->m_poImpl->GetDimensionCount();
13580 : }
13581 :
13582 : /************************************************************************/
13583 : /* GDALAttributeGetDimensionsSize() */
13584 : /************************************************************************/
13585 :
13586 : /** Return the dimension sizes of the attribute.
13587 : *
13588 : * The returned array must be freed with CPLFree()
13589 : *
13590 : * @param hAttr Attribute.
13591 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13592 : *
13593 : * @return an array of *pnCount values.
13594 : */
13595 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13596 : {
13597 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13598 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13599 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13600 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13601 22 : for (size_t i = 0; i < dims.size(); i++)
13602 : {
13603 11 : ret[i] = dims[i]->GetSize();
13604 : }
13605 11 : *pnCount = dims.size();
13606 11 : return ret;
13607 : }
13608 :
13609 : /************************************************************************/
13610 : /* GDALAttributeGetDataType() */
13611 : /************************************************************************/
13612 :
13613 : /** Return the data type
13614 : *
13615 : * The return must be freed with GDALExtendedDataTypeRelease().
13616 : */
13617 434 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13618 : {
13619 434 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13620 : return new GDALExtendedDataTypeHS(
13621 434 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13622 : }
13623 :
13624 : /************************************************************************/
13625 : /* GDALAttributeReadAsRaw() */
13626 : /************************************************************************/
13627 :
13628 : /** Return the raw value of an attribute.
13629 : *
13630 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13631 : *
13632 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13633 : *
13634 : * @param hAttr Attribute.
13635 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13636 : *
13637 : * @return a buffer of *pnSize bytes.
13638 : */
13639 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13640 : {
13641 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13642 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13643 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13644 6 : *pnSize = res.size();
13645 6 : auto ret = res.StealData();
13646 6 : if (!ret)
13647 : {
13648 0 : *pnSize = 0;
13649 0 : return nullptr;
13650 : }
13651 6 : return ret;
13652 : }
13653 :
13654 : /************************************************************************/
13655 : /* GDALAttributeFreeRawResult() */
13656 : /************************************************************************/
13657 :
13658 : /** Free the return of GDALAttributeAsRaw()
13659 : */
13660 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13661 : CPL_UNUSED size_t nSize)
13662 : {
13663 6 : VALIDATE_POINTER0(hAttr, __func__);
13664 6 : if (raw)
13665 : {
13666 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13667 6 : const auto nDTSize(dt.GetSize());
13668 6 : GByte *pabyPtr = raw;
13669 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13670 6 : CPLAssert(nSize == nDTSize * nEltCount);
13671 12 : for (size_t i = 0; i < nEltCount; ++i)
13672 : {
13673 6 : dt.FreeDynamicMemory(pabyPtr);
13674 6 : pabyPtr += nDTSize;
13675 : }
13676 6 : CPLFree(raw);
13677 : }
13678 : }
13679 :
13680 : /************************************************************************/
13681 : /* GDALAttributeReadAsString() */
13682 : /************************************************************************/
13683 :
13684 : /** Return the value of an attribute as a string.
13685 : *
13686 : * The returned string should not be freed, and its lifetime does not
13687 : * excess a next call to ReadAsString() on the same object, or the deletion
13688 : * of the object itself.
13689 : *
13690 : * This function will only return the first element if there are several.
13691 : *
13692 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13693 : *
13694 : * @return a string, or nullptr.
13695 : */
13696 108 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13697 : {
13698 108 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13699 108 : return hAttr->m_poImpl->ReadAsString();
13700 : }
13701 :
13702 : /************************************************************************/
13703 : /* GDALAttributeReadAsInt() */
13704 : /************************************************************************/
13705 :
13706 : /** Return the value of an attribute as a integer.
13707 : *
13708 : * This function will only return the first element if there are several.
13709 : *
13710 : * It can fail if its value can not be converted to integer.
13711 : *
13712 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13713 : *
13714 : * @return a integer, or INT_MIN in case of error.
13715 : */
13716 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13717 : {
13718 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13719 22 : return hAttr->m_poImpl->ReadAsInt();
13720 : }
13721 :
13722 : /************************************************************************/
13723 : /* GDALAttributeReadAsInt64() */
13724 : /************************************************************************/
13725 :
13726 : /** Return the value of an attribute as a int64_t.
13727 : *
13728 : * This function will only return the first element if there are several.
13729 : *
13730 : * It can fail if its value can not be converted to integer.
13731 : *
13732 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13733 : *
13734 : * @return an int64_t, or INT64_MIN in case of error.
13735 : */
13736 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13737 : {
13738 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13739 15 : return hAttr->m_poImpl->ReadAsInt64();
13740 : }
13741 :
13742 : /************************************************************************/
13743 : /* GDALAttributeReadAsDouble() */
13744 : /************************************************************************/
13745 :
13746 : /** Return the value of an attribute as a double.
13747 : *
13748 : * This function will only return the first element if there are several.
13749 : *
13750 : * It can fail if its value can not be converted to double.
13751 : *
13752 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13753 : *
13754 : * @return a double value.
13755 : */
13756 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13757 : {
13758 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13759 40 : return hAttr->m_poImpl->ReadAsDouble();
13760 : }
13761 :
13762 : /************************************************************************/
13763 : /* GDALAttributeReadAsStringArray() */
13764 : /************************************************************************/
13765 :
13766 : /** Return the value of an attribute as an array of strings.
13767 : *
13768 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13769 : *
13770 : * The return value must be freed with CSLDestroy().
13771 : */
13772 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13773 : {
13774 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13775 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13776 : }
13777 :
13778 : /************************************************************************/
13779 : /* GDALAttributeReadAsIntArray() */
13780 : /************************************************************************/
13781 :
13782 : /** Return the value of an attribute as an array of integers.
13783 : *
13784 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13785 : *
13786 : * @param hAttr Attribute
13787 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13788 : * @return array to be freed with CPLFree(), or nullptr.
13789 : */
13790 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13791 : {
13792 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13793 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13794 15 : *pnCount = 0;
13795 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13796 15 : if (tmp.empty())
13797 0 : return nullptr;
13798 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13799 15 : if (!ret)
13800 0 : return nullptr;
13801 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13802 15 : *pnCount = tmp.size();
13803 15 : return ret;
13804 : }
13805 :
13806 : /************************************************************************/
13807 : /* GDALAttributeReadAsInt64Array() */
13808 : /************************************************************************/
13809 :
13810 : /** Return the value of an attribute as an array of int64_t.
13811 : *
13812 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13813 : *
13814 : * @param hAttr Attribute
13815 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13816 : * @return array to be freed with CPLFree(), or nullptr.
13817 : */
13818 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13819 : {
13820 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13821 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13822 14 : *pnCount = 0;
13823 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13824 14 : if (tmp.empty())
13825 0 : return nullptr;
13826 : auto ret = static_cast<int64_t *>(
13827 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13828 14 : if (!ret)
13829 0 : return nullptr;
13830 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13831 14 : *pnCount = tmp.size();
13832 14 : return ret;
13833 : }
13834 :
13835 : /************************************************************************/
13836 : /* GDALAttributeReadAsDoubleArray() */
13837 : /************************************************************************/
13838 :
13839 : /** Return the value of an attribute as an array of doubles.
13840 : *
13841 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13842 : *
13843 : * @param hAttr Attribute
13844 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13845 : * @return array to be freed with CPLFree(), or nullptr.
13846 : */
13847 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13848 : {
13849 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13850 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13851 29 : *pnCount = 0;
13852 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13853 29 : if (tmp.empty())
13854 0 : return nullptr;
13855 : auto ret =
13856 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13857 29 : if (!ret)
13858 0 : return nullptr;
13859 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13860 29 : *pnCount = tmp.size();
13861 29 : return ret;
13862 : }
13863 :
13864 : /************************************************************************/
13865 : /* GDALAttributeWriteRaw() */
13866 : /************************************************************************/
13867 :
13868 : /** Write an attribute from raw values expressed in GetDataType()
13869 : *
13870 : * The values should be provided in the type of GetDataType() and there should
13871 : * be exactly GetTotalElementsCount() of them.
13872 : * If GetDataType() is a string, each value should be a char* pointer.
13873 : *
13874 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13875 : *
13876 : * @param hAttr Attribute
13877 : * @param pabyValue Buffer of nLen bytes.
13878 : * @param nLength Size of pabyValue in bytes. Should be equal to
13879 : * GetTotalElementsCount() * GetDataType().GetSize()
13880 : * @return TRUE in case of success.
13881 : */
13882 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13883 : size_t nLength)
13884 : {
13885 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13886 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13887 : }
13888 :
13889 : /************************************************************************/
13890 : /* GDALAttributeWriteString() */
13891 : /************************************************************************/
13892 :
13893 : /** Write an attribute from a string value.
13894 : *
13895 : * Type conversion will be performed if needed. If the attribute contains
13896 : * multiple values, only the first one will be updated.
13897 : *
13898 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13899 : *
13900 : * @param hAttr Attribute
13901 : * @param pszVal Pointer to a string.
13902 : * @return TRUE in case of success.
13903 : */
13904 207 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13905 : {
13906 207 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13907 207 : return hAttr->m_poImpl->Write(pszVal);
13908 : }
13909 :
13910 : /************************************************************************/
13911 : /* GDALAttributeWriteInt() */
13912 : /************************************************************************/
13913 :
13914 : /** Write an attribute from a integer value.
13915 : *
13916 : * Type conversion will be performed if needed. If the attribute contains
13917 : * multiple values, only the first one will be updated.
13918 : *
13919 : * This is the same as the C++ method GDALAttribute::WriteInt()
13920 : *
13921 : * @param hAttr Attribute
13922 : * @param nVal Value.
13923 : * @return TRUE in case of success.
13924 : */
13925 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13926 : {
13927 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13928 22 : return hAttr->m_poImpl->WriteInt(nVal);
13929 : }
13930 :
13931 : /************************************************************************/
13932 : /* GDALAttributeWriteInt64() */
13933 : /************************************************************************/
13934 :
13935 : /** Write an attribute from an int64_t value.
13936 : *
13937 : * Type conversion will be performed if needed. If the attribute contains
13938 : * multiple values, only the first one will be updated.
13939 : *
13940 : * This is the same as the C++ method GDALAttribute::WriteLong()
13941 : *
13942 : * @param hAttr Attribute
13943 : * @param nVal Value.
13944 : * @return TRUE in case of success.
13945 : */
13946 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13947 : {
13948 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13949 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13950 : }
13951 :
13952 : /************************************************************************/
13953 : /* GDALAttributeWriteDouble() */
13954 : /************************************************************************/
13955 :
13956 : /** Write an attribute from a double value.
13957 : *
13958 : * Type conversion will be performed if needed. If the attribute contains
13959 : * multiple values, only the first one will be updated.
13960 : *
13961 : * This is the same as the C++ method GDALAttribute::Write(double);
13962 : *
13963 : * @param hAttr Attribute
13964 : * @param dfVal Value.
13965 : *
13966 : * @return TRUE in case of success.
13967 : */
13968 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13969 : {
13970 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13971 11 : return hAttr->m_poImpl->Write(dfVal);
13972 : }
13973 :
13974 : /************************************************************************/
13975 : /* GDALAttributeWriteStringArray() */
13976 : /************************************************************************/
13977 :
13978 : /** Write an attribute from an array of strings.
13979 : *
13980 : * Type conversion will be performed if needed.
13981 : *
13982 : * Exactly GetTotalElementsCount() strings must be provided
13983 : *
13984 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13985 : *
13986 : * @param hAttr Attribute
13987 : * @param papszValues Array of strings.
13988 : * @return TRUE in case of success.
13989 : */
13990 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13991 : CSLConstList papszValues)
13992 : {
13993 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13994 8 : return hAttr->m_poImpl->Write(papszValues);
13995 : }
13996 :
13997 : /************************************************************************/
13998 : /* GDALAttributeWriteIntArray() */
13999 : /************************************************************************/
14000 :
14001 : /** Write an attribute from an array of int.
14002 : *
14003 : * Type conversion will be performed if needed.
14004 : *
14005 : * Exactly GetTotalElementsCount() strings must be provided
14006 : *
14007 : * This is the same as the C++ method GDALAttribute::Write(const int *,
14008 : * size_t)
14009 : *
14010 : * @param hAttr Attribute
14011 : * @param panValues Array of int.
14012 : * @param nCount Should be equal to GetTotalElementsCount().
14013 : * @return TRUE in case of success.
14014 : */
14015 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14016 : size_t nCount)
14017 : {
14018 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14019 11 : return hAttr->m_poImpl->Write(panValues, nCount);
14020 : }
14021 :
14022 : /************************************************************************/
14023 : /* GDALAttributeWriteInt64Array() */
14024 : /************************************************************************/
14025 :
14026 : /** Write an attribute from an array of int64_t.
14027 : *
14028 : * Type conversion will be performed if needed.
14029 : *
14030 : * Exactly GetTotalElementsCount() strings must be provided
14031 : *
14032 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14033 : * size_t)
14034 : *
14035 : * @param hAttr Attribute
14036 : * @param panValues Array of int64_t.
14037 : * @param nCount Should be equal to GetTotalElementsCount().
14038 : * @return TRUE in case of success.
14039 : */
14040 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14041 : size_t nCount)
14042 : {
14043 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14044 10 : return hAttr->m_poImpl->Write(panValues, nCount);
14045 : }
14046 :
14047 : /************************************************************************/
14048 : /* GDALAttributeWriteDoubleArray() */
14049 : /************************************************************************/
14050 :
14051 : /** Write an attribute from an array of double.
14052 : *
14053 : * Type conversion will be performed if needed.
14054 : *
14055 : * Exactly GetTotalElementsCount() strings must be provided
14056 : *
14057 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14058 : * size_t)
14059 : *
14060 : * @param hAttr Attribute
14061 : * @param padfValues Array of double.
14062 : * @param nCount Should be equal to GetTotalElementsCount().
14063 : * @return TRUE in case of success.
14064 : */
14065 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14066 : const double *padfValues, size_t nCount)
14067 : {
14068 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14069 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14070 : }
14071 :
14072 : /************************************************************************/
14073 : /* GDALAttributeRename() */
14074 : /************************************************************************/
14075 :
14076 : /** Rename the attribute.
14077 : *
14078 : * This is not implemented by all drivers.
14079 : *
14080 : * Drivers known to implement it: MEM, netCDF.
14081 : *
14082 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14083 : *
14084 : * @return true in case of success
14085 : * @since GDAL 3.8
14086 : */
14087 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14088 : {
14089 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14090 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14091 27 : return hAttr->m_poImpl->Rename(pszNewName);
14092 : }
14093 :
14094 : /************************************************************************/
14095 : /* GDALDimensionRelease() */
14096 : /************************************************************************/
14097 :
14098 : /** Release the GDAL in-memory object associated with a GDALDimension.
14099 : *
14100 : * Note: when applied on a object coming from a driver, this does not
14101 : * destroy the object in the file, database, etc...
14102 : */
14103 5324 : void GDALDimensionRelease(GDALDimensionH hDim)
14104 : {
14105 5324 : delete hDim;
14106 5324 : }
14107 :
14108 : /************************************************************************/
14109 : /* GDALDimensionGetName() */
14110 : /************************************************************************/
14111 :
14112 : /** Return dimension name.
14113 : *
14114 : * This is the same as the C++ method GDALDimension::GetName()
14115 : */
14116 296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14117 : {
14118 296 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14119 296 : return hDim->m_poImpl->GetName().c_str();
14120 : }
14121 :
14122 : /************************************************************************/
14123 : /* GDALDimensionGetFullName() */
14124 : /************************************************************************/
14125 :
14126 : /** Return dimension full name.
14127 : *
14128 : * This is the same as the C++ method GDALDimension::GetFullName()
14129 : */
14130 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14131 : {
14132 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14133 82 : return hDim->m_poImpl->GetFullName().c_str();
14134 : }
14135 :
14136 : /************************************************************************/
14137 : /* GDALDimensionGetType() */
14138 : /************************************************************************/
14139 :
14140 : /** Return dimension type.
14141 : *
14142 : * This is the same as the C++ method GDALDimension::GetType()
14143 : */
14144 70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14145 : {
14146 70 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14147 70 : return hDim->m_poImpl->GetType().c_str();
14148 : }
14149 :
14150 : /************************************************************************/
14151 : /* GDALDimensionGetDirection() */
14152 : /************************************************************************/
14153 :
14154 : /** Return dimension direction.
14155 : *
14156 : * This is the same as the C++ method GDALDimension::GetDirection()
14157 : */
14158 38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14159 : {
14160 38 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14161 38 : return hDim->m_poImpl->GetDirection().c_str();
14162 : }
14163 :
14164 : /************************************************************************/
14165 : /* GDALDimensionGetSize() */
14166 : /************************************************************************/
14167 :
14168 : /** Return the size, that is the number of values along the dimension.
14169 : *
14170 : * This is the same as the C++ method GDALDimension::GetSize()
14171 : */
14172 3962 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14173 : {
14174 3962 : VALIDATE_POINTER1(hDim, __func__, 0);
14175 3962 : return hDim->m_poImpl->GetSize();
14176 : }
14177 :
14178 : /************************************************************************/
14179 : /* GDALDimensionGetIndexingVariable() */
14180 : /************************************************************************/
14181 :
14182 : /** Return the variable that is used to index the dimension (if there is one).
14183 : *
14184 : * This is the array, typically one-dimensional, describing the values taken
14185 : * by the dimension.
14186 : *
14187 : * The returned value should be freed with GDALMDArrayRelease().
14188 : *
14189 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14190 : */
14191 140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14192 : {
14193 140 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14194 280 : auto var(hDim->m_poImpl->GetIndexingVariable());
14195 140 : if (!var)
14196 11 : return nullptr;
14197 129 : return new GDALMDArrayHS(var);
14198 : }
14199 :
14200 : /************************************************************************/
14201 : /* GDALDimensionSetIndexingVariable() */
14202 : /************************************************************************/
14203 :
14204 : /** Set the variable that is used to index the dimension.
14205 : *
14206 : * This is the array, typically one-dimensional, describing the values taken
14207 : * by the dimension.
14208 : *
14209 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14210 : *
14211 : * @return TRUE in case of success.
14212 : */
14213 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14214 : {
14215 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14216 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14217 46 : : nullptr);
14218 : }
14219 :
14220 : /************************************************************************/
14221 : /* GDALDimensionRename() */
14222 : /************************************************************************/
14223 :
14224 : /** Rename the dimension.
14225 : *
14226 : * This is not implemented by all drivers.
14227 : *
14228 : * Drivers known to implement it: MEM, netCDF.
14229 : *
14230 : * This is the same as the C++ method GDALDimension::Rename()
14231 : *
14232 : * @return true in case of success
14233 : * @since GDAL 3.8
14234 : */
14235 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14236 : {
14237 31 : VALIDATE_POINTER1(hDim, __func__, false);
14238 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14239 31 : return hDim->m_poImpl->Rename(pszNewName);
14240 : }
14241 :
14242 : /************************************************************************/
14243 : /* GDALDatasetGetRootGroup() */
14244 : /************************************************************************/
14245 :
14246 : /** Return the root GDALGroup of this dataset.
14247 : *
14248 : * Only valid for multidimensional datasets.
14249 : *
14250 : * The returned value must be freed with GDALGroupRelease().
14251 : *
14252 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14253 : *
14254 : * @since GDAL 3.1
14255 : */
14256 1269 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14257 : {
14258 1269 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14259 1269 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14260 1269 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14261 : }
14262 :
14263 : /************************************************************************/
14264 : /* GDALRasterBandAsMDArray() */
14265 : /************************************************************************/
14266 :
14267 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14268 : *
14269 : * The band must be linked to a GDALDataset. If this dataset is not already
14270 : * marked as shared, it will be, so that the returned array holds a reference
14271 : * to it.
14272 : *
14273 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14274 : * returned array will have an associated indexing variable.
14275 : *
14276 : * The returned pointer must be released with GDALMDArrayRelease().
14277 : *
14278 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14279 : *
14280 : * @return a new array, or NULL.
14281 : *
14282 : * @since GDAL 3.1
14283 : */
14284 24 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14285 : {
14286 24 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14287 48 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14288 24 : if (!poArray)
14289 0 : return nullptr;
14290 24 : return new GDALMDArrayHS(poArray);
14291 : }
14292 :
14293 : /************************************************************************/
14294 : /* GDALDatasetAsMDArray() */
14295 : /************************************************************************/
14296 :
14297 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14298 : *
14299 : * If this dataset is not already marked as shared, it will be, so that the
14300 : * returned array holds a reference to it.
14301 : *
14302 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14303 : * returned array will have an associated indexing variable.
14304 : *
14305 : * The currently supported list of options is:
14306 : * <ul>
14307 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14308 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14309 : * and the last (fastest changing direction) is X
14310 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14311 : * and the last (fastest changing direction) is Band.
14312 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14313 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14314 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14315 : * "Y,X,Band" is use.
14316 : * </li>
14317 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14318 : * item from which to build the band indexing variable.
14319 : * <ul>
14320 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14321 : * <li>"{None}" means that no band indexing variable must be created.</li>
14322 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14323 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14324 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14325 : * </ul>
14326 : * </li>
14327 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14328 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14329 : * Defaults to String.
14330 : * </li>
14331 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14332 : * Defaults to "Band".
14333 : * </li>
14334 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14335 : * </li>
14336 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14337 : * </li>
14338 : * </ul>
14339 : *
14340 : * The returned pointer must be released with GDALMDArrayRelease().
14341 : *
14342 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14343 : * GDALDataset::AsMDArray()
14344 : *
14345 : * This is the same as the C++ method GDALDataset::AsMDArray().
14346 : *
14347 : * @param hDS Dataset handle.
14348 : * @param papszOptions Null-terminated list of strings, or nullptr.
14349 : * @return a new array, or NULL.
14350 : *
14351 : * @since GDAL 3.12
14352 : */
14353 14 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14354 : {
14355 14 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14356 28 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14357 14 : if (!poArray)
14358 3 : return nullptr;
14359 11 : return new GDALMDArrayHS(poArray);
14360 : }
14361 :
14362 : /************************************************************************/
14363 : /* GDALMDArrayAsClassicDataset() */
14364 : /************************************************************************/
14365 :
14366 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14367 : *
14368 : * Only 2D or more arrays are supported.
14369 : *
14370 : * In the case of > 2D arrays, additional dimensions will be represented as
14371 : * raster bands.
14372 : *
14373 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14374 : * GDALDataset::AsMDArray()
14375 : *
14376 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14377 : *
14378 : * @param hArray Array.
14379 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14380 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14381 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14382 : */
14383 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14384 : size_t iYDim)
14385 : {
14386 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14387 0 : return GDALDataset::ToHandle(
14388 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14389 : }
14390 :
14391 : /************************************************************************/
14392 : /* GDALMDArrayAsClassicDatasetEx() */
14393 : /************************************************************************/
14394 :
14395 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14396 : *
14397 : * Only 2D or more arrays are supported.
14398 : *
14399 : * In the case of > 2D arrays, additional dimensions will be represented as
14400 : * raster bands.
14401 : *
14402 : * The "reverse" method is GDALRasterBand::AsMDArray().
14403 : *
14404 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14405 : * @param hArray Array.
14406 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14407 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14408 : * Ignored if the dimension count is 1.
14409 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14410 : * BAND_IMAGERY_METADATA option.
14411 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14412 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14413 : * @since GDAL 3.8
14414 : */
14415 102 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14416 : size_t iYDim, GDALGroupH hRootGroup,
14417 : CSLConstList papszOptions)
14418 : {
14419 102 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14420 204 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14421 204 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14422 204 : papszOptions));
14423 : }
14424 :
14425 : //! @cond Doxygen_Suppress
14426 :
14427 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14428 : const std::string &osName,
14429 : const std::string &osValue,
14430 180 : GDALExtendedDataTypeSubType eSubType)
14431 : : GDALAbstractMDArray(osParentName, osName),
14432 : GDALAttribute(osParentName, osName),
14433 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14434 : {
14435 180 : }
14436 :
14437 : const std::vector<std::shared_ptr<GDALDimension>> &
14438 30 : GDALAttributeString::GetDimensions() const
14439 : {
14440 30 : return m_dims;
14441 : }
14442 :
14443 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14444 : {
14445 21 : return m_dt;
14446 : }
14447 :
14448 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14449 : const GPtrDiff_t *,
14450 : const GDALExtendedDataType &bufferDataType,
14451 : void *pDstBuffer) const
14452 : {
14453 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14454 0 : return false;
14455 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14456 10 : if (!pszStr)
14457 0 : return false;
14458 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14459 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14460 10 : return true;
14461 : }
14462 :
14463 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14464 : const std::string &osName,
14465 66 : double dfValue)
14466 : : GDALAbstractMDArray(osParentName, osName),
14467 : GDALAttribute(osParentName, osName),
14468 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14469 : {
14470 66 : }
14471 :
14472 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14473 : const std::string &osName,
14474 27 : int nValue)
14475 : : GDALAbstractMDArray(osParentName, osName),
14476 : GDALAttribute(osParentName, osName),
14477 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14478 : {
14479 27 : }
14480 :
14481 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14482 : const std::string &osName,
14483 7 : const std::vector<GUInt32> &anValues)
14484 : : GDALAbstractMDArray(osParentName, osName),
14485 : GDALAttribute(osParentName, osName),
14486 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14487 : {
14488 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14489 14 : std::string(), "dim0", std::string(), std::string(),
14490 7 : m_anValuesUInt32.size()));
14491 7 : }
14492 :
14493 : const std::vector<std::shared_ptr<GDALDimension>> &
14494 14 : GDALAttributeNumeric::GetDimensions() const
14495 : {
14496 14 : return m_dims;
14497 : }
14498 :
14499 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14500 : {
14501 8 : return m_dt;
14502 : }
14503 :
14504 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14505 : const size_t *count, const GInt64 *arrayStep,
14506 : const GPtrDiff_t *bufferStride,
14507 : const GDALExtendedDataType &bufferDataType,
14508 : void *pDstBuffer) const
14509 : {
14510 4 : if (m_dims.empty())
14511 : {
14512 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14513 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14514 : bufferDataType);
14515 : else
14516 : {
14517 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14518 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14519 : bufferDataType);
14520 : }
14521 : }
14522 : else
14523 : {
14524 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14525 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14526 30 : for (size_t i = 0; i < count[0]; ++i)
14527 : {
14528 29 : GDALExtendedDataType::CopyValue(
14529 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14530 29 : i * arrayStep[0])],
14531 29 : m_dt, pabyDstBuffer, bufferDataType);
14532 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14533 : }
14534 : }
14535 4 : return true;
14536 : }
14537 :
14538 224 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14539 : const std::string &osParentName, const std::string &osName,
14540 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14541 224 : double dfIncrement, double dfOffsetInIncrement)
14542 : : GDALAbstractMDArray(osParentName, osName),
14543 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14544 : m_dfIncrement(dfIncrement),
14545 448 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14546 : {
14547 224 : }
14548 :
14549 224 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14550 : const std::string &osParentName, const std::string &osName,
14551 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14552 : double dfIncrement, double dfOffsetInIncrement)
14553 : {
14554 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14555 224 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14556 224 : poArray->SetSelf(poArray);
14557 224 : return poArray;
14558 : }
14559 :
14560 : const std::vector<std::shared_ptr<GDALDimension>> &
14561 1351 : GDALMDArrayRegularlySpaced::GetDimensions() const
14562 : {
14563 1351 : return m_dims;
14564 : }
14565 :
14566 570 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14567 : {
14568 570 : return m_dt;
14569 : }
14570 :
14571 : std::vector<std::shared_ptr<GDALAttribute>>
14572 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14573 : {
14574 4 : return m_attributes;
14575 : }
14576 :
14577 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14578 : const std::shared_ptr<GDALAttribute> &poAttr)
14579 : {
14580 0 : m_attributes.emplace_back(poAttr);
14581 0 : }
14582 :
14583 293 : bool GDALMDArrayRegularlySpaced::IRead(
14584 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14585 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14586 : void *pDstBuffer) const
14587 : {
14588 293 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14589 16237 : for (size_t i = 0; i < count[0]; i++)
14590 : {
14591 15944 : const double dfVal =
14592 15944 : m_dfStart +
14593 15944 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14594 15944 : m_dfOffsetInIncrement) *
14595 15944 : m_dfIncrement;
14596 15944 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14597 : bufferDataType);
14598 15944 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14599 : }
14600 293 : return true;
14601 : }
14602 :
14603 3999 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14604 : const std::string &osParentName, const std::string &osName,
14605 3999 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14606 3999 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14607 : {
14608 3999 : }
14609 :
14610 : std::shared_ptr<GDALMDArray>
14611 1459 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14612 : {
14613 1459 : return m_poIndexingVariable.lock();
14614 : }
14615 :
14616 : // cppcheck-suppress passedByValue
14617 644 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14618 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14619 : {
14620 644 : m_poIndexingVariable = poIndexingVariable;
14621 644 : return true;
14622 : }
14623 :
14624 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14625 : {
14626 33 : m_nSize = nNewSize;
14627 33 : }
14628 :
14629 : /************************************************************************/
14630 : /* GDALPamMultiDim::Private */
14631 : /************************************************************************/
14632 :
14633 : struct GDALPamMultiDim::Private
14634 : {
14635 : std::string m_osFilename{};
14636 : std::string m_osPamFilename{};
14637 :
14638 : struct Statistics
14639 : {
14640 : bool bHasStats = false;
14641 : bool bApproxStats = false;
14642 : double dfMin = 0;
14643 : double dfMax = 0;
14644 : double dfMean = 0;
14645 : double dfStdDev = 0;
14646 : GUInt64 nValidCount = 0;
14647 : };
14648 :
14649 : struct ArrayInfo
14650 : {
14651 : std::shared_ptr<OGRSpatialReference> poSRS{};
14652 : // cppcheck-suppress unusedStructMember
14653 : Statistics stats{};
14654 : };
14655 :
14656 : typedef std::pair<std::string, std::string> NameContext;
14657 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14658 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14659 : bool m_bDirty = false;
14660 : bool m_bLoaded = false;
14661 : };
14662 :
14663 : /************************************************************************/
14664 : /* GDALPamMultiDim */
14665 : /************************************************************************/
14666 :
14667 1636 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14668 1636 : : d(new Private())
14669 : {
14670 1636 : d->m_osFilename = osFilename;
14671 1636 : }
14672 :
14673 : /************************************************************************/
14674 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14675 : /************************************************************************/
14676 :
14677 1636 : GDALPamMultiDim::~GDALPamMultiDim()
14678 : {
14679 1636 : if (d->m_bDirty)
14680 30 : Save();
14681 1636 : }
14682 :
14683 : /************************************************************************/
14684 : /* GDALPamMultiDim::Load() */
14685 : /************************************************************************/
14686 :
14687 107 : void GDALPamMultiDim::Load()
14688 : {
14689 107 : if (d->m_bLoaded)
14690 96 : return;
14691 45 : d->m_bLoaded = true;
14692 :
14693 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14694 45 : d->m_osPamFilename =
14695 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14696 45 : CPLXMLTreeCloser oTree(nullptr);
14697 : {
14698 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14699 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14700 : }
14701 45 : if (!oTree)
14702 : {
14703 34 : return;
14704 : }
14705 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14706 11 : if (!poPAMMultiDim)
14707 0 : return;
14708 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14709 24 : psIter = psIter->psNext)
14710 : {
14711 24 : if (psIter->eType == CXT_Element &&
14712 24 : strcmp(psIter->pszValue, "Array") == 0)
14713 : {
14714 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14715 13 : if (!pszName)
14716 0 : continue;
14717 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14718 : const auto oKey =
14719 26 : std::pair<std::string, std::string>(pszName, pszContext);
14720 :
14721 : /* --------------------------------------------------------------------
14722 : */
14723 : /* Check for an SRS node. */
14724 : /* --------------------------------------------------------------------
14725 : */
14726 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14727 13 : if (psSRSNode)
14728 : {
14729 : std::shared_ptr<OGRSpatialReference> poSRS =
14730 6 : std::make_shared<OGRSpatialReference>();
14731 3 : poSRS->SetFromUserInput(
14732 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14733 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14734 3 : const char *pszMapping = CPLGetXMLValue(
14735 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14736 3 : if (pszMapping)
14737 : {
14738 : char **papszTokens =
14739 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14740 6 : std::vector<int> anMapping;
14741 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14742 : {
14743 6 : anMapping.push_back(atoi(papszTokens[i]));
14744 : }
14745 3 : CSLDestroy(papszTokens);
14746 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14747 : }
14748 : else
14749 : {
14750 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14751 : }
14752 :
14753 : const char *pszCoordinateEpoch =
14754 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14755 3 : if (pszCoordinateEpoch)
14756 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14757 :
14758 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14759 : }
14760 :
14761 : const CPLXMLNode *psStatistics =
14762 13 : CPLGetXMLNode(psIter, "Statistics");
14763 13 : if (psStatistics)
14764 : {
14765 7 : Private::Statistics sStats;
14766 7 : sStats.bHasStats = true;
14767 7 : sStats.bApproxStats = CPLTestBool(
14768 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14769 7 : sStats.dfMin =
14770 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14771 7 : sStats.dfMax =
14772 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14773 7 : sStats.dfMean =
14774 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14775 7 : sStats.dfStdDev =
14776 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14777 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14778 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14779 7 : d->m_oMapArray[oKey].stats = sStats;
14780 13 : }
14781 : }
14782 : else
14783 : {
14784 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14785 11 : psIter->psNext = nullptr;
14786 11 : d->m_apoOtherNodes.emplace_back(
14787 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14788 11 : psIter->psNext = psNextBackup;
14789 : }
14790 : }
14791 : }
14792 :
14793 : /************************************************************************/
14794 : /* GDALPamMultiDim::Save() */
14795 : /************************************************************************/
14796 :
14797 30 : void GDALPamMultiDim::Save()
14798 : {
14799 : CPLXMLTreeCloser oTree(
14800 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14801 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14802 : {
14803 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14804 : }
14805 112 : for (const auto &kv : d->m_oMapArray)
14806 : {
14807 : CPLXMLNode *psArrayNode =
14808 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14809 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14810 82 : if (!kv.first.second.empty())
14811 : {
14812 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14813 : kv.first.second.c_str());
14814 : }
14815 82 : if (kv.second.poSRS)
14816 : {
14817 71 : char *pszWKT = nullptr;
14818 : {
14819 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14820 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14821 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14822 : }
14823 : CPLXMLNode *psSRSNode =
14824 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14825 71 : CPLFree(pszWKT);
14826 : const auto &mapping =
14827 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14828 142 : CPLString osMapping;
14829 213 : for (size_t i = 0; i < mapping.size(); ++i)
14830 : {
14831 142 : if (!osMapping.empty())
14832 71 : osMapping += ",";
14833 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14834 : }
14835 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14836 : osMapping.c_str());
14837 :
14838 : const double dfCoordinateEpoch =
14839 71 : kv.second.poSRS->GetCoordinateEpoch();
14840 71 : if (dfCoordinateEpoch > 0)
14841 : {
14842 : std::string osCoordinateEpoch =
14843 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14844 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14845 : {
14846 6 : while (osCoordinateEpoch.back() == '0')
14847 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14848 : }
14849 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14850 : osCoordinateEpoch.c_str());
14851 : }
14852 : }
14853 :
14854 82 : if (kv.second.stats.bHasStats)
14855 : {
14856 : CPLXMLNode *psMDArray =
14857 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14858 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14859 8 : kv.second.stats.bApproxStats ? "1"
14860 : : "0");
14861 8 : CPLCreateXMLElementAndValue(
14862 : psMDArray, "Minimum",
14863 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14864 8 : CPLCreateXMLElementAndValue(
14865 : psMDArray, "Maximum",
14866 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14867 8 : CPLCreateXMLElementAndValue(
14868 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14869 8 : CPLCreateXMLElementAndValue(
14870 : psMDArray, "StdDev",
14871 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14872 8 : CPLCreateXMLElementAndValue(
14873 : psMDArray, "ValidSampleCount",
14874 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14875 : }
14876 : }
14877 :
14878 : int bSaved;
14879 60 : CPLErrorAccumulator oErrorAccumulator;
14880 : {
14881 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14882 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14883 : bSaved =
14884 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14885 : }
14886 :
14887 30 : const char *pszNewPam = nullptr;
14888 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14889 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14890 : {
14891 0 : CPLErrorReset();
14892 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14893 : }
14894 : else
14895 : {
14896 30 : oErrorAccumulator.ReplayErrors();
14897 : }
14898 30 : }
14899 :
14900 : /************************************************************************/
14901 : /* GDALPamMultiDim::GetSpatialRef() */
14902 : /************************************************************************/
14903 :
14904 : std::shared_ptr<OGRSpatialReference>
14905 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14906 : const std::string &osContext)
14907 : {
14908 10 : Load();
14909 : auto oIter =
14910 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14911 10 : if (oIter != d->m_oMapArray.end())
14912 2 : return oIter->second.poSRS;
14913 8 : return nullptr;
14914 : }
14915 :
14916 : /************************************************************************/
14917 : /* GDALPamMultiDim::SetSpatialRef() */
14918 : /************************************************************************/
14919 :
14920 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14921 : const std::string &osContext,
14922 : const OGRSpatialReference *poSRS)
14923 : {
14924 72 : Load();
14925 72 : d->m_bDirty = true;
14926 72 : if (poSRS && !poSRS->IsEmpty())
14927 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14928 : poSRS->Clone());
14929 : else
14930 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14931 1 : .poSRS.reset();
14932 72 : }
14933 :
14934 : /************************************************************************/
14935 : /* GetStatistics() */
14936 : /************************************************************************/
14937 :
14938 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14939 : const std::string &osContext,
14940 : bool bApproxOK, double *pdfMin,
14941 : double *pdfMax, double *pdfMean,
14942 : double *pdfStdDev, GUInt64 *pnValidCount)
14943 : {
14944 16 : Load();
14945 : auto oIter =
14946 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14947 16 : if (oIter == d->m_oMapArray.end())
14948 9 : return CE_Failure;
14949 7 : const auto &stats = oIter->second.stats;
14950 7 : if (!stats.bHasStats)
14951 1 : return CE_Failure;
14952 6 : if (!bApproxOK && stats.bApproxStats)
14953 0 : return CE_Failure;
14954 6 : if (pdfMin)
14955 6 : *pdfMin = stats.dfMin;
14956 6 : if (pdfMax)
14957 6 : *pdfMax = stats.dfMax;
14958 6 : if (pdfMean)
14959 6 : *pdfMean = stats.dfMean;
14960 6 : if (pdfStdDev)
14961 6 : *pdfStdDev = stats.dfStdDev;
14962 6 : if (pnValidCount)
14963 6 : *pnValidCount = stats.nValidCount;
14964 6 : return CE_None;
14965 : }
14966 :
14967 : /************************************************************************/
14968 : /* SetStatistics() */
14969 : /************************************************************************/
14970 :
14971 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14972 : const std::string &osContext,
14973 : bool bApproxStats, double dfMin,
14974 : double dfMax, double dfMean,
14975 : double dfStdDev, GUInt64 nValidCount)
14976 : {
14977 8 : Load();
14978 8 : d->m_bDirty = true;
14979 : auto &stats =
14980 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14981 8 : stats.bHasStats = true;
14982 8 : stats.bApproxStats = bApproxStats;
14983 8 : stats.dfMin = dfMin;
14984 8 : stats.dfMax = dfMax;
14985 8 : stats.dfMean = dfMean;
14986 8 : stats.dfStdDev = dfStdDev;
14987 8 : stats.nValidCount = nValidCount;
14988 8 : }
14989 :
14990 : /************************************************************************/
14991 : /* ClearStatistics() */
14992 : /************************************************************************/
14993 :
14994 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14995 : const std::string &osContext)
14996 : {
14997 0 : Load();
14998 0 : d->m_bDirty = true;
14999 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
15000 : false;
15001 0 : }
15002 :
15003 : /************************************************************************/
15004 : /* ClearStatistics() */
15005 : /************************************************************************/
15006 :
15007 1 : void GDALPamMultiDim::ClearStatistics()
15008 : {
15009 1 : Load();
15010 1 : d->m_bDirty = true;
15011 3 : for (auto &kv : d->m_oMapArray)
15012 2 : kv.second.stats.bHasStats = false;
15013 1 : }
15014 :
15015 : /************************************************************************/
15016 : /* GetPAM() */
15017 : /************************************************************************/
15018 :
15019 : /*static*/ std::shared_ptr<GDALPamMultiDim>
15020 939 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15021 : {
15022 939 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15023 939 : if (poPamArray)
15024 581 : return poPamArray->GetPAM();
15025 358 : return nullptr;
15026 : }
15027 :
15028 : /************************************************************************/
15029 : /* GDALPamMDArray */
15030 : /************************************************************************/
15031 :
15032 4498 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15033 : const std::string &osName,
15034 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15035 0 : const std::string &osContext)
15036 : :
15037 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15038 : GDALAbstractMDArray(osParentName, osName),
15039 : #endif
15040 4498 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15041 : {
15042 4498 : }
15043 :
15044 : /************************************************************************/
15045 : /* GDALPamMDArray::SetSpatialRef() */
15046 : /************************************************************************/
15047 :
15048 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15049 : {
15050 72 : if (!m_poPam)
15051 0 : return false;
15052 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15053 72 : return true;
15054 : }
15055 :
15056 : /************************************************************************/
15057 : /* GDALPamMDArray::GetSpatialRef() */
15058 : /************************************************************************/
15059 :
15060 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15061 : {
15062 10 : if (!m_poPam)
15063 0 : return nullptr;
15064 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15065 : }
15066 :
15067 : /************************************************************************/
15068 : /* GetStatistics() */
15069 : /************************************************************************/
15070 :
15071 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15072 : double *pdfMin, double *pdfMax,
15073 : double *pdfMean, double *pdfStdDev,
15074 : GUInt64 *pnValidCount,
15075 : GDALProgressFunc pfnProgress,
15076 : void *pProgressData)
15077 : {
15078 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15079 : bApproxOK, pdfMin, pdfMax, pdfMean,
15080 16 : pdfStdDev, pnValidCount) == CE_None)
15081 : {
15082 6 : return CE_None;
15083 : }
15084 10 : if (!bForce)
15085 4 : return CE_Warning;
15086 :
15087 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15088 : pdfMean, pdfStdDev, pnValidCount,
15089 6 : pfnProgress, pProgressData);
15090 : }
15091 :
15092 : /************************************************************************/
15093 : /* SetStatistics() */
15094 : /************************************************************************/
15095 :
15096 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15097 : double dfMax, double dfMean, double dfStdDev,
15098 : GUInt64 nValidCount,
15099 : CSLConstList /* papszOptions */)
15100 : {
15101 8 : if (!m_poPam)
15102 0 : return false;
15103 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15104 : dfMax, dfMean, dfStdDev, nValidCount);
15105 8 : return true;
15106 : }
15107 :
15108 : /************************************************************************/
15109 : /* ClearStatistics() */
15110 : /************************************************************************/
15111 :
15112 0 : void GDALPamMDArray::ClearStatistics()
15113 : {
15114 0 : if (!m_poPam)
15115 0 : return;
15116 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15117 : }
15118 :
15119 : /************************************************************************/
15120 : /* GDALMDIAsAttribute::GetDimensions() */
15121 : /************************************************************************/
15122 :
15123 : const std::vector<std::shared_ptr<GDALDimension>> &
15124 29 : GDALMDIAsAttribute::GetDimensions() const
15125 : {
15126 29 : return m_dims;
15127 : }
15128 :
15129 : /************************************************************************/
15130 : /* GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo() */
15131 : /************************************************************************/
15132 :
15133 58 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
15134 : {
15135 29 : clear();
15136 29 : }
15137 :
15138 : /************************************************************************/
15139 : /* GDALMDArrayRawBlockInfo::clear() */
15140 : /************************************************************************/
15141 :
15142 47 : void GDALMDArrayRawBlockInfo::clear()
15143 : {
15144 47 : CPLFree(pszFilename);
15145 47 : pszFilename = nullptr;
15146 47 : CSLDestroy(papszInfo);
15147 47 : papszInfo = nullptr;
15148 47 : nOffset = 0;
15149 47 : nSize = 0;
15150 47 : CPLFree(pabyInlineData);
15151 47 : pabyInlineData = nullptr;
15152 47 : }
15153 :
15154 : /************************************************************************/
15155 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15156 : /************************************************************************/
15157 :
15158 4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15159 4 : const GDALMDArrayRawBlockInfo &other)
15160 4 : : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
15161 4 : nOffset(other.nOffset), nSize(other.nSize),
15162 4 : papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
15163 : {
15164 4 : if (other.pabyInlineData)
15165 : {
15166 3 : pabyInlineData = static_cast<GByte *>(
15167 3 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15168 3 : if (pabyInlineData)
15169 3 : memcpy(pabyInlineData, other.pabyInlineData,
15170 3 : static_cast<size_t>(other.nSize));
15171 : }
15172 4 : }
15173 :
15174 : /************************************************************************/
15175 : /* GDALMDArrayRawBlockInfo::operator=() */
15176 : /************************************************************************/
15177 :
15178 : GDALMDArrayRawBlockInfo &
15179 7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
15180 : {
15181 7 : if (this != &other)
15182 : {
15183 5 : CPLFree(pszFilename);
15184 5 : pszFilename =
15185 5 : other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
15186 5 : nOffset = other.nOffset;
15187 5 : nSize = other.nSize;
15188 5 : CSLDestroy(papszInfo);
15189 5 : papszInfo = CSLDuplicate(other.papszInfo);
15190 5 : CPLFree(pabyInlineData);
15191 5 : pabyInlineData = nullptr;
15192 5 : if (other.pabyInlineData)
15193 : {
15194 4 : pabyInlineData = static_cast<GByte *>(
15195 4 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15196 4 : if (pabyInlineData)
15197 4 : memcpy(pabyInlineData, other.pabyInlineData,
15198 4 : static_cast<size_t>(other.nSize));
15199 : }
15200 : }
15201 7 : return *this;
15202 : }
15203 :
15204 : /************************************************************************/
15205 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15206 : /************************************************************************/
15207 :
15208 2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15209 2 : GDALMDArrayRawBlockInfo &&other)
15210 2 : : pszFilename(other.pszFilename), nOffset(other.nOffset),
15211 2 : nSize(other.nSize), papszInfo(other.papszInfo),
15212 2 : pabyInlineData(other.pabyInlineData)
15213 : {
15214 2 : other.pszFilename = nullptr;
15215 2 : other.papszInfo = nullptr;
15216 2 : other.pabyInlineData = nullptr;
15217 2 : }
15218 :
15219 : /************************************************************************/
15220 : /* GDALMDArrayRawBlockInfo::operator=() */
15221 : /************************************************************************/
15222 :
15223 : GDALMDArrayRawBlockInfo &
15224 2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
15225 : {
15226 2 : if (this != &other)
15227 : {
15228 2 : std::swap(pszFilename, other.pszFilename);
15229 2 : nOffset = other.nOffset;
15230 2 : nSize = other.nSize;
15231 2 : std::swap(papszInfo, other.papszInfo);
15232 2 : std::swap(pabyInlineData, other.pabyInlineData);
15233 : }
15234 2 : return *this;
15235 : }
15236 :
15237 : //! @endcond
15238 :
15239 : /************************************************************************/
15240 : /* GDALMDArray::GetRawBlockInfo() */
15241 : /************************************************************************/
15242 :
15243 : /** Return information on a raw block.
15244 : *
15245 : * The block coordinates must be between 0 and
15246 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15247 : * 0 and GetDimensionCount()-1.
15248 : *
15249 : * If the queried block has valid coordinates but is missing in the dataset,
15250 : * all fields of info will be set to 0/nullptr, but the function will return
15251 : * true.
15252 : *
15253 : * This method is only implemented by a subset of drivers. The base
15254 : * implementation just returns false and empty info.
15255 : *
15256 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15257 : *
15258 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15259 : * value is "LITTLE" or "BIG".
15260 : *
15261 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15262 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15263 : * comma-separated)
15264 : *
15265 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15266 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15267 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15268 : * "[idx0,...,idxN]" with the permutation).
15269 : *
15270 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15271 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15272 : * that for each queried VRT block, there is one and only one source that
15273 : * is used to fill the VRT block and that the block size of this source is
15274 : * exactly the one of the VRT block.
15275 : *
15276 : * This is the same as C function GDALMDArrayGetRawBlockInfo().
15277 : *
15278 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15279 : * coordinates.
15280 : * @param[out] info structure to fill with block information.
15281 : * @return true in case of success, or false if an error occurs.
15282 : * @since 3.12
15283 : */
15284 0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
15285 : GDALMDArrayRawBlockInfo &info) const
15286 : {
15287 : (void)panBlockCoordinates;
15288 0 : info.clear();
15289 0 : return false;
15290 : }
15291 :
15292 : /************************************************************************/
15293 : /* GDALMDArrayGetRawBlockInfo() */
15294 : /************************************************************************/
15295 :
15296 : /** Return information on a raw block.
15297 : *
15298 : * The block coordinates must be between 0 and
15299 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15300 : * 0 and GetDimensionCount()-1.
15301 : *
15302 : * If the queried block has valid coordinates but is missing in the dataset,
15303 : * all fields of info will be set to 0/nullptr, but the function will return
15304 : * true.
15305 : *
15306 : * This method is only implemented by a subset of drivers. The base
15307 : * implementation just returns false and empty info.
15308 : *
15309 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15310 : *
15311 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15312 : * value is "LITTLE" or "BIG".
15313 : *
15314 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15315 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15316 : * comma-separated)
15317 : *
15318 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15319 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15320 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15321 : * "[idx0,...,idxN]" with the permutation).
15322 : *
15323 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15324 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15325 : * that for each queried VRT block, there is one and only one source that
15326 : * is used to fill the VRT block and that the block size of this source is
15327 : * exactly the one of the VRT block.
15328 : *
15329 : * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
15330 : *
15331 : * @param hArray handle to array.
15332 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15333 : * coordinates.
15334 : * @param[out] psBlockInfo structure to fill with block information.
15335 : * Must be allocated with GDALMDArrayRawBlockInfoCreate(),
15336 : * and freed with GDALMDArrayRawBlockInfoRelease().
15337 : * @return true in case of success, or false if an error occurs.
15338 : * @since 3.12
15339 : */
15340 18 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
15341 : const uint64_t *panBlockCoordinates,
15342 : GDALMDArrayRawBlockInfo *psBlockInfo)
15343 : {
15344 18 : VALIDATE_POINTER1(hArray, __func__, false);
15345 18 : VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
15346 18 : VALIDATE_POINTER1(psBlockInfo, __func__, false);
15347 18 : return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
15348 : }
15349 :
15350 : /************************************************************************/
15351 : /* GDALMDArrayRawBlockInfoCreate() */
15352 : /************************************************************************/
15353 :
15354 : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
15355 : *
15356 : * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
15357 : *
15358 : * @since 3.12
15359 : */
15360 18 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
15361 : {
15362 18 : return new GDALMDArrayRawBlockInfo();
15363 : }
15364 :
15365 : /************************************************************************/
15366 : /* GDALMDArrayRawBlockInfoRelease() */
15367 : /************************************************************************/
15368 :
15369 : /** Free an instance of GDALMDArrayRawBlockInfo.
15370 : *
15371 : * @since 3.12
15372 : */
15373 18 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
15374 : {
15375 18 : delete psBlockInfo;
15376 18 : }
|