Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_pam_multidim.h"
31 : #include "gdal_rat.h"
32 : #include "gdal_utils.h"
33 : #include "cpl_safemaths.hpp"
34 : #include "memmultidim.h"
35 : #include "ogrsf_frmts.h"
36 : #include "gdalmultidim_priv.h"
37 :
38 : #if defined(__clang__) || defined(_MSC_VER)
39 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40 : #endif
41 :
42 : /************************************************************************/
43 : /* GDALMDArrayUnscaled */
44 : /************************************************************************/
45 :
46 : class GDALMDArrayUnscaled final : public GDALPamMDArray
47 : {
48 : private:
49 : std::shared_ptr<GDALMDArray> m_poParent{};
50 : const GDALExtendedDataType m_dt;
51 : bool m_bHasNoData;
52 : const double m_dfScale;
53 : const double m_dfOffset;
54 : std::vector<GByte> m_abyRawNoData{};
55 :
56 : protected:
57 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58 : double dfScale, double dfOffset,
59 : double dfOverriddenDstNodata, GDALDataType eDT)
60 26 : : GDALAbstractMDArray(std::string(),
61 26 : "Unscaled view of " + poParent->GetFullName()),
62 : GDALPamMDArray(
63 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
64 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65 13 : m_poParent(std::move(poParent)),
66 : m_dt(GDALExtendedDataType::Create(eDT)),
67 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
69 : {
70 13 : m_abyRawNoData.resize(m_dt.GetSize());
71 : const auto eNonComplexDT =
72 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73 26 : GDALCopyWords64(
74 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77 13 : }
78 :
79 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81 : const GDALExtendedDataType &bufferDataType,
82 : void *pDstBuffer) const override;
83 :
84 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86 : const GDALExtendedDataType &bufferDataType,
87 : const void *pSrcBuffer) override;
88 :
89 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90 : CSLConstList papszOptions) const override
91 : {
92 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93 : }
94 :
95 : public:
96 : static std::shared_ptr<GDALMDArrayUnscaled>
97 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98 : double dfOffset, double dfDstNodata, GDALDataType eDT)
99 : {
100 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102 13 : newAr->SetSelf(newAr);
103 13 : return newAr;
104 : }
105 :
106 1 : bool IsWritable() const override
107 : {
108 1 : return m_poParent->IsWritable();
109 : }
110 :
111 15 : const std::string &GetFilename() const override
112 : {
113 15 : return m_poParent->GetFilename();
114 : }
115 :
116 : const std::vector<std::shared_ptr<GDALDimension>> &
117 220 : GetDimensions() const override
118 : {
119 220 : return m_poParent->GetDimensions();
120 : }
121 :
122 103 : const GDALExtendedDataType &GetDataType() const override
123 : {
124 103 : return m_dt;
125 : }
126 :
127 1 : const std::string &GetUnit() const override
128 : {
129 1 : return m_poParent->GetUnit();
130 : }
131 :
132 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133 : {
134 1 : return m_poParent->GetSpatialRef();
135 : }
136 :
137 6 : const void *GetRawNoDataValue() const override
138 : {
139 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140 : }
141 :
142 1 : bool SetRawNoDataValue(const void *pRawNoData) override
143 : {
144 1 : m_bHasNoData = true;
145 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146 1 : return true;
147 : }
148 :
149 4 : std::vector<GUInt64> GetBlockSize() const override
150 : {
151 4 : return m_poParent->GetBlockSize();
152 : }
153 :
154 : std::shared_ptr<GDALAttribute>
155 0 : GetAttribute(const std::string &osName) const override
156 : {
157 0 : return m_poParent->GetAttribute(osName);
158 : }
159 :
160 : std::vector<std::shared_ptr<GDALAttribute>>
161 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
162 : {
163 1 : return m_poParent->GetAttributes(papszOptions);
164 : }
165 :
166 0 : bool SetUnit(const std::string &osUnit) override
167 : {
168 0 : return m_poParent->SetUnit(osUnit);
169 : }
170 :
171 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172 : {
173 0 : return m_poParent->SetSpatialRef(poSRS);
174 : }
175 :
176 : std::shared_ptr<GDALAttribute>
177 1 : CreateAttribute(const std::string &osName,
178 : const std::vector<GUInt64> &anDimensions,
179 : const GDALExtendedDataType &oDataType,
180 : CSLConstList papszOptions = nullptr) override
181 : {
182 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183 1 : papszOptions);
184 : }
185 : };
186 :
187 : /************************************************************************/
188 : /* ~GDALIHasAttribute() */
189 : /************************************************************************/
190 :
191 : GDALIHasAttribute::~GDALIHasAttribute() = default;
192 :
193 : /************************************************************************/
194 : /* GetAttribute() */
195 : /************************************************************************/
196 :
197 : /** Return an attribute by its name.
198 : *
199 : * If the attribute does not exist, nullptr should be silently returned.
200 : *
201 : * @note Driver implementation: this method will fallback to
202 : * GetAttributeFromAttributes() is not explicitly implemented
203 : *
204 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
205 : *
206 : * This is the same as the C function GDALGroupGetAttribute() or
207 : * GDALMDArrayGetAttribute().
208 : *
209 : * @param osName Attribute name
210 : * @return the attribute, or nullptr if it does not exist or an error occurred.
211 : */
212 : std::shared_ptr<GDALAttribute>
213 10737 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
214 : {
215 10737 : return GetAttributeFromAttributes(osName);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetAttributeFromAttributes() */
220 : /************************************************************************/
221 :
222 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
223 : */
224 : std::shared_ptr<GDALAttribute>
225 10737 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226 : {
227 21474 : auto attrs(GetAttributes());
228 85091 : for (const auto &attr : attrs)
229 : {
230 83301 : if (attr->GetName() == osName)
231 8947 : return attr;
232 : }
233 1790 : return nullptr;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetAttributes() */
238 : /************************************************************************/
239 :
240 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241 : *
242 : * If the attribute does not exist, nullptr should be silently returned.
243 : *
244 : * @note Driver implementation: optionally implemented. If implemented,
245 : * GetAttribute() should also be implemented.
246 : *
247 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
248 : *
249 : * This is the same as the C function GDALGroupGetAttributes() or
250 : * GDALMDArrayGetAttributes().
251 :
252 : * @param papszOptions Driver specific options determining how attributes
253 : * should be retrieved. Pass nullptr for default behavior.
254 : *
255 : * @return the attributes.
256 : */
257 : std::vector<std::shared_ptr<GDALAttribute>>
258 203 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259 : {
260 203 : return {};
261 : }
262 :
263 : /************************************************************************/
264 : /* CreateAttribute() */
265 : /************************************************************************/
266 :
267 : /** Create an attribute within a GDALMDArray or GDALGroup.
268 : *
269 : * The attribute might not be "physically" created until a value is written
270 : * into it.
271 : *
272 : * Optionally implemented.
273 : *
274 : * Drivers known to implement it: MEM, netCDF
275 : *
276 : * This is the same as the C function GDALGroupCreateAttribute() or
277 : * GDALMDArrayCreateAttribute()
278 : *
279 : * @param osName Attribute name.
280 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
281 : * dimension first to the fastest varying dimension last.
282 : * Empty for a scalar attribute (common case)
283 : * @param oDataType Attribute data type.
284 : * @param papszOptions Driver specific options determining how the attribute.
285 : * should be created.
286 : *
287 : * @return the new attribute, or nullptr if case of error
288 : */
289 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290 : CPL_UNUSED const std::string &osName,
291 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292 : CPL_UNUSED const GDALExtendedDataType &oDataType,
293 : CPL_UNUSED CSLConstList papszOptions)
294 : {
295 0 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "CreateAttribute() not implemented");
297 0 : return nullptr;
298 : }
299 :
300 : /************************************************************************/
301 : /* DeleteAttribute() */
302 : /************************************************************************/
303 :
304 : /** Delete an attribute from a GDALMDArray or GDALGroup.
305 : *
306 : * Optionally implemented.
307 : *
308 : * After this call, if a previously obtained instance of the deleted object
309 : * is still alive, no method other than for freeing it should be invoked.
310 : *
311 : * Drivers known to implement it: MEM, netCDF
312 : *
313 : * This is the same as the C function GDALGroupDeleteAttribute() or
314 : * GDALMDArrayDeleteAttribute()
315 : *
316 : * @param osName Attribute name.
317 : * @param papszOptions Driver specific options determining how the attribute.
318 : * should be deleted.
319 : *
320 : * @return true in case of success
321 : * @since GDAL 3.8
322 : */
323 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324 : CPL_UNUSED CSLConstList papszOptions)
325 : {
326 0 : CPLError(CE_Failure, CPLE_NotSupported,
327 : "DeleteAttribute() not implemented");
328 0 : return false;
329 : }
330 :
331 : /************************************************************************/
332 : /* GDALGroup() */
333 : /************************************************************************/
334 :
335 : //! @cond Doxygen_Suppress
336 14050 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 14050 : const std::string &osContext)
338 14050 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 28100 : !osParentName.empty()
341 21273 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 35323 : m_osContext(osContext)
344 : {
345 14050 : }
346 :
347 : //! @endcond
348 :
349 : /************************************************************************/
350 : /* ~GDALGroup() */
351 : /************************************************************************/
352 :
353 : GDALGroup::~GDALGroup() = default;
354 :
355 : /************************************************************************/
356 : /* GetMDArrayNames() */
357 : /************************************************************************/
358 :
359 : /** Return the list of multidimensional array names contained in this group.
360 : *
361 : * @note Driver implementation: optionally implemented. If implemented,
362 : * OpenMDArray() should also be implemented.
363 : *
364 : * Drivers known to implement it: MEM, netCDF.
365 : *
366 : * This is the same as the C function GDALGroupGetMDArrayNames().
367 : *
368 : * @param papszOptions Driver specific options determining how arrays
369 : * should be retrieved. Pass nullptr for default behavior.
370 : *
371 : * @return the array names.
372 : */
373 : std::vector<std::string>
374 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375 : {
376 0 : return {};
377 : }
378 :
379 : /************************************************************************/
380 : /* GetMDArrayFullNamesRecursive() */
381 : /************************************************************************/
382 :
383 : /** Return the list of multidimensional array full names contained in this
384 : * group and its subgroups.
385 : *
386 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387 : *
388 : * @param papszGroupOptions Driver specific options determining how groups
389 : * should be retrieved. Pass nullptr for default behavior.
390 : * @param papszArrayOptions Driver specific options determining how arrays
391 : * should be retrieved. Pass nullptr for default behavior.
392 : *
393 : * @return the array full names.
394 : *
395 : * @since 3.11
396 : */
397 : std::vector<std::string>
398 10 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399 : CSLConstList papszArrayOptions) const
400 : {
401 10 : std::vector<std::string> ret;
402 20 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
403 10 : stackGroups.push_back(nullptr); // nullptr means this
404 23 : while (!stackGroups.empty())
405 : {
406 26 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407 13 : stackGroups.erase(stackGroups.begin());
408 13 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409 31 : for (const std::string &arrayName :
410 75 : poCurGroup->GetMDArrayNames(papszArrayOptions))
411 : {
412 62 : std::string osFullName = poCurGroup->GetFullName();
413 31 : if (!osFullName.empty() && osFullName.back() != '/')
414 3 : osFullName += '/';
415 31 : osFullName += arrayName;
416 31 : ret.push_back(std::move(osFullName));
417 : }
418 13 : auto insertionPoint = stackGroups.begin();
419 3 : for (const auto &osSubGroup :
420 19 : poCurGroup->GetGroupNames(papszGroupOptions))
421 : {
422 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423 3 : if (poSubGroup)
424 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
425 : }
426 : }
427 :
428 20 : return ret;
429 : }
430 :
431 : /************************************************************************/
432 : /* OpenMDArray() */
433 : /************************************************************************/
434 :
435 : /** Open and return a multidimensional array.
436 : *
437 : * @note Driver implementation: optionally implemented. If implemented,
438 : * GetMDArrayNames() should also be implemented.
439 : *
440 : * Drivers known to implement it: MEM, netCDF.
441 : *
442 : * This is the same as the C function GDALGroupOpenMDArray().
443 : *
444 : * @param osName Array name.
445 : * @param papszOptions Driver specific options determining how the array should
446 : * be opened. Pass nullptr for default behavior.
447 : *
448 : * @return the array, or nullptr.
449 : */
450 : std::shared_ptr<GDALMDArray>
451 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452 : CPL_UNUSED CSLConstList papszOptions) const
453 : {
454 0 : return nullptr;
455 : }
456 :
457 : /************************************************************************/
458 : /* GetGroupNames() */
459 : /************************************************************************/
460 :
461 : /** Return the list of sub-groups contained in this group.
462 : *
463 : * @note Driver implementation: optionally implemented. If implemented,
464 : * OpenGroup() should also be implemented.
465 : *
466 : * Drivers known to implement it: MEM, netCDF.
467 : *
468 : * This is the same as the C function GDALGroupGetGroupNames().
469 : *
470 : * @param papszOptions Driver specific options determining how groups
471 : * should be retrieved. Pass nullptr for default behavior.
472 : *
473 : * @return the group names.
474 : */
475 : std::vector<std::string>
476 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477 : {
478 4 : return {};
479 : }
480 :
481 : /************************************************************************/
482 : /* OpenGroup() */
483 : /************************************************************************/
484 :
485 : /** Open and return a sub-group.
486 : *
487 : * @note Driver implementation: optionally implemented. If implemented,
488 : * GetGroupNames() should also be implemented.
489 : *
490 : * Drivers known to implement it: MEM, netCDF.
491 : *
492 : * This is the same as the C function GDALGroupOpenGroup().
493 : *
494 : * @param osName Sub-group name.
495 : * @param papszOptions Driver specific options determining how the sub-group
496 : * should be opened. Pass nullptr for default behavior.
497 : *
498 : * @return the group, or nullptr.
499 : */
500 : std::shared_ptr<GDALGroup>
501 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502 : CPL_UNUSED CSLConstList papszOptions) const
503 : {
504 4 : return nullptr;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetVectorLayerNames() */
509 : /************************************************************************/
510 :
511 : /** Return the list of layer names contained in this group.
512 : *
513 : * @note Driver implementation: optionally implemented. If implemented,
514 : * OpenVectorLayer() should also be implemented.
515 : *
516 : * Drivers known to implement it: OpenFileGDB, FileGDB
517 : *
518 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519 : * GDALDataset::GetLayer() should then be used.
520 : *
521 : * This is the same as the C function GDALGroupGetVectorLayerNames().
522 : *
523 : * @param papszOptions Driver specific options determining how layers
524 : * should be retrieved. Pass nullptr for default behavior.
525 : *
526 : * @return the vector layer names.
527 : * @since GDAL 3.4
528 : */
529 : std::vector<std::string>
530 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 1 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* OpenVectorLayer() */
537 : /************************************************************************/
538 :
539 : /** Open and return a vector layer.
540 : *
541 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542 : * lifetime of the returned OGRLayer* is linked to the one of the owner
543 : * dataset (contrary to the general design of this class where objects can be
544 : * used independently of the object that returned them)
545 : *
546 : * @note Driver implementation: optionally implemented. If implemented,
547 : * GetVectorLayerNames() should also be implemented.
548 : *
549 : * Drivers known to implement it: MEM, netCDF.
550 : *
551 : * This is the same as the C function GDALGroupOpenVectorLayer().
552 : *
553 : * @param osName Vector layer name.
554 : * @param papszOptions Driver specific options determining how the layer should
555 : * be opened. Pass nullptr for default behavior.
556 : *
557 : * @return the group, or nullptr.
558 : */
559 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560 : CPL_UNUSED CSLConstList papszOptions) const
561 : {
562 2 : return nullptr;
563 : }
564 :
565 : /************************************************************************/
566 : /* GetDimensions() */
567 : /************************************************************************/
568 :
569 : /** Return the list of dimensions contained in this group and used by its
570 : * arrays.
571 : *
572 : * This is for dimensions that can potentially be used by several arrays.
573 : * Not all drivers might implement this. To retrieve the dimensions used by
574 : * a specific array, use GDALMDArray::GetDimensions().
575 : *
576 : * Drivers known to implement it: MEM, netCDF
577 : *
578 : * This is the same as the C function GDALGroupGetDimensions().
579 : *
580 : * @param papszOptions Driver specific options determining how groups
581 : * should be retrieved. Pass nullptr for default behavior.
582 : *
583 : * @return the dimensions.
584 : */
585 : std::vector<std::shared_ptr<GDALDimension>>
586 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587 : {
588 11 : return {};
589 : }
590 :
591 : /************************************************************************/
592 : /* GetStructuralInfo() */
593 : /************************************************************************/
594 :
595 : /** Return structural information on the group.
596 : *
597 : * This may be the compression, etc..
598 : *
599 : * The return value should not be freed and is valid until GDALGroup is
600 : * released or this function called again.
601 : *
602 : * This is the same as the C function GDALGroupGetStructuralInfo().
603 : */
604 46 : CSLConstList GDALGroup::GetStructuralInfo() const
605 : {
606 46 : return nullptr;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateGroup() */
611 : /************************************************************************/
612 :
613 : /** Create a sub-group within a group.
614 : *
615 : * Optionally implemented by drivers.
616 : *
617 : * Drivers known to implement it: MEM, netCDF
618 : *
619 : * This is the same as the C function GDALGroupCreateGroup().
620 : *
621 : * @param osName Sub-group name.
622 : * @param papszOptions Driver specific options determining how the sub-group
623 : * should be created.
624 : *
625 : * @return the new sub-group, or nullptr in case of error.
626 : */
627 : std::shared_ptr<GDALGroup>
628 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629 : CPL_UNUSED CSLConstList papszOptions)
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632 0 : return nullptr;
633 : }
634 :
635 : /************************************************************************/
636 : /* DeleteGroup() */
637 : /************************************************************************/
638 :
639 : /** Delete a sub-group from a group.
640 : *
641 : * Optionally implemented.
642 : *
643 : * After this call, if a previously obtained instance of the deleted object
644 : * is still alive, no method other than for freeing it should be invoked.
645 : *
646 : * Drivers known to implement it: MEM, Zarr
647 : *
648 : * This is the same as the C function GDALGroupDeleteGroup().
649 : *
650 : * @param osName Sub-group name.
651 : * @param papszOptions Driver specific options determining how the group.
652 : * should be deleted.
653 : *
654 : * @return true in case of success
655 : * @since GDAL 3.8
656 : */
657 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658 : CPL_UNUSED CSLConstList papszOptions)
659 : {
660 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661 0 : return false;
662 : }
663 :
664 : /************************************************************************/
665 : /* CreateDimension() */
666 : /************************************************************************/
667 :
668 : /** Create a dimension within a group.
669 : *
670 : * @note Driver implementation: drivers supporting CreateDimension() should
671 : * implement this method, but do not have necessarily to implement
672 : * GDALGroup::GetDimensions().
673 : *
674 : * Drivers known to implement it: MEM, netCDF
675 : *
676 : * This is the same as the C function GDALGroupCreateDimension().
677 : *
678 : * @param osName Dimension name.
679 : * @param osType Dimension type (might be empty, and ignored by drivers)
680 : * @param osDirection Dimension direction (might be empty, and ignored by
681 : * drivers)
682 : * @param nSize Number of values indexed by this dimension. Should be > 0.
683 : * @param papszOptions Driver specific options determining how the dimension
684 : * should be created.
685 : *
686 : * @return the new dimension, or nullptr if case of error
687 : */
688 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691 : CPL_UNUSED CSLConstList papszOptions)
692 : {
693 0 : CPLError(CE_Failure, CPLE_NotSupported,
694 : "CreateDimension() not implemented");
695 0 : return nullptr;
696 : }
697 :
698 : /************************************************************************/
699 : /* CreateMDArray() */
700 : /************************************************************************/
701 :
702 : /** Create a multidimensional array within a group.
703 : *
704 : * It is recommended that the GDALDimension objects passed in aoDimensions
705 : * belong to this group, either by retrieving them with GetDimensions()
706 : * or creating a new one with CreateDimension().
707 : *
708 : * Optionally implemented.
709 : *
710 : * Drivers known to implement it: MEM, netCDF
711 : *
712 : * This is the same as the C function GDALGroupCreateMDArray().
713 : *
714 : * @note Driver implementation: drivers should take into account the possibility
715 : * that GDALDimension object passed in aoDimensions might belong to a different
716 : * group / dataset / driver and act accordingly.
717 : *
718 : * @param osName Array name.
719 : * @param aoDimensions List of dimensions, ordered from the slowest varying
720 : * dimension first to the fastest varying dimension last.
721 : * Might be empty for a scalar array (if supported by
722 : * driver)
723 : * @param oDataType Array data type.
724 : * @param papszOptions Driver specific options determining how the array
725 : * should be created.
726 : *
727 : * @return the new array, or nullptr in case of error
728 : */
729 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730 : CPL_UNUSED const std::string &osName,
731 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732 : CPL_UNUSED const GDALExtendedDataType &oDataType,
733 : CPL_UNUSED CSLConstList papszOptions)
734 : {
735 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736 0 : return nullptr;
737 : }
738 :
739 : /************************************************************************/
740 : /* DeleteMDArray() */
741 : /************************************************************************/
742 :
743 : /** Delete an array from a group.
744 : *
745 : * Optionally implemented.
746 : *
747 : * After this call, if a previously obtained instance of the deleted object
748 : * is still alive, no method other than for freeing it should be invoked.
749 : *
750 : * Drivers known to implement it: MEM, Zarr
751 : *
752 : * This is the same as the C function GDALGroupDeleteMDArray().
753 : *
754 : * @param osName Arrayname.
755 : * @param papszOptions Driver specific options determining how the array.
756 : * should be deleted.
757 : *
758 : * @return true in case of success
759 : * @since GDAL 3.8
760 : */
761 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762 : CPL_UNUSED CSLConstList papszOptions)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765 0 : return false;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetTotalCopyCost() */
770 : /************************************************************************/
771 :
772 : /** Return a total "cost" to copy the group.
773 : *
774 : * Used as a parameter for CopFrom()
775 : */
776 33 : GUInt64 GDALGroup::GetTotalCopyCost() const
777 : {
778 33 : GUInt64 nCost = COPY_COST;
779 33 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780 :
781 66 : auto groupNames = GetGroupNames();
782 39 : for (const auto &name : groupNames)
783 : {
784 12 : auto subGroup = OpenGroup(name);
785 6 : if (subGroup)
786 : {
787 6 : nCost += subGroup->GetTotalCopyCost();
788 : }
789 : }
790 :
791 33 : auto arrayNames = GetMDArrayNames();
792 102 : for (const auto &name : arrayNames)
793 : {
794 138 : auto array = OpenMDArray(name);
795 69 : if (array)
796 : {
797 69 : nCost += array->GetTotalCopyCost();
798 : }
799 : }
800 66 : return nCost;
801 : }
802 :
803 : /************************************************************************/
804 : /* CopyFrom() */
805 : /************************************************************************/
806 :
807 : /** Copy the content of a group into a new (generally empty) group.
808 : *
809 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
811 : * of some output drivers this is not recommended)
812 : * @param poSrcGroup Source group. Must NOT be nullptr.
813 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
814 : * stop the copy. In relaxed mode, the copy will be attempted to
815 : * be pursued.
816 : * @param nCurCost Should be provided as a variable initially set to 0.
817 : * @param nTotalCost Total cost from GetTotalCopyCost().
818 : * @param pfnProgress Progress callback, or nullptr.
819 : * @param pProgressData Progress user data, or 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 2103 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1267 : std::shared_ptr<GDALGroup> &curGroupHolder,
1268 : std::string &osLastPart) const
1269 : {
1270 2103 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1271 6 : return nullptr;
1272 2097 : const GDALGroup *poCurGroup = this;
1273 : CPLStringList aosTokens(
1274 4194 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1275 2097 : if (aosTokens.size() == 0)
1276 : {
1277 0 : return nullptr;
1278 : }
1279 :
1280 2572 : for (int i = 0; i < aosTokens.size() - 1; i++)
1281 : {
1282 487 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1283 487 : if (!curGroupHolder)
1284 : {
1285 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1286 : aosTokens[i]);
1287 12 : return nullptr;
1288 : }
1289 475 : poCurGroup = curGroupHolder.get();
1290 : }
1291 2085 : osLastPart = aosTokens[aosTokens.size() - 1];
1292 2085 : 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 793 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1304 : CSLConstList papszOptions) const
1305 : {
1306 1586 : std::string osName;
1307 793 : std::shared_ptr<GDALGroup> curGroupHolder;
1308 793 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1309 793 : if (poGroup == nullptr)
1310 12 : return nullptr;
1311 781 : 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 1094 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1463 : CSLConstList papszOptions) const
1464 : {
1465 2188 : std::string osName;
1466 1094 : std::shared_ptr<GDALGroup> curGroupHolder;
1467 1094 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1468 1094 : if (poGroup == nullptr)
1469 4 : return nullptr;
1470 1090 : 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 36765 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1614 : {
1615 36765 : 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 36765 : 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 171449 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1637 171449 : const std::string &osName)
1638 : : m_osName(osName),
1639 : m_osFullName(
1640 171449 : !osParentName.empty()
1641 339942 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1642 682840 : : osName)
1643 : {
1644 171449 : }
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 64852 : size_t GDALAbstractMDArray::GetDimensionCount() const
1684 : {
1685 64852 : 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 85656 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1722 : const GDALExtendedDataType &srcType,
1723 : void *pDst,
1724 : const GDALExtendedDataType &dstType)
1725 : {
1726 162973 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1727 77317 : dstType.GetClass() == GEDTC_NUMERIC)
1728 : {
1729 75072 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1730 : dstType.GetNumericDataType(), 0, 1);
1731 75072 : return true;
1732 : }
1733 18531 : if (srcType.GetClass() == GEDTC_STRING &&
1734 7947 : dstType.GetClass() == GEDTC_STRING)
1735 : {
1736 : const char *srcStrPtr;
1737 6699 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1738 6699 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1739 6699 : *reinterpret_cast<void **>(pDst) = pszDup;
1740 6699 : return true;
1741 : }
1742 6130 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1743 2245 : dstType.GetClass() == GEDTC_STRING)
1744 : {
1745 2245 : const char *str = nullptr;
1746 2245 : switch (srcType.GetNumericDataType())
1747 : {
1748 0 : case GDT_Unknown:
1749 0 : break;
1750 531 : case GDT_UInt8:
1751 531 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1752 531 : break;
1753 3 : case GDT_Int8:
1754 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1755 3 : break;
1756 96 : case GDT_UInt16:
1757 96 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1758 96 : break;
1759 0 : case GDT_Int16:
1760 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1761 0 : break;
1762 153 : case GDT_UInt32:
1763 153 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1764 153 : break;
1765 68 : case GDT_Int32:
1766 68 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1767 68 : 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 53 : case GDT_Int64:
1775 53 : str = CPLSPrintf(CPL_FRMT_GIB,
1776 : static_cast<GIntBig>(
1777 : *static_cast<const std::int64_t *>(pSrc)));
1778 53 : break;
1779 0 : case GDT_Float16:
1780 0 : str = CPLSPrintf("%.5g",
1781 : double(*static_cast<const GFloat16 *>(pSrc)));
1782 0 : break;
1783 449 : case GDT_Float32:
1784 898 : str = CPLSPrintf(
1785 : "%.9g",
1786 449 : static_cast<double>(*static_cast<const float *>(pSrc)));
1787 449 : break;
1788 890 : case GDT_Float64:
1789 890 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1790 890 : 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 2245 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1826 2245 : *reinterpret_cast<void **>(pDst) = pszDup;
1827 2245 : return true;
1828 : }
1829 2888 : 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 784 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1856 392 : dstType.GetClass() == GEDTC_COMPOUND)
1857 : {
1858 392 : const auto &srcComponents = srcType.GetComponents();
1859 392 : const auto &dstComponents = dstType.GetComponents();
1860 392 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1861 392 : GByte *pabyDst = static_cast<GByte *>(pDst);
1862 :
1863 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1864 784 : srcComponentMap;
1865 2210 : for (const auto &srcComp : srcComponents)
1866 : {
1867 1818 : srcComponentMap[srcComp->GetName()] = &srcComp;
1868 : }
1869 954 : for (const auto &dstComp : dstComponents)
1870 : {
1871 562 : auto oIter = srcComponentMap.find(dstComp->GetName());
1872 562 : if (oIter == srcComponentMap.end())
1873 0 : return false;
1874 562 : const auto &srcComp = *(oIter->second);
1875 1686 : if (!GDALExtendedDataType::CopyValue(
1876 562 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1877 562 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1878 : {
1879 0 : return false;
1880 : }
1881 : }
1882 392 : 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 507 : 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 507 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1906 : const auto nDstStrideInBytes =
1907 507 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1908 773 : 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 1039 : 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 241 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1923 241 : GByte *pabyDst = static_cast<GByte *>(pDst);
1924 482 : for (size_t i = 0; i < nValues; ++i)
1925 : {
1926 241 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1927 0 : return false;
1928 241 : pabySrc += nSrcStrideInBytes;
1929 241 : pabyDst += nDstStrideInBytes;
1930 : }
1931 : }
1932 507 : return true;
1933 : }
1934 :
1935 : /************************************************************************/
1936 : /* CheckReadWriteParams() */
1937 : /************************************************************************/
1938 : //! @cond Doxygen_Suppress
1939 21170 : 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 21170 : const auto &dims = GetDimensions();
1955 21170 : if (dims.empty())
1956 : {
1957 13478 : if (buffer_alloc_start)
1958 : {
1959 12647 : const size_t elementSize = bufferDataType.GetSize();
1960 12647 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1961 12647 : const GByte *paby_buffer_alloc_start =
1962 : static_cast<const GByte *>(buffer_alloc_start);
1963 12647 : const GByte *paby_buffer_alloc_end =
1964 : paby_buffer_alloc_start + buffer_alloc_size;
1965 :
1966 12647 : if (paby_buffer < paby_buffer_alloc_start ||
1967 12647 : paby_buffer + elementSize > paby_buffer_alloc_end)
1968 : {
1969 0 : lamda_error();
1970 0 : return false;
1971 : }
1972 : }
1973 13478 : return true;
1974 : }
1975 :
1976 7692 : if (arrayStep == nullptr)
1977 : {
1978 1869 : tmp_arrayStep.resize(dims.size(), 1);
1979 1869 : arrayStep = tmp_arrayStep.data();
1980 : }
1981 20603 : for (size_t i = 0; i < dims.size(); i++)
1982 : {
1983 12911 : assert(count);
1984 12911 : 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 7692 : bool bufferStride_all_positive = true;
1992 7692 : if (bufferStride == nullptr)
1993 : {
1994 1446 : GPtrDiff_t stride = 1;
1995 1446 : 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 3205 : for (size_t i = dims.size(); i != 0;)
1999 : {
2000 1759 : --i;
2001 1759 : tmp_bufferStride.push_back(stride);
2002 1759 : GUInt64 newStride = 0;
2003 : bool bOK;
2004 : try
2005 : {
2006 1759 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2007 3518 : CPLSM(static_cast<uint64_t>(count[i])))
2008 1759 : .v();
2009 1759 : bOK = static_cast<size_t>(newStride) == newStride &&
2010 1759 : newStride < std::numeric_limits<size_t>::max() / 2;
2011 : }
2012 0 : catch (...)
2013 : {
2014 0 : bOK = false;
2015 : }
2016 1759 : if (!bOK)
2017 : {
2018 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2019 0 : return false;
2020 : }
2021 1759 : stride = static_cast<GPtrDiff_t>(newStride);
2022 : }
2023 1446 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2024 1446 : bufferStride = tmp_bufferStride.data();
2025 : }
2026 : else
2027 : {
2028 17276 : for (size_t i = 0; i < dims.size(); i++)
2029 : {
2030 11091 : if (bufferStride[i] < 0)
2031 : {
2032 61 : bufferStride_all_positive = false;
2033 61 : break;
2034 : }
2035 : }
2036 : }
2037 20574 : for (size_t i = 0; i < dims.size(); i++)
2038 : {
2039 12892 : assert(arrayStartIdx);
2040 12892 : assert(count);
2041 12892 : 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 12890 : if (arrayStep[i] >= 0)
2052 : {
2053 : try
2054 : {
2055 12032 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2056 12034 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2057 48131 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2058 12032 : .v() >= dims[i]->GetSize();
2059 : }
2060 1 : catch (...)
2061 : {
2062 1 : bOverflow = true;
2063 : }
2064 12033 : 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 857 : bOverflow =
2080 857 : arrayStartIdx[i] <
2081 857 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2082 1714 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2083 : ? (static_cast<uint64_t>(1) << 63)
2084 1714 : : static_cast<uint64_t>(-arrayStep[i])))
2085 857 : .v();
2086 : }
2087 0 : catch (...)
2088 : {
2089 0 : bOverflow = true;
2090 : }
2091 857 : 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 7682 : if (buffer_alloc_start)
2104 : {
2105 3541 : const size_t elementSize = bufferDataType.GetSize();
2106 3541 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2107 3541 : const GByte *paby_buffer_alloc_start =
2108 : static_cast<const GByte *>(buffer_alloc_start);
2109 3541 : const GByte *paby_buffer_alloc_end =
2110 : paby_buffer_alloc_start + buffer_alloc_size;
2111 3541 : if (bufferStride_all_positive)
2112 : {
2113 3541 : if (paby_buffer < paby_buffer_alloc_start)
2114 : {
2115 0 : lamda_error();
2116 0 : return false;
2117 : }
2118 3541 : GUInt64 nOffset = elementSize;
2119 10093 : for (size_t i = 0; i < dims.size(); i++)
2120 : {
2121 : try
2122 : {
2123 6552 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2124 6552 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2125 13104 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2126 26208 : CPLSM(static_cast<uint64_t>(elementSize)))
2127 6552 : .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 3541 : 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 7682 : 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 12339 : 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 12339 : 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 24678 : std::vector<GInt64> tmp_arrayStep;
2255 24678 : std::vector<GPtrDiff_t> tmp_bufferStride;
2256 12339 : 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 12339 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2265 12339 : 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 2925 : 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 2925 : 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 5850 : std::vector<GInt64> tmp_arrayStep;
2360 5850 : std::vector<GPtrDiff_t> tmp_bufferStride;
2361 2925 : 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 2925 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2370 2925 : 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 1592 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2384 : {
2385 1592 : const auto &dims = GetDimensions();
2386 1592 : if (dims.empty())
2387 815 : return 1;
2388 777 : GUInt64 nElts = 1;
2389 1692 : for (const auto &dim : dims)
2390 : {
2391 : try
2392 : {
2393 915 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2394 2745 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2395 915 : .v();
2396 : }
2397 0 : catch (...)
2398 : {
2399 0 : return 0;
2400 : }
2401 : }
2402 777 : 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 309 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2432 : {
2433 309 : 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 99 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2457 : {
2458 99 : const auto &dims = GetDimensions();
2459 99 : const auto &nDTSize = GetDataType().GetSize();
2460 99 : std::vector<size_t> anChunkSize;
2461 198 : auto blockSize = GetBlockSize();
2462 99 : CPLAssert(blockSize.size() == dims.size());
2463 99 : size_t nChunkSize = nDTSize;
2464 99 : bool bOverflow = false;
2465 99 : 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 268 : for (size_t i = 0; i < dims.size(); i++)
2470 : {
2471 : const auto sizeDimI =
2472 338 : std::max(static_cast<size_t>(1),
2473 338 : static_cast<size_t>(
2474 338 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2475 169 : std::min(blockSize[i], dims[i]->GetSize()))));
2476 169 : anChunkSize.push_back(sizeDimI);
2477 169 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2478 : {
2479 4 : bOverflow = true;
2480 : }
2481 : else
2482 : {
2483 165 : nChunkSize *= sizeDimI;
2484 : }
2485 : }
2486 99 : 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 99 : 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 99 : nChunkSize = nDTSize;
2511 198 : std::vector<size_t> anAccBlockSizeFromStart;
2512 268 : for (size_t i = 0; i < dims.size(); i++)
2513 : {
2514 169 : nChunkSize *= anChunkSize[i];
2515 169 : anAccBlockSizeFromStart.push_back(nChunkSize);
2516 : }
2517 99 : if (nChunkSize <= nMaxChunkMemory / 2)
2518 : {
2519 95 : size_t nVoxelsFromEnd = 1;
2520 256 : for (size_t i = dims.size(); i > 0;)
2521 : {
2522 161 : --i;
2523 : const auto nCurBlockSize =
2524 161 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2525 161 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2526 161 : if (nMul >= 2)
2527 : {
2528 153 : const auto nSizeThisDim(dims[i]->GetSize());
2529 : const auto nBlocksThisDim =
2530 153 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2531 153 : anChunkSize[i] = static_cast<size_t>(std::min(
2532 153 : anChunkSize[i] *
2533 306 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2534 153 : nSizeThisDim));
2535 : }
2536 161 : nVoxelsFromEnd *= anChunkSize[i];
2537 : }
2538 : }
2539 99 : 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 8629 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2606 : {
2607 8629 : 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 8629 : 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 22562 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2735 : {
2736 22562 : const void *pNoData = GetRawNoDataValue();
2737 22562 : double dfNoData = 0.0;
2738 22562 : const auto &eDT = GetDataType();
2739 22562 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2740 22562 : if (ok)
2741 : {
2742 22233 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2743 : GDT_Float64, 0, 1);
2744 : }
2745 22562 : if (pbHasNoData)
2746 499 : *pbHasNoData = ok;
2747 22562 : 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 4 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2880 : {
2881 4 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2882 4 : bool bRet = false;
2883 4 : if (GDALExtendedDataType::CopyValue(&nNoData,
2884 8 : GDALExtendedDataType::Create(GDT_Int64),
2885 4 : pRawNoData, GetDataType()))
2886 : {
2887 4 : bRet = SetRawNoDataValue(pRawNoData);
2888 : }
2889 4 : CPLFree(pRawNoData);
2890 4 : 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 22 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3029 : CPL_UNUSED GDALDataType *peStorageType) const
3030 : {
3031 22 : if (pbHasScale)
3032 22 : *pbHasScale = false;
3033 22 : 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 22 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3059 : CPL_UNUSED GDALDataType *peStorageType) const
3060 : {
3061 22 : if (pbHasOffset)
3062 22 : *pbHasOffset = false;
3063 22 : 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 97 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3108 : const GUInt64 *count,
3109 : const size_t *chunkSize,
3110 : FuncProcessPerChunkType pfnFunc,
3111 : void *pUserData)
3112 : {
3113 97 : const auto &dims = GetDimensions();
3114 97 : if (dims.empty())
3115 : {
3116 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3117 : }
3118 :
3119 : // Sanity check
3120 95 : size_t nTotalChunkSize = 1;
3121 241 : for (size_t i = 0; i < dims.size(); i++)
3122 : {
3123 153 : const auto nSizeThisDim(dims[i]->GetSize());
3124 153 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3125 151 : 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 296 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3133 147 : 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 146 : nTotalChunkSize *= chunkSize[i];
3140 : }
3141 :
3142 88 : size_t dimIdx = 0;
3143 176 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3144 176 : 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 176 : std::vector<Stack> stack(dims.size());
3155 88 : GUInt64 iCurChunk = 0;
3156 88 : GUInt64 nChunkCount = 1;
3157 233 : for (size_t i = 0; i < dims.size(); i++)
3158 : {
3159 145 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3160 145 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3161 145 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3162 145 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3163 145 : if (stack[i].nBlocksMinusOne == 0)
3164 : {
3165 140 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3166 140 : 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 88 : lbl_next_depth:
3176 343 : if (dimIdx == dims.size())
3177 : {
3178 121 : ++iCurChunk;
3179 121 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3180 : iCurChunk, nChunkCount, pUserData))
3181 : {
3182 3 : return false;
3183 : }
3184 : }
3185 : else
3186 : {
3187 222 : 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 222 : dimIdx++;
3212 222 : goto lbl_next_depth;
3213 216 : lbl_return_to_caller_end_of_loop:
3214 216 : if (dimIdx == 0)
3215 85 : goto end;
3216 : }
3217 :
3218 249 : assert(dimIdx > 0);
3219 249 : dimIdx--;
3220 : // cppcheck-suppress negativeContainerIndex
3221 249 : switch (stack[dimIdx].return_point)
3222 : {
3223 216 : case Caller::CALLER_END_OF_LOOP:
3224 216 : 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 85 : end:
3229 85 : return true;
3230 : }
3231 :
3232 : /************************************************************************/
3233 : /* GDALAttribute() */
3234 : /************************************************************************/
3235 :
3236 : //! @cond Doxygen_Suppress
3237 161710 : 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 161710 : : GDALAbstractMDArray(osParentName, osName)
3241 : #endif
3242 : {
3243 161710 : }
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 746 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3260 : {
3261 746 : const auto &dims = GetDimensions();
3262 746 : std::vector<GUInt64> ret;
3263 746 : ret.reserve(dims.size());
3264 925 : for (const auto &dim : dims)
3265 179 : ret.push_back(dim->GetSize());
3266 746 : 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 4577 : const char *GDALAttribute::ReadAsString() const
3415 : {
3416 4577 : const auto nDims = GetDimensionCount();
3417 9154 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3418 9154 : std::vector<size_t> count(1 + nDims, 1);
3419 4577 : char *szRet = nullptr;
3420 4577 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3421 4577 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3422 13730 : sizeof(szRet)) ||
3423 4576 : szRet == nullptr)
3424 : {
3425 5 : return nullptr;
3426 : }
3427 4572 : m_osCachedVal = szRet;
3428 4572 : CPLFree(szRet);
3429 4572 : 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 2903 : int GDALAttribute::ReadAsInt() const
3447 : {
3448 2903 : const auto nDims = GetDimensionCount();
3449 5806 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3450 2903 : std::vector<size_t> count(1 + nDims, 1);
3451 2903 : int nRet = INT_MIN;
3452 2903 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3453 5806 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3454 5806 : 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 131 : int64_t GDALAttribute::ReadAsInt64() const
3472 : {
3473 131 : const auto nDims = GetDimensionCount();
3474 262 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3475 131 : std::vector<size_t> count(1 + nDims, 1);
3476 131 : int64_t nRet = INT64_MIN;
3477 131 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3478 262 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3479 262 : 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 4003 : double GDALAttribute::ReadAsDouble() const
3497 : {
3498 4003 : const auto nDims = GetDimensionCount();
3499 8006 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3500 4003 : std::vector<size_t> count(1 + nDims, 1);
3501 4003 : double dfRet = 0;
3502 4003 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3503 4003 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3504 4003 : sizeof(dfRet));
3505 8006 : 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 216 : CPLStringList GDALAttribute::ReadAsStringArray() const
3517 : {
3518 216 : const auto nElts = GetTotalElementsCount();
3519 216 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3520 0 : return CPLStringList();
3521 : char **papszList = static_cast<char **>(
3522 216 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3523 216 : const auto &dims = GetDimensions();
3524 216 : const auto nDims = GetDimensionCount();
3525 432 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3526 432 : std::vector<size_t> count(1 + nDims);
3527 342 : for (size_t i = 0; i < nDims; i++)
3528 : {
3529 126 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3530 : }
3531 216 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3532 216 : GDALExtendedDataType::CreateString(), papszList, papszList,
3533 216 : sizeof(char *) * static_cast<int>(nElts));
3534 791 : for (int i = 0; i < static_cast<int>(nElts); i++)
3535 : {
3536 575 : if (papszList[i] == nullptr)
3537 13 : papszList[i] = CPLStrdup("");
3538 : }
3539 216 : 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 18 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3551 : {
3552 18 : const auto nElts = GetTotalElementsCount();
3553 : #if SIZEOF_VOIDP == 4
3554 : if (nElts > static_cast<size_t>(nElts))
3555 : return {};
3556 : #endif
3557 18 : std::vector<int> res(static_cast<size_t>(nElts));
3558 18 : const auto &dims = GetDimensions();
3559 18 : const auto nDims = GetDimensionCount();
3560 36 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3561 36 : std::vector<size_t> count(1 + nDims);
3562 38 : for (size_t i = 0; i < nDims; i++)
3563 : {
3564 20 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3565 : }
3566 18 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3567 36 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3568 18 : res.size() * sizeof(res[0]));
3569 36 : 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 90 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3581 : {
3582 90 : const auto nElts = GetTotalElementsCount();
3583 : #if SIZEOF_VOIDP == 4
3584 : if (nElts > static_cast<size_t>(nElts))
3585 : return {};
3586 : #endif
3587 90 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3588 90 : const auto &dims = GetDimensions();
3589 90 : const auto nDims = GetDimensionCount();
3590 180 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3591 180 : std::vector<size_t> count(1 + nDims);
3592 180 : for (size_t i = 0; i < nDims; i++)
3593 : {
3594 90 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3595 : }
3596 90 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3597 180 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3598 90 : res.size() * sizeof(res[0]));
3599 180 : 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 123 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3611 : {
3612 123 : const auto nElts = GetTotalElementsCount();
3613 : #if SIZEOF_VOIDP == 4
3614 : if (nElts > static_cast<size_t>(nElts))
3615 : return {};
3616 : #endif
3617 123 : std::vector<double> res(static_cast<size_t>(nElts));
3618 123 : const auto &dims = GetDimensions();
3619 123 : const auto nDims = GetDimensionCount();
3620 246 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3621 246 : std::vector<size_t> count(1 + nDims);
3622 230 : for (size_t i = 0; i < nDims; i++)
3623 : {
3624 107 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3625 : }
3626 123 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3627 246 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3628 123 : res.size() * sizeof(res[0]));
3629 246 : 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 396 : bool GDALAttribute::Write(const char *pszValue)
3684 : {
3685 396 : const auto nDims = GetDimensionCount();
3686 792 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3687 396 : std::vector<size_t> count(1 + nDims, 1);
3688 396 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3689 792 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3690 792 : 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 23 : bool GDALAttribute::WriteInt(int nVal)
3708 : {
3709 23 : const auto nDims = GetDimensionCount();
3710 46 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3711 23 : std::vector<size_t> count(1 + nDims, 1);
3712 23 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3713 46 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3714 46 : 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 14 : bool GDALAttribute::WriteInt64(int64_t nVal)
3732 : {
3733 14 : const auto nDims = GetDimensionCount();
3734 28 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3735 14 : std::vector<size_t> count(1 + nDims, 1);
3736 14 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3737 28 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3738 28 : 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 40 : bool GDALAttribute::Write(double dfVal)
3756 : {
3757 40 : const auto nDims = GetDimensionCount();
3758 80 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3759 40 : std::vector<size_t> count(1 + nDims, 1);
3760 40 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3761 80 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3762 80 : 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 9 : bool GDALAttribute::Write(CSLConstList vals)
3781 : {
3782 9 : 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 8 : const auto nDims = GetDimensionCount();
3788 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3789 8 : std::vector<size_t> count(1 + nDims);
3790 8 : const auto &dims = GetDimensions();
3791 17 : for (size_t i = 0; i < nDims; i++)
3792 9 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3793 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3794 8 : GDALExtendedDataType::CreateString(), vals, vals,
3795 16 : 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 12 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3815 : {
3816 12 : if (nVals != GetTotalElementsCount())
3817 : {
3818 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3819 1 : return false;
3820 : }
3821 11 : const auto nDims = GetDimensionCount();
3822 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3823 11 : std::vector<size_t> count(1 + nDims);
3824 11 : const auto &dims = GetDimensions();
3825 22 : for (size_t i = 0; i < nDims; i++)
3826 11 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3827 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3828 11 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3829 22 : 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 13 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3849 : {
3850 13 : if (nVals != GetTotalElementsCount())
3851 : {
3852 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3853 0 : return false;
3854 : }
3855 13 : const auto nDims = GetDimensionCount();
3856 26 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3857 13 : std::vector<size_t> count(1 + nDims);
3858 13 : const auto &dims = GetDimensions();
3859 26 : for (size_t i = 0; i < nDims; i++)
3860 13 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3861 13 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3862 13 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3863 13 : static_cast<size_t>(GetTotalElementsCount()) *
3864 13 : 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 8 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3884 : {
3885 8 : if (nVals != GetTotalElementsCount())
3886 : {
3887 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3888 1 : return false;
3889 : }
3890 7 : const auto nDims = GetDimensionCount();
3891 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3892 7 : std::vector<size_t> count(1 + nDims);
3893 7 : const auto &dims = GetDimensions();
3894 15 : for (size_t i = 0; i < nDims; i++)
3895 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3896 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3897 7 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3898 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3899 : }
3900 :
3901 : /************************************************************************/
3902 : /* GDALMDArray() */
3903 : /************************************************************************/
3904 :
3905 : //! @cond Doxygen_Suppress
3906 9739 : 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 9739 : m_osContext(osContext)
3914 : {
3915 9739 : }
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 26 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4251 : CSLConstList papszOptions) const
4252 : {
4253 26 : const auto nDimCount = GetDimensionCount();
4254 26 : if (nDimCount == 0)
4255 2 : return true;
4256 :
4257 48 : std::vector<GUInt64> tmp_arrayStartIdx;
4258 24 : if (arrayStartIdx == nullptr)
4259 : {
4260 0 : tmp_arrayStartIdx.resize(nDimCount);
4261 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4262 : }
4263 :
4264 48 : std::vector<size_t> tmp_count;
4265 24 : 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 48 : std::vector<GInt64> tmp_arrayStep;
4285 48 : std::vector<GPtrDiff_t> tmp_bufferStride;
4286 24 : const GInt64 *arrayStep = nullptr;
4287 24 : const GPtrDiff_t *bufferStride = nullptr;
4288 24 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4289 48 : GDALExtendedDataType::Create(GDT_Unknown),
4290 : nullptr, nullptr, 0, tmp_arrayStep,
4291 : tmp_bufferStride))
4292 : {
4293 1 : return false;
4294 : }
4295 :
4296 23 : 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 33 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4318 : {
4319 33 : std::string ret;
4320 621 : for (const char ch : inputName)
4321 : {
4322 588 : if (!isalnum(static_cast<unsigned char>(ch)))
4323 140 : ret += '_';
4324 : else
4325 448 : ret += ch;
4326 : }
4327 33 : return ret;
4328 : }
4329 :
4330 : //! @endcond
4331 :
4332 : /************************************************************************/
4333 : /* GetCacheRootGroup() */
4334 : /************************************************************************/
4335 :
4336 : //! @cond Doxygen_Suppress
4337 : std::shared_ptr<GDALGroup>
4338 3298 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4339 : std::string &osCacheFilenameOut) const
4340 : {
4341 3298 : const auto &osFilename = GetFilename();
4342 3298 : 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 3297 : osCacheFilenameOut = osFilename + ".gmac";
4350 3297 : 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 3297 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4362 3297 : if (pszProxy != nullptr)
4363 7 : osCacheFilenameOut = pszProxy;
4364 :
4365 3297 : std::unique_ptr<GDALDataset> poDS;
4366 : VSIStatBufL sStat;
4367 3297 : 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 3297 : if (poDS)
4374 : {
4375 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4376 28 : return poDS->GetRootGroup();
4377 : }
4378 :
4379 3269 : if (bCanCreate)
4380 : {
4381 5 : const char *pszDrvName = "netCDF";
4382 5 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4383 5 : if (poDrv == nullptr)
4384 : {
4385 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4386 : pszDrvName);
4387 0 : return nullptr;
4388 : }
4389 : {
4390 10 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4391 10 : CPLErrorStateBackuper oErrorStateBackuper;
4392 5 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4393 : nullptr, nullptr));
4394 : }
4395 5 : 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 5 : if (poDS)
4406 : {
4407 5 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4408 5 : 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 3264 : 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 5882 : 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 5882 : if (!m_bHasTriedCachedArray)
4537 : {
4538 3307 : m_bHasTriedCachedArray = true;
4539 3307 : if (IsCacheable())
4540 : {
4541 3307 : const auto &osFilename = GetFilename();
4542 5903 : if (!osFilename.empty() &&
4543 5903 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4544 : {
4545 5172 : std::string osCacheFilename;
4546 5172 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4547 2586 : 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 5882 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4587 5882 : 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 11764 : std::vector<GInt64> tmp_arrayStep;
4595 11764 : std::vector<GPtrDiff_t> tmp_bufferStride;
4596 5882 : 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 5873 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4605 5873 : 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 1454 : 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 1454 : const size_t nDims(GetDimensionCount());
4647 1454 : size_t nCurStrideForRowMajorStrides = 1;
4648 1454 : bool bRowMajorStrides = true;
4649 1454 : size_t nElts = 1;
4650 1454 : size_t nLastIdx = 0;
4651 3628 : for (size_t i = nDims; i > 0;)
4652 : {
4653 2174 : --i;
4654 2174 : if (bufferStride[i] < 0)
4655 0 : return false;
4656 2174 : if (static_cast<size_t>(bufferStride[i]) !=
4657 : nCurStrideForRowMajorStrides)
4658 : {
4659 415 : bRowMajorStrides = false;
4660 : }
4661 : // Integer overflows have already been checked in CheckReadWriteParams()
4662 2174 : nCurStrideForRowMajorStrides *= count[i];
4663 2174 : nElts *= count[i];
4664 2174 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4665 : }
4666 1454 : if (bRowMajorStrides)
4667 1113 : return false;
4668 341 : 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 320 : 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 320 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4739 : // Use specialized implementation for well-known data types when no
4740 : // type conversion is needed
4741 320 : if (eSrcDataType == eDstDataType)
4742 : {
4743 125 : if (nSrcDataTypeSize == 1)
4744 : {
4745 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4746 : count, bufferStride);
4747 72 : return;
4748 : }
4749 84 : else if (nSrcDataTypeSize == 2)
4750 : {
4751 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4752 : count, bufferStride);
4753 1 : return;
4754 : }
4755 83 : else if (nSrcDataTypeSize == 4)
4756 : {
4757 22 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4758 : count, bufferStride);
4759 22 : return;
4760 : }
4761 61 : else if (nSrcDataTypeSize == 8)
4762 : {
4763 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4764 : count, bufferStride);
4765 8 : return;
4766 : }
4767 : }
4768 :
4769 248 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4770 496 : std::vector<size_t> anStackCount(nDims);
4771 496 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4772 248 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4773 248 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4774 248 : size_t iDim = 0;
4775 :
4776 517 : lbl_next_depth:
4777 517 : if (iDim == nDims - 1)
4778 : {
4779 507 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4780 507 : pabyDstBufferStack[iDim], eDstDataType,
4781 507 : bufferStride[iDim], count[iDim]);
4782 507 : 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 517 : 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 339 : 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 339 : const size_t nDims(GetDimensionCount());
4852 339 : if (nDims == 0)
4853 : {
4854 0 : CPLAssert(false);
4855 : return false; // shouldn't happen
4856 : }
4857 339 : size_t nElts = 1;
4858 800 : for (size_t i = 0; i < nDims; ++i)
4859 461 : nElts *= count[i];
4860 :
4861 678 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4862 339 : tmpBufferStrides.back() = 1;
4863 461 : 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 339 : const auto &eDT = GetDataType();
4870 339 : const auto nDTSize = eDT.GetSize();
4871 483 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4872 499 : 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 320 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4909 320 : if (pTempBuffer == nullptr)
4910 0 : return false;
4911 :
4912 320 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4913 320 : pTempBuffer))
4914 : {
4915 0 : VSIFree(pTempBuffer);
4916 0 : return false;
4917 : }
4918 320 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4919 : count, bufferStride);
4920 :
4921 320 : if (eDT.NeedsFreeDynamicMemory())
4922 : {
4923 237 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4924 474 : for (size_t i = 0; i < nElts; ++i)
4925 : {
4926 237 : eDT.FreeDynamicMemory(pabyPtr);
4927 237 : pabyPtr += nDTSize;
4928 : }
4929 : }
4930 :
4931 320 : VSIFree(pTempBuffer);
4932 320 : 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 847 : 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 2541 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5072 2541 : poParent->GetFullName() +
5073 1694 : " (" + viewExpr + ")"),
5074 1694 : GDALPamMDArray(std::string(),
5075 1694 : "Sliced view of " + poParent->GetFullName() + " (" +
5076 1694 : viewExpr + ")",
5077 1694 : GDALPamMultiDim::GetPAM(poParent),
5078 : poParent->GetContext()),
5079 1694 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5080 847 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5081 847 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5082 847 : m_parentRanges(std::move(parentRanges)),
5083 847 : m_parentStart(m_poParent->GetDimensionCount()),
5084 847 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5085 847 : m_parentStep(m_poParent->GetDimensionCount()),
5086 6776 : m_parentStride(m_poParent->GetDimensionCount())
5087 : {
5088 847 : }
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 847 : 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 847 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5113 847 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5114 :
5115 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5116 847 : poParent, viewExpr, std::move(dims),
5117 847 : std::move(mapDimIdxToParentDimIdx),
5118 847 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5119 847 : newAr->SetSelf(newAr);
5120 847 : return newAr;
5121 : }
5122 :
5123 228 : bool IsWritable() const override
5124 : {
5125 228 : return m_poParent->IsWritable();
5126 : }
5127 :
5128 1834 : const std::string &GetFilename() const override
5129 : {
5130 1834 : return m_poParent->GetFilename();
5131 : }
5132 :
5133 : const std::vector<std::shared_ptr<GDALDimension>> &
5134 5912 : GetDimensions() const override
5135 : {
5136 5912 : return m_dims;
5137 : }
5138 :
5139 2424 : const GDALExtendedDataType &GetDataType() const override
5140 : {
5141 2424 : return m_poParent->GetDataType();
5142 : }
5143 :
5144 4 : const std::string &GetUnit() const override
5145 : {
5146 4 : 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 104 : const void *GetRawNoDataValue() const override
5183 : {
5184 104 : return m_poParent->GetRawNoDataValue();
5185 : }
5186 :
5187 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5188 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5189 :
5190 4 : double GetOffset(bool *pbHasOffset,
5191 : GDALDataType *peStorageType) const override
5192 : {
5193 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5194 : }
5195 :
5196 4 : double GetScale(bool *pbHasScale,
5197 : GDALDataType *peStorageType) const override
5198 : {
5199 4 : 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 550 : std::vector<GUInt64> GetBlockSize() const override
5209 : {
5210 550 : std::vector<GUInt64> ret(GetDimensionCount());
5211 1100 : const auto parentBlockSize(m_poParent->GetBlockSize());
5212 1474 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5213 : {
5214 924 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5215 924 : if (iOldAxis != static_cast<size_t>(-1))
5216 : {
5217 924 : ret[i] = parentBlockSize[iOldAxis];
5218 : }
5219 : }
5220 1100 : return ret;
5221 : }
5222 :
5223 : std::shared_ptr<GDALAttribute>
5224 14 : GetAttribute(const std::string &osName) const override
5225 : {
5226 14 : return m_poParent->GetAttribute(osName);
5227 : }
5228 :
5229 : std::vector<std::shared_ptr<GDALAttribute>>
5230 37 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5231 : {
5232 37 : return m_poParent->GetAttributes(papszOptions);
5233 : }
5234 : };
5235 :
5236 : /************************************************************************/
5237 : /* PrepareParentArrays() */
5238 : /************************************************************************/
5239 :
5240 756 : void GDALSlicedMDArray::PrepareParentArrays(
5241 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5242 : const GPtrDiff_t *bufferStride) const
5243 : {
5244 756 : const size_t nParentDimCount = m_parentRanges.size();
5245 2130 : for (size_t i = 0; i < nParentDimCount; i++)
5246 : {
5247 : // For dimensions in parent that have no existence in sliced array
5248 1374 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5249 : }
5250 :
5251 1898 : for (size_t i = 0; i < m_dims.size(); i++)
5252 : {
5253 1142 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5254 1142 : if (iParent != static_cast<size_t>(-1))
5255 : {
5256 1140 : m_parentStart[iParent] =
5257 1140 : m_parentRanges[iParent].m_nIncr >= 0
5258 1140 : ? m_parentRanges[iParent].m_nStartIdx +
5259 848 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5260 292 : : m_parentRanges[iParent].m_nStartIdx -
5261 584 : arrayStartIdx[i] *
5262 292 : static_cast<GUInt64>(
5263 292 : -m_parentRanges[iParent].m_nIncr);
5264 1140 : m_parentCount[iParent] = count[i];
5265 1140 : if (arrayStep)
5266 : {
5267 1139 : m_parentStep[iParent] =
5268 1139 : count[i] == 1 ? 1 :
5269 : // other checks should have ensured this does
5270 : // not overflow
5271 945 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5272 : }
5273 1140 : if (bufferStride)
5274 : {
5275 1139 : m_parentStride[iParent] = bufferStride[i];
5276 : }
5277 : }
5278 : }
5279 756 : }
5280 :
5281 : /************************************************************************/
5282 : /* IRead() */
5283 : /************************************************************************/
5284 :
5285 723 : 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 723 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5292 1446 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5293 723 : m_parentStep.data(), m_parentStride.data(),
5294 723 : 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 709 : 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 709 : const auto &srcDims(self->GetDimensions());
5337 709 : if (srcDims.empty())
5338 : {
5339 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5340 2 : return nullptr;
5341 : }
5342 :
5343 1414 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5344 707 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5345 :
5346 1414 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5347 1414 : std::vector<size_t> mapDimIdxToParentDimIdx;
5348 1414 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5349 707 : newDims.reserve(nTokens);
5350 707 : mapDimIdxToParentDimIdx.reserve(nTokens);
5351 707 : parentRanges.reserve(nTokens);
5352 :
5353 707 : bool bGotEllipsis = false;
5354 707 : size_t nCurSrcDim = 0;
5355 1414 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5356 2091 : for (size_t i = 0; i < nTokens; i++)
5357 : {
5358 1401 : const char *pszIdxSpec = aosTokens[i];
5359 1401 : if (EQUAL(pszIdxSpec, "..."))
5360 : {
5361 129 : if (bGotEllipsis)
5362 : {
5363 2 : CPLError(CE_Failure, CPLE_AppDefined,
5364 : "Only one single ellipsis is supported");
5365 2 : return nullptr;
5366 : }
5367 127 : bGotEllipsis = true;
5368 127 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5369 263 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5370 : {
5371 136 : parentRanges.emplace_back(0, 1);
5372 136 : newDims.push_back(srcDims[nCurSrcDim]);
5373 136 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5374 : }
5375 127 : continue;
5376 : }
5377 1272 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5378 1269 : 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 1269 : 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 942 : 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 941 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5418 941 : int nRangeTokens = aosRangeTokens.size();
5419 941 : if (nRangeTokens > 3)
5420 : {
5421 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5422 : pszIdxSpec);
5423 1 : return nullptr;
5424 : }
5425 940 : if (nRangeTokens <= 1)
5426 : {
5427 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5428 : pszIdxSpec);
5429 1 : return nullptr;
5430 : }
5431 939 : const char *pszStart = aosRangeTokens[0];
5432 939 : const char *pszEnd = aosRangeTokens[1];
5433 939 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5434 939 : GDALSlicedMDArray::Range range;
5435 939 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5436 939 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5437 1877 : if (range.m_nIncr == 0 ||
5438 938 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5439 : {
5440 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5441 1 : return nullptr;
5442 : }
5443 938 : auto startIdx(CPLAtoGIntBig(pszStart));
5444 938 : 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 938 : const bool bPosIncr = range.m_nIncr > 0;
5452 938 : range.m_nStartIdx = startIdx;
5453 1876 : range.m_nStartIdx = EQUAL(pszStart, "")
5454 938 : ? (bPosIncr ? 0 : nDimSize - 1)
5455 : : range.m_nStartIdx;
5456 938 : if (range.m_nStartIdx >= nDimSize - 1)
5457 286 : range.m_nStartIdx = nDimSize - 1;
5458 938 : auto endIdx(CPLAtoGIntBig(pszEnd));
5459 938 : 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 938 : GUInt64 nEndIdx = endIdx;
5468 938 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5469 938 : 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 934 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5480 934 : const auto nAbsIncr = std::abs(range.m_nIncr);
5481 934 : const GUInt64 newSize =
5482 302 : (pszStart[0] == 0 && pszEnd[0] == 0 &&
5483 302 : range.m_nStartIdx == nEndIdx)
5484 1868 : ? 1
5485 : : bPosIncr
5486 1061 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5487 128 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5488 934 : const auto &poSrcDim = srcDims[nCurSrcDim];
5489 1484 : 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 1506 : std::string osNewDimName(poSrcDim->GetName());
5497 753 : if (bRenameDimensions)
5498 : {
5499 : osNewDimName =
5500 1410 : "subset_" + poSrcDim->GetName() +
5501 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5502 : "_" CPL_FRMT_GUIB,
5503 705 : static_cast<GUIntBig>(range.m_nStartIdx),
5504 705 : static_cast<GIntBig>(range.m_nIncr),
5505 705 : static_cast<GUIntBig>(newSize));
5506 : }
5507 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5508 1506 : std::string(), osNewDimName, poSrcDim->GetType(),
5509 753 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5510 : : std::string(),
5511 1506 : newSize);
5512 753 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5513 910 : if (poSrcIndexingVar &&
5514 910 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5515 157 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5516 : {
5517 : std::vector<std::shared_ptr<GDALDimension>>
5518 628 : indexingVarNewDims{poNewDim};
5519 314 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5520 : std::vector<std::shared_ptr<GDALMDArray>>
5521 314 : indexingVarNewIndexingVar;
5522 : std::vector<GDALSlicedMDArray::Range>
5523 314 : indexingVarParentRanges{range};
5524 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5525 : poSrcIndexingVar, pszIdxSpec,
5526 157 : std::move(indexingVarNewDims),
5527 157 : std::move(indexingVarMapDimIdxToParentDimIdx),
5528 157 : std::move(indexingVarNewIndexingVar),
5529 471 : std::move(indexingVarParentRanges));
5530 157 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5531 157 : apoNewIndexingVariables.push_back(
5532 157 : std::move(poNewIndexingVar));
5533 : }
5534 753 : newDims.push_back(std::move(poNewDim));
5535 : }
5536 934 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5537 934 : parentRanges.emplace_back(range);
5538 : }
5539 :
5540 1254 : nCurSrcDim++;
5541 : }
5542 763 : 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 690 : GDALMDArray::ViewSpec viewSpec;
5550 690 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5551 690 : viewSpec.m_parentRanges = parentRanges;
5552 690 : viewSpecs.emplace_back(std::move(viewSpec));
5553 :
5554 1380 : return GDALSlicedMDArray::Create(
5555 690 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5556 1380 : 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 465 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5573 : const std::string &fieldName,
5574 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5575 1860 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5576 930 : " of " +
5577 465 : poParent->GetFullName()),
5578 : GDALPamMDArray(
5579 930 : std::string(),
5580 930 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5581 930 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5582 : m_poParent(poParent), m_dt(srcComp->GetType()),
5583 2325 : m_srcCompName(srcComp->GetName())
5584 : {
5585 465 : m_pabyNoData.resize(m_dt.GetSize());
5586 465 : }
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 465 : 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 465 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5607 465 : newAr->SetSelf(newAr);
5608 465 : return newAr;
5609 : }
5610 :
5611 930 : ~GDALExtractFieldMDArray() override
5612 465 : {
5613 465 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5614 930 : }
5615 :
5616 200 : bool IsWritable() const override
5617 : {
5618 200 : return m_poParent->IsWritable();
5619 : }
5620 :
5621 1314 : const std::string &GetFilename() const override
5622 : {
5623 1314 : return m_poParent->GetFilename();
5624 : }
5625 :
5626 : const std::vector<std::shared_ptr<GDALDimension>> &
5627 1384 : GetDimensions() const override
5628 : {
5629 1384 : return m_poParent->GetDimensions();
5630 : }
5631 :
5632 1137 : const GDALExtendedDataType &GetDataType() const override
5633 : {
5634 1137 : 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 99 : const void *GetRawNoDataValue() const override
5648 : {
5649 99 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5650 99 : if (parentNoData == nullptr)
5651 6 : return nullptr;
5652 :
5653 93 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5654 93 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5655 :
5656 186 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5657 186 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5658 186 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5659 93 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5660 279 : std::move(comps)));
5661 :
5662 93 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5663 93 : &m_pabyNoData[0], tmpDT);
5664 :
5665 93 : 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 201 : std::vector<GUInt64> GetBlockSize() const override
5681 : {
5682 201 : return m_poParent->GetBlockSize();
5683 : }
5684 : };
5685 :
5686 : /************************************************************************/
5687 : /* IRead() */
5688 : /************************************************************************/
5689 :
5690 414 : 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 828 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5698 828 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5699 828 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5700 : auto tmpDT(GDALExtendedDataType::Create(
5701 828 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5702 :
5703 414 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5704 828 : tmpDT, pDstBuffer);
5705 : }
5706 :
5707 : /************************************************************************/
5708 : /* CreateFieldNameExtractArray() */
5709 : /************************************************************************/
5710 :
5711 : static std::shared_ptr<GDALMDArray>
5712 466 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5713 : const std::string &fieldName)
5714 : {
5715 466 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5716 466 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5717 983 : for (const auto &comp : self->GetDataType().GetComponents())
5718 : {
5719 982 : if (comp->GetName() == fieldName)
5720 : {
5721 465 : srcComp = ∁
5722 465 : break;
5723 : }
5724 : }
5725 466 : 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 465 : 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 1109 : GDALMDArray::GetView(const std::string &viewExpr) const
5820 : {
5821 2218 : std::vector<ViewSpec> viewSpecs;
5822 2218 : return GetView(viewExpr, true, viewSpecs);
5823 : }
5824 :
5825 : //! @cond Doxygen_Suppress
5826 : std::shared_ptr<GDALMDArray>
5827 1181 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5828 : std::vector<ViewSpec> &viewSpecs) const
5829 : {
5830 2362 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5831 1181 : if (!self)
5832 : {
5833 1 : CPLError(CE_Failure, CPLE_AppDefined,
5834 : "Driver implementation issue: m_pSelf not set !");
5835 1 : return nullptr;
5836 : }
5837 1180 : std::string curExpr(viewExpr);
5838 : while (true)
5839 : {
5840 1183 : if (curExpr.empty() || curExpr[0] != '[')
5841 : {
5842 2 : CPLError(CE_Failure, CPLE_AppDefined,
5843 : "Slice string should start with ['");
5844 1180 : return nullptr;
5845 : }
5846 :
5847 1181 : std::string fieldName;
5848 : size_t endExpr;
5849 1181 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5850 : {
5851 470 : 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 468 : size_t idx = 2;
5858 5119 : for (; idx < curExpr.size(); idx++)
5859 : {
5860 5118 : const char ch = curExpr[idx];
5861 5118 : if (ch == curExpr[1])
5862 467 : break;
5863 4651 : if (ch == '\\' && idx + 1 < curExpr.size())
5864 : {
5865 1 : fieldName += curExpr[idx + 1];
5866 1 : idx++;
5867 : }
5868 : else
5869 : {
5870 4650 : fieldName += ch;
5871 : }
5872 : }
5873 468 : 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 466 : endExpr = idx + 1;
5880 : }
5881 : else
5882 : {
5883 711 : endExpr = curExpr.find(']');
5884 : }
5885 1177 : if (endExpr == std::string::npos)
5886 : {
5887 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5888 1 : return nullptr;
5889 : }
5890 1176 : if (endExpr == 1)
5891 : {
5892 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5893 1 : return nullptr;
5894 : }
5895 1175 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5896 :
5897 1175 : if (!fieldName.empty())
5898 : {
5899 932 : ViewSpec viewSpec;
5900 466 : viewSpec.m_osFieldName = fieldName;
5901 466 : viewSpecs.emplace_back(std::move(viewSpec));
5902 : }
5903 :
5904 1175 : auto newArray = !fieldName.empty()
5905 : ? CreateFieldNameExtractArray(self, fieldName)
5906 : : CreateSlicedArray(self, viewExpr, activeSlice,
5907 1175 : bRenameDimensions, viewSpecs);
5908 :
5909 1175 : if (endExpr == curExpr.size() - 1)
5910 : {
5911 1172 : 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 349 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7572 : {
7573 349 : dfStart = 0;
7574 349 : dfIncrement = 0;
7575 349 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7576 0 : return false;
7577 349 : const auto nSize = GetDimensions()[0]->GetSize();
7578 349 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7579 2 : return false;
7580 :
7581 347 : size_t nCount = static_cast<size_t>(nSize);
7582 694 : std::vector<double> adfTmp;
7583 : try
7584 : {
7585 347 : adfTmp.resize(nCount);
7586 : }
7587 0 : catch (const std::exception &)
7588 : {
7589 0 : return false;
7590 : }
7591 :
7592 347 : GUInt64 anStart[1] = {0};
7593 347 : size_t anCount[1] = {nCount};
7594 :
7595 : const auto IsRegularlySpacedInternal =
7596 44577 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7597 : {
7598 400 : dfStart = adfTmp[0];
7599 400 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7600 400 : if (dfIncrement == 0)
7601 : {
7602 3 : return false;
7603 : }
7604 11033 : for (size_t i = 1; i < anCount[0]; i++)
7605 : {
7606 10648 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7607 10648 : 1e-3 * fabs(dfIncrement))
7608 : {
7609 12 : return false;
7610 : }
7611 : }
7612 385 : return true;
7613 347 : };
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 347 : const auto nBlockSize = GetBlockSize()[0];
7620 347 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7621 : {
7622 : size_t nReducedCount =
7623 56 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7624 176 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7625 120 : nReducedCount *= 2;
7626 56 : anCount[0] = nReducedCount;
7627 56 : if (!Read(anStart, anCount, nullptr, nullptr,
7628 112 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7629 : {
7630 0 : return false;
7631 : }
7632 56 : if (!IsRegularlySpacedInternal())
7633 : {
7634 3 : return false;
7635 : }
7636 :
7637 : // Get next values
7638 53 : anStart[0] = nReducedCount;
7639 53 : anCount[0] = nCount - nReducedCount;
7640 : }
7641 :
7642 344 : if (!Read(anStart, anCount, nullptr, nullptr,
7643 688 : GDALExtendedDataType::Create(GDT_Float64),
7644 344 : &adfTmp[static_cast<size_t>(anStart[0])]))
7645 : {
7646 0 : return false;
7647 : }
7648 :
7649 344 : 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 448 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7669 : bool bPixelIsPoint,
7670 : GDALGeoTransform >) const
7671 : {
7672 448 : const auto &dims(GetDimensions());
7673 896 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7674 896 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7675 448 : double dfXStart = 0.0;
7676 448 : double dfXSpacing = 0.0;
7677 448 : double dfYStart = 0.0;
7678 448 : double dfYSpacing = 0.0;
7679 1052 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7680 604 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7681 884 : poVarY && poVarY->GetDimensionCount() == 1 &&
7682 291 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7683 1036 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7684 286 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7685 : {
7686 286 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7687 286 : gt[1] = dfXSpacing;
7688 286 : gt[2] = 0;
7689 286 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7690 286 : gt[4] = 0;
7691 286 : gt[5] = dfYSpacing;
7692 286 : return true;
7693 : }
7694 162 : 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 : int GetOverviewCount() override;
8735 : GDALRasterBand *GetOverview(int idx) override;
8736 : };
8737 :
8738 : class GDALDatasetFromArray final : public GDALPamDataset
8739 : {
8740 : friend class GDALRasterBandFromArray;
8741 :
8742 : std::shared_ptr<GDALMDArray> m_poArray;
8743 : const size_t m_iXDim;
8744 : const size_t m_iYDim;
8745 : const CPLStringList m_aosOptions;
8746 : GDALGeoTransform m_gt{};
8747 : bool m_bHasGT = false;
8748 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8749 : GDALMultiDomainMetadata m_oMDD{};
8750 : std::string m_osOvrFilename{};
8751 : bool m_bOverviewsDiscovered = false;
8752 : std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
8753 : m_apoOverviews{};
8754 :
8755 : public:
8756 424 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8757 : size_t iXDim, size_t iYDim,
8758 : const CPLStringList &aosOptions)
8759 424 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
8760 424 : m_aosOptions(aosOptions)
8761 : {
8762 : // Initialize an overview filename from the filename of the array
8763 : // and its name.
8764 424 : const std::string &osFilename = m_poArray->GetFilename();
8765 424 : if (!osFilename.empty())
8766 : {
8767 385 : m_osOvrFilename = osFilename;
8768 385 : m_osOvrFilename += '.';
8769 24959 : for (char ch : m_poArray->GetName())
8770 : {
8771 24574 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8772 21450 : (ch >= 'a' && ch <= 'z') || ch == '_')
8773 : {
8774 19387 : m_osOvrFilename += ch;
8775 : }
8776 : else
8777 : {
8778 5187 : m_osOvrFilename += '_';
8779 : }
8780 : }
8781 385 : m_osOvrFilename += ".ovr";
8782 385 : oOvManager.Initialize(this);
8783 : }
8784 424 : }
8785 :
8786 : static std::unique_ptr<GDALDatasetFromArray>
8787 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8788 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8789 : CSLConstList papszOptions);
8790 :
8791 : ~GDALDatasetFromArray() override;
8792 :
8793 596 : CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override
8794 : {
8795 596 : CPLErr eErr = CE_None;
8796 596 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8797 : {
8798 596 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8799 : CE_None)
8800 0 : eErr = CE_Failure;
8801 596 : m_poArray.reset();
8802 : }
8803 596 : return eErr;
8804 : }
8805 :
8806 83 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8807 : {
8808 83 : gt = m_gt;
8809 83 : return m_bHasGT ? CE_None : CE_Failure;
8810 : }
8811 :
8812 93 : const OGRSpatialReference *GetSpatialRef() const override
8813 : {
8814 93 : if (m_poArray->GetDimensionCount() < 2)
8815 3 : return nullptr;
8816 90 : m_poSRS = m_poArray->GetSpatialRef();
8817 90 : if (m_poSRS)
8818 : {
8819 46 : m_poSRS.reset(m_poSRS->Clone());
8820 92 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8821 138 : for (auto &m : axisMapping)
8822 : {
8823 92 : if (m == static_cast<int>(m_iXDim) + 1)
8824 46 : m = 1;
8825 46 : else if (m == static_cast<int>(m_iYDim) + 1)
8826 46 : m = 2;
8827 : }
8828 46 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8829 : }
8830 90 : return m_poSRS.get();
8831 : }
8832 :
8833 8 : CPLErr SetMetadata(CSLConstList papszMetadata,
8834 : const char *pszDomain) override
8835 : {
8836 8 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8837 : }
8838 :
8839 183 : CSLConstList GetMetadata(const char *pszDomain) override
8840 : {
8841 183 : return m_oMDD.GetMetadata(pszDomain);
8842 : }
8843 :
8844 247 : const char *GetMetadataItem(const char *pszName,
8845 : const char *pszDomain) override
8846 : {
8847 447 : if (!m_osOvrFilename.empty() && pszName &&
8848 467 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8849 20 : EQUAL(pszDomain, "OVERVIEWS"))
8850 : {
8851 20 : return m_osOvrFilename.c_str();
8852 : }
8853 227 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8854 : }
8855 :
8856 62 : void DiscoverOverviews()
8857 : {
8858 62 : if (!m_bOverviewsDiscovered)
8859 : {
8860 20 : m_bOverviewsDiscovered = true;
8861 20 : if (const int nOverviews = m_poArray->GetOverviewCount())
8862 : {
8863 8 : if (auto poRootGroup = m_poArray->GetRootGroup())
8864 : {
8865 4 : const size_t nDims = m_poArray->GetDimensionCount();
8866 8 : CPLStringList aosOptions(m_aosOptions);
8867 4 : aosOptions.SetNameValue("LOAD_PAM", "NO");
8868 11 : for (int iOvr = 0; iOvr < nOverviews; ++iOvr)
8869 : {
8870 14 : if (auto poOvrArray = m_poArray->GetOverview(iOvr))
8871 : {
8872 14 : if (poOvrArray->GetDimensionCount() == nDims &&
8873 7 : poOvrArray->GetDataType() ==
8874 7 : m_poArray->GetDataType())
8875 : {
8876 : auto poOvrDS =
8877 7 : Create(poOvrArray, m_iXDim, m_iYDim,
8878 14 : poRootGroup, aosOptions);
8879 7 : if (poOvrDS)
8880 : {
8881 7 : m_apoOverviews.push_back(
8882 14 : std::unique_ptr<
8883 : GDALDataset,
8884 : GDALDatasetUniquePtrReleaser>(
8885 7 : poOvrDS.release()));
8886 : }
8887 : }
8888 : }
8889 : }
8890 : }
8891 : }
8892 : }
8893 62 : }
8894 : };
8895 :
8896 848 : GDALDatasetFromArray::~GDALDatasetFromArray()
8897 : {
8898 424 : GDALDatasetFromArray::Close();
8899 848 : }
8900 :
8901 : /************************************************************************/
8902 : /* GDALRasterBandFromArray() */
8903 : /************************************************************************/
8904 :
8905 500 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8906 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8907 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8908 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8909 500 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8910 : {
8911 500 : const auto &poArray(poDSIn->m_poArray);
8912 500 : const auto &dims(poArray->GetDimensions());
8913 500 : const auto nDimCount(dims.size());
8914 1000 : const auto blockSize(poArray->GetBlockSize());
8915 484 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8916 984 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8917 330 : blockSize[poDSIn->m_iYDim]))
8918 : : 1;
8919 500 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8920 345 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8921 345 : blockSize[poDSIn->m_iXDim]))
8922 500 : : poDSIn->GetRasterXSize();
8923 500 : eDataType = poArray->GetDataType().GetNumericDataType();
8924 500 : eAccess = poDSIn->eAccess;
8925 500 : m_anOffset.resize(nDimCount);
8926 500 : m_anCount.resize(nDimCount, 1);
8927 500 : m_anStride.resize(nDimCount);
8928 1615 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8929 : {
8930 1115 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8931 : {
8932 262 : std::string dimName(dims[i]->GetName());
8933 131 : GUInt64 nIndex = anOtherDimCoord[j];
8934 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8935 : // subsetted dimensions as generated by GetView()
8936 131 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8937 : {
8938 : CPLStringList aosTokens(
8939 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8940 6 : if (aosTokens.size() == 5)
8941 : {
8942 6 : dimName = aosTokens[1];
8943 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8944 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8945 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8946 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8947 0 : : nStartDim - (nIndex * -nIncrDim);
8948 : }
8949 : }
8950 131 : if (nDimCount != 3 || dimName != "Band")
8951 : {
8952 70 : SetMetadataItem(
8953 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8954 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8955 : }
8956 :
8957 131 : auto indexingVar = dims[i]->GetIndexingVariable();
8958 :
8959 : // If the indexing variable is also listed in band parameter arrays,
8960 : // then don't use our default formatting
8961 131 : if (indexingVar)
8962 : {
8963 47 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8964 : {
8965 14 : if (oItem.poArray->GetFullName() ==
8966 14 : indexingVar->GetFullName())
8967 : {
8968 12 : indexingVar.reset();
8969 12 : break;
8970 : }
8971 : }
8972 : }
8973 :
8974 164 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8975 33 : indexingVar->GetDimensions()[0]->GetSize() ==
8976 33 : dims[i]->GetSize())
8977 : {
8978 33 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8979 : {
8980 0 : if (!bHasWarned)
8981 : {
8982 0 : CPLError(
8983 : CE_Warning, CPLE_AppDefined,
8984 : "Maximum delay to load band metadata from "
8985 : "dimension indexing variables has expired. "
8986 : "Increase the value of the "
8987 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8988 : "option of GDALMDArray::AsClassicDataset() "
8989 : "(also accessible as the "
8990 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8991 : "configuration option), "
8992 : "or set it to 'unlimited' for unlimited delay. ");
8993 0 : bHasWarned = true;
8994 : }
8995 : }
8996 : else
8997 : {
8998 33 : size_t nCount = 1;
8999 33 : const auto &dt(indexingVar->GetDataType());
9000 66 : std::vector<GByte> abyTmp(dt.GetSize());
9001 66 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
9002 33 : nullptr, nullptr, dt, &abyTmp[0]))
9003 : {
9004 33 : char *pszTmp = nullptr;
9005 33 : GDALExtendedDataType::CopyValue(
9006 33 : &abyTmp[0], dt, &pszTmp,
9007 66 : GDALExtendedDataType::CreateString());
9008 33 : dt.FreeDynamicMemory(abyTmp.data());
9009 33 : if (pszTmp)
9010 : {
9011 33 : SetMetadataItem(
9012 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
9013 : pszTmp);
9014 33 : CPLFree(pszTmp);
9015 : }
9016 :
9017 33 : const auto &unit(indexingVar->GetUnit());
9018 33 : if (!unit.empty())
9019 : {
9020 12 : SetMetadataItem(
9021 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
9022 : unit.c_str());
9023 : }
9024 : }
9025 : }
9026 : }
9027 :
9028 149 : for (const auto &oItem : aoBandParameterMetadataItems[j])
9029 : {
9030 36 : CPLString osVal;
9031 :
9032 18 : size_t nCount = 1;
9033 18 : const auto &dt(oItem.poArray->GetDataType());
9034 18 : if (oItem.bDefinitionUsesPctForG)
9035 : {
9036 : // There is one and only one %[x][.y]f|g in osDefinition
9037 16 : std::vector<GByte> abyTmp(dt.GetSize());
9038 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9039 8 : nullptr, nullptr, dt, &abyTmp[0]))
9040 : {
9041 8 : double dfVal = 0;
9042 8 : GDALExtendedDataType::CopyValue(
9043 8 : &abyTmp[0], dt, &dfVal,
9044 16 : GDALExtendedDataType::Create(GDT_Float64));
9045 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
9046 8 : dt.FreeDynamicMemory(abyTmp.data());
9047 : }
9048 : }
9049 : else
9050 : {
9051 : // There should be zero or one %s in osDefinition
9052 10 : char *pszValue = nullptr;
9053 10 : if (dt.GetClass() == GEDTC_STRING)
9054 : {
9055 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
9056 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
9057 2 : dt, &pszValue));
9058 : }
9059 : else
9060 : {
9061 16 : std::vector<GByte> abyTmp(dt.GetSize());
9062 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9063 : nullptr, nullptr, dt,
9064 8 : &abyTmp[0]))
9065 : {
9066 8 : GDALExtendedDataType::CopyValue(
9067 8 : &abyTmp[0], dt, &pszValue,
9068 16 : GDALExtendedDataType::CreateString());
9069 : }
9070 : }
9071 :
9072 10 : if (pszValue)
9073 : {
9074 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9075 10 : CPLFree(pszValue);
9076 : }
9077 : }
9078 18 : if (!osVal.empty())
9079 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9080 : }
9081 :
9082 131 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9083 : {
9084 : auto &poCentralWavelengthArray =
9085 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9086 4 : size_t nCount = 1;
9087 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9088 8 : std::vector<GByte> abyTmp(dt.GetSize());
9089 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9090 : &nCount, nullptr, nullptr,
9091 4 : dt, &abyTmp[0]))
9092 : {
9093 4 : double dfVal = 0;
9094 4 : GDALExtendedDataType::CopyValue(
9095 4 : &abyTmp[0], dt, &dfVal,
9096 8 : GDALExtendedDataType::Create(GDT_Float64));
9097 4 : dt.FreeDynamicMemory(abyTmp.data());
9098 4 : SetMetadataItem(
9099 : "CENTRAL_WAVELENGTH_UM",
9100 : CPLSPrintf(
9101 4 : "%g", dfVal * aoBandImageryMetadata[j]
9102 4 : .dfCentralWavelengthToMicrometer),
9103 : "IMAGERY");
9104 : }
9105 : }
9106 :
9107 131 : if (aoBandImageryMetadata[j].poFWHMArray)
9108 : {
9109 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9110 2 : size_t nCount = 1;
9111 2 : const auto &dt(poFWHMArray->GetDataType());
9112 4 : std::vector<GByte> abyTmp(dt.GetSize());
9113 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9114 2 : nullptr, dt, &abyTmp[0]))
9115 : {
9116 2 : double dfVal = 0;
9117 2 : GDALExtendedDataType::CopyValue(
9118 2 : &abyTmp[0], dt, &dfVal,
9119 4 : GDALExtendedDataType::Create(GDT_Float64));
9120 2 : dt.FreeDynamicMemory(abyTmp.data());
9121 2 : SetMetadataItem(
9122 : "FWHM_UM",
9123 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9124 2 : .dfFWHMToMicrometer),
9125 : "IMAGERY");
9126 : }
9127 : }
9128 :
9129 131 : m_anOffset[i] = anOtherDimCoord[j];
9130 131 : j++;
9131 : }
9132 : }
9133 500 : }
9134 :
9135 : /************************************************************************/
9136 : /* GetNoDataValue() */
9137 : /************************************************************************/
9138 :
9139 139 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9140 : {
9141 139 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9142 139 : const auto &poArray(l_poDS->m_poArray);
9143 139 : bool bHasNodata = false;
9144 139 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9145 139 : if (pbHasNoData)
9146 125 : *pbHasNoData = bHasNodata;
9147 139 : return res;
9148 : }
9149 :
9150 : /************************************************************************/
9151 : /* GetNoDataValueAsInt64() */
9152 : /************************************************************************/
9153 :
9154 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9155 : {
9156 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9157 1 : const auto &poArray(l_poDS->m_poArray);
9158 1 : bool bHasNodata = false;
9159 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9160 1 : if (pbHasNoData)
9161 1 : *pbHasNoData = bHasNodata;
9162 1 : return nodata;
9163 : }
9164 :
9165 : /************************************************************************/
9166 : /* GetNoDataValueAsUInt64() */
9167 : /************************************************************************/
9168 :
9169 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9170 : {
9171 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9172 1 : const auto &poArray(l_poDS->m_poArray);
9173 1 : bool bHasNodata = false;
9174 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9175 1 : if (pbHasNoData)
9176 1 : *pbHasNoData = bHasNodata;
9177 1 : return nodata;
9178 : }
9179 :
9180 : /************************************************************************/
9181 : /* GetOffset() */
9182 : /************************************************************************/
9183 :
9184 42 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9185 : {
9186 42 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9187 42 : const auto &poArray(l_poDS->m_poArray);
9188 42 : bool bHasValue = false;
9189 42 : double dfRes = poArray->GetOffset(&bHasValue);
9190 42 : if (pbHasOffset)
9191 23 : *pbHasOffset = bHasValue;
9192 42 : return dfRes;
9193 : }
9194 :
9195 : /************************************************************************/
9196 : /* GetUnitType() */
9197 : /************************************************************************/
9198 :
9199 52 : const char *GDALRasterBandFromArray::GetUnitType()
9200 : {
9201 52 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9202 52 : const auto &poArray(l_poDS->m_poArray);
9203 52 : return poArray->GetUnit().c_str();
9204 : }
9205 :
9206 : /************************************************************************/
9207 : /* GetScale() */
9208 : /************************************************************************/
9209 :
9210 40 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9211 : {
9212 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9213 40 : const auto &poArray(l_poDS->m_poArray);
9214 40 : bool bHasValue = false;
9215 40 : double dfRes = poArray->GetScale(&bHasValue);
9216 40 : if (pbHasScale)
9217 21 : *pbHasScale = bHasValue;
9218 40 : return dfRes;
9219 : }
9220 :
9221 : /************************************************************************/
9222 : /* IReadBlock() */
9223 : /************************************************************************/
9224 :
9225 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9226 : void *pImage)
9227 : {
9228 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9229 68 : const int nXOff = nBlockXOff * nBlockXSize;
9230 68 : const int nYOff = nBlockYOff * nBlockYSize;
9231 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9232 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9233 : GDALRasterIOExtraArg sExtraArg;
9234 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9235 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9236 : nReqXSize, nReqYSize, eDataType, nDTSize,
9237 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9238 : }
9239 :
9240 : /************************************************************************/
9241 : /* IWriteBlock() */
9242 : /************************************************************************/
9243 :
9244 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9245 : void *pImage)
9246 : {
9247 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9248 1 : const int nXOff = nBlockXOff * nBlockXSize;
9249 1 : const int nYOff = nBlockYOff * nBlockYSize;
9250 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9251 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9252 : GDALRasterIOExtraArg sExtraArg;
9253 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9254 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9255 : nReqXSize, nReqYSize, eDataType, nDTSize,
9256 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9257 : }
9258 :
9259 : /************************************************************************/
9260 : /* IRasterIO() */
9261 : /************************************************************************/
9262 :
9263 463 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9264 : int nYOff, int nXSize, int nYSize,
9265 : void *pData, int nBufXSize,
9266 : int nBufYSize, GDALDataType eBufType,
9267 : GSpacing nPixelSpaceBuf,
9268 : GSpacing nLineSpaceBuf,
9269 : GDALRasterIOExtraArg *psExtraArg)
9270 : {
9271 463 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9272 463 : const auto &poArray(l_poDS->m_poArray);
9273 463 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9274 463 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9275 463 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9276 463 : (nLineSpaceBuf % nBufferDTSize) == 0)
9277 : {
9278 463 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9279 463 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9280 926 : m_anStride[l_poDS->m_iXDim] =
9281 463 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9282 463 : if (poArray->GetDimensionCount() >= 2)
9283 : {
9284 450 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9285 450 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9286 450 : m_anStride[l_poDS->m_iYDim] =
9287 450 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9288 : }
9289 463 : if (eRWFlag == GF_Read)
9290 : {
9291 914 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9292 457 : m_anStride.data(),
9293 914 : GDALExtendedDataType::Create(eBufType), pData)
9294 457 : ? CE_None
9295 457 : : CE_Failure;
9296 : }
9297 : else
9298 : {
9299 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9300 6 : m_anStride.data(),
9301 12 : GDALExtendedDataType::Create(eBufType), pData)
9302 6 : ? CE_None
9303 6 : : CE_Failure;
9304 : }
9305 : }
9306 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9307 : pData, nBufXSize, nBufYSize, eBufType,
9308 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9309 : }
9310 :
9311 : /************************************************************************/
9312 : /* GetColorInterpretation() */
9313 : /************************************************************************/
9314 :
9315 74 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9316 : {
9317 74 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9318 74 : const auto &poArray(l_poDS->m_poArray);
9319 222 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9320 74 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9321 : {
9322 6 : bool bOK = false;
9323 6 : GUInt64 nStartIndex = 0;
9324 6 : if (poArray->GetDimensionCount() == 2 &&
9325 0 : poAttr->GetDimensionCount() == 0)
9326 : {
9327 0 : bOK = true;
9328 : }
9329 6 : else if (poArray->GetDimensionCount() == 3)
9330 : {
9331 6 : uint64_t nExtraDimSamples = 1;
9332 6 : const auto &apoDims = poArray->GetDimensions();
9333 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9334 : {
9335 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9336 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9337 : }
9338 6 : if (poAttr->GetDimensionsSize() ==
9339 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9340 : {
9341 6 : bOK = true;
9342 : }
9343 6 : nStartIndex = nBand - 1;
9344 : }
9345 6 : if (bOK)
9346 : {
9347 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9348 6 : const size_t nCount = 1;
9349 6 : const GInt64 arrayStep = 1;
9350 6 : const GPtrDiff_t bufferStride = 1;
9351 6 : char *pszValue = nullptr;
9352 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9353 6 : oStringDT, &pszValue);
9354 6 : if (pszValue)
9355 : {
9356 : const auto eColorInterp =
9357 6 : GDALGetColorInterpretationByName(pszValue);
9358 6 : CPLFree(pszValue);
9359 6 : return eColorInterp;
9360 : }
9361 : }
9362 : }
9363 68 : return GCI_Undefined;
9364 : }
9365 :
9366 : /************************************************************************/
9367 : /* GDALRasterBandFromArray::GetOverviewCount() */
9368 : /************************************************************************/
9369 :
9370 34 : int GDALRasterBandFromArray::GetOverviewCount()
9371 : {
9372 34 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9373 34 : if (nPAMCount)
9374 2 : return nPAMCount;
9375 32 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9376 32 : l_poDS->DiscoverOverviews();
9377 32 : return static_cast<int>(l_poDS->m_apoOverviews.size());
9378 : }
9379 :
9380 : /************************************************************************/
9381 : /* GDALRasterBandFromArray::GetOverview() */
9382 : /************************************************************************/
9383 :
9384 31 : GDALRasterBand *GDALRasterBandFromArray::GetOverview(int idx)
9385 : {
9386 31 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9387 31 : if (nPAMCount)
9388 1 : return GDALPamRasterBand::GetOverview(idx);
9389 30 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9390 30 : l_poDS->DiscoverOverviews();
9391 30 : if (idx < 0 || static_cast<size_t>(idx) >= l_poDS->m_apoOverviews.size())
9392 : {
9393 8 : return nullptr;
9394 : }
9395 22 : return l_poDS->m_apoOverviews[idx]->GetRasterBand(nBand);
9396 : }
9397 :
9398 : /************************************************************************/
9399 : /* GDALDatasetFromArray::Create() */
9400 : /************************************************************************/
9401 :
9402 476 : std::unique_ptr<GDALDatasetFromArray> GDALDatasetFromArray::Create(
9403 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9404 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9405 :
9406 : {
9407 476 : const auto nDimCount(array->GetDimensionCount());
9408 476 : if (nDimCount == 0)
9409 : {
9410 1 : CPLError(CE_Failure, CPLE_NotSupported,
9411 : "Unsupported number of dimensions");
9412 1 : return nullptr;
9413 : }
9414 949 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9415 474 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9416 : {
9417 1 : CPLError(CE_Failure, CPLE_NotSupported,
9418 : "Only arrays with numeric data types "
9419 : "can be exposed as classic GDALDataset");
9420 1 : return nullptr;
9421 : }
9422 474 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9423 451 : (nDimCount >= 2 && iXDim == iYDim))
9424 : {
9425 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9426 8 : return nullptr;
9427 : }
9428 466 : GUInt64 nTotalBands = 1;
9429 466 : const auto &dims(array->GetDimensions());
9430 1473 : for (size_t i = 0; i < nDimCount; ++i)
9431 : {
9432 1008 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9433 : {
9434 94 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9435 : {
9436 1 : CPLError(CE_Failure, CPLE_AppDefined,
9437 : "Too many bands. Operate on a sliced view");
9438 1 : return nullptr;
9439 : }
9440 93 : nTotalBands *= dims[i]->GetSize();
9441 : }
9442 : }
9443 :
9444 930 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9445 930 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9446 1472 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9447 : {
9448 1007 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9449 : {
9450 93 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9451 93 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9452 93 : ++j;
9453 : }
9454 : }
9455 :
9456 465 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9457 :
9458 : const char *pszBandMetadata =
9459 465 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9460 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9461 930 : nNewDimCount);
9462 465 : if (pszBandMetadata)
9463 : {
9464 32 : if (!poRootGroup)
9465 : {
9466 1 : CPLError(CE_Failure, CPLE_AppDefined,
9467 : "Root group should be provided when BAND_METADATA is set");
9468 24 : return nullptr;
9469 : }
9470 31 : CPLJSONDocument oDoc;
9471 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9472 : {
9473 1 : CPLError(CE_Failure, CPLE_AppDefined,
9474 : "Invalid JSON content for BAND_METADATA");
9475 1 : return nullptr;
9476 : }
9477 30 : auto oRoot = oDoc.GetRoot();
9478 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9479 : {
9480 1 : CPLError(CE_Failure, CPLE_AppDefined,
9481 : "Value of BAND_METADATA should be an array");
9482 1 : return nullptr;
9483 : }
9484 :
9485 29 : auto oArray = oRoot.ToArray();
9486 38 : for (int j = 0; j < oArray.Size(); ++j)
9487 : {
9488 30 : const auto oJsonItem = oArray[j];
9489 30 : MetadataItem oItem;
9490 30 : size_t iExtraDimIdx = 0;
9491 :
9492 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9493 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9494 0 : std::shared_ptr<GDALMDArray> poArray;
9495 0 : std::shared_ptr<GDALAttribute> poAttribute;
9496 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9497 : {
9498 1 : CPLError(CE_Failure, CPLE_AppDefined,
9499 : "BAND_METADATA[%d][\"array\"] or "
9500 : "BAND_METADATA[%d][\"attribute\"] is missing",
9501 : j, j);
9502 1 : return nullptr;
9503 : }
9504 48 : else if (!osBandArrayFullname.empty() &&
9505 19 : !osBandAttributeName.empty())
9506 : {
9507 1 : CPLError(
9508 : CE_Failure, CPLE_AppDefined,
9509 : "BAND_METADATA[%d][\"array\"] and "
9510 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9511 : j, j);
9512 1 : return nullptr;
9513 : }
9514 28 : else if (!osBandArrayFullname.empty())
9515 : {
9516 : poArray =
9517 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9518 18 : if (!poArray)
9519 : {
9520 1 : CPLError(CE_Failure, CPLE_AppDefined,
9521 : "Array %s cannot be found",
9522 : osBandArrayFullname.c_str());
9523 3 : return nullptr;
9524 : }
9525 17 : if (poArray->GetDimensionCount() != 1)
9526 : {
9527 1 : CPLError(CE_Failure, CPLE_AppDefined,
9528 : "Array %s is not a 1D array",
9529 : osBandArrayFullname.c_str());
9530 1 : return nullptr;
9531 : }
9532 : const auto &osAuxArrayDimName =
9533 16 : poArray->GetDimensions()[0]->GetName();
9534 : auto oIter =
9535 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9536 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9537 : {
9538 1 : CPLError(
9539 : CE_Failure, CPLE_AppDefined,
9540 : "Dimension %s of array %s is not a non-X/Y dimension "
9541 : "of array %s",
9542 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9543 1 : array->GetName().c_str());
9544 1 : return nullptr;
9545 : }
9546 15 : iExtraDimIdx = oIter->second;
9547 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9548 : }
9549 : else
9550 : {
9551 10 : CPLAssert(!osBandAttributeName.empty());
9552 10 : poAttribute = !osBandAttributeName.empty() &&
9553 10 : osBandAttributeName[0] == '/'
9554 24 : ? poRootGroup->OpenAttributeFromFullname(
9555 : osBandAttributeName)
9556 10 : : array->GetAttribute(osBandAttributeName);
9557 10 : if (!poAttribute)
9558 : {
9559 2 : CPLError(CE_Failure, CPLE_AppDefined,
9560 : "Attribute %s cannot be found",
9561 : osBandAttributeName.c_str());
9562 8 : return nullptr;
9563 : }
9564 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9565 8 : if (aoAttrDims.size() != 1)
9566 : {
9567 4 : CPLError(CE_Failure, CPLE_AppDefined,
9568 : "Attribute %s is not a 1D array",
9569 : osBandAttributeName.c_str());
9570 4 : return nullptr;
9571 : }
9572 4 : bool found = false;
9573 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9574 : {
9575 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9576 5 : ->GetSize() == aoAttrDims[0])
9577 : {
9578 4 : if (found)
9579 : {
9580 2 : CPLError(CE_Failure, CPLE_AppDefined,
9581 : "Several dimensions of %s have the same "
9582 : "size as attribute %s. Cannot infer which "
9583 : "one to bind to!",
9584 1 : array->GetName().c_str(),
9585 : osBandAttributeName.c_str());
9586 1 : return nullptr;
9587 : }
9588 3 : found = true;
9589 3 : iExtraDimIdx = iter.second;
9590 : }
9591 : }
9592 3 : if (!found)
9593 : {
9594 2 : CPLError(
9595 : CE_Failure, CPLE_AppDefined,
9596 : "No dimension of %s has the same size as attribute %s",
9597 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9598 1 : return nullptr;
9599 : }
9600 : }
9601 :
9602 17 : oItem.osName = oJsonItem.GetString("item_name");
9603 17 : if (oItem.osName.empty())
9604 : {
9605 1 : CPLError(CE_Failure, CPLE_AppDefined,
9606 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9607 1 : return nullptr;
9608 : }
9609 :
9610 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9611 :
9612 : // Check correctness of definition
9613 16 : bool bFirstNumericFormatter = true;
9614 16 : std::string osModDefinition;
9615 16 : bool bDefinitionUsesPctForG = false;
9616 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9617 : {
9618 70 : if (osDefinition[k] == '%')
9619 : {
9620 15 : osModDefinition += osDefinition[k];
9621 15 : if (k + 1 == osDefinition.size())
9622 : {
9623 1 : CPLError(CE_Failure, CPLE_AppDefined,
9624 : "Value of "
9625 : "BAND_METADATA[%d][\"item_value\"] = "
9626 : "%s is invalid at offset %d",
9627 : j, osDefinition.c_str(), int(k));
9628 1 : return nullptr;
9629 : }
9630 14 : ++k;
9631 14 : if (osDefinition[k] == '%')
9632 : {
9633 1 : osModDefinition += osDefinition[k];
9634 1 : continue;
9635 : }
9636 13 : if (!bFirstNumericFormatter)
9637 : {
9638 1 : CPLError(CE_Failure, CPLE_AppDefined,
9639 : "Value of "
9640 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9641 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9642 : "formatters should be specified at most once",
9643 : j, osDefinition.c_str(), int(k));
9644 1 : return nullptr;
9645 : }
9646 12 : bFirstNumericFormatter = false;
9647 19 : for (; k < osDefinition.size(); ++k)
9648 : {
9649 19 : osModDefinition += osDefinition[k];
9650 38 : if (!((osDefinition[k] >= '0' &&
9651 16 : osDefinition[k] <= '9') ||
9652 15 : osDefinition[k] == '.'))
9653 12 : break;
9654 : }
9655 24 : if (k == osDefinition.size() ||
9656 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9657 5 : osDefinition[k] != 's'))
9658 : {
9659 1 : CPLError(CE_Failure, CPLE_AppDefined,
9660 : "Value of "
9661 : "BAND_METADATA[%d][\"item_value\"] = "
9662 : "%s is invalid at offset %d: only "
9663 : "%%[x][.y]f|g or %%s formatters are accepted",
9664 : j, osDefinition.c_str(), int(k));
9665 1 : return nullptr;
9666 : }
9667 11 : bDefinitionUsesPctForG =
9668 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9669 11 : if (bDefinitionUsesPctForG)
9670 : {
9671 12 : if (poArray &&
9672 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9673 : {
9674 1 : CPLError(CE_Failure, CPLE_AppDefined,
9675 : "Data type of %s array is not numeric",
9676 1 : poArray->GetName().c_str());
9677 1 : return nullptr;
9678 : }
9679 8 : else if (poAttribute &&
9680 2 : poAttribute->GetDataType().GetClass() !=
9681 6 : GEDTC_NUMERIC)
9682 : {
9683 0 : CPLError(CE_Failure, CPLE_AppDefined,
9684 : "Data type of %s attribute is not numeric",
9685 0 : poAttribute->GetFullName().c_str());
9686 0 : return nullptr;
9687 : }
9688 : }
9689 : }
9690 62 : else if (osDefinition[k] == '$' &&
9691 62 : k + 1 < osDefinition.size() &&
9692 7 : osDefinition[k + 1] == '{')
9693 : {
9694 7 : const auto nPos = osDefinition.find('}', k);
9695 7 : if (nPos == std::string::npos)
9696 : {
9697 1 : CPLError(CE_Failure, CPLE_AppDefined,
9698 : "Value of "
9699 : "BAND_METADATA[%d][\"item_value\"] = "
9700 : "%s is invalid at offset %d",
9701 : j, osDefinition.c_str(), int(k));
9702 3 : return nullptr;
9703 : }
9704 : const auto osAttrName =
9705 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9706 0 : std::shared_ptr<GDALAttribute> poAttr;
9707 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9708 : {
9709 4 : poAttr = poArray->GetAttribute(osAttrName);
9710 4 : if (!poAttr)
9711 : {
9712 1 : CPLError(
9713 : CE_Failure, CPLE_AppDefined,
9714 : "Value of "
9715 : "BAND_METADATA[%d][\"item_value\"] = "
9716 : "%s is invalid: %s is not an attribute of %s",
9717 : j, osDefinition.c_str(), osAttrName.c_str(),
9718 1 : poArray->GetName().c_str());
9719 1 : return nullptr;
9720 : }
9721 : }
9722 : else
9723 : {
9724 : poAttr =
9725 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9726 2 : if (!poAttr)
9727 : {
9728 1 : CPLError(CE_Failure, CPLE_AppDefined,
9729 : "Value of "
9730 : "BAND_METADATA[%d][\"item_value\"] = "
9731 : "%s is invalid: %s is not an attribute",
9732 : j, osDefinition.c_str(),
9733 : osAttrName.c_str());
9734 1 : return nullptr;
9735 : }
9736 : }
9737 4 : k = nPos;
9738 4 : const char *pszValue = poAttr->ReadAsString();
9739 4 : if (!pszValue)
9740 : {
9741 0 : CPLError(CE_Failure, CPLE_AppDefined,
9742 : "Cannot get value of attribute %s as a "
9743 : "string",
9744 : osAttrName.c_str());
9745 0 : return nullptr;
9746 : }
9747 4 : osModDefinition += pszValue;
9748 : }
9749 : else
9750 : {
9751 48 : osModDefinition += osDefinition[k];
9752 : }
9753 : }
9754 :
9755 9 : if (poArray)
9756 8 : oItem.poArray = std::move(poArray);
9757 : else
9758 1 : oItem.poArray = std::move(poAttribute);
9759 9 : oItem.osDefinition = std::move(osModDefinition);
9760 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9761 :
9762 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9763 9 : std::move(oItem));
9764 : }
9765 : }
9766 :
9767 882 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9768 : const char *pszBandImageryMetadata =
9769 441 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9770 441 : if (pszBandImageryMetadata)
9771 : {
9772 20 : if (!poRootGroup)
9773 : {
9774 1 : CPLError(CE_Failure, CPLE_AppDefined,
9775 : "Root group should be provided when BAND_IMAGERY_METADATA "
9776 : "is set");
9777 17 : return nullptr;
9778 : }
9779 19 : CPLJSONDocument oDoc;
9780 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9781 : {
9782 1 : CPLError(CE_Failure, CPLE_AppDefined,
9783 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9784 1 : return nullptr;
9785 : }
9786 18 : auto oRoot = oDoc.GetRoot();
9787 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9788 : {
9789 1 : CPLError(CE_Failure, CPLE_AppDefined,
9790 : "Value of BAND_IMAGERY_METADATA should be an object");
9791 1 : return nullptr;
9792 : }
9793 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9794 : {
9795 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9796 20 : oJsonItem.GetName() == "FWHM_UM")
9797 : {
9798 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9799 : const auto osBandAttributeName =
9800 34 : oJsonItem.GetString("attribute");
9801 0 : std::shared_ptr<GDALMDArray> poArray;
9802 0 : std::shared_ptr<GDALAttribute> poAttribute;
9803 17 : size_t iExtraDimIdx = 0;
9804 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9805 : {
9806 2 : CPLError(CE_Failure, CPLE_AppDefined,
9807 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9808 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9809 : "missing",
9810 2 : oJsonItem.GetName().c_str(),
9811 2 : oJsonItem.GetName().c_str());
9812 1 : return nullptr;
9813 : }
9814 25 : else if (!osBandArrayFullname.empty() &&
9815 9 : !osBandAttributeName.empty())
9816 : {
9817 2 : CPLError(CE_Failure, CPLE_AppDefined,
9818 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9819 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9820 : "mutually exclusive",
9821 2 : oJsonItem.GetName().c_str(),
9822 2 : oJsonItem.GetName().c_str());
9823 1 : return nullptr;
9824 : }
9825 15 : else if (!osBandArrayFullname.empty())
9826 : {
9827 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9828 8 : osBandArrayFullname);
9829 8 : if (!poArray)
9830 : {
9831 1 : CPLError(CE_Failure, CPLE_AppDefined,
9832 : "Array %s cannot be found",
9833 : osBandArrayFullname.c_str());
9834 3 : return nullptr;
9835 : }
9836 7 : if (poArray->GetDimensionCount() != 1)
9837 : {
9838 1 : CPLError(CE_Failure, CPLE_AppDefined,
9839 : "Array %s is not a 1D array",
9840 : osBandArrayFullname.c_str());
9841 1 : return nullptr;
9842 : }
9843 : const auto &osAuxArrayDimName =
9844 6 : poArray->GetDimensions()[0]->GetName();
9845 : auto oIter =
9846 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9847 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9848 : {
9849 1 : CPLError(CE_Failure, CPLE_AppDefined,
9850 : "Dimension \"%s\" of array \"%s\" is not a "
9851 : "non-X/Y dimension of array \"%s\"",
9852 : osAuxArrayDimName.c_str(),
9853 : osBandArrayFullname.c_str(),
9854 1 : array->GetName().c_str());
9855 1 : return nullptr;
9856 : }
9857 5 : iExtraDimIdx = oIter->second;
9858 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9859 : }
9860 : else
9861 : {
9862 : poAttribute =
9863 7 : !osBandAttributeName.empty() &&
9864 7 : osBandAttributeName[0] == '/'
9865 16 : ? poRootGroup->OpenAttributeFromFullname(
9866 : osBandAttributeName)
9867 7 : : array->GetAttribute(osBandAttributeName);
9868 7 : if (!poAttribute)
9869 : {
9870 2 : CPLError(CE_Failure, CPLE_AppDefined,
9871 : "Attribute %s cannot be found",
9872 : osBandAttributeName.c_str());
9873 6 : return nullptr;
9874 : }
9875 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9876 5 : if (aoAttrDims.size() != 1)
9877 : {
9878 2 : CPLError(CE_Failure, CPLE_AppDefined,
9879 : "Attribute %s is not a 1D array",
9880 : osBandAttributeName.c_str());
9881 2 : return nullptr;
9882 : }
9883 3 : bool found = false;
9884 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9885 : {
9886 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9887 4 : ->GetSize() == aoAttrDims[0])
9888 : {
9889 3 : if (found)
9890 : {
9891 2 : CPLError(CE_Failure, CPLE_AppDefined,
9892 : "Several dimensions of %s have the "
9893 : "same size as attribute %s. Cannot "
9894 : "infer which one to bind to!",
9895 1 : array->GetName().c_str(),
9896 : osBandAttributeName.c_str());
9897 1 : return nullptr;
9898 : }
9899 2 : found = true;
9900 2 : iExtraDimIdx = iter.second;
9901 : }
9902 : }
9903 2 : if (!found)
9904 : {
9905 2 : CPLError(CE_Failure, CPLE_AppDefined,
9906 : "No dimension of %s has the same size as "
9907 : "attribute %s",
9908 1 : array->GetName().c_str(),
9909 : osBandAttributeName.c_str());
9910 1 : return nullptr;
9911 : }
9912 : }
9913 :
9914 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9915 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9916 : {
9917 4 : if (osUnit.back() != '}')
9918 : {
9919 2 : CPLError(CE_Failure, CPLE_AppDefined,
9920 : "Value of "
9921 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9922 : "%s is invalid",
9923 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9924 2 : return nullptr;
9925 : }
9926 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9927 0 : std::shared_ptr<GDALAttribute> poAttr;
9928 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9929 : {
9930 2 : poAttr = poArray->GetAttribute(osAttrName);
9931 2 : if (!poAttr)
9932 : {
9933 2 : CPLError(
9934 : CE_Failure, CPLE_AppDefined,
9935 : "Value of "
9936 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9937 : "%s is invalid: %s is not an attribute of %s",
9938 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9939 : osAttrName.c_str(),
9940 : osBandArrayFullname.c_str());
9941 1 : return nullptr;
9942 : }
9943 : }
9944 : else
9945 : {
9946 : poAttr =
9947 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9948 1 : if (!poAttr)
9949 : {
9950 0 : CPLError(
9951 : CE_Failure, CPLE_AppDefined,
9952 : "Value of "
9953 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9954 : "%s is invalid: %s is not an attribute",
9955 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9956 : osAttrName.c_str());
9957 0 : return nullptr;
9958 : }
9959 : }
9960 :
9961 2 : const char *pszValue = poAttr->ReadAsString();
9962 2 : if (!pszValue)
9963 : {
9964 0 : CPLError(CE_Failure, CPLE_AppDefined,
9965 : "Cannot get value of attribute %s of %s as a "
9966 : "string",
9967 : osAttrName.c_str(),
9968 : osBandArrayFullname.c_str());
9969 0 : return nullptr;
9970 : }
9971 2 : osUnit = pszValue;
9972 : }
9973 4 : double dfConvToUM = 1.0;
9974 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9975 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9976 3 : osUnit == "nanometers")
9977 : {
9978 1 : dfConvToUM = 1e-3;
9979 : }
9980 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9981 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9982 1 : osUnit == "micrometers"))
9983 : {
9984 2 : CPLError(CE_Failure, CPLE_AppDefined,
9985 : "Unhandled value for "
9986 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9987 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9988 1 : return nullptr;
9989 : }
9990 :
9991 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9992 :
9993 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9994 3 : if (poArray)
9995 2 : abstractArray = std::move(poArray);
9996 : else
9997 1 : abstractArray = std::move(poAttribute);
9998 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9999 : {
10000 2 : item.poCentralWavelengthArray = std::move(abstractArray);
10001 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
10002 : }
10003 : else
10004 : {
10005 1 : item.poFWHMArray = std::move(abstractArray);
10006 1 : item.dfFWHMToMicrometer = dfConvToUM;
10007 : }
10008 : }
10009 : else
10010 : {
10011 1 : CPLError(CE_Warning, CPLE_AppDefined,
10012 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
10013 2 : oJsonItem.GetName().c_str());
10014 : }
10015 : }
10016 : }
10017 :
10018 : auto poDS = std::make_unique<GDALDatasetFromArray>(
10019 848 : array, iXDim, iYDim, CPLStringList(papszOptions));
10020 :
10021 424 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
10022 :
10023 424 : poDS->nRasterYSize =
10024 424 : nDimCount < 2 ? 1
10025 408 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
10026 408 : dims[iYDim]->GetSize()));
10027 848 : poDS->nRasterXSize = static_cast<int>(
10028 424 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
10029 :
10030 848 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
10031 848 : std::vector<GUInt64> anStackIters(nDimCount);
10032 848 : std::vector<size_t> anMapNewToOld(nNewDimCount);
10033 1306 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
10034 : {
10035 882 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
10036 : {
10037 50 : anMapNewToOld[j] = i;
10038 50 : j++;
10039 : }
10040 : }
10041 :
10042 424 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
10043 :
10044 848 : const auto attrs(array->GetAttributes());
10045 594 : for (const auto &attr : attrs)
10046 : {
10047 170 : if (attr->GetName() == "spatial:registration")
10048 : {
10049 : // From https://github.com/zarr-conventions/spatial
10050 6 : const char *pszValue = attr->ReadAsString();
10051 6 : if (pszValue && strcmp(pszValue, "pixel") == 0)
10052 5 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10053 : GDALMD_AOP_AREA);
10054 1 : else if (pszValue && strcmp(pszValue, "node") == 0)
10055 1 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10056 : GDALMD_AOP_POINT);
10057 0 : else if (pszValue)
10058 0 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), pszValue);
10059 : }
10060 164 : else if (attr->GetName() == "gdal:geotransform")
10061 : {
10062 : // From Zarr driver
10063 4 : const auto doubleArray = attr->ReadAsDoubleArray();
10064 2 : if (doubleArray.size() == 6)
10065 : {
10066 2 : poDS->m_bHasGT = true;
10067 2 : poDS->m_gt = GDALGeoTransform(doubleArray.data());
10068 : }
10069 : }
10070 162 : else if (attr->GetName() != "COLOR_INTERPRETATION")
10071 : {
10072 300 : auto stringArray = attr->ReadAsStringArray();
10073 300 : std::string val;
10074 150 : if (stringArray.size() > 1)
10075 : {
10076 59 : val += '{';
10077 : }
10078 585 : for (int i = 0; i < stringArray.size(); ++i)
10079 : {
10080 435 : if (i > 0)
10081 285 : val += ',';
10082 435 : val += stringArray[i];
10083 : }
10084 150 : if (stringArray.size() > 1)
10085 : {
10086 59 : val += '}';
10087 : }
10088 150 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
10089 : }
10090 : }
10091 :
10092 424 : const char *pszDelay = CSLFetchNameValueDef(
10093 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
10094 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
10095 : const double dfDelay =
10096 424 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
10097 424 : const auto nStartTime = time(nullptr);
10098 424 : bool bHasWarned = false;
10099 : // Instantiate bands by iterating over non-XY variables
10100 424 : size_t iDim = 0;
10101 424 : int nCurBand = 1;
10102 552 : lbl_next_depth:
10103 552 : if (iDim < nNewDimCount)
10104 : {
10105 52 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
10106 52 : anOtherDimCoord[iDim] = 0;
10107 : while (true)
10108 : {
10109 128 : ++iDim;
10110 128 : goto lbl_next_depth;
10111 128 : lbl_return_to_caller:
10112 128 : --iDim;
10113 128 : --anStackIters[iDim];
10114 128 : if (anStackIters[iDim] == 0)
10115 52 : break;
10116 76 : ++anOtherDimCoord[iDim];
10117 : }
10118 : }
10119 : else
10120 : {
10121 1000 : poDS->SetBand(nCurBand,
10122 : new GDALRasterBandFromArray(
10123 500 : poDS.get(), anOtherDimCoord,
10124 : aoBandParameterMetadataItems, aoBandImageryMetadata,
10125 500 : dfDelay, nStartTime, bHasWarned));
10126 500 : ++nCurBand;
10127 : }
10128 552 : if (iDim > 0)
10129 128 : goto lbl_return_to_caller;
10130 :
10131 809 : if (!array->GetFilename().empty() &&
10132 385 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "LOAD_PAM", "YES")))
10133 : {
10134 379 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10135 : std::string osDerivedDatasetName(
10136 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10137 758 : int(iYDim), array->GetFullName().c_str()));
10138 379 : if (!array->GetContext().empty())
10139 : {
10140 2 : osDerivedDatasetName += " with context ";
10141 2 : osDerivedDatasetName += array->GetContext();
10142 : }
10143 379 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10144 379 : poDS->TryLoadXML();
10145 :
10146 2 : for (const auto &[pszKey, pszValue] :
10147 : cpl::IterateNameValue(static_cast<CSLConstList>(
10148 381 : poDS->GDALPamDataset::GetMetadata())))
10149 : {
10150 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10151 : }
10152 : }
10153 :
10154 424 : return poDS;
10155 : }
10156 :
10157 : /************************************************************************/
10158 : /* AsClassicDataset() */
10159 : /************************************************************************/
10160 :
10161 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10162 : *
10163 : * In the case of > 2D arrays, additional dimensions will be represented as
10164 : * raster bands.
10165 : *
10166 : * The "reverse" method is GDALRasterBand::AsMDArray().
10167 : *
10168 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10169 : *
10170 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10171 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10172 : * Ignored if the dimension count is 1.
10173 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10174 : * and BAND_IMAGERY_METADATA option.
10175 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10176 : * nullptr. Current supported options are:
10177 : * <ul>
10178 : * <li>BAND_METADATA: JSON serialized array defining which
10179 : * arrays of the poRootGroup, indexed by non-X and Y
10180 : * dimensions, should be mapped as band metadata items.
10181 : * Each array item should be an object with the
10182 : * following members:
10183 : * - "array": full name of a band parameter array.
10184 : * Such array must be a one
10185 : * dimensional array, and its dimension must be one of
10186 : * the dimensions of the array on which the method is
10187 : * called (excluding the X and Y dimensions).
10188 : * - "attribute": name relative to *this array or full
10189 : * name of a single dimension numeric array whose size
10190 : * must be one of the dimensions of *this array
10191 : * (excluding the X and Y dimensions).
10192 : * "array" and "attribute" are mutually exclusive.
10193 : * - "item_name": band metadata item name
10194 : * - "item_value": (optional) String, where "%[x][.y]f",
10195 : * "%[x][.y]g" or "%s" printf-like formatting can be
10196 : * used to format the corresponding value of the
10197 : * parameter array. The percentage character should be
10198 : * repeated: "%%"
10199 : * "${attribute_name}" can also be used to include the
10200 : * value of an attribute for "array" when set and if
10201 : * not starting with '/'. Otherwise if starting with
10202 : * '/', it is the full path to the attribute.
10203 : *
10204 : * If "item_value" is not provided, a default formatting
10205 : * of the value will be applied.
10206 : *
10207 : * Example:
10208 : * [
10209 : * {
10210 : * "array": "/sensor_band_parameters/wavelengths",
10211 : * "item_name": "WAVELENGTH",
10212 : * "item_value": "%.1f ${units}"
10213 : * },
10214 : * {
10215 : * "array": "/sensor_band_parameters/fwhm",
10216 : * "item_name": "FWHM"
10217 : * },
10218 : * {
10219 : * "array": "/sensor_band_parameters/fwhm",
10220 : * "item_name": "FWHM_UNIT",
10221 : * "item_value": "${units}"
10222 : * }
10223 : * ]
10224 : *
10225 : * Example for Planet Labs Tanager radiance products:
10226 : * [
10227 : * {
10228 : * "attribute": "center_wavelengths",
10229 : * "item_name": "WAVELENGTH",
10230 : * "item_value": "%.1f ${center_wavelengths_units}"
10231 : * }
10232 : * ]
10233 : *
10234 : * </li>
10235 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10236 : * JSON serialized object defining which arrays of the
10237 : * poRootGroup, indexed by non-X and Y dimensions,
10238 : * should be mapped as band metadata items in the
10239 : * band IMAGERY domain.
10240 : * The object currently accepts 2 members:
10241 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10242 : * micrometers.
10243 : * - "FWHM_UM": Full-width half-maximum
10244 : * in micrometers.
10245 : * The value of each member should be an object with the
10246 : * following members:
10247 : * - "array": full name of a band parameter array.
10248 : * Such array must be a one dimensional array, and its
10249 : * dimension must be one of the dimensions of the
10250 : * array on which the method is called
10251 : * (excluding the X and Y dimensions).
10252 : * - "attribute": name relative to *this array or full
10253 : * name of a single dimension numeric array whose size
10254 : * must be one of the dimensions of *this array
10255 : * (excluding the X and Y dimensions).
10256 : * "array" and "attribute" are mutually exclusive,
10257 : * and one of them is required.
10258 : * - "unit": (optional) unit of the values pointed in
10259 : * the above array.
10260 : * Can be a literal string or a string of the form
10261 : * "${attribute_name}" to point to an attribute for
10262 : * "array" when set and if no starting
10263 : * with '/'. Otherwise if starting with '/', it is
10264 : * the full path to the attribute.
10265 : * Accepted values are "um", "micrometer"
10266 : * (with UK vs US spelling, singular or plural), "nm",
10267 : * "nanometer" (with UK vs US spelling, singular or
10268 : * plural)
10269 : * If not provided, micrometer is assumed.
10270 : *
10271 : * Example for EMIT datasets:
10272 : * {
10273 : * "CENTRAL_WAVELENGTH_UM": {
10274 : * "array": "/sensor_band_parameters/wavelengths",
10275 : * "unit": "${units}"
10276 : * },
10277 : * "FWHM_UM": {
10278 : * "array": "/sensor_band_parameters/fwhm",
10279 : * "unit": "${units}"
10280 : * }
10281 : * }
10282 : *
10283 : * Example for Planet Labs Tanager radiance products:
10284 : * {
10285 : * "CENTRAL_WAVELENGTH_UM": {
10286 : * "attribute": "center_wavelengths",
10287 : * "unit": "${center_wavelengths_units}"
10288 : * },
10289 : * "FWHM_UM": {
10290 : * "attribute": "fwhm",
10291 : * "unit": "${fwhm_units}"
10292 : * }
10293 : * }
10294 : *
10295 : * </li>
10296 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10297 : * seconds allowed to set the DIM_{dimname}_VALUE band
10298 : * metadata items from the indexing variable of the
10299 : * dimensions.
10300 : * Default value is 5. 'unlimited' can be used to mean
10301 : * unlimited delay. Can also be defined globally with
10302 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10303 : * option.</li>
10304 : * </ul>
10305 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10306 : */
10307 : GDALDataset *
10308 469 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10309 : const std::shared_ptr<GDALGroup> &poRootGroup,
10310 : CSLConstList papszOptions) const
10311 : {
10312 938 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10313 469 : if (!self)
10314 : {
10315 0 : CPLError(CE_Failure, CPLE_AppDefined,
10316 : "Driver implementation issue: m_pSelf not set !");
10317 0 : return nullptr;
10318 : }
10319 938 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10320 : papszOptions)
10321 469 : .release();
10322 : }
10323 :
10324 : /************************************************************************/
10325 : /* GetStatistics() */
10326 : /************************************************************************/
10327 :
10328 : /**
10329 : * \brief Fetch statistics.
10330 : *
10331 : * Returns the minimum, maximum, mean and standard deviation of all
10332 : * pixel values in this array.
10333 : *
10334 : * If bForce is FALSE results will only be returned if it can be done
10335 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10336 : * results cannot be returned efficiently, the method will return CE_Warning
10337 : * but no warning will have been issued. This is a non-standard use of
10338 : * the CE_Warning return value to indicate "nothing done".
10339 : *
10340 : * When cached statistics are not available, and bForce is TRUE,
10341 : * ComputeStatistics() is called.
10342 : *
10343 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10344 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10345 : * after the first request.
10346 : *
10347 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10348 : *
10349 : * This method is the same as the C function GDALMDArrayGetStatistics().
10350 : *
10351 : * @param bApproxOK Currently ignored. In the future, should be set to true
10352 : * if statistics on the whole array are wished, or to false if a subset of it
10353 : * may be used.
10354 : *
10355 : * @param bForce If false statistics will only be returned if it can
10356 : * be done without rescanning the image.
10357 : *
10358 : * @param pdfMin Location into which to load image minimum (may be NULL).
10359 : *
10360 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10361 : *
10362 : * @param pdfMean Location into which to load image mean (may be NULL).
10363 : *
10364 : * @param pdfStdDev Location into which to load image standard deviation
10365 : * (may be NULL).
10366 : *
10367 : * @param pnValidCount Number of samples whose value is different from the
10368 : * nodata value. (may be NULL)
10369 : *
10370 : * @param pfnProgress a function to call to report progress, or NULL.
10371 : *
10372 : * @param pProgressData application data to pass to the progress function.
10373 : *
10374 : * @return CE_None on success, CE_Warning if no values returned,
10375 : * CE_Failure if an error occurs.
10376 : *
10377 : * @since GDAL 3.2
10378 : */
10379 :
10380 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10381 : double *pdfMax, double *pdfMean,
10382 : double *pdfStdDev, GUInt64 *pnValidCount,
10383 : GDALProgressFunc pfnProgress,
10384 : void *pProgressData)
10385 : {
10386 10 : if (!bForce)
10387 1 : return CE_Warning;
10388 :
10389 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10390 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10391 9 : ? CE_None
10392 9 : : CE_Failure;
10393 : }
10394 :
10395 : /************************************************************************/
10396 : /* ComputeStatistics() */
10397 : /************************************************************************/
10398 :
10399 : /**
10400 : * \brief Compute statistics.
10401 : *
10402 : * Returns the minimum, maximum, mean and standard deviation of all
10403 : * pixel values in this array.
10404 : *
10405 : * Pixels taken into account in statistics are those whose mask value
10406 : * (as determined by GetMask()) is non-zero.
10407 : *
10408 : * Once computed, the statistics will generally be "set" back on the
10409 : * owing dataset.
10410 : *
10411 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10412 : *
10413 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10414 : * and GDALMDArrayComputeStatisticsEx().
10415 : *
10416 : * @param bApproxOK Currently ignored. In the future, should be set to true
10417 : * if statistics on the whole array are wished, or to false if a subset of it
10418 : * may be used.
10419 : *
10420 : * @param pdfMin Location into which to load image minimum (may be NULL).
10421 : *
10422 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10423 : *
10424 : * @param pdfMean Location into which to load image mean (may be NULL).
10425 : *
10426 : * @param pdfStdDev Location into which to load image standard deviation
10427 : * (may be NULL).
10428 : *
10429 : * @param pnValidCount Number of samples whose value is different from the
10430 : * nodata value. (may be NULL)
10431 : *
10432 : * @param pfnProgress a function to call to report progress, or NULL.
10433 : *
10434 : * @param pProgressData application data to pass to the progress function.
10435 : *
10436 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10437 : * Options are driver specific. For now the netCDF and Zarr
10438 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10439 : * to add or update the actual_range attribute with the
10440 : * computed min/max, only if done on the full array, in non
10441 : * approximate mode, and the dataset is opened in update
10442 : * mode.
10443 : *
10444 : * @return true on success
10445 : *
10446 : * @since GDAL 3.2
10447 : */
10448 :
10449 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10450 : double *pdfMax, double *pdfMean,
10451 : double *pdfStdDev, GUInt64 *pnValidCount,
10452 : GDALProgressFunc pfnProgress,
10453 : void *pProgressData,
10454 : CSLConstList papszOptions)
10455 : {
10456 : struct StatsPerChunkType
10457 : {
10458 : const GDALMDArray *array = nullptr;
10459 : std::shared_ptr<GDALMDArray> poMask{};
10460 : double dfMin = cpl::NumericLimits<double>::max();
10461 : double dfMax = -cpl::NumericLimits<double>::max();
10462 : double dfMean = 0.0;
10463 : double dfM2 = 0.0;
10464 : GUInt64 nValidCount = 0;
10465 : std::vector<GByte> abyData{};
10466 : std::vector<double> adfData{};
10467 : std::vector<GByte> abyMaskData{};
10468 : GDALProgressFunc pfnProgress = nullptr;
10469 : void *pProgressData = nullptr;
10470 : };
10471 :
10472 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10473 : const GUInt64 *chunkArrayStartIdx,
10474 : const size_t *chunkCount, GUInt64 iCurChunk,
10475 : GUInt64 nChunkCount, void *pUserData)
10476 : {
10477 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10478 13 : const GDALMDArray *array = data->array;
10479 13 : const GDALMDArray *poMask = data->poMask.get();
10480 13 : const size_t nDims = array->GetDimensionCount();
10481 13 : size_t nVals = 1;
10482 34 : for (size_t i = 0; i < nDims; i++)
10483 21 : nVals *= chunkCount[i];
10484 :
10485 : // Get mask
10486 13 : data->abyMaskData.resize(nVals);
10487 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10488 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10489 : {
10490 0 : return false;
10491 : }
10492 :
10493 : // Get data
10494 13 : const auto &oType = array->GetDataType();
10495 13 : if (oType.GetNumericDataType() == GDT_Float64)
10496 : {
10497 6 : data->adfData.resize(nVals);
10498 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10499 6 : oType, &data->adfData[0]))
10500 : {
10501 0 : return false;
10502 : }
10503 : }
10504 : else
10505 : {
10506 7 : data->abyData.resize(nVals * oType.GetSize());
10507 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10508 7 : oType, &data->abyData[0]))
10509 : {
10510 0 : return false;
10511 : }
10512 7 : data->adfData.resize(nVals);
10513 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10514 7 : static_cast<int>(oType.GetSize()),
10515 7 : &data->adfData[0], GDT_Float64,
10516 : static_cast<int>(sizeof(double)),
10517 : static_cast<GPtrDiff_t>(nVals));
10518 : }
10519 912 : for (size_t i = 0; i < nVals; i++)
10520 : {
10521 899 : if (data->abyMaskData[i])
10522 : {
10523 894 : const double dfValue = data->adfData[i];
10524 894 : data->dfMin = std::min(data->dfMin, dfValue);
10525 894 : data->dfMax = std::max(data->dfMax, dfValue);
10526 894 : data->nValidCount++;
10527 894 : const double dfDelta = dfValue - data->dfMean;
10528 894 : data->dfMean += dfDelta / data->nValidCount;
10529 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10530 : }
10531 : }
10532 13 : if (data->pfnProgress &&
10533 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10534 : "", data->pProgressData))
10535 : {
10536 0 : return false;
10537 : }
10538 13 : return true;
10539 : };
10540 :
10541 13 : const auto &oType = GetDataType();
10542 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10543 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10544 : {
10545 0 : CPLError(
10546 : CE_Failure, CPLE_NotSupported,
10547 : "Statistics can only be computed on non-complex numeric data type");
10548 0 : return false;
10549 : }
10550 :
10551 13 : const size_t nDims = GetDimensionCount();
10552 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10553 26 : std::vector<GUInt64> count(nDims);
10554 13 : const auto &poDims = GetDimensions();
10555 34 : for (size_t i = 0; i < nDims; i++)
10556 : {
10557 21 : count[i] = poDims[i]->GetSize();
10558 : }
10559 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10560 : const size_t nMaxChunkSize =
10561 : pszSwathSize
10562 13 : ? static_cast<size_t>(
10563 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10564 0 : CPLAtoGIntBig(pszSwathSize)))
10565 : : static_cast<size_t>(
10566 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10567 13 : GDALGetCacheMax64() / 4));
10568 26 : StatsPerChunkType sData;
10569 13 : sData.array = this;
10570 13 : sData.poMask = GetMask(nullptr);
10571 13 : if (sData.poMask == nullptr)
10572 : {
10573 0 : return false;
10574 : }
10575 13 : sData.pfnProgress = pfnProgress;
10576 13 : sData.pProgressData = pProgressData;
10577 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10578 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10579 13 : PerChunkFunc, &sData))
10580 : {
10581 0 : return false;
10582 : }
10583 :
10584 13 : if (pdfMin)
10585 13 : *pdfMin = sData.dfMin;
10586 :
10587 13 : if (pdfMax)
10588 13 : *pdfMax = sData.dfMax;
10589 :
10590 13 : if (pdfMean)
10591 11 : *pdfMean = sData.dfMean;
10592 :
10593 : const double dfStdDev =
10594 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10595 13 : if (pdfStdDev)
10596 11 : *pdfStdDev = dfStdDev;
10597 :
10598 13 : if (pnValidCount)
10599 11 : *pnValidCount = sData.nValidCount;
10600 :
10601 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10602 13 : sData.nValidCount, papszOptions);
10603 :
10604 13 : return true;
10605 : }
10606 :
10607 : /************************************************************************/
10608 : /* SetStatistics() */
10609 : /************************************************************************/
10610 : //! @cond Doxygen_Suppress
10611 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10612 : double /* dfMax */, double /* dfMean */,
10613 : double /* dfStdDev */,
10614 : GUInt64 /* nValidCount */,
10615 : CSLConstList /* papszOptions */)
10616 : {
10617 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10618 5 : return false;
10619 : }
10620 :
10621 : //! @endcond
10622 :
10623 : /************************************************************************/
10624 : /* ClearStatistics() */
10625 : /************************************************************************/
10626 :
10627 : /**
10628 : * \brief Clear statistics.
10629 : *
10630 : * @since GDAL 3.4
10631 : */
10632 0 : void GDALMDArray::ClearStatistics()
10633 : {
10634 0 : }
10635 :
10636 : /************************************************************************/
10637 : /* GetCoordinateVariables() */
10638 : /************************************************************************/
10639 :
10640 : /**
10641 : * \brief Return coordinate variables.
10642 : *
10643 : * Coordinate variables are an alternate way of indexing an array that can
10644 : * be sometimes used. For example, an array collected through remote sensing
10645 : * might be indexed by (scanline, pixel). But there can be
10646 : * a longitude and latitude arrays alongside that are also both indexed by
10647 : * (scanline, pixel), and are referenced from operational arrays for
10648 : * reprojection purposes.
10649 : *
10650 : * For netCDF, this will return the arrays referenced by the "coordinates"
10651 : * attribute.
10652 : *
10653 : * This method is the same as the C function
10654 : * GDALMDArrayGetCoordinateVariables().
10655 : *
10656 : * @return a vector of arrays
10657 : *
10658 : * @since GDAL 3.4
10659 : */
10660 :
10661 : std::vector<std::shared_ptr<GDALMDArray>>
10662 13 : GDALMDArray::GetCoordinateVariables() const
10663 : {
10664 13 : return {};
10665 : }
10666 :
10667 : /************************************************************************/
10668 : /* ~GDALExtendedDataType() */
10669 : /************************************************************************/
10670 :
10671 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10672 :
10673 : /************************************************************************/
10674 : /* GDALExtendedDataType() */
10675 : /************************************************************************/
10676 :
10677 44306 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10678 44306 : GDALExtendedDataTypeSubType eSubType)
10679 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10680 44306 : m_nMaxStringLength(nMaxStringLength)
10681 : {
10682 44306 : }
10683 :
10684 : /************************************************************************/
10685 : /* GDALExtendedDataType() */
10686 : /************************************************************************/
10687 :
10688 334371 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10689 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10690 334371 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10691 : {
10692 334371 : }
10693 :
10694 : /************************************************************************/
10695 : /* GDALExtendedDataType() */
10696 : /************************************************************************/
10697 :
10698 63 : GDALExtendedDataType::GDALExtendedDataType(
10699 : const std::string &osName, GDALDataType eBaseType,
10700 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10701 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10702 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10703 : {
10704 63 : }
10705 :
10706 : /************************************************************************/
10707 : /* GDALExtendedDataType() */
10708 : /************************************************************************/
10709 :
10710 1765 : GDALExtendedDataType::GDALExtendedDataType(
10711 : const std::string &osName, size_t nTotalSize,
10712 1765 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10713 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10714 1765 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10715 : {
10716 1765 : }
10717 :
10718 : /************************************************************************/
10719 : /* GDALExtendedDataType() */
10720 : /************************************************************************/
10721 :
10722 : /** Move constructor. */
10723 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10724 :
10725 : /************************************************************************/
10726 : /* GDALExtendedDataType() */
10727 : /************************************************************************/
10728 :
10729 : /** Copy constructor. */
10730 29284 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10731 58568 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10732 29284 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10733 29284 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10734 29284 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10735 : {
10736 29284 : if (m_eClass == GEDTC_COMPOUND)
10737 : {
10738 481 : for (const auto &elt : other.m_aoComponents)
10739 : {
10740 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10741 : }
10742 : }
10743 29284 : }
10744 :
10745 : /************************************************************************/
10746 : /* operator= () */
10747 : /************************************************************************/
10748 :
10749 : /** Copy assignment. */
10750 : GDALExtendedDataType &
10751 8168 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10752 : {
10753 8168 : if (this != &other)
10754 : {
10755 8168 : m_osName = other.m_osName;
10756 8168 : m_eClass = other.m_eClass;
10757 8168 : m_eSubType = other.m_eSubType;
10758 8168 : m_eNumericDT = other.m_eNumericDT;
10759 8168 : m_nSize = other.m_nSize;
10760 8168 : m_nMaxStringLength = other.m_nMaxStringLength;
10761 8168 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10762 8168 : m_aoComponents.clear();
10763 8168 : if (m_eClass == GEDTC_COMPOUND)
10764 : {
10765 0 : for (const auto &elt : other.m_aoComponents)
10766 : {
10767 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10768 : }
10769 : }
10770 : }
10771 8168 : return *this;
10772 : }
10773 :
10774 : /************************************************************************/
10775 : /* operator= () */
10776 : /************************************************************************/
10777 :
10778 : /** Move assignment. */
10779 : GDALExtendedDataType &
10780 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10781 :
10782 : /************************************************************************/
10783 : /* Create() */
10784 : /************************************************************************/
10785 :
10786 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10787 : *
10788 : * This is the same as the C function GDALExtendedDataTypeCreate()
10789 : *
10790 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10791 : * GDT_TypeCount
10792 : */
10793 334364 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10794 : {
10795 334364 : return GDALExtendedDataType(eType);
10796 : }
10797 :
10798 : /************************************************************************/
10799 : /* Create() */
10800 : /************************************************************************/
10801 :
10802 : /** Return a new GDALExtendedDataType from a raster attribute table.
10803 : *
10804 : * @param osName Type name
10805 : * @param eBaseType Base integer data type.
10806 : * @param poRAT Raster attribute table. Must not be NULL.
10807 : * @since 3.12
10808 : */
10809 : GDALExtendedDataType
10810 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10811 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10812 : {
10813 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10814 : }
10815 :
10816 : /************************************************************************/
10817 : /* Create() */
10818 : /************************************************************************/
10819 :
10820 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10821 : *
10822 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10823 : *
10824 : * @param osName Type name.
10825 : * @param nTotalSize Total size of the type in bytes.
10826 : * Should be large enough to store all components.
10827 : * @param components Components of the compound type.
10828 : */
10829 1772 : GDALExtendedDataType GDALExtendedDataType::Create(
10830 : const std::string &osName, size_t nTotalSize,
10831 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10832 : {
10833 1772 : size_t nLastOffset = 0;
10834 : // Some arbitrary threshold to avoid potential integer overflows
10835 1772 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10836 : {
10837 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10838 2 : return GDALExtendedDataType(GDT_Unknown);
10839 : }
10840 7512 : for (const auto &comp : components)
10841 : {
10842 : // Check alignment too ?
10843 5743 : if (comp->GetOffset() < nLastOffset)
10844 : {
10845 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10846 1 : return GDALExtendedDataType(GDT_Unknown);
10847 : }
10848 5742 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10849 : }
10850 1769 : if (nTotalSize < nLastOffset)
10851 : {
10852 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10853 1 : return GDALExtendedDataType(GDT_Unknown);
10854 : }
10855 1768 : if (nTotalSize == 0 || components.empty())
10856 : {
10857 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10858 3 : return GDALExtendedDataType(GDT_Unknown);
10859 : }
10860 1765 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10861 : }
10862 :
10863 : /************************************************************************/
10864 : /* Create() */
10865 : /************************************************************************/
10866 :
10867 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10868 : *
10869 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10870 : *
10871 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10872 : * unknown/unlimited
10873 : * @param eSubType Subtype.
10874 : */
10875 : GDALExtendedDataType
10876 44306 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10877 : GDALExtendedDataTypeSubType eSubType)
10878 : {
10879 44306 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10880 : }
10881 :
10882 : /************************************************************************/
10883 : /* operator==() */
10884 : /************************************************************************/
10885 :
10886 : /** Equality operator.
10887 : *
10888 : * This is the same as the C function GDALExtendedDataTypeEquals().
10889 : */
10890 5019 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10891 : {
10892 4992 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10893 10011 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10894 : {
10895 768 : return false;
10896 : }
10897 4251 : if (m_eClass == GEDTC_NUMERIC)
10898 : {
10899 1115 : return m_eNumericDT == other.m_eNumericDT;
10900 : }
10901 3136 : if (m_eClass == GEDTC_STRING)
10902 : {
10903 2746 : return true;
10904 : }
10905 390 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10906 390 : if (m_aoComponents.size() != other.m_aoComponents.size())
10907 : {
10908 2 : return false;
10909 : }
10910 2324 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10911 : {
10912 1936 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10913 : {
10914 0 : return false;
10915 : }
10916 : }
10917 388 : return true;
10918 : }
10919 :
10920 : /************************************************************************/
10921 : /* CanConvertTo() */
10922 : /************************************************************************/
10923 :
10924 : /** Return whether this data type can be converted to the other one.
10925 : *
10926 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10927 : *
10928 : * @param other Target data type for the conversion being considered.
10929 : */
10930 21904 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10931 : {
10932 21904 : if (m_eClass == GEDTC_NUMERIC)
10933 : {
10934 15892 : if (m_eNumericDT == GDT_Unknown)
10935 0 : return false;
10936 15892 : if (other.m_eClass == GEDTC_NUMERIC &&
10937 14456 : other.m_eNumericDT == GDT_Unknown)
10938 0 : return false;
10939 17328 : return other.m_eClass == GEDTC_NUMERIC ||
10940 17328 : other.m_eClass == GEDTC_STRING;
10941 : }
10942 6012 : if (m_eClass == GEDTC_STRING)
10943 : {
10944 5418 : return other.m_eClass == m_eClass;
10945 : }
10946 594 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10947 594 : if (other.m_eClass != GEDTC_COMPOUND)
10948 0 : return false;
10949 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10950 1188 : srcComponents;
10951 2822 : for (const auto &srcComp : m_aoComponents)
10952 : {
10953 2228 : srcComponents[srcComp->GetName()] = &srcComp;
10954 : }
10955 1345 : for (const auto &dstComp : other.m_aoComponents)
10956 : {
10957 752 : auto oIter = srcComponents.find(dstComp->GetName());
10958 752 : if (oIter == srcComponents.end())
10959 1 : return false;
10960 751 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10961 0 : return false;
10962 : }
10963 593 : return true;
10964 : }
10965 :
10966 : /************************************************************************/
10967 : /* NeedsFreeDynamicMemory() */
10968 : /************************************************************************/
10969 :
10970 : /** Return whether the data type holds dynamically allocated memory, that
10971 : * needs to be freed with FreeDynamicMemory().
10972 : *
10973 : */
10974 5141 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10975 : {
10976 5141 : switch (m_eClass)
10977 : {
10978 1683 : case GEDTC_STRING:
10979 1683 : return true;
10980 :
10981 3198 : case GEDTC_NUMERIC:
10982 3198 : return false;
10983 :
10984 260 : case GEDTC_COMPOUND:
10985 : {
10986 388 : for (const auto &comp : m_aoComponents)
10987 : {
10988 366 : if (comp->GetType().NeedsFreeDynamicMemory())
10989 238 : return true;
10990 : }
10991 : }
10992 : }
10993 22 : return false;
10994 : }
10995 :
10996 : /************************************************************************/
10997 : /* FreeDynamicMemory() */
10998 : /************************************************************************/
10999 :
11000 : /** Release the dynamic memory (strings typically) from a raw value.
11001 : *
11002 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
11003 : *
11004 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
11005 : */
11006 7469 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
11007 : {
11008 7469 : switch (m_eClass)
11009 : {
11010 4894 : case GEDTC_STRING:
11011 : {
11012 : char *pszStr;
11013 4894 : memcpy(&pszStr, pBuffer, sizeof(char *));
11014 4894 : if (pszStr)
11015 : {
11016 3729 : VSIFree(pszStr);
11017 : }
11018 4894 : break;
11019 : }
11020 :
11021 2246 : case GEDTC_NUMERIC:
11022 : {
11023 2246 : break;
11024 : }
11025 :
11026 329 : case GEDTC_COMPOUND:
11027 : {
11028 329 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
11029 2069 : for (const auto &comp : m_aoComponents)
11030 : {
11031 3480 : comp->GetType().FreeDynamicMemory(pabyBuffer +
11032 1740 : comp->GetOffset());
11033 : }
11034 329 : break;
11035 : }
11036 : }
11037 7469 : }
11038 :
11039 : /************************************************************************/
11040 : /* ~GDALEDTComponent() */
11041 : /************************************************************************/
11042 :
11043 : GDALEDTComponent::~GDALEDTComponent() = default;
11044 :
11045 : /************************************************************************/
11046 : /* GDALEDTComponent() */
11047 : /************************************************************************/
11048 :
11049 : /** constructor of a GDALEDTComponent
11050 : *
11051 : * This is the same as the C function GDALEDTComponendCreate()
11052 : *
11053 : * @param name Component name
11054 : * @param offset Offset in byte of the component in the compound data type.
11055 : * In case of nesting of compound data type, this should be
11056 : * the offset to the immediate belonging data type, not to the
11057 : * higher level one.
11058 : * @param type Component data type.
11059 : */
11060 5734 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
11061 5734 : const GDALExtendedDataType &type)
11062 5734 : : m_osName(name), m_nOffset(offset), m_oType(type)
11063 : {
11064 5734 : }
11065 :
11066 : /************************************************************************/
11067 : /* GDALEDTComponent() */
11068 : /************************************************************************/
11069 :
11070 : /** Copy constructor. */
11071 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
11072 :
11073 : /************************************************************************/
11074 : /* operator==() */
11075 : /************************************************************************/
11076 :
11077 : /** Equality operator.
11078 : */
11079 1936 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
11080 : {
11081 3872 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
11082 3872 : m_oType == other.m_oType;
11083 : }
11084 :
11085 : /************************************************************************/
11086 : /* ~GDALDimension() */
11087 : /************************************************************************/
11088 :
11089 : GDALDimension::~GDALDimension() = default;
11090 :
11091 : /************************************************************************/
11092 : /* GDALDimension() */
11093 : /************************************************************************/
11094 :
11095 : //! @cond Doxygen_Suppress
11096 : /** Constructor.
11097 : *
11098 : * @param osParentName Parent name
11099 : * @param osName name
11100 : * @param osType type. See GetType().
11101 : * @param osDirection direction. See GetDirection().
11102 : * @param nSize size.
11103 : */
11104 12300 : GDALDimension::GDALDimension(const std::string &osParentName,
11105 : const std::string &osName,
11106 : const std::string &osType,
11107 12300 : const std::string &osDirection, GUInt64 nSize)
11108 : : m_osName(osName),
11109 : m_osFullName(
11110 12300 : !osParentName.empty()
11111 17090 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
11112 : : osName),
11113 41690 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
11114 : {
11115 12300 : }
11116 :
11117 : //! @endcond
11118 :
11119 : /************************************************************************/
11120 : /* GetIndexingVariable() */
11121 : /************************************************************************/
11122 :
11123 : /** Return the variable that is used to index the dimension (if there is one).
11124 : *
11125 : * This is the array, typically one-dimensional, describing the values taken
11126 : * by the dimension.
11127 : */
11128 67 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11129 : {
11130 67 : return nullptr;
11131 : }
11132 :
11133 : /************************************************************************/
11134 : /* SetIndexingVariable() */
11135 : /************************************************************************/
11136 :
11137 : /** Set the variable that is used to index the dimension.
11138 : *
11139 : * This is the array, typically one-dimensional, describing the values taken
11140 : * by the dimension.
11141 : *
11142 : * Optionally implemented by drivers.
11143 : *
11144 : * Drivers known to implement it: MEM.
11145 : *
11146 : * @param poArray Variable to use to index the dimension.
11147 : * @return true in case of success.
11148 : */
11149 11 : bool GDALDimension::SetIndexingVariable(
11150 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11151 : {
11152 11 : CPLError(CE_Failure, CPLE_NotSupported,
11153 : "SetIndexingVariable() not implemented");
11154 11 : return false;
11155 : }
11156 :
11157 : /************************************************************************/
11158 : /* Rename() */
11159 : /************************************************************************/
11160 :
11161 : /** Rename the dimension.
11162 : *
11163 : * This is not implemented by all drivers.
11164 : *
11165 : * Drivers known to implement it: MEM, netCDF, ZARR.
11166 : *
11167 : * This is the same as the C function GDALDimensionRename().
11168 : *
11169 : * @param osNewName New name.
11170 : *
11171 : * @return true in case of success
11172 : * @since GDAL 3.8
11173 : */
11174 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11175 : {
11176 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11177 0 : return false;
11178 : }
11179 :
11180 : /************************************************************************/
11181 : /* BaseRename() */
11182 : /************************************************************************/
11183 :
11184 : //! @cond Doxygen_Suppress
11185 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11186 : {
11187 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11188 8 : m_osFullName += osNewName;
11189 8 : m_osName = osNewName;
11190 8 : }
11191 :
11192 : //! @endcond
11193 :
11194 : //! @cond Doxygen_Suppress
11195 : /************************************************************************/
11196 : /* ParentRenamed() */
11197 : /************************************************************************/
11198 :
11199 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11200 : {
11201 8 : m_osFullName = osNewParentFullName;
11202 8 : m_osFullName += "/";
11203 8 : m_osFullName += m_osName;
11204 8 : }
11205 :
11206 : //! @endcond
11207 :
11208 : //! @cond Doxygen_Suppress
11209 : /************************************************************************/
11210 : /* ParentDeleted() */
11211 : /************************************************************************/
11212 :
11213 4 : void GDALDimension::ParentDeleted()
11214 : {
11215 4 : }
11216 :
11217 : //! @endcond
11218 :
11219 : /************************************************************************/
11220 : /************************************************************************/
11221 : /************************************************************************/
11222 : /* C API */
11223 : /************************************************************************/
11224 : /************************************************************************/
11225 : /************************************************************************/
11226 :
11227 : /************************************************************************/
11228 : /* GDALExtendedDataTypeCreate() */
11229 : /************************************************************************/
11230 :
11231 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11232 : *
11233 : * This is the same as the C++ method GDALExtendedDataType::Create()
11234 : *
11235 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11236 : *
11237 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11238 : * GDT_TypeCount
11239 : *
11240 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11241 : */
11242 2158 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11243 : {
11244 2158 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11245 : {
11246 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11247 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11248 0 : return nullptr;
11249 : }
11250 : return new GDALExtendedDataTypeHS(
11251 2158 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11252 : }
11253 :
11254 : /************************************************************************/
11255 : /* GDALExtendedDataTypeCreateString() */
11256 : /************************************************************************/
11257 :
11258 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11259 : *
11260 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11261 : *
11262 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11263 : *
11264 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11265 : */
11266 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11267 : {
11268 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11269 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11270 : }
11271 :
11272 : /************************************************************************/
11273 : /* GDALExtendedDataTypeCreateStringEx() */
11274 : /************************************************************************/
11275 :
11276 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11277 : *
11278 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11279 : *
11280 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11281 : *
11282 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11283 : * @since GDAL 3.4
11284 : */
11285 : GDALExtendedDataTypeH
11286 229 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11287 : GDALExtendedDataTypeSubType eSubType)
11288 : {
11289 229 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11290 229 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11291 : }
11292 :
11293 : /************************************************************************/
11294 : /* GDALExtendedDataTypeCreateCompound() */
11295 : /************************************************************************/
11296 :
11297 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11298 : *
11299 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11300 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11301 : *
11302 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11303 : *
11304 : * @param pszName Type name.
11305 : * @param nTotalSize Total size of the type in bytes.
11306 : * Should be large enough to store all components.
11307 : * @param nComponents Number of components in comps array.
11308 : * @param comps Components.
11309 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11310 : */
11311 : GDALExtendedDataTypeH
11312 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11313 : size_t nComponents,
11314 : const GDALEDTComponentH *comps)
11315 : {
11316 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11317 54 : for (size_t i = 0; i < nComponents; i++)
11318 : {
11319 : compsCpp.emplace_back(
11320 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11321 : }
11322 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11323 66 : std::move(compsCpp));
11324 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11325 6 : return nullptr;
11326 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11327 : }
11328 :
11329 : /************************************************************************/
11330 : /* GDALExtendedDataTypeRelease() */
11331 : /************************************************************************/
11332 :
11333 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11334 : *
11335 : * Note: when applied on a object coming from a driver, this does not
11336 : * destroy the object in the file, database, etc...
11337 : */
11338 8419 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11339 : {
11340 8419 : delete hEDT;
11341 8419 : }
11342 :
11343 : /************************************************************************/
11344 : /* GDALExtendedDataTypeGetName() */
11345 : /************************************************************************/
11346 :
11347 : /** Return type name.
11348 : *
11349 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11350 : */
11351 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11352 : {
11353 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11354 8 : return hEDT->m_poImpl->GetName().c_str();
11355 : }
11356 :
11357 : /************************************************************************/
11358 : /* GDALExtendedDataTypeGetClass() */
11359 : /************************************************************************/
11360 :
11361 : /** Return type class.
11362 : *
11363 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11364 : */
11365 : GDALExtendedDataTypeClass
11366 12808 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11367 : {
11368 12808 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11369 12808 : return hEDT->m_poImpl->GetClass();
11370 : }
11371 :
11372 : /************************************************************************/
11373 : /* GDALExtendedDataTypeGetNumericDataType() */
11374 : /************************************************************************/
11375 :
11376 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11377 : *
11378 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11379 : */
11380 3450 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11381 : {
11382 3450 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11383 3450 : return hEDT->m_poImpl->GetNumericDataType();
11384 : }
11385 :
11386 : /************************************************************************/
11387 : /* GDALExtendedDataTypeGetSize() */
11388 : /************************************************************************/
11389 :
11390 : /** Return data type size in bytes.
11391 : *
11392 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11393 : */
11394 3304 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11395 : {
11396 3304 : VALIDATE_POINTER1(hEDT, __func__, 0);
11397 3304 : return hEDT->m_poImpl->GetSize();
11398 : }
11399 :
11400 : /************************************************************************/
11401 : /* GDALExtendedDataTypeGetMaxStringLength() */
11402 : /************************************************************************/
11403 :
11404 : /** Return the maximum length of a string in bytes.
11405 : *
11406 : * 0 indicates unknown/unlimited string.
11407 : *
11408 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11409 : */
11410 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11411 : {
11412 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11413 3 : return hEDT->m_poImpl->GetMaxStringLength();
11414 : }
11415 :
11416 : /************************************************************************/
11417 : /* GDALExtendedDataTypeCanConvertTo() */
11418 : /************************************************************************/
11419 :
11420 : /** Return whether this data type can be converted to the other one.
11421 : *
11422 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11423 : *
11424 : * @param hSourceEDT Source data type for the conversion being considered.
11425 : * @param hTargetEDT Target data type for the conversion being considered.
11426 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11427 : */
11428 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11429 : GDALExtendedDataTypeH hTargetEDT)
11430 : {
11431 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11432 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11433 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11434 : }
11435 :
11436 : /************************************************************************/
11437 : /* GDALExtendedDataTypeEquals() */
11438 : /************************************************************************/
11439 :
11440 : /** Return whether this data type is equal to another one.
11441 : *
11442 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11443 : *
11444 : * @param hFirstEDT First data type.
11445 : * @param hSecondEDT Second data type.
11446 : * @return TRUE if they are equal. FALSE otherwise.
11447 : */
11448 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11449 : GDALExtendedDataTypeH hSecondEDT)
11450 : {
11451 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11452 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11453 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11454 : }
11455 :
11456 : /************************************************************************/
11457 : /* GDALExtendedDataTypeGetSubType() */
11458 : /************************************************************************/
11459 :
11460 : /** Return the subtype of a type.
11461 : *
11462 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11463 : *
11464 : * @param hEDT Data type.
11465 : * @return subtype.
11466 : * @since 3.4
11467 : */
11468 : GDALExtendedDataTypeSubType
11469 116 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11470 : {
11471 116 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11472 116 : return hEDT->m_poImpl->GetSubType();
11473 : }
11474 :
11475 : /************************************************************************/
11476 : /* GDALExtendedDataTypeGetRAT() */
11477 : /************************************************************************/
11478 :
11479 : /** Return associated raster attribute table, when there is one.
11480 : *
11481 : * * For the netCDF driver, the RAT will capture enumerated types, with
11482 : * a "value" column with an integer value and a "name" column with the
11483 : * associated name.
11484 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11485 : *
11486 : * @param hEDT Data type.
11487 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11488 : * @since 3.12
11489 : */
11490 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11491 : {
11492 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11493 1 : return GDALRasterAttributeTable::ToHandle(
11494 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11495 : }
11496 :
11497 : /************************************************************************/
11498 : /* GDALExtendedDataTypeGetComponents() */
11499 : /************************************************************************/
11500 :
11501 : /** Return the components of the data type (only valid when GetClass() ==
11502 : * GEDTC_COMPOUND)
11503 : *
11504 : * The returned array and its content must be freed with
11505 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11506 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11507 : * individual array members).
11508 : *
11509 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11510 : *
11511 : * @param hEDT Data type
11512 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11513 : * @return an array of *pnCount components.
11514 : */
11515 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11516 : size_t *pnCount)
11517 : {
11518 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11519 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11520 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11521 : auto ret = static_cast<GDALEDTComponentH *>(
11522 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11523 131 : for (size_t i = 0; i < components.size(); i++)
11524 : {
11525 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11526 : }
11527 44 : *pnCount = components.size();
11528 44 : return ret;
11529 : }
11530 :
11531 : /************************************************************************/
11532 : /* GDALExtendedDataTypeFreeComponents() */
11533 : /************************************************************************/
11534 :
11535 : /** Free the return of GDALExtendedDataTypeGetComponents().
11536 : *
11537 : * @param components return value of GDALExtendedDataTypeGetComponents()
11538 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11539 : */
11540 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11541 : size_t nCount)
11542 : {
11543 131 : for (size_t i = 0; i < nCount; i++)
11544 : {
11545 87 : delete components[i];
11546 : }
11547 44 : CPLFree(components);
11548 44 : }
11549 :
11550 : /************************************************************************/
11551 : /* GDALEDTComponentCreate() */
11552 : /************************************************************************/
11553 :
11554 : /** Create a new GDALEDTComponent.
11555 : *
11556 : * The returned value must be freed with GDALEDTComponentRelease().
11557 : *
11558 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11559 : */
11560 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11561 : GDALExtendedDataTypeH hType)
11562 : {
11563 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11564 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11565 : return new GDALEDTComponentHS(
11566 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11567 : }
11568 :
11569 : /************************************************************************/
11570 : /* GDALEDTComponentRelease() */
11571 : /************************************************************************/
11572 :
11573 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11574 : *
11575 : * Note: when applied on a object coming from a driver, this does not
11576 : * destroy the object in the file, database, etc...
11577 : */
11578 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11579 : {
11580 61 : delete hComp;
11581 61 : }
11582 :
11583 : /************************************************************************/
11584 : /* GDALEDTComponentGetName() */
11585 : /************************************************************************/
11586 :
11587 : /** Return the name.
11588 : *
11589 : * The returned pointer is valid until hComp is released.
11590 : *
11591 : * This is the same as the C++ method GDALEDTComponent::GetName().
11592 : */
11593 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11594 : {
11595 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11596 33 : return hComp->m_poImpl->GetName().c_str();
11597 : }
11598 :
11599 : /************************************************************************/
11600 : /* GDALEDTComponentGetOffset() */
11601 : /************************************************************************/
11602 :
11603 : /** Return the offset (in bytes) of the component in the compound data type.
11604 : *
11605 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11606 : */
11607 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11608 : {
11609 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11610 31 : return hComp->m_poImpl->GetOffset();
11611 : }
11612 :
11613 : /************************************************************************/
11614 : /* GDALEDTComponentGetType() */
11615 : /************************************************************************/
11616 :
11617 : /** Return the data type of the component.
11618 : *
11619 : * This is the same as the C++ method GDALEDTComponent::GetType().
11620 : */
11621 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11622 : {
11623 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11624 : return new GDALExtendedDataTypeHS(
11625 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11626 : }
11627 :
11628 : /************************************************************************/
11629 : /* GDALGroupRelease() */
11630 : /************************************************************************/
11631 :
11632 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11633 : *
11634 : * Note: when applied on a object coming from a driver, this does not
11635 : * destroy the object in the file, database, etc...
11636 : */
11637 2238 : void GDALGroupRelease(GDALGroupH hGroup)
11638 : {
11639 2238 : delete hGroup;
11640 2238 : }
11641 :
11642 : /************************************************************************/
11643 : /* GDALGroupGetName() */
11644 : /************************************************************************/
11645 :
11646 : /** Return the name of the group.
11647 : *
11648 : * The returned pointer is valid until hGroup is released.
11649 : *
11650 : * This is the same as the C++ method GDALGroup::GetName().
11651 : */
11652 97 : const char *GDALGroupGetName(GDALGroupH hGroup)
11653 : {
11654 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11655 97 : return hGroup->m_poImpl->GetName().c_str();
11656 : }
11657 :
11658 : /************************************************************************/
11659 : /* GDALGroupGetFullName() */
11660 : /************************************************************************/
11661 :
11662 : /** Return the full name of the group.
11663 : *
11664 : * The returned pointer is valid until hGroup is released.
11665 : *
11666 : * This is the same as the C++ method GDALGroup::GetFullName().
11667 : */
11668 48 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11669 : {
11670 48 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11671 48 : return hGroup->m_poImpl->GetFullName().c_str();
11672 : }
11673 :
11674 : /************************************************************************/
11675 : /* GDALGroupGetMDArrayNames() */
11676 : /************************************************************************/
11677 :
11678 : /** Return the list of multidimensional array names contained in this group.
11679 : *
11680 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11681 : *
11682 : * @return the array names, to be freed with CSLDestroy()
11683 : */
11684 338 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11685 : {
11686 338 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11687 676 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11688 676 : CPLStringList res;
11689 853 : for (const auto &name : names)
11690 : {
11691 515 : res.AddString(name.c_str());
11692 : }
11693 338 : return res.StealList();
11694 : }
11695 :
11696 : /************************************************************************/
11697 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11698 : /************************************************************************/
11699 :
11700 : /** Return the list of multidimensional array full names contained in this
11701 : * group and its subgroups.
11702 : *
11703 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11704 : *
11705 : * @return the array names, to be freed with CSLDestroy()
11706 : *
11707 : * @since 3.11
11708 : */
11709 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11710 : CSLConstList papszGroupOptions,
11711 : CSLConstList papszArrayOptions)
11712 : {
11713 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11714 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11715 2 : papszGroupOptions, papszArrayOptions);
11716 2 : CPLStringList res;
11717 5 : for (const auto &name : names)
11718 : {
11719 4 : res.AddString(name.c_str());
11720 : }
11721 1 : return res.StealList();
11722 : }
11723 :
11724 : /************************************************************************/
11725 : /* GDALGroupOpenMDArray() */
11726 : /************************************************************************/
11727 :
11728 : /** Open and return a multidimensional array.
11729 : *
11730 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11731 : *
11732 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11733 : */
11734 1506 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11735 : CSLConstList papszOptions)
11736 : {
11737 1506 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11738 1506 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11739 4518 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11740 4518 : papszOptions);
11741 1506 : if (!array)
11742 30 : return nullptr;
11743 1476 : return new GDALMDArrayHS(array);
11744 : }
11745 :
11746 : /************************************************************************/
11747 : /* GDALGroupOpenMDArrayFromFullname() */
11748 : /************************************************************************/
11749 :
11750 : /** Open and return a multidimensional array from its fully qualified name.
11751 : *
11752 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11753 : *
11754 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11755 : *
11756 : * @since GDAL 3.2
11757 : */
11758 18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11759 : const char *pszFullname,
11760 : CSLConstList papszOptions)
11761 : {
11762 18 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11763 18 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11764 18 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11765 54 : std::string(pszFullname), papszOptions);
11766 18 : if (!array)
11767 2 : return nullptr;
11768 16 : return new GDALMDArrayHS(array);
11769 : }
11770 :
11771 : /************************************************************************/
11772 : /* GDALGroupResolveMDArray() */
11773 : /************************************************************************/
11774 :
11775 : /** Locate an array in a group and its subgroups by name.
11776 : *
11777 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11778 : * @since GDAL 3.2
11779 : */
11780 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11781 : const char *pszStartingPoint,
11782 : CSLConstList papszOptions)
11783 : {
11784 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11785 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11786 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11787 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11788 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11789 19 : if (!array)
11790 2 : return nullptr;
11791 17 : return new GDALMDArrayHS(array);
11792 : }
11793 :
11794 : /************************************************************************/
11795 : /* GDALGroupGetGroupNames() */
11796 : /************************************************************************/
11797 :
11798 : /** Return the list of sub-groups contained in this group.
11799 : *
11800 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11801 : *
11802 : * @return the group names, to be freed with CSLDestroy()
11803 : */
11804 104 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11805 : {
11806 104 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11807 208 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11808 208 : CPLStringList res;
11809 237 : for (const auto &name : names)
11810 : {
11811 133 : res.AddString(name.c_str());
11812 : }
11813 104 : return res.StealList();
11814 : }
11815 :
11816 : /************************************************************************/
11817 : /* GDALGroupOpenGroup() */
11818 : /************************************************************************/
11819 :
11820 : /** Open and return a sub-group.
11821 : *
11822 : * This is the same as the C++ method GDALGroup::OpenGroup().
11823 : *
11824 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11825 : */
11826 199 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11827 : CSLConstList papszOptions)
11828 : {
11829 199 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11830 199 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11831 : auto subGroup =
11832 597 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11833 199 : if (!subGroup)
11834 30 : return nullptr;
11835 169 : return new GDALGroupHS(subGroup);
11836 : }
11837 :
11838 : /************************************************************************/
11839 : /* GDALGroupGetVectorLayerNames() */
11840 : /************************************************************************/
11841 :
11842 : /** Return the list of layer names contained in this group.
11843 : *
11844 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11845 : *
11846 : * @return the group names, to be freed with CSLDestroy()
11847 : * @since 3.4
11848 : */
11849 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11850 : CSLConstList papszOptions)
11851 : {
11852 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11853 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11854 16 : CPLStringList res;
11855 18 : for (const auto &name : names)
11856 : {
11857 10 : res.AddString(name.c_str());
11858 : }
11859 8 : return res.StealList();
11860 : }
11861 :
11862 : /************************************************************************/
11863 : /* GDALGroupOpenVectorLayer() */
11864 : /************************************************************************/
11865 :
11866 : /** Open and return a vector layer.
11867 : *
11868 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11869 : *
11870 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11871 : * the returned handled if only valid while the parent GDALDatasetH is kept
11872 : * opened.
11873 : *
11874 : * @return the vector layer, or nullptr.
11875 : * @since 3.4
11876 : */
11877 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11878 : const char *pszVectorLayerName,
11879 : CSLConstList papszOptions)
11880 : {
11881 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11882 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11883 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11884 24 : std::string(pszVectorLayerName), papszOptions));
11885 : }
11886 :
11887 : /************************************************************************/
11888 : /* GDALGroupOpenMDArrayFromFullname() */
11889 : /************************************************************************/
11890 :
11891 : /** Open and return a sub-group from its fully qualified name.
11892 : *
11893 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11894 : *
11895 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11896 : *
11897 : * @since GDAL 3.2
11898 : */
11899 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11900 : const char *pszFullname,
11901 : CSLConstList papszOptions)
11902 : {
11903 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11904 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11905 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11906 9 : std::string(pszFullname), papszOptions);
11907 3 : if (!subGroup)
11908 2 : return nullptr;
11909 1 : return new GDALGroupHS(subGroup);
11910 : }
11911 :
11912 : /************************************************************************/
11913 : /* GDALGroupGetDimensions() */
11914 : /************************************************************************/
11915 :
11916 : /** Return the list of dimensions contained in this group and used by its
11917 : * arrays.
11918 : *
11919 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11920 : * array itself needs to be freed, CPLFree() should be called (and
11921 : * GDALDimensionRelease() on individual array members).
11922 : *
11923 : * This is the same as the C++ method GDALGroup::GetDimensions().
11924 : *
11925 : * @param hGroup Group.
11926 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11927 : * @param papszOptions Driver specific options determining how dimensions
11928 : * should be retrieved. Pass nullptr for default behavior.
11929 : *
11930 : * @return an array of *pnCount dimensions.
11931 : */
11932 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11933 : CSLConstList papszOptions)
11934 : {
11935 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11936 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11937 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11938 : auto ret = static_cast<GDALDimensionH *>(
11939 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11940 230 : for (size_t i = 0; i < dims.size(); i++)
11941 : {
11942 157 : ret[i] = new GDALDimensionHS(dims[i]);
11943 : }
11944 73 : *pnCount = dims.size();
11945 73 : return ret;
11946 : }
11947 :
11948 : /************************************************************************/
11949 : /* GDALGroupGetAttribute() */
11950 : /************************************************************************/
11951 :
11952 : /** Return an attribute by its name.
11953 : *
11954 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11955 : *
11956 : * The returned attribute must be freed with GDALAttributeRelease().
11957 : */
11958 120 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11959 : {
11960 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11961 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11962 360 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11963 120 : if (attr)
11964 116 : return new GDALAttributeHS(attr);
11965 4 : return nullptr;
11966 : }
11967 :
11968 : /************************************************************************/
11969 : /* GDALGroupGetAttributes() */
11970 : /************************************************************************/
11971 :
11972 : /** Return the list of attributes contained in this group.
11973 : *
11974 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11975 : * array itself needs to be freed, CPLFree() should be called (and
11976 : * GDALAttributeRelease() on individual array members).
11977 : *
11978 : * This is the same as the C++ method GDALGroup::GetAttributes().
11979 : *
11980 : * @param hGroup Group.
11981 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11982 : * @param papszOptions Driver specific options determining how attributes
11983 : * should be retrieved. Pass nullptr for default behavior.
11984 : *
11985 : * @return an array of *pnCount attributes.
11986 : */
11987 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11988 : CSLConstList papszOptions)
11989 : {
11990 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11991 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11992 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11993 : auto ret = static_cast<GDALAttributeH *>(
11994 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11995 229 : for (size_t i = 0; i < attrs.size(); i++)
11996 : {
11997 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11998 : }
11999 71 : *pnCount = attrs.size();
12000 71 : return ret;
12001 : }
12002 :
12003 : /************************************************************************/
12004 : /* GDALGroupGetStructuralInfo() */
12005 : /************************************************************************/
12006 :
12007 : /** Return structural information on the group.
12008 : *
12009 : * This may be the compression, etc..
12010 : *
12011 : * The return value should not be freed and is valid until GDALGroup is
12012 : * released or this function called again.
12013 : *
12014 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
12015 : */
12016 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
12017 : {
12018 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12019 4 : return hGroup->m_poImpl->GetStructuralInfo();
12020 : }
12021 :
12022 : /************************************************************************/
12023 : /* GDALGroupGetDataTypeCount() */
12024 : /************************************************************************/
12025 :
12026 : /** Return the number of data types associated with the group
12027 : * (typically enumerations).
12028 : *
12029 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
12030 : *
12031 : * @since 3.12
12032 : */
12033 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
12034 : {
12035 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
12036 4 : return hGroup->m_poImpl->GetDataTypes().size();
12037 : }
12038 :
12039 : /************************************************************************/
12040 : /* GDALGroupGetDataType() */
12041 : /************************************************************************/
12042 :
12043 : /** Return one of the data types associated with the group.
12044 : *
12045 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
12046 : *
12047 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
12048 : * or nullptr in case of error.
12049 : * @since 3.12
12050 : */
12051 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
12052 : {
12053 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12054 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
12055 0 : return nullptr;
12056 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
12057 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
12058 : }
12059 :
12060 : /************************************************************************/
12061 : /* GDALReleaseAttributes() */
12062 : /************************************************************************/
12063 :
12064 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
12065 : *
12066 : * @param attributes return pointer of above methods
12067 : * @param nCount *pnCount value returned by above methods
12068 : */
12069 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
12070 : {
12071 418 : for (size_t i = 0; i < nCount; i++)
12072 : {
12073 288 : delete attributes[i];
12074 : }
12075 130 : CPLFree(attributes);
12076 130 : }
12077 :
12078 : /************************************************************************/
12079 : /* GDALGroupCreateGroup() */
12080 : /************************************************************************/
12081 :
12082 : /** Create a sub-group within a group.
12083 : *
12084 : * This is the same as the C++ method GDALGroup::CreateGroup().
12085 : *
12086 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
12087 : */
12088 185 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12089 : CSLConstList papszOptions)
12090 : {
12091 185 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12092 185 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
12093 555 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
12094 555 : papszOptions);
12095 185 : if (!ret)
12096 52 : return nullptr;
12097 133 : return new GDALGroupHS(ret);
12098 : }
12099 :
12100 : /************************************************************************/
12101 : /* GDALGroupDeleteGroup() */
12102 : /************************************************************************/
12103 :
12104 : /** Delete a sub-group from a group.
12105 : *
12106 : * After this call, if a previously obtained instance of the deleted object
12107 : * is still alive, no method other than for freeing it should be invoked.
12108 : *
12109 : * This is the same as the C++ method GDALGroup::DeleteGroup().
12110 : *
12111 : * @return true in case of success.
12112 : * @since GDAL 3.8
12113 : */
12114 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12115 : CSLConstList papszOptions)
12116 : {
12117 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12118 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12119 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12120 20 : papszOptions);
12121 : }
12122 :
12123 : /************************************************************************/
12124 : /* GDALGroupCreateDimension() */
12125 : /************************************************************************/
12126 :
12127 : /** Create a dimension within a group.
12128 : *
12129 : * This is the same as the C++ method GDALGroup::CreateDimension().
12130 : *
12131 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12132 : */
12133 769 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12134 : const char *pszType,
12135 : const char *pszDirection, GUInt64 nSize,
12136 : CSLConstList papszOptions)
12137 : {
12138 769 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12139 769 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12140 769 : auto ret = hGroup->m_poImpl->CreateDimension(
12141 1538 : std::string(pszName), std::string(pszType ? pszType : ""),
12142 3076 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12143 769 : if (!ret)
12144 9 : return nullptr;
12145 760 : return new GDALDimensionHS(ret);
12146 : }
12147 :
12148 : /************************************************************************/
12149 : /* GDALGroupCreateMDArray() */
12150 : /************************************************************************/
12151 :
12152 : /** Create a multidimensional array within a group.
12153 : *
12154 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12155 : *
12156 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12157 : */
12158 711 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12159 : size_t nDimensions,
12160 : GDALDimensionH *pahDimensions,
12161 : GDALExtendedDataTypeH hEDT,
12162 : CSLConstList papszOptions)
12163 : {
12164 711 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12165 711 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12166 711 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12167 1422 : std::vector<std::shared_ptr<GDALDimension>> dims;
12168 711 : dims.reserve(nDimensions);
12169 1696 : for (size_t i = 0; i < nDimensions; i++)
12170 985 : dims.push_back(pahDimensions[i]->m_poImpl);
12171 2133 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12172 2133 : *(hEDT->m_poImpl), papszOptions);
12173 711 : if (!ret)
12174 64 : return nullptr;
12175 647 : return new GDALMDArrayHS(ret);
12176 : }
12177 :
12178 : /************************************************************************/
12179 : /* GDALGroupDeleteMDArray() */
12180 : /************************************************************************/
12181 :
12182 : /** Delete an array from a group.
12183 : *
12184 : * After this call, if a previously obtained instance of the deleted object
12185 : * is still alive, no method other than for freeing it should be invoked.
12186 : *
12187 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12188 : *
12189 : * @return true in case of success.
12190 : * @since GDAL 3.8
12191 : */
12192 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12193 : CSLConstList papszOptions)
12194 : {
12195 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12196 20 : VALIDATE_POINTER1(pszName, __func__, false);
12197 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12198 : }
12199 :
12200 : /************************************************************************/
12201 : /* GDALGroupCreateAttribute() */
12202 : /************************************************************************/
12203 :
12204 : /** Create a attribute within a group.
12205 : *
12206 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12207 : *
12208 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12209 : */
12210 142 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12211 : size_t nDimensions,
12212 : const GUInt64 *panDimensions,
12213 : GDALExtendedDataTypeH hEDT,
12214 : CSLConstList papszOptions)
12215 : {
12216 142 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12217 142 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12218 284 : std::vector<GUInt64> dims;
12219 142 : dims.reserve(nDimensions);
12220 200 : for (size_t i = 0; i < nDimensions; i++)
12221 58 : dims.push_back(panDimensions[i]);
12222 142 : auto ret = hGroup->m_poImpl->CreateAttribute(
12223 426 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12224 142 : if (!ret)
12225 18 : return nullptr;
12226 124 : return new GDALAttributeHS(ret);
12227 : }
12228 :
12229 : /************************************************************************/
12230 : /* GDALGroupDeleteAttribute() */
12231 : /************************************************************************/
12232 :
12233 : /** Delete an attribute from a group.
12234 : *
12235 : * After this call, if a previously obtained instance of the deleted object
12236 : * is still alive, no method other than for freeing it should be invoked.
12237 : *
12238 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12239 : *
12240 : * @return true in case of success.
12241 : * @since GDAL 3.8
12242 : */
12243 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12244 : CSLConstList papszOptions)
12245 : {
12246 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12247 25 : VALIDATE_POINTER1(pszName, __func__, false);
12248 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12249 25 : papszOptions);
12250 : }
12251 :
12252 : /************************************************************************/
12253 : /* GDALGroupRename() */
12254 : /************************************************************************/
12255 :
12256 : /** Rename the group.
12257 : *
12258 : * This is not implemented by all drivers.
12259 : *
12260 : * Drivers known to implement it: MEM, netCDF.
12261 : *
12262 : * This is the same as the C++ method GDALGroup::Rename()
12263 : *
12264 : * @return true in case of success
12265 : * @since GDAL 3.8
12266 : */
12267 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12268 : {
12269 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12270 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12271 45 : return hGroup->m_poImpl->Rename(pszNewName);
12272 : }
12273 :
12274 : /************************************************************************/
12275 : /* GDALGroupSubsetDimensionFromSelection() */
12276 : /************************************************************************/
12277 :
12278 : /** Return a virtual group whose one dimension has been subset according to a
12279 : * selection.
12280 : *
12281 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12282 : *
12283 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12284 : */
12285 : GDALGroupH
12286 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12287 : const char *pszSelection,
12288 : CPL_UNUSED CSLConstList papszOptions)
12289 : {
12290 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12291 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12292 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12293 42 : std::string(pszSelection));
12294 14 : if (!hNewGroup)
12295 8 : return nullptr;
12296 6 : return new GDALGroupHS(hNewGroup);
12297 : }
12298 :
12299 : /************************************************************************/
12300 : /* GDALMDArrayRelease() */
12301 : /************************************************************************/
12302 :
12303 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12304 : *
12305 : * Note: when applied on a object coming from a driver, this does not
12306 : * destroy the object in the file, database, etc...
12307 : */
12308 2890 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12309 : {
12310 2890 : delete hMDArray;
12311 2890 : }
12312 :
12313 : /************************************************************************/
12314 : /* GDALMDArrayGetName() */
12315 : /************************************************************************/
12316 :
12317 : /** Return array name.
12318 : *
12319 : * This is the same as the C++ method GDALMDArray::GetName()
12320 : */
12321 84 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12322 : {
12323 84 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12324 84 : return hArray->m_poImpl->GetName().c_str();
12325 : }
12326 :
12327 : /************************************************************************/
12328 : /* GDALMDArrayGetFullName() */
12329 : /************************************************************************/
12330 :
12331 : /** Return array full name.
12332 : *
12333 : * This is the same as the C++ method GDALMDArray::GetFullName()
12334 : */
12335 68 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12336 : {
12337 68 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12338 68 : return hArray->m_poImpl->GetFullName().c_str();
12339 : }
12340 :
12341 : /************************************************************************/
12342 : /* GDALMDArrayGetName() */
12343 : /************************************************************************/
12344 :
12345 : /** Return the total number of values in the array.
12346 : *
12347 : * This is the same as the C++ method
12348 : * GDALAbstractMDArray::GetTotalElementsCount()
12349 : */
12350 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12351 : {
12352 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12353 6 : return hArray->m_poImpl->GetTotalElementsCount();
12354 : }
12355 :
12356 : /************************************************************************/
12357 : /* GDALMDArrayGetDimensionCount() */
12358 : /************************************************************************/
12359 :
12360 : /** Return the number of dimensions.
12361 : *
12362 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12363 : */
12364 14054 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12365 : {
12366 14054 : VALIDATE_POINTER1(hArray, __func__, 0);
12367 14054 : return hArray->m_poImpl->GetDimensionCount();
12368 : }
12369 :
12370 : /************************************************************************/
12371 : /* GDALMDArrayGetDimensions() */
12372 : /************************************************************************/
12373 :
12374 : /** Return the dimensions of the array
12375 : *
12376 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12377 : * array itself needs to be freed, CPLFree() should be called (and
12378 : * GDALDimensionRelease() on individual array members).
12379 : *
12380 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12381 : *
12382 : * @param hArray Array.
12383 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12384 : *
12385 : * @return an array of *pnCount dimensions.
12386 : */
12387 3101 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12388 : {
12389 3101 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12390 3101 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12391 3101 : const auto &dims(hArray->m_poImpl->GetDimensions());
12392 : auto ret = static_cast<GDALDimensionH *>(
12393 3101 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12394 8866 : for (size_t i = 0; i < dims.size(); i++)
12395 : {
12396 5765 : ret[i] = new GDALDimensionHS(dims[i]);
12397 : }
12398 3101 : *pnCount = dims.size();
12399 3101 : return ret;
12400 : }
12401 :
12402 : /************************************************************************/
12403 : /* GDALReleaseDimensions() */
12404 : /************************************************************************/
12405 :
12406 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12407 : *
12408 : * @param dims return pointer of above methods
12409 : * @param nCount *pnCount value returned by above methods
12410 : */
12411 3174 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12412 : {
12413 9096 : for (size_t i = 0; i < nCount; i++)
12414 : {
12415 5922 : delete dims[i];
12416 : }
12417 3174 : CPLFree(dims);
12418 3174 : }
12419 :
12420 : /************************************************************************/
12421 : /* GDALMDArrayGetDataType() */
12422 : /************************************************************************/
12423 :
12424 : /** Return the data type
12425 : *
12426 : * The return must be freed with GDALExtendedDataTypeRelease().
12427 : */
12428 5464 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12429 : {
12430 5464 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12431 : return new GDALExtendedDataTypeHS(
12432 5464 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12433 : }
12434 :
12435 : /************************************************************************/
12436 : /* GDALMDArrayRead() */
12437 : /************************************************************************/
12438 :
12439 : /** Read part or totality of a multidimensional array.
12440 : *
12441 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12442 : *
12443 : * @return TRUE in case of success.
12444 : */
12445 2629 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12446 : const size_t *count, const GInt64 *arrayStep,
12447 : const GPtrDiff_t *bufferStride,
12448 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12449 : const void *pDstBufferAllocStart,
12450 : size_t nDstBufferAllocSize)
12451 : {
12452 2629 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12453 2629 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12454 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12455 : {
12456 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12457 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12458 : }
12459 2629 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12460 2629 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12461 5258 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12462 2629 : *(bufferDataType->m_poImpl), pDstBuffer,
12463 2629 : pDstBufferAllocStart, nDstBufferAllocSize);
12464 : }
12465 :
12466 : /************************************************************************/
12467 : /* GDALMDArrayWrite() */
12468 : /************************************************************************/
12469 :
12470 : /** Write part or totality of a multidimensional array.
12471 : *
12472 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12473 : *
12474 : * @return TRUE in case of success.
12475 : */
12476 657 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12477 : const size_t *count, const GInt64 *arrayStep,
12478 : const GPtrDiff_t *bufferStride,
12479 : GDALExtendedDataTypeH bufferDataType,
12480 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12481 : size_t nSrcBufferAllocSize)
12482 : {
12483 657 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12484 657 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12485 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12486 : {
12487 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12488 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12489 : }
12490 657 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12491 657 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12492 1314 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12493 657 : bufferStride, *(bufferDataType->m_poImpl),
12494 : pSrcBuffer, pSrcBufferAllocStart,
12495 657 : nSrcBufferAllocSize);
12496 : }
12497 :
12498 : /************************************************************************/
12499 : /* GDALMDArrayAdviseRead() */
12500 : /************************************************************************/
12501 :
12502 : /** Advise driver of upcoming read requests.
12503 : *
12504 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12505 : *
12506 : * @return TRUE in case of success.
12507 : *
12508 : * @since GDAL 3.2
12509 : */
12510 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12511 : const size_t *count)
12512 : {
12513 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12514 : }
12515 :
12516 : /************************************************************************/
12517 : /* GDALMDArrayAdviseReadEx() */
12518 : /************************************************************************/
12519 :
12520 : /** Advise driver of upcoming read requests.
12521 : *
12522 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12523 : *
12524 : * @return TRUE in case of success.
12525 : *
12526 : * @since GDAL 3.4
12527 : */
12528 23 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12529 : const size_t *count, CSLConstList papszOptions)
12530 : {
12531 23 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12532 23 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12533 : }
12534 :
12535 : /************************************************************************/
12536 : /* GDALMDArrayGetAttribute() */
12537 : /************************************************************************/
12538 :
12539 : /** Return an attribute by its name.
12540 : *
12541 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12542 : *
12543 : * The returned attribute must be freed with GDALAttributeRelease().
12544 : */
12545 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12546 : {
12547 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12548 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12549 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12550 120 : if (attr)
12551 111 : return new GDALAttributeHS(attr);
12552 9 : return nullptr;
12553 : }
12554 :
12555 : /************************************************************************/
12556 : /* GDALMDArrayGetAttributes() */
12557 : /************************************************************************/
12558 :
12559 : /** Return the list of attributes contained in this array.
12560 : *
12561 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12562 : * array itself needs to be freed, CPLFree() should be called (and
12563 : * GDALAttributeRelease() on individual array members).
12564 : *
12565 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12566 : *
12567 : * @param hArray Array.
12568 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12569 : * @param papszOptions Driver specific options determining how attributes
12570 : * should be retrieved. Pass nullptr for default behavior.
12571 : *
12572 : * @return an array of *pnCount attributes.
12573 : */
12574 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12575 : CSLConstList papszOptions)
12576 : {
12577 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12578 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12579 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12580 : auto ret = static_cast<GDALAttributeH *>(
12581 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12582 189 : for (size_t i = 0; i < attrs.size(); i++)
12583 : {
12584 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12585 : }
12586 59 : *pnCount = attrs.size();
12587 59 : return ret;
12588 : }
12589 :
12590 : /************************************************************************/
12591 : /* GDALMDArrayCreateAttribute() */
12592 : /************************************************************************/
12593 :
12594 : /** Create a attribute within an array.
12595 : *
12596 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12597 : *
12598 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12599 : */
12600 188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12601 : const char *pszName,
12602 : size_t nDimensions,
12603 : const GUInt64 *panDimensions,
12604 : GDALExtendedDataTypeH hEDT,
12605 : CSLConstList papszOptions)
12606 : {
12607 188 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12608 188 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12609 188 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12610 376 : std::vector<GUInt64> dims;
12611 188 : dims.reserve(nDimensions);
12612 249 : for (size_t i = 0; i < nDimensions; i++)
12613 61 : dims.push_back(panDimensions[i]);
12614 188 : auto ret = hArray->m_poImpl->CreateAttribute(
12615 564 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12616 188 : if (!ret)
12617 9 : return nullptr;
12618 179 : return new GDALAttributeHS(ret);
12619 : }
12620 :
12621 : /************************************************************************/
12622 : /* GDALMDArrayDeleteAttribute() */
12623 : /************************************************************************/
12624 :
12625 : /** Delete an attribute from an array.
12626 : *
12627 : * After this call, if a previously obtained instance of the deleted object
12628 : * is still alive, no method other than for freeing it should be invoked.
12629 : *
12630 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12631 : *
12632 : * @return true in case of success.
12633 : * @since GDAL 3.8
12634 : */
12635 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12636 : CSLConstList papszOptions)
12637 : {
12638 24 : VALIDATE_POINTER1(hArray, __func__, false);
12639 24 : VALIDATE_POINTER1(pszName, __func__, false);
12640 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12641 24 : papszOptions);
12642 : }
12643 :
12644 : /************************************************************************/
12645 : /* GDALMDArrayGetRawNoDataValue() */
12646 : /************************************************************************/
12647 :
12648 : /** Return the nodata value as a "raw" value.
12649 : *
12650 : * The value returned might be nullptr in case of no nodata value. When
12651 : * a nodata value is registered, a non-nullptr will be returned whose size in
12652 : * bytes is GetDataType().GetSize().
12653 : *
12654 : * The returned value should not be modified or freed.
12655 : *
12656 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12657 : *
12658 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12659 : */
12660 77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12661 : {
12662 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12663 77 : return hArray->m_poImpl->GetRawNoDataValue();
12664 : }
12665 :
12666 : /************************************************************************/
12667 : /* GDALMDArrayGetNoDataValueAsDouble() */
12668 : /************************************************************************/
12669 :
12670 : /** Return the nodata value as a double.
12671 : *
12672 : * The value returned might be nullptr in case of no nodata value. When
12673 : * a nodata value is registered, a non-nullptr will be returned whose size in
12674 : * bytes is GetDataType().GetSize().
12675 : *
12676 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12677 : *
12678 : * @param hArray Array handle.
12679 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12680 : * if a nodata value exists and can be converted to double. Might be nullptr.
12681 : *
12682 : * @return the nodata value as a double. A 0.0 value might also indicate the
12683 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12684 : * will be set to false then).
12685 : */
12686 121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12687 : int *pbHasNoDataValue)
12688 : {
12689 121 : VALIDATE_POINTER1(hArray, __func__, 0);
12690 121 : bool bHasNodataValue = false;
12691 121 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12692 121 : if (pbHasNoDataValue)
12693 121 : *pbHasNoDataValue = bHasNodataValue;
12694 121 : return ret;
12695 : }
12696 :
12697 : /************************************************************************/
12698 : /* GDALMDArrayGetNoDataValueAsInt64() */
12699 : /************************************************************************/
12700 :
12701 : /** Return the nodata value as a Int64.
12702 : *
12703 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12704 : *
12705 : * @param hArray Array handle.
12706 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12707 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12708 : *
12709 : * @return the nodata value as a Int64.
12710 : * @since GDAL 3.5
12711 : */
12712 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12713 : int *pbHasNoDataValue)
12714 : {
12715 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12716 11 : bool bHasNodataValue = false;
12717 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12718 11 : if (pbHasNoDataValue)
12719 11 : *pbHasNoDataValue = bHasNodataValue;
12720 11 : return ret;
12721 : }
12722 :
12723 : /************************************************************************/
12724 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12725 : /************************************************************************/
12726 :
12727 : /** Return the nodata value as a UInt64.
12728 : *
12729 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12730 : *
12731 : * @param hArray Array handle.
12732 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12733 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12734 : *
12735 : * @return the nodata value as a UInt64.
12736 : * @since GDAL 3.5
12737 : */
12738 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12739 : int *pbHasNoDataValue)
12740 : {
12741 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12742 7 : bool bHasNodataValue = false;
12743 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12744 7 : if (pbHasNoDataValue)
12745 7 : *pbHasNoDataValue = bHasNodataValue;
12746 7 : return ret;
12747 : }
12748 :
12749 : /************************************************************************/
12750 : /* GDALMDArraySetRawNoDataValue() */
12751 : /************************************************************************/
12752 :
12753 : /** Set the nodata value as a "raw" value.
12754 : *
12755 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12756 : * void*).
12757 : *
12758 : * @return TRUE in case of success.
12759 : */
12760 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12761 : {
12762 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12763 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12764 : }
12765 :
12766 : /************************************************************************/
12767 : /* GDALMDArraySetNoDataValueAsDouble() */
12768 : /************************************************************************/
12769 :
12770 : /** Set the nodata value as a double.
12771 : *
12772 : * If the natural data type of the attribute/array is not double, type
12773 : * conversion will occur to the type returned by GetDataType().
12774 : *
12775 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12776 : *
12777 : * @return TRUE in case of success.
12778 : */
12779 55 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12780 : {
12781 55 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12782 55 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12783 : }
12784 :
12785 : /************************************************************************/
12786 : /* GDALMDArraySetNoDataValueAsInt64() */
12787 : /************************************************************************/
12788 :
12789 : /** Set the nodata value as a Int64.
12790 : *
12791 : * If the natural data type of the attribute/array is not Int64, type conversion
12792 : * will occur to the type returned by GetDataType().
12793 : *
12794 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12795 : *
12796 : * @return TRUE in case of success.
12797 : * @since GDAL 3.5
12798 : */
12799 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12800 : {
12801 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12802 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12803 : }
12804 :
12805 : /************************************************************************/
12806 : /* GDALMDArraySetNoDataValueAsUInt64() */
12807 : /************************************************************************/
12808 :
12809 : /** Set the nodata value as a UInt64.
12810 : *
12811 : * If the natural data type of the attribute/array is not UInt64, type
12812 : * conversion will occur to the type returned by GetDataType().
12813 : *
12814 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12815 : *
12816 : * @return TRUE in case of success.
12817 : * @since GDAL 3.5
12818 : */
12819 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12820 : uint64_t nNoDataValue)
12821 : {
12822 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12823 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12824 : }
12825 :
12826 : /************************************************************************/
12827 : /* GDALMDArrayResize() */
12828 : /************************************************************************/
12829 :
12830 : /** Resize an array to new dimensions.
12831 : *
12832 : * Not all drivers may allow this operation, and with restrictions (e.g.
12833 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12834 : *
12835 : * Resizing a dimension used in other arrays will cause those other arrays
12836 : * to be resized.
12837 : *
12838 : * This is the same as the C++ method GDALMDArray::Resize().
12839 : *
12840 : * @param hArray Array.
12841 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12842 : * new size of each indexing dimension.
12843 : * @param papszOptions Options. (Driver specific)
12844 : * @return true in case of success.
12845 : * @since GDAL 3.7
12846 : */
12847 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12848 : CSLConstList papszOptions)
12849 : {
12850 42 : VALIDATE_POINTER1(hArray, __func__, false);
12851 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12852 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12853 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12854 : {
12855 83 : anNewDimSizes[i] = panNewDimSizes[i];
12856 : }
12857 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12858 : }
12859 :
12860 : /************************************************************************/
12861 : /* GDALMDArraySetScale() */
12862 : /************************************************************************/
12863 :
12864 : /** Set the scale value to apply to raw values.
12865 : *
12866 : * unscaled_value = raw_value * GetScale() + GetOffset()
12867 : *
12868 : * This is the same as the C++ method GDALMDArray::SetScale().
12869 : *
12870 : * @return TRUE in case of success.
12871 : */
12872 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12873 : {
12874 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12875 0 : return hArray->m_poImpl->SetScale(dfScale);
12876 : }
12877 :
12878 : /************************************************************************/
12879 : /* GDALMDArraySetScaleEx() */
12880 : /************************************************************************/
12881 :
12882 : /** Set the scale value to apply to raw values.
12883 : *
12884 : * unscaled_value = raw_value * GetScale() + GetOffset()
12885 : *
12886 : * This is the same as the C++ method GDALMDArray::SetScale().
12887 : *
12888 : * @return TRUE in case of success.
12889 : * @since GDAL 3.3
12890 : */
12891 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12892 : GDALDataType eStorageType)
12893 : {
12894 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12895 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12896 : }
12897 :
12898 : /************************************************************************/
12899 : /* GDALMDArraySetOffset() */
12900 : /************************************************************************/
12901 :
12902 : /** Set 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::SetOffset().
12907 : *
12908 : * @return TRUE in case of success.
12909 : */
12910 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12911 : {
12912 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12913 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12914 : }
12915 :
12916 : /************************************************************************/
12917 : /* GDALMDArraySetOffsetEx() */
12918 : /************************************************************************/
12919 :
12920 : /** Set the scale value to apply to raw values.
12921 : *
12922 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12923 : *
12924 : * This is the same as the C++ method GDALMDArray::SetOffset().
12925 : *
12926 : * @return TRUE in case of success.
12927 : * @since GDAL 3.3
12928 : */
12929 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12930 : GDALDataType eStorageType)
12931 : {
12932 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12933 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12934 : }
12935 :
12936 : /************************************************************************/
12937 : /* GDALMDArrayGetScale() */
12938 : /************************************************************************/
12939 :
12940 : /** Get the scale value to apply to raw values.
12941 : *
12942 : * unscaled_value = raw_value * GetScale() + GetOffset()
12943 : *
12944 : * This is the same as the C++ method GDALMDArray::GetScale().
12945 : *
12946 : * @return the scale value
12947 : */
12948 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12949 : {
12950 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12951 105 : bool bHasValue = false;
12952 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12953 105 : if (pbHasValue)
12954 105 : *pbHasValue = bHasValue;
12955 105 : return dfRet;
12956 : }
12957 :
12958 : /************************************************************************/
12959 : /* GDALMDArrayGetScaleEx() */
12960 : /************************************************************************/
12961 :
12962 : /** Get the scale value to apply to raw values.
12963 : *
12964 : * unscaled_value = raw_value * GetScale() + GetScale()
12965 : *
12966 : * This is the same as the C++ method GDALMDArray::GetScale().
12967 : *
12968 : * @return the scale value
12969 : * @since GDAL 3.3
12970 : */
12971 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12972 : GDALDataType *peStorageType)
12973 : {
12974 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12975 5 : bool bHasValue = false;
12976 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12977 5 : if (pbHasValue)
12978 5 : *pbHasValue = bHasValue;
12979 5 : return dfRet;
12980 : }
12981 :
12982 : /************************************************************************/
12983 : /* GDALMDArrayGetOffset() */
12984 : /************************************************************************/
12985 :
12986 : /** Get the scale value to apply to raw values.
12987 : *
12988 : * unscaled_value = raw_value * GetScale() + GetOffset()
12989 : *
12990 : * This is the same as the C++ method GDALMDArray::GetOffset().
12991 : *
12992 : * @return the scale value
12993 : */
12994 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12995 : {
12996 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12997 102 : bool bHasValue = false;
12998 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12999 102 : if (pbHasValue)
13000 102 : *pbHasValue = bHasValue;
13001 102 : return dfRet;
13002 : }
13003 :
13004 : /************************************************************************/
13005 : /* GDALMDArrayGetOffsetEx() */
13006 : /************************************************************************/
13007 :
13008 : /** Get the scale value to apply to raw values.
13009 : *
13010 : * unscaled_value = raw_value * GetScale() + GetOffset()
13011 : *
13012 : * This is the same as the C++ method GDALMDArray::GetOffset().
13013 : *
13014 : * @return the scale value
13015 : * @since GDAL 3.3
13016 : */
13017 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
13018 : GDALDataType *peStorageType)
13019 : {
13020 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13021 5 : bool bHasValue = false;
13022 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
13023 5 : if (pbHasValue)
13024 5 : *pbHasValue = bHasValue;
13025 5 : return dfRet;
13026 : }
13027 :
13028 : /************************************************************************/
13029 : /* GDALMDArrayGetBlockSize() */
13030 : /************************************************************************/
13031 :
13032 : /** Return the "natural" block size of the array along all dimensions.
13033 : *
13034 : * Some drivers might organize the array in tiles/blocks and reading/writing
13035 : * aligned on those tile/block boundaries will be more efficient.
13036 : *
13037 : * The returned number of elements in the vector is the same as
13038 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
13039 : * the natural block size along the considered dimension.
13040 : * "Flat" arrays will typically return a vector of values set to 0.
13041 : *
13042 : * The default implementation will return a vector of values set to 0.
13043 : *
13044 : * This method is used by GetProcessingChunkSize().
13045 : *
13046 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
13047 : * theoretical case of a 32-bit platform, this might exceed its size_t
13048 : * allocation capabilities.
13049 : *
13050 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
13051 : *
13052 : * @return the block size, in number of elements along each dimension.
13053 : */
13054 105 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
13055 : {
13056 105 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13057 105 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13058 105 : auto res = hArray->m_poImpl->GetBlockSize();
13059 105 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
13060 322 : for (size_t i = 0; i < res.size(); i++)
13061 : {
13062 217 : ret[i] = res[i];
13063 : }
13064 105 : *pnCount = res.size();
13065 105 : return ret;
13066 : }
13067 :
13068 : /************************************************************************/
13069 : /* GDALMDArrayGetProcessingChunkSize() */
13070 : /************************************************************************/
13071 :
13072 : /** \brief Return an optimal chunk size for read/write operations, given the
13073 : * natural block size and memory constraints specified.
13074 : *
13075 : * This method will use GetBlockSize() to define a chunk whose dimensions are
13076 : * multiple of those returned by GetBlockSize() (unless the block define by
13077 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
13078 : * returned by this method).
13079 : *
13080 : * This is the same as the C++ method
13081 : * GDALAbstractMDArray::GetProcessingChunkSize().
13082 : *
13083 : * @param hArray Array.
13084 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13085 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
13086 : * chunk.
13087 : *
13088 : * @return the chunk size, in number of elements along each dimension.
13089 : */
13090 :
13091 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
13092 : size_t nMaxChunkMemory)
13093 : {
13094 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13095 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13096 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
13097 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
13098 3 : for (size_t i = 0; i < res.size(); i++)
13099 : {
13100 2 : ret[i] = res[i];
13101 : }
13102 1 : *pnCount = res.size();
13103 1 : return ret;
13104 : }
13105 :
13106 : /************************************************************************/
13107 : /* GDALMDArrayGetStructuralInfo() */
13108 : /************************************************************************/
13109 :
13110 : /** Return structural information on the array.
13111 : *
13112 : * This may be the compression, etc..
13113 : *
13114 : * The return value should not be freed and is valid until GDALMDArray is
13115 : * released or this function called again.
13116 : *
13117 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
13118 : */
13119 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13120 : {
13121 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13122 15 : return hArray->m_poImpl->GetStructuralInfo();
13123 : }
13124 :
13125 : /************************************************************************/
13126 : /* GDALMDArrayGetView() */
13127 : /************************************************************************/
13128 :
13129 : /** Return a view of the array using slicing or field access.
13130 : *
13131 : * The returned object should be released with GDALMDArrayRelease().
13132 : *
13133 : * This is the same as the C++ method GDALMDArray::GetView().
13134 : */
13135 438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13136 : {
13137 438 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13138 438 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13139 1314 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13140 438 : if (!sliced)
13141 23 : return nullptr;
13142 415 : return new GDALMDArrayHS(sliced);
13143 : }
13144 :
13145 : /************************************************************************/
13146 : /* GDALMDArrayTranspose() */
13147 : /************************************************************************/
13148 :
13149 : /** Return a view of the array whose axis have been reordered.
13150 : *
13151 : * The returned object should be released with GDALMDArrayRelease().
13152 : *
13153 : * This is the same as the C++ method GDALMDArray::Transpose().
13154 : */
13155 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13156 : const int *panMapNewAxisToOldAxis)
13157 : {
13158 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13159 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13160 44 : if (nNewAxisCount)
13161 : {
13162 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13163 : nNewAxisCount * sizeof(int));
13164 : }
13165 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13166 44 : if (!reordered)
13167 7 : return nullptr;
13168 37 : return new GDALMDArrayHS(reordered);
13169 : }
13170 :
13171 : /************************************************************************/
13172 : /* GDALMDArrayGetUnscaled() */
13173 : /************************************************************************/
13174 :
13175 : /** Return an array that is the unscaled version of the current one.
13176 : *
13177 : * That is each value of the unscaled array will be
13178 : * unscaled_value = raw_value * GetScale() + GetOffset()
13179 : *
13180 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13181 : * from unscaled values to raw values.
13182 : *
13183 : * The returned object should be released with GDALMDArrayRelease().
13184 : *
13185 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13186 : */
13187 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13188 : {
13189 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13190 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13191 13 : if (!unscaled)
13192 0 : return nullptr;
13193 13 : return new GDALMDArrayHS(unscaled);
13194 : }
13195 :
13196 : /************************************************************************/
13197 : /* GDALMDArrayGetMask() */
13198 : /************************************************************************/
13199 :
13200 : /** Return an array that is a mask for the current array
13201 : *
13202 : * This array will be of type Byte, with values set to 0 to indicate invalid
13203 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13204 : *
13205 : * The returned object should be released with GDALMDArrayRelease().
13206 : *
13207 : * This is the same as the C++ method GDALMDArray::GetMask().
13208 : */
13209 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13210 : {
13211 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13212 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13213 35 : if (!unscaled)
13214 7 : return nullptr;
13215 28 : return new GDALMDArrayHS(unscaled);
13216 : }
13217 :
13218 : /************************************************************************/
13219 : /* GDALMDArrayGetResampled() */
13220 : /************************************************************************/
13221 :
13222 : /** Return an array that is a resampled / reprojected view of the current array
13223 : *
13224 : * This is the same as the C++ method GDALMDArray::GetResampled().
13225 : *
13226 : * Currently this method can only resample along the last 2 dimensions, unless
13227 : * orthorectifying a NASA EMIT dataset.
13228 : *
13229 : * The returned object should be released with GDALMDArrayRelease().
13230 : *
13231 : * @since 3.4
13232 : */
13233 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13234 : const GDALDimensionH *pahNewDims,
13235 : GDALRIOResampleAlg resampleAlg,
13236 : OGRSpatialReferenceH hTargetSRS,
13237 : CSLConstList papszOptions)
13238 : {
13239 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13240 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13241 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13242 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13243 : {
13244 78 : if (pahNewDims[i])
13245 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13246 : }
13247 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13248 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13249 68 : papszOptions);
13250 34 : if (!poNewArray)
13251 8 : return nullptr;
13252 26 : return new GDALMDArrayHS(poNewArray);
13253 : }
13254 :
13255 : /************************************************************************/
13256 : /* GDALMDArraySetUnit() */
13257 : /************************************************************************/
13258 :
13259 : /** Set the variable unit.
13260 : *
13261 : * Values should conform as much as possible with those allowed by
13262 : * the NetCDF CF conventions:
13263 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13264 : * but others might be returned.
13265 : *
13266 : * Few examples are "meter", "degrees", "second", ...
13267 : * Empty value means unknown.
13268 : *
13269 : * This is the same as the C function GDALMDArraySetUnit()
13270 : *
13271 : * @param hArray array.
13272 : * @param pszUnit unit name.
13273 : * @return TRUE in case of success.
13274 : */
13275 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13276 : {
13277 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13278 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13279 : }
13280 :
13281 : /************************************************************************/
13282 : /* GDALMDArrayGetUnit() */
13283 : /************************************************************************/
13284 :
13285 : /** Return the array unit.
13286 : *
13287 : * Values should conform as much as possible with those allowed by
13288 : * the NetCDF CF conventions:
13289 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13290 : * but others might be returned.
13291 : *
13292 : * Few examples are "meter", "degrees", "second", ...
13293 : * Empty value means unknown.
13294 : *
13295 : * The return value should not be freed and is valid until GDALMDArray is
13296 : * released or this function called again.
13297 : *
13298 : * This is the same as the C++ method GDALMDArray::GetUnit().
13299 : */
13300 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13301 : {
13302 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13303 113 : return hArray->m_poImpl->GetUnit().c_str();
13304 : }
13305 :
13306 : /************************************************************************/
13307 : /* GDALMDArrayGetSpatialRef() */
13308 : /************************************************************************/
13309 :
13310 : /** Assign a spatial reference system object to the array.
13311 : *
13312 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13313 : * @return TRUE in case of success.
13314 : */
13315 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13316 : {
13317 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13318 60 : return hArray->m_poImpl->SetSpatialRef(
13319 60 : OGRSpatialReference::FromHandle(hSRS));
13320 : }
13321 :
13322 : /************************************************************************/
13323 : /* GDALMDArrayGetSpatialRef() */
13324 : /************************************************************************/
13325 :
13326 : /** Return the spatial reference system object associated with the array.
13327 : *
13328 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13329 : *
13330 : * The returned object must be freed with OSRDestroySpatialReference().
13331 : */
13332 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13333 : {
13334 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13335 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13336 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13337 : }
13338 :
13339 : /************************************************************************/
13340 : /* GDALMDArrayGetStatistics() */
13341 : /************************************************************************/
13342 :
13343 : /**
13344 : * \brief Fetch statistics.
13345 : *
13346 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13347 : *
13348 : * @since GDAL 3.2
13349 : */
13350 :
13351 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13352 : int bApproxOK, int bForce, double *pdfMin,
13353 : double *pdfMax, double *pdfMean,
13354 : double *pdfStdDev, GUInt64 *pnValidCount,
13355 : GDALProgressFunc pfnProgress,
13356 : void *pProgressData)
13357 : {
13358 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13359 30 : return hArray->m_poImpl->GetStatistics(
13360 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13361 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13362 : }
13363 :
13364 : /************************************************************************/
13365 : /* GDALMDArrayComputeStatistics() */
13366 : /************************************************************************/
13367 :
13368 : /**
13369 : * \brief Compute statistics.
13370 : *
13371 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13372 : *
13373 : * @since GDAL 3.2
13374 : * @see GDALMDArrayComputeStatisticsEx()
13375 : */
13376 :
13377 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13378 : int bApproxOK, double *pdfMin, double *pdfMax,
13379 : double *pdfMean, double *pdfStdDev,
13380 : GUInt64 *pnValidCount,
13381 : GDALProgressFunc pfnProgress,
13382 : void *pProgressData)
13383 : {
13384 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13385 0 : return hArray->m_poImpl->ComputeStatistics(
13386 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13387 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13388 : }
13389 :
13390 : /************************************************************************/
13391 : /* GDALMDArrayComputeStatisticsEx() */
13392 : /************************************************************************/
13393 :
13394 : /**
13395 : * \brief Compute statistics.
13396 : *
13397 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13398 : *
13399 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13400 : *
13401 : * @since GDAL 3.8
13402 : */
13403 :
13404 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13405 : int bApproxOK, double *pdfMin,
13406 : double *pdfMax, double *pdfMean,
13407 : double *pdfStdDev, GUInt64 *pnValidCount,
13408 : GDALProgressFunc pfnProgress,
13409 : void *pProgressData,
13410 : CSLConstList papszOptions)
13411 : {
13412 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13413 8 : return hArray->m_poImpl->ComputeStatistics(
13414 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13415 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13416 : }
13417 :
13418 : /************************************************************************/
13419 : /* GDALMDArrayGetCoordinateVariables() */
13420 : /************************************************************************/
13421 :
13422 : /** Return coordinate variables.
13423 : *
13424 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13425 : * itself needs to be freed, CPLFree() should be called (and
13426 : * GDALMDArrayRelease() on individual array members).
13427 : *
13428 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13429 : *
13430 : * @param hArray Array.
13431 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13432 : *
13433 : * @return an array of *pnCount arrays.
13434 : * @since 3.4
13435 : */
13436 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13437 : size_t *pnCount)
13438 : {
13439 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13440 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13441 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13442 : auto ret = static_cast<GDALMDArrayH *>(
13443 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13444 29 : for (size_t i = 0; i < coordinates.size(); i++)
13445 : {
13446 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13447 : }
13448 13 : *pnCount = coordinates.size();
13449 13 : return ret;
13450 : }
13451 :
13452 : /************************************************************************/
13453 : /* GDALMDArrayGetGridded() */
13454 : /************************************************************************/
13455 :
13456 : /** Return a gridded array from scattered point data, that is from an array
13457 : * whose last dimension is the indexing variable of X and Y arrays.
13458 : *
13459 : * The returned object should be released with GDALMDArrayRelease().
13460 : *
13461 : * This is the same as the C++ method GDALMDArray::GetGridded().
13462 : *
13463 : * @since GDAL 3.7
13464 : */
13465 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13466 : const char *pszGridOptions,
13467 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13468 : CSLConstList papszOptions)
13469 : {
13470 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13471 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13472 22 : auto gridded = hArray->m_poImpl->GetGridded(
13473 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13474 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13475 22 : if (!gridded)
13476 19 : return nullptr;
13477 3 : return new GDALMDArrayHS(gridded);
13478 : }
13479 :
13480 : /************************************************************************/
13481 : /* GDALMDArrayGetMeshGrid() */
13482 : /************************************************************************/
13483 :
13484 : /** Return a list of multidimensional arrays from a list of one-dimensional
13485 : * arrays.
13486 : *
13487 : * This is typically used to transform one-dimensional longitude, latitude
13488 : * arrays into 2D ones.
13489 : *
13490 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13491 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13492 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13493 : * repeated to fill the matrix along the first dimension for x1, the second
13494 : * for x2 and so on.
13495 : *
13496 : * For example, if x = [1, 2], and y = [3, 4, 5],
13497 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13498 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13499 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13500 : *
13501 : * and
13502 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13503 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13504 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13505 : *
13506 : * The currently supported options are:
13507 : * <ul>
13508 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13509 : * output.
13510 : * </li>
13511 : * </ul>
13512 : *
13513 : * This is the same as
13514 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13515 : * function.
13516 : *
13517 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13518 : * If only the array itself needs to be freed, CPLFree() should be called
13519 : * (and GDALMDArrayRelease() on individual array members).
13520 : *
13521 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13522 : *
13523 : * @param pahInputArrays Input arrays
13524 : * @param nCountInputArrays Number of input arrays
13525 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13526 : * @param papszOptions NULL, or NULL terminated list of options.
13527 : *
13528 : * @return an array of *pnCountOutputArrays arrays.
13529 : * @since 3.10
13530 : */
13531 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13532 : size_t nCountInputArrays,
13533 : size_t *pnCountOutputArrays,
13534 : CSLConstList papszOptions)
13535 : {
13536 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13537 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13538 :
13539 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13540 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13541 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13542 :
13543 : const auto apoOutputArrays =
13544 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13545 : auto ret = static_cast<GDALMDArrayH *>(
13546 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13547 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13548 : {
13549 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13550 : }
13551 7 : *pnCountOutputArrays = apoOutputArrays.size();
13552 7 : return ret;
13553 : }
13554 :
13555 : /************************************************************************/
13556 : /* GDALReleaseArrays() */
13557 : /************************************************************************/
13558 :
13559 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13560 : *
13561 : * @param arrays return pointer of above methods
13562 : * @param nCount *pnCount value returned by above methods
13563 : */
13564 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13565 : {
13566 46 : for (size_t i = 0; i < nCount; i++)
13567 : {
13568 26 : delete arrays[i];
13569 : }
13570 20 : CPLFree(arrays);
13571 20 : }
13572 :
13573 : /************************************************************************/
13574 : /* GDALMDArrayCache() */
13575 : /************************************************************************/
13576 :
13577 : /**
13578 : * \brief Cache the content of the array into an auxiliary filename.
13579 : *
13580 : * This is the same as the C++ method GDALMDArray::Cache().
13581 : *
13582 : * @since GDAL 3.4
13583 : */
13584 :
13585 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13586 : {
13587 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13588 7 : return hArray->m_poImpl->Cache(papszOptions);
13589 : }
13590 :
13591 : /************************************************************************/
13592 : /* GDALMDArrayRename() */
13593 : /************************************************************************/
13594 :
13595 : /** Rename the array.
13596 : *
13597 : * This is not implemented by all drivers.
13598 : *
13599 : * Drivers known to implement it: MEM, netCDF, Zarr.
13600 : *
13601 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13602 : *
13603 : * @return true in case of success
13604 : * @since GDAL 3.8
13605 : */
13606 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13607 : {
13608 28 : VALIDATE_POINTER1(hArray, __func__, false);
13609 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13610 28 : return hArray->m_poImpl->Rename(pszNewName);
13611 : }
13612 :
13613 : /************************************************************************/
13614 : /* GDALAttributeRelease() */
13615 : /************************************************************************/
13616 :
13617 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13618 : *
13619 : * Note: when applied on a object coming from a driver, this does not
13620 : * destroy the object in the file, database, etc...
13621 : */
13622 818 : void GDALAttributeRelease(GDALAttributeH hAttr)
13623 : {
13624 818 : delete hAttr;
13625 818 : }
13626 :
13627 : /************************************************************************/
13628 : /* GDALAttributeGetName() */
13629 : /************************************************************************/
13630 :
13631 : /** Return the name of the attribute.
13632 : *
13633 : * The returned pointer is valid until hAttr is released.
13634 : *
13635 : * This is the same as the C++ method GDALAttribute::GetName().
13636 : */
13637 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13638 : {
13639 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13640 361 : return hAttr->m_poImpl->GetName().c_str();
13641 : }
13642 :
13643 : /************************************************************************/
13644 : /* GDALAttributeGetFullName() */
13645 : /************************************************************************/
13646 :
13647 : /** Return the full name of the attribute.
13648 : *
13649 : * The returned pointer is valid until hAttr is released.
13650 : *
13651 : * This is the same as the C++ method GDALAttribute::GetFullName().
13652 : */
13653 50 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13654 : {
13655 50 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13656 50 : return hAttr->m_poImpl->GetFullName().c_str();
13657 : }
13658 :
13659 : /************************************************************************/
13660 : /* GDALAttributeGetTotalElementsCount() */
13661 : /************************************************************************/
13662 :
13663 : /** Return the total number of values in the attribute.
13664 : *
13665 : * This is the same as the C++ method
13666 : * GDALAbstractMDArray::GetTotalElementsCount()
13667 : */
13668 190 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13669 : {
13670 190 : VALIDATE_POINTER1(hAttr, __func__, 0);
13671 190 : return hAttr->m_poImpl->GetTotalElementsCount();
13672 : }
13673 :
13674 : /************************************************************************/
13675 : /* GDALAttributeGetDimensionCount() */
13676 : /************************************************************************/
13677 :
13678 : /** Return the number of dimensions.
13679 : *
13680 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13681 : */
13682 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13683 : {
13684 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13685 12 : return hAttr->m_poImpl->GetDimensionCount();
13686 : }
13687 :
13688 : /************************************************************************/
13689 : /* GDALAttributeGetDimensionsSize() */
13690 : /************************************************************************/
13691 :
13692 : /** Return the dimension sizes of the attribute.
13693 : *
13694 : * The returned array must be freed with CPLFree()
13695 : *
13696 : * @param hAttr Attribute.
13697 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13698 : *
13699 : * @return an array of *pnCount values.
13700 : */
13701 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13702 : {
13703 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13704 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13705 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13706 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13707 22 : for (size_t i = 0; i < dims.size(); i++)
13708 : {
13709 11 : ret[i] = dims[i]->GetSize();
13710 : }
13711 11 : *pnCount = dims.size();
13712 11 : return ret;
13713 : }
13714 :
13715 : /************************************************************************/
13716 : /* GDALAttributeGetDataType() */
13717 : /************************************************************************/
13718 :
13719 : /** Return the data type
13720 : *
13721 : * The return must be freed with GDALExtendedDataTypeRelease().
13722 : */
13723 483 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13724 : {
13725 483 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13726 : return new GDALExtendedDataTypeHS(
13727 483 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13728 : }
13729 :
13730 : /************************************************************************/
13731 : /* GDALAttributeReadAsRaw() */
13732 : /************************************************************************/
13733 :
13734 : /** Return the raw value of an attribute.
13735 : *
13736 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13737 : *
13738 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13739 : *
13740 : * @param hAttr Attribute.
13741 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13742 : *
13743 : * @return a buffer of *pnSize bytes.
13744 : */
13745 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13746 : {
13747 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13748 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13749 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13750 6 : *pnSize = res.size();
13751 6 : auto ret = res.StealData();
13752 6 : if (!ret)
13753 : {
13754 0 : *pnSize = 0;
13755 0 : return nullptr;
13756 : }
13757 6 : return ret;
13758 : }
13759 :
13760 : /************************************************************************/
13761 : /* GDALAttributeFreeRawResult() */
13762 : /************************************************************************/
13763 :
13764 : /** Free the return of GDALAttributeAsRaw()
13765 : */
13766 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13767 : CPL_UNUSED size_t nSize)
13768 : {
13769 6 : VALIDATE_POINTER0(hAttr, __func__);
13770 6 : if (raw)
13771 : {
13772 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13773 6 : const auto nDTSize(dt.GetSize());
13774 6 : GByte *pabyPtr = raw;
13775 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13776 6 : CPLAssert(nSize == nDTSize * nEltCount);
13777 12 : for (size_t i = 0; i < nEltCount; ++i)
13778 : {
13779 6 : dt.FreeDynamicMemory(pabyPtr);
13780 6 : pabyPtr += nDTSize;
13781 : }
13782 6 : CPLFree(raw);
13783 : }
13784 : }
13785 :
13786 : /************************************************************************/
13787 : /* GDALAttributeReadAsString() */
13788 : /************************************************************************/
13789 :
13790 : /** Return the value of an attribute as a string.
13791 : *
13792 : * The returned string should not be freed, and its lifetime does not
13793 : * excess a next call to ReadAsString() on the same object, or the deletion
13794 : * of the object itself.
13795 : *
13796 : * This function will only return the first element if there are several.
13797 : *
13798 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13799 : *
13800 : * @return a string, or nullptr.
13801 : */
13802 115 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13803 : {
13804 115 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13805 115 : return hAttr->m_poImpl->ReadAsString();
13806 : }
13807 :
13808 : /************************************************************************/
13809 : /* GDALAttributeReadAsInt() */
13810 : /************************************************************************/
13811 :
13812 : /** Return the value of an attribute as a integer.
13813 : *
13814 : * This function will only return the first element if there are several.
13815 : *
13816 : * It can fail if its value can not be converted to integer.
13817 : *
13818 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13819 : *
13820 : * @return a integer, or INT_MIN in case of error.
13821 : */
13822 25 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13823 : {
13824 25 : VALIDATE_POINTER1(hAttr, __func__, 0);
13825 25 : return hAttr->m_poImpl->ReadAsInt();
13826 : }
13827 :
13828 : /************************************************************************/
13829 : /* GDALAttributeReadAsInt64() */
13830 : /************************************************************************/
13831 :
13832 : /** Return the value of an attribute as a int64_t.
13833 : *
13834 : * This function will only return the first element if there are several.
13835 : *
13836 : * It can fail if its value can not be converted to integer.
13837 : *
13838 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13839 : *
13840 : * @return an int64_t, or INT64_MIN in case of error.
13841 : */
13842 27 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13843 : {
13844 27 : VALIDATE_POINTER1(hAttr, __func__, 0);
13845 27 : return hAttr->m_poImpl->ReadAsInt64();
13846 : }
13847 :
13848 : /************************************************************************/
13849 : /* GDALAttributeReadAsDouble() */
13850 : /************************************************************************/
13851 :
13852 : /** Return the value of an attribute as a double.
13853 : *
13854 : * This function will only return the first element if there are several.
13855 : *
13856 : * It can fail if its value can not be converted to double.
13857 : *
13858 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13859 : *
13860 : * @return a double value.
13861 : */
13862 55 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13863 : {
13864 55 : VALIDATE_POINTER1(hAttr, __func__, 0);
13865 55 : return hAttr->m_poImpl->ReadAsDouble();
13866 : }
13867 :
13868 : /************************************************************************/
13869 : /* GDALAttributeReadAsStringArray() */
13870 : /************************************************************************/
13871 :
13872 : /** Return the value of an attribute as an array of strings.
13873 : *
13874 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13875 : *
13876 : * The return value must be freed with CSLDestroy().
13877 : */
13878 22 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13879 : {
13880 22 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13881 22 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13882 : }
13883 :
13884 : /************************************************************************/
13885 : /* GDALAttributeReadAsIntArray() */
13886 : /************************************************************************/
13887 :
13888 : /** Return the value of an attribute as an array of integers.
13889 : *
13890 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13891 : *
13892 : * @param hAttr Attribute
13893 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13894 : * @return array to be freed with CPLFree(), or nullptr.
13895 : */
13896 18 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13897 : {
13898 18 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13899 18 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13900 18 : *pnCount = 0;
13901 36 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13902 18 : if (tmp.empty())
13903 0 : return nullptr;
13904 18 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13905 18 : if (!ret)
13906 0 : return nullptr;
13907 18 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13908 18 : *pnCount = tmp.size();
13909 18 : return ret;
13910 : }
13911 :
13912 : /************************************************************************/
13913 : /* GDALAttributeReadAsInt64Array() */
13914 : /************************************************************************/
13915 :
13916 : /** Return the value of an attribute as an array of int64_t.
13917 : *
13918 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13919 : *
13920 : * @param hAttr Attribute
13921 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13922 : * @return array to be freed with CPLFree(), or nullptr.
13923 : */
13924 26 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13925 : {
13926 26 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13927 26 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13928 26 : *pnCount = 0;
13929 52 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13930 26 : if (tmp.empty())
13931 0 : return nullptr;
13932 : auto ret = static_cast<int64_t *>(
13933 26 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13934 26 : if (!ret)
13935 0 : return nullptr;
13936 26 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13937 26 : *pnCount = tmp.size();
13938 26 : return ret;
13939 : }
13940 :
13941 : /************************************************************************/
13942 : /* GDALAttributeReadAsDoubleArray() */
13943 : /************************************************************************/
13944 :
13945 : /** Return the value of an attribute as an array of doubles.
13946 : *
13947 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13948 : *
13949 : * @param hAttr Attribute
13950 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13951 : * @return array to be freed with CPLFree(), or nullptr.
13952 : */
13953 44 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13954 : {
13955 44 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13956 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13957 44 : *pnCount = 0;
13958 88 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13959 44 : if (tmp.empty())
13960 0 : return nullptr;
13961 : auto ret =
13962 44 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13963 44 : if (!ret)
13964 0 : return nullptr;
13965 44 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13966 44 : *pnCount = tmp.size();
13967 44 : return ret;
13968 : }
13969 :
13970 : /************************************************************************/
13971 : /* GDALAttributeWriteRaw() */
13972 : /************************************************************************/
13973 :
13974 : /** Write an attribute from raw values expressed in GetDataType()
13975 : *
13976 : * The values should be provided in the type of GetDataType() and there should
13977 : * be exactly GetTotalElementsCount() of them.
13978 : * If GetDataType() is a string, each value should be a char* pointer.
13979 : *
13980 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13981 : *
13982 : * @param hAttr Attribute
13983 : * @param pabyValue Buffer of nLen bytes.
13984 : * @param nLength Size of pabyValue in bytes. Should be equal to
13985 : * GetTotalElementsCount() * GetDataType().GetSize()
13986 : * @return TRUE in case of success.
13987 : */
13988 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13989 : size_t nLength)
13990 : {
13991 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13992 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13993 : }
13994 :
13995 : /************************************************************************/
13996 : /* GDALAttributeWriteString() */
13997 : /************************************************************************/
13998 :
13999 : /** Write an attribute from a string value.
14000 : *
14001 : * Type conversion will be performed if needed. If the attribute contains
14002 : * multiple values, only the first one will be updated.
14003 : *
14004 : * This is the same as the C++ method GDALAttribute::Write(const char*)
14005 : *
14006 : * @param hAttr Attribute
14007 : * @param pszVal Pointer to a string.
14008 : * @return TRUE in case of success.
14009 : */
14010 210 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
14011 : {
14012 210 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14013 210 : return hAttr->m_poImpl->Write(pszVal);
14014 : }
14015 :
14016 : /************************************************************************/
14017 : /* GDALAttributeWriteInt() */
14018 : /************************************************************************/
14019 :
14020 : /** Write an attribute from a integer value.
14021 : *
14022 : * Type conversion will be performed if needed. If the attribute contains
14023 : * multiple values, only the first one will be updated.
14024 : *
14025 : * This is the same as the C++ method GDALAttribute::WriteInt()
14026 : *
14027 : * @param hAttr Attribute
14028 : * @param nVal Value.
14029 : * @return TRUE in case of success.
14030 : */
14031 23 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
14032 : {
14033 23 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14034 23 : return hAttr->m_poImpl->WriteInt(nVal);
14035 : }
14036 :
14037 : /************************************************************************/
14038 : /* GDALAttributeWriteInt64() */
14039 : /************************************************************************/
14040 :
14041 : /** Write an attribute from an int64_t value.
14042 : *
14043 : * Type conversion will be performed if needed. If the attribute contains
14044 : * multiple values, only the first one will be updated.
14045 : *
14046 : * This is the same as the C++ method GDALAttribute::WriteLong()
14047 : *
14048 : * @param hAttr Attribute
14049 : * @param nVal Value.
14050 : * @return TRUE in case of success.
14051 : */
14052 14 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
14053 : {
14054 14 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14055 14 : return hAttr->m_poImpl->WriteInt64(nVal);
14056 : }
14057 :
14058 : /************************************************************************/
14059 : /* GDALAttributeWriteDouble() */
14060 : /************************************************************************/
14061 :
14062 : /** Write an attribute from a double value.
14063 : *
14064 : * Type conversion will be performed if needed. If the attribute contains
14065 : * multiple values, only the first one will be updated.
14066 : *
14067 : * This is the same as the C++ method GDALAttribute::Write(double);
14068 : *
14069 : * @param hAttr Attribute
14070 : * @param dfVal Value.
14071 : *
14072 : * @return TRUE in case of success.
14073 : */
14074 12 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
14075 : {
14076 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14077 12 : return hAttr->m_poImpl->Write(dfVal);
14078 : }
14079 :
14080 : /************************************************************************/
14081 : /* GDALAttributeWriteStringArray() */
14082 : /************************************************************************/
14083 :
14084 : /** Write an attribute from an array of strings.
14085 : *
14086 : * Type conversion will be performed if needed.
14087 : *
14088 : * Exactly GetTotalElementsCount() strings must be provided
14089 : *
14090 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
14091 : *
14092 : * @param hAttr Attribute
14093 : * @param papszValues Array of strings.
14094 : * @return TRUE in case of success.
14095 : */
14096 9 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
14097 : CSLConstList papszValues)
14098 : {
14099 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14100 9 : return hAttr->m_poImpl->Write(papszValues);
14101 : }
14102 :
14103 : /************************************************************************/
14104 : /* GDALAttributeWriteIntArray() */
14105 : /************************************************************************/
14106 :
14107 : /** Write an attribute from an array of int.
14108 : *
14109 : * Type conversion will be performed if needed.
14110 : *
14111 : * Exactly GetTotalElementsCount() strings must be provided
14112 : *
14113 : * This is the same as the C++ method GDALAttribute::Write(const int *,
14114 : * size_t)
14115 : *
14116 : * @param hAttr Attribute
14117 : * @param panValues Array of int.
14118 : * @param nCount Should be equal to GetTotalElementsCount().
14119 : * @return TRUE in case of success.
14120 : */
14121 12 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14122 : size_t nCount)
14123 : {
14124 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14125 12 : return hAttr->m_poImpl->Write(panValues, nCount);
14126 : }
14127 :
14128 : /************************************************************************/
14129 : /* GDALAttributeWriteInt64Array() */
14130 : /************************************************************************/
14131 :
14132 : /** Write an attribute from an array of int64_t.
14133 : *
14134 : * Type conversion will be performed if needed.
14135 : *
14136 : * Exactly GetTotalElementsCount() strings must be provided
14137 : *
14138 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14139 : * size_t)
14140 : *
14141 : * @param hAttr Attribute
14142 : * @param panValues Array of int64_t.
14143 : * @param nCount Should be equal to GetTotalElementsCount().
14144 : * @return TRUE in case of success.
14145 : */
14146 13 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14147 : size_t nCount)
14148 : {
14149 13 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14150 13 : return hAttr->m_poImpl->Write(panValues, nCount);
14151 : }
14152 :
14153 : /************************************************************************/
14154 : /* GDALAttributeWriteDoubleArray() */
14155 : /************************************************************************/
14156 :
14157 : /** Write an attribute from an array of double.
14158 : *
14159 : * Type conversion will be performed if needed.
14160 : *
14161 : * Exactly GetTotalElementsCount() strings must be provided
14162 : *
14163 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14164 : * size_t)
14165 : *
14166 : * @param hAttr Attribute
14167 : * @param padfValues Array of double.
14168 : * @param nCount Should be equal to GetTotalElementsCount().
14169 : * @return TRUE in case of success.
14170 : */
14171 8 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14172 : const double *padfValues, size_t nCount)
14173 : {
14174 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14175 8 : return hAttr->m_poImpl->Write(padfValues, nCount);
14176 : }
14177 :
14178 : /************************************************************************/
14179 : /* GDALAttributeRename() */
14180 : /************************************************************************/
14181 :
14182 : /** Rename the attribute.
14183 : *
14184 : * This is not implemented by all drivers.
14185 : *
14186 : * Drivers known to implement it: MEM, netCDF.
14187 : *
14188 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14189 : *
14190 : * @return true in case of success
14191 : * @since GDAL 3.8
14192 : */
14193 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14194 : {
14195 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14196 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14197 27 : return hAttr->m_poImpl->Rename(pszNewName);
14198 : }
14199 :
14200 : /************************************************************************/
14201 : /* GDALDimensionRelease() */
14202 : /************************************************************************/
14203 :
14204 : /** Release the GDAL in-memory object associated with a GDALDimension.
14205 : *
14206 : * Note: when applied on a object coming from a driver, this does not
14207 : * destroy the object in the file, database, etc...
14208 : */
14209 6612 : void GDALDimensionRelease(GDALDimensionH hDim)
14210 : {
14211 6612 : delete hDim;
14212 6612 : }
14213 :
14214 : /************************************************************************/
14215 : /* GDALDimensionGetName() */
14216 : /************************************************************************/
14217 :
14218 : /** Return dimension name.
14219 : *
14220 : * This is the same as the C++ method GDALDimension::GetName()
14221 : */
14222 296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14223 : {
14224 296 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14225 296 : return hDim->m_poImpl->GetName().c_str();
14226 : }
14227 :
14228 : /************************************************************************/
14229 : /* GDALDimensionGetFullName() */
14230 : /************************************************************************/
14231 :
14232 : /** Return dimension full name.
14233 : *
14234 : * This is the same as the C++ method GDALDimension::GetFullName()
14235 : */
14236 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14237 : {
14238 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14239 82 : return hDim->m_poImpl->GetFullName().c_str();
14240 : }
14241 :
14242 : /************************************************************************/
14243 : /* GDALDimensionGetType() */
14244 : /************************************************************************/
14245 :
14246 : /** Return dimension type.
14247 : *
14248 : * This is the same as the C++ method GDALDimension::GetType()
14249 : */
14250 70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14251 : {
14252 70 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14253 70 : return hDim->m_poImpl->GetType().c_str();
14254 : }
14255 :
14256 : /************************************************************************/
14257 : /* GDALDimensionGetDirection() */
14258 : /************************************************************************/
14259 :
14260 : /** Return dimension direction.
14261 : *
14262 : * This is the same as the C++ method GDALDimension::GetDirection()
14263 : */
14264 38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14265 : {
14266 38 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14267 38 : return hDim->m_poImpl->GetDirection().c_str();
14268 : }
14269 :
14270 : /************************************************************************/
14271 : /* GDALDimensionGetSize() */
14272 : /************************************************************************/
14273 :
14274 : /** Return the size, that is the number of values along the dimension.
14275 : *
14276 : * This is the same as the C++ method GDALDimension::GetSize()
14277 : */
14278 5240 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14279 : {
14280 5240 : VALIDATE_POINTER1(hDim, __func__, 0);
14281 5240 : return hDim->m_poImpl->GetSize();
14282 : }
14283 :
14284 : /************************************************************************/
14285 : /* GDALDimensionGetIndexingVariable() */
14286 : /************************************************************************/
14287 :
14288 : /** Return the variable that is used to index the dimension (if there is one).
14289 : *
14290 : * This is the array, typically one-dimensional, describing the values taken
14291 : * by the dimension.
14292 : *
14293 : * The returned value should be freed with GDALMDArrayRelease().
14294 : *
14295 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14296 : */
14297 140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14298 : {
14299 140 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14300 280 : auto var(hDim->m_poImpl->GetIndexingVariable());
14301 140 : if (!var)
14302 11 : return nullptr;
14303 129 : return new GDALMDArrayHS(var);
14304 : }
14305 :
14306 : /************************************************************************/
14307 : /* GDALDimensionSetIndexingVariable() */
14308 : /************************************************************************/
14309 :
14310 : /** Set the variable that is used to index the dimension.
14311 : *
14312 : * This is the array, typically one-dimensional, describing the values taken
14313 : * by the dimension.
14314 : *
14315 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14316 : *
14317 : * @return TRUE in case of success.
14318 : */
14319 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14320 : {
14321 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14322 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14323 46 : : nullptr);
14324 : }
14325 :
14326 : /************************************************************************/
14327 : /* GDALDimensionRename() */
14328 : /************************************************************************/
14329 :
14330 : /** Rename the dimension.
14331 : *
14332 : * This is not implemented by all drivers.
14333 : *
14334 : * Drivers known to implement it: MEM, netCDF.
14335 : *
14336 : * This is the same as the C++ method GDALDimension::Rename()
14337 : *
14338 : * @return true in case of success
14339 : * @since GDAL 3.8
14340 : */
14341 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14342 : {
14343 31 : VALIDATE_POINTER1(hDim, __func__, false);
14344 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14345 31 : return hDim->m_poImpl->Rename(pszNewName);
14346 : }
14347 :
14348 : /************************************************************************/
14349 : /* GDALDatasetGetRootGroup() */
14350 : /************************************************************************/
14351 :
14352 : /** Return the root GDALGroup of this dataset.
14353 : *
14354 : * Only valid for multidimensional datasets.
14355 : *
14356 : * The returned value must be freed with GDALGroupRelease().
14357 : *
14358 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14359 : *
14360 : * @since GDAL 3.1
14361 : */
14362 1933 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14363 : {
14364 1933 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14365 1933 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14366 1933 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14367 : }
14368 :
14369 : /************************************************************************/
14370 : /* GDALRasterBandAsMDArray() */
14371 : /************************************************************************/
14372 :
14373 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14374 : *
14375 : * The band must be linked to a GDALDataset. If this dataset is not already
14376 : * marked as shared, it will be, so that the returned array holds a reference
14377 : * to it.
14378 : *
14379 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14380 : * returned array will have an associated indexing variable.
14381 : *
14382 : * The returned pointer must be released with GDALMDArrayRelease().
14383 : *
14384 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14385 : *
14386 : * @return a new array, or NULL.
14387 : *
14388 : * @since GDAL 3.1
14389 : */
14390 25 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14391 : {
14392 25 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14393 50 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14394 25 : if (!poArray)
14395 0 : return nullptr;
14396 25 : return new GDALMDArrayHS(poArray);
14397 : }
14398 :
14399 : /************************************************************************/
14400 : /* GDALDatasetAsMDArray() */
14401 : /************************************************************************/
14402 :
14403 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14404 : *
14405 : * If this dataset is not already marked as shared, it will be, so that the
14406 : * returned array holds a reference to it.
14407 : *
14408 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14409 : * returned array will have an associated indexing variable.
14410 : *
14411 : * The currently supported list of options is:
14412 : * <ul>
14413 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14414 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14415 : * and the last (fastest changing direction) is X
14416 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14417 : * and the last (fastest changing direction) is Band.
14418 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14419 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14420 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14421 : * "Y,X,Band" is use.
14422 : * </li>
14423 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14424 : * item from which to build the band indexing variable.
14425 : * <ul>
14426 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14427 : * <li>"{None}" means that no band indexing variable must be created.</li>
14428 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14429 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14430 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14431 : * </ul>
14432 : * </li>
14433 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14434 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14435 : * Defaults to String.
14436 : * </li>
14437 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14438 : * Defaults to "Band".
14439 : * </li>
14440 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14441 : * </li>
14442 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14443 : * </li>
14444 : * </ul>
14445 : *
14446 : * The returned pointer must be released with GDALMDArrayRelease().
14447 : *
14448 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14449 : * GDALDataset::AsMDArray()
14450 : *
14451 : * This is the same as the C++ method GDALDataset::AsMDArray().
14452 : *
14453 : * @param hDS Dataset handle.
14454 : * @param papszOptions Null-terminated list of strings, or nullptr.
14455 : * @return a new array, or NULL.
14456 : *
14457 : * @since GDAL 3.12
14458 : */
14459 15 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14460 : {
14461 15 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14462 30 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14463 15 : if (!poArray)
14464 3 : return nullptr;
14465 12 : return new GDALMDArrayHS(poArray);
14466 : }
14467 :
14468 : /************************************************************************/
14469 : /* GDALMDArrayAsClassicDataset() */
14470 : /************************************************************************/
14471 :
14472 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14473 : *
14474 : * Only 2D or more arrays are supported.
14475 : *
14476 : * In the case of > 2D arrays, additional dimensions will be represented as
14477 : * raster bands.
14478 : *
14479 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14480 : * GDALDataset::AsMDArray()
14481 : *
14482 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14483 : *
14484 : * @param hArray Array.
14485 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14486 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14487 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14488 : */
14489 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14490 : size_t iYDim)
14491 : {
14492 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14493 0 : return GDALDataset::ToHandle(
14494 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14495 : }
14496 :
14497 : /************************************************************************/
14498 : /* GDALMDArrayAsClassicDatasetEx() */
14499 : /************************************************************************/
14500 :
14501 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14502 : *
14503 : * Only 2D or more arrays are supported.
14504 : *
14505 : * In the case of > 2D arrays, additional dimensions will be represented as
14506 : * raster bands.
14507 : *
14508 : * The "reverse" method is GDALRasterBand::AsMDArray().
14509 : *
14510 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14511 : * @param hArray Array.
14512 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14513 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14514 : * Ignored if the dimension count is 1.
14515 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14516 : * BAND_IMAGERY_METADATA option.
14517 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14518 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14519 : * @since GDAL 3.8
14520 : */
14521 103 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14522 : size_t iYDim, GDALGroupH hRootGroup,
14523 : CSLConstList papszOptions)
14524 : {
14525 103 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14526 206 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14527 206 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14528 206 : papszOptions));
14529 : }
14530 :
14531 : //! @cond Doxygen_Suppress
14532 :
14533 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14534 : const std::string &osName,
14535 : const std::string &osValue,
14536 180 : GDALExtendedDataTypeSubType eSubType)
14537 : : GDALAbstractMDArray(osParentName, osName),
14538 : GDALAttribute(osParentName, osName),
14539 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14540 : {
14541 180 : }
14542 :
14543 : const std::vector<std::shared_ptr<GDALDimension>> &
14544 30 : GDALAttributeString::GetDimensions() const
14545 : {
14546 30 : return m_dims;
14547 : }
14548 :
14549 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14550 : {
14551 21 : return m_dt;
14552 : }
14553 :
14554 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14555 : const GPtrDiff_t *,
14556 : const GDALExtendedDataType &bufferDataType,
14557 : void *pDstBuffer) const
14558 : {
14559 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14560 0 : return false;
14561 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14562 10 : if (!pszStr)
14563 0 : return false;
14564 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14565 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14566 10 : return true;
14567 : }
14568 :
14569 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14570 : const std::string &osName,
14571 66 : double dfValue)
14572 : : GDALAbstractMDArray(osParentName, osName),
14573 : GDALAttribute(osParentName, osName),
14574 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14575 : {
14576 66 : }
14577 :
14578 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14579 : const std::string &osName,
14580 27 : int nValue)
14581 : : GDALAbstractMDArray(osParentName, osName),
14582 : GDALAttribute(osParentName, osName),
14583 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14584 : {
14585 27 : }
14586 :
14587 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14588 : const std::string &osName,
14589 7 : const std::vector<GUInt32> &anValues)
14590 : : GDALAbstractMDArray(osParentName, osName),
14591 : GDALAttribute(osParentName, osName),
14592 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14593 : {
14594 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14595 14 : std::string(), "dim0", std::string(), std::string(),
14596 7 : m_anValuesUInt32.size()));
14597 7 : }
14598 :
14599 : const std::vector<std::shared_ptr<GDALDimension>> &
14600 14 : GDALAttributeNumeric::GetDimensions() const
14601 : {
14602 14 : return m_dims;
14603 : }
14604 :
14605 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14606 : {
14607 8 : return m_dt;
14608 : }
14609 :
14610 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14611 : const size_t *count, const GInt64 *arrayStep,
14612 : const GPtrDiff_t *bufferStride,
14613 : const GDALExtendedDataType &bufferDataType,
14614 : void *pDstBuffer) const
14615 : {
14616 4 : if (m_dims.empty())
14617 : {
14618 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14619 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14620 : bufferDataType);
14621 : else
14622 : {
14623 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14624 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14625 : bufferDataType);
14626 : }
14627 : }
14628 : else
14629 : {
14630 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14631 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14632 30 : for (size_t i = 0; i < count[0]; ++i)
14633 : {
14634 29 : GDALExtendedDataType::CopyValue(
14635 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14636 29 : i * arrayStep[0])],
14637 29 : m_dt, pabyDstBuffer, bufferDataType);
14638 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14639 : }
14640 : }
14641 4 : return true;
14642 : }
14643 :
14644 380 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14645 : const std::string &osParentName, const std::string &osName,
14646 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14647 380 : double dfIncrement, double dfOffsetInIncrement)
14648 : : GDALAbstractMDArray(osParentName, osName),
14649 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14650 : m_dfIncrement(dfIncrement),
14651 760 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14652 : {
14653 380 : }
14654 :
14655 380 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14656 : const std::string &osParentName, const std::string &osName,
14657 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14658 : double dfIncrement, double dfOffsetInIncrement)
14659 : {
14660 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14661 380 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14662 380 : poArray->SetSelf(poArray);
14663 380 : return poArray;
14664 : }
14665 :
14666 : const std::vector<std::shared_ptr<GDALDimension>> &
14667 1876 : GDALMDArrayRegularlySpaced::GetDimensions() const
14668 : {
14669 1876 : return m_dims;
14670 : }
14671 :
14672 679 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14673 : {
14674 679 : return m_dt;
14675 : }
14676 :
14677 : std::vector<std::shared_ptr<GDALAttribute>>
14678 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14679 : {
14680 4 : return m_attributes;
14681 : }
14682 :
14683 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14684 : const std::shared_ptr<GDALAttribute> &poAttr)
14685 : {
14686 0 : m_attributes.emplace_back(poAttr);
14687 0 : }
14688 :
14689 248 : bool GDALMDArrayRegularlySpaced::IRead(
14690 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14691 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14692 : void *pDstBuffer) const
14693 : {
14694 248 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14695 4690 : for (size_t i = 0; i < count[0]; i++)
14696 : {
14697 4442 : const double dfVal =
14698 4442 : m_dfStart +
14699 4442 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14700 4442 : m_dfOffsetInIncrement) *
14701 4442 : m_dfIncrement;
14702 4442 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14703 : bufferDataType);
14704 4442 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14705 : }
14706 248 : return true;
14707 : }
14708 :
14709 268 : bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
14710 : double &dfIncrement) const
14711 : {
14712 268 : dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
14713 268 : dfIncrement = m_dfIncrement;
14714 268 : return true;
14715 : }
14716 :
14717 6213 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14718 : const std::string &osParentName, const std::string &osName,
14719 6213 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14720 6213 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14721 : {
14722 6213 : }
14723 :
14724 : std::shared_ptr<GDALMDArray>
14725 1898 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14726 : {
14727 1898 : return m_poIndexingVariable.lock();
14728 : }
14729 :
14730 : // cppcheck-suppress passedByValue
14731 942 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14732 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14733 : {
14734 942 : m_poIndexingVariable = poIndexingVariable;
14735 942 : return true;
14736 : }
14737 :
14738 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14739 : {
14740 33 : m_nSize = nNewSize;
14741 33 : }
14742 :
14743 : /************************************************************************/
14744 : /* GDALPamMultiDim::Private */
14745 : /************************************************************************/
14746 :
14747 : struct GDALPamMultiDim::Private
14748 : {
14749 : std::string m_osFilename{};
14750 : std::string m_osPamFilename{};
14751 :
14752 : struct Statistics
14753 : {
14754 : bool bHasStats = false;
14755 : bool bApproxStats = false;
14756 : double dfMin = 0;
14757 : double dfMax = 0;
14758 : double dfMean = 0;
14759 : double dfStdDev = 0;
14760 : GUInt64 nValidCount = 0;
14761 : };
14762 :
14763 : struct ArrayInfo
14764 : {
14765 : std::shared_ptr<OGRSpatialReference> poSRS{};
14766 : // cppcheck-suppress unusedStructMember
14767 : Statistics stats{};
14768 : };
14769 :
14770 : typedef std::pair<std::string, std::string> NameContext;
14771 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14772 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14773 : bool m_bDirty = false;
14774 : bool m_bLoaded = false;
14775 : };
14776 :
14777 : /************************************************************************/
14778 : /* GDALPamMultiDim */
14779 : /************************************************************************/
14780 :
14781 2516 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14782 2516 : : d(new Private())
14783 : {
14784 2516 : d->m_osFilename = osFilename;
14785 2516 : }
14786 :
14787 : /************************************************************************/
14788 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14789 : /************************************************************************/
14790 :
14791 2516 : GDALPamMultiDim::~GDALPamMultiDim()
14792 : {
14793 2516 : if (d->m_bDirty)
14794 30 : Save();
14795 2516 : }
14796 :
14797 : /************************************************************************/
14798 : /* GDALPamMultiDim::Load() */
14799 : /************************************************************************/
14800 :
14801 116 : void GDALPamMultiDim::Load()
14802 : {
14803 116 : if (d->m_bLoaded)
14804 105 : return;
14805 47 : d->m_bLoaded = true;
14806 :
14807 47 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14808 47 : d->m_osPamFilename =
14809 94 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14810 47 : CPLXMLTreeCloser oTree(nullptr);
14811 : {
14812 94 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14813 47 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14814 : }
14815 47 : if (!oTree)
14816 : {
14817 36 : return;
14818 : }
14819 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14820 11 : if (!poPAMMultiDim)
14821 0 : return;
14822 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14823 24 : psIter = psIter->psNext)
14824 : {
14825 24 : if (psIter->eType == CXT_Element &&
14826 24 : strcmp(psIter->pszValue, "Array") == 0)
14827 : {
14828 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14829 13 : if (!pszName)
14830 0 : continue;
14831 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14832 : const auto oKey =
14833 26 : std::pair<std::string, std::string>(pszName, pszContext);
14834 :
14835 : /* --------------------------------------------------------------------
14836 : */
14837 : /* Check for an SRS node. */
14838 : /* --------------------------------------------------------------------
14839 : */
14840 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14841 13 : if (psSRSNode)
14842 : {
14843 : std::shared_ptr<OGRSpatialReference> poSRS =
14844 6 : std::make_shared<OGRSpatialReference>();
14845 3 : poSRS->SetFromUserInput(
14846 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14847 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14848 3 : const char *pszMapping = CPLGetXMLValue(
14849 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14850 3 : if (pszMapping)
14851 : {
14852 : char **papszTokens =
14853 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14854 6 : std::vector<int> anMapping;
14855 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14856 : {
14857 6 : anMapping.push_back(atoi(papszTokens[i]));
14858 : }
14859 3 : CSLDestroy(papszTokens);
14860 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14861 : }
14862 : else
14863 : {
14864 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14865 : }
14866 :
14867 : const char *pszCoordinateEpoch =
14868 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14869 3 : if (pszCoordinateEpoch)
14870 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14871 :
14872 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14873 : }
14874 :
14875 : const CPLXMLNode *psStatistics =
14876 13 : CPLGetXMLNode(psIter, "Statistics");
14877 13 : if (psStatistics)
14878 : {
14879 7 : Private::Statistics sStats;
14880 7 : sStats.bHasStats = true;
14881 7 : sStats.bApproxStats = CPLTestBool(
14882 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14883 7 : sStats.dfMin =
14884 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14885 7 : sStats.dfMax =
14886 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14887 7 : sStats.dfMean =
14888 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14889 7 : sStats.dfStdDev =
14890 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14891 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14892 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14893 7 : d->m_oMapArray[oKey].stats = sStats;
14894 13 : }
14895 : }
14896 : else
14897 : {
14898 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14899 11 : psIter->psNext = nullptr;
14900 11 : d->m_apoOtherNodes.emplace_back(
14901 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14902 11 : psIter->psNext = psNextBackup;
14903 : }
14904 : }
14905 : }
14906 :
14907 : /************************************************************************/
14908 : /* GDALPamMultiDim::Save() */
14909 : /************************************************************************/
14910 :
14911 30 : void GDALPamMultiDim::Save()
14912 : {
14913 : CPLXMLTreeCloser oTree(
14914 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14915 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14916 : {
14917 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14918 : }
14919 112 : for (const auto &kv : d->m_oMapArray)
14920 : {
14921 : CPLXMLNode *psArrayNode =
14922 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14923 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14924 82 : if (!kv.first.second.empty())
14925 : {
14926 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14927 : kv.first.second.c_str());
14928 : }
14929 82 : if (kv.second.poSRS)
14930 : {
14931 71 : char *pszWKT = nullptr;
14932 : {
14933 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14934 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14935 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14936 : }
14937 : CPLXMLNode *psSRSNode =
14938 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14939 71 : CPLFree(pszWKT);
14940 : const auto &mapping =
14941 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14942 142 : CPLString osMapping;
14943 213 : for (size_t i = 0; i < mapping.size(); ++i)
14944 : {
14945 142 : if (!osMapping.empty())
14946 71 : osMapping += ",";
14947 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14948 : }
14949 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14950 : osMapping.c_str());
14951 :
14952 : const double dfCoordinateEpoch =
14953 71 : kv.second.poSRS->GetCoordinateEpoch();
14954 71 : if (dfCoordinateEpoch > 0)
14955 : {
14956 : std::string osCoordinateEpoch =
14957 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14958 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14959 : {
14960 6 : while (osCoordinateEpoch.back() == '0')
14961 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14962 : }
14963 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14964 : osCoordinateEpoch.c_str());
14965 : }
14966 : }
14967 :
14968 82 : if (kv.second.stats.bHasStats)
14969 : {
14970 : CPLXMLNode *psMDArray =
14971 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14972 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14973 8 : kv.second.stats.bApproxStats ? "1"
14974 : : "0");
14975 8 : CPLCreateXMLElementAndValue(
14976 : psMDArray, "Minimum",
14977 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14978 8 : CPLCreateXMLElementAndValue(
14979 : psMDArray, "Maximum",
14980 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14981 8 : CPLCreateXMLElementAndValue(
14982 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14983 8 : CPLCreateXMLElementAndValue(
14984 : psMDArray, "StdDev",
14985 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14986 8 : CPLCreateXMLElementAndValue(
14987 : psMDArray, "ValidSampleCount",
14988 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14989 : }
14990 : }
14991 :
14992 : int bSaved;
14993 60 : CPLErrorAccumulator oErrorAccumulator;
14994 : {
14995 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14996 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14997 : bSaved =
14998 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14999 : }
15000 :
15001 30 : const char *pszNewPam = nullptr;
15002 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
15003 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
15004 : {
15005 0 : CPLErrorReset();
15006 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
15007 : }
15008 : else
15009 : {
15010 30 : oErrorAccumulator.ReplayErrors();
15011 : }
15012 30 : }
15013 :
15014 : /************************************************************************/
15015 : /* GDALPamMultiDim::GetSpatialRef() */
15016 : /************************************************************************/
15017 :
15018 : std::shared_ptr<OGRSpatialReference>
15019 19 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
15020 : const std::string &osContext)
15021 : {
15022 19 : Load();
15023 : auto oIter =
15024 19 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15025 19 : if (oIter != d->m_oMapArray.end())
15026 2 : return oIter->second.poSRS;
15027 17 : return nullptr;
15028 : }
15029 :
15030 : /************************************************************************/
15031 : /* GDALPamMultiDim::SetSpatialRef() */
15032 : /************************************************************************/
15033 :
15034 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
15035 : const std::string &osContext,
15036 : const OGRSpatialReference *poSRS)
15037 : {
15038 72 : Load();
15039 72 : d->m_bDirty = true;
15040 72 : if (poSRS && !poSRS->IsEmpty())
15041 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
15042 : poSRS->Clone());
15043 : else
15044 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
15045 1 : .poSRS.reset();
15046 72 : }
15047 :
15048 : /************************************************************************/
15049 : /* GetStatistics() */
15050 : /************************************************************************/
15051 :
15052 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
15053 : const std::string &osContext,
15054 : bool bApproxOK, double *pdfMin,
15055 : double *pdfMax, double *pdfMean,
15056 : double *pdfStdDev, GUInt64 *pnValidCount)
15057 : {
15058 16 : Load();
15059 : auto oIter =
15060 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15061 16 : if (oIter == d->m_oMapArray.end())
15062 9 : return CE_Failure;
15063 7 : const auto &stats = oIter->second.stats;
15064 7 : if (!stats.bHasStats)
15065 1 : return CE_Failure;
15066 6 : if (!bApproxOK && stats.bApproxStats)
15067 0 : return CE_Failure;
15068 6 : if (pdfMin)
15069 6 : *pdfMin = stats.dfMin;
15070 6 : if (pdfMax)
15071 6 : *pdfMax = stats.dfMax;
15072 6 : if (pdfMean)
15073 6 : *pdfMean = stats.dfMean;
15074 6 : if (pdfStdDev)
15075 6 : *pdfStdDev = stats.dfStdDev;
15076 6 : if (pnValidCount)
15077 6 : *pnValidCount = stats.nValidCount;
15078 6 : return CE_None;
15079 : }
15080 :
15081 : /************************************************************************/
15082 : /* SetStatistics() */
15083 : /************************************************************************/
15084 :
15085 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
15086 : const std::string &osContext,
15087 : bool bApproxStats, double dfMin,
15088 : double dfMax, double dfMean,
15089 : double dfStdDev, GUInt64 nValidCount)
15090 : {
15091 8 : Load();
15092 8 : d->m_bDirty = true;
15093 : auto &stats =
15094 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
15095 8 : stats.bHasStats = true;
15096 8 : stats.bApproxStats = bApproxStats;
15097 8 : stats.dfMin = dfMin;
15098 8 : stats.dfMax = dfMax;
15099 8 : stats.dfMean = dfMean;
15100 8 : stats.dfStdDev = dfStdDev;
15101 8 : stats.nValidCount = nValidCount;
15102 8 : }
15103 :
15104 : /************************************************************************/
15105 : /* ClearStatistics() */
15106 : /************************************************************************/
15107 :
15108 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
15109 : const std::string &osContext)
15110 : {
15111 0 : Load();
15112 0 : d->m_bDirty = true;
15113 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
15114 : false;
15115 0 : }
15116 :
15117 : /************************************************************************/
15118 : /* ClearStatistics() */
15119 : /************************************************************************/
15120 :
15121 1 : void GDALPamMultiDim::ClearStatistics()
15122 : {
15123 1 : Load();
15124 1 : d->m_bDirty = true;
15125 3 : for (auto &kv : d->m_oMapArray)
15126 2 : kv.second.stats.bHasStats = false;
15127 1 : }
15128 :
15129 : /************************************************************************/
15130 : /* GetPAM() */
15131 : /************************************************************************/
15132 :
15133 : /*static*/ std::shared_ptr<GDALPamMultiDim>
15134 1463 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15135 : {
15136 1463 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15137 1463 : if (poPamArray)
15138 730 : return poPamArray->GetPAM();
15139 733 : return nullptr;
15140 : }
15141 :
15142 : /************************************************************************/
15143 : /* GDALPamMDArray */
15144 : /************************************************************************/
15145 :
15146 5891 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15147 : const std::string &osName,
15148 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15149 0 : const std::string &osContext)
15150 : :
15151 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15152 : GDALAbstractMDArray(osParentName, osName),
15153 : #endif
15154 5891 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15155 : {
15156 5891 : }
15157 :
15158 : /************************************************************************/
15159 : /* GDALPamMDArray::SetSpatialRef() */
15160 : /************************************************************************/
15161 :
15162 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15163 : {
15164 72 : if (!m_poPam)
15165 0 : return false;
15166 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15167 72 : return true;
15168 : }
15169 :
15170 : /************************************************************************/
15171 : /* GDALPamMDArray::GetSpatialRef() */
15172 : /************************************************************************/
15173 :
15174 19 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15175 : {
15176 19 : if (!m_poPam)
15177 0 : return nullptr;
15178 19 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15179 : }
15180 :
15181 : /************************************************************************/
15182 : /* GetStatistics() */
15183 : /************************************************************************/
15184 :
15185 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15186 : double *pdfMin, double *pdfMax,
15187 : double *pdfMean, double *pdfStdDev,
15188 : GUInt64 *pnValidCount,
15189 : GDALProgressFunc pfnProgress,
15190 : void *pProgressData)
15191 : {
15192 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15193 : bApproxOK, pdfMin, pdfMax, pdfMean,
15194 16 : pdfStdDev, pnValidCount) == CE_None)
15195 : {
15196 6 : return CE_None;
15197 : }
15198 10 : if (!bForce)
15199 4 : return CE_Warning;
15200 :
15201 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15202 : pdfMean, pdfStdDev, pnValidCount,
15203 6 : pfnProgress, pProgressData);
15204 : }
15205 :
15206 : /************************************************************************/
15207 : /* SetStatistics() */
15208 : /************************************************************************/
15209 :
15210 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15211 : double dfMax, double dfMean, double dfStdDev,
15212 : GUInt64 nValidCount,
15213 : CSLConstList /* papszOptions */)
15214 : {
15215 8 : if (!m_poPam)
15216 0 : return false;
15217 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15218 : dfMax, dfMean, dfStdDev, nValidCount);
15219 8 : return true;
15220 : }
15221 :
15222 : /************************************************************************/
15223 : /* ClearStatistics() */
15224 : /************************************************************************/
15225 :
15226 0 : void GDALPamMDArray::ClearStatistics()
15227 : {
15228 0 : if (!m_poPam)
15229 0 : return;
15230 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15231 : }
15232 :
15233 : /************************************************************************/
15234 : /* GDALMDIAsAttribute::GetDimensions() */
15235 : /************************************************************************/
15236 :
15237 : const std::vector<std::shared_ptr<GDALDimension>> &
15238 29 : GDALMDIAsAttribute::GetDimensions() const
15239 : {
15240 29 : return m_dims;
15241 : }
15242 :
15243 : /************************************************************************/
15244 : /* GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo() */
15245 : /************************************************************************/
15246 :
15247 64 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
15248 : {
15249 32 : clear();
15250 32 : }
15251 :
15252 : /************************************************************************/
15253 : /* GDALMDArrayRawBlockInfo::clear() */
15254 : /************************************************************************/
15255 :
15256 53 : void GDALMDArrayRawBlockInfo::clear()
15257 : {
15258 53 : CPLFree(pszFilename);
15259 53 : pszFilename = nullptr;
15260 53 : CSLDestroy(papszInfo);
15261 53 : papszInfo = nullptr;
15262 53 : nOffset = 0;
15263 53 : nSize = 0;
15264 53 : CPLFree(pabyInlineData);
15265 53 : pabyInlineData = nullptr;
15266 53 : }
15267 :
15268 : /************************************************************************/
15269 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15270 : /************************************************************************/
15271 :
15272 4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15273 4 : const GDALMDArrayRawBlockInfo &other)
15274 4 : : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
15275 4 : nOffset(other.nOffset), nSize(other.nSize),
15276 4 : papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
15277 : {
15278 4 : if (other.pabyInlineData)
15279 : {
15280 3 : pabyInlineData = static_cast<GByte *>(
15281 3 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15282 3 : if (pabyInlineData)
15283 3 : memcpy(pabyInlineData, other.pabyInlineData,
15284 3 : static_cast<size_t>(other.nSize));
15285 : }
15286 4 : }
15287 :
15288 : /************************************************************************/
15289 : /* GDALMDArrayRawBlockInfo::operator=() */
15290 : /************************************************************************/
15291 :
15292 : GDALMDArrayRawBlockInfo &
15293 7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
15294 : {
15295 7 : if (this != &other)
15296 : {
15297 5 : CPLFree(pszFilename);
15298 5 : pszFilename =
15299 5 : other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
15300 5 : nOffset = other.nOffset;
15301 5 : nSize = other.nSize;
15302 5 : CSLDestroy(papszInfo);
15303 5 : papszInfo = CSLDuplicate(other.papszInfo);
15304 5 : CPLFree(pabyInlineData);
15305 5 : pabyInlineData = nullptr;
15306 5 : if (other.pabyInlineData)
15307 : {
15308 4 : pabyInlineData = static_cast<GByte *>(
15309 4 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15310 4 : if (pabyInlineData)
15311 4 : memcpy(pabyInlineData, other.pabyInlineData,
15312 4 : static_cast<size_t>(other.nSize));
15313 : }
15314 : }
15315 7 : return *this;
15316 : }
15317 :
15318 : /************************************************************************/
15319 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15320 : /************************************************************************/
15321 :
15322 2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15323 2 : GDALMDArrayRawBlockInfo &&other)
15324 2 : : pszFilename(other.pszFilename), nOffset(other.nOffset),
15325 2 : nSize(other.nSize), papszInfo(other.papszInfo),
15326 2 : pabyInlineData(other.pabyInlineData)
15327 : {
15328 2 : other.pszFilename = nullptr;
15329 2 : other.papszInfo = nullptr;
15330 2 : other.pabyInlineData = nullptr;
15331 2 : }
15332 :
15333 : /************************************************************************/
15334 : /* GDALMDArrayRawBlockInfo::operator=() */
15335 : /************************************************************************/
15336 :
15337 : GDALMDArrayRawBlockInfo &
15338 2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
15339 : {
15340 2 : if (this != &other)
15341 : {
15342 2 : std::swap(pszFilename, other.pszFilename);
15343 2 : nOffset = other.nOffset;
15344 2 : nSize = other.nSize;
15345 2 : std::swap(papszInfo, other.papszInfo);
15346 2 : std::swap(pabyInlineData, other.pabyInlineData);
15347 : }
15348 2 : return *this;
15349 : }
15350 :
15351 : //! @endcond
15352 :
15353 : /************************************************************************/
15354 : /* GDALMDArray::GetRawBlockInfo() */
15355 : /************************************************************************/
15356 :
15357 : /** Return information on a raw block.
15358 : *
15359 : * The block coordinates must be between 0 and
15360 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15361 : * 0 and GetDimensionCount()-1.
15362 : *
15363 : * If the queried block has valid coordinates but is missing in the dataset,
15364 : * all fields of info will be set to 0/nullptr, but the function will return
15365 : * true.
15366 : *
15367 : * This method is only implemented by a subset of drivers. The base
15368 : * implementation just returns false and empty info.
15369 : *
15370 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15371 : *
15372 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15373 : * value is "LITTLE" or "BIG".
15374 : *
15375 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15376 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15377 : * comma-separated)
15378 : *
15379 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15380 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15381 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15382 : * "[idx0,...,idxN]" with the permutation).
15383 : *
15384 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15385 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15386 : * that for each queried VRT block, there is one and only one source that
15387 : * is used to fill the VRT block and that the block size of this source is
15388 : * exactly the one of the VRT block.
15389 : *
15390 : * This is the same as C function GDALMDArrayGetRawBlockInfo().
15391 : *
15392 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15393 : * coordinates.
15394 : * @param[out] info structure to fill with block information.
15395 : * @return true in case of success, or false if an error occurs.
15396 : * @since 3.12
15397 : */
15398 0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
15399 : GDALMDArrayRawBlockInfo &info) const
15400 : {
15401 : (void)panBlockCoordinates;
15402 0 : info.clear();
15403 0 : return false;
15404 : }
15405 :
15406 : /************************************************************************/
15407 : /* GDALMDArrayGetRawBlockInfo() */
15408 : /************************************************************************/
15409 :
15410 : /** Return information on a raw block.
15411 : *
15412 : * The block coordinates must be between 0 and
15413 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15414 : * 0 and GetDimensionCount()-1.
15415 : *
15416 : * If the queried block has valid coordinates but is missing in the dataset,
15417 : * all fields of info will be set to 0/nullptr, but the function will return
15418 : * true.
15419 : *
15420 : * This method is only implemented by a subset of drivers. The base
15421 : * implementation just returns false and empty info.
15422 : *
15423 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15424 : *
15425 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15426 : * value is "LITTLE" or "BIG".
15427 : *
15428 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15429 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15430 : * comma-separated)
15431 : *
15432 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15433 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15434 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15435 : * "[idx0,...,idxN]" with the permutation).
15436 : *
15437 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15438 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15439 : * that for each queried VRT block, there is one and only one source that
15440 : * is used to fill the VRT block and that the block size of this source is
15441 : * exactly the one of the VRT block.
15442 : *
15443 : * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
15444 : *
15445 : * @param hArray handle to array.
15446 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15447 : * coordinates.
15448 : * @param[out] psBlockInfo structure to fill with block information.
15449 : * Must be allocated with GDALMDArrayRawBlockInfoCreate(),
15450 : * and freed with GDALMDArrayRawBlockInfoRelease().
15451 : * @return true in case of success, or false if an error occurs.
15452 : * @since 3.12
15453 : */
15454 21 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
15455 : const uint64_t *panBlockCoordinates,
15456 : GDALMDArrayRawBlockInfo *psBlockInfo)
15457 : {
15458 21 : VALIDATE_POINTER1(hArray, __func__, false);
15459 21 : VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
15460 21 : VALIDATE_POINTER1(psBlockInfo, __func__, false);
15461 21 : return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
15462 : }
15463 :
15464 : /************************************************************************/
15465 : /* GDALMDArrayRawBlockInfoCreate() */
15466 : /************************************************************************/
15467 :
15468 : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
15469 : *
15470 : * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
15471 : *
15472 : * @since 3.12
15473 : */
15474 21 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
15475 : {
15476 21 : return new GDALMDArrayRawBlockInfo();
15477 : }
15478 :
15479 : /************************************************************************/
15480 : /* GDALMDArrayRawBlockInfoRelease() */
15481 : /************************************************************************/
15482 :
15483 : /** Free an instance of GDALMDArrayRawBlockInfo.
15484 : *
15485 : * @since 3.12
15486 : */
15487 21 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
15488 : {
15489 21 : delete psBlockInfo;
15490 21 : }
15491 :
15492 : /************************************************************************/
15493 : /* GDALMDArray::GetOverviewCount() */
15494 : /************************************************************************/
15495 :
15496 : /**
15497 : * \brief Return the number of overview arrays available.
15498 : *
15499 : * This method is the same as the C function GDALMDArrayGetOverviewCount().
15500 : *
15501 : * @return overview count, zero if none.
15502 : *
15503 : * @since 3.13
15504 : */
15505 :
15506 115 : int GDALMDArray::GetOverviewCount() const
15507 : {
15508 115 : return 0;
15509 : }
15510 :
15511 : /************************************************************************/
15512 : /* GDALMDArrayGetOverviewCount() */
15513 : /************************************************************************/
15514 : /**
15515 : * \brief Return the number of overview arrays available.
15516 : *
15517 : * This method is the same as the C++ method GDALMDArray::GetOverviewCount().
15518 : *
15519 : * @param hArray Array.
15520 : * @return overview count, zero if none.
15521 : *
15522 : * @since 3.13
15523 : */
15524 :
15525 104 : int GDALMDArrayGetOverviewCount(GDALMDArrayH hArray)
15526 : {
15527 104 : VALIDATE_POINTER1(hArray, __func__, 0);
15528 104 : return hArray->m_poImpl->GetOverviewCount();
15529 : }
15530 :
15531 : /************************************************************************/
15532 : /* GDALMDArray::GetOverview() */
15533 : /************************************************************************/
15534 :
15535 : /**
15536 : * \brief Get overview array object.
15537 : *
15538 : * This method is the same as the C function GDALMDArrayGetOverview().
15539 : *
15540 : * @param nIdx overview index between 0 and GetOverviewCount()-1.
15541 : *
15542 : * @return overview GDALMDArray, or nullptr
15543 : *
15544 : * @since 3.13
15545 : */
15546 :
15547 70 : std::shared_ptr<GDALMDArray> GDALMDArray::GetOverview(int nIdx) const
15548 : {
15549 : (void)nIdx;
15550 70 : return nullptr;
15551 : }
15552 :
15553 : /************************************************************************/
15554 : /* GDALMDArrayGetOverview() */
15555 : /************************************************************************/
15556 :
15557 : /**
15558 : * \brief Get overview array object.
15559 : *
15560 : * This method is the same as the C++ method GDALMDArray::GetOverview().
15561 : *
15562 : * @param hArray Array.
15563 : * @param nIdx overview index between 0 and GDALMDArrayGetOverviewCount()-1.
15564 : *
15565 : * @return overview GDALMDArray, or nullptr.
15566 : * Must be released with GDALMDArrayRelease()
15567 : *
15568 : * @since 3.13
15569 : */
15570 :
15571 113 : GDALMDArrayH GDALMDArrayGetOverview(GDALMDArrayH hArray, int nIdx)
15572 : {
15573 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
15574 226 : auto poOverview = hArray->m_poImpl->GetOverview(nIdx);
15575 113 : if (!poOverview)
15576 89 : return nullptr;
15577 24 : return new GDALMDArrayHS(poOverview);
15578 : }
|