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 12488 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 12488 : const std::string &osContext)
338 12488 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 24976 : !osParentName.empty()
341 18940 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 31428 : m_osContext(osContext)
344 : {
345 12488 : }
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 nullptr.
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 2169 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1267 : std::shared_ptr<GDALGroup> &curGroupHolder,
1268 : std::string &osLastPart) const
1269 : {
1270 2169 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1271 6 : return nullptr;
1272 2163 : const GDALGroup *poCurGroup = this;
1273 : CPLStringList aosTokens(
1274 4326 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1275 2163 : if (aosTokens.size() == 0)
1276 : {
1277 0 : return nullptr;
1278 : }
1279 :
1280 2685 : for (int i = 0; i < aosTokens.size() - 1; i++)
1281 : {
1282 534 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1283 534 : if (!curGroupHolder)
1284 : {
1285 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1286 : aosTokens[i]);
1287 12 : return nullptr;
1288 : }
1289 522 : poCurGroup = curGroupHolder.get();
1290 : }
1291 2151 : osLastPart = aosTokens[aosTokens.size() - 1];
1292 2151 : 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 798 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1304 : CSLConstList papszOptions) const
1305 : {
1306 1596 : std::string osName;
1307 798 : std::shared_ptr<GDALGroup> curGroupHolder;
1308 798 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1309 798 : if (poGroup == nullptr)
1310 12 : return nullptr;
1311 786 : 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 1155 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1463 : CSLConstList papszOptions) const
1464 : {
1465 2310 : std::string osName;
1466 1155 : std::shared_ptr<GDALGroup> curGroupHolder;
1467 1155 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1468 1155 : if (poGroup == nullptr)
1469 4 : return nullptr;
1470 1151 : 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 37010 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1614 : {
1615 37010 : 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 37010 : 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 171557 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1637 171557 : const std::string &osName)
1638 : : m_osName(osName),
1639 : m_osFullName(
1640 171557 : !osParentName.empty()
1641 340156 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1642 683270 : : osName)
1643 : {
1644 171557 : }
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 53947 : size_t GDALAbstractMDArray::GetDimensionCount() const
1684 : {
1685 53947 : 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 609911 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1722 : const GDALExtendedDataType &srcType,
1723 : void *pDst,
1724 : const GDALExtendedDataType &dstType)
1725 : {
1726 1211460 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1727 601554 : dstType.GetClass() == GEDTC_NUMERIC)
1728 : {
1729 599307 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1730 : dstType.GetNumericDataType(), 0, 1);
1731 599307 : return true;
1732 : }
1733 18569 : if (srcType.GetClass() == GEDTC_STRING &&
1734 7965 : dstType.GetClass() == GEDTC_STRING)
1735 : {
1736 : const char *srcStrPtr;
1737 6715 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1738 6715 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1739 6715 : *reinterpret_cast<void **>(pDst) = pszDup;
1740 6715 : return true;
1741 : }
1742 6136 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1743 2247 : dstType.GetClass() == GEDTC_STRING)
1744 : {
1745 2247 : const char *str = nullptr;
1746 2247 : 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 892 : case GDT_Float64:
1789 892 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1790 892 : 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 2247 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1826 2247 : *reinterpret_cast<void **>(pDst) = pszDup;
1827 2247 : return true;
1828 : }
1829 2892 : if (srcType.GetClass() == GEDTC_STRING &&
1830 1250 : dstType.GetClass() == GEDTC_NUMERIC)
1831 : {
1832 : const char *srcStrPtr;
1833 1250 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1834 1250 : 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 1248 : 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 1246 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1850 1246 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1851 : dstType.GetNumericDataType(), 0, 1);
1852 : }
1853 1250 : 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 several values 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 21350 : 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 21350 : const auto &dims = GetDimensions();
1955 21350 : if (dims.empty())
1956 : {
1957 13496 : if (buffer_alloc_start)
1958 : {
1959 12657 : const size_t elementSize = bufferDataType.GetSize();
1960 12657 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1961 12657 : const GByte *paby_buffer_alloc_start =
1962 : static_cast<const GByte *>(buffer_alloc_start);
1963 12657 : const GByte *paby_buffer_alloc_end =
1964 : paby_buffer_alloc_start + buffer_alloc_size;
1965 :
1966 12657 : if (paby_buffer < paby_buffer_alloc_start ||
1967 12657 : paby_buffer + elementSize > paby_buffer_alloc_end)
1968 : {
1969 0 : lamda_error();
1970 0 : return false;
1971 : }
1972 : }
1973 13496 : return true;
1974 : }
1975 :
1976 7854 : if (arrayStep == nullptr)
1977 : {
1978 1957 : tmp_arrayStep.resize(dims.size(), 1);
1979 1957 : arrayStep = tmp_arrayStep.data();
1980 : }
1981 21110 : for (size_t i = 0; i < dims.size(); i++)
1982 : {
1983 13256 : assert(count);
1984 13256 : 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 7854 : bool bufferStride_all_positive = true;
1992 7854 : if (bufferStride == nullptr)
1993 : {
1994 1525 : GPtrDiff_t stride = 1;
1995 1525 : 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 3455 : for (size_t i = dims.size(); i != 0;)
1999 : {
2000 1930 : --i;
2001 1930 : tmp_bufferStride.push_back(stride);
2002 1930 : GUInt64 newStride = 0;
2003 : bool bOK;
2004 : try
2005 : {
2006 1930 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2007 3860 : CPLSM(static_cast<uint64_t>(count[i])))
2008 1930 : .v();
2009 1930 : bOK = static_cast<size_t>(newStride) == newStride &&
2010 1930 : newStride < std::numeric_limits<size_t>::max() / 2;
2011 : }
2012 0 : catch (...)
2013 : {
2014 0 : bOK = false;
2015 : }
2016 1930 : if (!bOK)
2017 : {
2018 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2019 0 : return false;
2020 : }
2021 1930 : stride = static_cast<GPtrDiff_t>(newStride);
2022 : }
2023 1525 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2024 1525 : bufferStride = tmp_bufferStride.data();
2025 : }
2026 : else
2027 : {
2028 17533 : for (size_t i = 0; i < dims.size(); i++)
2029 : {
2030 11265 : if (bufferStride[i] < 0)
2031 : {
2032 61 : bufferStride_all_positive = false;
2033 61 : break;
2034 : }
2035 : }
2036 : }
2037 21081 : for (size_t i = 0; i < dims.size(); i++)
2038 : {
2039 13237 : assert(arrayStartIdx);
2040 13237 : assert(count);
2041 13237 : 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 13235 : if (arrayStep[i] >= 0)
2052 : {
2053 : try
2054 : {
2055 12378 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2056 12380 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2057 49515 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2058 12378 : .v() >= dims[i]->GetSize();
2059 : }
2060 1 : catch (...)
2061 : {
2062 1 : bOverflow = true;
2063 : }
2064 12379 : 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 856 : bOverflow =
2080 856 : arrayStartIdx[i] <
2081 856 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2082 1712 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2083 : ? (static_cast<uint64_t>(1) << 63)
2084 1712 : : static_cast<uint64_t>(-arrayStep[i])))
2085 856 : .v();
2086 : }
2087 0 : catch (...)
2088 : {
2089 0 : bOverflow = true;
2090 : }
2091 856 : 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 7844 : if (buffer_alloc_start)
2104 : {
2105 3594 : const size_t elementSize = bufferDataType.GetSize();
2106 3594 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2107 3594 : const GByte *paby_buffer_alloc_start =
2108 : static_cast<const GByte *>(buffer_alloc_start);
2109 3594 : const GByte *paby_buffer_alloc_end =
2110 : paby_buffer_alloc_start + buffer_alloc_size;
2111 3594 : if (bufferStride_all_positive)
2112 : {
2113 3594 : if (paby_buffer < paby_buffer_alloc_start)
2114 : {
2115 0 : lamda_error();
2116 0 : return false;
2117 : }
2118 3594 : GUInt64 nOffset = elementSize;
2119 10252 : for (size_t i = 0; i < dims.size(); i++)
2120 : {
2121 : try
2122 : {
2123 6658 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2124 6658 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2125 13316 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2126 26632 : CPLSM(static_cast<uint64_t>(elementSize)))
2127 6658 : .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 3594 : 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 7844 : 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 12349 : 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 12349 : 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 24698 : std::vector<GInt64> tmp_arrayStep;
2255 24698 : std::vector<GPtrDiff_t> tmp_bufferStride;
2256 12349 : 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 12349 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2265 12349 : 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 2965 : 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 2965 : 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 5930 : std::vector<GInt64> tmp_arrayStep;
2360 5930 : std::vector<GPtrDiff_t> tmp_bufferStride;
2361 2965 : 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 2965 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2370 2965 : 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 312 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2432 : {
2433 312 : 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 8740 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2606 : {
2607 8740 : 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 8740 : 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 22567 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2735 : {
2736 22567 : const void *pNoData = GetRawNoDataValue();
2737 22567 : double dfNoData = 0.0;
2738 22567 : const auto &eDT = GetDataType();
2739 22567 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2740 22567 : if (ok)
2741 : {
2742 22238 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2743 : GDT_Float64, 0, 1);
2744 : }
2745 22567 : if (pbHasNoData)
2746 500 : *pbHasNoData = ok;
2747 22567 : 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 14 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2766 : {
2767 14 : const void *pNoData = GetRawNoDataValue();
2768 14 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2769 14 : const auto &eDT = GetDataType();
2770 14 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2771 14 : if (ok)
2772 : {
2773 10 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2774 : GDT_Int64, 0, 1);
2775 : }
2776 14 : if (pbHasNoData)
2777 14 : *pbHasNoData = ok;
2778 14 : 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 63 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2851 : {
2852 63 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2853 63 : bool bRet = false;
2854 63 : if (GDALExtendedDataType::CopyValue(
2855 126 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2856 63 : GetDataType()))
2857 : {
2858 63 : bRet = SetRawNoDataValue(pRawNoData);
2859 : }
2860 63 : CPLFree(pRawNoData);
2861 63 : 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 161728 : 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 161728 : : GDALAbstractMDArray(osParentName, osName)
3241 : #endif
3242 : {
3243 161728 : }
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 4587 : const char *GDALAttribute::ReadAsString() const
3415 : {
3416 4587 : const auto nDims = GetDimensionCount();
3417 9174 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3418 9174 : std::vector<size_t> count(1 + nDims, 1);
3419 4587 : char *szRet = nullptr;
3420 4587 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3421 4587 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3422 13760 : sizeof(szRet)) ||
3423 4586 : szRet == nullptr)
3424 : {
3425 5 : return nullptr;
3426 : }
3427 4582 : m_osCachedVal = szRet;
3428 4582 : CPLFree(szRet);
3429 4582 : 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 9829 : 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 9829 : m_osContext(osContext)
3914 : {
3915 9829 : }
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 nullptr.
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 61 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4251 : CSLConstList papszOptions) const
4252 : {
4253 61 : const auto nDimCount = GetDimensionCount();
4254 61 : if (nDimCount == 0)
4255 2 : return true;
4256 :
4257 118 : std::vector<GUInt64> tmp_arrayStartIdx;
4258 59 : if (arrayStartIdx == nullptr)
4259 : {
4260 0 : tmp_arrayStartIdx.resize(nDimCount);
4261 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4262 : }
4263 :
4264 118 : std::vector<size_t> tmp_count;
4265 59 : 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 118 : std::vector<GInt64> tmp_arrayStep;
4285 118 : std::vector<GPtrDiff_t> tmp_bufferStride;
4286 59 : const GInt64 *arrayStep = nullptr;
4287 59 : const GPtrDiff_t *bufferStride = nullptr;
4288 59 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4289 118 : GDALExtendedDataType::Create(GDT_Unknown),
4290 : nullptr, nullptr, 0, tmp_arrayStep,
4291 : tmp_bufferStride))
4292 : {
4293 1 : return false;
4294 : }
4295 :
4296 58 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4297 : }
4298 :
4299 : /************************************************************************/
4300 : /* IAdviseRead() */
4301 : /************************************************************************/
4302 :
4303 : //! @cond Doxygen_Suppress
4304 18 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4305 : CSLConstList /* papszOptions*/) const
4306 : {
4307 18 : 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 3339 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4339 : std::string &osCacheFilenameOut) const
4340 : {
4341 3339 : const auto &osFilename = GetFilename();
4342 3339 : 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 3338 : osCacheFilenameOut = osFilename + ".gmac";
4350 3338 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4351 : {
4352 2 : const auto nPosQuestionMark = osFilename.find('?');
4353 2 : 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 3338 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4362 3338 : if (pszProxy != nullptr)
4363 7 : osCacheFilenameOut = pszProxy;
4364 :
4365 : // .gmac sidecars are local-only; skip stat for non-local filesystems.
4366 6660 : if (!bCanCreate && pszProxy == nullptr &&
4367 3322 : !VSIIsLocal(osCacheFilenameOut.c_str()))
4368 : {
4369 2 : return nullptr;
4370 : }
4371 :
4372 3336 : std::unique_ptr<GDALDataset> poDS;
4373 : VSIStatBufL sStat;
4374 3336 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4375 : {
4376 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4377 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4378 : nullptr, nullptr, nullptr));
4379 : }
4380 3336 : if (poDS)
4381 : {
4382 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4383 28 : return poDS->GetRootGroup();
4384 : }
4385 :
4386 3308 : if (bCanCreate)
4387 : {
4388 5 : const char *pszDrvName = "netCDF";
4389 5 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4390 5 : if (poDrv == nullptr)
4391 : {
4392 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4393 : pszDrvName);
4394 0 : return nullptr;
4395 : }
4396 : {
4397 10 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4398 10 : CPLErrorStateBackuper oErrorStateBackuper;
4399 5 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4400 : nullptr, nullptr));
4401 : }
4402 5 : if (!poDS)
4403 : {
4404 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4405 1 : if (pszProxy)
4406 : {
4407 1 : osCacheFilenameOut = pszProxy;
4408 1 : poDS.reset(poDrv->CreateMultiDimensional(
4409 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4410 : }
4411 : }
4412 5 : if (poDS)
4413 : {
4414 5 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4415 5 : return poDS->GetRootGroup();
4416 : }
4417 : else
4418 : {
4419 0 : CPLError(CE_Failure, CPLE_AppDefined,
4420 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4421 : "configuration option to write the cache in "
4422 : "another directory",
4423 : osCacheFilenameOut.c_str());
4424 : }
4425 : }
4426 :
4427 3303 : return nullptr;
4428 : }
4429 :
4430 : //! @endcond
4431 :
4432 : /************************************************************************/
4433 : /* Cache() */
4434 : /************************************************************************/
4435 :
4436 : /** Cache the content of the array into an auxiliary filename.
4437 : *
4438 : * The main purpose of this method is to be able to cache views that are
4439 : * expensive to compute, such as transposed arrays.
4440 : *
4441 : * The array will be stored in a file whose name is the one of
4442 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4443 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4444 : *
4445 : * If the .gmac file cannot be written next to the dataset, the
4446 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4447 : * directory.
4448 : *
4449 : * The GDALMDArray::Read() method will automatically use the cache when it
4450 : * exists. There is no timestamp checks between the source array and the cached
4451 : * array. If the source arrays changes, the cache must be manually deleted.
4452 : *
4453 : * This is the same as the C function GDALMDArrayCache()
4454 : *
4455 : * @note Driver implementation: optionally implemented.
4456 : *
4457 : * @param papszOptions List of options, null terminated, or NULL. Currently
4458 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4459 : * to specify the block size of the cached array.
4460 : * @return true in case of success.
4461 : */
4462 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4463 : {
4464 14 : std::string osCacheFilename;
4465 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4466 7 : if (!poRG)
4467 1 : return false;
4468 :
4469 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4470 6 : if (poRG->OpenMDArray(osCachedArrayName))
4471 : {
4472 2 : CPLError(CE_Failure, CPLE_NotSupported,
4473 : "An array with same name %s already exists in %s",
4474 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4475 2 : return false;
4476 : }
4477 :
4478 8 : CPLStringList aosOptions;
4479 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4480 4 : const auto &aoDims = GetDimensions();
4481 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4482 4 : if (!aoDims.empty())
4483 : {
4484 : std::string osBlockSize(
4485 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4486 4 : if (osBlockSize.empty())
4487 : {
4488 6 : const auto anBlockSize = GetBlockSize();
4489 3 : int idxDim = 0;
4490 10 : for (auto nBlockSize : anBlockSize)
4491 : {
4492 7 : if (idxDim > 0)
4493 4 : osBlockSize += ',';
4494 7 : if (nBlockSize == 0)
4495 7 : nBlockSize = 256;
4496 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4497 : osBlockSize +=
4498 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4499 7 : idxDim++;
4500 : }
4501 : }
4502 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4503 :
4504 4 : int idxDim = 0;
4505 13 : for (const auto &poDim : aoDims)
4506 : {
4507 9 : auto poNewDim = poRG->CreateDimension(
4508 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4509 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4510 9 : if (!poNewDim)
4511 0 : return false;
4512 9 : aoNewDims.emplace_back(poNewDim);
4513 9 : idxDim++;
4514 : }
4515 : }
4516 :
4517 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4518 8 : GetDataType(), aosOptions.List());
4519 4 : if (!poCachedArray)
4520 : {
4521 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4522 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4523 0 : return false;
4524 : }
4525 :
4526 4 : GUInt64 nCost = 0;
4527 8 : return poCachedArray->CopyFrom(nullptr, this,
4528 : false, // strict
4529 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4530 : }
4531 :
4532 : /************************************************************************/
4533 : /* Read() */
4534 : /************************************************************************/
4535 :
4536 5977 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4537 : const GInt64 *arrayStep, // step in elements
4538 : const GPtrDiff_t *bufferStride, // stride in elements
4539 : const GDALExtendedDataType &bufferDataType,
4540 : void *pDstBuffer, const void *pDstBufferAllocStart,
4541 : size_t nDstBufferAllocSize) const
4542 : {
4543 5977 : if (!m_bHasTriedCachedArray)
4544 : {
4545 3341 : m_bHasTriedCachedArray = true;
4546 3341 : if (IsCacheable())
4547 : {
4548 3341 : const auto &osFilename = GetFilename();
4549 5960 : if (!osFilename.empty() &&
4550 5960 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4551 : {
4552 5218 : std::string osCacheFilename;
4553 5218 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4554 2609 : if (poRG)
4555 : {
4556 : const std::string osCachedArrayName(
4557 32 : MassageName(GetFullName()));
4558 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4559 16 : if (m_poCachedArray)
4560 : {
4561 6 : const auto &dims = GetDimensions();
4562 : const auto &cachedDims =
4563 6 : m_poCachedArray->GetDimensions();
4564 6 : const size_t nDims = dims.size();
4565 : bool ok =
4566 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4567 6 : cachedDims.size() == nDims;
4568 19 : for (size_t i = 0; ok && i < nDims; ++i)
4569 : {
4570 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4571 : }
4572 6 : if (ok)
4573 : {
4574 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4575 : osCachedArrayName.c_str(),
4576 : osCacheFilename.c_str());
4577 : }
4578 : else
4579 : {
4580 0 : CPLError(CE_Warning, CPLE_AppDefined,
4581 : "Cached array %s in %s has incompatible "
4582 : "characteristics with current array.",
4583 : osCachedArrayName.c_str(),
4584 : osCacheFilename.c_str());
4585 0 : m_poCachedArray.reset();
4586 : }
4587 : }
4588 : }
4589 : }
4590 : }
4591 : }
4592 :
4593 5977 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4594 5977 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4595 : {
4596 0 : CPLError(CE_Failure, CPLE_AppDefined,
4597 : "Array data type is not convertible to buffer data type");
4598 0 : return false;
4599 : }
4600 :
4601 11954 : std::vector<GInt64> tmp_arrayStep;
4602 11954 : std::vector<GPtrDiff_t> tmp_bufferStride;
4603 5977 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4604 : bufferStride, bufferDataType, pDstBuffer,
4605 : pDstBufferAllocStart, nDstBufferAllocSize,
4606 : tmp_arrayStep, tmp_bufferStride))
4607 : {
4608 9 : return false;
4609 : }
4610 :
4611 5968 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4612 5968 : bufferDataType, pDstBuffer);
4613 : }
4614 :
4615 : /************************************************************************/
4616 : /* GetRootGroup() */
4617 : /************************************************************************/
4618 :
4619 : /** Return the root group to which this arrays belongs too.
4620 : *
4621 : * Note that arrays may be free standing and some drivers may not implement
4622 : * this method, hence nullptr may be returned.
4623 : *
4624 : * It is used internally by the GetResampled() method to detect if GLT
4625 : * orthorectification is available.
4626 : *
4627 : * @return the root group, or nullptr.
4628 : * @since GDAL 3.8
4629 : */
4630 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4631 : {
4632 0 : return nullptr;
4633 : }
4634 :
4635 : //! @cond Doxygen_Suppress
4636 :
4637 : /************************************************************************/
4638 : /* IsTransposedRequest() */
4639 : /************************************************************************/
4640 :
4641 1467 : bool GDALMDArray::IsTransposedRequest(
4642 : const size_t *count,
4643 : const GPtrDiff_t *bufferStride) const // stride in elements
4644 : {
4645 : /*
4646 : For example:
4647 : count = [2,3,4]
4648 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4649 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4650 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4651 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4652 : */
4653 1467 : const size_t nDims(GetDimensionCount());
4654 1467 : size_t nCurStrideForRowMajorStrides = 1;
4655 1467 : bool bRowMajorStrides = true;
4656 1467 : size_t nElts = 1;
4657 1467 : size_t nLastIdx = 0;
4658 3663 : for (size_t i = nDims; i > 0;)
4659 : {
4660 2196 : --i;
4661 2196 : if (bufferStride[i] < 0)
4662 0 : return false;
4663 2196 : if (static_cast<size_t>(bufferStride[i]) !=
4664 : nCurStrideForRowMajorStrides)
4665 : {
4666 410 : bRowMajorStrides = false;
4667 : }
4668 : // Integer overflows have already been checked in CheckReadWriteParams()
4669 2196 : nCurStrideForRowMajorStrides *= count[i];
4670 2196 : nElts *= count[i];
4671 2196 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4672 : }
4673 1467 : if (bRowMajorStrides)
4674 1131 : return false;
4675 336 : return nLastIdx == nElts - 1;
4676 : }
4677 :
4678 : /************************************************************************/
4679 : /* CopyToFinalBufferSameDataType() */
4680 : /************************************************************************/
4681 :
4682 : template <size_t N>
4683 68 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4684 : size_t nDims, const size_t *count,
4685 : const GPtrDiff_t *bufferStride)
4686 : {
4687 136 : std::vector<size_t> anStackCount(nDims);
4688 136 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4689 68 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4690 : #if defined(__GNUC__)
4691 : #pragma GCC diagnostic push
4692 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4693 : #endif
4694 68 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4695 : #if defined(__GNUC__)
4696 : #pragma GCC diagnostic pop
4697 : #endif
4698 68 : size_t iDim = 0;
4699 :
4700 575 : lbl_next_depth:
4701 575 : if (iDim == nDims - 1)
4702 : {
4703 495 : size_t n = count[iDim];
4704 495 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4705 495 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4706 18174 : while (n > 0)
4707 : {
4708 17679 : --n;
4709 17679 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4710 17679 : pabyDstBuffer += bufferStrideLastDim;
4711 17679 : pabySrcBuffer += N;
4712 : }
4713 : }
4714 : else
4715 : {
4716 80 : anStackCount[iDim] = count[iDim];
4717 : while (true)
4718 : {
4719 507 : ++iDim;
4720 507 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4721 507 : goto lbl_next_depth;
4722 507 : lbl_return_to_caller_in_loop:
4723 507 : --iDim;
4724 507 : --anStackCount[iDim];
4725 507 : if (anStackCount[iDim] == 0)
4726 80 : break;
4727 427 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4728 : }
4729 : }
4730 575 : if (iDim > 0)
4731 507 : goto lbl_return_to_caller_in_loop;
4732 68 : }
4733 :
4734 : /************************************************************************/
4735 : /* CopyToFinalBuffer() */
4736 : /************************************************************************/
4737 :
4738 316 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4739 : const GDALExtendedDataType &eSrcDataType,
4740 : void *pDstBuffer,
4741 : const GDALExtendedDataType &eDstDataType,
4742 : size_t nDims, const size_t *count,
4743 : const GPtrDiff_t *bufferStride)
4744 : {
4745 316 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4746 : // Use specialized implementation for well-known data types when no
4747 : // type conversion is needed
4748 316 : if (eSrcDataType == eDstDataType)
4749 : {
4750 121 : if (nSrcDataTypeSize == 1)
4751 : {
4752 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4753 : count, bufferStride);
4754 68 : return;
4755 : }
4756 80 : else if (nSrcDataTypeSize == 2)
4757 : {
4758 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4759 : count, bufferStride);
4760 1 : return;
4761 : }
4762 79 : else if (nSrcDataTypeSize == 4)
4763 : {
4764 18 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4765 : count, bufferStride);
4766 18 : return;
4767 : }
4768 61 : else if (nSrcDataTypeSize == 8)
4769 : {
4770 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4771 : count, bufferStride);
4772 8 : return;
4773 : }
4774 : }
4775 :
4776 248 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4777 496 : std::vector<size_t> anStackCount(nDims);
4778 496 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4779 248 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4780 248 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4781 248 : size_t iDim = 0;
4782 :
4783 517 : lbl_next_depth:
4784 517 : if (iDim == nDims - 1)
4785 : {
4786 507 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4787 507 : pabyDstBufferStack[iDim], eDstDataType,
4788 507 : bufferStride[iDim], count[iDim]);
4789 507 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4790 : }
4791 : else
4792 : {
4793 10 : anStackCount[iDim] = count[iDim];
4794 : while (true)
4795 : {
4796 269 : ++iDim;
4797 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4798 269 : goto lbl_next_depth;
4799 269 : lbl_return_to_caller_in_loop:
4800 269 : --iDim;
4801 269 : --anStackCount[iDim];
4802 269 : if (anStackCount[iDim] == 0)
4803 10 : break;
4804 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4805 : }
4806 : }
4807 517 : if (iDim > 0)
4808 269 : goto lbl_return_to_caller_in_loop;
4809 : }
4810 :
4811 : /************************************************************************/
4812 : /* TransposeLast2Dims() */
4813 : /************************************************************************/
4814 :
4815 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4816 : const GDALExtendedDataType &eDT,
4817 : const size_t nDims, const size_t *count,
4818 : const size_t nEltsNonLast2Dims)
4819 : {
4820 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4821 19 : const auto nDTSize = eDT.GetSize();
4822 : void *pTempBufferForLast2DimsTranspose =
4823 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4824 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4825 0 : return false;
4826 :
4827 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4828 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4829 : {
4830 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4831 : pTempBufferForLast2DimsTranspose,
4832 39 : eDT.GetNumericDataType(), count[nDims - 1],
4833 39 : count[nDims - 2]);
4834 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4835 : nDTSize * nEltsLast2Dims);
4836 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4837 : }
4838 :
4839 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4840 :
4841 19 : return true;
4842 : }
4843 :
4844 : /************************************************************************/
4845 : /* ReadForTransposedRequest() */
4846 : /************************************************************************/
4847 :
4848 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4849 : // transposed view yield to extremely poor/unusable performance. This fixes
4850 : // this by using temporary memory to read in a contiguous buffer in a
4851 : // row-major order, and then do the transposition to the final buffer.
4852 :
4853 335 : bool GDALMDArray::ReadForTransposedRequest(
4854 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4855 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4856 : void *pDstBuffer) const
4857 : {
4858 335 : const size_t nDims(GetDimensionCount());
4859 335 : if (nDims == 0)
4860 : {
4861 0 : CPLAssert(false);
4862 : return false; // shouldn't happen
4863 : }
4864 335 : size_t nElts = 1;
4865 784 : for (size_t i = 0; i < nDims; ++i)
4866 449 : nElts *= count[i];
4867 :
4868 670 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4869 335 : tmpBufferStrides.back() = 1;
4870 449 : for (size_t i = nDims - 1; i > 0;)
4871 : {
4872 114 : --i;
4873 114 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4874 : }
4875 :
4876 335 : const auto &eDT = GetDataType();
4877 335 : const auto nDTSize = eDT.GetSize();
4878 475 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4879 491 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4880 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4881 : {
4882 : // Optimization of the optimization if only the last 2 dims are
4883 : // transposed that saves on temporary buffer allocation
4884 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4885 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4886 23 : bool bRowMajorStridesForNonLast2Dims = true;
4887 23 : size_t nEltsNonLast2Dims = 1;
4888 40 : for (size_t i = nDims - 2; i > 0;)
4889 : {
4890 17 : --i;
4891 17 : if (static_cast<size_t>(bufferStride[i]) !=
4892 : nCurStrideForRowMajorStrides)
4893 : {
4894 4 : bRowMajorStridesForNonLast2Dims = false;
4895 : }
4896 : // Integer overflows have already been checked in
4897 : // CheckReadWriteParams()
4898 17 : nCurStrideForRowMajorStrides *= count[i];
4899 17 : nEltsNonLast2Dims *= count[i];
4900 : }
4901 23 : if (bRowMajorStridesForNonLast2Dims)
4902 : {
4903 : // We read in the final buffer!
4904 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4905 19 : eDT, pDstBuffer))
4906 : {
4907 0 : return false;
4908 : }
4909 :
4910 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4911 19 : nEltsNonLast2Dims);
4912 : }
4913 : }
4914 :
4915 316 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4916 316 : if (pTempBuffer == nullptr)
4917 0 : return false;
4918 :
4919 316 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4920 316 : pTempBuffer))
4921 : {
4922 0 : VSIFree(pTempBuffer);
4923 0 : return false;
4924 : }
4925 316 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4926 : count, bufferStride);
4927 :
4928 316 : if (eDT.NeedsFreeDynamicMemory())
4929 : {
4930 237 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4931 474 : for (size_t i = 0; i < nElts; ++i)
4932 : {
4933 237 : eDT.FreeDynamicMemory(pabyPtr);
4934 237 : pabyPtr += nDTSize;
4935 : }
4936 : }
4937 :
4938 316 : VSIFree(pTempBuffer);
4939 316 : return true;
4940 : }
4941 :
4942 : /************************************************************************/
4943 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4944 : /************************************************************************/
4945 :
4946 : // Returns true if at all following conditions are met:
4947 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4948 : // defines a row-major ordered contiguous buffer.
4949 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4950 : const size_t *count, const GInt64 *arrayStep,
4951 : const GPtrDiff_t *bufferStride,
4952 : const GDALExtendedDataType &bufferDataType) const
4953 : {
4954 78 : if (bufferDataType != GetDataType())
4955 5 : return false;
4956 73 : size_t nExpectedStride = 1;
4957 166 : for (size_t i = GetDimensionCount(); i > 0;)
4958 : {
4959 96 : --i;
4960 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4961 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4962 : {
4963 3 : return false;
4964 : }
4965 93 : nExpectedStride *= count[i];
4966 : }
4967 70 : return true;
4968 : }
4969 :
4970 : /************************************************************************/
4971 : /* ReadUsingContiguousIRead() */
4972 : /************************************************************************/
4973 :
4974 : // Used for example by the TileDB driver when requesting it with
4975 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4976 : // not defining a row-major ordered contiguous buffer.
4977 : // Should only be called when at least one of the above conditions are true,
4978 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4979 : // returning none.
4980 : // This method will call IRead() again with arrayStep[] == 1,
4981 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4982 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4983 : // content of that temporary buffer onto pDstBuffer.
4984 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4985 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4986 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4987 : void *pDstBuffer) const
4988 : {
4989 7 : const size_t nDims(GetDimensionCount());
4990 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4991 14 : std::vector<size_t> anTmpCount(nDims);
4992 7 : const auto &oType = GetDataType();
4993 7 : size_t nMemArraySize = oType.GetSize();
4994 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4995 7 : GPtrDiff_t nStride = 1;
4996 18 : for (size_t i = nDims; i > 0;)
4997 : {
4998 11 : --i;
4999 11 : if (arrayStep[i] > 0)
5000 9 : anTmpStartIdx[i] = arrayStartIdx[i];
5001 : else
5002 2 : anTmpStartIdx[i] =
5003 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
5004 : const uint64_t nCount =
5005 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
5006 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
5007 : {
5008 0 : CPLError(CE_Failure, CPLE_AppDefined,
5009 : "Read() failed due to too large memory requirement");
5010 0 : return false;
5011 : }
5012 11 : anTmpCount[i] = static_cast<size_t>(nCount);
5013 11 : nMemArraySize *= anTmpCount[i];
5014 11 : anTmpStride[i] = nStride;
5015 11 : nStride *= anTmpCount[i];
5016 : }
5017 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
5018 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
5019 7 : if (!pTmpBuffer)
5020 0 : return false;
5021 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
5022 14 : std::vector<GInt64>(nDims, 1).data(), // steps
5023 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
5024 : {
5025 0 : return false;
5026 : }
5027 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5028 18 : for (size_t i = 0; i < nDims; ++i)
5029 : {
5030 11 : if (arrayStep[i] > 0)
5031 9 : anTmpStartIdx[i] = 0;
5032 : else
5033 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5034 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5035 22 : std::string(), std::string(), std::string(), std::string(),
5036 22 : anTmpCount[i]);
5037 : }
5038 : auto poMEMArray =
5039 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5040 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5041 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5042 7 : bufferStride, bufferDataType, pDstBuffer);
5043 : }
5044 :
5045 : //! @endcond
5046 :
5047 : /************************************************************************/
5048 : /* GDALSlicedMDArray */
5049 : /************************************************************************/
5050 :
5051 : class GDALSlicedMDArray final : public GDALPamMDArray
5052 : {
5053 : private:
5054 : std::shared_ptr<GDALMDArray> m_poParent{};
5055 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5056 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5057 : std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5058 : std::vector<Range>
5059 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5060 :
5061 : mutable std::vector<GUInt64> m_parentStart;
5062 : mutable std::vector<size_t> m_parentCount;
5063 : mutable std::vector<GInt64> m_parentStep;
5064 : mutable std::vector<GPtrDiff_t> m_parentStride;
5065 :
5066 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5067 : const GInt64 *arrayStep,
5068 : const GPtrDiff_t *bufferStride) const;
5069 :
5070 : protected:
5071 847 : explicit GDALSlicedMDArray(
5072 : const std::shared_ptr<GDALMDArray> &poParent,
5073 : const std::string &viewExpr,
5074 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5075 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5076 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5077 : std::vector<Range> &&parentRanges)
5078 2541 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5079 2541 : poParent->GetFullName() +
5080 1694 : " (" + viewExpr + ")"),
5081 1694 : GDALPamMDArray(std::string(),
5082 1694 : "Sliced view of " + poParent->GetFullName() + " (" +
5083 1694 : viewExpr + ")",
5084 1694 : GDALPamMultiDim::GetPAM(poParent),
5085 : poParent->GetContext()),
5086 1694 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5087 847 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5088 847 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5089 847 : m_parentRanges(std::move(parentRanges)),
5090 847 : m_parentStart(m_poParent->GetDimensionCount()),
5091 847 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5092 847 : m_parentStep(m_poParent->GetDimensionCount()),
5093 6776 : m_parentStride(m_poParent->GetDimensionCount())
5094 : {
5095 847 : }
5096 :
5097 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5098 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5099 : const GDALExtendedDataType &bufferDataType,
5100 : void *pDstBuffer) const override;
5101 :
5102 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5103 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5104 : const GDALExtendedDataType &bufferDataType,
5105 : const void *pSrcBuffer) override;
5106 :
5107 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5108 : CSLConstList papszOptions) const override;
5109 :
5110 : public:
5111 : static std::shared_ptr<GDALSlicedMDArray>
5112 847 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5113 : const std::string &viewExpr,
5114 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5115 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5116 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5117 : std::vector<Range> &&parentRanges)
5118 : {
5119 847 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5120 847 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5121 :
5122 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5123 847 : poParent, viewExpr, std::move(dims),
5124 847 : std::move(mapDimIdxToParentDimIdx),
5125 847 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5126 847 : newAr->SetSelf(newAr);
5127 847 : return newAr;
5128 : }
5129 :
5130 228 : bool IsWritable() const override
5131 : {
5132 228 : return m_poParent->IsWritable();
5133 : }
5134 :
5135 1834 : const std::string &GetFilename() const override
5136 : {
5137 1834 : return m_poParent->GetFilename();
5138 : }
5139 :
5140 : const std::vector<std::shared_ptr<GDALDimension>> &
5141 5930 : GetDimensions() const override
5142 : {
5143 5930 : return m_dims;
5144 : }
5145 :
5146 2433 : const GDALExtendedDataType &GetDataType() const override
5147 : {
5148 2433 : return m_poParent->GetDataType();
5149 : }
5150 :
5151 4 : const std::string &GetUnit() const override
5152 : {
5153 4 : return m_poParent->GetUnit();
5154 : }
5155 :
5156 : // bool SetUnit(const std::string& osUnit) override { return
5157 : // m_poParent->SetUnit(osUnit); }
5158 :
5159 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5160 : {
5161 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5162 2 : if (!poSrcSRS)
5163 1 : return nullptr;
5164 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5165 2 : std::vector<int> dstMapping;
5166 3 : for (int srcAxis : srcMapping)
5167 : {
5168 2 : bool bFound = false;
5169 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5170 : {
5171 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5172 3 : srcAxis - 1)
5173 : {
5174 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5175 2 : bFound = true;
5176 2 : break;
5177 : }
5178 : }
5179 2 : if (!bFound)
5180 : {
5181 0 : dstMapping.push_back(0);
5182 : }
5183 : }
5184 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5185 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5186 1 : return poClone;
5187 : }
5188 :
5189 104 : const void *GetRawNoDataValue() const override
5190 : {
5191 104 : return m_poParent->GetRawNoDataValue();
5192 : }
5193 :
5194 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5195 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5196 :
5197 4 : double GetOffset(bool *pbHasOffset,
5198 : GDALDataType *peStorageType) const override
5199 : {
5200 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5201 : }
5202 :
5203 4 : double GetScale(bool *pbHasScale,
5204 : GDALDataType *peStorageType) const override
5205 : {
5206 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
5207 : }
5208 :
5209 : // bool SetOffset(double dfOffset) override { return
5210 : // m_poParent->SetOffset(dfOffset); }
5211 :
5212 : // bool SetScale(double dfScale) override { return
5213 : // m_poParent->SetScale(dfScale); }
5214 :
5215 550 : std::vector<GUInt64> GetBlockSize() const override
5216 : {
5217 550 : std::vector<GUInt64> ret(GetDimensionCount());
5218 1100 : const auto parentBlockSize(m_poParent->GetBlockSize());
5219 1474 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5220 : {
5221 924 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5222 924 : if (iOldAxis != static_cast<size_t>(-1))
5223 : {
5224 924 : ret[i] = parentBlockSize[iOldAxis];
5225 : }
5226 : }
5227 1100 : return ret;
5228 : }
5229 :
5230 : std::shared_ptr<GDALAttribute>
5231 14 : GetAttribute(const std::string &osName) const override
5232 : {
5233 14 : return m_poParent->GetAttribute(osName);
5234 : }
5235 :
5236 : std::vector<std::shared_ptr<GDALAttribute>>
5237 37 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5238 : {
5239 37 : return m_poParent->GetAttributes(papszOptions);
5240 : }
5241 : };
5242 :
5243 : /************************************************************************/
5244 : /* PrepareParentArrays() */
5245 : /************************************************************************/
5246 :
5247 765 : void GDALSlicedMDArray::PrepareParentArrays(
5248 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5249 : const GPtrDiff_t *bufferStride) const
5250 : {
5251 765 : const size_t nParentDimCount = m_parentRanges.size();
5252 2157 : for (size_t i = 0; i < nParentDimCount; i++)
5253 : {
5254 : // For dimensions in parent that have no existence in sliced array
5255 1392 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5256 : }
5257 :
5258 1925 : for (size_t i = 0; i < m_dims.size(); i++)
5259 : {
5260 1160 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5261 1160 : if (iParent != static_cast<size_t>(-1))
5262 : {
5263 1158 : m_parentStart[iParent] =
5264 1158 : m_parentRanges[iParent].m_nIncr >= 0
5265 1158 : ? m_parentRanges[iParent].m_nStartIdx +
5266 857 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5267 301 : : m_parentRanges[iParent].m_nStartIdx -
5268 602 : arrayStartIdx[i] *
5269 301 : static_cast<GUInt64>(
5270 301 : -m_parentRanges[iParent].m_nIncr);
5271 1158 : m_parentCount[iParent] = count[i];
5272 1158 : if (arrayStep)
5273 : {
5274 1157 : m_parentStep[iParent] =
5275 1157 : count[i] == 1 ? 1 :
5276 : // other checks should have ensured this does
5277 : // not overflow
5278 953 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5279 : }
5280 1158 : if (bufferStride)
5281 : {
5282 1157 : m_parentStride[iParent] = bufferStride[i];
5283 : }
5284 : }
5285 : }
5286 765 : }
5287 :
5288 : /************************************************************************/
5289 : /* IRead() */
5290 : /************************************************************************/
5291 :
5292 732 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5293 : const GInt64 *arrayStep,
5294 : const GPtrDiff_t *bufferStride,
5295 : const GDALExtendedDataType &bufferDataType,
5296 : void *pDstBuffer) const
5297 : {
5298 732 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5299 1464 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5300 732 : m_parentStep.data(), m_parentStride.data(),
5301 732 : bufferDataType, pDstBuffer);
5302 : }
5303 :
5304 : /************************************************************************/
5305 : /* IWrite() */
5306 : /************************************************************************/
5307 :
5308 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5309 : const size_t *count, const GInt64 *arrayStep,
5310 : const GPtrDiff_t *bufferStride,
5311 : const GDALExtendedDataType &bufferDataType,
5312 : const void *pSrcBuffer)
5313 : {
5314 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5315 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5316 32 : m_parentStep.data(), m_parentStride.data(),
5317 32 : bufferDataType, pSrcBuffer);
5318 : }
5319 :
5320 : /************************************************************************/
5321 : /* IAdviseRead() */
5322 : /************************************************************************/
5323 :
5324 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5325 : const size_t *count,
5326 : CSLConstList papszOptions) const
5327 : {
5328 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5329 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5330 1 : papszOptions);
5331 : }
5332 :
5333 : /************************************************************************/
5334 : /* CreateSlicedArray() */
5335 : /************************************************************************/
5336 :
5337 : static std::shared_ptr<GDALMDArray>
5338 709 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5339 : const std::string &viewExpr, const std::string &activeSlice,
5340 : bool bRenameDimensions,
5341 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5342 : {
5343 709 : const auto &srcDims(self->GetDimensions());
5344 709 : if (srcDims.empty())
5345 : {
5346 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5347 2 : return nullptr;
5348 : }
5349 :
5350 1414 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5351 707 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5352 :
5353 1414 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5354 1414 : std::vector<size_t> mapDimIdxToParentDimIdx;
5355 1414 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5356 707 : newDims.reserve(nTokens);
5357 707 : mapDimIdxToParentDimIdx.reserve(nTokens);
5358 707 : parentRanges.reserve(nTokens);
5359 :
5360 707 : bool bGotEllipsis = false;
5361 707 : size_t nCurSrcDim = 0;
5362 1414 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5363 2091 : for (size_t i = 0; i < nTokens; i++)
5364 : {
5365 1401 : const char *pszIdxSpec = aosTokens[i];
5366 1401 : if (EQUAL(pszIdxSpec, "..."))
5367 : {
5368 129 : if (bGotEllipsis)
5369 : {
5370 2 : CPLError(CE_Failure, CPLE_AppDefined,
5371 : "Only one single ellipsis is supported");
5372 2 : return nullptr;
5373 : }
5374 127 : bGotEllipsis = true;
5375 127 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5376 263 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5377 : {
5378 136 : parentRanges.emplace_back(0, 1);
5379 136 : newDims.push_back(srcDims[nCurSrcDim]);
5380 136 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5381 : }
5382 127 : continue;
5383 : }
5384 1272 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5385 1269 : EQUAL(pszIdxSpec, "np.newaxis"))
5386 : {
5387 3 : newDims.push_back(std::make_shared<GDALDimension>(
5388 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5389 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5390 3 : continue;
5391 : }
5392 1269 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5393 : {
5394 327 : if (nCurSrcDim >= srcDims.size())
5395 : {
5396 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5397 : activeSlice.c_str());
5398 7 : return nullptr;
5399 : }
5400 :
5401 325 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5402 325 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5403 325 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5404 321 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5405 : {
5406 5 : CPLError(CE_Failure, CPLE_AppDefined,
5407 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5408 5 : return nullptr;
5409 : }
5410 320 : if (nVal < 0)
5411 0 : nVal += nDimSize;
5412 320 : parentRanges.emplace_back(nVal, 0);
5413 : }
5414 : else
5415 : {
5416 942 : if (nCurSrcDim >= srcDims.size())
5417 : {
5418 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5419 : activeSlice.c_str());
5420 8 : return nullptr;
5421 : }
5422 :
5423 : CPLStringList aosRangeTokens(
5424 941 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5425 941 : int nRangeTokens = aosRangeTokens.size();
5426 941 : if (nRangeTokens > 3)
5427 : {
5428 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5429 : pszIdxSpec);
5430 1 : return nullptr;
5431 : }
5432 940 : if (nRangeTokens <= 1)
5433 : {
5434 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5435 : pszIdxSpec);
5436 1 : return nullptr;
5437 : }
5438 939 : const char *pszStart = aosRangeTokens[0];
5439 939 : const char *pszEnd = aosRangeTokens[1];
5440 939 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5441 939 : GDALSlicedMDArray::Range range;
5442 939 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5443 939 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5444 1877 : if (range.m_nIncr == 0 ||
5445 938 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5446 : {
5447 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5448 1 : return nullptr;
5449 : }
5450 938 : auto startIdx(CPLAtoGIntBig(pszStart));
5451 938 : if (startIdx < 0)
5452 : {
5453 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5454 0 : startIdx = 0;
5455 : else
5456 0 : startIdx = nDimSize + startIdx;
5457 : }
5458 938 : const bool bPosIncr = range.m_nIncr > 0;
5459 938 : range.m_nStartIdx = startIdx;
5460 1876 : range.m_nStartIdx = EQUAL(pszStart, "")
5461 938 : ? (bPosIncr ? 0 : nDimSize - 1)
5462 : : range.m_nStartIdx;
5463 938 : if (range.m_nStartIdx >= nDimSize - 1)
5464 286 : range.m_nStartIdx = nDimSize - 1;
5465 938 : auto endIdx(CPLAtoGIntBig(pszEnd));
5466 938 : if (endIdx < 0)
5467 : {
5468 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5469 1 : if (nDimSize < positiveEndIdx)
5470 0 : endIdx = 0;
5471 : else
5472 1 : endIdx = nDimSize - positiveEndIdx;
5473 : }
5474 938 : GUInt64 nEndIdx = endIdx;
5475 938 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5476 938 : if (pszStart[0] || pszEnd[0])
5477 : {
5478 636 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5479 633 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5480 : {
5481 4 : CPLError(CE_Failure, CPLE_AppDefined,
5482 : "Output dimension of size 0 is not allowed");
5483 4 : return nullptr;
5484 : }
5485 : }
5486 934 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5487 934 : const auto nAbsIncr = std::abs(range.m_nIncr);
5488 934 : const GUInt64 newSize =
5489 302 : (pszStart[0] == 0 && pszEnd[0] == 0 &&
5490 302 : range.m_nStartIdx == nEndIdx)
5491 1868 : ? 1
5492 : : bPosIncr
5493 1061 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5494 128 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5495 934 : const auto &poSrcDim = srcDims[nCurSrcDim];
5496 1484 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5497 550 : newSize == poSrcDim->GetSize())
5498 : {
5499 181 : newDims.push_back(poSrcDim);
5500 : }
5501 : else
5502 : {
5503 1506 : std::string osNewDimName(poSrcDim->GetName());
5504 753 : if (bRenameDimensions)
5505 : {
5506 : osNewDimName =
5507 1410 : "subset_" + poSrcDim->GetName() +
5508 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5509 : "_" CPL_FRMT_GUIB,
5510 705 : static_cast<GUIntBig>(range.m_nStartIdx),
5511 705 : static_cast<GIntBig>(range.m_nIncr),
5512 705 : static_cast<GUIntBig>(newSize));
5513 : }
5514 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5515 1506 : std::string(), osNewDimName, poSrcDim->GetType(),
5516 753 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5517 : : std::string(),
5518 1506 : newSize);
5519 753 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5520 910 : if (poSrcIndexingVar &&
5521 910 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5522 157 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5523 : {
5524 : std::vector<std::shared_ptr<GDALDimension>>
5525 628 : indexingVarNewDims{poNewDim};
5526 314 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5527 : std::vector<std::shared_ptr<GDALMDArray>>
5528 314 : indexingVarNewIndexingVar;
5529 : std::vector<GDALSlicedMDArray::Range>
5530 314 : indexingVarParentRanges{range};
5531 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5532 : poSrcIndexingVar, pszIdxSpec,
5533 157 : std::move(indexingVarNewDims),
5534 157 : std::move(indexingVarMapDimIdxToParentDimIdx),
5535 157 : std::move(indexingVarNewIndexingVar),
5536 471 : std::move(indexingVarParentRanges));
5537 157 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5538 157 : apoNewIndexingVariables.push_back(
5539 157 : std::move(poNewIndexingVar));
5540 : }
5541 753 : newDims.push_back(std::move(poNewDim));
5542 : }
5543 934 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5544 934 : parentRanges.emplace_back(range);
5545 : }
5546 :
5547 1254 : nCurSrcDim++;
5548 : }
5549 763 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5550 : {
5551 73 : parentRanges.emplace_back(0, 1);
5552 73 : newDims.push_back(srcDims[nCurSrcDim]);
5553 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5554 : }
5555 :
5556 690 : GDALMDArray::ViewSpec viewSpec;
5557 690 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5558 690 : viewSpec.m_parentRanges = parentRanges;
5559 690 : viewSpecs.emplace_back(std::move(viewSpec));
5560 :
5561 1380 : return GDALSlicedMDArray::Create(
5562 690 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5563 1380 : std::move(apoNewIndexingVariables), std::move(parentRanges));
5564 : }
5565 :
5566 : /************************************************************************/
5567 : /* GDALExtractFieldMDArray */
5568 : /************************************************************************/
5569 :
5570 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5571 : {
5572 : private:
5573 : std::shared_ptr<GDALMDArray> m_poParent{};
5574 : GDALExtendedDataType m_dt;
5575 : std::string m_srcCompName;
5576 : mutable std::vector<GByte> m_pabyNoData{};
5577 :
5578 : protected:
5579 465 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5580 : const std::string &fieldName,
5581 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5582 1860 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5583 930 : " of " +
5584 465 : poParent->GetFullName()),
5585 : GDALPamMDArray(
5586 930 : std::string(),
5587 930 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5588 930 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5589 : m_poParent(poParent), m_dt(srcComp->GetType()),
5590 2325 : m_srcCompName(srcComp->GetName())
5591 : {
5592 465 : m_pabyNoData.resize(m_dt.GetSize());
5593 465 : }
5594 :
5595 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5596 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5597 : const GDALExtendedDataType &bufferDataType,
5598 : void *pDstBuffer) const override;
5599 :
5600 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5601 : CSLConstList papszOptions) const override
5602 : {
5603 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5604 : }
5605 :
5606 : public:
5607 : static std::shared_ptr<GDALExtractFieldMDArray>
5608 465 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5609 : const std::string &fieldName,
5610 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5611 : {
5612 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5613 465 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5614 465 : newAr->SetSelf(newAr);
5615 465 : return newAr;
5616 : }
5617 :
5618 930 : ~GDALExtractFieldMDArray() override
5619 465 : {
5620 465 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5621 930 : }
5622 :
5623 200 : bool IsWritable() const override
5624 : {
5625 200 : return m_poParent->IsWritable();
5626 : }
5627 :
5628 1314 : const std::string &GetFilename() const override
5629 : {
5630 1314 : return m_poParent->GetFilename();
5631 : }
5632 :
5633 : const std::vector<std::shared_ptr<GDALDimension>> &
5634 1384 : GetDimensions() const override
5635 : {
5636 1384 : return m_poParent->GetDimensions();
5637 : }
5638 :
5639 1137 : const GDALExtendedDataType &GetDataType() const override
5640 : {
5641 1137 : return m_dt;
5642 : }
5643 :
5644 2 : const std::string &GetUnit() const override
5645 : {
5646 2 : return m_poParent->GetUnit();
5647 : }
5648 :
5649 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5650 : {
5651 2 : return m_poParent->GetSpatialRef();
5652 : }
5653 :
5654 99 : const void *GetRawNoDataValue() const override
5655 : {
5656 99 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5657 99 : if (parentNoData == nullptr)
5658 6 : return nullptr;
5659 :
5660 93 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5661 93 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5662 :
5663 186 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5664 186 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5665 186 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5666 93 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5667 279 : std::move(comps)));
5668 :
5669 93 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5670 93 : &m_pabyNoData[0], tmpDT);
5671 :
5672 93 : return &m_pabyNoData[0];
5673 : }
5674 :
5675 2 : double GetOffset(bool *pbHasOffset,
5676 : GDALDataType *peStorageType) const override
5677 : {
5678 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5679 : }
5680 :
5681 2 : double GetScale(bool *pbHasScale,
5682 : GDALDataType *peStorageType) const override
5683 : {
5684 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5685 : }
5686 :
5687 201 : std::vector<GUInt64> GetBlockSize() const override
5688 : {
5689 201 : return m_poParent->GetBlockSize();
5690 : }
5691 : };
5692 :
5693 : /************************************************************************/
5694 : /* IRead() */
5695 : /************************************************************************/
5696 :
5697 414 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5698 : const size_t *count,
5699 : const GInt64 *arrayStep,
5700 : const GPtrDiff_t *bufferStride,
5701 : const GDALExtendedDataType &bufferDataType,
5702 : void *pDstBuffer) const
5703 : {
5704 828 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5705 828 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5706 828 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5707 : auto tmpDT(GDALExtendedDataType::Create(
5708 828 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5709 :
5710 414 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5711 828 : tmpDT, pDstBuffer);
5712 : }
5713 :
5714 : /************************************************************************/
5715 : /* CreateFieldNameExtractArray() */
5716 : /************************************************************************/
5717 :
5718 : static std::shared_ptr<GDALMDArray>
5719 466 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5720 : const std::string &fieldName)
5721 : {
5722 466 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5723 466 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5724 983 : for (const auto &comp : self->GetDataType().GetComponents())
5725 : {
5726 982 : if (comp->GetName() == fieldName)
5727 : {
5728 465 : srcComp = ∁
5729 465 : break;
5730 : }
5731 : }
5732 466 : if (srcComp == nullptr)
5733 : {
5734 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5735 : fieldName.c_str());
5736 1 : return nullptr;
5737 : }
5738 465 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5739 : }
5740 :
5741 : /************************************************************************/
5742 : /* GetView() */
5743 : /************************************************************************/
5744 :
5745 : // clang-format off
5746 : /** Return a view of the array using slicing or field access.
5747 : *
5748 : * The slice expression uses the same syntax as NumPy basic slicing and
5749 : * indexing. See
5750 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5751 : * Or it can use field access by name. See
5752 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5753 : *
5754 : * Multiple [] bracket elements can be concatenated, with a slice expression
5755 : * or field name inside each.
5756 : *
5757 : * For basic slicing and indexing, inside each [] bracket element, a list of
5758 : * indexes that apply to successive source dimensions, can be specified, using
5759 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5760 : * or newaxis, using a comma separator.
5761 : *
5762 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5763 : * <ul>
5764 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5765 : * at index 1 in the first dimension, and index 2 in the second dimension
5766 : * from the source array. That is 5</li>
5767 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5768 : * implemented internally doing this intermediate slicing approach.</li>
5769 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5770 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5771 : * first dimension. That is [4,5,6,7].</li>
5772 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5773 : * second dimension. That is [2,6].</li>
5774 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5775 : * the second dimension. That is [[2],[6]].</li>
5776 : * <li>GetView("[::,2]"): Same as
5777 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5778 : * ellipsis only expands to one dimension here.</li>
5779 : * <li>GetView("[:,::2]"):
5780 : * returns a 2-dimensional array, with even-indexed elements of the second
5781 : * dimension. That is [[0,2],[4,6]].</li>
5782 : * <li>GetView("[:,1::2]"): returns a
5783 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5784 : * is [[1,3],[5,7]].</li>
5785 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5786 : * array, with elements of the second dimension with index in the range [1,3[.
5787 : * That is [[1,2],[5,6]].</li>
5788 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5789 : * array, with the values in first dimension reversed. That is
5790 : * [[4,5,6,7],[0,1,2,3]].</li>
5791 : * <li>GetView("[newaxis,...]"): returns a
5792 : * 3-dimensional array, with an additional dimension of size 1 put at the
5793 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5794 : * </ul>
5795 : *
5796 : * One difference with NumPy behavior is that ranges that would result in
5797 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5798 : * GDAL multidimensional model).
5799 : *
5800 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5801 : * Multiple field specification is not supported currently.
5802 : *
5803 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5804 : *
5805 : * \note When using the GDAL Python bindings, natural Python syntax can be
5806 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5807 : * ar.GetView("[0,::,1]['foo']")
5808 : * \note When using the C++ API and integer indexing only, you may use the
5809 : * at(idx0, idx1, ...) method.
5810 : *
5811 : * The returned array holds a reference to the original one, and thus is
5812 : * a view of it (not a copy). If the content of the original array changes,
5813 : * the content of the view array too. When using basic slicing and indexing,
5814 : * the view can be written if the underlying array is writable.
5815 : *
5816 : * This is the same as the C function GDALMDArrayGetView()
5817 : *
5818 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5819 : * access.
5820 : * @return a new array, that holds a reference to the original one, and thus is
5821 : * a view of it (not a copy), or nullptr in case of error.
5822 : */
5823 : // clang-format on
5824 :
5825 : std::shared_ptr<GDALMDArray>
5826 1109 : GDALMDArray::GetView(const std::string &viewExpr) const
5827 : {
5828 2218 : std::vector<ViewSpec> viewSpecs;
5829 2218 : return GetView(viewExpr, true, viewSpecs);
5830 : }
5831 :
5832 : //! @cond Doxygen_Suppress
5833 : std::shared_ptr<GDALMDArray>
5834 1181 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5835 : std::vector<ViewSpec> &viewSpecs) const
5836 : {
5837 2362 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5838 1181 : if (!self)
5839 : {
5840 1 : CPLError(CE_Failure, CPLE_AppDefined,
5841 : "Driver implementation issue: m_pSelf not set !");
5842 1 : return nullptr;
5843 : }
5844 1180 : std::string curExpr(viewExpr);
5845 : while (true)
5846 : {
5847 1183 : if (curExpr.empty() || curExpr[0] != '[')
5848 : {
5849 2 : CPLError(CE_Failure, CPLE_AppDefined,
5850 : "Slice string should start with ['");
5851 1180 : return nullptr;
5852 : }
5853 :
5854 1181 : std::string fieldName;
5855 : size_t endExpr;
5856 1181 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5857 : {
5858 470 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5859 : {
5860 2 : CPLError(CE_Failure, CPLE_AppDefined,
5861 : "Field access not allowed on non-compound data type");
5862 2 : return nullptr;
5863 : }
5864 468 : size_t idx = 2;
5865 5119 : for (; idx < curExpr.size(); idx++)
5866 : {
5867 5118 : const char ch = curExpr[idx];
5868 5118 : if (ch == curExpr[1])
5869 467 : break;
5870 4651 : if (ch == '\\' && idx + 1 < curExpr.size())
5871 : {
5872 1 : fieldName += curExpr[idx + 1];
5873 1 : idx++;
5874 : }
5875 : else
5876 : {
5877 4650 : fieldName += ch;
5878 : }
5879 : }
5880 468 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5881 : {
5882 2 : CPLError(CE_Failure, CPLE_AppDefined,
5883 : "Invalid field access specification");
5884 2 : return nullptr;
5885 : }
5886 466 : endExpr = idx + 1;
5887 : }
5888 : else
5889 : {
5890 711 : endExpr = curExpr.find(']');
5891 : }
5892 1177 : if (endExpr == std::string::npos)
5893 : {
5894 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5895 1 : return nullptr;
5896 : }
5897 1176 : if (endExpr == 1)
5898 : {
5899 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5900 1 : return nullptr;
5901 : }
5902 1175 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5903 :
5904 1175 : if (!fieldName.empty())
5905 : {
5906 932 : ViewSpec viewSpec;
5907 466 : viewSpec.m_osFieldName = fieldName;
5908 466 : viewSpecs.emplace_back(std::move(viewSpec));
5909 : }
5910 :
5911 1175 : auto newArray = !fieldName.empty()
5912 : ? CreateFieldNameExtractArray(self, fieldName)
5913 : : CreateSlicedArray(self, viewExpr, activeSlice,
5914 1175 : bRenameDimensions, viewSpecs);
5915 :
5916 1175 : if (endExpr == curExpr.size() - 1)
5917 : {
5918 1172 : return newArray;
5919 : }
5920 3 : self = std::move(newArray);
5921 3 : curExpr = curExpr.substr(endExpr + 1);
5922 3 : }
5923 : }
5924 :
5925 : //! @endcond
5926 :
5927 : std::shared_ptr<GDALMDArray>
5928 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5929 : {
5930 19 : std::string osExpr("[");
5931 19 : bool bFirst = true;
5932 45 : for (const auto &idx : indices)
5933 : {
5934 26 : if (!bFirst)
5935 7 : osExpr += ',';
5936 26 : bFirst = false;
5937 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5938 : }
5939 57 : return GetView(osExpr + ']');
5940 : }
5941 :
5942 : /************************************************************************/
5943 : /* operator[] */
5944 : /************************************************************************/
5945 :
5946 : /** Return a view of the array using field access
5947 : *
5948 : * Equivalent of GetView("['fieldName']")
5949 : *
5950 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5951 : */
5952 : std::shared_ptr<GDALMDArray>
5953 2 : GDALMDArray::operator[](const std::string &fieldName) const
5954 : {
5955 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5956 4 : .replaceAll('\\', "\\\\")
5957 4 : .replaceAll('\'', "\\\'")
5958 6 : .c_str()));
5959 : }
5960 :
5961 : /************************************************************************/
5962 : /* GDALMDArrayTransposed */
5963 : /************************************************************************/
5964 :
5965 : class GDALMDArrayTransposed final : public GDALPamMDArray
5966 : {
5967 : private:
5968 : std::shared_ptr<GDALMDArray> m_poParent{};
5969 : std::vector<int> m_anMapNewAxisToOldAxis{};
5970 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5971 :
5972 : mutable std::vector<GUInt64> m_parentStart;
5973 : mutable std::vector<size_t> m_parentCount;
5974 : mutable std::vector<GInt64> m_parentStep;
5975 : mutable std::vector<GPtrDiff_t> m_parentStride;
5976 :
5977 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5978 : const GInt64 *arrayStep,
5979 : const GPtrDiff_t *bufferStride) const;
5980 :
5981 : static std::string
5982 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5983 : {
5984 84 : std::string ret;
5985 84 : ret += '[';
5986 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5987 : {
5988 228 : if (i > 0)
5989 144 : ret += ',';
5990 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5991 : }
5992 84 : ret += ']';
5993 84 : return ret;
5994 : }
5995 :
5996 : protected:
5997 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5998 : const std::vector<int> &anMapNewAxisToOldAxis,
5999 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
6000 84 : : GDALAbstractMDArray(std::string(),
6001 84 : "Transposed view of " + poParent->GetFullName() +
6002 84 : " along " +
6003 42 : MappingToStr(anMapNewAxisToOldAxis)),
6004 84 : GDALPamMDArray(std::string(),
6005 84 : "Transposed view of " + poParent->GetFullName() +
6006 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
6007 84 : GDALPamMultiDim::GetPAM(poParent),
6008 : poParent->GetContext()),
6009 42 : m_poParent(std::move(poParent)),
6010 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
6011 42 : m_dims(std::move(dims)),
6012 42 : m_parentStart(m_poParent->GetDimensionCount()),
6013 42 : m_parentCount(m_poParent->GetDimensionCount()),
6014 42 : m_parentStep(m_poParent->GetDimensionCount()),
6015 336 : m_parentStride(m_poParent->GetDimensionCount())
6016 : {
6017 42 : }
6018 :
6019 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6020 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6021 : const GDALExtendedDataType &bufferDataType,
6022 : void *pDstBuffer) const override;
6023 :
6024 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
6025 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6026 : const GDALExtendedDataType &bufferDataType,
6027 : const void *pSrcBuffer) override;
6028 :
6029 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6030 : CSLConstList papszOptions) const override;
6031 :
6032 : public:
6033 : static std::shared_ptr<GDALMDArrayTransposed>
6034 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6035 : const std::vector<int> &anMapNewAxisToOldAxis)
6036 : {
6037 42 : const auto &parentDims(poParent->GetDimensions());
6038 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
6039 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6040 : {
6041 114 : if (iOldAxis < 0)
6042 : {
6043 1 : dims.push_back(std::make_shared<GDALDimension>(
6044 2 : std::string(), "newaxis", std::string(), std::string(), 1));
6045 : }
6046 : else
6047 : {
6048 113 : dims.emplace_back(parentDims[iOldAxis]);
6049 : }
6050 : }
6051 :
6052 : auto newAr(
6053 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6054 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
6055 42 : newAr->SetSelf(newAr);
6056 84 : return newAr;
6057 : }
6058 :
6059 1 : bool IsWritable() const override
6060 : {
6061 1 : return m_poParent->IsWritable();
6062 : }
6063 :
6064 84 : const std::string &GetFilename() const override
6065 : {
6066 84 : return m_poParent->GetFilename();
6067 : }
6068 :
6069 : const std::vector<std::shared_ptr<GDALDimension>> &
6070 358 : GetDimensions() const override
6071 : {
6072 358 : return m_dims;
6073 : }
6074 :
6075 141 : const GDALExtendedDataType &GetDataType() const override
6076 : {
6077 141 : return m_poParent->GetDataType();
6078 : }
6079 :
6080 4 : const std::string &GetUnit() const override
6081 : {
6082 4 : return m_poParent->GetUnit();
6083 : }
6084 :
6085 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6086 : {
6087 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6088 5 : if (!poSrcSRS)
6089 2 : return nullptr;
6090 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6091 6 : std::vector<int> dstMapping;
6092 9 : for (int srcAxis : srcMapping)
6093 : {
6094 6 : bool bFound = false;
6095 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6096 : {
6097 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6098 : {
6099 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6100 6 : bFound = true;
6101 6 : break;
6102 : }
6103 : }
6104 6 : if (!bFound)
6105 : {
6106 0 : dstMapping.push_back(0);
6107 : }
6108 : }
6109 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6110 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6111 3 : return poClone;
6112 : }
6113 :
6114 4 : const void *GetRawNoDataValue() const override
6115 : {
6116 4 : return m_poParent->GetRawNoDataValue();
6117 : }
6118 :
6119 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6120 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6121 :
6122 4 : double GetOffset(bool *pbHasOffset,
6123 : GDALDataType *peStorageType) const override
6124 : {
6125 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6126 : }
6127 :
6128 4 : double GetScale(bool *pbHasScale,
6129 : GDALDataType *peStorageType) const override
6130 : {
6131 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6132 : }
6133 :
6134 : // bool SetOffset(double dfOffset) override { return
6135 : // m_poParent->SetOffset(dfOffset); }
6136 :
6137 : // bool SetScale(double dfScale) override { return
6138 : // m_poParent->SetScale(dfScale); }
6139 :
6140 3 : std::vector<GUInt64> GetBlockSize() const override
6141 : {
6142 3 : std::vector<GUInt64> ret(GetDimensionCount());
6143 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6144 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6145 : {
6146 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6147 8 : if (iOldAxis >= 0)
6148 : {
6149 7 : ret[i] = parentBlockSize[iOldAxis];
6150 : }
6151 : }
6152 6 : return ret;
6153 : }
6154 :
6155 : std::shared_ptr<GDALAttribute>
6156 1 : GetAttribute(const std::string &osName) const override
6157 : {
6158 1 : return m_poParent->GetAttribute(osName);
6159 : }
6160 :
6161 : std::vector<std::shared_ptr<GDALAttribute>>
6162 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6163 : {
6164 6 : return m_poParent->GetAttributes(papszOptions);
6165 : }
6166 : };
6167 :
6168 : /************************************************************************/
6169 : /* PrepareParentArrays() */
6170 : /************************************************************************/
6171 :
6172 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6173 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6174 : const GPtrDiff_t *bufferStride) const
6175 : {
6176 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6177 : {
6178 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6179 129 : if (iOldAxis >= 0)
6180 : {
6181 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6182 128 : m_parentCount[iOldAxis] = count[i];
6183 128 : if (arrayStep) // only null when called from IAdviseRead()
6184 : {
6185 126 : m_parentStep[iOldAxis] = arrayStep[i];
6186 : }
6187 128 : if (bufferStride) // only null when called from IAdviseRead()
6188 : {
6189 126 : m_parentStride[iOldAxis] = bufferStride[i];
6190 : }
6191 : }
6192 : }
6193 47 : }
6194 :
6195 : /************************************************************************/
6196 : /* IRead() */
6197 : /************************************************************************/
6198 :
6199 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6200 : const size_t *count, const GInt64 *arrayStep,
6201 : const GPtrDiff_t *bufferStride,
6202 : const GDALExtendedDataType &bufferDataType,
6203 : void *pDstBuffer) const
6204 : {
6205 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6206 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6207 44 : m_parentStep.data(), m_parentStride.data(),
6208 44 : bufferDataType, pDstBuffer);
6209 : }
6210 :
6211 : /************************************************************************/
6212 : /* IWrite() */
6213 : /************************************************************************/
6214 :
6215 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6216 : const size_t *count, const GInt64 *arrayStep,
6217 : const GPtrDiff_t *bufferStride,
6218 : const GDALExtendedDataType &bufferDataType,
6219 : const void *pSrcBuffer)
6220 : {
6221 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6222 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6223 2 : m_parentStep.data(), m_parentStride.data(),
6224 2 : bufferDataType, pSrcBuffer);
6225 : }
6226 :
6227 : /************************************************************************/
6228 : /* IAdviseRead() */
6229 : /************************************************************************/
6230 :
6231 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6232 : const size_t *count,
6233 : CSLConstList papszOptions) const
6234 : {
6235 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6236 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6237 1 : papszOptions);
6238 : }
6239 :
6240 : /************************************************************************/
6241 : /* Transpose() */
6242 : /************************************************************************/
6243 :
6244 : /** Return a view of the array whose axis have been reordered.
6245 : *
6246 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6247 : * and GetDimensionCount() - 1, and each only once.
6248 : * -1 can be used as a special index value to ask for the insertion of a new
6249 : * axis of size 1.
6250 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6251 : * index of one of its dimension, it corresponds to the axis of index
6252 : * anMapNewAxisToOldAxis[i] from the current array.
6253 : *
6254 : * This is similar to the numpy.transpose() method
6255 : *
6256 : * The returned array holds a reference to the original one, and thus is
6257 : * a view of it (not a copy). If the content of the original array changes,
6258 : * the content of the view array too. The view can be written if the underlying
6259 : * array is writable.
6260 : *
6261 : * Note that I/O performance in such a transposed view might be poor.
6262 : *
6263 : * This is the same as the C function GDALMDArrayTranspose().
6264 : *
6265 : * @return a new array, that holds a reference to the original one, and thus is
6266 : * a view of it (not a copy), or nullptr in case of error.
6267 : */
6268 : std::shared_ptr<GDALMDArray>
6269 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6270 : {
6271 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6272 50 : if (!self)
6273 : {
6274 0 : CPLError(CE_Failure, CPLE_AppDefined,
6275 : "Driver implementation issue: m_pSelf not set !");
6276 0 : return nullptr;
6277 : }
6278 50 : const int nDims = static_cast<int>(GetDimensionCount());
6279 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6280 50 : int nCountOldAxis = 0;
6281 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6282 : {
6283 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6284 : {
6285 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6286 4 : return nullptr;
6287 : }
6288 130 : if (iOldAxis >= 0)
6289 : {
6290 128 : if (alreadyUsedOldAxis[iOldAxis])
6291 : {
6292 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6293 : iOldAxis);
6294 1 : return nullptr;
6295 : }
6296 127 : alreadyUsedOldAxis[iOldAxis] = true;
6297 127 : nCountOldAxis++;
6298 : }
6299 : }
6300 46 : if (nCountOldAxis != nDims)
6301 : {
6302 4 : CPLError(CE_Failure, CPLE_AppDefined,
6303 : "One or several original axis missing");
6304 4 : return nullptr;
6305 : }
6306 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6307 : }
6308 :
6309 : /************************************************************************/
6310 : /* IRead() */
6311 : /************************************************************************/
6312 :
6313 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6314 : const size_t *count, const GInt64 *arrayStep,
6315 : const GPtrDiff_t *bufferStride,
6316 : const GDALExtendedDataType &bufferDataType,
6317 : void *pDstBuffer) const
6318 : {
6319 16 : const double dfScale = m_dfScale;
6320 16 : const double dfOffset = m_dfOffset;
6321 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6322 : const auto dtDouble =
6323 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6324 16 : const size_t nDTSize = dtDouble.GetSize();
6325 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6326 :
6327 16 : double adfSrcNoData[2] = {0, 0};
6328 16 : if (m_bHasNoData)
6329 : {
6330 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6331 9 : m_poParent->GetDataType(),
6332 : &adfSrcNoData[0], dtDouble);
6333 : }
6334 :
6335 16 : const auto nDims = GetDimensions().size();
6336 16 : if (nDims == 0)
6337 : {
6338 : double adfVal[2];
6339 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6340 : dtDouble, &adfVal[0]))
6341 : {
6342 0 : return false;
6343 : }
6344 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6345 : {
6346 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6347 6 : if (bDTIsComplex)
6348 : {
6349 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6350 : }
6351 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6352 : bufferDataType);
6353 : }
6354 : else
6355 : {
6356 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6357 : pDstBuffer, bufferDataType);
6358 : }
6359 9 : return true;
6360 : }
6361 :
6362 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6363 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6364 7 : void *pTempBuffer = pDstBuffer;
6365 7 : if (bTempBufferNeeded)
6366 : {
6367 2 : size_t nElts = 1;
6368 2 : actualBufferStrideVector.resize(nDims);
6369 7 : for (size_t i = 0; i < nDims; i++)
6370 5 : nElts *= count[i];
6371 2 : actualBufferStrideVector.back() = 1;
6372 5 : for (size_t i = nDims - 1; i > 0;)
6373 : {
6374 3 : --i;
6375 3 : actualBufferStrideVector[i] =
6376 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6377 : }
6378 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6379 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6380 2 : if (!pTempBuffer)
6381 0 : return false;
6382 : }
6383 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6384 : actualBufferStridePtr, dtDouble, pTempBuffer))
6385 : {
6386 0 : if (bTempBufferNeeded)
6387 0 : VSIFree(pTempBuffer);
6388 0 : return false;
6389 : }
6390 :
6391 : struct Stack
6392 : {
6393 : size_t nIters = 0;
6394 : double *src_ptr = nullptr;
6395 : GByte *dst_ptr = nullptr;
6396 : GPtrDiff_t src_inc_offset = 0;
6397 : GPtrDiff_t dst_inc_offset = 0;
6398 : };
6399 :
6400 7 : std::vector<Stack> stack(nDims);
6401 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6402 23 : for (size_t i = 0; i < nDims; i++)
6403 : {
6404 32 : stack[i].src_inc_offset =
6405 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6406 16 : stack[i].dst_inc_offset =
6407 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6408 : }
6409 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6410 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6411 :
6412 7 : size_t dimIdx = 0;
6413 7 : const size_t nDimsMinus1 = nDims - 1;
6414 : GByte abyDstNoData[16];
6415 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6416 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6417 : bufferDataType);
6418 :
6419 37 : lbl_next_depth:
6420 37 : if (dimIdx == nDimsMinus1)
6421 : {
6422 25 : auto nIters = count[dimIdx];
6423 25 : double *padfVal = stack[dimIdx].src_ptr;
6424 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6425 : while (true)
6426 : {
6427 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6428 : {
6429 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6430 88 : if (bDTIsComplex)
6431 : {
6432 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6433 : }
6434 88 : if (bTempBufferNeeded)
6435 : {
6436 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6437 : dst_ptr, bufferDataType);
6438 : }
6439 : }
6440 : else
6441 : {
6442 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6443 : }
6444 :
6445 92 : if ((--nIters) == 0)
6446 25 : break;
6447 67 : padfVal += stack[dimIdx].src_inc_offset;
6448 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6449 : }
6450 : }
6451 : else
6452 : {
6453 12 : stack[dimIdx].nIters = count[dimIdx];
6454 : while (true)
6455 : {
6456 30 : dimIdx++;
6457 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6458 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6459 30 : goto lbl_next_depth;
6460 30 : lbl_return_to_caller:
6461 30 : dimIdx--;
6462 30 : if ((--stack[dimIdx].nIters) == 0)
6463 12 : break;
6464 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6465 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6466 : }
6467 : }
6468 37 : if (dimIdx > 0)
6469 30 : goto lbl_return_to_caller;
6470 :
6471 7 : if (bTempBufferNeeded)
6472 2 : VSIFree(pTempBuffer);
6473 7 : return true;
6474 : }
6475 :
6476 : /************************************************************************/
6477 : /* IWrite() */
6478 : /************************************************************************/
6479 :
6480 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6481 : const size_t *count, const GInt64 *arrayStep,
6482 : const GPtrDiff_t *bufferStride,
6483 : const GDALExtendedDataType &bufferDataType,
6484 : const void *pSrcBuffer)
6485 : {
6486 16 : const double dfScale = m_dfScale;
6487 16 : const double dfOffset = m_dfOffset;
6488 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6489 : const auto dtDouble =
6490 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6491 16 : const size_t nDTSize = dtDouble.GetSize();
6492 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6493 : const bool bSelfAndParentHaveNoData =
6494 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6495 16 : double dfNoData = 0;
6496 16 : if (m_bHasNoData)
6497 : {
6498 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6499 : &dfNoData, GDT_Float64, 0, 1);
6500 : }
6501 :
6502 16 : double adfSrcNoData[2] = {0, 0};
6503 16 : if (bSelfAndParentHaveNoData)
6504 : {
6505 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6506 7 : m_poParent->GetDataType(),
6507 : &adfSrcNoData[0], dtDouble);
6508 : }
6509 :
6510 16 : const auto nDims = GetDimensions().size();
6511 16 : if (nDims == 0)
6512 : {
6513 : double adfVal[2];
6514 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6515 : dtDouble);
6516 16 : if (bSelfAndParentHaveNoData &&
6517 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6518 : {
6519 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6520 2 : bufferStride, m_poParent->GetDataType(),
6521 4 : m_poParent->GetRawNoDataValue());
6522 : }
6523 : else
6524 : {
6525 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6526 8 : if (bDTIsComplex)
6527 : {
6528 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6529 : }
6530 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6531 8 : bufferStride, dtDouble, &adfVal[0]);
6532 : }
6533 : }
6534 :
6535 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6536 6 : size_t nElts = 1;
6537 6 : tmpBufferStrideVector.resize(nDims);
6538 20 : for (size_t i = 0; i < nDims; i++)
6539 14 : nElts *= count[i];
6540 6 : tmpBufferStrideVector.back() = 1;
6541 14 : for (size_t i = nDims - 1; i > 0;)
6542 : {
6543 8 : --i;
6544 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6545 : }
6546 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6547 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6548 6 : if (!pTempBuffer)
6549 0 : return false;
6550 :
6551 : struct Stack
6552 : {
6553 : size_t nIters = 0;
6554 : double *dst_ptr = nullptr;
6555 : const GByte *src_ptr = nullptr;
6556 : GPtrDiff_t src_inc_offset = 0;
6557 : GPtrDiff_t dst_inc_offset = 0;
6558 : };
6559 :
6560 6 : std::vector<Stack> stack(nDims);
6561 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6562 20 : for (size_t i = 0; i < nDims; i++)
6563 : {
6564 28 : stack[i].dst_inc_offset =
6565 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6566 14 : stack[i].src_inc_offset =
6567 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6568 : }
6569 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6570 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6571 :
6572 6 : size_t dimIdx = 0;
6573 6 : const size_t nDimsMinus1 = nDims - 1;
6574 :
6575 34 : lbl_next_depth:
6576 34 : if (dimIdx == nDimsMinus1)
6577 : {
6578 23 : auto nIters = count[dimIdx];
6579 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6580 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6581 : while (true)
6582 : {
6583 : double adfVal[2];
6584 : const double *padfSrcVal;
6585 86 : if (bIsBufferDataTypeNativeDataType)
6586 : {
6587 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6588 : }
6589 : else
6590 : {
6591 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6592 : &adfVal[0], dtDouble);
6593 36 : padfSrcVal = adfVal;
6594 : }
6595 :
6596 148 : if (bSelfAndParentHaveNoData &&
6597 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6598 : {
6599 3 : dst_ptr[0] = adfSrcNoData[0];
6600 3 : if (bDTIsComplex)
6601 : {
6602 1 : dst_ptr[1] = adfSrcNoData[1];
6603 : }
6604 : }
6605 : else
6606 : {
6607 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6608 83 : if (bDTIsComplex)
6609 : {
6610 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6611 : }
6612 : }
6613 :
6614 86 : if ((--nIters) == 0)
6615 23 : break;
6616 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6617 63 : src_ptr += stack[dimIdx].src_inc_offset;
6618 63 : }
6619 : }
6620 : else
6621 : {
6622 11 : stack[dimIdx].nIters = count[dimIdx];
6623 : while (true)
6624 : {
6625 28 : dimIdx++;
6626 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6627 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6628 28 : goto lbl_next_depth;
6629 28 : lbl_return_to_caller:
6630 28 : dimIdx--;
6631 28 : if ((--stack[dimIdx].nIters) == 0)
6632 11 : break;
6633 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6634 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6635 : }
6636 : }
6637 34 : if (dimIdx > 0)
6638 28 : goto lbl_return_to_caller;
6639 :
6640 : // If the parent array is not double/complex-double, then convert the
6641 : // values to it, before calling Write(), as some implementations can be
6642 : // very slow when doing the type conversion.
6643 6 : const auto &eParentDT = m_poParent->GetDataType();
6644 6 : const size_t nParentDTSize = eParentDT.GetSize();
6645 6 : if (nParentDTSize <= nDTSize / 2)
6646 : {
6647 : // Copy in-place by making sure that source and target do not overlap
6648 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6649 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6650 :
6651 : // Copy first element
6652 : {
6653 6 : std::vector<GByte> abyTemp(nParentDTSize);
6654 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6655 6 : static_cast<int>(nDTSize), &abyTemp[0],
6656 : eParentNumericDT, static_cast<int>(nParentDTSize),
6657 : 1);
6658 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6659 : }
6660 : // Remaining elements
6661 86 : for (size_t i = 1; i < nElts; ++i)
6662 : {
6663 80 : GDALCopyWords64(
6664 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6665 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6666 : eParentNumericDT, 0, 1);
6667 : }
6668 : }
6669 :
6670 : const bool ret =
6671 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6672 : eParentDT, pTempBuffer);
6673 :
6674 6 : VSIFree(pTempBuffer);
6675 6 : return ret;
6676 : }
6677 :
6678 : /************************************************************************/
6679 : /* GetUnscaled() */
6680 : /************************************************************************/
6681 :
6682 : /** Return an array that is the unscaled version of the current one.
6683 : *
6684 : * That is each value of the unscaled array will be
6685 : * unscaled_value = raw_value * GetScale() + GetOffset()
6686 : *
6687 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6688 : * from unscaled values to raw values.
6689 : *
6690 : * This is the same as the C function GDALMDArrayGetUnscaled().
6691 : *
6692 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6693 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6694 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6695 : * @return a new array, that holds a reference to the original one, and thus is
6696 : * a view of it (not a copy), or nullptr in case of error.
6697 : */
6698 : std::shared_ptr<GDALMDArray>
6699 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6700 : double dfOverriddenDstNodata) const
6701 : {
6702 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6703 17 : if (!self)
6704 : {
6705 0 : CPLError(CE_Failure, CPLE_AppDefined,
6706 : "Driver implementation issue: m_pSelf not set !");
6707 0 : return nullptr;
6708 : }
6709 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6710 : {
6711 0 : CPLError(CE_Failure, CPLE_AppDefined,
6712 : "GetUnscaled() only supports numeric data type");
6713 0 : return nullptr;
6714 : }
6715 : const double dfScale =
6716 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6717 : const double dfOffset =
6718 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6719 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6720 4 : return self;
6721 :
6722 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6723 13 : ? GDT_CFloat64
6724 13 : : GDT_Float64;
6725 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6726 : {
6727 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6728 0 : eDT = GDT_Float16;
6729 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6730 1 : eDT = GDT_Float32;
6731 : }
6732 :
6733 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6734 13 : dfOverriddenDstNodata, eDT);
6735 : }
6736 :
6737 : /************************************************************************/
6738 : /* GDALMDArrayMask */
6739 : /************************************************************************/
6740 :
6741 : class GDALMDArrayMask final : public GDALPamMDArray
6742 : {
6743 : private:
6744 : std::shared_ptr<GDALMDArray> m_poParent{};
6745 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
6746 : double m_dfMissingValue = 0.0;
6747 : bool m_bHasMissingValue = false;
6748 : double m_dfFillValue = 0.0;
6749 : bool m_bHasFillValue = false;
6750 : double m_dfValidMin = 0.0;
6751 : bool m_bHasValidMin = false;
6752 : double m_dfValidMax = 0.0;
6753 : bool m_bHasValidMax = false;
6754 : std::vector<uint32_t> m_anValidFlagMasks{};
6755 : std::vector<uint32_t> m_anValidFlagValues{};
6756 :
6757 : bool Init(CSLConstList papszOptions);
6758 :
6759 : template <typename Type>
6760 : void
6761 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6762 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6763 : const void *pTempBuffer,
6764 : const GDALExtendedDataType &oTmpBufferDT,
6765 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6766 :
6767 : protected:
6768 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6769 96 : : GDALAbstractMDArray(std::string(),
6770 96 : "Mask of " + poParent->GetFullName()),
6771 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6772 96 : GDALPamMultiDim::GetPAM(poParent),
6773 : poParent->GetContext()),
6774 240 : m_poParent(std::move(poParent))
6775 : {
6776 48 : }
6777 :
6778 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6779 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6780 : const GDALExtendedDataType &bufferDataType,
6781 : void *pDstBuffer) const override;
6782 :
6783 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6784 : CSLConstList papszOptions) const override
6785 : {
6786 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6787 : }
6788 :
6789 : public:
6790 : static std::shared_ptr<GDALMDArrayMask>
6791 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6792 : CSLConstList papszOptions);
6793 :
6794 1 : bool IsWritable() const override
6795 : {
6796 1 : return false;
6797 : }
6798 :
6799 54 : const std::string &GetFilename() const override
6800 : {
6801 54 : return m_poParent->GetFilename();
6802 : }
6803 :
6804 : const std::vector<std::shared_ptr<GDALDimension>> &
6805 382 : GetDimensions() const override
6806 : {
6807 382 : return m_poParent->GetDimensions();
6808 : }
6809 :
6810 138 : const GDALExtendedDataType &GetDataType() const override
6811 : {
6812 138 : return m_dt;
6813 : }
6814 :
6815 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6816 : {
6817 1 : return m_poParent->GetSpatialRef();
6818 : }
6819 :
6820 2 : std::vector<GUInt64> GetBlockSize() const override
6821 : {
6822 2 : return m_poParent->GetBlockSize();
6823 : }
6824 : };
6825 :
6826 : /************************************************************************/
6827 : /* GDALMDArrayMask::Create() */
6828 : /************************************************************************/
6829 :
6830 : /* static */ std::shared_ptr<GDALMDArrayMask>
6831 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6832 : CSLConstList papszOptions)
6833 : {
6834 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6835 48 : newAr->SetSelf(newAr);
6836 48 : if (!newAr->Init(papszOptions))
6837 6 : return nullptr;
6838 42 : return newAr;
6839 : }
6840 :
6841 : /************************************************************************/
6842 : /* GDALMDArrayMask::Init() */
6843 : /************************************************************************/
6844 :
6845 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6846 : {
6847 : const auto GetSingleValNumericAttr =
6848 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6849 : {
6850 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6851 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6852 : {
6853 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6854 21 : if (anDimSizes.empty() ||
6855 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6856 : {
6857 11 : bHasVal = true;
6858 11 : dfVal = poAttr->ReadAsDouble();
6859 : }
6860 : }
6861 192 : };
6862 :
6863 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6864 48 : m_dfMissingValue);
6865 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6866 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6867 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6868 :
6869 : {
6870 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6871 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6872 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6873 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6874 : {
6875 6 : m_bHasValidMin = true;
6876 6 : m_bHasValidMax = true;
6877 6 : auto vals = poValidRange->ReadAsDoubleArray();
6878 6 : CPLAssert(vals.size() == 2);
6879 6 : m_dfValidMin = vals[0];
6880 6 : m_dfValidMax = vals[1];
6881 : }
6882 : }
6883 :
6884 : // Take into account
6885 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6886 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6887 : const char *pszUnmaskFlags =
6888 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6889 48 : if (pszUnmaskFlags)
6890 : {
6891 : const auto IsScalarStringAttr =
6892 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6893 : {
6894 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6895 26 : (poAttr->GetDimensionsSize().empty() ||
6896 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6897 26 : poAttr->GetDimensionsSize()[0] == 1));
6898 : };
6899 :
6900 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6901 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6902 : {
6903 1 : CPLError(CE_Failure, CPLE_AppDefined,
6904 : "UNMASK_FLAGS option specified but array has no "
6905 : "flag_meanings attribute");
6906 1 : return false;
6907 : }
6908 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6909 13 : if (!pszFlagMeanings)
6910 : {
6911 1 : CPLError(CE_Failure, CPLE_AppDefined,
6912 : "Cannot read flag_meanings attribute");
6913 1 : return false;
6914 : }
6915 :
6916 : const auto IsSingleDimNumericAttr =
6917 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6918 : {
6919 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6920 26 : poAttr->GetDimensionsSize().size() == 1;
6921 : };
6922 :
6923 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6924 : const bool bHasFlagValues =
6925 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6926 :
6927 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6928 : const bool bHasFlagMasks =
6929 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6930 :
6931 12 : if (!bHasFlagValues && !bHasFlagMasks)
6932 : {
6933 1 : CPLError(CE_Failure, CPLE_AppDefined,
6934 : "Cannot find flag_values and/or flag_masks attribute");
6935 1 : return false;
6936 : }
6937 :
6938 : const CPLStringList aosUnmaskFlags(
6939 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6940 : const CPLStringList aosFlagMeanings(
6941 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6942 :
6943 11 : if (bHasFlagValues)
6944 : {
6945 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6946 : // We could support Int64 or UInt64, but more work...
6947 7 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6948 7 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6949 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6950 : {
6951 0 : CPLError(CE_Failure, CPLE_NotSupported,
6952 : "Unsupported data type for flag_values attribute: %s",
6953 : GDALGetDataTypeName(eType));
6954 0 : return false;
6955 : }
6956 : }
6957 :
6958 11 : if (bHasFlagMasks)
6959 : {
6960 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6961 : // We could support Int64 or UInt64, but more work...
6962 6 : if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6963 6 : eType != GDT_UInt16 && eType != GDT_Int16 &&
6964 0 : eType != GDT_UInt32 && eType != GDT_Int32)
6965 : {
6966 0 : CPLError(CE_Failure, CPLE_NotSupported,
6967 : "Unsupported data type for flag_masks attribute: %s",
6968 : GDALGetDataTypeName(eType));
6969 0 : return false;
6970 : }
6971 : }
6972 :
6973 : const std::vector<double> adfValues(
6974 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6975 11 : : std::vector<double>());
6976 : const std::vector<double> adfMasks(
6977 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6978 11 : : std::vector<double>());
6979 :
6980 18 : if (bHasFlagValues &&
6981 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6982 : {
6983 1 : CPLError(CE_Failure, CPLE_AppDefined,
6984 : "Number of values in flag_values attribute is different "
6985 : "from the one in flag_meanings");
6986 1 : return false;
6987 : }
6988 :
6989 16 : if (bHasFlagMasks &&
6990 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6991 : {
6992 1 : CPLError(CE_Failure, CPLE_AppDefined,
6993 : "Number of values in flag_masks attribute is different "
6994 : "from the one in flag_meanings");
6995 1 : return false;
6996 : }
6997 :
6998 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6999 : {
7000 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
7001 11 : if (nIdxFlag < 0)
7002 : {
7003 1 : CPLError(
7004 : CE_Failure, CPLE_AppDefined,
7005 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
7006 : aosUnmaskFlags[i], pszFlagMeanings);
7007 1 : return false;
7008 : }
7009 :
7010 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
7011 : {
7012 0 : CPLError(CE_Failure, CPLE_AppDefined,
7013 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
7014 0 : adfValues[nIdxFlag]);
7015 0 : return false;
7016 : }
7017 :
7018 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
7019 : {
7020 0 : CPLError(CE_Failure, CPLE_AppDefined,
7021 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
7022 0 : adfMasks[nIdxFlag]);
7023 0 : return false;
7024 : }
7025 :
7026 10 : if (bHasFlagValues)
7027 : {
7028 12 : m_anValidFlagValues.push_back(
7029 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
7030 : }
7031 :
7032 10 : if (bHasFlagMasks)
7033 : {
7034 12 : m_anValidFlagMasks.push_back(
7035 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
7036 : }
7037 : }
7038 : }
7039 :
7040 42 : return true;
7041 : }
7042 :
7043 : /************************************************************************/
7044 : /* IRead() */
7045 : /************************************************************************/
7046 :
7047 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7048 : const GInt64 *arrayStep,
7049 : const GPtrDiff_t *bufferStride,
7050 : const GDALExtendedDataType &bufferDataType,
7051 : void *pDstBuffer) const
7052 : {
7053 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7054 : {
7055 0 : CPLError(CE_Failure, CPLE_AppDefined,
7056 : "%s: only reading to a numeric data type is supported",
7057 : __func__);
7058 0 : return false;
7059 : }
7060 51 : size_t nElts = 1;
7061 51 : const size_t nDims = GetDimensionCount();
7062 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7063 139 : for (size_t i = 0; i < nDims; i++)
7064 88 : nElts *= count[i];
7065 51 : if (nDims > 0)
7066 : {
7067 46 : tmpBufferStrideVector.back() = 1;
7068 88 : for (size_t i = nDims - 1; i > 0;)
7069 : {
7070 42 : --i;
7071 42 : tmpBufferStrideVector[i] =
7072 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7073 : }
7074 : }
7075 :
7076 : /* Optimized case: if we are an integer data type and that there is no */
7077 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7078 : /* directly */
7079 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7080 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7081 34 : m_anValidFlagMasks.empty() &&
7082 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7083 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7084 : {
7085 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7086 7 : if (bBufferDataTypeIsByte) // Byte case
7087 : {
7088 4 : bool bContiguous = true;
7089 10 : for (size_t i = 0; i < nDims; i++)
7090 : {
7091 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7092 : {
7093 1 : bContiguous = false;
7094 1 : break;
7095 : }
7096 : }
7097 4 : if (bContiguous)
7098 : {
7099 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7100 3 : memset(pDstBuffer, 1, nElts);
7101 3 : return true;
7102 : }
7103 : }
7104 :
7105 : struct Stack
7106 : {
7107 : size_t nIters = 0;
7108 : GByte *dst_ptr = nullptr;
7109 : GPtrDiff_t dst_inc_offset = 0;
7110 : };
7111 :
7112 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7113 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7114 13 : for (size_t i = 0; i < nDims; i++)
7115 : {
7116 9 : stack[i].dst_inc_offset =
7117 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7118 : }
7119 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7120 :
7121 4 : size_t dimIdx = 0;
7122 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7123 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7124 4 : CPLAssert(nBufferDTSize <= 16);
7125 4 : const GByte flag = 1;
7126 4 : GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
7127 : bufferDataType.GetNumericDataType(), 0, 1);
7128 :
7129 28 : lbl_next_depth:
7130 28 : if (dimIdx == nDimsMinus1)
7131 : {
7132 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7133 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7134 :
7135 : while (true)
7136 : {
7137 : // cppcheck-suppress knownConditionTrueFalse
7138 73 : if (bBufferDataTypeIsByte)
7139 : {
7140 24 : *dst_ptr = flag;
7141 : }
7142 : else
7143 : {
7144 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7145 : }
7146 :
7147 73 : if ((--nIters) == 0)
7148 19 : break;
7149 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7150 : }
7151 : }
7152 : else
7153 : {
7154 9 : stack[dimIdx].nIters = count[dimIdx];
7155 : while (true)
7156 : {
7157 24 : dimIdx++;
7158 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7159 24 : goto lbl_next_depth;
7160 24 : lbl_return_to_caller:
7161 24 : dimIdx--;
7162 24 : if ((--stack[dimIdx].nIters) == 0)
7163 9 : break;
7164 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7165 : }
7166 : }
7167 28 : if (dimIdx > 0)
7168 24 : goto lbl_return_to_caller;
7169 :
7170 4 : return true;
7171 : }
7172 :
7173 : const auto oTmpBufferDT =
7174 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7175 : ? GDALExtendedDataType::Create(GDT_Float64)
7176 88 : : m_poParent->GetDataType();
7177 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7178 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7179 44 : if (!pTempBuffer)
7180 0 : return false;
7181 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7182 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7183 : pTempBuffer))
7184 : {
7185 0 : VSIFree(pTempBuffer);
7186 0 : return false;
7187 : }
7188 :
7189 44 : switch (oTmpBufferDT.GetNumericDataType())
7190 : {
7191 7 : case GDT_UInt8:
7192 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7193 : pTempBuffer, oTmpBufferDT,
7194 : tmpBufferStrideVector);
7195 7 : break;
7196 :
7197 0 : case GDT_Int8:
7198 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7199 : pTempBuffer, oTmpBufferDT,
7200 : tmpBufferStrideVector);
7201 0 : break;
7202 :
7203 1 : case GDT_UInt16:
7204 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7205 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7206 : tmpBufferStrideVector);
7207 1 : break;
7208 :
7209 14 : case GDT_Int16:
7210 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7211 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7212 : tmpBufferStrideVector);
7213 14 : break;
7214 :
7215 1 : case GDT_UInt32:
7216 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7217 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7218 : tmpBufferStrideVector);
7219 1 : break;
7220 :
7221 5 : case GDT_Int32:
7222 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7223 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7224 : tmpBufferStrideVector);
7225 5 : break;
7226 :
7227 0 : case GDT_UInt64:
7228 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7229 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7230 : tmpBufferStrideVector);
7231 0 : break;
7232 :
7233 0 : case GDT_Int64:
7234 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7235 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7236 : tmpBufferStrideVector);
7237 0 : break;
7238 :
7239 0 : case GDT_Float16:
7240 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7241 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7242 : tmpBufferStrideVector);
7243 0 : break;
7244 :
7245 7 : case GDT_Float32:
7246 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7247 : pTempBuffer, oTmpBufferDT,
7248 : tmpBufferStrideVector);
7249 7 : break;
7250 :
7251 9 : case GDT_Float64:
7252 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7253 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7254 : tmpBufferStrideVector);
7255 9 : break;
7256 0 : case GDT_Unknown:
7257 : case GDT_CInt16:
7258 : case GDT_CInt32:
7259 : case GDT_CFloat16:
7260 : case GDT_CFloat32:
7261 : case GDT_CFloat64:
7262 : case GDT_TypeCount:
7263 0 : CPLAssert(false);
7264 : break;
7265 : }
7266 :
7267 44 : VSIFree(pTempBuffer);
7268 :
7269 44 : return true;
7270 : }
7271 :
7272 : /************************************************************************/
7273 : /* IsValidForDT() */
7274 : /************************************************************************/
7275 :
7276 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7277 : {
7278 40 : if (std::isnan(dfVal))
7279 0 : return false;
7280 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7281 0 : return false;
7282 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7283 0 : return false;
7284 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7285 : }
7286 :
7287 9 : template <> bool IsValidForDT<double>(double)
7288 : {
7289 9 : return true;
7290 : }
7291 :
7292 : /************************************************************************/
7293 : /* IsNan() */
7294 : /************************************************************************/
7295 :
7296 1438 : template <typename Type> inline bool IsNan(Type)
7297 : {
7298 1438 : return false;
7299 : }
7300 :
7301 65 : template <> bool IsNan<double>(double val)
7302 : {
7303 65 : return std::isnan(val);
7304 : }
7305 :
7306 26 : template <> bool IsNan<float>(float val)
7307 : {
7308 26 : return std::isnan(val);
7309 : }
7310 :
7311 : /************************************************************************/
7312 : /* ReadInternal() */
7313 : /************************************************************************/
7314 :
7315 : template <typename Type>
7316 44 : void GDALMDArrayMask::ReadInternal(
7317 : const size_t *count, const GPtrDiff_t *bufferStride,
7318 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7319 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7320 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7321 : {
7322 44 : const size_t nDims = GetDimensionCount();
7323 :
7324 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7325 : {
7326 220 : if (bHasVal)
7327 : {
7328 49 : if (IsValidForDT<Type>(dfVal))
7329 : {
7330 49 : return static_cast<Type>(dfVal);
7331 : }
7332 : else
7333 : {
7334 0 : bHasVal = false;
7335 : }
7336 : }
7337 171 : return 0;
7338 : };
7339 :
7340 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7341 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7342 : const Type nNoDataValue =
7343 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7344 44 : bool bHasMissingValue = m_bHasMissingValue;
7345 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7346 44 : bool bHasFillValue = m_bHasFillValue;
7347 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7348 44 : bool bHasValidMin = m_bHasValidMin;
7349 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7350 44 : bool bHasValidMax = m_bHasValidMax;
7351 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7352 44 : const bool bHasValidFlags =
7353 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7354 :
7355 351 : const auto IsValidFlag = [this](Type v)
7356 : {
7357 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7358 : {
7359 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7360 : {
7361 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7362 : m_anValidFlagValues[i])
7363 : {
7364 4 : return true;
7365 : }
7366 : }
7367 : }
7368 42 : else if (!m_anValidFlagValues.empty())
7369 : {
7370 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7371 : {
7372 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7373 : {
7374 4 : return true;
7375 : }
7376 : }
7377 : }
7378 : else /* if( !m_anValidFlagMasks.empty() ) */
7379 : {
7380 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7381 : {
7382 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7383 : {
7384 9 : return true;
7385 : }
7386 : }
7387 : }
7388 37 : return false;
7389 : };
7390 :
7391 : #define GET_MASK_FOR_SAMPLE(v) \
7392 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7393 : !(bHasMissingValue && v == nMissingValue) && \
7394 : !(bHasFillValue && v == nFillValue) && \
7395 : !(bHasValidMin && v < nValidMin) && \
7396 : !(bHasValidMax && v > nValidMax) && \
7397 : (!bHasValidFlags || IsValidFlag(v)));
7398 :
7399 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7400 : /* Optimized case: Byte output and output buffer is contiguous */
7401 44 : if (bBufferDataTypeIsByte)
7402 : {
7403 40 : bool bContiguous = true;
7404 103 : for (size_t i = 0; i < nDims; i++)
7405 : {
7406 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7407 : {
7408 1 : bContiguous = false;
7409 1 : break;
7410 : }
7411 : }
7412 40 : if (bContiguous)
7413 : {
7414 39 : size_t nElts = 1;
7415 102 : for (size_t i = 0; i < nDims; i++)
7416 63 : nElts *= count[i];
7417 :
7418 1113 : for (size_t i = 0; i < nElts; i++)
7419 : {
7420 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7421 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7422 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7423 : }
7424 39 : return;
7425 : }
7426 : }
7427 :
7428 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7429 :
7430 : struct Stack
7431 : {
7432 : size_t nIters = 0;
7433 : const GByte *src_ptr = nullptr;
7434 : GByte *dst_ptr = nullptr;
7435 : GPtrDiff_t src_inc_offset = 0;
7436 : GPtrDiff_t dst_inc_offset = 0;
7437 : };
7438 :
7439 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7440 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7441 15 : for (size_t i = 0; i < nDims; i++)
7442 : {
7443 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7444 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7445 10 : stack[i].dst_inc_offset =
7446 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7447 : }
7448 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7449 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7450 :
7451 5 : size_t dimIdx = 0;
7452 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7453 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7454 5 : CPLAssert(nBufferDTSize <= 16);
7455 15 : for (GByte flag = 0; flag <= 1; flag++)
7456 : {
7457 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7458 : bufferDataType.GetNumericDataType(), 0, 1);
7459 : }
7460 :
7461 43 : lbl_next_depth:
7462 43 : if (dimIdx == nDimsMinus1)
7463 : {
7464 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7465 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7466 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7467 :
7468 420 : while (true)
7469 : {
7470 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7471 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7472 :
7473 455 : if (bBufferDataTypeIsByte)
7474 : {
7475 24 : *dst_ptr = flag;
7476 : }
7477 : else
7478 : {
7479 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7480 : }
7481 :
7482 455 : if ((--nIters) == 0)
7483 35 : break;
7484 420 : src_ptr += stack[dimIdx].src_inc_offset;
7485 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7486 : }
7487 : }
7488 : else
7489 : {
7490 8 : stack[dimIdx].nIters = count[dimIdx];
7491 : while (true)
7492 : {
7493 38 : dimIdx++;
7494 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7495 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7496 38 : goto lbl_next_depth;
7497 38 : lbl_return_to_caller:
7498 38 : dimIdx--;
7499 38 : if ((--stack[dimIdx].nIters) == 0)
7500 8 : break;
7501 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7502 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7503 : }
7504 : }
7505 43 : if (dimIdx > 0)
7506 38 : goto lbl_return_to_caller;
7507 : }
7508 :
7509 : /************************************************************************/
7510 : /* GetMask() */
7511 : /************************************************************************/
7512 :
7513 : /** Return an array that is a mask for the current array
7514 :
7515 : This array will be of type Byte, with values set to 0 to indicate invalid
7516 : pixels of the current array, and values set to 1 to indicate valid pixels.
7517 :
7518 : The generic implementation honours the NoDataValue, as well as various
7519 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7520 : and valid_range.
7521 :
7522 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7523 : can be used to specify strings of the "flag_meanings" attribute
7524 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7525 : for which pixels matching any of those flags will be set at 1 in the mask array,
7526 : and pixels matching none of those flags will be set at 0.
7527 : For example, let's consider the following netCDF variable defined with:
7528 : \verbatim
7529 : l2p_flags:valid_min = 0s ;
7530 : l2p_flags:valid_max = 256s ;
7531 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7532 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7533 : \endverbatim
7534 :
7535 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7536 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7537 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7538 : will be 1.
7539 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7540 : will be 0.
7541 :
7542 : This is the same as the C function GDALMDArrayGetMask().
7543 :
7544 : @param papszOptions NULL-terminated list of options, or NULL.
7545 :
7546 : @return a new array, that holds a reference to the original one, and thus is
7547 : a view of it (not a copy), or nullptr in case of error.
7548 : */
7549 : std::shared_ptr<GDALMDArray>
7550 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7551 : {
7552 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7553 49 : if (!self)
7554 : {
7555 0 : CPLError(CE_Failure, CPLE_AppDefined,
7556 : "Driver implementation issue: m_pSelf not set !");
7557 0 : return nullptr;
7558 : }
7559 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7560 : {
7561 1 : CPLError(CE_Failure, CPLE_AppDefined,
7562 : "GetMask() only supports numeric data type");
7563 1 : return nullptr;
7564 : }
7565 48 : return GDALMDArrayMask::Create(self, papszOptions);
7566 : }
7567 :
7568 : /************************************************************************/
7569 : /* IsRegularlySpaced() */
7570 : /************************************************************************/
7571 :
7572 : /** Returns whether an array is a 1D regularly spaced array.
7573 : *
7574 : * @param[out] dfStart First value in the array
7575 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7576 : * @return true if the array is regularly spaced.
7577 : */
7578 347 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7579 : {
7580 347 : dfStart = 0;
7581 347 : dfIncrement = 0;
7582 347 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7583 0 : return false;
7584 347 : const auto nSize = GetDimensions()[0]->GetSize();
7585 347 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7586 2 : return false;
7587 :
7588 345 : size_t nCount = static_cast<size_t>(nSize);
7589 690 : std::vector<double> adfTmp;
7590 : try
7591 : {
7592 345 : adfTmp.resize(nCount);
7593 : }
7594 0 : catch (const std::exception &)
7595 : {
7596 0 : return false;
7597 : }
7598 :
7599 345 : GUInt64 anStart[1] = {0};
7600 345 : size_t anCount[1] = {nCount};
7601 :
7602 : const auto IsRegularlySpacedInternal =
7603 44415 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7604 : {
7605 398 : dfStart = adfTmp[0];
7606 398 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7607 398 : if (dfIncrement == 0)
7608 : {
7609 3 : return false;
7610 : }
7611 10993 : for (size_t i = 1; i < anCount[0]; i++)
7612 : {
7613 10610 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7614 10610 : 1e-3 * fabs(dfIncrement))
7615 : {
7616 12 : return false;
7617 : }
7618 : }
7619 383 : return true;
7620 345 : };
7621 :
7622 : // First try with the first block(s). This can avoid excessive processing
7623 : // time, for example with Zarr datasets.
7624 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7625 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7626 345 : const auto nBlockSize = GetBlockSize()[0];
7627 345 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7628 : {
7629 : size_t nReducedCount =
7630 56 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7631 176 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7632 120 : nReducedCount *= 2;
7633 56 : anCount[0] = nReducedCount;
7634 56 : if (!Read(anStart, anCount, nullptr, nullptr,
7635 112 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7636 : {
7637 0 : return false;
7638 : }
7639 56 : if (!IsRegularlySpacedInternal())
7640 : {
7641 3 : return false;
7642 : }
7643 :
7644 : // Get next values
7645 53 : anStart[0] = nReducedCount;
7646 53 : anCount[0] = nCount - nReducedCount;
7647 : }
7648 :
7649 342 : if (!Read(anStart, anCount, nullptr, nullptr,
7650 684 : GDALExtendedDataType::Create(GDT_Float64),
7651 342 : &adfTmp[static_cast<size_t>(anStart[0])]))
7652 : {
7653 0 : return false;
7654 : }
7655 :
7656 342 : return IsRegularlySpacedInternal();
7657 : }
7658 :
7659 : /************************************************************************/
7660 : /* GuessGeoTransform() */
7661 : /************************************************************************/
7662 :
7663 : /** Returns whether 2 specified dimensions form a geotransform
7664 : *
7665 : * @param nDimX Index of the X axis.
7666 : * @param nDimY Index of the Y axis.
7667 : * @param bPixelIsPoint Whether the geotransform should be returned
7668 : * with the pixel-is-point (pixel-center) convention
7669 : * (bPixelIsPoint = true), or with the pixel-is-area
7670 : * (top left corner convention)
7671 : * (bPixelIsPoint = false)
7672 : * @param[out] gt Computed geotransform
7673 : * @return true if a geotransform could be computed.
7674 : */
7675 462 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7676 : bool bPixelIsPoint,
7677 : GDALGeoTransform >) const
7678 : {
7679 462 : const auto &dims(GetDimensions());
7680 924 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7681 924 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7682 462 : double dfXStart = 0.0;
7683 462 : double dfXSpacing = 0.0;
7684 462 : double dfYStart = 0.0;
7685 462 : double dfYSpacing = 0.0;
7686 1068 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7687 606 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7688 885 : poVarY && poVarY->GetDimensionCount() == 1 &&
7689 291 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7690 1051 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7691 286 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7692 : {
7693 286 : gt.xorig = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7694 286 : gt.xscale = dfXSpacing;
7695 286 : gt.xrot = 0;
7696 286 : gt.yorig = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7697 286 : gt.yrot = 0;
7698 286 : gt.yscale = dfYSpacing;
7699 286 : return true;
7700 : }
7701 176 : return false;
7702 : }
7703 :
7704 : /** Returns whether 2 specified dimensions form a geotransform
7705 : *
7706 : * @param nDimX Index of the X axis.
7707 : * @param nDimY Index of the Y axis.
7708 : * @param bPixelIsPoint Whether the geotransform should be returned
7709 : * with the pixel-is-point (pixel-center) convention
7710 : * (bPixelIsPoint = true), or with the pixel-is-area
7711 : * (top left corner convention)
7712 : * (bPixelIsPoint = false)
7713 : * @param[out] adfGeoTransform Computed geotransform
7714 : * @return true if a geotransform could be computed.
7715 : */
7716 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7717 : bool bPixelIsPoint,
7718 : double adfGeoTransform[6]) const
7719 : {
7720 0 : GDALGeoTransform *gt =
7721 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7722 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7723 : }
7724 :
7725 : /************************************************************************/
7726 : /* GDALMDArrayResampled */
7727 : /************************************************************************/
7728 :
7729 : class GDALMDArrayResampledDataset;
7730 :
7731 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7732 : {
7733 : protected:
7734 : CPLErr IReadBlock(int, int, void *) override;
7735 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7736 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7737 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7738 : GSpacing nLineSpaceBuf,
7739 : GDALRasterIOExtraArg *psExtraArg) override;
7740 :
7741 : public:
7742 : explicit GDALMDArrayResampledDatasetRasterBand(
7743 : GDALMDArrayResampledDataset *poDSIn);
7744 :
7745 : double GetNoDataValue(int *pbHasNoData) override;
7746 : };
7747 :
7748 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7749 : {
7750 : friend class GDALMDArrayResampled;
7751 : friend class GDALMDArrayResampledDatasetRasterBand;
7752 :
7753 : std::shared_ptr<GDALMDArray> m_poArray;
7754 : const size_t m_iXDim;
7755 : const size_t m_iYDim;
7756 : GDALGeoTransform m_gt{};
7757 : bool m_bHasGT = false;
7758 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7759 :
7760 : std::vector<GUInt64> m_anOffset{};
7761 : std::vector<size_t> m_anCount{};
7762 : std::vector<GPtrDiff_t> m_anStride{};
7763 :
7764 : std::string m_osFilenameLong{};
7765 : std::string m_osFilenameLat{};
7766 :
7767 : public:
7768 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7769 : size_t iXDim, size_t iYDim)
7770 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7771 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7772 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7773 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7774 : {
7775 24 : const auto &dims(m_poArray->GetDimensions());
7776 :
7777 24 : nRasterYSize = static_cast<int>(
7778 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7779 24 : nRasterXSize = static_cast<int>(
7780 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7781 :
7782 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7783 :
7784 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7785 24 : }
7786 :
7787 : ~GDALMDArrayResampledDataset() override;
7788 :
7789 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7790 : {
7791 43 : gt = m_gt;
7792 43 : return m_bHasGT ? CE_None : CE_Failure;
7793 : }
7794 :
7795 105 : const OGRSpatialReference *GetSpatialRef() const override
7796 : {
7797 105 : m_poSRS = m_poArray->GetSpatialRef();
7798 105 : if (m_poSRS)
7799 : {
7800 79 : m_poSRS.reset(m_poSRS->Clone());
7801 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7802 237 : for (auto &m : axisMapping)
7803 : {
7804 158 : if (m == static_cast<int>(m_iXDim) + 1)
7805 79 : m = 1;
7806 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7807 79 : m = 2;
7808 : }
7809 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7810 : }
7811 105 : return m_poSRS.get();
7812 : }
7813 :
7814 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7815 : const std::string &osFilenameLat)
7816 : {
7817 5 : m_osFilenameLong = osFilenameLong;
7818 5 : m_osFilenameLat = osFilenameLat;
7819 10 : CPLStringList aosGeoLoc;
7820 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7821 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7822 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7823 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7824 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7825 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7826 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7827 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7828 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7829 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7830 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7831 5 : }
7832 : };
7833 :
7834 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7835 : {
7836 24 : if (!m_osFilenameLong.empty())
7837 5 : VSIUnlink(m_osFilenameLong.c_str());
7838 24 : if (!m_osFilenameLat.empty())
7839 5 : VSIUnlink(m_osFilenameLat.c_str());
7840 48 : }
7841 :
7842 : /************************************************************************/
7843 : /* GDALMDArrayResampledDatasetRasterBand() */
7844 : /************************************************************************/
7845 :
7846 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7847 24 : GDALMDArrayResampledDataset *poDSIn)
7848 : {
7849 24 : const auto &poArray(poDSIn->m_poArray);
7850 24 : const auto blockSize(poArray->GetBlockSize());
7851 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7852 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7853 13 : blockSize[poDSIn->m_iYDim]))
7854 24 : : 1;
7855 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7856 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7857 13 : blockSize[poDSIn->m_iXDim]))
7858 24 : : poDSIn->GetRasterXSize();
7859 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7860 24 : eAccess = poDSIn->eAccess;
7861 24 : }
7862 :
7863 : /************************************************************************/
7864 : /* GetNoDataValue() */
7865 : /************************************************************************/
7866 :
7867 54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7868 : {
7869 54 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7870 54 : const auto &poArray(l_poDS->m_poArray);
7871 54 : bool bHasNodata = false;
7872 54 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7873 54 : if (pbHasNoData)
7874 48 : *pbHasNoData = bHasNodata;
7875 54 : return dfRes;
7876 : }
7877 :
7878 : /************************************************************************/
7879 : /* IReadBlock() */
7880 : /************************************************************************/
7881 :
7882 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7883 : int nBlockYOff,
7884 : void *pImage)
7885 : {
7886 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7887 0 : const int nXOff = nBlockXOff * nBlockXSize;
7888 0 : const int nYOff = nBlockYOff * nBlockYSize;
7889 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7890 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7891 : GDALRasterIOExtraArg sExtraArg;
7892 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7893 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7894 : nReqXSize, nReqYSize, eDataType, nDTSize,
7895 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7896 : }
7897 :
7898 : /************************************************************************/
7899 : /* IRasterIO() */
7900 : /************************************************************************/
7901 :
7902 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7903 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7904 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7905 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7906 : GDALRasterIOExtraArg *psExtraArg)
7907 : {
7908 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7909 32 : const auto &poArray(l_poDS->m_poArray);
7910 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7911 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7912 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7913 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7914 : {
7915 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7916 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7917 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7918 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7919 :
7920 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7921 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7922 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7923 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7924 :
7925 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7926 32 : l_poDS->m_anCount.data(), nullptr,
7927 32 : l_poDS->m_anStride.data(),
7928 64 : GDALExtendedDataType::Create(eBufType), pData)
7929 32 : ? CE_None
7930 32 : : CE_Failure;
7931 : }
7932 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7933 : pData, nBufXSize, nBufYSize, eBufType,
7934 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7935 : }
7936 :
7937 : class GDALMDArrayResampled final : public GDALPamMDArray
7938 : {
7939 : private:
7940 : std::shared_ptr<GDALMDArray> m_poParent{};
7941 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7942 : std::vector<GUInt64> m_anBlockSize;
7943 : GDALExtendedDataType m_dt;
7944 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7945 : std::shared_ptr<GDALMDArray> m_poVarX{};
7946 : std::shared_ptr<GDALMDArray> m_poVarY{};
7947 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7948 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7949 :
7950 : protected:
7951 21 : GDALMDArrayResampled(
7952 : const std::shared_ptr<GDALMDArray> &poParent,
7953 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7954 : const std::vector<GUInt64> &anBlockSize)
7955 42 : : GDALAbstractMDArray(std::string(),
7956 42 : "Resampled view of " + poParent->GetFullName()),
7957 : GDALPamMDArray(
7958 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7959 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7960 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7961 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7962 : {
7963 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7964 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7965 21 : }
7966 :
7967 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7968 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7969 : const GDALExtendedDataType &bufferDataType,
7970 : void *pDstBuffer) const override;
7971 :
7972 : public:
7973 : static std::shared_ptr<GDALMDArray>
7974 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7975 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7976 : GDALRIOResampleAlg resampleAlg,
7977 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7978 :
7979 42 : ~GDALMDArrayResampled() override
7980 21 : {
7981 : // First close the warped VRT
7982 21 : m_poReprojectedDS.reset();
7983 21 : m_poParentDS.reset();
7984 42 : }
7985 :
7986 11 : bool IsWritable() const override
7987 : {
7988 11 : return false;
7989 : }
7990 :
7991 74 : const std::string &GetFilename() const override
7992 : {
7993 74 : return m_poParent->GetFilename();
7994 : }
7995 :
7996 : const std::vector<std::shared_ptr<GDALDimension>> &
7997 257 : GetDimensions() const override
7998 : {
7999 257 : return m_apoDims;
8000 : }
8001 :
8002 109 : const GDALExtendedDataType &GetDataType() const override
8003 : {
8004 109 : return m_dt;
8005 : }
8006 :
8007 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
8008 : {
8009 21 : return m_poSRS;
8010 : }
8011 :
8012 12 : std::vector<GUInt64> GetBlockSize() const override
8013 : {
8014 12 : return m_anBlockSize;
8015 : }
8016 :
8017 : std::shared_ptr<GDALAttribute>
8018 1 : GetAttribute(const std::string &osName) const override
8019 : {
8020 1 : return m_poParent->GetAttribute(osName);
8021 : }
8022 :
8023 : std::vector<std::shared_ptr<GDALAttribute>>
8024 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
8025 : {
8026 12 : return m_poParent->GetAttributes(papszOptions);
8027 : }
8028 :
8029 1 : const std::string &GetUnit() const override
8030 : {
8031 1 : return m_poParent->GetUnit();
8032 : }
8033 :
8034 1 : const void *GetRawNoDataValue() const override
8035 : {
8036 1 : return m_poParent->GetRawNoDataValue();
8037 : }
8038 :
8039 1 : double GetOffset(bool *pbHasOffset,
8040 : GDALDataType *peStorageType) const override
8041 : {
8042 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
8043 : }
8044 :
8045 1 : double GetScale(bool *pbHasScale,
8046 : GDALDataType *peStorageType) const override
8047 : {
8048 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
8049 : }
8050 : };
8051 :
8052 : /************************************************************************/
8053 : /* GDALMDArrayResampled::Create() */
8054 : /************************************************************************/
8055 :
8056 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8057 : const std::shared_ptr<GDALMDArray> &poParent,
8058 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8059 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8060 : CSLConstList /* papszOptions */)
8061 : {
8062 29 : const char *pszResampleAlg = "nearest";
8063 29 : bool unsupported = false;
8064 29 : switch (resampleAlg)
8065 : {
8066 16 : case GRIORA_NearestNeighbour:
8067 16 : pszResampleAlg = "nearest";
8068 16 : break;
8069 2 : case GRIORA_Bilinear:
8070 2 : pszResampleAlg = "bilinear";
8071 2 : break;
8072 5 : case GRIORA_Cubic:
8073 5 : pszResampleAlg = "cubic";
8074 5 : break;
8075 1 : case GRIORA_CubicSpline:
8076 1 : pszResampleAlg = "cubicspline";
8077 1 : break;
8078 1 : case GRIORA_Lanczos:
8079 1 : pszResampleAlg = "lanczos";
8080 1 : break;
8081 1 : case GRIORA_Average:
8082 1 : pszResampleAlg = "average";
8083 1 : break;
8084 1 : case GRIORA_Mode:
8085 1 : pszResampleAlg = "mode";
8086 1 : break;
8087 1 : case GRIORA_Gauss:
8088 1 : unsupported = true;
8089 1 : break;
8090 0 : case GRIORA_RESERVED_START:
8091 0 : unsupported = true;
8092 0 : break;
8093 0 : case GRIORA_RESERVED_END:
8094 0 : unsupported = true;
8095 0 : break;
8096 1 : case GRIORA_RMS:
8097 1 : pszResampleAlg = "rms";
8098 1 : break;
8099 : }
8100 29 : if (unsupported)
8101 : {
8102 1 : CPLError(CE_Failure, CPLE_NotSupported,
8103 : "Unsupported resample method for GetResampled()");
8104 1 : return nullptr;
8105 : }
8106 :
8107 28 : if (poParent->GetDimensionCount() < 2)
8108 : {
8109 1 : CPLError(CE_Failure, CPLE_NotSupported,
8110 : "GetResampled() only supports 2 dimensions or more");
8111 1 : return nullptr;
8112 : }
8113 :
8114 27 : const auto &aoParentDims = poParent->GetDimensions();
8115 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8116 : {
8117 2 : CPLError(CE_Failure, CPLE_AppDefined,
8118 : "GetResampled(): apoNewDims size should be the same as "
8119 : "GetDimensionCount()");
8120 2 : return nullptr;
8121 : }
8122 :
8123 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8124 25 : apoNewDims.reserve(apoNewDimsIn.size());
8125 :
8126 50 : std::vector<GUInt64> anBlockSize;
8127 25 : anBlockSize.reserve(apoNewDimsIn.size());
8128 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8129 :
8130 50 : auto apoParentDims = poParent->GetDimensions();
8131 : // Special case for NASA EMIT datasets
8132 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8133 7 : apoParentDims[0]->GetName() == "downtrack" &&
8134 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8135 2 : apoParentDims[2]->GetName() == "bands");
8136 :
8137 : const size_t iYDimParent =
8138 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8139 : const size_t iXDimParent =
8140 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8141 :
8142 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8143 : {
8144 53 : if (i == iYDimParent || i == iXDimParent)
8145 48 : continue;
8146 5 : if (apoNewDimsIn[i] == nullptr)
8147 : {
8148 3 : apoNewDims.emplace_back(aoParentDims[i]);
8149 : }
8150 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8151 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8152 : {
8153 1 : CPLError(CE_Failure, CPLE_AppDefined,
8154 : "GetResampled(): apoNewDims[%u] should be the same "
8155 : "as its parent",
8156 : i);
8157 1 : return nullptr;
8158 : }
8159 : else
8160 : {
8161 1 : apoNewDims.emplace_back(aoParentDims[i]);
8162 : }
8163 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8164 : }
8165 :
8166 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8167 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8168 :
8169 24 : double dfXStart = 0.0;
8170 24 : double dfXSpacing = 0.0;
8171 24 : bool gotXSpacing = false;
8172 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8173 24 : if (poNewDimX)
8174 : {
8175 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8176 : {
8177 0 : CPLError(CE_Failure, CPLE_NotSupported,
8178 : "Too big size for X dimension");
8179 0 : return nullptr;
8180 : }
8181 4 : auto var = poNewDimX->GetIndexingVariable();
8182 4 : if (var)
8183 : {
8184 2 : if (var->GetDimensionCount() != 1 ||
8185 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8186 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8187 : {
8188 0 : CPLError(CE_Failure, CPLE_NotSupported,
8189 : "New X dimension should be indexed by a regularly "
8190 : "spaced variable");
8191 0 : return nullptr;
8192 : }
8193 1 : gotXSpacing = true;
8194 : }
8195 : }
8196 :
8197 24 : double dfYStart = 0.0;
8198 24 : double dfYSpacing = 0.0;
8199 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8200 24 : bool gotYSpacing = false;
8201 24 : if (poNewDimY)
8202 : {
8203 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8204 : {
8205 0 : CPLError(CE_Failure, CPLE_NotSupported,
8206 : "Too big size for Y dimension");
8207 0 : return nullptr;
8208 : }
8209 4 : auto var = poNewDimY->GetIndexingVariable();
8210 4 : if (var)
8211 : {
8212 2 : if (var->GetDimensionCount() != 1 ||
8213 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8214 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8215 : {
8216 0 : CPLError(CE_Failure, CPLE_NotSupported,
8217 : "New Y dimension should be indexed by a regularly "
8218 : "spaced variable");
8219 0 : return nullptr;
8220 : }
8221 1 : gotYSpacing = true;
8222 : }
8223 : }
8224 :
8225 : // This limitation could probably be removed
8226 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8227 : {
8228 0 : CPLError(CE_Failure, CPLE_NotSupported,
8229 : "Either none of new X or Y dimension should have an indexing "
8230 : "variable, or both should both should have one.");
8231 0 : return nullptr;
8232 : }
8233 :
8234 48 : std::string osDstWKT;
8235 24 : if (poTargetSRS)
8236 : {
8237 2 : char *pszDstWKT = nullptr;
8238 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8239 : {
8240 0 : CPLFree(pszDstWKT);
8241 0 : return nullptr;
8242 : }
8243 2 : osDstWKT = pszDstWKT;
8244 2 : CPLFree(pszDstWKT);
8245 : }
8246 :
8247 : // Use coordinate variables for geolocation array
8248 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8249 24 : bool useGeolocationArray = false;
8250 24 : if (apoCoordinateVars.size() >= 2)
8251 : {
8252 0 : std::shared_ptr<GDALMDArray> poLongVar;
8253 0 : std::shared_ptr<GDALMDArray> poLatVar;
8254 15 : for (const auto &poCoordVar : apoCoordinateVars)
8255 : {
8256 10 : const auto &osName = poCoordVar->GetName();
8257 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8258 20 : std::string osStandardName;
8259 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8260 2 : poAttr->GetDimensionCount() == 0)
8261 : {
8262 2 : const char *pszStandardName = poAttr->ReadAsString();
8263 2 : if (pszStandardName)
8264 2 : osStandardName = pszStandardName;
8265 : }
8266 21 : if (osName == "lon" || osName == "longitude" ||
8267 21 : osName == "Longitude" || osStandardName == "longitude")
8268 : {
8269 5 : poLongVar = poCoordVar;
8270 : }
8271 6 : else if (osName == "lat" || osName == "latitude" ||
8272 6 : osName == "Latitude" || osStandardName == "latitude")
8273 : {
8274 5 : poLatVar = poCoordVar;
8275 : }
8276 : }
8277 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8278 : {
8279 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8280 5 : const auto &longDims = poLongVar->GetDimensions();
8281 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8282 5 : const auto &latDims = poLatVar->GetDimensions();
8283 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8284 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8285 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8286 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8287 : {
8288 : // Geolocation arrays are 1D, and of consistent size with
8289 : // the variable
8290 0 : useGeolocationArray = true;
8291 : }
8292 1 : else if ((longDimCount == 2 ||
8293 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8294 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8295 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8296 1 : (latDimCount == 2 ||
8297 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8298 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8299 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8300 :
8301 : {
8302 : // Geolocation arrays are 2D (or 3D with first dimension of
8303 : // size 1, as found in Sentinel 5P products), and of consistent
8304 : // size with the variable
8305 5 : useGeolocationArray = true;
8306 : }
8307 : else
8308 : {
8309 0 : CPLDebug(
8310 : "GDAL",
8311 : "Longitude and latitude coordinate variables found, "
8312 : "but their characteristics are not compatible of using "
8313 : "them as geolocation arrays");
8314 : }
8315 5 : if (useGeolocationArray)
8316 : {
8317 10 : CPLDebug("GDAL",
8318 : "Setting geolocation array from variables %s and %s",
8319 5 : poLongVar->GetName().c_str(),
8320 5 : poLatVar->GetName().c_str());
8321 : const std::string osFilenameLong =
8322 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8323 : const std::string osFilenameLat =
8324 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8325 : std::unique_ptr<GDALDataset> poTmpLongDS(
8326 : longDimCount == 1
8327 0 : ? poLongVar->AsClassicDataset(0, 0)
8328 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8329 15 : longDimCount - 2));
8330 5 : auto hTIFFLongDS = GDALTranslate(
8331 : osFilenameLong.c_str(),
8332 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8333 : std::unique_ptr<GDALDataset> poTmpLatDS(
8334 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8335 20 : : poLatVar->AsClassicDataset(
8336 15 : latDimCount - 1, latDimCount - 2));
8337 5 : auto hTIFFLatDS = GDALTranslate(
8338 : osFilenameLat.c_str(),
8339 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8340 5 : const bool bError =
8341 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8342 5 : GDALClose(hTIFFLongDS);
8343 5 : GDALClose(hTIFFLatDS);
8344 5 : if (bError)
8345 : {
8346 0 : VSIUnlink(osFilenameLong.c_str());
8347 0 : VSIUnlink(osFilenameLat.c_str());
8348 0 : return nullptr;
8349 : }
8350 :
8351 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8352 : }
8353 : }
8354 : else
8355 : {
8356 0 : CPLDebug("GDAL",
8357 : "Coordinate variables available for %s, but "
8358 : "longitude and/or latitude variables were not identified",
8359 0 : poParent->GetName().c_str());
8360 : }
8361 : }
8362 :
8363 : // Build gdalwarp arguments
8364 48 : CPLStringList aosArgv;
8365 :
8366 24 : aosArgv.AddString("-of");
8367 24 : aosArgv.AddString("VRT");
8368 :
8369 24 : aosArgv.AddString("-r");
8370 24 : aosArgv.AddString(pszResampleAlg);
8371 :
8372 24 : if (!osDstWKT.empty())
8373 : {
8374 2 : aosArgv.AddString("-t_srs");
8375 2 : aosArgv.AddString(osDstWKT.c_str());
8376 : }
8377 :
8378 24 : if (useGeolocationArray)
8379 5 : aosArgv.AddString("-geoloc");
8380 :
8381 24 : if (gotXSpacing && gotYSpacing)
8382 : {
8383 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8384 : const double dfXMax =
8385 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8386 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8387 : const double dfYMin =
8388 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8389 1 : aosArgv.AddString("-te");
8390 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8391 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8392 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8393 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8394 : }
8395 :
8396 24 : if (poNewDimX && poNewDimY)
8397 : {
8398 3 : aosArgv.AddString("-ts");
8399 : aosArgv.AddString(
8400 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8401 : aosArgv.AddString(
8402 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8403 : }
8404 21 : else if (poNewDimX)
8405 : {
8406 1 : aosArgv.AddString("-ts");
8407 : aosArgv.AddString(
8408 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8409 1 : aosArgv.AddString("0");
8410 : }
8411 20 : else if (poNewDimY)
8412 : {
8413 1 : aosArgv.AddString("-ts");
8414 1 : aosArgv.AddString("0");
8415 : aosArgv.AddString(
8416 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8417 : }
8418 :
8419 : // Create a warped VRT dataset
8420 : GDALWarpAppOptions *psOptions =
8421 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8422 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8423 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8424 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8425 24 : GDALWarpAppOptionsFree(psOptions);
8426 24 : if (poReprojectedDS == nullptr)
8427 3 : return nullptr;
8428 :
8429 : int nBlockXSize;
8430 : int nBlockYSize;
8431 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8432 21 : anBlockSize.emplace_back(nBlockYSize);
8433 21 : anBlockSize.emplace_back(nBlockXSize);
8434 :
8435 21 : GDALGeoTransform gt;
8436 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8437 21 : CPLAssert(eErr == CE_None);
8438 21 : CPL_IGNORE_RET_VAL(eErr);
8439 :
8440 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8441 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8442 42 : poReprojectedDS->GetRasterYSize());
8443 : auto varY = GDALMDArrayRegularlySpaced::Create(
8444 63 : std::string(), poDimY->GetName(), poDimY, gt.yorig + gt.yscale / 2,
8445 84 : gt.yscale, 0);
8446 21 : poDimY->SetIndexingVariable(varY);
8447 :
8448 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8449 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8450 42 : poReprojectedDS->GetRasterXSize());
8451 : auto varX = GDALMDArrayRegularlySpaced::Create(
8452 63 : std::string(), poDimX->GetName(), poDimX, gt.xorig + gt.xscale / 2,
8453 84 : gt.xscale, 0);
8454 21 : poDimX->SetIndexingVariable(varX);
8455 :
8456 21 : apoNewDims.emplace_back(poDimY);
8457 21 : apoNewDims.emplace_back(poDimX);
8458 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8459 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8460 21 : newAr->SetSelf(newAr);
8461 21 : if (poTargetSRS)
8462 : {
8463 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8464 : }
8465 : else
8466 : {
8467 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8468 : }
8469 21 : newAr->m_poVarX = varX;
8470 21 : newAr->m_poVarY = varY;
8471 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8472 21 : newAr->m_poParentDS = std::move(poParentDS);
8473 :
8474 : // If the input array is y,x,band ordered, the above newAr is
8475 : // actually band,y,x ordered as it is more convenient for
8476 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8477 : // array to the order of the input array
8478 21 : if (bYXBandOrder)
8479 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8480 :
8481 19 : return newAr;
8482 : }
8483 :
8484 : /************************************************************************/
8485 : /* GDALMDArrayResampled::IRead() */
8486 : /************************************************************************/
8487 :
8488 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8489 : const size_t *count, const GInt64 *arrayStep,
8490 : const GPtrDiff_t *bufferStride,
8491 : const GDALExtendedDataType &bufferDataType,
8492 : void *pDstBuffer) const
8493 : {
8494 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8495 0 : return false;
8496 :
8497 : struct Stack
8498 : {
8499 : size_t nIters = 0;
8500 : GByte *dst_ptr = nullptr;
8501 : GPtrDiff_t dst_inc_offset = 0;
8502 : };
8503 :
8504 29 : const auto nDims = GetDimensionCount();
8505 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8506 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8507 92 : for (size_t i = 0; i < nDims; i++)
8508 : {
8509 63 : stack[i].dst_inc_offset =
8510 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8511 : }
8512 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8513 :
8514 29 : size_t dimIdx = 0;
8515 29 : const size_t iDimY = nDims - 2;
8516 29 : const size_t iDimX = nDims - 1;
8517 : // Use an array to avoid a false positive warning from CLang Static
8518 : // Analyzer about flushCaches being never read
8519 29 : bool flushCaches[] = {false};
8520 : const bool bYXBandOrder =
8521 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8522 :
8523 38 : lbl_next_depth:
8524 38 : if (dimIdx == iDimY)
8525 : {
8526 33 : if (flushCaches[0])
8527 : {
8528 5 : flushCaches[0] = false;
8529 : // When changing of 2D slice, flush GDAL 2D buffers
8530 5 : m_poParentDS->FlushCache(false);
8531 5 : m_poReprojectedDS->FlushCache(false);
8532 : }
8533 :
8534 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8535 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8536 : arrayStep, bufferStride, bufferDataType,
8537 33 : stack[dimIdx].dst_ptr))
8538 : {
8539 0 : return false;
8540 : }
8541 : }
8542 : else
8543 : {
8544 5 : stack[dimIdx].nIters = count[dimIdx];
8545 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8546 5 : arrayStartIdx[dimIdx])
8547 : {
8548 1 : flushCaches[0] = true;
8549 : }
8550 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8551 5 : arrayStartIdx[dimIdx];
8552 : while (true)
8553 : {
8554 9 : dimIdx++;
8555 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8556 9 : goto lbl_next_depth;
8557 9 : lbl_return_to_caller:
8558 9 : dimIdx--;
8559 9 : if ((--stack[dimIdx].nIters) == 0)
8560 5 : break;
8561 4 : flushCaches[0] = true;
8562 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8563 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8564 : }
8565 : }
8566 38 : if (dimIdx > 0)
8567 9 : goto lbl_return_to_caller;
8568 :
8569 29 : return true;
8570 : }
8571 :
8572 : /************************************************************************/
8573 : /* GetResampled() */
8574 : /************************************************************************/
8575 :
8576 : /** Return an array that is a resampled / reprojected view of the current array
8577 : *
8578 : * This is the same as the C function GDALMDArrayGetResampled().
8579 : *
8580 : * Currently this method can only resample along the last 2 dimensions, unless
8581 : * orthorectifying a NASA EMIT dataset.
8582 : *
8583 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8584 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8585 : *
8586 : * Options available are:
8587 : * <ul>
8588 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8589 : * Can be set to NO to use generic reprojection method.
8590 : * </li>
8591 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8592 : * orthorectification to take into account the value of the
8593 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8594 : * current array along the band dimension are valid.</li>
8595 : * </ul>
8596 : *
8597 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8598 : * apoNewDims[i] can be NULL to let the method automatically
8599 : * determine it.
8600 : * @param resampleAlg Resampling algorithm
8601 : * @param poTargetSRS Target SRS, or nullptr
8602 : * @param papszOptions NULL-terminated list of options, or NULL.
8603 : *
8604 : * @return a new array, that holds a reference to the original one, and thus is
8605 : * a view of it (not a copy), or nullptr in case of error.
8606 : *
8607 : * @since 3.4
8608 : */
8609 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8610 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8611 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8612 : CSLConstList papszOptions) const
8613 : {
8614 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8615 38 : if (!self)
8616 : {
8617 0 : CPLError(CE_Failure, CPLE_AppDefined,
8618 : "Driver implementation issue: m_pSelf not set !");
8619 0 : return nullptr;
8620 : }
8621 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8622 : {
8623 0 : CPLError(CE_Failure, CPLE_AppDefined,
8624 : "GetResampled() only supports numeric data type");
8625 0 : return nullptr;
8626 : }
8627 :
8628 : // Special case for NASA EMIT datasets
8629 76 : auto apoDims = GetDimensions();
8630 36 : if (poTargetSRS == nullptr &&
8631 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8632 20 : apoDims[1]->GetName() == "crosstrack" &&
8633 10 : apoDims[2]->GetName() == "bands" &&
8634 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8635 1 : apoNewDims ==
8636 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8637 30 : apoDims[2]})) ||
8638 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8639 3 : apoDims[1]->GetName() == "crosstrack" &&
8640 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8641 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8642 : "EMIT_ORTHORECTIFICATION", "YES")))
8643 : {
8644 9 : auto poRootGroup = GetRootGroup();
8645 9 : if (poRootGroup)
8646 : {
8647 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8648 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8649 9 : if (poAttrGeotransform &&
8650 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8651 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8652 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8653 9 : poLocationGroup)
8654 : {
8655 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8656 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8657 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8658 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8659 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8660 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8661 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8662 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8663 : {
8664 : return CreateGLTOrthorectified(
8665 : self, poRootGroup, poGLT_X, poGLT_Y,
8666 : /* nGLTIndexOffset = */ -1,
8667 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8668 : }
8669 : }
8670 : }
8671 : }
8672 :
8673 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8674 : "EMIT_ORTHORECTIFICATION", "NO")))
8675 : {
8676 0 : CPLError(CE_Failure, CPLE_AppDefined,
8677 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8678 : "parameters are not compatible with it");
8679 0 : return nullptr;
8680 : }
8681 :
8682 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8683 29 : poTargetSRS, papszOptions);
8684 : }
8685 :
8686 : /************************************************************************/
8687 : /* GDALDatasetFromArray() */
8688 : /************************************************************************/
8689 :
8690 : class GDALDatasetFromArray;
8691 :
8692 : namespace
8693 : {
8694 : struct MetadataItem
8695 : {
8696 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8697 : std::string osName{};
8698 : std::string osDefinition{};
8699 : bool bDefinitionUsesPctForG = false;
8700 : };
8701 :
8702 : struct BandImageryMetadata
8703 : {
8704 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8705 : double dfCentralWavelengthToMicrometer = 1.0;
8706 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8707 : double dfFWHMToMicrometer = 1.0;
8708 : };
8709 :
8710 : } // namespace
8711 :
8712 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8713 : {
8714 : std::vector<GUInt64> m_anOffset{};
8715 : std::vector<size_t> m_anCount{};
8716 : std::vector<GPtrDiff_t> m_anStride{};
8717 :
8718 : protected:
8719 : CPLErr IReadBlock(int, int, void *) override;
8720 : CPLErr IWriteBlock(int, int, void *) override;
8721 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8722 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8723 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8724 : GSpacing nLineSpaceBuf,
8725 : GDALRasterIOExtraArg *psExtraArg) override;
8726 :
8727 : public:
8728 : explicit GDALRasterBandFromArray(
8729 : GDALDatasetFromArray *poDSIn,
8730 : const std::vector<GUInt64> &anOtherDimCoord,
8731 : const std::vector<std::vector<MetadataItem>>
8732 : &aoBandParameterMetadataItems,
8733 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8734 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8735 :
8736 : double GetNoDataValue(int *pbHasNoData) override;
8737 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8738 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8739 : double GetOffset(int *pbHasOffset) override;
8740 : double GetScale(int *pbHasScale) override;
8741 : const char *GetUnitType() override;
8742 : GDALColorInterp GetColorInterpretation() override;
8743 : int GetOverviewCount() override;
8744 : GDALRasterBand *GetOverview(int idx) override;
8745 : CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
8746 : int nBufXSize, int nBufYSize, GDALDataType eBufType,
8747 : CSLConstList papszOptions) override;
8748 : };
8749 :
8750 : class GDALDatasetFromArray final : public GDALPamDataset
8751 : {
8752 : friend class GDALRasterBandFromArray;
8753 :
8754 : std::shared_ptr<GDALMDArray> m_poArray;
8755 : const size_t m_iXDim;
8756 : const size_t m_iYDim;
8757 : const CPLStringList m_aosOptions;
8758 : GDALGeoTransform m_gt{};
8759 : bool m_bHasGT = false;
8760 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8761 : GDALMultiDomainMetadata m_oMDD{};
8762 : std::string m_osOvrFilename{};
8763 : bool m_bOverviewsDiscovered = false;
8764 : std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
8765 : m_apoOverviews{};
8766 :
8767 : public:
8768 438 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8769 : size_t iXDim, size_t iYDim,
8770 : const CPLStringList &aosOptions)
8771 438 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
8772 438 : m_aosOptions(aosOptions)
8773 : {
8774 : // Initialize an overview filename from the filename of the array
8775 : // and its name.
8776 438 : const std::string &osFilename = m_poArray->GetFilename();
8777 438 : if (!osFilename.empty())
8778 : {
8779 396 : m_osOvrFilename = osFilename;
8780 396 : m_osOvrFilename += '.';
8781 25032 : for (char ch : m_poArray->GetName())
8782 : {
8783 24636 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8784 21512 : (ch >= 'a' && ch <= 'z') || ch == '_')
8785 : {
8786 19449 : m_osOvrFilename += ch;
8787 : }
8788 : else
8789 : {
8790 5187 : m_osOvrFilename += '_';
8791 : }
8792 : }
8793 396 : m_osOvrFilename += ".ovr";
8794 396 : oOvManager.Initialize(this);
8795 : }
8796 438 : }
8797 :
8798 : static std::unique_ptr<GDALDatasetFromArray>
8799 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8800 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8801 : CSLConstList papszOptions);
8802 :
8803 : ~GDALDatasetFromArray() override;
8804 :
8805 621 : CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override
8806 : {
8807 621 : CPLErr eErr = CE_None;
8808 621 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8809 : {
8810 621 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8811 : CE_None)
8812 0 : eErr = CE_Failure;
8813 621 : m_poArray.reset();
8814 : }
8815 621 : return eErr;
8816 : }
8817 :
8818 83 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8819 : {
8820 83 : gt = m_gt;
8821 83 : return m_bHasGT ? CE_None : CE_Failure;
8822 : }
8823 :
8824 93 : const OGRSpatialReference *GetSpatialRef() const override
8825 : {
8826 93 : if (m_poArray->GetDimensionCount() < 2)
8827 3 : return nullptr;
8828 90 : m_poSRS = m_poArray->GetSpatialRef();
8829 90 : if (m_poSRS)
8830 : {
8831 46 : m_poSRS.reset(m_poSRS->Clone());
8832 92 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8833 138 : for (auto &m : axisMapping)
8834 : {
8835 92 : if (m == static_cast<int>(m_iXDim) + 1)
8836 46 : m = 1;
8837 46 : else if (m == static_cast<int>(m_iYDim) + 1)
8838 46 : m = 2;
8839 : }
8840 46 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8841 : }
8842 90 : return m_poSRS.get();
8843 : }
8844 :
8845 8 : CPLErr SetMetadata(CSLConstList papszMetadata,
8846 : const char *pszDomain) override
8847 : {
8848 8 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8849 : }
8850 :
8851 183 : CSLConstList GetMetadata(const char *pszDomain) override
8852 : {
8853 183 : return m_oMDD.GetMetadata(pszDomain);
8854 : }
8855 :
8856 248 : const char *GetMetadataItem(const char *pszName,
8857 : const char *pszDomain) override
8858 : {
8859 449 : if (!m_osOvrFilename.empty() && pszName &&
8860 470 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8861 21 : EQUAL(pszDomain, "OVERVIEWS"))
8862 : {
8863 21 : return m_osOvrFilename.c_str();
8864 : }
8865 227 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8866 : }
8867 :
8868 63 : void DiscoverOverviews()
8869 : {
8870 63 : if (!m_bOverviewsDiscovered)
8871 : {
8872 21 : m_bOverviewsDiscovered = true;
8873 21 : if (const int nOverviews = m_poArray->GetOverviewCount())
8874 : {
8875 10 : if (auto poRootGroup = m_poArray->GetRootGroup())
8876 : {
8877 5 : const size_t nDims = m_poArray->GetDimensionCount();
8878 10 : CPLStringList aosOptions(m_aosOptions);
8879 5 : aosOptions.SetNameValue("LOAD_PAM", "NO");
8880 14 : for (int iOvr = 0; iOvr < nOverviews; ++iOvr)
8881 : {
8882 18 : if (auto poOvrArray = m_poArray->GetOverview(iOvr))
8883 : {
8884 18 : if (poOvrArray->GetDimensionCount() == nDims &&
8885 9 : poOvrArray->GetDataType() ==
8886 9 : m_poArray->GetDataType())
8887 : {
8888 : auto poOvrDS =
8889 9 : Create(poOvrArray, m_iXDim, m_iYDim,
8890 18 : poRootGroup, aosOptions);
8891 9 : if (poOvrDS)
8892 : {
8893 9 : m_apoOverviews.push_back(
8894 18 : std::unique_ptr<
8895 : GDALDataset,
8896 : GDALDatasetUniquePtrReleaser>(
8897 9 : poOvrDS.release()));
8898 : }
8899 : }
8900 : }
8901 : }
8902 : }
8903 : }
8904 : }
8905 63 : }
8906 : };
8907 :
8908 876 : GDALDatasetFromArray::~GDALDatasetFromArray()
8909 : {
8910 438 : GDALDatasetFromArray::Close();
8911 876 : }
8912 :
8913 : /************************************************************************/
8914 : /* GDALRasterBandFromArray() */
8915 : /************************************************************************/
8916 :
8917 515 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8918 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8919 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8920 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8921 515 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8922 : {
8923 515 : const auto &poArray(poDSIn->m_poArray);
8924 515 : const auto &dims(poArray->GetDimensions());
8925 515 : const auto nDimCount(dims.size());
8926 1030 : const auto blockSize(poArray->GetBlockSize());
8927 :
8928 496 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8929 1011 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8930 339 : blockSize[poDSIn->m_iYDim]))
8931 : : 1;
8932 515 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8933 356 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8934 356 : blockSize[poDSIn->m_iXDim]))
8935 515 : : poDSIn->GetRasterXSize();
8936 :
8937 515 : eDataType = poArray->GetDataType().GetNumericDataType();
8938 515 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
8939 :
8940 515 : if (nDTSize > 0)
8941 : {
8942 : // If the above computed block size exceeds INT_MAX or 1/100th of the
8943 : // maximum allowed size for the block cache, divide its shape by two,
8944 : // along the largest dimension. Only do that while there are at least
8945 : // one dimension with 2 pixels.
8946 12 : while (
8947 1009 : (nBlockXSize >= 2 || nBlockYSize >= 2) &&
8948 482 : (nBlockXSize > INT_MAX / nBlockYSize / nDTSize ||
8949 479 : (nBlockXSize > GDALGetCacheMax64() / 100 / nBlockYSize / nDTSize)))
8950 : {
8951 12 : if (nBlockXSize > nBlockYSize)
8952 12 : nBlockXSize /= 2;
8953 : else
8954 0 : nBlockYSize /= 2;
8955 : }
8956 : }
8957 :
8958 515 : eAccess = poDSIn->eAccess;
8959 515 : m_anOffset.resize(nDimCount);
8960 515 : m_anCount.resize(nDimCount, 1);
8961 515 : m_anStride.resize(nDimCount);
8962 1659 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8963 : {
8964 1144 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8965 : {
8966 266 : std::string dimName(dims[i]->GetName());
8967 133 : GUInt64 nIndex = anOtherDimCoord[j];
8968 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8969 : // subsetted dimensions as generated by GetView()
8970 133 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8971 : {
8972 : CPLStringList aosTokens(
8973 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8974 6 : if (aosTokens.size() == 5)
8975 : {
8976 6 : dimName = aosTokens[1];
8977 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8978 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8979 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8980 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8981 0 : : nStartDim - (nIndex * -nIncrDim);
8982 : }
8983 : }
8984 133 : if (nDimCount != 3 || dimName != "Band")
8985 : {
8986 72 : SetMetadataItem(
8987 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8988 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8989 : }
8990 :
8991 133 : auto indexingVar = dims[i]->GetIndexingVariable();
8992 :
8993 : // If the indexing variable is also listed in band parameter arrays,
8994 : // then don't use our default formatting
8995 133 : if (indexingVar)
8996 : {
8997 49 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8998 : {
8999 14 : if (oItem.poArray->GetFullName() ==
9000 14 : indexingVar->GetFullName())
9001 : {
9002 12 : indexingVar.reset();
9003 12 : break;
9004 : }
9005 : }
9006 : }
9007 :
9008 168 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
9009 35 : indexingVar->GetDimensions()[0]->GetSize() ==
9010 35 : dims[i]->GetSize())
9011 : {
9012 35 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
9013 : {
9014 0 : if (!bHasWarned)
9015 : {
9016 0 : CPLError(
9017 : CE_Warning, CPLE_AppDefined,
9018 : "Maximum delay to load band metadata from "
9019 : "dimension indexing variables has expired. "
9020 : "Increase the value of the "
9021 : "LOAD_EXTRA_DIM_METADATA_DELAY "
9022 : "option of GDALMDArray::AsClassicDataset() "
9023 : "(also accessible as the "
9024 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
9025 : "configuration option), "
9026 : "or set it to 'unlimited' for unlimited delay. ");
9027 0 : bHasWarned = true;
9028 : }
9029 : }
9030 : else
9031 : {
9032 35 : size_t nCount = 1;
9033 35 : const auto &dt(indexingVar->GetDataType());
9034 70 : std::vector<GByte> abyTmp(dt.GetSize());
9035 70 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
9036 35 : nullptr, nullptr, dt, &abyTmp[0]))
9037 : {
9038 35 : char *pszTmp = nullptr;
9039 35 : GDALExtendedDataType::CopyValue(
9040 35 : &abyTmp[0], dt, &pszTmp,
9041 70 : GDALExtendedDataType::CreateString());
9042 35 : dt.FreeDynamicMemory(abyTmp.data());
9043 35 : if (pszTmp)
9044 : {
9045 35 : SetMetadataItem(
9046 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
9047 : pszTmp);
9048 35 : CPLFree(pszTmp);
9049 : }
9050 :
9051 35 : const auto &unit(indexingVar->GetUnit());
9052 35 : if (!unit.empty())
9053 : {
9054 12 : SetMetadataItem(
9055 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
9056 : unit.c_str());
9057 : }
9058 : }
9059 : }
9060 : }
9061 :
9062 151 : for (const auto &oItem : aoBandParameterMetadataItems[j])
9063 : {
9064 36 : CPLString osVal;
9065 :
9066 18 : size_t nCount = 1;
9067 18 : const auto &dt(oItem.poArray->GetDataType());
9068 18 : if (oItem.bDefinitionUsesPctForG)
9069 : {
9070 : // There is one and only one %[x][.y]f|g in osDefinition
9071 16 : std::vector<GByte> abyTmp(dt.GetSize());
9072 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9073 8 : nullptr, nullptr, dt, &abyTmp[0]))
9074 : {
9075 8 : double dfVal = 0;
9076 8 : GDALExtendedDataType::CopyValue(
9077 8 : &abyTmp[0], dt, &dfVal,
9078 16 : GDALExtendedDataType::Create(GDT_Float64));
9079 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
9080 8 : dt.FreeDynamicMemory(abyTmp.data());
9081 : }
9082 : }
9083 : else
9084 : {
9085 : // There should be zero or one %s in osDefinition
9086 10 : char *pszValue = nullptr;
9087 10 : if (dt.GetClass() == GEDTC_STRING)
9088 : {
9089 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
9090 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
9091 2 : dt, &pszValue));
9092 : }
9093 : else
9094 : {
9095 16 : std::vector<GByte> abyTmp(dt.GetSize());
9096 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9097 : nullptr, nullptr, dt,
9098 8 : &abyTmp[0]))
9099 : {
9100 8 : GDALExtendedDataType::CopyValue(
9101 8 : &abyTmp[0], dt, &pszValue,
9102 16 : GDALExtendedDataType::CreateString());
9103 : }
9104 : }
9105 :
9106 10 : if (pszValue)
9107 : {
9108 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9109 10 : CPLFree(pszValue);
9110 : }
9111 : }
9112 18 : if (!osVal.empty())
9113 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9114 : }
9115 :
9116 133 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9117 : {
9118 : auto &poCentralWavelengthArray =
9119 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9120 4 : size_t nCount = 1;
9121 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9122 8 : std::vector<GByte> abyTmp(dt.GetSize());
9123 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9124 : &nCount, nullptr, nullptr,
9125 4 : dt, &abyTmp[0]))
9126 : {
9127 4 : double dfVal = 0;
9128 4 : GDALExtendedDataType::CopyValue(
9129 4 : &abyTmp[0], dt, &dfVal,
9130 8 : GDALExtendedDataType::Create(GDT_Float64));
9131 4 : dt.FreeDynamicMemory(abyTmp.data());
9132 4 : SetMetadataItem(
9133 : "CENTRAL_WAVELENGTH_UM",
9134 : CPLSPrintf(
9135 4 : "%g", dfVal * aoBandImageryMetadata[j]
9136 4 : .dfCentralWavelengthToMicrometer),
9137 : "IMAGERY");
9138 : }
9139 : }
9140 :
9141 133 : if (aoBandImageryMetadata[j].poFWHMArray)
9142 : {
9143 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9144 2 : size_t nCount = 1;
9145 2 : const auto &dt(poFWHMArray->GetDataType());
9146 4 : std::vector<GByte> abyTmp(dt.GetSize());
9147 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9148 2 : nullptr, dt, &abyTmp[0]))
9149 : {
9150 2 : double dfVal = 0;
9151 2 : GDALExtendedDataType::CopyValue(
9152 2 : &abyTmp[0], dt, &dfVal,
9153 4 : GDALExtendedDataType::Create(GDT_Float64));
9154 2 : dt.FreeDynamicMemory(abyTmp.data());
9155 2 : SetMetadataItem(
9156 : "FWHM_UM",
9157 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9158 2 : .dfFWHMToMicrometer),
9159 : "IMAGERY");
9160 : }
9161 : }
9162 :
9163 133 : m_anOffset[i] = anOtherDimCoord[j];
9164 133 : j++;
9165 : }
9166 : }
9167 515 : }
9168 :
9169 : /************************************************************************/
9170 : /* GetNoDataValue() */
9171 : /************************************************************************/
9172 :
9173 139 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9174 : {
9175 139 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9176 139 : const auto &poArray(l_poDS->m_poArray);
9177 139 : bool bHasNodata = false;
9178 139 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9179 139 : if (pbHasNoData)
9180 125 : *pbHasNoData = bHasNodata;
9181 139 : return res;
9182 : }
9183 :
9184 : /************************************************************************/
9185 : /* GetNoDataValueAsInt64() */
9186 : /************************************************************************/
9187 :
9188 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9189 : {
9190 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9191 1 : const auto &poArray(l_poDS->m_poArray);
9192 1 : bool bHasNodata = false;
9193 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9194 1 : if (pbHasNoData)
9195 1 : *pbHasNoData = bHasNodata;
9196 1 : return nodata;
9197 : }
9198 :
9199 : /************************************************************************/
9200 : /* GetNoDataValueAsUInt64() */
9201 : /************************************************************************/
9202 :
9203 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9204 : {
9205 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9206 1 : const auto &poArray(l_poDS->m_poArray);
9207 1 : bool bHasNodata = false;
9208 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9209 1 : if (pbHasNoData)
9210 1 : *pbHasNoData = bHasNodata;
9211 1 : return nodata;
9212 : }
9213 :
9214 : /************************************************************************/
9215 : /* GetOffset() */
9216 : /************************************************************************/
9217 :
9218 42 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9219 : {
9220 42 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9221 42 : const auto &poArray(l_poDS->m_poArray);
9222 42 : bool bHasValue = false;
9223 42 : double dfRes = poArray->GetOffset(&bHasValue);
9224 42 : if (pbHasOffset)
9225 23 : *pbHasOffset = bHasValue;
9226 42 : return dfRes;
9227 : }
9228 :
9229 : /************************************************************************/
9230 : /* GetUnitType() */
9231 : /************************************************************************/
9232 :
9233 52 : const char *GDALRasterBandFromArray::GetUnitType()
9234 : {
9235 52 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9236 52 : const auto &poArray(l_poDS->m_poArray);
9237 52 : return poArray->GetUnit().c_str();
9238 : }
9239 :
9240 : /************************************************************************/
9241 : /* GetScale() */
9242 : /************************************************************************/
9243 :
9244 40 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9245 : {
9246 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9247 40 : const auto &poArray(l_poDS->m_poArray);
9248 40 : bool bHasValue = false;
9249 40 : double dfRes = poArray->GetScale(&bHasValue);
9250 40 : if (pbHasScale)
9251 21 : *pbHasScale = bHasValue;
9252 40 : return dfRes;
9253 : }
9254 :
9255 : /************************************************************************/
9256 : /* IReadBlock() */
9257 : /************************************************************************/
9258 :
9259 82 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9260 : void *pImage)
9261 : {
9262 82 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9263 82 : const int nXOff = nBlockXOff * nBlockXSize;
9264 82 : const int nYOff = nBlockYOff * nBlockYSize;
9265 82 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9266 82 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9267 : GDALRasterIOExtraArg sExtraArg;
9268 82 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9269 164 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9270 : nReqXSize, nReqYSize, eDataType, nDTSize,
9271 164 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9272 : }
9273 :
9274 : /************************************************************************/
9275 : /* IWriteBlock() */
9276 : /************************************************************************/
9277 :
9278 2 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9279 : void *pImage)
9280 : {
9281 2 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9282 2 : const int nXOff = nBlockXOff * nBlockXSize;
9283 2 : const int nYOff = nBlockYOff * nBlockYSize;
9284 2 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9285 2 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9286 : GDALRasterIOExtraArg sExtraArg;
9287 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9288 4 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9289 : nReqXSize, nReqYSize, eDataType, nDTSize,
9290 4 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9291 : }
9292 :
9293 : /************************************************************************/
9294 : /* AdviseRead() */
9295 : /************************************************************************/
9296 :
9297 39 : CPLErr GDALRasterBandFromArray::AdviseRead(int nXOff, int nYOff, int nXSize,
9298 : int nYSize, int nBufXSize,
9299 : int nBufYSize,
9300 : GDALDataType /*eBufType*/,
9301 : CSLConstList papszOptions)
9302 : {
9303 39 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9304 39 : int bStopProcessing = FALSE;
9305 78 : const CPLErr eErr = l_poDS->ValidateRasterIOOrAdviseReadParameters(
9306 : "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
9307 39 : nBufXSize, nBufYSize, 1, &nBand);
9308 39 : if (eErr != CE_None || bStopProcessing)
9309 0 : return eErr;
9310 :
9311 39 : const auto &poArray(l_poDS->m_poArray);
9312 78 : std::vector<GUInt64> anArrayStartIdx = m_anOffset;
9313 39 : std::vector<size_t> anCount = m_anCount;
9314 39 : anArrayStartIdx[l_poDS->m_iXDim] = nXOff;
9315 39 : anCount[l_poDS->m_iXDim] = nXSize;
9316 39 : if (poArray->GetDimensionCount() >= 2)
9317 : {
9318 36 : anArrayStartIdx[l_poDS->m_iYDim] = nYOff;
9319 36 : anCount[l_poDS->m_iYDim] = nYSize;
9320 : }
9321 39 : return poArray->AdviseRead(anArrayStartIdx.data(), anCount.data(),
9322 : papszOptions)
9323 39 : ? CE_None
9324 39 : : CE_Failure;
9325 : }
9326 :
9327 : /************************************************************************/
9328 : /* IRasterIO() */
9329 : /************************************************************************/
9330 :
9331 479 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9332 : int nYOff, int nXSize, int nYSize,
9333 : void *pData, int nBufXSize,
9334 : int nBufYSize, GDALDataType eBufType,
9335 : GSpacing nPixelSpaceBuf,
9336 : GSpacing nLineSpaceBuf,
9337 : GDALRasterIOExtraArg *psExtraArg)
9338 : {
9339 479 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9340 479 : const auto &poArray(l_poDS->m_poArray);
9341 479 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9342 : // If reading/writing at full resolution and with proper stride, go
9343 : // directly to the array, but, for performance reasons,
9344 : // only if exactly on chunk boundaries, otherwise go through the block cache.
9345 479 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9346 479 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9347 479 : (nLineSpaceBuf % nBufferDTSize) == 0 && (nXOff % nBlockXSize) == 0 &&
9348 479 : (nYOff % nBlockYSize) == 0 &&
9349 479 : ((nXSize % nBlockXSize) == 0 || nXOff + nXSize == nRasterXSize) &&
9350 476 : ((nYSize % nBlockYSize) == 0 || nYOff + nYSize == nRasterYSize))
9351 : {
9352 476 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9353 476 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9354 952 : m_anStride[l_poDS->m_iXDim] =
9355 476 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9356 476 : if (poArray->GetDimensionCount() >= 2)
9357 : {
9358 463 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9359 463 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9360 463 : m_anStride[l_poDS->m_iYDim] =
9361 463 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9362 : }
9363 476 : if (eRWFlag == GF_Read)
9364 : {
9365 940 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9366 470 : m_anStride.data(),
9367 940 : GDALExtendedDataType::Create(eBufType), pData)
9368 470 : ? CE_None
9369 470 : : CE_Failure;
9370 : }
9371 : else
9372 : {
9373 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9374 6 : m_anStride.data(),
9375 12 : GDALExtendedDataType::Create(eBufType), pData)
9376 6 : ? CE_None
9377 6 : : CE_Failure;
9378 : }
9379 : }
9380 3 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9381 : pData, nBufXSize, nBufYSize, eBufType,
9382 3 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9383 : }
9384 :
9385 : /************************************************************************/
9386 : /* GetColorInterpretation() */
9387 : /************************************************************************/
9388 :
9389 74 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9390 : {
9391 74 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9392 74 : const auto &poArray(l_poDS->m_poArray);
9393 222 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9394 74 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9395 : {
9396 6 : bool bOK = false;
9397 6 : GUInt64 nStartIndex = 0;
9398 6 : if (poArray->GetDimensionCount() == 2 &&
9399 0 : poAttr->GetDimensionCount() == 0)
9400 : {
9401 0 : bOK = true;
9402 : }
9403 6 : else if (poArray->GetDimensionCount() == 3)
9404 : {
9405 6 : uint64_t nExtraDimSamples = 1;
9406 6 : const auto &apoDims = poArray->GetDimensions();
9407 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9408 : {
9409 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9410 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9411 : }
9412 6 : if (poAttr->GetDimensionsSize() ==
9413 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9414 : {
9415 6 : bOK = true;
9416 : }
9417 6 : nStartIndex = nBand - 1;
9418 : }
9419 6 : if (bOK)
9420 : {
9421 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9422 6 : const size_t nCount = 1;
9423 6 : const GInt64 arrayStep = 1;
9424 6 : const GPtrDiff_t bufferStride = 1;
9425 6 : char *pszValue = nullptr;
9426 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9427 6 : oStringDT, &pszValue);
9428 6 : if (pszValue)
9429 : {
9430 : const auto eColorInterp =
9431 6 : GDALGetColorInterpretationByName(pszValue);
9432 6 : CPLFree(pszValue);
9433 6 : return eColorInterp;
9434 : }
9435 : }
9436 : }
9437 68 : return GCI_Undefined;
9438 : }
9439 :
9440 : /************************************************************************/
9441 : /* GDALRasterBandFromArray::GetOverviewCount() */
9442 : /************************************************************************/
9443 :
9444 35 : int GDALRasterBandFromArray::GetOverviewCount()
9445 : {
9446 35 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9447 35 : if (nPAMCount)
9448 2 : return nPAMCount;
9449 33 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9450 33 : l_poDS->DiscoverOverviews();
9451 33 : return static_cast<int>(l_poDS->m_apoOverviews.size());
9452 : }
9453 :
9454 : /************************************************************************/
9455 : /* GDALRasterBandFromArray::GetOverview() */
9456 : /************************************************************************/
9457 :
9458 31 : GDALRasterBand *GDALRasterBandFromArray::GetOverview(int idx)
9459 : {
9460 31 : const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9461 31 : if (nPAMCount)
9462 1 : return GDALPamRasterBand::GetOverview(idx);
9463 30 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9464 30 : l_poDS->DiscoverOverviews();
9465 30 : if (idx < 0 || static_cast<size_t>(idx) >= l_poDS->m_apoOverviews.size())
9466 : {
9467 8 : return nullptr;
9468 : }
9469 22 : return l_poDS->m_apoOverviews[idx]->GetRasterBand(nBand);
9470 : }
9471 :
9472 : /************************************************************************/
9473 : /* GDALDatasetFromArray::Create() */
9474 : /************************************************************************/
9475 :
9476 491 : std::unique_ptr<GDALDatasetFromArray> GDALDatasetFromArray::Create(
9477 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9478 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9479 :
9480 : {
9481 491 : const auto nDimCount(array->GetDimensionCount());
9482 491 : if (nDimCount == 0)
9483 : {
9484 1 : CPLError(CE_Failure, CPLE_NotSupported,
9485 : "Unsupported number of dimensions");
9486 1 : return nullptr;
9487 : }
9488 979 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9489 489 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9490 : {
9491 1 : CPLError(CE_Failure, CPLE_NotSupported,
9492 : "Only arrays with numeric data types "
9493 : "can be exposed as classic GDALDataset");
9494 1 : return nullptr;
9495 : }
9496 489 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9497 463 : (nDimCount >= 2 && iXDim == iYDim))
9498 : {
9499 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9500 8 : return nullptr;
9501 : }
9502 481 : GUInt64 nTotalBands = 1;
9503 481 : const auto &dims(array->GetDimensions());
9504 1516 : for (size_t i = 0; i < nDimCount; ++i)
9505 : {
9506 1036 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9507 : {
9508 95 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9509 : {
9510 1 : CPLError(CE_Failure, CPLE_AppDefined,
9511 : "Too many bands. Operate on a sliced view");
9512 1 : return nullptr;
9513 : }
9514 94 : nTotalBands *= dims[i]->GetSize();
9515 : }
9516 : }
9517 :
9518 960 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9519 960 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9520 1515 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9521 : {
9522 1035 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9523 : {
9524 94 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9525 94 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9526 94 : ++j;
9527 : }
9528 : }
9529 :
9530 480 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9531 :
9532 : const char *pszBandMetadata =
9533 480 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9534 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9535 960 : nNewDimCount);
9536 480 : if (pszBandMetadata)
9537 : {
9538 32 : if (!poRootGroup)
9539 : {
9540 1 : CPLError(CE_Failure, CPLE_AppDefined,
9541 : "Root group should be provided when BAND_METADATA is set");
9542 24 : return nullptr;
9543 : }
9544 31 : CPLJSONDocument oDoc;
9545 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9546 : {
9547 1 : CPLError(CE_Failure, CPLE_AppDefined,
9548 : "Invalid JSON content for BAND_METADATA");
9549 1 : return nullptr;
9550 : }
9551 30 : auto oRoot = oDoc.GetRoot();
9552 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9553 : {
9554 1 : CPLError(CE_Failure, CPLE_AppDefined,
9555 : "Value of BAND_METADATA should be an array");
9556 1 : return nullptr;
9557 : }
9558 :
9559 29 : auto oArray = oRoot.ToArray();
9560 38 : for (int j = 0; j < oArray.Size(); ++j)
9561 : {
9562 30 : const auto oJsonItem = oArray[j];
9563 30 : MetadataItem oItem;
9564 30 : size_t iExtraDimIdx = 0;
9565 :
9566 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9567 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9568 0 : std::shared_ptr<GDALMDArray> poArray;
9569 0 : std::shared_ptr<GDALAttribute> poAttribute;
9570 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9571 : {
9572 1 : CPLError(CE_Failure, CPLE_AppDefined,
9573 : "BAND_METADATA[%d][\"array\"] or "
9574 : "BAND_METADATA[%d][\"attribute\"] is missing",
9575 : j, j);
9576 1 : return nullptr;
9577 : }
9578 48 : else if (!osBandArrayFullname.empty() &&
9579 19 : !osBandAttributeName.empty())
9580 : {
9581 1 : CPLError(
9582 : CE_Failure, CPLE_AppDefined,
9583 : "BAND_METADATA[%d][\"array\"] and "
9584 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9585 : j, j);
9586 1 : return nullptr;
9587 : }
9588 28 : else if (!osBandArrayFullname.empty())
9589 : {
9590 : poArray =
9591 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9592 18 : if (!poArray)
9593 : {
9594 1 : CPLError(CE_Failure, CPLE_AppDefined,
9595 : "Array %s cannot be found",
9596 : osBandArrayFullname.c_str());
9597 3 : return nullptr;
9598 : }
9599 17 : if (poArray->GetDimensionCount() != 1)
9600 : {
9601 1 : CPLError(CE_Failure, CPLE_AppDefined,
9602 : "Array %s is not a 1D array",
9603 : osBandArrayFullname.c_str());
9604 1 : return nullptr;
9605 : }
9606 : const auto &osAuxArrayDimName =
9607 16 : poArray->GetDimensions()[0]->GetName();
9608 : auto oIter =
9609 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9610 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9611 : {
9612 1 : CPLError(
9613 : CE_Failure, CPLE_AppDefined,
9614 : "Dimension %s of array %s is not a non-X/Y dimension "
9615 : "of array %s",
9616 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9617 1 : array->GetName().c_str());
9618 1 : return nullptr;
9619 : }
9620 15 : iExtraDimIdx = oIter->second;
9621 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9622 : }
9623 : else
9624 : {
9625 10 : CPLAssert(!osBandAttributeName.empty());
9626 10 : poAttribute = !osBandAttributeName.empty() &&
9627 10 : osBandAttributeName[0] == '/'
9628 24 : ? poRootGroup->OpenAttributeFromFullname(
9629 : osBandAttributeName)
9630 10 : : array->GetAttribute(osBandAttributeName);
9631 10 : if (!poAttribute)
9632 : {
9633 2 : CPLError(CE_Failure, CPLE_AppDefined,
9634 : "Attribute %s cannot be found",
9635 : osBandAttributeName.c_str());
9636 8 : return nullptr;
9637 : }
9638 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9639 8 : if (aoAttrDims.size() != 1)
9640 : {
9641 4 : CPLError(CE_Failure, CPLE_AppDefined,
9642 : "Attribute %s is not a 1D array",
9643 : osBandAttributeName.c_str());
9644 4 : return nullptr;
9645 : }
9646 4 : bool found = false;
9647 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9648 : {
9649 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9650 5 : ->GetSize() == aoAttrDims[0])
9651 : {
9652 4 : if (found)
9653 : {
9654 2 : CPLError(CE_Failure, CPLE_AppDefined,
9655 : "Several dimensions of %s have the same "
9656 : "size as attribute %s. Cannot infer which "
9657 : "one to bind to!",
9658 1 : array->GetName().c_str(),
9659 : osBandAttributeName.c_str());
9660 1 : return nullptr;
9661 : }
9662 3 : found = true;
9663 3 : iExtraDimIdx = iter.second;
9664 : }
9665 : }
9666 3 : if (!found)
9667 : {
9668 2 : CPLError(
9669 : CE_Failure, CPLE_AppDefined,
9670 : "No dimension of %s has the same size as attribute %s",
9671 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9672 1 : return nullptr;
9673 : }
9674 : }
9675 :
9676 17 : oItem.osName = oJsonItem.GetString("item_name");
9677 17 : if (oItem.osName.empty())
9678 : {
9679 1 : CPLError(CE_Failure, CPLE_AppDefined,
9680 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9681 1 : return nullptr;
9682 : }
9683 :
9684 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9685 :
9686 : // Check correctness of definition
9687 16 : bool bFirstNumericFormatter = true;
9688 16 : std::string osModDefinition;
9689 16 : bool bDefinitionUsesPctForG = false;
9690 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9691 : {
9692 70 : if (osDefinition[k] == '%')
9693 : {
9694 15 : osModDefinition += osDefinition[k];
9695 15 : if (k + 1 == osDefinition.size())
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 1 : return nullptr;
9703 : }
9704 14 : ++k;
9705 14 : if (osDefinition[k] == '%')
9706 : {
9707 1 : osModDefinition += osDefinition[k];
9708 1 : continue;
9709 : }
9710 13 : if (!bFirstNumericFormatter)
9711 : {
9712 1 : CPLError(CE_Failure, CPLE_AppDefined,
9713 : "Value of "
9714 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9715 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9716 : "formatters should be specified at most once",
9717 : j, osDefinition.c_str(), int(k));
9718 1 : return nullptr;
9719 : }
9720 12 : bFirstNumericFormatter = false;
9721 19 : for (; k < osDefinition.size(); ++k)
9722 : {
9723 19 : osModDefinition += osDefinition[k];
9724 38 : if (!((osDefinition[k] >= '0' &&
9725 16 : osDefinition[k] <= '9') ||
9726 15 : osDefinition[k] == '.'))
9727 12 : break;
9728 : }
9729 24 : if (k == osDefinition.size() ||
9730 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9731 5 : osDefinition[k] != 's'))
9732 : {
9733 1 : CPLError(CE_Failure, CPLE_AppDefined,
9734 : "Value of "
9735 : "BAND_METADATA[%d][\"item_value\"] = "
9736 : "%s is invalid at offset %d: only "
9737 : "%%[x][.y]f|g or %%s formatters are accepted",
9738 : j, osDefinition.c_str(), int(k));
9739 1 : return nullptr;
9740 : }
9741 11 : bDefinitionUsesPctForG =
9742 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9743 11 : if (bDefinitionUsesPctForG)
9744 : {
9745 12 : if (poArray &&
9746 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9747 : {
9748 1 : CPLError(CE_Failure, CPLE_AppDefined,
9749 : "Data type of %s array is not numeric",
9750 1 : poArray->GetName().c_str());
9751 1 : return nullptr;
9752 : }
9753 8 : else if (poAttribute &&
9754 2 : poAttribute->GetDataType().GetClass() !=
9755 6 : GEDTC_NUMERIC)
9756 : {
9757 0 : CPLError(CE_Failure, CPLE_AppDefined,
9758 : "Data type of %s attribute is not numeric",
9759 0 : poAttribute->GetFullName().c_str());
9760 0 : return nullptr;
9761 : }
9762 : }
9763 : }
9764 62 : else if (osDefinition[k] == '$' &&
9765 62 : k + 1 < osDefinition.size() &&
9766 7 : osDefinition[k + 1] == '{')
9767 : {
9768 7 : const auto nPos = osDefinition.find('}', k);
9769 7 : if (nPos == std::string::npos)
9770 : {
9771 1 : CPLError(CE_Failure, CPLE_AppDefined,
9772 : "Value of "
9773 : "BAND_METADATA[%d][\"item_value\"] = "
9774 : "%s is invalid at offset %d",
9775 : j, osDefinition.c_str(), int(k));
9776 3 : return nullptr;
9777 : }
9778 : const auto osAttrName =
9779 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9780 0 : std::shared_ptr<GDALAttribute> poAttr;
9781 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9782 : {
9783 4 : poAttr = poArray->GetAttribute(osAttrName);
9784 4 : if (!poAttr)
9785 : {
9786 1 : CPLError(
9787 : CE_Failure, CPLE_AppDefined,
9788 : "Value of "
9789 : "BAND_METADATA[%d][\"item_value\"] = "
9790 : "%s is invalid: %s is not an attribute of %s",
9791 : j, osDefinition.c_str(), osAttrName.c_str(),
9792 1 : poArray->GetName().c_str());
9793 1 : return nullptr;
9794 : }
9795 : }
9796 : else
9797 : {
9798 : poAttr =
9799 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9800 2 : if (!poAttr)
9801 : {
9802 1 : CPLError(CE_Failure, CPLE_AppDefined,
9803 : "Value of "
9804 : "BAND_METADATA[%d][\"item_value\"] = "
9805 : "%s is invalid: %s is not an attribute",
9806 : j, osDefinition.c_str(),
9807 : osAttrName.c_str());
9808 1 : return nullptr;
9809 : }
9810 : }
9811 4 : k = nPos;
9812 4 : const char *pszValue = poAttr->ReadAsString();
9813 4 : if (!pszValue)
9814 : {
9815 0 : CPLError(CE_Failure, CPLE_AppDefined,
9816 : "Cannot get value of attribute %s as a "
9817 : "string",
9818 : osAttrName.c_str());
9819 0 : return nullptr;
9820 : }
9821 4 : osModDefinition += pszValue;
9822 : }
9823 : else
9824 : {
9825 48 : osModDefinition += osDefinition[k];
9826 : }
9827 : }
9828 :
9829 9 : if (poArray)
9830 8 : oItem.poArray = std::move(poArray);
9831 : else
9832 1 : oItem.poArray = std::move(poAttribute);
9833 9 : oItem.osDefinition = std::move(osModDefinition);
9834 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9835 :
9836 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9837 9 : std::move(oItem));
9838 : }
9839 : }
9840 :
9841 912 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9842 : const char *pszBandImageryMetadata =
9843 456 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9844 456 : if (pszBandImageryMetadata)
9845 : {
9846 20 : if (!poRootGroup)
9847 : {
9848 1 : CPLError(CE_Failure, CPLE_AppDefined,
9849 : "Root group should be provided when BAND_IMAGERY_METADATA "
9850 : "is set");
9851 17 : return nullptr;
9852 : }
9853 19 : CPLJSONDocument oDoc;
9854 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9855 : {
9856 1 : CPLError(CE_Failure, CPLE_AppDefined,
9857 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9858 1 : return nullptr;
9859 : }
9860 18 : auto oRoot = oDoc.GetRoot();
9861 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9862 : {
9863 1 : CPLError(CE_Failure, CPLE_AppDefined,
9864 : "Value of BAND_IMAGERY_METADATA should be an object");
9865 1 : return nullptr;
9866 : }
9867 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9868 : {
9869 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9870 20 : oJsonItem.GetName() == "FWHM_UM")
9871 : {
9872 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9873 : const auto osBandAttributeName =
9874 34 : oJsonItem.GetString("attribute");
9875 0 : std::shared_ptr<GDALMDArray> poArray;
9876 0 : std::shared_ptr<GDALAttribute> poAttribute;
9877 17 : size_t iExtraDimIdx = 0;
9878 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9879 : {
9880 2 : CPLError(CE_Failure, CPLE_AppDefined,
9881 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9882 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9883 : "missing",
9884 2 : oJsonItem.GetName().c_str(),
9885 2 : oJsonItem.GetName().c_str());
9886 1 : return nullptr;
9887 : }
9888 25 : else if (!osBandArrayFullname.empty() &&
9889 9 : !osBandAttributeName.empty())
9890 : {
9891 2 : CPLError(CE_Failure, CPLE_AppDefined,
9892 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9893 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9894 : "mutually exclusive",
9895 2 : oJsonItem.GetName().c_str(),
9896 2 : oJsonItem.GetName().c_str());
9897 1 : return nullptr;
9898 : }
9899 15 : else if (!osBandArrayFullname.empty())
9900 : {
9901 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9902 8 : osBandArrayFullname);
9903 8 : if (!poArray)
9904 : {
9905 1 : CPLError(CE_Failure, CPLE_AppDefined,
9906 : "Array %s cannot be found",
9907 : osBandArrayFullname.c_str());
9908 3 : return nullptr;
9909 : }
9910 7 : if (poArray->GetDimensionCount() != 1)
9911 : {
9912 1 : CPLError(CE_Failure, CPLE_AppDefined,
9913 : "Array %s is not a 1D array",
9914 : osBandArrayFullname.c_str());
9915 1 : return nullptr;
9916 : }
9917 : const auto &osAuxArrayDimName =
9918 6 : poArray->GetDimensions()[0]->GetName();
9919 : auto oIter =
9920 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9921 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9922 : {
9923 1 : CPLError(CE_Failure, CPLE_AppDefined,
9924 : "Dimension \"%s\" of array \"%s\" is not a "
9925 : "non-X/Y dimension of array \"%s\"",
9926 : osAuxArrayDimName.c_str(),
9927 : osBandArrayFullname.c_str(),
9928 1 : array->GetName().c_str());
9929 1 : return nullptr;
9930 : }
9931 5 : iExtraDimIdx = oIter->second;
9932 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9933 : }
9934 : else
9935 : {
9936 : poAttribute =
9937 7 : !osBandAttributeName.empty() &&
9938 7 : osBandAttributeName[0] == '/'
9939 16 : ? poRootGroup->OpenAttributeFromFullname(
9940 : osBandAttributeName)
9941 7 : : array->GetAttribute(osBandAttributeName);
9942 7 : if (!poAttribute)
9943 : {
9944 2 : CPLError(CE_Failure, CPLE_AppDefined,
9945 : "Attribute %s cannot be found",
9946 : osBandAttributeName.c_str());
9947 6 : return nullptr;
9948 : }
9949 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9950 5 : if (aoAttrDims.size() != 1)
9951 : {
9952 2 : CPLError(CE_Failure, CPLE_AppDefined,
9953 : "Attribute %s is not a 1D array",
9954 : osBandAttributeName.c_str());
9955 2 : return nullptr;
9956 : }
9957 3 : bool found = false;
9958 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9959 : {
9960 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9961 4 : ->GetSize() == aoAttrDims[0])
9962 : {
9963 3 : if (found)
9964 : {
9965 2 : CPLError(CE_Failure, CPLE_AppDefined,
9966 : "Several dimensions of %s have the "
9967 : "same size as attribute %s. Cannot "
9968 : "infer which one to bind to!",
9969 1 : array->GetName().c_str(),
9970 : osBandAttributeName.c_str());
9971 1 : return nullptr;
9972 : }
9973 2 : found = true;
9974 2 : iExtraDimIdx = iter.second;
9975 : }
9976 : }
9977 2 : if (!found)
9978 : {
9979 2 : CPLError(CE_Failure, CPLE_AppDefined,
9980 : "No dimension of %s has the same size as "
9981 : "attribute %s",
9982 1 : array->GetName().c_str(),
9983 : osBandAttributeName.c_str());
9984 1 : return nullptr;
9985 : }
9986 : }
9987 :
9988 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9989 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9990 : {
9991 4 : if (osUnit.back() != '}')
9992 : {
9993 2 : CPLError(CE_Failure, CPLE_AppDefined,
9994 : "Value of "
9995 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9996 : "%s is invalid",
9997 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9998 2 : return nullptr;
9999 : }
10000 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
10001 0 : std::shared_ptr<GDALAttribute> poAttr;
10002 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
10003 : {
10004 2 : poAttr = poArray->GetAttribute(osAttrName);
10005 2 : if (!poAttr)
10006 : {
10007 2 : CPLError(
10008 : CE_Failure, CPLE_AppDefined,
10009 : "Value of "
10010 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
10011 : "%s is invalid: %s is not an attribute of %s",
10012 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
10013 : osAttrName.c_str(),
10014 : osBandArrayFullname.c_str());
10015 1 : return nullptr;
10016 : }
10017 : }
10018 : else
10019 : {
10020 : poAttr =
10021 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
10022 1 : if (!poAttr)
10023 : {
10024 0 : CPLError(
10025 : CE_Failure, CPLE_AppDefined,
10026 : "Value of "
10027 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
10028 : "%s is invalid: %s is not an attribute",
10029 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
10030 : osAttrName.c_str());
10031 0 : return nullptr;
10032 : }
10033 : }
10034 :
10035 2 : const char *pszValue = poAttr->ReadAsString();
10036 2 : if (!pszValue)
10037 : {
10038 0 : CPLError(CE_Failure, CPLE_AppDefined,
10039 : "Cannot get value of attribute %s of %s as a "
10040 : "string",
10041 : osAttrName.c_str(),
10042 : osBandArrayFullname.c_str());
10043 0 : return nullptr;
10044 : }
10045 2 : osUnit = pszValue;
10046 : }
10047 4 : double dfConvToUM = 1.0;
10048 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
10049 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
10050 3 : osUnit == "nanometers")
10051 : {
10052 1 : dfConvToUM = 1e-3;
10053 : }
10054 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
10055 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
10056 1 : osUnit == "micrometers"))
10057 : {
10058 2 : CPLError(CE_Failure, CPLE_AppDefined,
10059 : "Unhandled value for "
10060 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
10061 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
10062 1 : return nullptr;
10063 : }
10064 :
10065 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
10066 :
10067 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
10068 3 : if (poArray)
10069 2 : abstractArray = std::move(poArray);
10070 : else
10071 1 : abstractArray = std::move(poAttribute);
10072 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
10073 : {
10074 2 : item.poCentralWavelengthArray = std::move(abstractArray);
10075 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
10076 : }
10077 : else
10078 : {
10079 1 : item.poFWHMArray = std::move(abstractArray);
10080 1 : item.dfFWHMToMicrometer = dfConvToUM;
10081 : }
10082 : }
10083 : else
10084 : {
10085 1 : CPLError(CE_Warning, CPLE_AppDefined,
10086 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
10087 2 : oJsonItem.GetName().c_str());
10088 : }
10089 : }
10090 : }
10091 :
10092 420 : if ((nDimCount >= 2 &&
10093 878 : dims[iYDim]->GetSize() > static_cast<uint64_t>(INT_MAX)) ||
10094 439 : dims[iXDim]->GetSize() > static_cast<uint64_t>(INT_MAX))
10095 : {
10096 1 : CPLError(CE_Failure, CPLE_AppDefined,
10097 : "Array is too large to be exposed as a GDAL dataset");
10098 1 : return nullptr;
10099 : }
10100 :
10101 : auto poDS = std::make_unique<GDALDatasetFromArray>(
10102 876 : array, iXDim, iYDim, CPLStringList(papszOptions));
10103 :
10104 438 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
10105 :
10106 438 : poDS->nRasterYSize =
10107 438 : nDimCount < 2 ? 1 : static_cast<int>(dims[iYDim]->GetSize());
10108 :
10109 438 : poDS->nRasterXSize = static_cast<int>(dims[iXDim]->GetSize());
10110 :
10111 876 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
10112 876 : std::vector<GUInt64> anStackIters(nDimCount);
10113 876 : std::vector<size_t> anMapNewToOld(nNewDimCount);
10114 1346 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
10115 : {
10116 908 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
10117 : {
10118 51 : anMapNewToOld[j] = i;
10119 51 : j++;
10120 : }
10121 : }
10122 :
10123 438 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
10124 :
10125 876 : const auto attrs(array->GetAttributes());
10126 608 : for (const auto &attr : attrs)
10127 : {
10128 170 : if (attr->GetName() == "spatial:registration")
10129 : {
10130 : // From https://github.com/zarr-conventions/spatial
10131 6 : const char *pszValue = attr->ReadAsString();
10132 6 : if (pszValue && strcmp(pszValue, "pixel") == 0)
10133 5 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10134 : GDALMD_AOP_AREA);
10135 1 : else if (pszValue && strcmp(pszValue, "node") == 0)
10136 1 : poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10137 : GDALMD_AOP_POINT);
10138 0 : else if (pszValue)
10139 0 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), pszValue);
10140 : }
10141 164 : else if (attr->GetName() == "gdal:geotransform")
10142 : {
10143 : // From Zarr driver
10144 4 : const auto doubleArray = attr->ReadAsDoubleArray();
10145 2 : if (doubleArray.size() == 6)
10146 : {
10147 2 : poDS->m_bHasGT = true;
10148 2 : poDS->m_gt = GDALGeoTransform(doubleArray.data());
10149 : }
10150 : }
10151 162 : else if (attr->GetName() != "COLOR_INTERPRETATION")
10152 : {
10153 300 : auto stringArray = attr->ReadAsStringArray();
10154 300 : std::string val;
10155 150 : if (stringArray.size() > 1)
10156 : {
10157 59 : val += '{';
10158 : }
10159 585 : for (int i = 0; i < stringArray.size(); ++i)
10160 : {
10161 435 : if (i > 0)
10162 285 : val += ',';
10163 435 : val += stringArray[i];
10164 : }
10165 150 : if (stringArray.size() > 1)
10166 : {
10167 59 : val += '}';
10168 : }
10169 150 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
10170 : }
10171 : }
10172 :
10173 438 : const char *pszDelay = CSLFetchNameValueDef(
10174 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
10175 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
10176 : const double dfDelay =
10177 438 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
10178 438 : const auto nStartTime = time(nullptr);
10179 438 : bool bHasWarned = false;
10180 : // Instantiate bands by iterating over non-XY variables
10181 438 : size_t iDim = 0;
10182 438 : int nCurBand = 1;
10183 568 : lbl_next_depth:
10184 568 : if (iDim < nNewDimCount)
10185 : {
10186 53 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
10187 53 : anOtherDimCoord[iDim] = 0;
10188 : while (true)
10189 : {
10190 130 : ++iDim;
10191 130 : goto lbl_next_depth;
10192 130 : lbl_return_to_caller:
10193 130 : --iDim;
10194 130 : --anStackIters[iDim];
10195 130 : if (anStackIters[iDim] == 0)
10196 53 : break;
10197 77 : ++anOtherDimCoord[iDim];
10198 : }
10199 : }
10200 : else
10201 : {
10202 1030 : poDS->SetBand(nCurBand,
10203 515 : std::make_unique<GDALRasterBandFromArray>(
10204 515 : poDS.get(), anOtherDimCoord,
10205 : aoBandParameterMetadataItems, aoBandImageryMetadata,
10206 : dfDelay, nStartTime, bHasWarned));
10207 515 : ++nCurBand;
10208 : }
10209 568 : if (iDim > 0)
10210 130 : goto lbl_return_to_caller;
10211 :
10212 834 : if (!array->GetFilename().empty() &&
10213 396 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "LOAD_PAM", "YES")))
10214 : {
10215 388 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10216 : std::string osDerivedDatasetName(
10217 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10218 776 : int(iYDim), array->GetFullName().c_str()));
10219 388 : if (!array->GetContext().empty())
10220 : {
10221 2 : osDerivedDatasetName += " with context ";
10222 2 : osDerivedDatasetName += array->GetContext();
10223 : }
10224 388 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10225 388 : poDS->TryLoadXML();
10226 :
10227 2 : for (const auto &[pszKey, pszValue] :
10228 : cpl::IterateNameValue(static_cast<CSLConstList>(
10229 390 : poDS->GDALPamDataset::GetMetadata())))
10230 : {
10231 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10232 : }
10233 : }
10234 :
10235 438 : return poDS;
10236 : }
10237 :
10238 : /************************************************************************/
10239 : /* AsClassicDataset() */
10240 : /************************************************************************/
10241 :
10242 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10243 : *
10244 : * In the case of > 2D arrays, additional dimensions will be represented as
10245 : * raster bands.
10246 : *
10247 : * The "reverse" method is GDALRasterBand::AsMDArray().
10248 : *
10249 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10250 : *
10251 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10252 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10253 : * Ignored if the dimension count is 1.
10254 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10255 : * and BAND_IMAGERY_METADATA option.
10256 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10257 : * nullptr. Current supported options are:
10258 : * <ul>
10259 : * <li>BAND_METADATA: JSON serialized array defining which
10260 : * arrays of the poRootGroup, indexed by non-X and Y
10261 : * dimensions, should be mapped as band metadata items.
10262 : * Each array item should be an object with the
10263 : * following members:
10264 : * - "array": full name of a band parameter array.
10265 : * Such array must be a one
10266 : * dimensional array, and its dimension must be one of
10267 : * the dimensions of the array on which the method is
10268 : * called (excluding the X and Y dimensions).
10269 : * - "attribute": name relative to *this array or full
10270 : * name of a single dimension numeric array whose size
10271 : * must be one of the dimensions of *this array
10272 : * (excluding the X and Y dimensions).
10273 : * "array" and "attribute" are mutually exclusive.
10274 : * - "item_name": band metadata item name
10275 : * - "item_value": (optional) String, where "%[x][.y]f",
10276 : * "%[x][.y]g" or "%s" printf-like formatting can be
10277 : * used to format the corresponding value of the
10278 : * parameter array. The percentage character should be
10279 : * repeated: "%%"
10280 : * "${attribute_name}" can also be used to include the
10281 : * value of an attribute for "array" when set and if
10282 : * not starting with '/'. Otherwise if starting with
10283 : * '/', it is the full path to the attribute.
10284 : *
10285 : * If "item_value" is not provided, a default formatting
10286 : * of the value will be applied.
10287 : *
10288 : * Example:
10289 : * [
10290 : * {
10291 : * "array": "/sensor_band_parameters/wavelengths",
10292 : * "item_name": "WAVELENGTH",
10293 : * "item_value": "%.1f ${units}"
10294 : * },
10295 : * {
10296 : * "array": "/sensor_band_parameters/fwhm",
10297 : * "item_name": "FWHM"
10298 : * },
10299 : * {
10300 : * "array": "/sensor_band_parameters/fwhm",
10301 : * "item_name": "FWHM_UNIT",
10302 : * "item_value": "${units}"
10303 : * }
10304 : * ]
10305 : *
10306 : * Example for Planet Labs Tanager radiance products:
10307 : * [
10308 : * {
10309 : * "attribute": "center_wavelengths",
10310 : * "item_name": "WAVELENGTH",
10311 : * "item_value": "%.1f ${center_wavelengths_units}"
10312 : * }
10313 : * ]
10314 : *
10315 : * </li>
10316 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10317 : * JSON serialized object defining which arrays of the
10318 : * poRootGroup, indexed by non-X and Y dimensions,
10319 : * should be mapped as band metadata items in the
10320 : * band IMAGERY domain.
10321 : * The object currently accepts 2 members:
10322 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10323 : * micrometers.
10324 : * - "FWHM_UM": Full-width half-maximum
10325 : * in micrometers.
10326 : * The value of each member should be an object with the
10327 : * following members:
10328 : * - "array": full name of a band parameter array.
10329 : * Such array must be a one dimensional array, and its
10330 : * dimension must be one of the dimensions of the
10331 : * array on which the method is called
10332 : * (excluding the X and Y dimensions).
10333 : * - "attribute": name relative to *this array or full
10334 : * name of a single dimension numeric array whose size
10335 : * must be one of the dimensions of *this array
10336 : * (excluding the X and Y dimensions).
10337 : * "array" and "attribute" are mutually exclusive,
10338 : * and one of them is required.
10339 : * - "unit": (optional) unit of the values pointed in
10340 : * the above array.
10341 : * Can be a literal string or a string of the form
10342 : * "${attribute_name}" to point to an attribute for
10343 : * "array" when set and if no starting
10344 : * with '/'. Otherwise if starting with '/', it is
10345 : * the full path to the attribute.
10346 : * Accepted values are "um", "micrometer"
10347 : * (with UK vs US spelling, singular or plural), "nm",
10348 : * "nanometer" (with UK vs US spelling, singular or
10349 : * plural)
10350 : * If not provided, micrometer is assumed.
10351 : *
10352 : * Example for EMIT datasets:
10353 : * {
10354 : * "CENTRAL_WAVELENGTH_UM": {
10355 : * "array": "/sensor_band_parameters/wavelengths",
10356 : * "unit": "${units}"
10357 : * },
10358 : * "FWHM_UM": {
10359 : * "array": "/sensor_band_parameters/fwhm",
10360 : * "unit": "${units}"
10361 : * }
10362 : * }
10363 : *
10364 : * Example for Planet Labs Tanager radiance products:
10365 : * {
10366 : * "CENTRAL_WAVELENGTH_UM": {
10367 : * "attribute": "center_wavelengths",
10368 : * "unit": "${center_wavelengths_units}"
10369 : * },
10370 : * "FWHM_UM": {
10371 : * "attribute": "fwhm",
10372 : * "unit": "${fwhm_units}"
10373 : * }
10374 : * }
10375 : *
10376 : * </li>
10377 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10378 : * seconds allowed to set the DIM_{dimname}_VALUE band
10379 : * metadata items from the indexing variable of the
10380 : * dimensions.
10381 : * Default value is 5. 'unlimited' can be used to mean
10382 : * unlimited delay. Can also be defined globally with
10383 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10384 : * option.</li>
10385 : * </ul>
10386 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10387 : */
10388 : GDALDataset *
10389 482 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10390 : const std::shared_ptr<GDALGroup> &poRootGroup,
10391 : CSLConstList papszOptions) const
10392 : {
10393 964 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10394 482 : if (!self)
10395 : {
10396 0 : CPLError(CE_Failure, CPLE_AppDefined,
10397 : "Driver implementation issue: m_pSelf not set !");
10398 0 : return nullptr;
10399 : }
10400 964 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10401 : papszOptions)
10402 482 : .release();
10403 : }
10404 :
10405 : /************************************************************************/
10406 : /* GetStatistics() */
10407 : /************************************************************************/
10408 :
10409 : /**
10410 : * \brief Fetch statistics.
10411 : *
10412 : * Returns the minimum, maximum, mean and standard deviation of all
10413 : * pixel values in this array.
10414 : *
10415 : * If bForce is FALSE results will only be returned if it can be done
10416 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10417 : * results cannot be returned efficiently, the method will return CE_Warning
10418 : * but no warning will have been issued. This is a non-standard use of
10419 : * the CE_Warning return value to indicate "nothing done".
10420 : *
10421 : * When cached statistics are not available, and bForce is TRUE,
10422 : * ComputeStatistics() is called.
10423 : *
10424 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10425 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10426 : * after the first request.
10427 : *
10428 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10429 : *
10430 : * This method is the same as the C function GDALMDArrayGetStatistics().
10431 : *
10432 : * @param bApproxOK Currently ignored. In the future, should be set to true
10433 : * if statistics on the whole array are wished, or to false if a subset of it
10434 : * may be used.
10435 : *
10436 : * @param bForce If false statistics will only be returned if it can
10437 : * be done without rescanning the image.
10438 : *
10439 : * @param pdfMin Location into which to load image minimum (may be NULL).
10440 : *
10441 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10442 : *
10443 : * @param pdfMean Location into which to load image mean (may be NULL).
10444 : *
10445 : * @param pdfStdDev Location into which to load image standard deviation
10446 : * (may be NULL).
10447 : *
10448 : * @param pnValidCount Number of samples whose value is different from the
10449 : * nodata value. (may be NULL)
10450 : *
10451 : * @param pfnProgress a function to call to report progress, or NULL.
10452 : *
10453 : * @param pProgressData application data to pass to the progress function.
10454 : *
10455 : * @return CE_None on success, CE_Warning if no values returned,
10456 : * CE_Failure if an error occurs.
10457 : *
10458 : * @since GDAL 3.2
10459 : */
10460 :
10461 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10462 : double *pdfMax, double *pdfMean,
10463 : double *pdfStdDev, GUInt64 *pnValidCount,
10464 : GDALProgressFunc pfnProgress,
10465 : void *pProgressData)
10466 : {
10467 10 : if (!bForce)
10468 1 : return CE_Warning;
10469 :
10470 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10471 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10472 9 : ? CE_None
10473 9 : : CE_Failure;
10474 : }
10475 :
10476 : /************************************************************************/
10477 : /* ComputeStatistics() */
10478 : /************************************************************************/
10479 :
10480 : /**
10481 : * \brief Compute statistics.
10482 : *
10483 : * Returns the minimum, maximum, mean and standard deviation of all
10484 : * pixel values in this array.
10485 : *
10486 : * Pixels taken into account in statistics are those whose mask value
10487 : * (as determined by GetMask()) is non-zero.
10488 : *
10489 : * Once computed, the statistics will generally be "set" back on the
10490 : * owing dataset.
10491 : *
10492 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10493 : *
10494 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10495 : * and GDALMDArrayComputeStatisticsEx().
10496 : *
10497 : * @param bApproxOK Currently ignored. In the future, should be set to true
10498 : * if statistics on the whole array are wished, or to false if a subset of it
10499 : * may be used.
10500 : *
10501 : * @param pdfMin Location into which to load image minimum (may be NULL).
10502 : *
10503 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10504 : *
10505 : * @param pdfMean Location into which to load image mean (may be NULL).
10506 : *
10507 : * @param pdfStdDev Location into which to load image standard deviation
10508 : * (may be NULL).
10509 : *
10510 : * @param pnValidCount Number of samples whose value is different from the
10511 : * nodata value. (may be NULL)
10512 : *
10513 : * @param pfnProgress a function to call to report progress, or NULL.
10514 : *
10515 : * @param pProgressData application data to pass to the progress function.
10516 : *
10517 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10518 : * Options are driver specific. For now the netCDF and Zarr
10519 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10520 : * to add or update the actual_range attribute with the
10521 : * computed min/max, only if done on the full array, in non
10522 : * approximate mode, and the dataset is opened in update
10523 : * mode.
10524 : *
10525 : * @return true on success
10526 : *
10527 : * @since GDAL 3.2
10528 : */
10529 :
10530 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10531 : double *pdfMax, double *pdfMean,
10532 : double *pdfStdDev, GUInt64 *pnValidCount,
10533 : GDALProgressFunc pfnProgress,
10534 : void *pProgressData,
10535 : CSLConstList papszOptions)
10536 : {
10537 : struct StatsPerChunkType
10538 : {
10539 : const GDALMDArray *array = nullptr;
10540 : std::shared_ptr<GDALMDArray> poMask{};
10541 : double dfMin = cpl::NumericLimits<double>::max();
10542 : double dfMax = -cpl::NumericLimits<double>::max();
10543 : double dfMean = 0.0;
10544 : double dfM2 = 0.0;
10545 : GUInt64 nValidCount = 0;
10546 : std::vector<GByte> abyData{};
10547 : std::vector<double> adfData{};
10548 : std::vector<GByte> abyMaskData{};
10549 : GDALProgressFunc pfnProgress = nullptr;
10550 : void *pProgressData = nullptr;
10551 : };
10552 :
10553 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10554 : const GUInt64 *chunkArrayStartIdx,
10555 : const size_t *chunkCount, GUInt64 iCurChunk,
10556 : GUInt64 nChunkCount, void *pUserData)
10557 : {
10558 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10559 13 : const GDALMDArray *array = data->array;
10560 13 : const GDALMDArray *poMask = data->poMask.get();
10561 13 : const size_t nDims = array->GetDimensionCount();
10562 13 : size_t nVals = 1;
10563 34 : for (size_t i = 0; i < nDims; i++)
10564 21 : nVals *= chunkCount[i];
10565 :
10566 : // Get mask
10567 13 : data->abyMaskData.resize(nVals);
10568 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10569 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10570 : {
10571 0 : return false;
10572 : }
10573 :
10574 : // Get data
10575 13 : const auto &oType = array->GetDataType();
10576 13 : if (oType.GetNumericDataType() == GDT_Float64)
10577 : {
10578 6 : data->adfData.resize(nVals);
10579 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10580 6 : oType, &data->adfData[0]))
10581 : {
10582 0 : return false;
10583 : }
10584 : }
10585 : else
10586 : {
10587 7 : data->abyData.resize(nVals * oType.GetSize());
10588 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10589 7 : oType, &data->abyData[0]))
10590 : {
10591 0 : return false;
10592 : }
10593 7 : data->adfData.resize(nVals);
10594 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10595 7 : static_cast<int>(oType.GetSize()),
10596 7 : &data->adfData[0], GDT_Float64,
10597 : static_cast<int>(sizeof(double)),
10598 : static_cast<GPtrDiff_t>(nVals));
10599 : }
10600 912 : for (size_t i = 0; i < nVals; i++)
10601 : {
10602 899 : if (data->abyMaskData[i])
10603 : {
10604 894 : const double dfValue = data->adfData[i];
10605 894 : data->dfMin = std::min(data->dfMin, dfValue);
10606 894 : data->dfMax = std::max(data->dfMax, dfValue);
10607 894 : data->nValidCount++;
10608 894 : const double dfDelta = dfValue - data->dfMean;
10609 894 : data->dfMean += dfDelta / data->nValidCount;
10610 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10611 : }
10612 : }
10613 13 : if (data->pfnProgress &&
10614 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10615 : "", data->pProgressData))
10616 : {
10617 0 : return false;
10618 : }
10619 13 : return true;
10620 : };
10621 :
10622 13 : const auto &oType = GetDataType();
10623 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10624 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10625 : {
10626 0 : CPLError(
10627 : CE_Failure, CPLE_NotSupported,
10628 : "Statistics can only be computed on non-complex numeric data type");
10629 0 : return false;
10630 : }
10631 :
10632 13 : const size_t nDims = GetDimensionCount();
10633 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10634 26 : std::vector<GUInt64> count(nDims);
10635 13 : const auto &poDims = GetDimensions();
10636 34 : for (size_t i = 0; i < nDims; i++)
10637 : {
10638 21 : count[i] = poDims[i]->GetSize();
10639 : }
10640 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10641 : const size_t nMaxChunkSize =
10642 : pszSwathSize
10643 13 : ? static_cast<size_t>(
10644 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10645 0 : CPLAtoGIntBig(pszSwathSize)))
10646 : : static_cast<size_t>(
10647 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10648 13 : GDALGetCacheMax64() / 4));
10649 26 : StatsPerChunkType sData;
10650 13 : sData.array = this;
10651 13 : sData.poMask = GetMask(nullptr);
10652 13 : if (sData.poMask == nullptr)
10653 : {
10654 0 : return false;
10655 : }
10656 13 : sData.pfnProgress = pfnProgress;
10657 13 : sData.pProgressData = pProgressData;
10658 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10659 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10660 13 : PerChunkFunc, &sData))
10661 : {
10662 0 : return false;
10663 : }
10664 :
10665 13 : if (pdfMin)
10666 13 : *pdfMin = sData.dfMin;
10667 :
10668 13 : if (pdfMax)
10669 13 : *pdfMax = sData.dfMax;
10670 :
10671 13 : if (pdfMean)
10672 11 : *pdfMean = sData.dfMean;
10673 :
10674 : const double dfStdDev =
10675 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10676 13 : if (pdfStdDev)
10677 11 : *pdfStdDev = dfStdDev;
10678 :
10679 13 : if (pnValidCount)
10680 11 : *pnValidCount = sData.nValidCount;
10681 :
10682 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10683 13 : sData.nValidCount, papszOptions);
10684 :
10685 13 : return true;
10686 : }
10687 :
10688 : /************************************************************************/
10689 : /* SetStatistics() */
10690 : /************************************************************************/
10691 : //! @cond Doxygen_Suppress
10692 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10693 : double /* dfMax */, double /* dfMean */,
10694 : double /* dfStdDev */,
10695 : GUInt64 /* nValidCount */,
10696 : CSLConstList /* papszOptions */)
10697 : {
10698 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10699 5 : return false;
10700 : }
10701 :
10702 : //! @endcond
10703 :
10704 : /************************************************************************/
10705 : /* ClearStatistics() */
10706 : /************************************************************************/
10707 :
10708 : /**
10709 : * \brief Clear statistics.
10710 : *
10711 : * @since GDAL 3.4
10712 : */
10713 0 : void GDALMDArray::ClearStatistics()
10714 : {
10715 0 : }
10716 :
10717 : /************************************************************************/
10718 : /* GetCoordinateVariables() */
10719 : /************************************************************************/
10720 :
10721 : /**
10722 : * \brief Return coordinate variables.
10723 : *
10724 : * Coordinate variables are an alternate way of indexing an array that can
10725 : * be sometimes used. For example, an array collected through remote sensing
10726 : * might be indexed by (scanline, pixel). But there can be
10727 : * a longitude and latitude arrays alongside that are also both indexed by
10728 : * (scanline, pixel), and are referenced from operational arrays for
10729 : * reprojection purposes.
10730 : *
10731 : * For netCDF, this will return the arrays referenced by the "coordinates"
10732 : * attribute.
10733 : *
10734 : * This method is the same as the C function
10735 : * GDALMDArrayGetCoordinateVariables().
10736 : *
10737 : * @return a vector of arrays
10738 : *
10739 : * @since GDAL 3.4
10740 : */
10741 :
10742 : std::vector<std::shared_ptr<GDALMDArray>>
10743 13 : GDALMDArray::GetCoordinateVariables() const
10744 : {
10745 13 : return {};
10746 : }
10747 :
10748 : /************************************************************************/
10749 : /* ~GDALExtendedDataType() */
10750 : /************************************************************************/
10751 :
10752 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10753 :
10754 : /************************************************************************/
10755 : /* GDALExtendedDataType() */
10756 : /************************************************************************/
10757 :
10758 44344 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10759 44344 : GDALExtendedDataTypeSubType eSubType)
10760 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10761 44344 : m_nMaxStringLength(nMaxStringLength)
10762 : {
10763 44344 : }
10764 :
10765 : /************************************************************************/
10766 : /* GDALExtendedDataType() */
10767 : /************************************************************************/
10768 :
10769 335189 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10770 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10771 335189 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10772 : {
10773 335189 : }
10774 :
10775 : /************************************************************************/
10776 : /* GDALExtendedDataType() */
10777 : /************************************************************************/
10778 :
10779 63 : GDALExtendedDataType::GDALExtendedDataType(
10780 : const std::string &osName, GDALDataType eBaseType,
10781 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10782 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10783 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10784 : {
10785 63 : }
10786 :
10787 : /************************************************************************/
10788 : /* GDALExtendedDataType() */
10789 : /************************************************************************/
10790 :
10791 1765 : GDALExtendedDataType::GDALExtendedDataType(
10792 : const std::string &osName, size_t nTotalSize,
10793 1765 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10794 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10795 1765 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10796 : {
10797 1765 : }
10798 :
10799 : /************************************************************************/
10800 : /* GDALExtendedDataType() */
10801 : /************************************************************************/
10802 :
10803 : /** Move constructor. */
10804 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10805 :
10806 : /************************************************************************/
10807 : /* GDALExtendedDataType() */
10808 : /************************************************************************/
10809 :
10810 : /** Copy constructor. */
10811 29923 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10812 59846 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10813 29923 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10814 29923 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10815 29923 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10816 : {
10817 29923 : if (m_eClass == GEDTC_COMPOUND)
10818 : {
10819 481 : for (const auto &elt : other.m_aoComponents)
10820 : {
10821 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10822 : }
10823 : }
10824 29923 : }
10825 :
10826 : /************************************************************************/
10827 : /* operator= () */
10828 : /************************************************************************/
10829 :
10830 : /** Copy assignment. */
10831 : GDALExtendedDataType &
10832 8564 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10833 : {
10834 8564 : if (this != &other)
10835 : {
10836 8564 : m_osName = other.m_osName;
10837 8564 : m_eClass = other.m_eClass;
10838 8564 : m_eSubType = other.m_eSubType;
10839 8564 : m_eNumericDT = other.m_eNumericDT;
10840 8564 : m_nSize = other.m_nSize;
10841 8564 : m_nMaxStringLength = other.m_nMaxStringLength;
10842 8564 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10843 8564 : m_aoComponents.clear();
10844 8564 : if (m_eClass == GEDTC_COMPOUND)
10845 : {
10846 0 : for (const auto &elt : other.m_aoComponents)
10847 : {
10848 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10849 : }
10850 : }
10851 : }
10852 8564 : return *this;
10853 : }
10854 :
10855 : /************************************************************************/
10856 : /* operator= () */
10857 : /************************************************************************/
10858 :
10859 : /** Move assignment. */
10860 : GDALExtendedDataType &
10861 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10862 :
10863 : /************************************************************************/
10864 : /* Create() */
10865 : /************************************************************************/
10866 :
10867 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10868 : *
10869 : * This is the same as the C function GDALExtendedDataTypeCreate()
10870 : *
10871 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10872 : * GDT_TypeCount
10873 : */
10874 335182 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10875 : {
10876 335182 : return GDALExtendedDataType(eType);
10877 : }
10878 :
10879 : /************************************************************************/
10880 : /* Create() */
10881 : /************************************************************************/
10882 :
10883 : /** Return a new GDALExtendedDataType from a raster attribute table.
10884 : *
10885 : * @param osName Type name
10886 : * @param eBaseType Base integer data type.
10887 : * @param poRAT Raster attribute table. Must not be NULL.
10888 : * @since 3.12
10889 : */
10890 : GDALExtendedDataType
10891 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10892 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10893 : {
10894 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10895 : }
10896 :
10897 : /************************************************************************/
10898 : /* Create() */
10899 : /************************************************************************/
10900 :
10901 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10902 : *
10903 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10904 : *
10905 : * @param osName Type name.
10906 : * @param nTotalSize Total size of the type in bytes.
10907 : * Should be large enough to store all components.
10908 : * @param components Components of the compound type.
10909 : */
10910 1772 : GDALExtendedDataType GDALExtendedDataType::Create(
10911 : const std::string &osName, size_t nTotalSize,
10912 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10913 : {
10914 1772 : size_t nLastOffset = 0;
10915 : // Some arbitrary threshold to avoid potential integer overflows
10916 1772 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10917 : {
10918 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10919 2 : return GDALExtendedDataType(GDT_Unknown);
10920 : }
10921 7512 : for (const auto &comp : components)
10922 : {
10923 : // Check alignment too ?
10924 5743 : if (comp->GetOffset() < nLastOffset)
10925 : {
10926 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10927 1 : return GDALExtendedDataType(GDT_Unknown);
10928 : }
10929 5742 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10930 : }
10931 1769 : if (nTotalSize < nLastOffset)
10932 : {
10933 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10934 1 : return GDALExtendedDataType(GDT_Unknown);
10935 : }
10936 1768 : if (nTotalSize == 0 || components.empty())
10937 : {
10938 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10939 3 : return GDALExtendedDataType(GDT_Unknown);
10940 : }
10941 1765 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10942 : }
10943 :
10944 : /************************************************************************/
10945 : /* Create() */
10946 : /************************************************************************/
10947 :
10948 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10949 : *
10950 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10951 : *
10952 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10953 : * unknown/unlimited
10954 : * @param eSubType Subtype.
10955 : */
10956 : GDALExtendedDataType
10957 44344 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10958 : GDALExtendedDataTypeSubType eSubType)
10959 : {
10960 44344 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10961 : }
10962 :
10963 : /************************************************************************/
10964 : /* operator==() */
10965 : /************************************************************************/
10966 :
10967 : /** Equality operator.
10968 : *
10969 : * This is the same as the C function GDALExtendedDataTypeEquals().
10970 : */
10971 5024 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10972 : {
10973 4997 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10974 10021 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10975 : {
10976 767 : return false;
10977 : }
10978 4257 : if (m_eClass == GEDTC_NUMERIC)
10979 : {
10980 1115 : return m_eNumericDT == other.m_eNumericDT;
10981 : }
10982 3142 : if (m_eClass == GEDTC_STRING)
10983 : {
10984 2752 : return true;
10985 : }
10986 390 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10987 390 : if (m_aoComponents.size() != other.m_aoComponents.size())
10988 : {
10989 2 : return false;
10990 : }
10991 2324 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10992 : {
10993 1936 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10994 : {
10995 0 : return false;
10996 : }
10997 : }
10998 388 : return true;
10999 : }
11000 :
11001 : /************************************************************************/
11002 : /* CanConvertTo() */
11003 : /************************************************************************/
11004 :
11005 : /** Return whether this data type can be converted to the other one.
11006 : *
11007 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
11008 : *
11009 : * @param other Target data type for the conversion being considered.
11010 : */
11011 22049 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
11012 : {
11013 22049 : if (m_eClass == GEDTC_NUMERIC)
11014 : {
11015 16015 : if (m_eNumericDT == GDT_Unknown)
11016 0 : return false;
11017 16015 : if (other.m_eClass == GEDTC_NUMERIC &&
11018 14579 : other.m_eNumericDT == GDT_Unknown)
11019 0 : return false;
11020 17451 : return other.m_eClass == GEDTC_NUMERIC ||
11021 17451 : other.m_eClass == GEDTC_STRING;
11022 : }
11023 6034 : if (m_eClass == GEDTC_STRING)
11024 : {
11025 5440 : return other.m_eClass == m_eClass;
11026 : }
11027 594 : CPLAssert(m_eClass == GEDTC_COMPOUND);
11028 594 : if (other.m_eClass != GEDTC_COMPOUND)
11029 0 : return false;
11030 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
11031 1188 : srcComponents;
11032 2822 : for (const auto &srcComp : m_aoComponents)
11033 : {
11034 2228 : srcComponents[srcComp->GetName()] = &srcComp;
11035 : }
11036 1345 : for (const auto &dstComp : other.m_aoComponents)
11037 : {
11038 752 : auto oIter = srcComponents.find(dstComp->GetName());
11039 752 : if (oIter == srcComponents.end())
11040 1 : return false;
11041 751 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
11042 0 : return false;
11043 : }
11044 593 : return true;
11045 : }
11046 :
11047 : /************************************************************************/
11048 : /* NeedsFreeDynamicMemory() */
11049 : /************************************************************************/
11050 :
11051 : /** Return whether the data type holds dynamically allocated memory, that
11052 : * needs to be freed with FreeDynamicMemory().
11053 : *
11054 : */
11055 5187 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
11056 : {
11057 5187 : switch (m_eClass)
11058 : {
11059 1691 : case GEDTC_STRING:
11060 1691 : return true;
11061 :
11062 3236 : case GEDTC_NUMERIC:
11063 3236 : return false;
11064 :
11065 260 : case GEDTC_COMPOUND:
11066 : {
11067 388 : for (const auto &comp : m_aoComponents)
11068 : {
11069 366 : if (comp->GetType().NeedsFreeDynamicMemory())
11070 238 : return true;
11071 : }
11072 : }
11073 : }
11074 22 : return false;
11075 : }
11076 :
11077 : /************************************************************************/
11078 : /* FreeDynamicMemory() */
11079 : /************************************************************************/
11080 :
11081 : /** Release the dynamic memory (strings typically) from a raw value.
11082 : *
11083 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
11084 : *
11085 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
11086 : */
11087 8510 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
11088 : {
11089 8510 : switch (m_eClass)
11090 : {
11091 4918 : case GEDTC_STRING:
11092 : {
11093 : char *pszStr;
11094 4918 : memcpy(&pszStr, pBuffer, sizeof(char *));
11095 4918 : if (pszStr)
11096 : {
11097 3745 : VSIFree(pszStr);
11098 : }
11099 4918 : break;
11100 : }
11101 :
11102 3263 : case GEDTC_NUMERIC:
11103 : {
11104 3263 : break;
11105 : }
11106 :
11107 329 : case GEDTC_COMPOUND:
11108 : {
11109 329 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
11110 2069 : for (const auto &comp : m_aoComponents)
11111 : {
11112 3480 : comp->GetType().FreeDynamicMemory(pabyBuffer +
11113 1740 : comp->GetOffset());
11114 : }
11115 329 : break;
11116 : }
11117 : }
11118 8510 : }
11119 :
11120 : /************************************************************************/
11121 : /* ~GDALEDTComponent() */
11122 : /************************************************************************/
11123 :
11124 : GDALEDTComponent::~GDALEDTComponent() = default;
11125 :
11126 : /************************************************************************/
11127 : /* GDALEDTComponent() */
11128 : /************************************************************************/
11129 :
11130 : /** constructor of a GDALEDTComponent
11131 : *
11132 : * This is the same as the C function GDALEDTComponendCreate()
11133 : *
11134 : * @param name Component name
11135 : * @param offset Offset in byte of the component in the compound data type.
11136 : * In case of nesting of compound data type, this should be
11137 : * the offset to the immediate belonging data type, not to the
11138 : * higher level one.
11139 : * @param type Component data type.
11140 : */
11141 5734 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
11142 5734 : const GDALExtendedDataType &type)
11143 5734 : : m_osName(name), m_nOffset(offset), m_oType(type)
11144 : {
11145 5734 : }
11146 :
11147 : /************************************************************************/
11148 : /* GDALEDTComponent() */
11149 : /************************************************************************/
11150 :
11151 : /** Copy constructor. */
11152 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
11153 :
11154 : /************************************************************************/
11155 : /* operator==() */
11156 : /************************************************************************/
11157 :
11158 : /** Equality operator.
11159 : */
11160 1936 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
11161 : {
11162 3872 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
11163 3872 : m_oType == other.m_oType;
11164 : }
11165 :
11166 : /************************************************************************/
11167 : /* ~GDALDimension() */
11168 : /************************************************************************/
11169 :
11170 : GDALDimension::~GDALDimension() = default;
11171 :
11172 : /************************************************************************/
11173 : /* GDALDimension() */
11174 : /************************************************************************/
11175 :
11176 : //! @cond Doxygen_Suppress
11177 : /** Constructor.
11178 : *
11179 : * @param osParentName Parent name
11180 : * @param osName name
11181 : * @param osType type. See GetType().
11182 : * @param osDirection direction. See GetDirection().
11183 : * @param nSize size.
11184 : */
11185 12519 : GDALDimension::GDALDimension(const std::string &osParentName,
11186 : const std::string &osName,
11187 : const std::string &osType,
11188 12519 : const std::string &osDirection, GUInt64 nSize)
11189 : : m_osName(osName),
11190 : m_osFullName(
11191 12519 : !osParentName.empty()
11192 17433 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
11193 : : osName),
11194 42471 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
11195 : {
11196 12519 : }
11197 :
11198 : //! @endcond
11199 :
11200 : /************************************************************************/
11201 : /* GetIndexingVariable() */
11202 : /************************************************************************/
11203 :
11204 : /** Return the variable that is used to index the dimension (if there is one).
11205 : *
11206 : * This is the array, typically one-dimensional, describing the values taken
11207 : * by the dimension.
11208 : */
11209 67 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11210 : {
11211 67 : return nullptr;
11212 : }
11213 :
11214 : /************************************************************************/
11215 : /* SetIndexingVariable() */
11216 : /************************************************************************/
11217 :
11218 : /** Set the variable that is used to index the dimension.
11219 : *
11220 : * This is the array, typically one-dimensional, describing the values taken
11221 : * by the dimension.
11222 : *
11223 : * Optionally implemented by drivers.
11224 : *
11225 : * Drivers known to implement it: MEM.
11226 : *
11227 : * @param poArray Variable to use to index the dimension.
11228 : * @return true in case of success.
11229 : */
11230 11 : bool GDALDimension::SetIndexingVariable(
11231 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11232 : {
11233 11 : CPLError(CE_Failure, CPLE_NotSupported,
11234 : "SetIndexingVariable() not implemented");
11235 11 : return false;
11236 : }
11237 :
11238 : /************************************************************************/
11239 : /* Rename() */
11240 : /************************************************************************/
11241 :
11242 : /** Rename the dimension.
11243 : *
11244 : * This is not implemented by all drivers.
11245 : *
11246 : * Drivers known to implement it: MEM, netCDF, ZARR.
11247 : *
11248 : * This is the same as the C function GDALDimensionRename().
11249 : *
11250 : * @param osNewName New name.
11251 : *
11252 : * @return true in case of success
11253 : * @since GDAL 3.8
11254 : */
11255 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11256 : {
11257 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11258 0 : return false;
11259 : }
11260 :
11261 : /************************************************************************/
11262 : /* BaseRename() */
11263 : /************************************************************************/
11264 :
11265 : //! @cond Doxygen_Suppress
11266 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11267 : {
11268 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11269 8 : m_osFullName += osNewName;
11270 8 : m_osName = osNewName;
11271 8 : }
11272 :
11273 : //! @endcond
11274 :
11275 : //! @cond Doxygen_Suppress
11276 : /************************************************************************/
11277 : /* ParentRenamed() */
11278 : /************************************************************************/
11279 :
11280 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11281 : {
11282 8 : m_osFullName = osNewParentFullName;
11283 8 : m_osFullName += "/";
11284 8 : m_osFullName += m_osName;
11285 8 : }
11286 :
11287 : //! @endcond
11288 :
11289 : //! @cond Doxygen_Suppress
11290 : /************************************************************************/
11291 : /* ParentDeleted() */
11292 : /************************************************************************/
11293 :
11294 4 : void GDALDimension::ParentDeleted()
11295 : {
11296 4 : }
11297 :
11298 : //! @endcond
11299 :
11300 : /************************************************************************/
11301 : /************************************************************************/
11302 : /************************************************************************/
11303 : /* C API */
11304 : /************************************************************************/
11305 : /************************************************************************/
11306 : /************************************************************************/
11307 :
11308 : /************************************************************************/
11309 : /* GDALExtendedDataTypeCreate() */
11310 : /************************************************************************/
11311 :
11312 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11313 : *
11314 : * This is the same as the C++ method GDALExtendedDataType::Create()
11315 : *
11316 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11317 : *
11318 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11319 : * GDT_TypeCount
11320 : *
11321 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11322 : */
11323 2171 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11324 : {
11325 2171 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11326 : {
11327 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11328 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11329 0 : return nullptr;
11330 : }
11331 : return new GDALExtendedDataTypeHS(
11332 2171 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11333 : }
11334 :
11335 : /************************************************************************/
11336 : /* GDALExtendedDataTypeCreateString() */
11337 : /************************************************************************/
11338 :
11339 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11340 : *
11341 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11342 : *
11343 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11344 : *
11345 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11346 : */
11347 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11348 : {
11349 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11350 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11351 : }
11352 :
11353 : /************************************************************************/
11354 : /* GDALExtendedDataTypeCreateStringEx() */
11355 : /************************************************************************/
11356 :
11357 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11358 : *
11359 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11360 : *
11361 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11362 : *
11363 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11364 : * @since GDAL 3.4
11365 : */
11366 : GDALExtendedDataTypeH
11367 229 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11368 : GDALExtendedDataTypeSubType eSubType)
11369 : {
11370 229 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11371 229 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11372 : }
11373 :
11374 : /************************************************************************/
11375 : /* GDALExtendedDataTypeCreateCompound() */
11376 : /************************************************************************/
11377 :
11378 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11379 : *
11380 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11381 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11382 : *
11383 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11384 : *
11385 : * @param pszName Type name.
11386 : * @param nTotalSize Total size of the type in bytes.
11387 : * Should be large enough to store all components.
11388 : * @param nComponents Number of components in comps array.
11389 : * @param comps Components.
11390 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11391 : */
11392 : GDALExtendedDataTypeH
11393 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11394 : size_t nComponents,
11395 : const GDALEDTComponentH *comps)
11396 : {
11397 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11398 54 : for (size_t i = 0; i < nComponents; i++)
11399 : {
11400 : compsCpp.emplace_back(
11401 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11402 : }
11403 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11404 66 : std::move(compsCpp));
11405 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11406 6 : return nullptr;
11407 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11408 : }
11409 :
11410 : /************************************************************************/
11411 : /* GDALExtendedDataTypeRelease() */
11412 : /************************************************************************/
11413 :
11414 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11415 : *
11416 : * Note: when applied on a object coming from a driver, this does not
11417 : * destroy the object in the file, database, etc...
11418 : */
11419 8546 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11420 : {
11421 8546 : delete hEDT;
11422 8546 : }
11423 :
11424 : /************************************************************************/
11425 : /* GDALExtendedDataTypeGetName() */
11426 : /************************************************************************/
11427 :
11428 : /** Return type name.
11429 : *
11430 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11431 : */
11432 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11433 : {
11434 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11435 8 : return hEDT->m_poImpl->GetName().c_str();
11436 : }
11437 :
11438 : /************************************************************************/
11439 : /* GDALExtendedDataTypeGetClass() */
11440 : /************************************************************************/
11441 :
11442 : /** Return type class.
11443 : *
11444 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11445 : */
11446 : GDALExtendedDataTypeClass
11447 13038 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11448 : {
11449 13038 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11450 13038 : return hEDT->m_poImpl->GetClass();
11451 : }
11452 :
11453 : /************************************************************************/
11454 : /* GDALExtendedDataTypeGetNumericDataType() */
11455 : /************************************************************************/
11456 :
11457 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11458 : *
11459 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11460 : */
11461 3528 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11462 : {
11463 3528 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11464 3528 : return hEDT->m_poImpl->GetNumericDataType();
11465 : }
11466 :
11467 : /************************************************************************/
11468 : /* GDALExtendedDataTypeGetSize() */
11469 : /************************************************************************/
11470 :
11471 : /** Return data type size in bytes.
11472 : *
11473 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11474 : */
11475 3353 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11476 : {
11477 3353 : VALIDATE_POINTER1(hEDT, __func__, 0);
11478 3353 : return hEDT->m_poImpl->GetSize();
11479 : }
11480 :
11481 : /************************************************************************/
11482 : /* GDALExtendedDataTypeGetMaxStringLength() */
11483 : /************************************************************************/
11484 :
11485 : /** Return the maximum length of a string in bytes.
11486 : *
11487 : * 0 indicates unknown/unlimited string.
11488 : *
11489 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11490 : */
11491 5 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11492 : {
11493 5 : VALIDATE_POINTER1(hEDT, __func__, 0);
11494 5 : return hEDT->m_poImpl->GetMaxStringLength();
11495 : }
11496 :
11497 : /************************************************************************/
11498 : /* GDALExtendedDataTypeCanConvertTo() */
11499 : /************************************************************************/
11500 :
11501 : /** Return whether this data type can be converted to the other one.
11502 : *
11503 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11504 : *
11505 : * @param hSourceEDT Source data type for the conversion being considered.
11506 : * @param hTargetEDT Target data type for the conversion being considered.
11507 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11508 : */
11509 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11510 : GDALExtendedDataTypeH hTargetEDT)
11511 : {
11512 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11513 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11514 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11515 : }
11516 :
11517 : /************************************************************************/
11518 : /* GDALExtendedDataTypeEquals() */
11519 : /************************************************************************/
11520 :
11521 : /** Return whether this data type is equal to another one.
11522 : *
11523 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11524 : *
11525 : * @param hFirstEDT First data type.
11526 : * @param hSecondEDT Second data type.
11527 : * @return TRUE if they are equal. FALSE otherwise.
11528 : */
11529 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11530 : GDALExtendedDataTypeH hSecondEDT)
11531 : {
11532 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11533 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11534 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11535 : }
11536 :
11537 : /************************************************************************/
11538 : /* GDALExtendedDataTypeGetSubType() */
11539 : /************************************************************************/
11540 :
11541 : /** Return the subtype of a type.
11542 : *
11543 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11544 : *
11545 : * @param hEDT Data type.
11546 : * @return subtype.
11547 : * @since 3.4
11548 : */
11549 : GDALExtendedDataTypeSubType
11550 116 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11551 : {
11552 116 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11553 116 : return hEDT->m_poImpl->GetSubType();
11554 : }
11555 :
11556 : /************************************************************************/
11557 : /* GDALExtendedDataTypeGetRAT() */
11558 : /************************************************************************/
11559 :
11560 : /** Return associated raster attribute table, when there is one.
11561 : *
11562 : * * For the netCDF driver, the RAT will capture enumerated types, with
11563 : * a "value" column with an integer value and a "name" column with the
11564 : * associated name.
11565 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11566 : *
11567 : * @param hEDT Data type.
11568 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11569 : * @since 3.12
11570 : */
11571 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11572 : {
11573 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11574 1 : return GDALRasterAttributeTable::ToHandle(
11575 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11576 : }
11577 :
11578 : /************************************************************************/
11579 : /* GDALExtendedDataTypeGetComponents() */
11580 : /************************************************************************/
11581 :
11582 : /** Return the components of the data type (only valid when GetClass() ==
11583 : * GEDTC_COMPOUND)
11584 : *
11585 : * The returned array and its content must be freed with
11586 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11587 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11588 : * individual array members).
11589 : *
11590 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11591 : *
11592 : * @param hEDT Data type
11593 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11594 : * @return an array of *pnCount components.
11595 : */
11596 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11597 : size_t *pnCount)
11598 : {
11599 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11600 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11601 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11602 : auto ret = static_cast<GDALEDTComponentH *>(
11603 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11604 131 : for (size_t i = 0; i < components.size(); i++)
11605 : {
11606 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11607 : }
11608 44 : *pnCount = components.size();
11609 44 : return ret;
11610 : }
11611 :
11612 : /************************************************************************/
11613 : /* GDALExtendedDataTypeFreeComponents() */
11614 : /************************************************************************/
11615 :
11616 : /** Free the return of GDALExtendedDataTypeGetComponents().
11617 : *
11618 : * @param components return value of GDALExtendedDataTypeGetComponents()
11619 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11620 : */
11621 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11622 : size_t nCount)
11623 : {
11624 131 : for (size_t i = 0; i < nCount; i++)
11625 : {
11626 87 : delete components[i];
11627 : }
11628 44 : CPLFree(components);
11629 44 : }
11630 :
11631 : /************************************************************************/
11632 : /* GDALEDTComponentCreate() */
11633 : /************************************************************************/
11634 :
11635 : /** Create a new GDALEDTComponent.
11636 : *
11637 : * The returned value must be freed with GDALEDTComponentRelease().
11638 : *
11639 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11640 : */
11641 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11642 : GDALExtendedDataTypeH hType)
11643 : {
11644 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11645 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11646 : return new GDALEDTComponentHS(
11647 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11648 : }
11649 :
11650 : /************************************************************************/
11651 : /* GDALEDTComponentRelease() */
11652 : /************************************************************************/
11653 :
11654 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11655 : *
11656 : * Note: when applied on a object coming from a driver, this does not
11657 : * destroy the object in the file, database, etc...
11658 : */
11659 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11660 : {
11661 61 : delete hComp;
11662 61 : }
11663 :
11664 : /************************************************************************/
11665 : /* GDALEDTComponentGetName() */
11666 : /************************************************************************/
11667 :
11668 : /** Return the name.
11669 : *
11670 : * The returned pointer is valid until hComp is released.
11671 : *
11672 : * This is the same as the C++ method GDALEDTComponent::GetName().
11673 : */
11674 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11675 : {
11676 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11677 33 : return hComp->m_poImpl->GetName().c_str();
11678 : }
11679 :
11680 : /************************************************************************/
11681 : /* GDALEDTComponentGetOffset() */
11682 : /************************************************************************/
11683 :
11684 : /** Return the offset (in bytes) of the component in the compound data type.
11685 : *
11686 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11687 : */
11688 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11689 : {
11690 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11691 31 : return hComp->m_poImpl->GetOffset();
11692 : }
11693 :
11694 : /************************************************************************/
11695 : /* GDALEDTComponentGetType() */
11696 : /************************************************************************/
11697 :
11698 : /** Return the data type of the component.
11699 : *
11700 : * This is the same as the C++ method GDALEDTComponent::GetType().
11701 : */
11702 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11703 : {
11704 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11705 : return new GDALExtendedDataTypeHS(
11706 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11707 : }
11708 :
11709 : /************************************************************************/
11710 : /* GDALGroupRelease() */
11711 : /************************************************************************/
11712 :
11713 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11714 : *
11715 : * Note: when applied on a object coming from a driver, this does not
11716 : * destroy the object in the file, database, etc...
11717 : */
11718 2278 : void GDALGroupRelease(GDALGroupH hGroup)
11719 : {
11720 2278 : delete hGroup;
11721 2278 : }
11722 :
11723 : /************************************************************************/
11724 : /* GDALGroupGetName() */
11725 : /************************************************************************/
11726 :
11727 : /** Return the name of the group.
11728 : *
11729 : * The returned pointer is valid until hGroup is released.
11730 : *
11731 : * This is the same as the C++ method GDALGroup::GetName().
11732 : */
11733 97 : const char *GDALGroupGetName(GDALGroupH hGroup)
11734 : {
11735 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11736 97 : return hGroup->m_poImpl->GetName().c_str();
11737 : }
11738 :
11739 : /************************************************************************/
11740 : /* GDALGroupGetFullName() */
11741 : /************************************************************************/
11742 :
11743 : /** Return the full name of the group.
11744 : *
11745 : * The returned pointer is valid until hGroup is released.
11746 : *
11747 : * This is the same as the C++ method GDALGroup::GetFullName().
11748 : */
11749 48 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11750 : {
11751 48 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11752 48 : return hGroup->m_poImpl->GetFullName().c_str();
11753 : }
11754 :
11755 : /************************************************************************/
11756 : /* GDALGroupGetMDArrayNames() */
11757 : /************************************************************************/
11758 :
11759 : /** Return the list of multidimensional array names contained in this group.
11760 : *
11761 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11762 : *
11763 : * @return the array names, to be freed with CSLDestroy()
11764 : */
11765 340 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11766 : {
11767 340 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11768 680 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11769 680 : CPLStringList res;
11770 857 : for (const auto &name : names)
11771 : {
11772 517 : res.AddString(name.c_str());
11773 : }
11774 340 : return res.StealList();
11775 : }
11776 :
11777 : /************************************************************************/
11778 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11779 : /************************************************************************/
11780 :
11781 : /** Return the list of multidimensional array full names contained in this
11782 : * group and its subgroups.
11783 : *
11784 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11785 : *
11786 : * @return the array names, to be freed with CSLDestroy()
11787 : *
11788 : * @since 3.11
11789 : */
11790 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11791 : CSLConstList papszGroupOptions,
11792 : CSLConstList papszArrayOptions)
11793 : {
11794 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11795 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11796 2 : papszGroupOptions, papszArrayOptions);
11797 2 : CPLStringList res;
11798 5 : for (const auto &name : names)
11799 : {
11800 4 : res.AddString(name.c_str());
11801 : }
11802 1 : return res.StealList();
11803 : }
11804 :
11805 : /************************************************************************/
11806 : /* GDALGroupOpenMDArray() */
11807 : /************************************************************************/
11808 :
11809 : /** Open and return a multidimensional array.
11810 : *
11811 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11812 : *
11813 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11814 : */
11815 1531 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11816 : CSLConstList papszOptions)
11817 : {
11818 1531 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11819 1531 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11820 4593 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11821 4593 : papszOptions);
11822 1531 : if (!array)
11823 30 : return nullptr;
11824 1501 : return new GDALMDArrayHS(array);
11825 : }
11826 :
11827 : /************************************************************************/
11828 : /* GDALGroupOpenMDArrayFromFullname() */
11829 : /************************************************************************/
11830 :
11831 : /** Open and return a multidimensional array from its fully qualified name.
11832 : *
11833 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11834 : *
11835 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11836 : *
11837 : * @since GDAL 3.2
11838 : */
11839 18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11840 : const char *pszFullname,
11841 : CSLConstList papszOptions)
11842 : {
11843 18 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11844 18 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11845 18 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11846 54 : std::string(pszFullname), papszOptions);
11847 18 : if (!array)
11848 2 : return nullptr;
11849 16 : return new GDALMDArrayHS(array);
11850 : }
11851 :
11852 : /************************************************************************/
11853 : /* GDALGroupResolveMDArray() */
11854 : /************************************************************************/
11855 :
11856 : /** Locate an array in a group and its subgroups by name.
11857 : *
11858 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11859 : * @since GDAL 3.2
11860 : */
11861 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11862 : const char *pszStartingPoint,
11863 : CSLConstList papszOptions)
11864 : {
11865 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11866 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11867 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11868 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11869 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11870 19 : if (!array)
11871 2 : return nullptr;
11872 17 : return new GDALMDArrayHS(array);
11873 : }
11874 :
11875 : /************************************************************************/
11876 : /* GDALGroupGetGroupNames() */
11877 : /************************************************************************/
11878 :
11879 : /** Return the list of sub-groups contained in this group.
11880 : *
11881 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11882 : *
11883 : * @return the group names, to be freed with CSLDestroy()
11884 : */
11885 104 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11886 : {
11887 104 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11888 208 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11889 208 : CPLStringList res;
11890 237 : for (const auto &name : names)
11891 : {
11892 133 : res.AddString(name.c_str());
11893 : }
11894 104 : return res.StealList();
11895 : }
11896 :
11897 : /************************************************************************/
11898 : /* GDALGroupOpenGroup() */
11899 : /************************************************************************/
11900 :
11901 : /** Open and return a sub-group.
11902 : *
11903 : * This is the same as the C++ method GDALGroup::OpenGroup().
11904 : *
11905 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11906 : */
11907 201 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11908 : CSLConstList papszOptions)
11909 : {
11910 201 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11911 201 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11912 : auto subGroup =
11913 603 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11914 201 : if (!subGroup)
11915 30 : return nullptr;
11916 171 : return new GDALGroupHS(subGroup);
11917 : }
11918 :
11919 : /************************************************************************/
11920 : /* GDALGroupGetVectorLayerNames() */
11921 : /************************************************************************/
11922 :
11923 : /** Return the list of layer names contained in this group.
11924 : *
11925 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11926 : *
11927 : * @return the group names, to be freed with CSLDestroy()
11928 : * @since 3.4
11929 : */
11930 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11931 : CSLConstList papszOptions)
11932 : {
11933 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11934 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11935 16 : CPLStringList res;
11936 18 : for (const auto &name : names)
11937 : {
11938 10 : res.AddString(name.c_str());
11939 : }
11940 8 : return res.StealList();
11941 : }
11942 :
11943 : /************************************************************************/
11944 : /* GDALGroupOpenVectorLayer() */
11945 : /************************************************************************/
11946 :
11947 : /** Open and return a vector layer.
11948 : *
11949 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11950 : *
11951 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11952 : * the returned handled if only valid while the parent GDALDatasetH is kept
11953 : * opened.
11954 : *
11955 : * @return the vector layer, or nullptr.
11956 : * @since 3.4
11957 : */
11958 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11959 : const char *pszVectorLayerName,
11960 : CSLConstList papszOptions)
11961 : {
11962 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11963 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11964 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11965 24 : std::string(pszVectorLayerName), papszOptions));
11966 : }
11967 :
11968 : /************************************************************************/
11969 : /* GDALGroupOpenMDArrayFromFullname() */
11970 : /************************************************************************/
11971 :
11972 : /** Open and return a sub-group from its fully qualified name.
11973 : *
11974 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11975 : *
11976 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11977 : *
11978 : * @since GDAL 3.2
11979 : */
11980 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11981 : const char *pszFullname,
11982 : CSLConstList papszOptions)
11983 : {
11984 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11985 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11986 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11987 9 : std::string(pszFullname), papszOptions);
11988 3 : if (!subGroup)
11989 2 : return nullptr;
11990 1 : return new GDALGroupHS(subGroup);
11991 : }
11992 :
11993 : /************************************************************************/
11994 : /* GDALGroupGetDimensions() */
11995 : /************************************************************************/
11996 :
11997 : /** Return the list of dimensions contained in this group and used by its
11998 : * arrays.
11999 : *
12000 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12001 : * array itself needs to be freed, CPLFree() should be called (and
12002 : * GDALDimensionRelease() on individual array members).
12003 : *
12004 : * This is the same as the C++ method GDALGroup::GetDimensions().
12005 : *
12006 : * @param hGroup Group.
12007 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12008 : * @param papszOptions Driver specific options determining how dimensions
12009 : * should be retrieved. Pass nullptr for default behavior.
12010 : *
12011 : * @return an array of *pnCount dimensions.
12012 : */
12013 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
12014 : CSLConstList papszOptions)
12015 : {
12016 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12017 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12018 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
12019 : auto ret = static_cast<GDALDimensionH *>(
12020 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12021 230 : for (size_t i = 0; i < dims.size(); i++)
12022 : {
12023 157 : ret[i] = new GDALDimensionHS(dims[i]);
12024 : }
12025 73 : *pnCount = dims.size();
12026 73 : return ret;
12027 : }
12028 :
12029 : /************************************************************************/
12030 : /* GDALGroupGetAttribute() */
12031 : /************************************************************************/
12032 :
12033 : /** Return an attribute by its name.
12034 : *
12035 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12036 : *
12037 : * The returned attribute must be freed with GDALAttributeRelease().
12038 : */
12039 120 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
12040 : {
12041 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12042 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12043 360 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
12044 120 : if (attr)
12045 116 : return new GDALAttributeHS(attr);
12046 4 : return nullptr;
12047 : }
12048 :
12049 : /************************************************************************/
12050 : /* GDALGroupGetAttributes() */
12051 : /************************************************************************/
12052 :
12053 : /** Return the list of attributes contained in this group.
12054 : *
12055 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12056 : * array itself needs to be freed, CPLFree() should be called (and
12057 : * GDALAttributeRelease() on individual array members).
12058 : *
12059 : * This is the same as the C++ method GDALGroup::GetAttributes().
12060 : *
12061 : * @param hGroup Group.
12062 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12063 : * @param papszOptions Driver specific options determining how attributes
12064 : * should be retrieved. Pass nullptr for default behavior.
12065 : *
12066 : * @return an array of *pnCount attributes.
12067 : */
12068 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
12069 : CSLConstList papszOptions)
12070 : {
12071 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12072 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12073 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
12074 : auto ret = static_cast<GDALAttributeH *>(
12075 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12076 229 : for (size_t i = 0; i < attrs.size(); i++)
12077 : {
12078 158 : ret[i] = new GDALAttributeHS(attrs[i]);
12079 : }
12080 71 : *pnCount = attrs.size();
12081 71 : return ret;
12082 : }
12083 :
12084 : /************************************************************************/
12085 : /* GDALGroupGetStructuralInfo() */
12086 : /************************************************************************/
12087 :
12088 : /** Return structural information on the group.
12089 : *
12090 : * This may be the compression, etc..
12091 : *
12092 : * The return value should not be freed and is valid until GDALGroup is
12093 : * released or this function called again.
12094 : *
12095 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
12096 : */
12097 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
12098 : {
12099 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12100 4 : return hGroup->m_poImpl->GetStructuralInfo();
12101 : }
12102 :
12103 : /************************************************************************/
12104 : /* GDALGroupGetDataTypeCount() */
12105 : /************************************************************************/
12106 :
12107 : /** Return the number of data types associated with the group
12108 : * (typically enumerations).
12109 : *
12110 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
12111 : *
12112 : * @since 3.12
12113 : */
12114 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
12115 : {
12116 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
12117 4 : return hGroup->m_poImpl->GetDataTypes().size();
12118 : }
12119 :
12120 : /************************************************************************/
12121 : /* GDALGroupGetDataType() */
12122 : /************************************************************************/
12123 :
12124 : /** Return one of the data types associated with the group.
12125 : *
12126 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
12127 : *
12128 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
12129 : * or nullptr in case of error.
12130 : * @since 3.12
12131 : */
12132 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
12133 : {
12134 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12135 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
12136 0 : return nullptr;
12137 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
12138 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
12139 : }
12140 :
12141 : /************************************************************************/
12142 : /* GDALReleaseAttributes() */
12143 : /************************************************************************/
12144 :
12145 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
12146 : *
12147 : * @param attributes return pointer of above methods
12148 : * @param nCount *pnCount value returned by above methods
12149 : */
12150 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
12151 : {
12152 418 : for (size_t i = 0; i < nCount; i++)
12153 : {
12154 288 : delete attributes[i];
12155 : }
12156 130 : CPLFree(attributes);
12157 130 : }
12158 :
12159 : /************************************************************************/
12160 : /* GDALGroupCreateGroup() */
12161 : /************************************************************************/
12162 :
12163 : /** Create a sub-group within a group.
12164 : *
12165 : * This is the same as the C++ method GDALGroup::CreateGroup().
12166 : *
12167 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
12168 : */
12169 185 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12170 : CSLConstList papszOptions)
12171 : {
12172 185 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12173 185 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
12174 555 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
12175 555 : papszOptions);
12176 185 : if (!ret)
12177 52 : return nullptr;
12178 133 : return new GDALGroupHS(ret);
12179 : }
12180 :
12181 : /************************************************************************/
12182 : /* GDALGroupDeleteGroup() */
12183 : /************************************************************************/
12184 :
12185 : /** Delete a sub-group from a group.
12186 : *
12187 : * After this call, if a previously obtained instance of the deleted object
12188 : * is still alive, no method other than for freeing it should be invoked.
12189 : *
12190 : * This is the same as the C++ method GDALGroup::DeleteGroup().
12191 : *
12192 : * @return true in case of success.
12193 : * @since GDAL 3.8
12194 : */
12195 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12196 : CSLConstList papszOptions)
12197 : {
12198 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12199 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12200 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12201 20 : papszOptions);
12202 : }
12203 :
12204 : /************************************************************************/
12205 : /* GDALGroupCreateDimension() */
12206 : /************************************************************************/
12207 :
12208 : /** Create a dimension within a group.
12209 : *
12210 : * This is the same as the C++ method GDALGroup::CreateDimension().
12211 : *
12212 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12213 : */
12214 796 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12215 : const char *pszType,
12216 : const char *pszDirection, GUInt64 nSize,
12217 : CSLConstList papszOptions)
12218 : {
12219 796 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12220 796 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12221 796 : auto ret = hGroup->m_poImpl->CreateDimension(
12222 1592 : std::string(pszName), std::string(pszType ? pszType : ""),
12223 3184 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12224 796 : if (!ret)
12225 9 : return nullptr;
12226 787 : return new GDALDimensionHS(ret);
12227 : }
12228 :
12229 : /************************************************************************/
12230 : /* GDALGroupCreateMDArray() */
12231 : /************************************************************************/
12232 :
12233 : /** Create a multidimensional array within a group.
12234 : *
12235 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12236 : *
12237 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12238 : */
12239 724 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12240 : size_t nDimensions,
12241 : GDALDimensionH *pahDimensions,
12242 : GDALExtendedDataTypeH hEDT,
12243 : CSLConstList papszOptions)
12244 : {
12245 724 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12246 724 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12247 724 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12248 1448 : std::vector<std::shared_ptr<GDALDimension>> dims;
12249 724 : dims.reserve(nDimensions);
12250 1736 : for (size_t i = 0; i < nDimensions; i++)
12251 1012 : dims.push_back(pahDimensions[i]->m_poImpl);
12252 2172 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12253 2172 : *(hEDT->m_poImpl), papszOptions);
12254 724 : if (!ret)
12255 66 : return nullptr;
12256 658 : return new GDALMDArrayHS(ret);
12257 : }
12258 :
12259 : /************************************************************************/
12260 : /* GDALGroupDeleteMDArray() */
12261 : /************************************************************************/
12262 :
12263 : /** Delete an array from a group.
12264 : *
12265 : * After this call, if a previously obtained instance of the deleted object
12266 : * is still alive, no method other than for freeing it should be invoked.
12267 : *
12268 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12269 : *
12270 : * @return true in case of success.
12271 : * @since GDAL 3.8
12272 : */
12273 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12274 : CSLConstList papszOptions)
12275 : {
12276 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12277 20 : VALIDATE_POINTER1(pszName, __func__, false);
12278 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12279 : }
12280 :
12281 : /************************************************************************/
12282 : /* GDALGroupCreateAttribute() */
12283 : /************************************************************************/
12284 :
12285 : /** Create a attribute within a group.
12286 : *
12287 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12288 : *
12289 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12290 : */
12291 142 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12292 : size_t nDimensions,
12293 : const GUInt64 *panDimensions,
12294 : GDALExtendedDataTypeH hEDT,
12295 : CSLConstList papszOptions)
12296 : {
12297 142 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12298 142 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12299 284 : std::vector<GUInt64> dims;
12300 142 : dims.reserve(nDimensions);
12301 200 : for (size_t i = 0; i < nDimensions; i++)
12302 58 : dims.push_back(panDimensions[i]);
12303 142 : auto ret = hGroup->m_poImpl->CreateAttribute(
12304 426 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12305 142 : if (!ret)
12306 18 : return nullptr;
12307 124 : return new GDALAttributeHS(ret);
12308 : }
12309 :
12310 : /************************************************************************/
12311 : /* GDALGroupDeleteAttribute() */
12312 : /************************************************************************/
12313 :
12314 : /** Delete an attribute from a group.
12315 : *
12316 : * After this call, if a previously obtained instance of the deleted object
12317 : * is still alive, no method other than for freeing it should be invoked.
12318 : *
12319 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12320 : *
12321 : * @return true in case of success.
12322 : * @since GDAL 3.8
12323 : */
12324 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12325 : CSLConstList papszOptions)
12326 : {
12327 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12328 25 : VALIDATE_POINTER1(pszName, __func__, false);
12329 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12330 25 : papszOptions);
12331 : }
12332 :
12333 : /************************************************************************/
12334 : /* GDALGroupRename() */
12335 : /************************************************************************/
12336 :
12337 : /** Rename the group.
12338 : *
12339 : * This is not implemented by all drivers.
12340 : *
12341 : * Drivers known to implement it: MEM, netCDF.
12342 : *
12343 : * This is the same as the C++ method GDALGroup::Rename()
12344 : *
12345 : * @return true in case of success
12346 : * @since GDAL 3.8
12347 : */
12348 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12349 : {
12350 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12351 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12352 45 : return hGroup->m_poImpl->Rename(pszNewName);
12353 : }
12354 :
12355 : /************************************************************************/
12356 : /* GDALGroupSubsetDimensionFromSelection() */
12357 : /************************************************************************/
12358 :
12359 : /** Return a virtual group whose one dimension has been subset according to a
12360 : * selection.
12361 : *
12362 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12363 : *
12364 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12365 : */
12366 : GDALGroupH
12367 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12368 : const char *pszSelection,
12369 : CPL_UNUSED CSLConstList papszOptions)
12370 : {
12371 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12372 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12373 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12374 42 : std::string(pszSelection));
12375 14 : if (!hNewGroup)
12376 8 : return nullptr;
12377 6 : return new GDALGroupHS(hNewGroup);
12378 : }
12379 :
12380 : /************************************************************************/
12381 : /* GDALMDArrayRelease() */
12382 : /************************************************************************/
12383 :
12384 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12385 : *
12386 : * Note: when applied on a object coming from a driver, this does not
12387 : * destroy the object in the file, database, etc...
12388 : */
12389 2928 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12390 : {
12391 2928 : delete hMDArray;
12392 2928 : }
12393 :
12394 : /************************************************************************/
12395 : /* GDALMDArrayGetName() */
12396 : /************************************************************************/
12397 :
12398 : /** Return array name.
12399 : *
12400 : * This is the same as the C++ method GDALMDArray::GetName()
12401 : */
12402 84 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12403 : {
12404 84 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12405 84 : return hArray->m_poImpl->GetName().c_str();
12406 : }
12407 :
12408 : /************************************************************************/
12409 : /* GDALMDArrayGetFullName() */
12410 : /************************************************************************/
12411 :
12412 : /** Return array full name.
12413 : *
12414 : * This is the same as the C++ method GDALMDArray::GetFullName()
12415 : */
12416 70 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12417 : {
12418 70 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12419 70 : return hArray->m_poImpl->GetFullName().c_str();
12420 : }
12421 :
12422 : /************************************************************************/
12423 : /* GDALMDArrayGetName() */
12424 : /************************************************************************/
12425 :
12426 : /** Return the total number of values in the array.
12427 : *
12428 : * This is the same as the C++ method
12429 : * GDALAbstractMDArray::GetTotalElementsCount()
12430 : */
12431 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12432 : {
12433 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12434 6 : return hArray->m_poImpl->GetTotalElementsCount();
12435 : }
12436 :
12437 : /************************************************************************/
12438 : /* GDALMDArrayGetDimensionCount() */
12439 : /************************************************************************/
12440 :
12441 : /** Return the number of dimensions.
12442 : *
12443 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12444 : */
12445 14255 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12446 : {
12447 14255 : VALIDATE_POINTER1(hArray, __func__, 0);
12448 14255 : return hArray->m_poImpl->GetDimensionCount();
12449 : }
12450 :
12451 : /************************************************************************/
12452 : /* GDALMDArrayGetDimensions() */
12453 : /************************************************************************/
12454 :
12455 : /** Return the dimensions of the array
12456 : *
12457 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12458 : * array itself needs to be freed, CPLFree() should be called (and
12459 : * GDALDimensionRelease() on individual array members).
12460 : *
12461 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12462 : *
12463 : * @param hArray Array.
12464 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12465 : *
12466 : * @return an array of *pnCount dimensions.
12467 : */
12468 3121 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12469 : {
12470 3121 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12471 3121 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12472 3121 : const auto &dims(hArray->m_poImpl->GetDimensions());
12473 : auto ret = static_cast<GDALDimensionH *>(
12474 3121 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12475 8921 : for (size_t i = 0; i < dims.size(); i++)
12476 : {
12477 5800 : ret[i] = new GDALDimensionHS(dims[i]);
12478 : }
12479 3121 : *pnCount = dims.size();
12480 3121 : return ret;
12481 : }
12482 :
12483 : /************************************************************************/
12484 : /* GDALReleaseDimensions() */
12485 : /************************************************************************/
12486 :
12487 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12488 : *
12489 : * @param dims return pointer of above methods
12490 : * @param nCount *pnCount value returned by above methods
12491 : */
12492 3194 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12493 : {
12494 9151 : for (size_t i = 0; i < nCount; i++)
12495 : {
12496 5957 : delete dims[i];
12497 : }
12498 3194 : CPLFree(dims);
12499 3194 : }
12500 :
12501 : /************************************************************************/
12502 : /* GDALMDArrayGetDataType() */
12503 : /************************************************************************/
12504 :
12505 : /** Return the data type
12506 : *
12507 : * The return must be freed with GDALExtendedDataTypeRelease().
12508 : */
12509 5578 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12510 : {
12511 5578 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12512 : return new GDALExtendedDataTypeHS(
12513 5578 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12514 : }
12515 :
12516 : /************************************************************************/
12517 : /* GDALMDArrayRead() */
12518 : /************************************************************************/
12519 :
12520 : /** Read part or totality of a multidimensional array.
12521 : *
12522 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12523 : *
12524 : * @return TRUE in case of success.
12525 : */
12526 2670 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12527 : const size_t *count, const GInt64 *arrayStep,
12528 : const GPtrDiff_t *bufferStride,
12529 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12530 : const void *pDstBufferAllocStart,
12531 : size_t nDstBufferAllocSize)
12532 : {
12533 2670 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12534 2670 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12535 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12536 : {
12537 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12538 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12539 : }
12540 2670 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12541 2670 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12542 5340 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12543 2670 : *(bufferDataType->m_poImpl), pDstBuffer,
12544 2670 : pDstBufferAllocStart, nDstBufferAllocSize);
12545 : }
12546 :
12547 : /************************************************************************/
12548 : /* GDALMDArrayWrite() */
12549 : /************************************************************************/
12550 :
12551 : /** Write part or totality of a multidimensional array.
12552 : *
12553 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12554 : *
12555 : * @return TRUE in case of success.
12556 : */
12557 669 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12558 : const size_t *count, const GInt64 *arrayStep,
12559 : const GPtrDiff_t *bufferStride,
12560 : GDALExtendedDataTypeH bufferDataType,
12561 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12562 : size_t nSrcBufferAllocSize)
12563 : {
12564 669 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12565 669 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12566 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12567 : {
12568 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12569 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12570 : }
12571 669 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12572 669 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12573 1338 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12574 669 : bufferStride, *(bufferDataType->m_poImpl),
12575 : pSrcBuffer, pSrcBufferAllocStart,
12576 669 : nSrcBufferAllocSize);
12577 : }
12578 :
12579 : /************************************************************************/
12580 : /* GDALMDArrayAdviseRead() */
12581 : /************************************************************************/
12582 :
12583 : /** Advise driver of upcoming read requests.
12584 : *
12585 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12586 : *
12587 : * @return TRUE in case of success.
12588 : *
12589 : * @since GDAL 3.2
12590 : */
12591 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12592 : const size_t *count)
12593 : {
12594 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12595 : }
12596 :
12597 : /************************************************************************/
12598 : /* GDALMDArrayAdviseReadEx() */
12599 : /************************************************************************/
12600 :
12601 : /** Advise driver of upcoming read requests.
12602 : *
12603 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12604 : *
12605 : * @return TRUE in case of success.
12606 : *
12607 : * @since GDAL 3.4
12608 : */
12609 19 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12610 : const size_t *count, CSLConstList papszOptions)
12611 : {
12612 19 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12613 19 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12614 : }
12615 :
12616 : /************************************************************************/
12617 : /* GDALMDArrayGetAttribute() */
12618 : /************************************************************************/
12619 :
12620 : /** Return an attribute by its name.
12621 : *
12622 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12623 : *
12624 : * The returned attribute must be freed with GDALAttributeRelease().
12625 : */
12626 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12627 : {
12628 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12629 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12630 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12631 120 : if (attr)
12632 111 : return new GDALAttributeHS(attr);
12633 9 : return nullptr;
12634 : }
12635 :
12636 : /************************************************************************/
12637 : /* GDALMDArrayGetAttributes() */
12638 : /************************************************************************/
12639 :
12640 : /** Return the list of attributes contained in this array.
12641 : *
12642 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12643 : * array itself needs to be freed, CPLFree() should be called (and
12644 : * GDALAttributeRelease() on individual array members).
12645 : *
12646 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12647 : *
12648 : * @param hArray Array.
12649 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12650 : * @param papszOptions Driver specific options determining how attributes
12651 : * should be retrieved. Pass nullptr for default behavior.
12652 : *
12653 : * @return an array of *pnCount attributes.
12654 : */
12655 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12656 : CSLConstList papszOptions)
12657 : {
12658 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12659 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12660 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12661 : auto ret = static_cast<GDALAttributeH *>(
12662 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12663 189 : for (size_t i = 0; i < attrs.size(); i++)
12664 : {
12665 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12666 : }
12667 59 : *pnCount = attrs.size();
12668 59 : return ret;
12669 : }
12670 :
12671 : /************************************************************************/
12672 : /* GDALMDArrayCreateAttribute() */
12673 : /************************************************************************/
12674 :
12675 : /** Create a attribute within an array.
12676 : *
12677 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12678 : *
12679 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12680 : */
12681 188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12682 : const char *pszName,
12683 : size_t nDimensions,
12684 : const GUInt64 *panDimensions,
12685 : GDALExtendedDataTypeH hEDT,
12686 : CSLConstList papszOptions)
12687 : {
12688 188 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12689 188 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12690 188 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12691 376 : std::vector<GUInt64> dims;
12692 188 : dims.reserve(nDimensions);
12693 249 : for (size_t i = 0; i < nDimensions; i++)
12694 61 : dims.push_back(panDimensions[i]);
12695 188 : auto ret = hArray->m_poImpl->CreateAttribute(
12696 564 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12697 188 : if (!ret)
12698 9 : return nullptr;
12699 179 : return new GDALAttributeHS(ret);
12700 : }
12701 :
12702 : /************************************************************************/
12703 : /* GDALMDArrayDeleteAttribute() */
12704 : /************************************************************************/
12705 :
12706 : /** Delete an attribute from an array.
12707 : *
12708 : * After this call, if a previously obtained instance of the deleted object
12709 : * is still alive, no method other than for freeing it should be invoked.
12710 : *
12711 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12712 : *
12713 : * @return true in case of success.
12714 : * @since GDAL 3.8
12715 : */
12716 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12717 : CSLConstList papszOptions)
12718 : {
12719 24 : VALIDATE_POINTER1(hArray, __func__, false);
12720 24 : VALIDATE_POINTER1(pszName, __func__, false);
12721 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12722 24 : papszOptions);
12723 : }
12724 :
12725 : /************************************************************************/
12726 : /* GDALMDArrayGetRawNoDataValue() */
12727 : /************************************************************************/
12728 :
12729 : /** Return the nodata value as a "raw" value.
12730 : *
12731 : * The value returned might be nullptr in case of no nodata value. When
12732 : * a nodata value is registered, a non-nullptr will be returned whose size in
12733 : * bytes is GetDataType().GetSize().
12734 : *
12735 : * The returned value should not be modified or freed.
12736 : *
12737 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12738 : *
12739 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12740 : */
12741 77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12742 : {
12743 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12744 77 : return hArray->m_poImpl->GetRawNoDataValue();
12745 : }
12746 :
12747 : /************************************************************************/
12748 : /* GDALMDArrayGetNoDataValueAsDouble() */
12749 : /************************************************************************/
12750 :
12751 : /** Return the nodata value as a double.
12752 : *
12753 : * The value returned might be nullptr in case of no nodata value. When
12754 : * a nodata value is registered, a non-nullptr will be returned whose size in
12755 : * bytes is GetDataType().GetSize().
12756 : *
12757 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12758 : *
12759 : * @param hArray Array handle.
12760 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12761 : * if a nodata value exists and can be converted to double. Might be nullptr.
12762 : *
12763 : * @return the nodata value as a double. A 0.0 value might also indicate the
12764 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12765 : * will be set to false then).
12766 : */
12767 122 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12768 : int *pbHasNoDataValue)
12769 : {
12770 122 : VALIDATE_POINTER1(hArray, __func__, 0);
12771 122 : bool bHasNodataValue = false;
12772 122 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12773 122 : if (pbHasNoDataValue)
12774 122 : *pbHasNoDataValue = bHasNodataValue;
12775 122 : return ret;
12776 : }
12777 :
12778 : /************************************************************************/
12779 : /* GDALMDArrayGetNoDataValueAsInt64() */
12780 : /************************************************************************/
12781 :
12782 : /** Return the nodata value as a Int64.
12783 : *
12784 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12785 : *
12786 : * @param hArray Array handle.
12787 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12788 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12789 : *
12790 : * @return the nodata value as a Int64.
12791 : * @since GDAL 3.5
12792 : */
12793 13 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12794 : int *pbHasNoDataValue)
12795 : {
12796 13 : VALIDATE_POINTER1(hArray, __func__, 0);
12797 13 : bool bHasNodataValue = false;
12798 13 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12799 13 : if (pbHasNoDataValue)
12800 13 : *pbHasNoDataValue = bHasNodataValue;
12801 13 : return ret;
12802 : }
12803 :
12804 : /************************************************************************/
12805 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12806 : /************************************************************************/
12807 :
12808 : /** Return the nodata value as a UInt64.
12809 : *
12810 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12811 : *
12812 : * @param hArray Array handle.
12813 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12814 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12815 : *
12816 : * @return the nodata value as a UInt64.
12817 : * @since GDAL 3.5
12818 : */
12819 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12820 : int *pbHasNoDataValue)
12821 : {
12822 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12823 7 : bool bHasNodataValue = false;
12824 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12825 7 : if (pbHasNoDataValue)
12826 7 : *pbHasNoDataValue = bHasNodataValue;
12827 7 : return ret;
12828 : }
12829 :
12830 : /************************************************************************/
12831 : /* GDALMDArraySetRawNoDataValue() */
12832 : /************************************************************************/
12833 :
12834 : /** Set the nodata value as a "raw" value.
12835 : *
12836 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12837 : * void*).
12838 : *
12839 : * @return TRUE in case of success.
12840 : */
12841 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12842 : {
12843 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12844 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12845 : }
12846 :
12847 : /************************************************************************/
12848 : /* GDALMDArraySetNoDataValueAsDouble() */
12849 : /************************************************************************/
12850 :
12851 : /** Set the nodata value as a double.
12852 : *
12853 : * If the natural data type of the attribute/array is not double, type
12854 : * conversion will occur to the type returned by GetDataType().
12855 : *
12856 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12857 : *
12858 : * @return TRUE in case of success.
12859 : */
12860 57 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12861 : {
12862 57 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12863 57 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12864 : }
12865 :
12866 : /************************************************************************/
12867 : /* GDALMDArraySetNoDataValueAsInt64() */
12868 : /************************************************************************/
12869 :
12870 : /** Set the nodata value as a Int64.
12871 : *
12872 : * If the natural data type of the attribute/array is not Int64, type conversion
12873 : * will occur to the type returned by GetDataType().
12874 : *
12875 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12876 : *
12877 : * @return TRUE in case of success.
12878 : * @since GDAL 3.5
12879 : */
12880 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12881 : {
12882 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12883 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12884 : }
12885 :
12886 : /************************************************************************/
12887 : /* GDALMDArraySetNoDataValueAsUInt64() */
12888 : /************************************************************************/
12889 :
12890 : /** Set the nodata value as a UInt64.
12891 : *
12892 : * If the natural data type of the attribute/array is not UInt64, type
12893 : * conversion will occur to the type returned by GetDataType().
12894 : *
12895 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12896 : *
12897 : * @return TRUE in case of success.
12898 : * @since GDAL 3.5
12899 : */
12900 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12901 : uint64_t nNoDataValue)
12902 : {
12903 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12904 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12905 : }
12906 :
12907 : /************************************************************************/
12908 : /* GDALMDArrayResize() */
12909 : /************************************************************************/
12910 :
12911 : /** Resize an array to new dimensions.
12912 : *
12913 : * Not all drivers may allow this operation, and with restrictions (e.g.
12914 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12915 : *
12916 : * Resizing a dimension used in other arrays will cause those other arrays
12917 : * to be resized.
12918 : *
12919 : * This is the same as the C++ method GDALMDArray::Resize().
12920 : *
12921 : * @param hArray Array.
12922 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12923 : * new size of each indexing dimension.
12924 : * @param papszOptions Options. (Driver specific)
12925 : * @return true in case of success.
12926 : * @since GDAL 3.7
12927 : */
12928 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12929 : CSLConstList papszOptions)
12930 : {
12931 42 : VALIDATE_POINTER1(hArray, __func__, false);
12932 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12933 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12934 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12935 : {
12936 83 : anNewDimSizes[i] = panNewDimSizes[i];
12937 : }
12938 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12939 : }
12940 :
12941 : /************************************************************************/
12942 : /* GDALMDArraySetScale() */
12943 : /************************************************************************/
12944 :
12945 : /** Set the scale value to apply to raw values.
12946 : *
12947 : * unscaled_value = raw_value * GetScale() + GetOffset()
12948 : *
12949 : * This is the same as the C++ method GDALMDArray::SetScale().
12950 : *
12951 : * @return TRUE in case of success.
12952 : */
12953 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12954 : {
12955 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12956 0 : return hArray->m_poImpl->SetScale(dfScale);
12957 : }
12958 :
12959 : /************************************************************************/
12960 : /* GDALMDArraySetScaleEx() */
12961 : /************************************************************************/
12962 :
12963 : /** Set the scale value to apply to raw values.
12964 : *
12965 : * unscaled_value = raw_value * GetScale() + GetOffset()
12966 : *
12967 : * This is the same as the C++ method GDALMDArray::SetScale().
12968 : *
12969 : * @return TRUE in case of success.
12970 : * @since GDAL 3.3
12971 : */
12972 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12973 : GDALDataType eStorageType)
12974 : {
12975 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12976 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12977 : }
12978 :
12979 : /************************************************************************/
12980 : /* GDALMDArraySetOffset() */
12981 : /************************************************************************/
12982 :
12983 : /** Set the scale value to apply to raw values.
12984 : *
12985 : * unscaled_value = raw_value * GetScale() + GetOffset()
12986 : *
12987 : * This is the same as the C++ method GDALMDArray::SetOffset().
12988 : *
12989 : * @return TRUE in case of success.
12990 : */
12991 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12992 : {
12993 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12994 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12995 : }
12996 :
12997 : /************************************************************************/
12998 : /* GDALMDArraySetOffsetEx() */
12999 : /************************************************************************/
13000 :
13001 : /** Set the scale value to apply to raw values.
13002 : *
13003 : * unscaled_value = raw_value * GetOffset() + GetOffset()
13004 : *
13005 : * This is the same as the C++ method GDALMDArray::SetOffset().
13006 : *
13007 : * @return TRUE in case of success.
13008 : * @since GDAL 3.3
13009 : */
13010 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
13011 : GDALDataType eStorageType)
13012 : {
13013 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13014 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
13015 : }
13016 :
13017 : /************************************************************************/
13018 : /* GDALMDArrayGetScale() */
13019 : /************************************************************************/
13020 :
13021 : /** Get the scale value to apply to raw values.
13022 : *
13023 : * unscaled_value = raw_value * GetScale() + GetOffset()
13024 : *
13025 : * This is the same as the C++ method GDALMDArray::GetScale().
13026 : *
13027 : * @return the scale value
13028 : */
13029 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
13030 : {
13031 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13032 105 : bool bHasValue = false;
13033 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
13034 105 : if (pbHasValue)
13035 105 : *pbHasValue = bHasValue;
13036 105 : return dfRet;
13037 : }
13038 :
13039 : /************************************************************************/
13040 : /* GDALMDArrayGetScaleEx() */
13041 : /************************************************************************/
13042 :
13043 : /** Get the scale value to apply to raw values.
13044 : *
13045 : * unscaled_value = raw_value * GetScale() + GetScale()
13046 : *
13047 : * This is the same as the C++ method GDALMDArray::GetScale().
13048 : *
13049 : * @return the scale value
13050 : * @since GDAL 3.3
13051 : */
13052 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
13053 : GDALDataType *peStorageType)
13054 : {
13055 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13056 5 : bool bHasValue = false;
13057 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
13058 5 : if (pbHasValue)
13059 5 : *pbHasValue = bHasValue;
13060 5 : return dfRet;
13061 : }
13062 :
13063 : /************************************************************************/
13064 : /* GDALMDArrayGetOffset() */
13065 : /************************************************************************/
13066 :
13067 : /** Get the scale value to apply to raw values.
13068 : *
13069 : * unscaled_value = raw_value * GetScale() + GetOffset()
13070 : *
13071 : * This is the same as the C++ method GDALMDArray::GetOffset().
13072 : *
13073 : * @return the scale value
13074 : */
13075 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
13076 : {
13077 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13078 102 : bool bHasValue = false;
13079 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
13080 102 : if (pbHasValue)
13081 102 : *pbHasValue = bHasValue;
13082 102 : return dfRet;
13083 : }
13084 :
13085 : /************************************************************************/
13086 : /* GDALMDArrayGetOffsetEx() */
13087 : /************************************************************************/
13088 :
13089 : /** Get the scale value to apply to raw values.
13090 : *
13091 : * unscaled_value = raw_value * GetScale() + GetOffset()
13092 : *
13093 : * This is the same as the C++ method GDALMDArray::GetOffset().
13094 : *
13095 : * @return the scale value
13096 : * @since GDAL 3.3
13097 : */
13098 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
13099 : GDALDataType *peStorageType)
13100 : {
13101 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
13102 5 : bool bHasValue = false;
13103 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
13104 5 : if (pbHasValue)
13105 5 : *pbHasValue = bHasValue;
13106 5 : return dfRet;
13107 : }
13108 :
13109 : /************************************************************************/
13110 : /* GDALMDArrayGetBlockSize() */
13111 : /************************************************************************/
13112 :
13113 : /** Return the "natural" block size of the array along all dimensions.
13114 : *
13115 : * Some drivers might organize the array in tiles/blocks and reading/writing
13116 : * aligned on those tile/block boundaries will be more efficient.
13117 : *
13118 : * The returned number of elements in the vector is the same as
13119 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
13120 : * the natural block size along the considered dimension.
13121 : * "Flat" arrays will typically return a vector of values set to 0.
13122 : *
13123 : * The default implementation will return a vector of values set to 0.
13124 : *
13125 : * This method is used by GetProcessingChunkSize().
13126 : *
13127 : * Pedantic note: the returned type is GUInt64, so in the highly unlikely
13128 : * theoretical case of a 32-bit platform, this might exceed its size_t
13129 : * allocation capabilities.
13130 : *
13131 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
13132 : *
13133 : * @return the block size, in number of elements along each dimension.
13134 : */
13135 110 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
13136 : {
13137 110 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13138 110 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13139 110 : auto res = hArray->m_poImpl->GetBlockSize();
13140 110 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
13141 339 : for (size_t i = 0; i < res.size(); i++)
13142 : {
13143 229 : ret[i] = res[i];
13144 : }
13145 110 : *pnCount = res.size();
13146 110 : return ret;
13147 : }
13148 :
13149 : /************************************************************************/
13150 : /* GDALMDArrayGetProcessingChunkSize() */
13151 : /************************************************************************/
13152 :
13153 : /** \brief Return an optimal chunk size for read/write operations, given the
13154 : * natural block size and memory constraints specified.
13155 : *
13156 : * This method will use GetBlockSize() to define a chunk whose dimensions are
13157 : * multiple of those returned by GetBlockSize() (unless the block define by
13158 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
13159 : * returned by this method).
13160 : *
13161 : * This is the same as the C++ method
13162 : * GDALAbstractMDArray::GetProcessingChunkSize().
13163 : *
13164 : * @param hArray Array.
13165 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13166 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
13167 : * chunk.
13168 : *
13169 : * @return the chunk size, in number of elements along each dimension.
13170 : */
13171 :
13172 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
13173 : size_t nMaxChunkMemory)
13174 : {
13175 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13176 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13177 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
13178 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
13179 3 : for (size_t i = 0; i < res.size(); i++)
13180 : {
13181 2 : ret[i] = res[i];
13182 : }
13183 1 : *pnCount = res.size();
13184 1 : return ret;
13185 : }
13186 :
13187 : /************************************************************************/
13188 : /* GDALMDArrayGetStructuralInfo() */
13189 : /************************************************************************/
13190 :
13191 : /** Return structural information on the array.
13192 : *
13193 : * This may be the compression, etc..
13194 : *
13195 : * The return value should not be freed and is valid until GDALMDArray is
13196 : * released or this function called again.
13197 : *
13198 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
13199 : */
13200 17 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13201 : {
13202 17 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13203 17 : return hArray->m_poImpl->GetStructuralInfo();
13204 : }
13205 :
13206 : /************************************************************************/
13207 : /* GDALMDArrayGetView() */
13208 : /************************************************************************/
13209 :
13210 : /** Return a view of the array using slicing or field access.
13211 : *
13212 : * The returned object should be released with GDALMDArrayRelease().
13213 : *
13214 : * This is the same as the C++ method GDALMDArray::GetView().
13215 : */
13216 438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13217 : {
13218 438 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13219 438 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13220 1314 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13221 438 : if (!sliced)
13222 23 : return nullptr;
13223 415 : return new GDALMDArrayHS(sliced);
13224 : }
13225 :
13226 : /************************************************************************/
13227 : /* GDALMDArrayTranspose() */
13228 : /************************************************************************/
13229 :
13230 : /** Return a view of the array whose axis have been reordered.
13231 : *
13232 : * The returned object should be released with GDALMDArrayRelease().
13233 : *
13234 : * This is the same as the C++ method GDALMDArray::Transpose().
13235 : */
13236 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13237 : const int *panMapNewAxisToOldAxis)
13238 : {
13239 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13240 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13241 44 : if (nNewAxisCount)
13242 : {
13243 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13244 : nNewAxisCount * sizeof(int));
13245 : }
13246 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13247 44 : if (!reordered)
13248 7 : return nullptr;
13249 37 : return new GDALMDArrayHS(reordered);
13250 : }
13251 :
13252 : /************************************************************************/
13253 : /* GDALMDArrayGetUnscaled() */
13254 : /************************************************************************/
13255 :
13256 : /** Return an array that is the unscaled version of the current one.
13257 : *
13258 : * That is each value of the unscaled array will be
13259 : * unscaled_value = raw_value * GetScale() + GetOffset()
13260 : *
13261 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13262 : * from unscaled values to raw values.
13263 : *
13264 : * The returned object should be released with GDALMDArrayRelease().
13265 : *
13266 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13267 : */
13268 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13269 : {
13270 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13271 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13272 13 : if (!unscaled)
13273 0 : return nullptr;
13274 13 : return new GDALMDArrayHS(unscaled);
13275 : }
13276 :
13277 : /************************************************************************/
13278 : /* GDALMDArrayGetMask() */
13279 : /************************************************************************/
13280 :
13281 : /** Return an array that is a mask for the current array
13282 : *
13283 : * This array will be of type Byte, with values set to 0 to indicate invalid
13284 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13285 : *
13286 : * The returned object should be released with GDALMDArrayRelease().
13287 : *
13288 : * This is the same as the C++ method GDALMDArray::GetMask().
13289 : */
13290 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13291 : {
13292 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13293 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13294 35 : if (!unscaled)
13295 7 : return nullptr;
13296 28 : return new GDALMDArrayHS(unscaled);
13297 : }
13298 :
13299 : /************************************************************************/
13300 : /* GDALMDArrayGetResampled() */
13301 : /************************************************************************/
13302 :
13303 : /** Return an array that is a resampled / reprojected view of the current array
13304 : *
13305 : * This is the same as the C++ method GDALMDArray::GetResampled().
13306 : *
13307 : * Currently this method can only resample along the last 2 dimensions, unless
13308 : * orthorectifying a NASA EMIT dataset.
13309 : *
13310 : * The returned object should be released with GDALMDArrayRelease().
13311 : *
13312 : * @since 3.4
13313 : */
13314 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13315 : const GDALDimensionH *pahNewDims,
13316 : GDALRIOResampleAlg resampleAlg,
13317 : OGRSpatialReferenceH hTargetSRS,
13318 : CSLConstList papszOptions)
13319 : {
13320 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13321 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13322 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13323 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13324 : {
13325 78 : if (pahNewDims[i])
13326 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13327 : }
13328 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13329 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13330 68 : papszOptions);
13331 34 : if (!poNewArray)
13332 8 : return nullptr;
13333 26 : return new GDALMDArrayHS(poNewArray);
13334 : }
13335 :
13336 : /************************************************************************/
13337 : /* GDALMDArraySetUnit() */
13338 : /************************************************************************/
13339 :
13340 : /** Set the variable unit.
13341 : *
13342 : * Values should conform as much as possible with those allowed by
13343 : * the NetCDF CF conventions:
13344 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13345 : * but others might be returned.
13346 : *
13347 : * Few examples are "meter", "degrees", "second", ...
13348 : * Empty value means unknown.
13349 : *
13350 : * This is the same as the C function GDALMDArraySetUnit()
13351 : *
13352 : * @param hArray array.
13353 : * @param pszUnit unit name.
13354 : * @return TRUE in case of success.
13355 : */
13356 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13357 : {
13358 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13359 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13360 : }
13361 :
13362 : /************************************************************************/
13363 : /* GDALMDArrayGetUnit() */
13364 : /************************************************************************/
13365 :
13366 : /** Return the array unit.
13367 : *
13368 : * Values should conform as much as possible with those allowed by
13369 : * the NetCDF CF conventions:
13370 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13371 : * but others might be returned.
13372 : *
13373 : * Few examples are "meter", "degrees", "second", ...
13374 : * Empty value means unknown.
13375 : *
13376 : * The return value should not be freed and is valid until GDALMDArray is
13377 : * released or this function called again.
13378 : *
13379 : * This is the same as the C++ method GDALMDArray::GetUnit().
13380 : */
13381 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13382 : {
13383 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13384 113 : return hArray->m_poImpl->GetUnit().c_str();
13385 : }
13386 :
13387 : /************************************************************************/
13388 : /* GDALMDArrayGetSpatialRef() */
13389 : /************************************************************************/
13390 :
13391 : /** Assign a spatial reference system object to the array.
13392 : *
13393 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13394 : * @return TRUE in case of success.
13395 : */
13396 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13397 : {
13398 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13399 60 : return hArray->m_poImpl->SetSpatialRef(
13400 60 : OGRSpatialReference::FromHandle(hSRS));
13401 : }
13402 :
13403 : /************************************************************************/
13404 : /* GDALMDArrayGetSpatialRef() */
13405 : /************************************************************************/
13406 :
13407 : /** Return the spatial reference system object associated with the array.
13408 : *
13409 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13410 : *
13411 : * The returned object must be freed with OSRDestroySpatialReference().
13412 : */
13413 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13414 : {
13415 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13416 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13417 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13418 : }
13419 :
13420 : /************************************************************************/
13421 : /* GDALMDArrayGetStatistics() */
13422 : /************************************************************************/
13423 :
13424 : /**
13425 : * \brief Fetch statistics.
13426 : *
13427 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13428 : *
13429 : * @since GDAL 3.2
13430 : */
13431 :
13432 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13433 : int bApproxOK, int bForce, double *pdfMin,
13434 : double *pdfMax, double *pdfMean,
13435 : double *pdfStdDev, GUInt64 *pnValidCount,
13436 : GDALProgressFunc pfnProgress,
13437 : void *pProgressData)
13438 : {
13439 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13440 30 : return hArray->m_poImpl->GetStatistics(
13441 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13442 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13443 : }
13444 :
13445 : /************************************************************************/
13446 : /* GDALMDArrayComputeStatistics() */
13447 : /************************************************************************/
13448 :
13449 : /**
13450 : * \brief Compute statistics.
13451 : *
13452 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13453 : *
13454 : * @since GDAL 3.2
13455 : * @see GDALMDArrayComputeStatisticsEx()
13456 : */
13457 :
13458 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13459 : int bApproxOK, double *pdfMin, double *pdfMax,
13460 : double *pdfMean, double *pdfStdDev,
13461 : GUInt64 *pnValidCount,
13462 : GDALProgressFunc pfnProgress,
13463 : void *pProgressData)
13464 : {
13465 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13466 0 : return hArray->m_poImpl->ComputeStatistics(
13467 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13468 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13469 : }
13470 :
13471 : /************************************************************************/
13472 : /* GDALMDArrayComputeStatisticsEx() */
13473 : /************************************************************************/
13474 :
13475 : /**
13476 : * \brief Compute statistics.
13477 : *
13478 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13479 : *
13480 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13481 : *
13482 : * @since GDAL 3.8
13483 : */
13484 :
13485 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13486 : int bApproxOK, double *pdfMin,
13487 : double *pdfMax, double *pdfMean,
13488 : double *pdfStdDev, GUInt64 *pnValidCount,
13489 : GDALProgressFunc pfnProgress,
13490 : void *pProgressData,
13491 : CSLConstList papszOptions)
13492 : {
13493 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13494 8 : return hArray->m_poImpl->ComputeStatistics(
13495 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13496 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13497 : }
13498 :
13499 : /************************************************************************/
13500 : /* GDALMDArrayGetCoordinateVariables() */
13501 : /************************************************************************/
13502 :
13503 : /** Return coordinate variables.
13504 : *
13505 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13506 : * itself needs to be freed, CPLFree() should be called (and
13507 : * GDALMDArrayRelease() on individual array members).
13508 : *
13509 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13510 : *
13511 : * @param hArray Array.
13512 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13513 : *
13514 : * @return an array of *pnCount arrays.
13515 : * @since 3.4
13516 : */
13517 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13518 : size_t *pnCount)
13519 : {
13520 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13521 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13522 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13523 : auto ret = static_cast<GDALMDArrayH *>(
13524 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13525 29 : for (size_t i = 0; i < coordinates.size(); i++)
13526 : {
13527 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13528 : }
13529 13 : *pnCount = coordinates.size();
13530 13 : return ret;
13531 : }
13532 :
13533 : /************************************************************************/
13534 : /* GDALMDArrayGetGridded() */
13535 : /************************************************************************/
13536 :
13537 : /** Return a gridded array from scattered point data, that is from an array
13538 : * whose last dimension is the indexing variable of X and Y arrays.
13539 : *
13540 : * The returned object should be released with GDALMDArrayRelease().
13541 : *
13542 : * This is the same as the C++ method GDALMDArray::GetGridded().
13543 : *
13544 : * @since GDAL 3.7
13545 : */
13546 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13547 : const char *pszGridOptions,
13548 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13549 : CSLConstList papszOptions)
13550 : {
13551 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13552 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13553 22 : auto gridded = hArray->m_poImpl->GetGridded(
13554 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13555 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13556 22 : if (!gridded)
13557 19 : return nullptr;
13558 3 : return new GDALMDArrayHS(gridded);
13559 : }
13560 :
13561 : /************************************************************************/
13562 : /* GDALMDArrayGetMeshGrid() */
13563 : /************************************************************************/
13564 :
13565 : /** Return a list of multidimensional arrays from a list of one-dimensional
13566 : * arrays.
13567 : *
13568 : * This is typically used to transform one-dimensional longitude, latitude
13569 : * arrays into 2D ones.
13570 : *
13571 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13572 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13573 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13574 : * repeated to fill the matrix along the first dimension for x1, the second
13575 : * for x2 and so on.
13576 : *
13577 : * For example, if x = [1, 2], and y = [3, 4, 5],
13578 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13579 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13580 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13581 : *
13582 : * and
13583 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13584 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13585 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13586 : *
13587 : * The currently supported options are:
13588 : * <ul>
13589 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13590 : * output.
13591 : * </li>
13592 : * </ul>
13593 : *
13594 : * This is the same as
13595 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13596 : * function.
13597 : *
13598 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13599 : * If only the array itself needs to be freed, CPLFree() should be called
13600 : * (and GDALMDArrayRelease() on individual array members).
13601 : *
13602 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13603 : *
13604 : * @param pahInputArrays Input arrays
13605 : * @param nCountInputArrays Number of input arrays
13606 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13607 : * @param papszOptions NULL, or NULL terminated list of options.
13608 : *
13609 : * @return an array of *pnCountOutputArrays arrays.
13610 : * @since 3.10
13611 : */
13612 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13613 : size_t nCountInputArrays,
13614 : size_t *pnCountOutputArrays,
13615 : CSLConstList papszOptions)
13616 : {
13617 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13618 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13619 :
13620 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13621 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13622 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13623 :
13624 : const auto apoOutputArrays =
13625 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13626 : auto ret = static_cast<GDALMDArrayH *>(
13627 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13628 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13629 : {
13630 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13631 : }
13632 7 : *pnCountOutputArrays = apoOutputArrays.size();
13633 7 : return ret;
13634 : }
13635 :
13636 : /************************************************************************/
13637 : /* GDALReleaseArrays() */
13638 : /************************************************************************/
13639 :
13640 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13641 : *
13642 : * @param arrays return pointer of above methods
13643 : * @param nCount *pnCount value returned by above methods
13644 : */
13645 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13646 : {
13647 46 : for (size_t i = 0; i < nCount; i++)
13648 : {
13649 26 : delete arrays[i];
13650 : }
13651 20 : CPLFree(arrays);
13652 20 : }
13653 :
13654 : /************************************************************************/
13655 : /* GDALMDArrayCache() */
13656 : /************************************************************************/
13657 :
13658 : /**
13659 : * \brief Cache the content of the array into an auxiliary filename.
13660 : *
13661 : * This is the same as the C++ method GDALMDArray::Cache().
13662 : *
13663 : * @since GDAL 3.4
13664 : */
13665 :
13666 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13667 : {
13668 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13669 7 : return hArray->m_poImpl->Cache(papszOptions);
13670 : }
13671 :
13672 : /************************************************************************/
13673 : /* GDALMDArrayRename() */
13674 : /************************************************************************/
13675 :
13676 : /** Rename the array.
13677 : *
13678 : * This is not implemented by all drivers.
13679 : *
13680 : * Drivers known to implement it: MEM, netCDF, Zarr.
13681 : *
13682 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13683 : *
13684 : * @return true in case of success
13685 : * @since GDAL 3.8
13686 : */
13687 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13688 : {
13689 28 : VALIDATE_POINTER1(hArray, __func__, false);
13690 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13691 28 : return hArray->m_poImpl->Rename(pszNewName);
13692 : }
13693 :
13694 : /************************************************************************/
13695 : /* GDALAttributeRelease() */
13696 : /************************************************************************/
13697 :
13698 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13699 : *
13700 : * Note: when applied on a object coming from a driver, this does not
13701 : * destroy the object in the file, database, etc...
13702 : */
13703 818 : void GDALAttributeRelease(GDALAttributeH hAttr)
13704 : {
13705 818 : delete hAttr;
13706 818 : }
13707 :
13708 : /************************************************************************/
13709 : /* GDALAttributeGetName() */
13710 : /************************************************************************/
13711 :
13712 : /** Return the name of the attribute.
13713 : *
13714 : * The returned pointer is valid until hAttr is released.
13715 : *
13716 : * This is the same as the C++ method GDALAttribute::GetName().
13717 : */
13718 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13719 : {
13720 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13721 361 : return hAttr->m_poImpl->GetName().c_str();
13722 : }
13723 :
13724 : /************************************************************************/
13725 : /* GDALAttributeGetFullName() */
13726 : /************************************************************************/
13727 :
13728 : /** Return the full name of the attribute.
13729 : *
13730 : * The returned pointer is valid until hAttr is released.
13731 : *
13732 : * This is the same as the C++ method GDALAttribute::GetFullName().
13733 : */
13734 50 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13735 : {
13736 50 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13737 50 : return hAttr->m_poImpl->GetFullName().c_str();
13738 : }
13739 :
13740 : /************************************************************************/
13741 : /* GDALAttributeGetTotalElementsCount() */
13742 : /************************************************************************/
13743 :
13744 : /** Return the total number of values in the attribute.
13745 : *
13746 : * This is the same as the C++ method
13747 : * GDALAbstractMDArray::GetTotalElementsCount()
13748 : */
13749 190 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13750 : {
13751 190 : VALIDATE_POINTER1(hAttr, __func__, 0);
13752 190 : return hAttr->m_poImpl->GetTotalElementsCount();
13753 : }
13754 :
13755 : /************************************************************************/
13756 : /* GDALAttributeGetDimensionCount() */
13757 : /************************************************************************/
13758 :
13759 : /** Return the number of dimensions.
13760 : *
13761 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13762 : */
13763 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13764 : {
13765 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13766 12 : return hAttr->m_poImpl->GetDimensionCount();
13767 : }
13768 :
13769 : /************************************************************************/
13770 : /* GDALAttributeGetDimensionsSize() */
13771 : /************************************************************************/
13772 :
13773 : /** Return the dimension sizes of the attribute.
13774 : *
13775 : * The returned array must be freed with CPLFree()
13776 : *
13777 : * @param hAttr Attribute.
13778 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13779 : *
13780 : * @return an array of *pnCount values.
13781 : */
13782 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13783 : {
13784 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13785 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13786 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13787 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13788 22 : for (size_t i = 0; i < dims.size(); i++)
13789 : {
13790 11 : ret[i] = dims[i]->GetSize();
13791 : }
13792 11 : *pnCount = dims.size();
13793 11 : return ret;
13794 : }
13795 :
13796 : /************************************************************************/
13797 : /* GDALAttributeGetDataType() */
13798 : /************************************************************************/
13799 :
13800 : /** Return the data type
13801 : *
13802 : * The return must be freed with GDALExtendedDataTypeRelease().
13803 : */
13804 483 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13805 : {
13806 483 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13807 : return new GDALExtendedDataTypeHS(
13808 483 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13809 : }
13810 :
13811 : /************************************************************************/
13812 : /* GDALAttributeReadAsRaw() */
13813 : /************************************************************************/
13814 :
13815 : /** Return the raw value of an attribute.
13816 : *
13817 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13818 : *
13819 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13820 : *
13821 : * @param hAttr Attribute.
13822 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13823 : *
13824 : * @return a buffer of *pnSize bytes.
13825 : */
13826 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13827 : {
13828 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13829 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13830 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13831 6 : *pnSize = res.size();
13832 6 : auto ret = res.StealData();
13833 6 : if (!ret)
13834 : {
13835 0 : *pnSize = 0;
13836 0 : return nullptr;
13837 : }
13838 6 : return ret;
13839 : }
13840 :
13841 : /************************************************************************/
13842 : /* GDALAttributeFreeRawResult() */
13843 : /************************************************************************/
13844 :
13845 : /** Free the return of GDALAttributeAsRaw()
13846 : */
13847 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13848 : CPL_UNUSED size_t nSize)
13849 : {
13850 6 : VALIDATE_POINTER0(hAttr, __func__);
13851 6 : if (raw)
13852 : {
13853 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13854 6 : const auto nDTSize(dt.GetSize());
13855 6 : GByte *pabyPtr = raw;
13856 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13857 6 : CPLAssert(nSize == nDTSize * nEltCount);
13858 12 : for (size_t i = 0; i < nEltCount; ++i)
13859 : {
13860 6 : dt.FreeDynamicMemory(pabyPtr);
13861 6 : pabyPtr += nDTSize;
13862 : }
13863 6 : CPLFree(raw);
13864 : }
13865 : }
13866 :
13867 : /************************************************************************/
13868 : /* GDALAttributeReadAsString() */
13869 : /************************************************************************/
13870 :
13871 : /** Return the value of an attribute as a string.
13872 : *
13873 : * The returned string should not be freed, and its lifetime does not
13874 : * excess a next call to ReadAsString() on the same object, or the deletion
13875 : * of the object itself.
13876 : *
13877 : * This function will only return the first element if there are several.
13878 : *
13879 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13880 : *
13881 : * @return a string, or nullptr.
13882 : */
13883 115 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13884 : {
13885 115 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13886 115 : return hAttr->m_poImpl->ReadAsString();
13887 : }
13888 :
13889 : /************************************************************************/
13890 : /* GDALAttributeReadAsInt() */
13891 : /************************************************************************/
13892 :
13893 : /** Return the value of an attribute as a integer.
13894 : *
13895 : * This function will only return the first element if there are several.
13896 : *
13897 : * It can fail if its value can not be converted to integer.
13898 : *
13899 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13900 : *
13901 : * @return a integer, or INT_MIN in case of error.
13902 : */
13903 25 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13904 : {
13905 25 : VALIDATE_POINTER1(hAttr, __func__, 0);
13906 25 : return hAttr->m_poImpl->ReadAsInt();
13907 : }
13908 :
13909 : /************************************************************************/
13910 : /* GDALAttributeReadAsInt64() */
13911 : /************************************************************************/
13912 :
13913 : /** Return the value of an attribute as a int64_t.
13914 : *
13915 : * This function will only return the first element if there are several.
13916 : *
13917 : * It can fail if its value can not be converted to integer.
13918 : *
13919 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13920 : *
13921 : * @return an int64_t, or INT64_MIN in case of error.
13922 : */
13923 27 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13924 : {
13925 27 : VALIDATE_POINTER1(hAttr, __func__, 0);
13926 27 : return hAttr->m_poImpl->ReadAsInt64();
13927 : }
13928 :
13929 : /************************************************************************/
13930 : /* GDALAttributeReadAsDouble() */
13931 : /************************************************************************/
13932 :
13933 : /** Return the value of an attribute as a double.
13934 : *
13935 : * This function will only return the first element if there are several.
13936 : *
13937 : * It can fail if its value can not be converted to double.
13938 : *
13939 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13940 : *
13941 : * @return a double value.
13942 : */
13943 55 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13944 : {
13945 55 : VALIDATE_POINTER1(hAttr, __func__, 0);
13946 55 : return hAttr->m_poImpl->ReadAsDouble();
13947 : }
13948 :
13949 : /************************************************************************/
13950 : /* GDALAttributeReadAsStringArray() */
13951 : /************************************************************************/
13952 :
13953 : /** Return the value of an attribute as an array of strings.
13954 : *
13955 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13956 : *
13957 : * The return value must be freed with CSLDestroy().
13958 : */
13959 22 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13960 : {
13961 22 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13962 22 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13963 : }
13964 :
13965 : /************************************************************************/
13966 : /* GDALAttributeReadAsIntArray() */
13967 : /************************************************************************/
13968 :
13969 : /** Return the value of an attribute as an array of integers.
13970 : *
13971 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13972 : *
13973 : * @param hAttr Attribute
13974 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13975 : * @return array to be freed with CPLFree(), or nullptr.
13976 : */
13977 18 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13978 : {
13979 18 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13980 18 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13981 18 : *pnCount = 0;
13982 36 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13983 18 : if (tmp.empty())
13984 0 : return nullptr;
13985 18 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13986 18 : if (!ret)
13987 0 : return nullptr;
13988 18 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13989 18 : *pnCount = tmp.size();
13990 18 : return ret;
13991 : }
13992 :
13993 : /************************************************************************/
13994 : /* GDALAttributeReadAsInt64Array() */
13995 : /************************************************************************/
13996 :
13997 : /** Return the value of an attribute as an array of int64_t.
13998 : *
13999 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
14000 : *
14001 : * @param hAttr Attribute
14002 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
14003 : * @return array to be freed with CPLFree(), or nullptr.
14004 : */
14005 26 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
14006 : {
14007 26 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14008 26 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
14009 26 : *pnCount = 0;
14010 52 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
14011 26 : if (tmp.empty())
14012 0 : return nullptr;
14013 : auto ret = static_cast<int64_t *>(
14014 26 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
14015 26 : if (!ret)
14016 0 : return nullptr;
14017 26 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
14018 26 : *pnCount = tmp.size();
14019 26 : return ret;
14020 : }
14021 :
14022 : /************************************************************************/
14023 : /* GDALAttributeReadAsDoubleArray() */
14024 : /************************************************************************/
14025 :
14026 : /** Return the value of an attribute as an array of doubles.
14027 : *
14028 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
14029 : *
14030 : * @param hAttr Attribute
14031 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
14032 : * @return array to be freed with CPLFree(), or nullptr.
14033 : */
14034 44 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
14035 : {
14036 44 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
14037 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
14038 44 : *pnCount = 0;
14039 88 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
14040 44 : if (tmp.empty())
14041 0 : return nullptr;
14042 : auto ret =
14043 44 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
14044 44 : if (!ret)
14045 0 : return nullptr;
14046 44 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
14047 44 : *pnCount = tmp.size();
14048 44 : return ret;
14049 : }
14050 :
14051 : /************************************************************************/
14052 : /* GDALAttributeWriteRaw() */
14053 : /************************************************************************/
14054 :
14055 : /** Write an attribute from raw values expressed in GetDataType()
14056 : *
14057 : * The values should be provided in the type of GetDataType() and there should
14058 : * be exactly GetTotalElementsCount() of them.
14059 : * If GetDataType() is a string, each value should be a char* pointer.
14060 : *
14061 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
14062 : *
14063 : * @param hAttr Attribute
14064 : * @param pabyValue Buffer of nLen bytes.
14065 : * @param nLength Size of pabyValue in bytes. Should be equal to
14066 : * GetTotalElementsCount() * GetDataType().GetSize()
14067 : * @return TRUE in case of success.
14068 : */
14069 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
14070 : size_t nLength)
14071 : {
14072 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14073 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
14074 : }
14075 :
14076 : /************************************************************************/
14077 : /* GDALAttributeWriteString() */
14078 : /************************************************************************/
14079 :
14080 : /** Write an attribute from a string value.
14081 : *
14082 : * Type conversion will be performed if needed. If the attribute contains
14083 : * multiple values, only the first one will be updated.
14084 : *
14085 : * This is the same as the C++ method GDALAttribute::Write(const char*)
14086 : *
14087 : * @param hAttr Attribute
14088 : * @param pszVal Pointer to a string.
14089 : * @return TRUE in case of success.
14090 : */
14091 210 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
14092 : {
14093 210 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14094 210 : return hAttr->m_poImpl->Write(pszVal);
14095 : }
14096 :
14097 : /************************************************************************/
14098 : /* GDALAttributeWriteInt() */
14099 : /************************************************************************/
14100 :
14101 : /** Write an attribute from a integer value.
14102 : *
14103 : * Type conversion will be performed if needed. If the attribute contains
14104 : * multiple values, only the first one will be updated.
14105 : *
14106 : * This is the same as the C++ method GDALAttribute::WriteInt()
14107 : *
14108 : * @param hAttr Attribute
14109 : * @param nVal Value.
14110 : * @return TRUE in case of success.
14111 : */
14112 23 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
14113 : {
14114 23 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14115 23 : return hAttr->m_poImpl->WriteInt(nVal);
14116 : }
14117 :
14118 : /************************************************************************/
14119 : /* GDALAttributeWriteInt64() */
14120 : /************************************************************************/
14121 :
14122 : /** Write an attribute from an int64_t value.
14123 : *
14124 : * Type conversion will be performed if needed. If the attribute contains
14125 : * multiple values, only the first one will be updated.
14126 : *
14127 : * This is the same as the C++ method GDALAttribute::WriteLong()
14128 : *
14129 : * @param hAttr Attribute
14130 : * @param nVal Value.
14131 : * @return TRUE in case of success.
14132 : */
14133 14 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
14134 : {
14135 14 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14136 14 : return hAttr->m_poImpl->WriteInt64(nVal);
14137 : }
14138 :
14139 : /************************************************************************/
14140 : /* GDALAttributeWriteDouble() */
14141 : /************************************************************************/
14142 :
14143 : /** Write an attribute from a double value.
14144 : *
14145 : * Type conversion will be performed if needed. If the attribute contains
14146 : * multiple values, only the first one will be updated.
14147 : *
14148 : * This is the same as the C++ method GDALAttribute::Write(double);
14149 : *
14150 : * @param hAttr Attribute
14151 : * @param dfVal Value.
14152 : *
14153 : * @return TRUE in case of success.
14154 : */
14155 12 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
14156 : {
14157 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14158 12 : return hAttr->m_poImpl->Write(dfVal);
14159 : }
14160 :
14161 : /************************************************************************/
14162 : /* GDALAttributeWriteStringArray() */
14163 : /************************************************************************/
14164 :
14165 : /** Write an attribute from an array of strings.
14166 : *
14167 : * Type conversion will be performed if needed.
14168 : *
14169 : * Exactly GetTotalElementsCount() strings must be provided
14170 : *
14171 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
14172 : *
14173 : * @param hAttr Attribute
14174 : * @param papszValues Array of strings.
14175 : * @return TRUE in case of success.
14176 : */
14177 9 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
14178 : CSLConstList papszValues)
14179 : {
14180 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14181 9 : return hAttr->m_poImpl->Write(papszValues);
14182 : }
14183 :
14184 : /************************************************************************/
14185 : /* GDALAttributeWriteIntArray() */
14186 : /************************************************************************/
14187 :
14188 : /** Write an attribute from an array of int.
14189 : *
14190 : * Type conversion will be performed if needed.
14191 : *
14192 : * Exactly GetTotalElementsCount() strings must be provided
14193 : *
14194 : * This is the same as the C++ method GDALAttribute::Write(const int *,
14195 : * size_t)
14196 : *
14197 : * @param hAttr Attribute
14198 : * @param panValues Array of int.
14199 : * @param nCount Should be equal to GetTotalElementsCount().
14200 : * @return TRUE in case of success.
14201 : */
14202 12 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14203 : size_t nCount)
14204 : {
14205 12 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14206 12 : return hAttr->m_poImpl->Write(panValues, nCount);
14207 : }
14208 :
14209 : /************************************************************************/
14210 : /* GDALAttributeWriteInt64Array() */
14211 : /************************************************************************/
14212 :
14213 : /** Write an attribute from an array of int64_t.
14214 : *
14215 : * Type conversion will be performed if needed.
14216 : *
14217 : * Exactly GetTotalElementsCount() strings must be provided
14218 : *
14219 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14220 : * size_t)
14221 : *
14222 : * @param hAttr Attribute
14223 : * @param panValues Array of int64_t.
14224 : * @param nCount Should be equal to GetTotalElementsCount().
14225 : * @return TRUE in case of success.
14226 : */
14227 13 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14228 : size_t nCount)
14229 : {
14230 13 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14231 13 : return hAttr->m_poImpl->Write(panValues, nCount);
14232 : }
14233 :
14234 : /************************************************************************/
14235 : /* GDALAttributeWriteDoubleArray() */
14236 : /************************************************************************/
14237 :
14238 : /** Write an attribute from an array of double.
14239 : *
14240 : * Type conversion will be performed if needed.
14241 : *
14242 : * Exactly GetTotalElementsCount() strings must be provided
14243 : *
14244 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14245 : * size_t)
14246 : *
14247 : * @param hAttr Attribute
14248 : * @param padfValues Array of double.
14249 : * @param nCount Should be equal to GetTotalElementsCount().
14250 : * @return TRUE in case of success.
14251 : */
14252 8 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14253 : const double *padfValues, size_t nCount)
14254 : {
14255 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14256 8 : return hAttr->m_poImpl->Write(padfValues, nCount);
14257 : }
14258 :
14259 : /************************************************************************/
14260 : /* GDALAttributeRename() */
14261 : /************************************************************************/
14262 :
14263 : /** Rename the attribute.
14264 : *
14265 : * This is not implemented by all drivers.
14266 : *
14267 : * Drivers known to implement it: MEM, netCDF.
14268 : *
14269 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14270 : *
14271 : * @return true in case of success
14272 : * @since GDAL 3.8
14273 : */
14274 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14275 : {
14276 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14277 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14278 27 : return hAttr->m_poImpl->Rename(pszNewName);
14279 : }
14280 :
14281 : /************************************************************************/
14282 : /* GDALDimensionRelease() */
14283 : /************************************************************************/
14284 :
14285 : /** Release the GDAL in-memory object associated with a GDALDimension.
14286 : *
14287 : * Note: when applied on a object coming from a driver, this does not
14288 : * destroy the object in the file, database, etc...
14289 : */
14290 6674 : void GDALDimensionRelease(GDALDimensionH hDim)
14291 : {
14292 6674 : delete hDim;
14293 6674 : }
14294 :
14295 : /************************************************************************/
14296 : /* GDALDimensionGetName() */
14297 : /************************************************************************/
14298 :
14299 : /** Return dimension name.
14300 : *
14301 : * This is the same as the C++ method GDALDimension::GetName()
14302 : */
14303 296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14304 : {
14305 296 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14306 296 : return hDim->m_poImpl->GetName().c_str();
14307 : }
14308 :
14309 : /************************************************************************/
14310 : /* GDALDimensionGetFullName() */
14311 : /************************************************************************/
14312 :
14313 : /** Return dimension full name.
14314 : *
14315 : * This is the same as the C++ method GDALDimension::GetFullName()
14316 : */
14317 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14318 : {
14319 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14320 82 : return hDim->m_poImpl->GetFullName().c_str();
14321 : }
14322 :
14323 : /************************************************************************/
14324 : /* GDALDimensionGetType() */
14325 : /************************************************************************/
14326 :
14327 : /** Return dimension type.
14328 : *
14329 : * This is the same as the C++ method GDALDimension::GetType()
14330 : */
14331 70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14332 : {
14333 70 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14334 70 : return hDim->m_poImpl->GetType().c_str();
14335 : }
14336 :
14337 : /************************************************************************/
14338 : /* GDALDimensionGetDirection() */
14339 : /************************************************************************/
14340 :
14341 : /** Return dimension direction.
14342 : *
14343 : * This is the same as the C++ method GDALDimension::GetDirection()
14344 : */
14345 38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14346 : {
14347 38 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14348 38 : return hDim->m_poImpl->GetDirection().c_str();
14349 : }
14350 :
14351 : /************************************************************************/
14352 : /* GDALDimensionGetSize() */
14353 : /************************************************************************/
14354 :
14355 : /** Return the size, that is the number of values along the dimension.
14356 : *
14357 : * This is the same as the C++ method GDALDimension::GetSize()
14358 : */
14359 5283 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14360 : {
14361 5283 : VALIDATE_POINTER1(hDim, __func__, 0);
14362 5283 : return hDim->m_poImpl->GetSize();
14363 : }
14364 :
14365 : /************************************************************************/
14366 : /* GDALDimensionGetIndexingVariable() */
14367 : /************************************************************************/
14368 :
14369 : /** Return the variable that is used to index the dimension (if there is one).
14370 : *
14371 : * This is the array, typically one-dimensional, describing the values taken
14372 : * by the dimension.
14373 : *
14374 : * The returned value should be freed with GDALMDArrayRelease().
14375 : *
14376 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14377 : */
14378 140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14379 : {
14380 140 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14381 280 : auto var(hDim->m_poImpl->GetIndexingVariable());
14382 140 : if (!var)
14383 11 : return nullptr;
14384 129 : return new GDALMDArrayHS(var);
14385 : }
14386 :
14387 : /************************************************************************/
14388 : /* GDALDimensionSetIndexingVariable() */
14389 : /************************************************************************/
14390 :
14391 : /** Set the variable that is used to index the dimension.
14392 : *
14393 : * This is the array, typically one-dimensional, describing the values taken
14394 : * by the dimension.
14395 : *
14396 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14397 : *
14398 : * @return TRUE in case of success.
14399 : */
14400 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14401 : {
14402 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14403 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14404 46 : : nullptr);
14405 : }
14406 :
14407 : /************************************************************************/
14408 : /* GDALDimensionRename() */
14409 : /************************************************************************/
14410 :
14411 : /** Rename the dimension.
14412 : *
14413 : * This is not implemented by all drivers.
14414 : *
14415 : * Drivers known to implement it: MEM, netCDF.
14416 : *
14417 : * This is the same as the C++ method GDALDimension::Rename()
14418 : *
14419 : * @return true in case of success
14420 : * @since GDAL 3.8
14421 : */
14422 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14423 : {
14424 31 : VALIDATE_POINTER1(hDim, __func__, false);
14425 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14426 31 : return hDim->m_poImpl->Rename(pszNewName);
14427 : }
14428 :
14429 : /************************************************************************/
14430 : /* GDALDatasetGetRootGroup() */
14431 : /************************************************************************/
14432 :
14433 : /** Return the root GDALGroup of this dataset.
14434 : *
14435 : * Only valid for multidimensional datasets.
14436 : *
14437 : * The returned value must be freed with GDALGroupRelease().
14438 : *
14439 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14440 : *
14441 : * @since GDAL 3.1
14442 : */
14443 1971 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14444 : {
14445 1971 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14446 1971 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14447 1971 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14448 : }
14449 :
14450 : /************************************************************************/
14451 : /* GDALRasterBandAsMDArray() */
14452 : /************************************************************************/
14453 :
14454 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14455 : *
14456 : * The band must be linked to a GDALDataset. If this dataset is not already
14457 : * marked as shared, it will be, so that the returned array holds a reference
14458 : * to it.
14459 : *
14460 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14461 : * returned array will have an associated indexing variable.
14462 : *
14463 : * The returned pointer must be released with GDALMDArrayRelease().
14464 : *
14465 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14466 : *
14467 : * @return a new array, or NULL.
14468 : *
14469 : * @since GDAL 3.1
14470 : */
14471 25 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14472 : {
14473 25 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14474 50 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14475 25 : if (!poArray)
14476 0 : return nullptr;
14477 25 : return new GDALMDArrayHS(poArray);
14478 : }
14479 :
14480 : /************************************************************************/
14481 : /* GDALDatasetAsMDArray() */
14482 : /************************************************************************/
14483 :
14484 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14485 : *
14486 : * If this dataset is not already marked as shared, it will be, so that the
14487 : * returned array holds a reference to it.
14488 : *
14489 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14490 : * returned array will have an associated indexing variable.
14491 : *
14492 : * The currently supported list of options is:
14493 : * <ul>
14494 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14495 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14496 : * and the last (fastest changing direction) is X
14497 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14498 : * and the last (fastest changing direction) is Band.
14499 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14500 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14501 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14502 : * "Y,X,Band" is use.
14503 : * </li>
14504 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14505 : * item from which to build the band indexing variable.
14506 : * <ul>
14507 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14508 : * <li>"{None}" means that no band indexing variable must be created.</li>
14509 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14510 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14511 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14512 : * </ul>
14513 : * </li>
14514 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14515 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14516 : * Defaults to String.
14517 : * </li>
14518 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14519 : * Defaults to "Band".
14520 : * </li>
14521 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14522 : * </li>
14523 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14524 : * </li>
14525 : * </ul>
14526 : *
14527 : * The returned pointer must be released with GDALMDArrayRelease().
14528 : *
14529 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14530 : * GDALDataset::AsMDArray()
14531 : *
14532 : * This is the same as the C++ method GDALDataset::AsMDArray().
14533 : *
14534 : * @param hDS Dataset handle.
14535 : * @param papszOptions Null-terminated list of strings, or nullptr.
14536 : * @return a new array, or NULL.
14537 : *
14538 : * @since GDAL 3.12
14539 : */
14540 15 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14541 : {
14542 15 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14543 30 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14544 15 : if (!poArray)
14545 3 : return nullptr;
14546 12 : return new GDALMDArrayHS(poArray);
14547 : }
14548 :
14549 : /************************************************************************/
14550 : /* GDALMDArrayAsClassicDataset() */
14551 : /************************************************************************/
14552 :
14553 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14554 : *
14555 : * Only 2D or more arrays are supported.
14556 : *
14557 : * In the case of > 2D arrays, additional dimensions will be represented as
14558 : * raster bands.
14559 : *
14560 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14561 : * GDALDataset::AsMDArray()
14562 : *
14563 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14564 : *
14565 : * @param hArray Array.
14566 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14567 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14568 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14569 : */
14570 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14571 : size_t iYDim)
14572 : {
14573 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14574 0 : return GDALDataset::ToHandle(
14575 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14576 : }
14577 :
14578 : /************************************************************************/
14579 : /* GDALMDArrayAsClassicDatasetEx() */
14580 : /************************************************************************/
14581 :
14582 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14583 : *
14584 : * Only 2D or more arrays are supported.
14585 : *
14586 : * In the case of > 2D arrays, additional dimensions will be represented as
14587 : * raster bands.
14588 : *
14589 : * The "reverse" method is GDALRasterBand::AsMDArray().
14590 : *
14591 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14592 : * @param hArray Array.
14593 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14594 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14595 : * Ignored if the dimension count is 1.
14596 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14597 : * BAND_IMAGERY_METADATA option.
14598 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14599 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14600 : * @since GDAL 3.8
14601 : */
14602 109 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14603 : size_t iYDim, GDALGroupH hRootGroup,
14604 : CSLConstList papszOptions)
14605 : {
14606 109 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14607 218 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14608 218 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14609 218 : papszOptions));
14610 : }
14611 :
14612 : //! @cond Doxygen_Suppress
14613 :
14614 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14615 : const std::string &osName,
14616 : const std::string &osValue,
14617 180 : GDALExtendedDataTypeSubType eSubType)
14618 : : GDALAbstractMDArray(osParentName, osName),
14619 : GDALAttribute(osParentName, osName),
14620 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14621 : {
14622 180 : }
14623 :
14624 : const std::vector<std::shared_ptr<GDALDimension>> &
14625 30 : GDALAttributeString::GetDimensions() const
14626 : {
14627 30 : return m_dims;
14628 : }
14629 :
14630 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14631 : {
14632 21 : return m_dt;
14633 : }
14634 :
14635 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14636 : const GPtrDiff_t *,
14637 : const GDALExtendedDataType &bufferDataType,
14638 : void *pDstBuffer) const
14639 : {
14640 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14641 0 : return false;
14642 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14643 10 : if (!pszStr)
14644 0 : return false;
14645 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14646 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14647 10 : return true;
14648 : }
14649 :
14650 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14651 : const std::string &osName,
14652 66 : double dfValue)
14653 : : GDALAbstractMDArray(osParentName, osName),
14654 : GDALAttribute(osParentName, osName),
14655 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14656 : {
14657 66 : }
14658 :
14659 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14660 : const std::string &osName,
14661 27 : int nValue)
14662 : : GDALAbstractMDArray(osParentName, osName),
14663 : GDALAttribute(osParentName, osName),
14664 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14665 : {
14666 27 : }
14667 :
14668 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14669 : const std::string &osName,
14670 7 : const std::vector<GUInt32> &anValues)
14671 : : GDALAbstractMDArray(osParentName, osName),
14672 : GDALAttribute(osParentName, osName),
14673 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14674 : {
14675 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14676 14 : std::string(), "dim0", std::string(), std::string(),
14677 7 : m_anValuesUInt32.size()));
14678 7 : }
14679 :
14680 : const std::vector<std::shared_ptr<GDALDimension>> &
14681 14 : GDALAttributeNumeric::GetDimensions() const
14682 : {
14683 14 : return m_dims;
14684 : }
14685 :
14686 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14687 : {
14688 8 : return m_dt;
14689 : }
14690 :
14691 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14692 : const size_t *count, const GInt64 *arrayStep,
14693 : const GPtrDiff_t *bufferStride,
14694 : const GDALExtendedDataType &bufferDataType,
14695 : void *pDstBuffer) const
14696 : {
14697 4 : if (m_dims.empty())
14698 : {
14699 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14700 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14701 : bufferDataType);
14702 : else
14703 : {
14704 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14705 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14706 : bufferDataType);
14707 : }
14708 : }
14709 : else
14710 : {
14711 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14712 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14713 30 : for (size_t i = 0; i < count[0]; ++i)
14714 : {
14715 29 : GDALExtendedDataType::CopyValue(
14716 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14717 29 : i * arrayStep[0])],
14718 29 : m_dt, pabyDstBuffer, bufferDataType);
14719 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14720 : }
14721 : }
14722 4 : return true;
14723 : }
14724 :
14725 380 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14726 : const std::string &osParentName, const std::string &osName,
14727 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14728 380 : double dfIncrement, double dfOffsetInIncrement)
14729 : : GDALAbstractMDArray(osParentName, osName),
14730 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14731 : m_dfIncrement(dfIncrement), m_dfOffsetInIncrement(dfOffsetInIncrement),
14732 760 : m_dims{poDim}
14733 : {
14734 380 : }
14735 :
14736 380 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14737 : const std::string &osParentName, const std::string &osName,
14738 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14739 : double dfIncrement, double dfOffsetInIncrement)
14740 : {
14741 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14742 380 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14743 380 : poArray->SetSelf(poArray);
14744 380 : return poArray;
14745 : }
14746 :
14747 : const std::vector<std::shared_ptr<GDALDimension>> &
14748 1876 : GDALMDArrayRegularlySpaced::GetDimensions() const
14749 : {
14750 1876 : return m_dims;
14751 : }
14752 :
14753 679 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14754 : {
14755 679 : return m_dt;
14756 : }
14757 :
14758 : std::vector<std::shared_ptr<GDALAttribute>>
14759 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14760 : {
14761 4 : return m_attributes;
14762 : }
14763 :
14764 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14765 : const std::shared_ptr<GDALAttribute> &poAttr)
14766 : {
14767 0 : m_attributes.emplace_back(poAttr);
14768 0 : }
14769 :
14770 248 : bool GDALMDArrayRegularlySpaced::IRead(
14771 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14772 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14773 : void *pDstBuffer) const
14774 : {
14775 248 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14776 4690 : for (size_t i = 0; i < count[0]; i++)
14777 : {
14778 4442 : const double dfVal =
14779 4442 : m_dfStart +
14780 4442 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14781 4442 : m_dfOffsetInIncrement) *
14782 4442 : m_dfIncrement;
14783 4442 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14784 : bufferDataType);
14785 4442 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14786 : }
14787 248 : return true;
14788 : }
14789 :
14790 268 : bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
14791 : double &dfIncrement) const
14792 : {
14793 268 : dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
14794 268 : dfIncrement = m_dfIncrement;
14795 268 : return true;
14796 : }
14797 :
14798 6426 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14799 : const std::string &osParentName, const std::string &osName,
14800 6426 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14801 6426 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14802 : {
14803 6426 : }
14804 :
14805 : std::shared_ptr<GDALMDArray>
14806 1926 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14807 : {
14808 1926 : return m_poIndexingVariable.lock();
14809 : }
14810 :
14811 : // cppcheck-suppress passedByValue
14812 950 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14813 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14814 : {
14815 950 : m_poIndexingVariable = poIndexingVariable;
14816 950 : return true;
14817 : }
14818 :
14819 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14820 : {
14821 33 : m_nSize = nNewSize;
14822 33 : }
14823 :
14824 : /************************************************************************/
14825 : /* GDALPamMultiDim::Private */
14826 : /************************************************************************/
14827 :
14828 : struct GDALPamMultiDim::Private
14829 : {
14830 : std::string m_osFilename{};
14831 : std::string m_osPamFilename{};
14832 :
14833 : struct Statistics
14834 : {
14835 : bool bHasStats = false;
14836 : bool bApproxStats = false;
14837 : double dfMin = 0;
14838 : double dfMax = 0;
14839 : double dfMean = 0;
14840 : double dfStdDev = 0;
14841 : GUInt64 nValidCount = 0;
14842 : };
14843 :
14844 : struct ArrayInfo
14845 : {
14846 : std::shared_ptr<OGRSpatialReference> poSRS{};
14847 : // cppcheck-suppress unusedStructMember
14848 : Statistics stats{};
14849 : };
14850 :
14851 : typedef std::pair<std::string, std::string> NameContext;
14852 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14853 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14854 : bool m_bDirty = false;
14855 : bool m_bLoaded = false;
14856 : };
14857 :
14858 : /************************************************************************/
14859 : /* GDALPamMultiDim */
14860 : /************************************************************************/
14861 :
14862 2561 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14863 2561 : : d(new Private())
14864 : {
14865 2561 : d->m_osFilename = osFilename;
14866 2561 : }
14867 :
14868 : /************************************************************************/
14869 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14870 : /************************************************************************/
14871 :
14872 2561 : GDALPamMultiDim::~GDALPamMultiDim()
14873 : {
14874 2561 : if (d->m_bDirty)
14875 30 : Save();
14876 2561 : }
14877 :
14878 : /************************************************************************/
14879 : /* GDALPamMultiDim::Load() */
14880 : /************************************************************************/
14881 :
14882 116 : void GDALPamMultiDim::Load()
14883 : {
14884 116 : if (d->m_bLoaded)
14885 105 : return;
14886 47 : d->m_bLoaded = true;
14887 :
14888 47 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14889 47 : d->m_osPamFilename =
14890 94 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14891 47 : CPLXMLTreeCloser oTree(nullptr);
14892 : {
14893 94 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14894 47 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14895 : }
14896 47 : if (!oTree)
14897 : {
14898 36 : return;
14899 : }
14900 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14901 11 : if (!poPAMMultiDim)
14902 0 : return;
14903 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14904 24 : psIter = psIter->psNext)
14905 : {
14906 24 : if (psIter->eType == CXT_Element &&
14907 24 : strcmp(psIter->pszValue, "Array") == 0)
14908 : {
14909 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14910 13 : if (!pszName)
14911 0 : continue;
14912 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14913 : const auto oKey =
14914 26 : std::pair<std::string, std::string>(pszName, pszContext);
14915 :
14916 : /* --------------------------------------------------------------------
14917 : */
14918 : /* Check for an SRS node. */
14919 : /* --------------------------------------------------------------------
14920 : */
14921 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14922 13 : if (psSRSNode)
14923 : {
14924 : std::shared_ptr<OGRSpatialReference> poSRS =
14925 6 : std::make_shared<OGRSpatialReference>();
14926 3 : poSRS->SetFromUserInput(
14927 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14928 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14929 3 : const char *pszMapping = CPLGetXMLValue(
14930 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14931 3 : if (pszMapping)
14932 : {
14933 : char **papszTokens =
14934 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14935 6 : std::vector<int> anMapping;
14936 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14937 : {
14938 6 : anMapping.push_back(atoi(papszTokens[i]));
14939 : }
14940 3 : CSLDestroy(papszTokens);
14941 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14942 : }
14943 : else
14944 : {
14945 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14946 : }
14947 :
14948 : const char *pszCoordinateEpoch =
14949 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14950 3 : if (pszCoordinateEpoch)
14951 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14952 :
14953 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14954 : }
14955 :
14956 : const CPLXMLNode *psStatistics =
14957 13 : CPLGetXMLNode(psIter, "Statistics");
14958 13 : if (psStatistics)
14959 : {
14960 7 : Private::Statistics sStats;
14961 7 : sStats.bHasStats = true;
14962 7 : sStats.bApproxStats = CPLTestBool(
14963 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14964 7 : sStats.dfMin =
14965 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14966 7 : sStats.dfMax =
14967 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14968 7 : sStats.dfMean =
14969 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14970 7 : sStats.dfStdDev =
14971 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14972 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14973 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14974 7 : d->m_oMapArray[oKey].stats = sStats;
14975 13 : }
14976 : }
14977 : else
14978 : {
14979 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14980 11 : psIter->psNext = nullptr;
14981 11 : d->m_apoOtherNodes.emplace_back(
14982 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14983 11 : psIter->psNext = psNextBackup;
14984 : }
14985 : }
14986 : }
14987 :
14988 : /************************************************************************/
14989 : /* GDALPamMultiDim::Save() */
14990 : /************************************************************************/
14991 :
14992 30 : void GDALPamMultiDim::Save()
14993 : {
14994 : CPLXMLTreeCloser oTree(
14995 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14996 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14997 : {
14998 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14999 : }
15000 112 : for (const auto &kv : d->m_oMapArray)
15001 : {
15002 : CPLXMLNode *psArrayNode =
15003 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
15004 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
15005 82 : if (!kv.first.second.empty())
15006 : {
15007 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
15008 : kv.first.second.c_str());
15009 : }
15010 82 : if (kv.second.poSRS)
15011 : {
15012 71 : char *pszWKT = nullptr;
15013 : {
15014 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
15015 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
15016 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
15017 : }
15018 : CPLXMLNode *psSRSNode =
15019 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
15020 71 : CPLFree(pszWKT);
15021 : const auto &mapping =
15022 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
15023 142 : CPLString osMapping;
15024 213 : for (size_t i = 0; i < mapping.size(); ++i)
15025 : {
15026 142 : if (!osMapping.empty())
15027 71 : osMapping += ",";
15028 142 : osMapping += CPLSPrintf("%d", mapping[i]);
15029 : }
15030 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
15031 : osMapping.c_str());
15032 :
15033 : const double dfCoordinateEpoch =
15034 71 : kv.second.poSRS->GetCoordinateEpoch();
15035 71 : if (dfCoordinateEpoch > 0)
15036 : {
15037 : std::string osCoordinateEpoch =
15038 2 : CPLSPrintf("%f", dfCoordinateEpoch);
15039 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
15040 : {
15041 6 : while (osCoordinateEpoch.back() == '0')
15042 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
15043 : }
15044 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
15045 : osCoordinateEpoch.c_str());
15046 : }
15047 : }
15048 :
15049 82 : if (kv.second.stats.bHasStats)
15050 : {
15051 : CPLXMLNode *psMDArray =
15052 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
15053 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
15054 8 : kv.second.stats.bApproxStats ? "1"
15055 : : "0");
15056 8 : CPLCreateXMLElementAndValue(
15057 : psMDArray, "Minimum",
15058 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
15059 8 : CPLCreateXMLElementAndValue(
15060 : psMDArray, "Maximum",
15061 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
15062 8 : CPLCreateXMLElementAndValue(
15063 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
15064 8 : CPLCreateXMLElementAndValue(
15065 : psMDArray, "StdDev",
15066 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
15067 8 : CPLCreateXMLElementAndValue(
15068 : psMDArray, "ValidSampleCount",
15069 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
15070 : }
15071 : }
15072 :
15073 : int bSaved;
15074 60 : CPLErrorAccumulator oErrorAccumulator;
15075 : {
15076 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
15077 30 : CPL_IGNORE_RET_VAL(oAccumulator);
15078 : bSaved =
15079 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
15080 : }
15081 :
15082 30 : const char *pszNewPam = nullptr;
15083 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
15084 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
15085 : {
15086 0 : CPLErrorReset();
15087 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
15088 : }
15089 : else
15090 : {
15091 30 : oErrorAccumulator.ReplayErrors();
15092 : }
15093 30 : }
15094 :
15095 : /************************************************************************/
15096 : /* GDALPamMultiDim::GetSpatialRef() */
15097 : /************************************************************************/
15098 :
15099 : std::shared_ptr<OGRSpatialReference>
15100 19 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
15101 : const std::string &osContext)
15102 : {
15103 19 : Load();
15104 : auto oIter =
15105 19 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15106 19 : if (oIter != d->m_oMapArray.end())
15107 2 : return oIter->second.poSRS;
15108 17 : return nullptr;
15109 : }
15110 :
15111 : /************************************************************************/
15112 : /* GDALPamMultiDim::SetSpatialRef() */
15113 : /************************************************************************/
15114 :
15115 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
15116 : const std::string &osContext,
15117 : const OGRSpatialReference *poSRS)
15118 : {
15119 72 : Load();
15120 72 : d->m_bDirty = true;
15121 72 : if (poSRS && !poSRS->IsEmpty())
15122 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
15123 : poSRS->Clone());
15124 : else
15125 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
15126 1 : .poSRS.reset();
15127 72 : }
15128 :
15129 : /************************************************************************/
15130 : /* GetStatistics() */
15131 : /************************************************************************/
15132 :
15133 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
15134 : const std::string &osContext,
15135 : bool bApproxOK, double *pdfMin,
15136 : double *pdfMax, double *pdfMean,
15137 : double *pdfStdDev, GUInt64 *pnValidCount)
15138 : {
15139 16 : Load();
15140 : auto oIter =
15141 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15142 16 : if (oIter == d->m_oMapArray.end())
15143 9 : return CE_Failure;
15144 7 : const auto &stats = oIter->second.stats;
15145 7 : if (!stats.bHasStats)
15146 1 : return CE_Failure;
15147 6 : if (!bApproxOK && stats.bApproxStats)
15148 0 : return CE_Failure;
15149 6 : if (pdfMin)
15150 6 : *pdfMin = stats.dfMin;
15151 6 : if (pdfMax)
15152 6 : *pdfMax = stats.dfMax;
15153 6 : if (pdfMean)
15154 6 : *pdfMean = stats.dfMean;
15155 6 : if (pdfStdDev)
15156 6 : *pdfStdDev = stats.dfStdDev;
15157 6 : if (pnValidCount)
15158 6 : *pnValidCount = stats.nValidCount;
15159 6 : return CE_None;
15160 : }
15161 :
15162 : /************************************************************************/
15163 : /* SetStatistics() */
15164 : /************************************************************************/
15165 :
15166 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
15167 : const std::string &osContext,
15168 : bool bApproxStats, double dfMin,
15169 : double dfMax, double dfMean,
15170 : double dfStdDev, GUInt64 nValidCount)
15171 : {
15172 8 : Load();
15173 8 : d->m_bDirty = true;
15174 : auto &stats =
15175 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
15176 8 : stats.bHasStats = true;
15177 8 : stats.bApproxStats = bApproxStats;
15178 8 : stats.dfMin = dfMin;
15179 8 : stats.dfMax = dfMax;
15180 8 : stats.dfMean = dfMean;
15181 8 : stats.dfStdDev = dfStdDev;
15182 8 : stats.nValidCount = nValidCount;
15183 8 : }
15184 :
15185 : /************************************************************************/
15186 : /* ClearStatistics() */
15187 : /************************************************************************/
15188 :
15189 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
15190 : const std::string &osContext)
15191 : {
15192 0 : Load();
15193 0 : d->m_bDirty = true;
15194 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
15195 : false;
15196 0 : }
15197 :
15198 : /************************************************************************/
15199 : /* ClearStatistics() */
15200 : /************************************************************************/
15201 :
15202 1 : void GDALPamMultiDim::ClearStatistics()
15203 : {
15204 1 : Load();
15205 1 : d->m_bDirty = true;
15206 3 : for (auto &kv : d->m_oMapArray)
15207 2 : kv.second.stats.bHasStats = false;
15208 1 : }
15209 :
15210 : /************************************************************************/
15211 : /* GetPAM() */
15212 : /************************************************************************/
15213 :
15214 : /*static*/ std::shared_ptr<GDALPamMultiDim>
15215 1463 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15216 : {
15217 1463 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15218 1463 : if (poPamArray)
15219 730 : return poPamArray->GetPAM();
15220 733 : return nullptr;
15221 : }
15222 :
15223 : /************************************************************************/
15224 : /* GDALPamMDArray */
15225 : /************************************************************************/
15226 :
15227 5959 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15228 : const std::string &osName,
15229 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15230 0 : const std::string &osContext)
15231 : :
15232 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15233 : GDALAbstractMDArray(osParentName, osName),
15234 : #endif
15235 5959 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15236 : {
15237 5959 : }
15238 :
15239 : /************************************************************************/
15240 : /* GDALPamMDArray::SetSpatialRef() */
15241 : /************************************************************************/
15242 :
15243 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15244 : {
15245 72 : if (!m_poPam)
15246 0 : return false;
15247 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15248 72 : return true;
15249 : }
15250 :
15251 : /************************************************************************/
15252 : /* GDALPamMDArray::GetSpatialRef() */
15253 : /************************************************************************/
15254 :
15255 19 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15256 : {
15257 19 : if (!m_poPam)
15258 0 : return nullptr;
15259 19 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15260 : }
15261 :
15262 : /************************************************************************/
15263 : /* GetStatistics() */
15264 : /************************************************************************/
15265 :
15266 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15267 : double *pdfMin, double *pdfMax,
15268 : double *pdfMean, double *pdfStdDev,
15269 : GUInt64 *pnValidCount,
15270 : GDALProgressFunc pfnProgress,
15271 : void *pProgressData)
15272 : {
15273 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15274 : bApproxOK, pdfMin, pdfMax, pdfMean,
15275 16 : pdfStdDev, pnValidCount) == CE_None)
15276 : {
15277 6 : return CE_None;
15278 : }
15279 10 : if (!bForce)
15280 4 : return CE_Warning;
15281 :
15282 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15283 : pdfMean, pdfStdDev, pnValidCount,
15284 6 : pfnProgress, pProgressData);
15285 : }
15286 :
15287 : /************************************************************************/
15288 : /* SetStatistics() */
15289 : /************************************************************************/
15290 :
15291 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15292 : double dfMax, double dfMean, double dfStdDev,
15293 : GUInt64 nValidCount,
15294 : CSLConstList /* papszOptions */)
15295 : {
15296 8 : if (!m_poPam)
15297 0 : return false;
15298 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15299 : dfMax, dfMean, dfStdDev, nValidCount);
15300 8 : return true;
15301 : }
15302 :
15303 : /************************************************************************/
15304 : /* ClearStatistics() */
15305 : /************************************************************************/
15306 :
15307 0 : void GDALPamMDArray::ClearStatistics()
15308 : {
15309 0 : if (!m_poPam)
15310 0 : return;
15311 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15312 : }
15313 :
15314 : /************************************************************************/
15315 : /* GDALMDIAsAttribute::GetDimensions() */
15316 : /************************************************************************/
15317 :
15318 : const std::vector<std::shared_ptr<GDALDimension>> &
15319 29 : GDALMDIAsAttribute::GetDimensions() const
15320 : {
15321 29 : return m_dims;
15322 : }
15323 :
15324 : /************************************************************************/
15325 : /* GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo() */
15326 : /************************************************************************/
15327 :
15328 64 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
15329 : {
15330 32 : clear();
15331 32 : }
15332 :
15333 : /************************************************************************/
15334 : /* GDALMDArrayRawBlockInfo::clear() */
15335 : /************************************************************************/
15336 :
15337 53 : void GDALMDArrayRawBlockInfo::clear()
15338 : {
15339 53 : CPLFree(pszFilename);
15340 53 : pszFilename = nullptr;
15341 53 : CSLDestroy(papszInfo);
15342 53 : papszInfo = nullptr;
15343 53 : nOffset = 0;
15344 53 : nSize = 0;
15345 53 : CPLFree(pabyInlineData);
15346 53 : pabyInlineData = nullptr;
15347 53 : }
15348 :
15349 : /************************************************************************/
15350 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15351 : /************************************************************************/
15352 :
15353 4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15354 4 : const GDALMDArrayRawBlockInfo &other)
15355 4 : : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
15356 4 : nOffset(other.nOffset), nSize(other.nSize),
15357 4 : papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
15358 : {
15359 4 : if (other.pabyInlineData)
15360 : {
15361 3 : pabyInlineData = static_cast<GByte *>(
15362 3 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15363 3 : if (pabyInlineData)
15364 3 : memcpy(pabyInlineData, other.pabyInlineData,
15365 3 : static_cast<size_t>(other.nSize));
15366 : }
15367 4 : }
15368 :
15369 : /************************************************************************/
15370 : /* GDALMDArrayRawBlockInfo::operator=() */
15371 : /************************************************************************/
15372 :
15373 : GDALMDArrayRawBlockInfo &
15374 7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
15375 : {
15376 7 : if (this != &other)
15377 : {
15378 5 : CPLFree(pszFilename);
15379 5 : pszFilename =
15380 5 : other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
15381 5 : nOffset = other.nOffset;
15382 5 : nSize = other.nSize;
15383 5 : CSLDestroy(papszInfo);
15384 5 : papszInfo = CSLDuplicate(other.papszInfo);
15385 5 : CPLFree(pabyInlineData);
15386 5 : pabyInlineData = nullptr;
15387 5 : if (other.pabyInlineData)
15388 : {
15389 4 : pabyInlineData = static_cast<GByte *>(
15390 4 : VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15391 4 : if (pabyInlineData)
15392 4 : memcpy(pabyInlineData, other.pabyInlineData,
15393 4 : static_cast<size_t>(other.nSize));
15394 : }
15395 : }
15396 7 : return *this;
15397 : }
15398 :
15399 : /************************************************************************/
15400 : /* GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo() */
15401 : /************************************************************************/
15402 :
15403 2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15404 2 : GDALMDArrayRawBlockInfo &&other)
15405 2 : : pszFilename(other.pszFilename), nOffset(other.nOffset),
15406 2 : nSize(other.nSize), papszInfo(other.papszInfo),
15407 2 : pabyInlineData(other.pabyInlineData)
15408 : {
15409 2 : other.pszFilename = nullptr;
15410 2 : other.papszInfo = nullptr;
15411 2 : other.pabyInlineData = nullptr;
15412 2 : }
15413 :
15414 : /************************************************************************/
15415 : /* GDALMDArrayRawBlockInfo::operator=() */
15416 : /************************************************************************/
15417 :
15418 : GDALMDArrayRawBlockInfo &
15419 2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
15420 : {
15421 2 : if (this != &other)
15422 : {
15423 2 : std::swap(pszFilename, other.pszFilename);
15424 2 : nOffset = other.nOffset;
15425 2 : nSize = other.nSize;
15426 2 : std::swap(papszInfo, other.papszInfo);
15427 2 : std::swap(pabyInlineData, other.pabyInlineData);
15428 : }
15429 2 : return *this;
15430 : }
15431 :
15432 : //! @endcond
15433 :
15434 : /************************************************************************/
15435 : /* GDALMDArray::GetRawBlockInfo() */
15436 : /************************************************************************/
15437 :
15438 : /** Return information on a raw block.
15439 : *
15440 : * The block coordinates must be between 0 and
15441 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15442 : * 0 and GetDimensionCount()-1.
15443 : *
15444 : * If the queried block has valid coordinates but is missing in the dataset,
15445 : * all fields of info will be set to 0/nullptr, but the function will return
15446 : * true.
15447 : *
15448 : * This method is only implemented by a subset of drivers. The base
15449 : * implementation just returns false and empty info.
15450 : *
15451 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15452 : *
15453 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15454 : * value is "LITTLE" or "BIG".
15455 : *
15456 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15457 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15458 : * comma-separated)
15459 : *
15460 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15461 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15462 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15463 : * "[idx0,...,idxN]" with the permutation).
15464 : *
15465 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15466 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15467 : * that for each queried VRT block, there is one and only one source that
15468 : * is used to fill the VRT block and that the block size of this source is
15469 : * exactly the one of the VRT block.
15470 : *
15471 : * This is the same as C function GDALMDArrayGetRawBlockInfo().
15472 : *
15473 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15474 : * coordinates.
15475 : * @param[out] info structure to fill with block information.
15476 : * @return true in case of success, or false if an error occurs.
15477 : * @since 3.12
15478 : */
15479 0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
15480 : GDALMDArrayRawBlockInfo &info) const
15481 : {
15482 : (void)panBlockCoordinates;
15483 0 : info.clear();
15484 0 : return false;
15485 : }
15486 :
15487 : /************************************************************************/
15488 : /* GDALMDArrayGetRawBlockInfo() */
15489 : /************************************************************************/
15490 :
15491 : /** Return information on a raw block.
15492 : *
15493 : * The block coordinates must be between 0 and
15494 : * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15495 : * 0 and GetDimensionCount()-1.
15496 : *
15497 : * If the queried block has valid coordinates but is missing in the dataset,
15498 : * all fields of info will be set to 0/nullptr, but the function will return
15499 : * true.
15500 : *
15501 : * This method is only implemented by a subset of drivers. The base
15502 : * implementation just returns false and empty info.
15503 : *
15504 : * The values returned in psBlockInfo->papszInfo are driver dependent.
15505 : *
15506 : * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15507 : * value is "LITTLE" or "BIG".
15508 : *
15509 : * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15510 : * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15511 : * comma-separated)
15512 : *
15513 : * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15514 : * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15515 : * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15516 : * "[idx0,...,idxN]" with the permutation).
15517 : *
15518 : * For VRT, the potential keys are the ones of the underlying source(s). Note
15519 : * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15520 : * that for each queried VRT block, there is one and only one source that
15521 : * is used to fill the VRT block and that the block size of this source is
15522 : * exactly the one of the VRT block.
15523 : *
15524 : * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
15525 : *
15526 : * @param hArray handle to array.
15527 : * @param panBlockCoordinates array of GetDimensionCount() values with the block
15528 : * coordinates.
15529 : * @param[out] psBlockInfo structure to fill with block information.
15530 : * Must be allocated with GDALMDArrayRawBlockInfoCreate(),
15531 : * and freed with GDALMDArrayRawBlockInfoRelease().
15532 : * @return true in case of success, or false if an error occurs.
15533 : * @since 3.12
15534 : */
15535 21 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
15536 : const uint64_t *panBlockCoordinates,
15537 : GDALMDArrayRawBlockInfo *psBlockInfo)
15538 : {
15539 21 : VALIDATE_POINTER1(hArray, __func__, false);
15540 21 : VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
15541 21 : VALIDATE_POINTER1(psBlockInfo, __func__, false);
15542 21 : return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
15543 : }
15544 :
15545 : /************************************************************************/
15546 : /* GDALMDArrayRawBlockInfoCreate() */
15547 : /************************************************************************/
15548 :
15549 : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
15550 : *
15551 : * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
15552 : *
15553 : * @since 3.12
15554 : */
15555 21 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
15556 : {
15557 21 : return new GDALMDArrayRawBlockInfo();
15558 : }
15559 :
15560 : /************************************************************************/
15561 : /* GDALMDArrayRawBlockInfoRelease() */
15562 : /************************************************************************/
15563 :
15564 : /** Free an instance of GDALMDArrayRawBlockInfo.
15565 : *
15566 : * @since 3.12
15567 : */
15568 21 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
15569 : {
15570 21 : delete psBlockInfo;
15571 21 : }
15572 :
15573 : /************************************************************************/
15574 : /* GDALMDArray::GetOverviewCount() */
15575 : /************************************************************************/
15576 :
15577 : /**
15578 : * \brief Return the number of overview arrays available.
15579 : *
15580 : * This method is the same as the C function GDALMDArrayGetOverviewCount().
15581 : *
15582 : * @return overview count, zero if none.
15583 : *
15584 : * @since 3.13
15585 : */
15586 :
15587 115 : int GDALMDArray::GetOverviewCount() const
15588 : {
15589 115 : return 0;
15590 : }
15591 :
15592 : /************************************************************************/
15593 : /* GDALMDArrayGetOverviewCount() */
15594 : /************************************************************************/
15595 : /**
15596 : * \brief Return the number of overview arrays available.
15597 : *
15598 : * This method is the same as the C++ method GDALMDArray::GetOverviewCount().
15599 : *
15600 : * @param hArray Array.
15601 : * @return overview count, zero if none.
15602 : *
15603 : * @since 3.13
15604 : */
15605 :
15606 105 : int GDALMDArrayGetOverviewCount(GDALMDArrayH hArray)
15607 : {
15608 105 : VALIDATE_POINTER1(hArray, __func__, 0);
15609 105 : return hArray->m_poImpl->GetOverviewCount();
15610 : }
15611 :
15612 : /************************************************************************/
15613 : /* GDALMDArray::GetOverview() */
15614 : /************************************************************************/
15615 :
15616 : /**
15617 : * \brief Get overview array object.
15618 : *
15619 : * This method is the same as the C function GDALMDArrayGetOverview().
15620 : *
15621 : * @param nIdx overview index between 0 and GetOverviewCount()-1.
15622 : *
15623 : * @return overview GDALMDArray, or nullptr
15624 : *
15625 : * @since 3.13
15626 : */
15627 :
15628 70 : std::shared_ptr<GDALMDArray> GDALMDArray::GetOverview(int nIdx) const
15629 : {
15630 : (void)nIdx;
15631 70 : return nullptr;
15632 : }
15633 :
15634 : /************************************************************************/
15635 : /* GDALMDArrayGetOverview() */
15636 : /************************************************************************/
15637 :
15638 : /**
15639 : * \brief Get overview array object.
15640 : *
15641 : * This method is the same as the C++ method GDALMDArray::GetOverview().
15642 : *
15643 : * @param hArray Array.
15644 : * @param nIdx overview index between 0 and GDALMDArrayGetOverviewCount()-1.
15645 : *
15646 : * @return overview GDALMDArray, or nullptr.
15647 : * Must be released with GDALMDArrayRelease()
15648 : *
15649 : * @since 3.13
15650 : */
15651 :
15652 115 : GDALMDArrayH GDALMDArrayGetOverview(GDALMDArrayH hArray, int nIdx)
15653 : {
15654 115 : VALIDATE_POINTER1(hArray, __func__, nullptr);
15655 230 : auto poOverview = hArray->m_poImpl->GetOverview(nIdx);
15656 115 : if (!poOverview)
15657 89 : return nullptr;
15658 26 : return new GDALMDArrayHS(poOverview);
15659 : }
|