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 <queue>
18 : #include <set>
19 : #include <utility>
20 : #include <time.h>
21 :
22 : #include <cmath>
23 : #include <ctype.h> // isalnum
24 :
25 : #include "cpl_error_internal.h"
26 : #include "cpl_float.h"
27 : #include "gdal_priv.h"
28 : #include "gdal_pam.h"
29 : #include "gdal_utils.h"
30 : #include "cpl_safemaths.hpp"
31 : #include "memmultidim.h"
32 : #include "ogrsf_frmts.h"
33 : #include "gdalmultidim_priv.h"
34 :
35 : #if defined(__clang__) || defined(_MSC_VER)
36 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
37 : #endif
38 :
39 : /************************************************************************/
40 : /* GDALMDArrayUnscaled */
41 : /************************************************************************/
42 :
43 : class GDALMDArrayUnscaled final : public GDALPamMDArray
44 : {
45 : private:
46 : std::shared_ptr<GDALMDArray> m_poParent{};
47 : const GDALExtendedDataType m_dt;
48 : bool m_bHasNoData;
49 : const double m_dfScale;
50 : const double m_dfOffset;
51 : std::vector<GByte> m_abyRawNoData{};
52 :
53 : protected:
54 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
55 : double dfScale, double dfOffset,
56 : double dfOverriddenDstNodata, GDALDataType eDT)
57 26 : : GDALAbstractMDArray(std::string(),
58 26 : "Unscaled view of " + poParent->GetFullName()),
59 : GDALPamMDArray(
60 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
61 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
62 13 : m_poParent(std::move(poParent)),
63 : m_dt(GDALExtendedDataType::Create(eDT)),
64 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
65 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
66 : {
67 13 : m_abyRawNoData.resize(m_dt.GetSize());
68 : const auto eNonComplexDT =
69 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
70 26 : GDALCopyWords64(
71 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
72 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
73 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
74 13 : }
75 :
76 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
77 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
78 : const GDALExtendedDataType &bufferDataType,
79 : void *pDstBuffer) const override;
80 :
81 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
82 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
83 : const GDALExtendedDataType &bufferDataType,
84 : const void *pSrcBuffer) override;
85 :
86 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
87 : CSLConstList papszOptions) const override
88 : {
89 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
90 : }
91 :
92 : public:
93 : static std::shared_ptr<GDALMDArrayUnscaled>
94 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
95 : double dfOffset, double dfDstNodata, GDALDataType eDT)
96 : {
97 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
98 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
99 13 : newAr->SetSelf(newAr);
100 13 : return newAr;
101 : }
102 :
103 1 : bool IsWritable() const override
104 : {
105 1 : return m_poParent->IsWritable();
106 : }
107 :
108 15 : const std::string &GetFilename() const override
109 : {
110 15 : return m_poParent->GetFilename();
111 : }
112 :
113 : const std::vector<std::shared_ptr<GDALDimension>> &
114 220 : GetDimensions() const override
115 : {
116 220 : return m_poParent->GetDimensions();
117 : }
118 :
119 103 : const GDALExtendedDataType &GetDataType() const override
120 : {
121 103 : return m_dt;
122 : }
123 :
124 1 : const std::string &GetUnit() const override
125 : {
126 1 : return m_poParent->GetUnit();
127 : }
128 :
129 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
130 : {
131 1 : return m_poParent->GetSpatialRef();
132 : }
133 :
134 6 : const void *GetRawNoDataValue() const override
135 : {
136 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
137 : }
138 :
139 1 : bool SetRawNoDataValue(const void *pRawNoData) override
140 : {
141 1 : m_bHasNoData = true;
142 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
143 1 : return true;
144 : }
145 :
146 4 : std::vector<GUInt64> GetBlockSize() const override
147 : {
148 4 : return m_poParent->GetBlockSize();
149 : }
150 :
151 : std::shared_ptr<GDALAttribute>
152 0 : GetAttribute(const std::string &osName) const override
153 : {
154 0 : return m_poParent->GetAttribute(osName);
155 : }
156 :
157 : std::vector<std::shared_ptr<GDALAttribute>>
158 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
159 : {
160 1 : return m_poParent->GetAttributes(papszOptions);
161 : }
162 :
163 0 : bool SetUnit(const std::string &osUnit) override
164 : {
165 0 : return m_poParent->SetUnit(osUnit);
166 : }
167 :
168 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
169 : {
170 0 : return m_poParent->SetSpatialRef(poSRS);
171 : }
172 :
173 : std::shared_ptr<GDALAttribute>
174 1 : CreateAttribute(const std::string &osName,
175 : const std::vector<GUInt64> &anDimensions,
176 : const GDALExtendedDataType &oDataType,
177 : CSLConstList papszOptions = nullptr) override
178 : {
179 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
180 1 : papszOptions);
181 : }
182 : };
183 :
184 : /************************************************************************/
185 : /* ~GDALIHasAttribute() */
186 : /************************************************************************/
187 :
188 : GDALIHasAttribute::~GDALIHasAttribute() = default;
189 :
190 : /************************************************************************/
191 : /* GetAttribute() */
192 : /************************************************************************/
193 :
194 : /** Return an attribute by its name.
195 : *
196 : * If the attribute does not exist, nullptr should be silently returned.
197 : *
198 : * @note Driver implementation: this method will fallback to
199 : * GetAttributeFromAttributes() is not explicitly implemented
200 : *
201 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
202 : *
203 : * This is the same as the C function GDALGroupGetAttribute() or
204 : * GDALMDArrayGetAttribute().
205 : *
206 : * @param osName Attribute name
207 : * @return the attribute, or nullptr if it does not exist or an error occurred.
208 : */
209 : std::shared_ptr<GDALAttribute>
210 1022 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
211 : {
212 1022 : return GetAttributeFromAttributes(osName);
213 : }
214 :
215 : /************************************************************************/
216 : /* GetAttributeFromAttributes() */
217 : /************************************************************************/
218 :
219 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
220 : */
221 : std::shared_ptr<GDALAttribute>
222 1022 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
223 : {
224 2044 : auto attrs(GetAttributes());
225 5357 : for (const auto &attr : attrs)
226 : {
227 5051 : if (attr->GetName() == osName)
228 716 : return attr;
229 : }
230 306 : return nullptr;
231 : }
232 :
233 : /************************************************************************/
234 : /* GetAttributes() */
235 : /************************************************************************/
236 :
237 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
238 : *
239 : * If the attribute does not exist, nullptr should be silently returned.
240 : *
241 : * @note Driver implementation: optionally implemented. If implemented,
242 : * GetAttribute() should also be implemented.
243 : *
244 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
245 : *
246 : * This is the same as the C function GDALGroupGetAttributes() or
247 : * GDALMDArrayGetAttributes().
248 :
249 : * @param papszOptions Driver specific options determining how attributes
250 : * should be retrieved. Pass nullptr for default behavior.
251 : *
252 : * @return the attributes.
253 : */
254 : std::vector<std::shared_ptr<GDALAttribute>>
255 41 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
256 : {
257 41 : return {};
258 : }
259 :
260 : /************************************************************************/
261 : /* CreateAttribute() */
262 : /************************************************************************/
263 :
264 : /** Create an attribute within a GDALMDArray or GDALGroup.
265 : *
266 : * The attribute might not be "physically" created until a value is written
267 : * into it.
268 : *
269 : * Optionally implemented.
270 : *
271 : * Drivers known to implement it: MEM, netCDF
272 : *
273 : * This is the same as the C function GDALGroupCreateAttribute() or
274 : * GDALMDArrayCreateAttribute()
275 : *
276 : * @param osName Attribute name.
277 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
278 : * dimension first to the fastest varying dimension last.
279 : * Empty for a scalar attribute (common case)
280 : * @param oDataType Attribute data type.
281 : * @param papszOptions Driver specific options determining how the attribute.
282 : * should be created.
283 : *
284 : * @return the new attribute, or nullptr if case of error
285 : */
286 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
287 : CPL_UNUSED const std::string &osName,
288 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
289 : CPL_UNUSED const GDALExtendedDataType &oDataType,
290 : CPL_UNUSED CSLConstList papszOptions)
291 : {
292 0 : CPLError(CE_Failure, CPLE_NotSupported,
293 : "CreateAttribute() not implemented");
294 0 : return nullptr;
295 : }
296 :
297 : /************************************************************************/
298 : /* DeleteAttribute() */
299 : /************************************************************************/
300 :
301 : /** Delete an attribute from a GDALMDArray or GDALGroup.
302 : *
303 : * Optionally implemented.
304 : *
305 : * After this call, if a previously obtained instance of the deleted object
306 : * is still alive, no method other than for freeing it should be invoked.
307 : *
308 : * Drivers known to implement it: MEM, netCDF
309 : *
310 : * This is the same as the C function GDALGroupDeleteAttribute() or
311 : * GDALMDArrayDeleteAttribute()
312 : *
313 : * @param osName Attribute name.
314 : * @param papszOptions Driver specific options determining how the attribute.
315 : * should be deleted.
316 : *
317 : * @return true in case of success
318 : * @since GDAL 3.8
319 : */
320 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
321 : CPL_UNUSED CSLConstList papszOptions)
322 : {
323 0 : CPLError(CE_Failure, CPLE_NotSupported,
324 : "DeleteAttribute() not implemented");
325 0 : return false;
326 : }
327 :
328 : /************************************************************************/
329 : /* GDALGroup() */
330 : /************************************************************************/
331 :
332 : //! @cond Doxygen_Suppress
333 6680 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
334 6680 : const std::string &osContext)
335 6680 : : m_osName(osParentName.empty() ? "/" : osName),
336 : m_osFullName(
337 13360 : !osParentName.empty()
338 10332 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
339 : : "/"),
340 17012 : m_osContext(osContext)
341 : {
342 6680 : }
343 :
344 : //! @endcond
345 :
346 : /************************************************************************/
347 : /* ~GDALGroup() */
348 : /************************************************************************/
349 :
350 : GDALGroup::~GDALGroup() = default;
351 :
352 : /************************************************************************/
353 : /* GetMDArrayNames() */
354 : /************************************************************************/
355 :
356 : /** Return the list of multidimensional array names contained in this group.
357 : *
358 : * @note Driver implementation: optionally implemented. If implemented,
359 : * OpenMDArray() should also be implemented.
360 : *
361 : * Drivers known to implement it: MEM, netCDF.
362 : *
363 : * This is the same as the C function GDALGroupGetMDArrayNames().
364 : *
365 : * @param papszOptions Driver specific options determining how arrays
366 : * should be retrieved. Pass nullptr for default behavior.
367 : *
368 : * @return the array names.
369 : */
370 : std::vector<std::string>
371 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
372 : {
373 0 : return {};
374 : }
375 :
376 : /************************************************************************/
377 : /* OpenMDArray() */
378 : /************************************************************************/
379 :
380 : /** Open and return a multidimensional array.
381 : *
382 : * @note Driver implementation: optionally implemented. If implemented,
383 : * GetMDArrayNames() should also be implemented.
384 : *
385 : * Drivers known to implement it: MEM, netCDF.
386 : *
387 : * This is the same as the C function GDALGroupOpenMDArray().
388 : *
389 : * @param osName Array name.
390 : * @param papszOptions Driver specific options determining how the array should
391 : * be opened. Pass nullptr for default behavior.
392 : *
393 : * @return the array, or nullptr.
394 : */
395 : std::shared_ptr<GDALMDArray>
396 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
397 : CPL_UNUSED CSLConstList papszOptions) const
398 : {
399 0 : return nullptr;
400 : }
401 :
402 : /************************************************************************/
403 : /* GetGroupNames() */
404 : /************************************************************************/
405 :
406 : /** Return the list of sub-groups contained in this group.
407 : *
408 : * @note Driver implementation: optionally implemented. If implemented,
409 : * OpenGroup() should also be implemented.
410 : *
411 : * Drivers known to implement it: MEM, netCDF.
412 : *
413 : * This is the same as the C function GDALGroupGetGroupNames().
414 : *
415 : * @param papszOptions Driver specific options determining how groups
416 : * should be retrieved. Pass nullptr for default behavior.
417 : *
418 : * @return the group names.
419 : */
420 : std::vector<std::string>
421 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
422 : {
423 4 : return {};
424 : }
425 :
426 : /************************************************************************/
427 : /* OpenGroup() */
428 : /************************************************************************/
429 :
430 : /** Open and return a sub-group.
431 : *
432 : * @note Driver implementation: optionally implemented. If implemented,
433 : * GetGroupNames() should also be implemented.
434 : *
435 : * Drivers known to implement it: MEM, netCDF.
436 : *
437 : * This is the same as the C function GDALGroupOpenGroup().
438 : *
439 : * @param osName Sub-group name.
440 : * @param papszOptions Driver specific options determining how the sub-group
441 : * should be opened. Pass nullptr for default behavior.
442 : *
443 : * @return the group, or nullptr.
444 : */
445 : std::shared_ptr<GDALGroup>
446 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
447 : CPL_UNUSED CSLConstList papszOptions) const
448 : {
449 4 : return nullptr;
450 : }
451 :
452 : /************************************************************************/
453 : /* GetVectorLayerNames() */
454 : /************************************************************************/
455 :
456 : /** Return the list of layer names contained in this group.
457 : *
458 : * @note Driver implementation: optionally implemented. If implemented,
459 : * OpenVectorLayer() should also be implemented.
460 : *
461 : * Drivers known to implement it: OpenFileGDB, FileGDB
462 : *
463 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
464 : * GDALDataset::GetLayer() should then be used.
465 : *
466 : * This is the same as the C function GDALGroupGetVectorLayerNames().
467 : *
468 : * @param papszOptions Driver specific options determining how layers
469 : * should be retrieved. Pass nullptr for default behavior.
470 : *
471 : * @return the vector layer names.
472 : * @since GDAL 3.4
473 : */
474 : std::vector<std::string>
475 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
476 : {
477 1 : return {};
478 : }
479 :
480 : /************************************************************************/
481 : /* OpenVectorLayer() */
482 : /************************************************************************/
483 :
484 : /** Open and return a vector layer.
485 : *
486 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
487 : * lifetime of the returned OGRLayer* is linked to the one of the owner
488 : * dataset (contrary to the general design of this class where objects can be
489 : * used independently of the object that returned them)
490 : *
491 : * @note Driver implementation: optionally implemented. If implemented,
492 : * GetVectorLayerNames() should also be implemented.
493 : *
494 : * Drivers known to implement it: MEM, netCDF.
495 : *
496 : * This is the same as the C function GDALGroupOpenVectorLayer().
497 : *
498 : * @param osName Vector layer name.
499 : * @param papszOptions Driver specific options determining how the layer should
500 : * be opened. Pass nullptr for default behavior.
501 : *
502 : * @return the group, or nullptr.
503 : */
504 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
505 : CPL_UNUSED CSLConstList papszOptions) const
506 : {
507 2 : return nullptr;
508 : }
509 :
510 : /************************************************************************/
511 : /* GetDimensions() */
512 : /************************************************************************/
513 :
514 : /** Return the list of dimensions contained in this group and used by its
515 : * arrays.
516 : *
517 : * This is for dimensions that can potentially be used by several arrays.
518 : * Not all drivers might implement this. To retrieve the dimensions used by
519 : * a specific array, use GDALMDArray::GetDimensions().
520 : *
521 : * Drivers known to implement it: MEM, netCDF
522 : *
523 : * This is the same as the C function GDALGroupGetDimensions().
524 : *
525 : * @param papszOptions Driver specific options determining how groups
526 : * should be retrieved. Pass nullptr for default behavior.
527 : *
528 : * @return the dimensions.
529 : */
530 : std::vector<std::shared_ptr<GDALDimension>>
531 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
532 : {
533 11 : return {};
534 : }
535 :
536 : /************************************************************************/
537 : /* GetStructuralInfo() */
538 : /************************************************************************/
539 :
540 : /** Return structural information on the group.
541 : *
542 : * This may be the compression, etc..
543 : *
544 : * The return value should not be freed and is valid until GDALGroup is
545 : * released or this function called again.
546 : *
547 : * This is the same as the C function GDALGroupGetStructuralInfo().
548 : */
549 29 : CSLConstList GDALGroup::GetStructuralInfo() const
550 : {
551 29 : return nullptr;
552 : }
553 :
554 : /************************************************************************/
555 : /* CreateGroup() */
556 : /************************************************************************/
557 :
558 : /** Create a sub-group within a group.
559 : *
560 : * Optionally implemented by drivers.
561 : *
562 : * Drivers known to implement it: MEM, netCDF
563 : *
564 : * This is the same as the C function GDALGroupCreateGroup().
565 : *
566 : * @param osName Sub-group name.
567 : * @param papszOptions Driver specific options determining how the sub-group
568 : * should be created.
569 : *
570 : * @return the new sub-group, or nullptr in case of error.
571 : */
572 : std::shared_ptr<GDALGroup>
573 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
574 : CPL_UNUSED CSLConstList papszOptions)
575 : {
576 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
577 0 : return nullptr;
578 : }
579 :
580 : /************************************************************************/
581 : /* DeleteGroup() */
582 : /************************************************************************/
583 :
584 : /** Delete a sub-group from a group.
585 : *
586 : * Optionally implemented.
587 : *
588 : * After this call, if a previously obtained instance of the deleted object
589 : * is still alive, no method other than for freeing it should be invoked.
590 : *
591 : * Drivers known to implement it: MEM, Zarr
592 : *
593 : * This is the same as the C function GDALGroupDeleteGroup().
594 : *
595 : * @param osName Sub-group name.
596 : * @param papszOptions Driver specific options determining how the group.
597 : * should be deleted.
598 : *
599 : * @return true in case of success
600 : * @since GDAL 3.8
601 : */
602 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
603 : CPL_UNUSED CSLConstList papszOptions)
604 : {
605 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
606 0 : return false;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateDimension() */
611 : /************************************************************************/
612 :
613 : /** Create a dimension within a group.
614 : *
615 : * @note Driver implementation: drivers supporting CreateDimension() should
616 : * implement this method, but do not have necessarily to implement
617 : * GDALGroup::GetDimensions().
618 : *
619 : * Drivers known to implement it: MEM, netCDF
620 : *
621 : * This is the same as the C function GDALGroupCreateDimension().
622 : *
623 : * @param osName Dimension name.
624 : * @param osType Dimension type (might be empty, and ignored by drivers)
625 : * @param osDirection Dimension direction (might be empty, and ignored by
626 : * drivers)
627 : * @param nSize Number of values indexed by this dimension. Should be > 0.
628 : * @param papszOptions Driver specific options determining how the dimension
629 : * should be created.
630 : *
631 : * @return the new dimension, or nullptr if case of error
632 : */
633 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
634 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
635 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
636 : CPL_UNUSED CSLConstList papszOptions)
637 : {
638 0 : CPLError(CE_Failure, CPLE_NotSupported,
639 : "CreateDimension() not implemented");
640 0 : return nullptr;
641 : }
642 :
643 : /************************************************************************/
644 : /* CreateMDArray() */
645 : /************************************************************************/
646 :
647 : /** Create a multidimensional array within a group.
648 : *
649 : * It is recommended that the GDALDimension objects passed in aoDimensions
650 : * belong to this group, either by retrieving them with GetDimensions()
651 : * or creating a new one with CreateDimension().
652 : *
653 : * Optionally implemented.
654 : *
655 : * Drivers known to implement it: MEM, netCDF
656 : *
657 : * This is the same as the C function GDALGroupCreateMDArray().
658 : *
659 : * @note Driver implementation: drivers should take into account the possibility
660 : * that GDALDimension object passed in aoDimensions might belong to a different
661 : * group / dataset / driver and act accordingly.
662 : *
663 : * @param osName Array name.
664 : * @param aoDimensions List of dimensions, ordered from the slowest varying
665 : * dimension first to the fastest varying dimension last.
666 : * Might be empty for a scalar array (if supported by
667 : * driver)
668 : * @param oDataType Array data type.
669 : * @param papszOptions Driver specific options determining how the array
670 : * should be created.
671 : *
672 : * @return the new array, or nullptr if case of error
673 : */
674 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
675 : CPL_UNUSED const std::string &osName,
676 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
677 : CPL_UNUSED const GDALExtendedDataType &oDataType,
678 : CPL_UNUSED CSLConstList papszOptions)
679 : {
680 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
681 0 : return nullptr;
682 : }
683 :
684 : /************************************************************************/
685 : /* DeleteMDArray() */
686 : /************************************************************************/
687 :
688 : /** Delete an array from a group.
689 : *
690 : * Optionally implemented.
691 : *
692 : * After this call, if a previously obtained instance of the deleted object
693 : * is still alive, no method other than for freeing it should be invoked.
694 : *
695 : * Drivers known to implement it: MEM, Zarr
696 : *
697 : * This is the same as the C function GDALGroupDeleteMDArray().
698 : *
699 : * @param osName Arrayname.
700 : * @param papszOptions Driver specific options determining how the array.
701 : * should be deleted.
702 : *
703 : * @return true in case of success
704 : * @since GDAL 3.8
705 : */
706 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
707 : CPL_UNUSED CSLConstList papszOptions)
708 : {
709 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
710 0 : return false;
711 : }
712 :
713 : /************************************************************************/
714 : /* GetTotalCopyCost() */
715 : /************************************************************************/
716 :
717 : /** Return a total "cost" to copy the group.
718 : *
719 : * Used as a parameter for CopFrom()
720 : */
721 22 : GUInt64 GDALGroup::GetTotalCopyCost() const
722 : {
723 22 : GUInt64 nCost = COPY_COST;
724 22 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
725 :
726 44 : auto groupNames = GetGroupNames();
727 26 : for (const auto &name : groupNames)
728 : {
729 8 : auto subGroup = OpenGroup(name);
730 4 : if (subGroup)
731 : {
732 4 : nCost += subGroup->GetTotalCopyCost();
733 : }
734 : }
735 :
736 22 : auto arrayNames = GetMDArrayNames();
737 61 : for (const auto &name : arrayNames)
738 : {
739 78 : auto array = OpenMDArray(name);
740 39 : if (array)
741 : {
742 39 : nCost += array->GetTotalCopyCost();
743 : }
744 : }
745 44 : return nCost;
746 : }
747 :
748 : /************************************************************************/
749 : /* CopyFrom() */
750 : /************************************************************************/
751 :
752 : /** Copy the content of a group into a new (generally empty) group.
753 : *
754 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
755 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
756 : * of some output drivers this is not recommended)
757 : * @param poSrcGroup Source group. Must NOT be nullptr.
758 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
759 : * stop the copy. In relaxed mode, the copy will be attempted to
760 : * be pursued.
761 : * @param nCurCost Should be provided as a variable initially set to 0.
762 : * @param nTotalCost Total cost from GetTotalCopyCost().
763 : * @param pfnProgress Progress callback, or nullptr.
764 : * @param pProgressData Progress user data, or nulptr.
765 : * @param papszOptions Creation options. Currently, only array creation
766 : * options are supported. They must be prefixed with
767 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
768 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
769 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
770 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
771 : * Restriction to arrays of a given name is done with adding
772 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
773 : * a full qualified name.
774 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
775 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
776 : * scaled to UInt16 with scale and offset values being computed from the minimum
777 : * and maximum of the source array. The integer data type used can be set with
778 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
779 : *
780 : * @return true in case of success (or partial success if bStrict == false).
781 : */
782 22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
783 : GDALDataset *poSrcDS,
784 : const std::shared_ptr<GDALGroup> &poSrcGroup,
785 : bool bStrict, GUInt64 &nCurCost,
786 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
787 : void *pProgressData, CSLConstList papszOptions)
788 : {
789 22 : if (pfnProgress == nullptr)
790 0 : pfnProgress = GDALDummyProgress;
791 :
792 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
793 : if (!(x)) \
794 : { \
795 : if (bStrict) \
796 : return false; \
797 : continue; \
798 : } \
799 : (void)0
800 :
801 : try
802 : {
803 22 : nCurCost += GDALGroup::COPY_COST;
804 :
805 44 : const auto srcDims = poSrcGroup->GetDimensions();
806 : std::map<std::string, std::shared_ptr<GDALDimension>>
807 44 : mapExistingDstDims;
808 44 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
809 56 : for (const auto &dim : srcDims)
810 : {
811 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
812 34 : dim->GetDirection(), dim->GetSize());
813 34 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
814 34 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
815 68 : auto poIndexingVarSrc(dim->GetIndexingVariable());
816 34 : if (poIndexingVarSrc)
817 : {
818 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
819 16 : ->GetName()] =
820 32 : dim->GetName();
821 : }
822 : }
823 :
824 44 : auto attrs = poSrcGroup->GetAttributes();
825 28 : for (const auto &attr : attrs)
826 : {
827 : auto dstAttr =
828 6 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
829 12 : attr->GetDataType());
830 6 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
831 6 : auto raw(attr->ReadAsRaw());
832 6 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
833 0 : return false;
834 : }
835 22 : if (!attrs.empty())
836 : {
837 4 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
838 4 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
839 0 : return false;
840 : }
841 :
842 : const auto CopyArray =
843 39 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
844 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
845 : papszOptions, bStrict, &nCurCost,
846 355 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
847 : {
848 : // Map source dimensions to target dimensions
849 78 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
850 39 : const auto &srcArrayDims(srcArray->GetDimensions());
851 99 : for (const auto &dim : srcArrayDims)
852 : {
853 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
854 60 : dim->GetFullName());
855 60 : if (dstDim && dstDim->GetSize() == dim->GetSize())
856 : {
857 50 : dstArrayDims.emplace_back(dstDim);
858 : }
859 : else
860 : {
861 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
862 19 : if (oIter != mapExistingDstDims.end() &&
863 9 : oIter->second->GetSize() == dim->GetSize())
864 : {
865 8 : dstArrayDims.emplace_back(oIter->second);
866 : }
867 : else
868 : {
869 2 : std::string newDimName;
870 2 : if (oIter == mapExistingDstDims.end())
871 : {
872 1 : newDimName = dim->GetName();
873 : }
874 : else
875 : {
876 1 : std::string newDimNamePrefix(srcArray->GetName() +
877 3 : '_' + dim->GetName());
878 1 : newDimName = newDimNamePrefix;
879 1 : int nIterCount = 2;
880 0 : while (
881 1 : cpl::contains(mapExistingDstDims, newDimName))
882 : {
883 0 : newDimName = newDimNamePrefix +
884 0 : CPLSPrintf("_%d", nIterCount);
885 0 : nIterCount++;
886 : }
887 : }
888 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
889 : dim->GetDirection(),
890 4 : dim->GetSize());
891 2 : if (!dstDim)
892 0 : return false;
893 2 : mapExistingDstDims[newDimName] = dstDim;
894 2 : dstArrayDims.emplace_back(dstDim);
895 : }
896 : }
897 : }
898 :
899 78 : CPLStringList aosArrayCO;
900 39 : bool bAutoScale = false;
901 39 : GDALDataType eAutoScaleType = GDT_UInt16;
902 46 : for (const char *pszItem : cpl::Iterate(papszOptions))
903 : {
904 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
905 : {
906 7 : const char *pszOption = pszItem + strlen("ARRAY:");
907 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
908 : {
909 1 : const char *pszNext = strchr(pszOption, ':');
910 1 : if (pszNext != nullptr)
911 : {
912 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
913 1 : if (static_cast<size_t>(nDim) ==
914 1 : dstArrayDims.size())
915 : {
916 1 : pszOption = pszNext + 1;
917 : }
918 : else
919 : {
920 0 : pszOption = nullptr;
921 : }
922 : }
923 : }
924 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
925 : {
926 2 : const char *pszName = pszOption + strlen("IF(NAME=");
927 2 : const char *pszNext = strchr(pszName, ':');
928 2 : if (pszNext != nullptr && pszNext > pszName &&
929 2 : pszNext[-1] == ')')
930 : {
931 4 : CPLString osName;
932 2 : osName.assign(pszName, pszNext - pszName - 1);
933 3 : if (osName == srcArray->GetName() ||
934 1 : osName == srcArray->GetFullName())
935 : {
936 2 : pszOption = pszNext + 1;
937 : }
938 : else
939 : {
940 0 : pszOption = nullptr;
941 : }
942 : }
943 : }
944 7 : if (pszOption)
945 : {
946 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
947 : {
948 : bAutoScale =
949 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
950 : }
951 5 : else if (STARTS_WITH_CI(pszOption,
952 : "AUTOSCALE_DATA_TYPE="))
953 : {
954 1 : const char *pszDataType =
955 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
956 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
957 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
958 1 : GDALDataTypeIsFloating(eAutoScaleType))
959 : {
960 0 : CPLError(CE_Failure, CPLE_NotSupported,
961 : "Unsupported value for "
962 : "AUTOSCALE_DATA_TYPE");
963 0 : return false;
964 : }
965 : }
966 : else
967 : {
968 4 : aosArrayCO.AddString(pszOption);
969 : }
970 : }
971 : }
972 : }
973 :
974 : auto oIterDimName =
975 39 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
976 39 : const auto &srcArrayType = srcArray->GetDataType();
977 :
978 39 : std::shared_ptr<GDALMDArray> dstArray;
979 :
980 : // Only autoscale non-indexing variables
981 39 : bool bHasOffset = false;
982 39 : bool bHasScale = false;
983 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
984 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
985 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
986 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
987 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
988 43 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
989 41 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
990 : {
991 2 : constexpr bool bApproxOK = false;
992 2 : constexpr bool bForce = true;
993 2 : double dfMin = 0.0;
994 2 : double dfMax = 0.0;
995 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
996 : nullptr, nullptr, nullptr, nullptr,
997 2 : nullptr) != CE_None)
998 : {
999 0 : CPLError(CE_Failure, CPLE_AppDefined,
1000 : "Could not retrieve statistics for array %s",
1001 0 : srcArray->GetName().c_str());
1002 0 : return false;
1003 : }
1004 2 : double dfDTMin = 0;
1005 2 : double dfDTMax = 0;
1006 : #define setDTMinMax(ctype) \
1007 : do \
1008 : { \
1009 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1010 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1011 : } while (0)
1012 :
1013 2 : switch (eAutoScaleType)
1014 : {
1015 0 : case GDT_Byte:
1016 0 : setDTMinMax(GByte);
1017 0 : break;
1018 0 : case GDT_Int8:
1019 0 : setDTMinMax(GInt8);
1020 0 : break;
1021 1 : case GDT_UInt16:
1022 1 : setDTMinMax(GUInt16);
1023 1 : break;
1024 1 : case GDT_Int16:
1025 1 : setDTMinMax(GInt16);
1026 1 : break;
1027 0 : case GDT_UInt32:
1028 0 : setDTMinMax(GUInt32);
1029 0 : break;
1030 0 : case GDT_Int32:
1031 0 : setDTMinMax(GInt32);
1032 0 : break;
1033 0 : case GDT_UInt64:
1034 0 : setDTMinMax(std::uint64_t);
1035 0 : break;
1036 0 : case GDT_Int64:
1037 0 : setDTMinMax(std::int64_t);
1038 0 : break;
1039 0 : case GDT_Float16:
1040 : case GDT_Float32:
1041 : case GDT_Float64:
1042 : case GDT_Unknown:
1043 : case GDT_CInt16:
1044 : case GDT_CInt32:
1045 : case GDT_CFloat16:
1046 : case GDT_CFloat32:
1047 : case GDT_CFloat64:
1048 : case GDT_TypeCount:
1049 0 : CPLAssert(false);
1050 : }
1051 :
1052 : dstArray =
1053 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1054 4 : GDALExtendedDataType::Create(eAutoScaleType),
1055 4 : aosArrayCO.List());
1056 2 : if (!dstArray)
1057 0 : return !bStrict;
1058 :
1059 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1060 : {
1061 : // If there's a nodata value in the source array, reserve
1062 : // DTMax for that purpose in the target scaled array
1063 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1064 : {
1065 0 : CPLError(CE_Failure, CPLE_AppDefined,
1066 : "Cannot set nodata value");
1067 0 : return false;
1068 : }
1069 1 : dfDTMax--;
1070 : }
1071 2 : const double dfScale =
1072 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1073 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1074 :
1075 4 : if (!dstArray->SetOffset(dfOffset) ||
1076 2 : !dstArray->SetScale(dfScale))
1077 : {
1078 0 : CPLError(CE_Failure, CPLE_AppDefined,
1079 : "Cannot set scale/offset");
1080 0 : return false;
1081 : }
1082 :
1083 2 : auto poUnscaled = dstArray->GetUnscaled();
1084 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1085 : {
1086 1 : poUnscaled->SetNoDataValue(
1087 : srcArray->GetNoDataValueAsDouble());
1088 : }
1089 :
1090 : // Copy source array into unscaled array
1091 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1092 : nCurCost, nTotalCost, pfnProgress,
1093 2 : pProgressData))
1094 : {
1095 0 : return false;
1096 : }
1097 : }
1098 : else
1099 : {
1100 74 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1101 74 : srcArrayType, aosArrayCO.List());
1102 37 : if (!dstArray)
1103 0 : return !bStrict;
1104 :
1105 74 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1106 : nCurCost, nTotalCost, pfnProgress,
1107 37 : pProgressData))
1108 : {
1109 0 : return false;
1110 : }
1111 : }
1112 :
1113 : // If this array is the indexing variable of a dimension, link them
1114 : // together.
1115 39 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1116 : {
1117 : auto oCorrespondingDimIter =
1118 16 : mapExistingDstDims.find(oIterDimName->second);
1119 16 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1120 : {
1121 : CPLErrorStateBackuper oErrorStateBackuper(
1122 16 : CPLQuietErrorHandler);
1123 32 : oCorrespondingDimIter->second->SetIndexingVariable(
1124 16 : std::move(dstArray));
1125 : }
1126 : }
1127 :
1128 39 : return true;
1129 22 : };
1130 :
1131 44 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1132 :
1133 : // Start by copying arrays that are indexing variables of dimensions
1134 61 : for (const auto &name : arrayNames)
1135 : {
1136 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1137 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1138 :
1139 39 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1140 39 : srcArray->GetName()))
1141 : {
1142 16 : if (!CopyArray(srcArray))
1143 0 : return false;
1144 : }
1145 : }
1146 :
1147 : // Then copy regular arrays
1148 61 : for (const auto &name : arrayNames)
1149 : {
1150 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1151 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1152 :
1153 39 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1154 39 : srcArray->GetName()))
1155 : {
1156 23 : if (!CopyArray(srcArray))
1157 0 : return false;
1158 : }
1159 : }
1160 :
1161 44 : const auto groupNames = poSrcGroup->GetGroupNames();
1162 26 : for (const auto &name : groupNames)
1163 : {
1164 4 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1165 4 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1166 4 : auto dstSubGroup = CreateGroup(name);
1167 4 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1168 8 : if (!dstSubGroup->CopyFrom(
1169 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1170 4 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1171 0 : return false;
1172 : }
1173 :
1174 22 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1175 0 : return false;
1176 :
1177 22 : return true;
1178 : }
1179 0 : catch (const std::exception &e)
1180 : {
1181 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1182 0 : return false;
1183 : }
1184 : }
1185 :
1186 : /************************************************************************/
1187 : /* GetInnerMostGroup() */
1188 : /************************************************************************/
1189 :
1190 : //! @cond Doxygen_Suppress
1191 : const GDALGroup *
1192 1181 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1193 : std::shared_ptr<GDALGroup> &curGroupHolder,
1194 : std::string &osLastPart) const
1195 : {
1196 1181 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1197 6 : return nullptr;
1198 1175 : const GDALGroup *poCurGroup = this;
1199 : CPLStringList aosTokens(
1200 2350 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1201 1175 : if (aosTokens.size() == 0)
1202 : {
1203 0 : return nullptr;
1204 : }
1205 :
1206 1512 : for (int i = 0; i < aosTokens.size() - 1; i++)
1207 : {
1208 345 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1209 345 : if (!curGroupHolder)
1210 : {
1211 8 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1212 : aosTokens[i]);
1213 8 : return nullptr;
1214 : }
1215 337 : poCurGroup = curGroupHolder.get();
1216 : }
1217 1167 : osLastPart = aosTokens[aosTokens.size() - 1];
1218 1167 : return poCurGroup;
1219 : }
1220 :
1221 : //! @endcond
1222 :
1223 : /************************************************************************/
1224 : /* OpenMDArrayFromFullname() */
1225 : /************************************************************************/
1226 :
1227 : /** Get an array from its fully qualified name */
1228 : std::shared_ptr<GDALMDArray>
1229 487 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1230 : CSLConstList papszOptions) const
1231 : {
1232 974 : std::string osName;
1233 487 : std::shared_ptr<GDALGroup> curGroupHolder;
1234 487 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1235 487 : if (poGroup == nullptr)
1236 10 : return nullptr;
1237 477 : return poGroup->OpenMDArray(osName, papszOptions);
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* ResolveMDArray() */
1242 : /************************************************************************/
1243 :
1244 : /** Locate an array in a group and its subgroups by name.
1245 : *
1246 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1247 : * used
1248 : * Otherwise the search will start from the group identified by osStartingPath,
1249 : * and an array whose name is osName will be looked for in this group (if
1250 : * osStartingPath is empty or "/", then the current group is used). If there
1251 : * is no match, then a recursive descendent search will be made in its
1252 : * subgroups. If there is no match in the subgroups, then the parent (if
1253 : * existing) of the group pointed by osStartingPath will be used as the new
1254 : * starting point for the search.
1255 : *
1256 : * @param osName name, qualified or not
1257 : * @param osStartingPath fully qualified name of the (sub-)group from which
1258 : * the search should be started. If this is a non-empty
1259 : * string, the group on which this method is called should
1260 : * nominally be the root group (otherwise the path will
1261 : * be interpreted as from the current group)
1262 : * @param papszOptions options to pass to OpenMDArray()
1263 : * @since GDAL 3.2
1264 : */
1265 : std::shared_ptr<GDALMDArray>
1266 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1267 : const std::string &osStartingPath,
1268 : CSLConstList papszOptions) const
1269 : {
1270 19 : if (!osName.empty() && osName[0] == '/')
1271 : {
1272 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1273 1 : if (poArray)
1274 1 : return poArray;
1275 : }
1276 36 : std::string osPath(osStartingPath);
1277 36 : std::set<std::string> oSetAlreadyVisited;
1278 :
1279 : while (true)
1280 : {
1281 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1282 0 : std::shared_ptr<GDALGroup> poGroup;
1283 :
1284 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1285 22 : bool goOn = false;
1286 22 : if (osPath.empty() || osPath == "/")
1287 : {
1288 11 : goOn = true;
1289 : }
1290 : else
1291 : {
1292 22 : std::string osLastPart;
1293 : const GDALGroup *poGroupPtr =
1294 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1295 11 : if (poGroupPtr)
1296 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1297 22 : if (poGroup &&
1298 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1299 : {
1300 11 : oQueue.push(poGroup);
1301 11 : goOn = true;
1302 : }
1303 : }
1304 :
1305 22 : if (goOn)
1306 : {
1307 17 : do
1308 : {
1309 : const GDALGroup *groupPtr;
1310 39 : if (!oQueue.empty())
1311 : {
1312 28 : poGroup = oQueue.front();
1313 28 : oQueue.pop();
1314 28 : groupPtr = poGroup.get();
1315 : }
1316 : else
1317 : {
1318 11 : groupPtr = this;
1319 : }
1320 :
1321 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1322 39 : if (poArray)
1323 16 : return poArray;
1324 :
1325 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1326 47 : for (const auto &osGroupName : aosGroupNames)
1327 : {
1328 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1329 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1330 48 : poSubGroup->GetFullName()))
1331 : {
1332 24 : oQueue.push(poSubGroup);
1333 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1334 : }
1335 : }
1336 23 : } while (!oQueue.empty());
1337 : }
1338 :
1339 6 : if (osPath.empty() || osPath == "/")
1340 2 : break;
1341 :
1342 4 : const auto nPos = osPath.rfind('/');
1343 4 : if (nPos == 0)
1344 1 : osPath = "/";
1345 : else
1346 : {
1347 3 : if (nPos == std::string::npos)
1348 0 : break;
1349 3 : osPath.resize(nPos);
1350 : }
1351 4 : }
1352 2 : return nullptr;
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* OpenGroupFromFullname() */
1357 : /************************************************************************/
1358 :
1359 : /** Get a group from its fully qualified name.
1360 : * @since GDAL 3.2
1361 : */
1362 : std::shared_ptr<GDALGroup>
1363 558 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1364 : CSLConstList papszOptions) const
1365 : {
1366 1116 : std::string osName;
1367 558 : std::shared_ptr<GDALGroup> curGroupHolder;
1368 558 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1369 558 : if (poGroup == nullptr)
1370 2 : return nullptr;
1371 556 : return poGroup->OpenGroup(osName, papszOptions);
1372 : }
1373 :
1374 : /************************************************************************/
1375 : /* OpenDimensionFromFullname() */
1376 : /************************************************************************/
1377 :
1378 : /** Get a dimension from its fully qualified name */
1379 : std::shared_ptr<GDALDimension>
1380 125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1381 : {
1382 250 : std::string osName;
1383 125 : std::shared_ptr<GDALGroup> curGroupHolder;
1384 125 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1385 125 : if (poGroup == nullptr)
1386 2 : return nullptr;
1387 246 : auto dims(poGroup->GetDimensions());
1388 203 : for (auto &dim : dims)
1389 : {
1390 164 : if (dim->GetName() == osName)
1391 84 : return dim;
1392 : }
1393 39 : return nullptr;
1394 : }
1395 :
1396 : /************************************************************************/
1397 : /* ClearStatistics() */
1398 : /************************************************************************/
1399 :
1400 : /**
1401 : * \brief Clear statistics.
1402 : *
1403 : * @since GDAL 3.4
1404 : */
1405 0 : void GDALGroup::ClearStatistics()
1406 : {
1407 0 : auto groupNames = GetGroupNames();
1408 0 : for (const auto &name : groupNames)
1409 : {
1410 0 : auto subGroup = OpenGroup(name);
1411 0 : if (subGroup)
1412 : {
1413 0 : subGroup->ClearStatistics();
1414 : }
1415 : }
1416 :
1417 0 : auto arrayNames = GetMDArrayNames();
1418 0 : for (const auto &name : arrayNames)
1419 : {
1420 0 : auto array = OpenMDArray(name);
1421 0 : if (array)
1422 : {
1423 0 : array->ClearStatistics();
1424 : }
1425 : }
1426 0 : }
1427 :
1428 : /************************************************************************/
1429 : /* Rename() */
1430 : /************************************************************************/
1431 :
1432 : /** Rename the group.
1433 : *
1434 : * This is not implemented by all drivers.
1435 : *
1436 : * Drivers known to implement it: MEM, netCDF, ZARR.
1437 : *
1438 : * This is the same as the C function GDALGroupRename().
1439 : *
1440 : * @param osNewName New name.
1441 : *
1442 : * @return true in case of success
1443 : * @since GDAL 3.8
1444 : */
1445 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1446 : {
1447 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1448 0 : return false;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* BaseRename() */
1453 : /************************************************************************/
1454 :
1455 : //! @cond Doxygen_Suppress
1456 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1457 : {
1458 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1459 8 : m_osFullName += osNewName;
1460 8 : m_osName = osNewName;
1461 :
1462 8 : NotifyChildrenOfRenaming();
1463 8 : }
1464 :
1465 : //! @endcond
1466 :
1467 : /************************************************************************/
1468 : /* ParentRenamed() */
1469 : /************************************************************************/
1470 :
1471 : //! @cond Doxygen_Suppress
1472 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1473 : {
1474 7 : m_osFullName = osNewParentFullName;
1475 7 : m_osFullName += "/";
1476 7 : m_osFullName += m_osName;
1477 :
1478 7 : NotifyChildrenOfRenaming();
1479 7 : }
1480 :
1481 : //! @endcond
1482 :
1483 : /************************************************************************/
1484 : /* Deleted() */
1485 : /************************************************************************/
1486 :
1487 : //! @cond Doxygen_Suppress
1488 22 : void GDALGroup::Deleted()
1489 : {
1490 22 : m_bValid = false;
1491 :
1492 22 : NotifyChildrenOfDeletion();
1493 22 : }
1494 :
1495 : //! @endcond
1496 :
1497 : /************************************************************************/
1498 : /* ParentDeleted() */
1499 : /************************************************************************/
1500 :
1501 : //! @cond Doxygen_Suppress
1502 3 : void GDALGroup::ParentDeleted()
1503 : {
1504 3 : Deleted();
1505 3 : }
1506 :
1507 : //! @endcond
1508 :
1509 : /************************************************************************/
1510 : /* CheckValidAndErrorOutIfNot() */
1511 : /************************************************************************/
1512 :
1513 : //! @cond Doxygen_Suppress
1514 12173 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1515 : {
1516 12173 : if (!m_bValid)
1517 : {
1518 14 : CPLError(CE_Failure, CPLE_AppDefined,
1519 : "This object has been deleted. No action on it is possible");
1520 : }
1521 12173 : return m_bValid;
1522 : }
1523 :
1524 : //! @endcond
1525 :
1526 : /************************************************************************/
1527 : /* ~GDALAbstractMDArray() */
1528 : /************************************************************************/
1529 :
1530 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1531 :
1532 : /************************************************************************/
1533 : /* GDALAbstractMDArray() */
1534 : /************************************************************************/
1535 :
1536 : //! @cond Doxygen_Suppress
1537 20254 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1538 20254 : const std::string &osName)
1539 : : m_osName(osName),
1540 : m_osFullName(
1541 20254 : !osParentName.empty()
1542 38869 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1543 79377 : : osName)
1544 : {
1545 20254 : }
1546 :
1547 : //! @endcond
1548 :
1549 : /************************************************************************/
1550 : /* GetDimensions() */
1551 : /************************************************************************/
1552 :
1553 : /** \fn GDALAbstractMDArray::GetDimensions() const
1554 : * \brief Return the dimensions of an attribute/array.
1555 : *
1556 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1557 : * similar to GDALAttributeGetDimensionsSize().
1558 : */
1559 :
1560 : /************************************************************************/
1561 : /* GetDataType() */
1562 : /************************************************************************/
1563 :
1564 : /** \fn GDALAbstractMDArray::GetDataType() const
1565 : * \brief Return the data type of an attribute/array.
1566 : *
1567 : * This is the same as the C functions GDALMDArrayGetDataType() and
1568 : * GDALAttributeGetDataType()
1569 : */
1570 :
1571 : /************************************************************************/
1572 : /* GetDimensionCount() */
1573 : /************************************************************************/
1574 :
1575 : /** Return the number of dimensions.
1576 : *
1577 : * Default implementation is GetDimensions().size(), and may be overridden by
1578 : * drivers if they have a faster / less expensive implementations.
1579 : *
1580 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1581 : * GDALAttributeGetDimensionCount().
1582 : *
1583 : */
1584 22593 : size_t GDALAbstractMDArray::GetDimensionCount() const
1585 : {
1586 22593 : return GetDimensions().size();
1587 : }
1588 :
1589 : /************************************************************************/
1590 : /* Rename() */
1591 : /************************************************************************/
1592 :
1593 : /** Rename the attribute/array.
1594 : *
1595 : * This is not implemented by all drivers.
1596 : *
1597 : * Drivers known to implement it: MEM, netCDF, Zarr.
1598 : *
1599 : * This is the same as the C functions GDALMDArrayRename() or
1600 : * GDALAttributeRename().
1601 : *
1602 : * @param osNewName New name.
1603 : *
1604 : * @return true in case of success
1605 : * @since GDAL 3.8
1606 : */
1607 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1608 : {
1609 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1610 0 : return false;
1611 : }
1612 :
1613 : /************************************************************************/
1614 : /* CopyValue() */
1615 : /************************************************************************/
1616 :
1617 : /** Convert a value from a source type to a destination type.
1618 : *
1619 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1620 : * that must be freed with CPLFree().
1621 : */
1622 78860 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1623 : const GDALExtendedDataType &srcType,
1624 : void *pDst,
1625 : const GDALExtendedDataType &dstType)
1626 : {
1627 154409 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1628 75549 : dstType.GetClass() == GEDTC_NUMERIC)
1629 : {
1630 75342 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1631 : dstType.GetNumericDataType(), 0, 1);
1632 75342 : return true;
1633 : }
1634 6654 : if (srcType.GetClass() == GEDTC_STRING &&
1635 3136 : dstType.GetClass() == GEDTC_STRING)
1636 : {
1637 : const char *srcStrPtr;
1638 2748 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1639 2748 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1640 2748 : *reinterpret_cast<void **>(pDst) = pszDup;
1641 2748 : return true;
1642 : }
1643 977 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1644 207 : dstType.GetClass() == GEDTC_STRING)
1645 : {
1646 207 : const char *str = nullptr;
1647 207 : switch (srcType.GetNumericDataType())
1648 : {
1649 0 : case GDT_Unknown:
1650 0 : break;
1651 0 : case GDT_Byte:
1652 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1653 0 : break;
1654 3 : case GDT_Int8:
1655 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1656 3 : break;
1657 48 : case GDT_UInt16:
1658 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1659 48 : break;
1660 0 : case GDT_Int16:
1661 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1662 0 : break;
1663 8 : case GDT_UInt32:
1664 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1665 8 : break;
1666 54 : case GDT_Int32:
1667 54 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1668 54 : break;
1669 0 : case GDT_UInt64:
1670 : str =
1671 0 : CPLSPrintf(CPL_FRMT_GUIB,
1672 : static_cast<GUIntBig>(
1673 : *static_cast<const std::uint64_t *>(pSrc)));
1674 0 : break;
1675 0 : case GDT_Int64:
1676 0 : str = CPLSPrintf(CPL_FRMT_GIB,
1677 : static_cast<GIntBig>(
1678 : *static_cast<const std::int64_t *>(pSrc)));
1679 0 : break;
1680 0 : case GDT_Float16:
1681 0 : str = CPLSPrintf("%.5g",
1682 : double(*static_cast<const GFloat16 *>(pSrc)));
1683 0 : break;
1684 17 : case GDT_Float32:
1685 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1686 17 : break;
1687 75 : case GDT_Float64:
1688 75 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1689 75 : break;
1690 2 : case GDT_CInt16:
1691 : {
1692 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1693 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1694 2 : break;
1695 : }
1696 0 : case GDT_CInt32:
1697 : {
1698 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1699 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1700 0 : break;
1701 : }
1702 0 : case GDT_CFloat16:
1703 : {
1704 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1705 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1706 0 : break;
1707 : }
1708 0 : case GDT_CFloat32:
1709 : {
1710 0 : const float *src = static_cast<const float *>(pSrc);
1711 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1712 0 : break;
1713 : }
1714 0 : case GDT_CFloat64:
1715 : {
1716 0 : const double *src = static_cast<const double *>(pSrc);
1717 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1718 0 : break;
1719 : }
1720 0 : case GDT_TypeCount:
1721 0 : CPLAssert(false);
1722 : break;
1723 : }
1724 207 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1725 207 : *reinterpret_cast<void **>(pDst) = pszDup;
1726 207 : return true;
1727 : }
1728 951 : if (srcType.GetClass() == GEDTC_STRING &&
1729 388 : dstType.GetClass() == GEDTC_NUMERIC)
1730 : {
1731 : const char *srcStrPtr;
1732 388 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1733 388 : if (dstType.GetNumericDataType() == GDT_Int64)
1734 : {
1735 2 : *(static_cast<int64_t *>(pDst)) =
1736 2 : srcStrPtr == nullptr ? 0
1737 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1738 : }
1739 386 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1740 : {
1741 2 : *(static_cast<uint64_t *>(pDst)) =
1742 2 : srcStrPtr == nullptr
1743 2 : ? 0
1744 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1745 : }
1746 : else
1747 : {
1748 : // FIXME GDT_UInt64
1749 384 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1750 384 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1751 : dstType.GetNumericDataType(), 0, 1);
1752 : }
1753 388 : return true;
1754 : }
1755 350 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1756 175 : dstType.GetClass() == GEDTC_COMPOUND)
1757 : {
1758 175 : const auto &srcComponents = srcType.GetComponents();
1759 175 : const auto &dstComponents = dstType.GetComponents();
1760 175 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1761 175 : GByte *pabyDst = static_cast<GByte *>(pDst);
1762 :
1763 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1764 350 : srcComponentMap;
1765 688 : for (const auto &srcComp : srcComponents)
1766 : {
1767 513 : srcComponentMap[srcComp->GetName()] = &srcComp;
1768 : }
1769 504 : for (const auto &dstComp : dstComponents)
1770 : {
1771 329 : auto oIter = srcComponentMap.find(dstComp->GetName());
1772 329 : if (oIter == srcComponentMap.end())
1773 0 : return false;
1774 329 : const auto &srcComp = *(oIter->second);
1775 987 : if (!GDALExtendedDataType::CopyValue(
1776 329 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1777 329 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1778 : {
1779 0 : return false;
1780 : }
1781 : }
1782 175 : return true;
1783 : }
1784 :
1785 0 : return false;
1786 : }
1787 :
1788 : /************************************************************************/
1789 : /* CopyValues() */
1790 : /************************************************************************/
1791 :
1792 : /** Convert severals value from a source type to a destination type.
1793 : *
1794 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1795 : * that must be freed with CPLFree().
1796 : */
1797 328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1798 : const GDALExtendedDataType &srcType,
1799 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1800 : const GDALExtendedDataType &dstType,
1801 : GPtrDiff_t nDstStrideInElts,
1802 : size_t nValues)
1803 : {
1804 : const auto nSrcStrideInBytes =
1805 328 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1806 : const auto nDstStrideInBytes =
1807 328 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1808 594 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1809 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1810 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1811 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1812 860 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1813 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1814 : {
1815 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1816 : static_cast<int>(nSrcStrideInBytes), pDst,
1817 : dstType.GetNumericDataType(),
1818 : static_cast<int>(nDstStrideInBytes), nValues);
1819 : }
1820 : else
1821 : {
1822 62 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1823 62 : GByte *pabyDst = static_cast<GByte *>(pDst);
1824 124 : for (size_t i = 0; i < nValues; ++i)
1825 : {
1826 62 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1827 0 : return false;
1828 62 : pabySrc += nSrcStrideInBytes;
1829 62 : pabyDst += nDstStrideInBytes;
1830 : }
1831 : }
1832 328 : return true;
1833 : }
1834 :
1835 : /************************************************************************/
1836 : /* CheckReadWriteParams() */
1837 : /************************************************************************/
1838 : //! @cond Doxygen_Suppress
1839 7897 : bool GDALAbstractMDArray::CheckReadWriteParams(
1840 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1841 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1842 : const void *buffer, const void *buffer_alloc_start,
1843 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1844 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1845 : {
1846 0 : const auto lamda_error = []()
1847 : {
1848 0 : CPLError(CE_Failure, CPLE_AppDefined,
1849 : "Not all elements pointed by buffer will fit in "
1850 : "[buffer_alloc_start, "
1851 : "buffer_alloc_start + buffer_alloc_size]");
1852 0 : };
1853 :
1854 7897 : const auto &dims = GetDimensions();
1855 7897 : if (dims.empty())
1856 : {
1857 3041 : if (buffer_alloc_start)
1858 : {
1859 2668 : const size_t elementSize = bufferDataType.GetSize();
1860 2668 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1861 2668 : const GByte *paby_buffer_alloc_start =
1862 : static_cast<const GByte *>(buffer_alloc_start);
1863 2668 : const GByte *paby_buffer_alloc_end =
1864 : paby_buffer_alloc_start + buffer_alloc_size;
1865 :
1866 2668 : if (paby_buffer < paby_buffer_alloc_start ||
1867 2668 : paby_buffer + elementSize > paby_buffer_alloc_end)
1868 : {
1869 0 : lamda_error();
1870 0 : return false;
1871 : }
1872 : }
1873 3041 : return true;
1874 : }
1875 :
1876 4856 : if (arrayStep == nullptr)
1877 : {
1878 1273 : tmp_arrayStep.resize(dims.size(), 1);
1879 1273 : arrayStep = tmp_arrayStep.data();
1880 : }
1881 13741 : for (size_t i = 0; i < dims.size(); i++)
1882 : {
1883 8885 : if (count[i] == 0)
1884 : {
1885 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1886 : static_cast<unsigned>(i));
1887 0 : return false;
1888 : }
1889 : }
1890 4856 : bool bufferStride_all_positive = true;
1891 4856 : if (bufferStride == nullptr)
1892 : {
1893 985 : GPtrDiff_t stride = 1;
1894 : // To compute strides we must proceed from the fastest varying dimension
1895 : // (the last one), and then reverse the result
1896 2241 : for (size_t i = dims.size(); i != 0;)
1897 : {
1898 1256 : --i;
1899 1256 : tmp_bufferStride.push_back(stride);
1900 1256 : GUInt64 newStride = 0;
1901 : bool bOK;
1902 : try
1903 : {
1904 1256 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1905 2512 : CPLSM(static_cast<uint64_t>(count[i])))
1906 1256 : .v();
1907 1256 : bOK = static_cast<size_t>(newStride) == newStride &&
1908 1256 : newStride < std::numeric_limits<size_t>::max() / 2;
1909 : }
1910 0 : catch (...)
1911 : {
1912 0 : bOK = false;
1913 : }
1914 1256 : if (!bOK)
1915 : {
1916 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1917 0 : return false;
1918 : }
1919 1256 : stride = static_cast<GPtrDiff_t>(newStride);
1920 : }
1921 985 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1922 985 : bufferStride = tmp_bufferStride.data();
1923 : }
1924 : else
1925 : {
1926 11498 : for (size_t i = 0; i < dims.size(); i++)
1927 : {
1928 7628 : if (bufferStride[i] < 0)
1929 : {
1930 1 : bufferStride_all_positive = false;
1931 1 : break;
1932 : }
1933 : }
1934 : }
1935 13712 : for (size_t i = 0; i < dims.size(); i++)
1936 : {
1937 8866 : if (arrayStartIdx[i] >= dims[i]->GetSize())
1938 : {
1939 2 : CPLError(CE_Failure, CPLE_AppDefined,
1940 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1941 : static_cast<unsigned>(i),
1942 2 : static_cast<GUInt64>(arrayStartIdx[i]),
1943 2 : static_cast<GUInt64>(dims[i]->GetSize()));
1944 2 : return false;
1945 : }
1946 : bool bOverflow;
1947 8864 : if (arrayStep[i] >= 0)
1948 : {
1949 : try
1950 : {
1951 8270 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
1952 8272 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
1953 33083 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
1954 8270 : .v() >= dims[i]->GetSize();
1955 : }
1956 1 : catch (...)
1957 : {
1958 1 : bOverflow = true;
1959 : }
1960 8271 : if (bOverflow)
1961 : {
1962 5 : CPLError(CE_Failure, CPLE_AppDefined,
1963 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
1964 : ">= " CPL_FRMT_GUIB,
1965 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1966 : static_cast<unsigned>(i),
1967 5 : static_cast<GUInt64>(dims[i]->GetSize()));
1968 5 : return false;
1969 : }
1970 : }
1971 : else
1972 : {
1973 : try
1974 : {
1975 593 : bOverflow =
1976 593 : arrayStartIdx[i] <
1977 593 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
1978 1186 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
1979 : ? (static_cast<uint64_t>(1) << 63)
1980 1186 : : static_cast<uint64_t>(-arrayStep[i])))
1981 593 : .v();
1982 : }
1983 0 : catch (...)
1984 : {
1985 0 : bOverflow = true;
1986 : }
1987 593 : if (bOverflow)
1988 : {
1989 3 : CPLError(
1990 : CE_Failure, CPLE_AppDefined,
1991 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
1992 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1993 : static_cast<unsigned>(i));
1994 3 : return false;
1995 : }
1996 : }
1997 : }
1998 :
1999 4846 : if (buffer_alloc_start)
2000 : {
2001 2539 : const size_t elementSize = bufferDataType.GetSize();
2002 2539 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2003 2539 : const GByte *paby_buffer_alloc_start =
2004 : static_cast<const GByte *>(buffer_alloc_start);
2005 2539 : const GByte *paby_buffer_alloc_end =
2006 : paby_buffer_alloc_start + buffer_alloc_size;
2007 2539 : if (bufferStride_all_positive)
2008 : {
2009 2539 : if (paby_buffer < paby_buffer_alloc_start)
2010 : {
2011 0 : lamda_error();
2012 0 : return false;
2013 : }
2014 2539 : GUInt64 nOffset = elementSize;
2015 7325 : for (size_t i = 0; i < dims.size(); i++)
2016 : {
2017 : try
2018 : {
2019 4786 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2020 4786 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2021 9572 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2022 19144 : CPLSM(static_cast<uint64_t>(elementSize)))
2023 4786 : .v();
2024 : }
2025 0 : catch (...)
2026 : {
2027 0 : lamda_error();
2028 0 : return false;
2029 : }
2030 : }
2031 : #if SIZEOF_VOIDP == 4
2032 : if (static_cast<size_t>(nOffset) != nOffset)
2033 : {
2034 : lamda_error();
2035 : return false;
2036 : }
2037 : #endif
2038 2539 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2039 : {
2040 0 : lamda_error();
2041 0 : return false;
2042 : }
2043 : }
2044 0 : else if (dims.size() < 31)
2045 : {
2046 : // Check all corners of the hypercube
2047 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2048 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2049 : {
2050 0 : const GByte *paby = paby_buffer;
2051 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2052 : i++)
2053 : {
2054 0 : if (iCornerCode & (1U << i))
2055 : {
2056 : // We should check for integer overflows
2057 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2058 : }
2059 : }
2060 0 : if (paby < paby_buffer_alloc_start ||
2061 0 : paby + elementSize > paby_buffer_alloc_end)
2062 : {
2063 0 : lamda_error();
2064 0 : return false;
2065 : }
2066 : }
2067 : }
2068 : }
2069 :
2070 4846 : return true;
2071 : }
2072 :
2073 : //! @endcond
2074 :
2075 : /************************************************************************/
2076 : /* Read() */
2077 : /************************************************************************/
2078 :
2079 : /** Read part or totality of a multidimensional array or attribute.
2080 : *
2081 : * This will extract the content of a hyper-rectangle from the array into
2082 : * a user supplied buffer.
2083 : *
2084 : * If bufferDataType is of type string, the values written in pDstBuffer
2085 : * will be char* pointers and the strings should be freed with CPLFree().
2086 : *
2087 : * This is the same as the C function GDALMDArrayRead().
2088 : *
2089 : * @param arrayStartIdx Values representing the starting index to read
2090 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2091 : * Array of GetDimensionCount() values. Must not be
2092 : * nullptr, unless for a zero-dimensional array.
2093 : *
2094 : * @param count Values representing the number of values to extract in
2095 : * each dimension.
2096 : * Array of GetDimensionCount() values. Must not be
2097 : * nullptr, unless for a zero-dimensional array.
2098 : *
2099 : * @param arrayStep Spacing between values to extract in each dimension.
2100 : * The spacing is in number of array elements, not bytes.
2101 : * If provided, must contain GetDimensionCount() values.
2102 : * If set to nullptr, [1, 1, ... 1] will be used as a
2103 : * default to indicate consecutive elements.
2104 : *
2105 : * @param bufferStride Spacing between values to store in pDstBuffer.
2106 : * The spacing is in number of array elements, not bytes.
2107 : * If provided, must contain GetDimensionCount() values.
2108 : * Negative values are possible (for example to reorder
2109 : * from bottom-to-top to top-to-bottom).
2110 : * If set to nullptr, will be set so that pDstBuffer is
2111 : * written in a compact way, with elements of the last /
2112 : * fastest varying dimension being consecutive.
2113 : *
2114 : * @param bufferDataType Data type of values in pDstBuffer.
2115 : *
2116 : * @param pDstBuffer User buffer to store the values read. Should be big
2117 : * enough to store the number of values indicated by
2118 : * count[] and with the spacing of bufferStride[].
2119 : *
2120 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2121 : * validity of pDstBuffer. pDstBufferAllocStart
2122 : * should be the pointer returned by the malloc() or equivalent call used to
2123 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2124 : * bufferStride[] values are all positive), but not necessarily. If specified,
2125 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2126 : * validation is needed, nullptr can be passed.
2127 : *
2128 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2129 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2130 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2131 : * set to the appropriate value.
2132 : * If no validation is needed, 0 can be passed.
2133 : *
2134 : * @return true in case of success.
2135 : */
2136 2345 : bool GDALAbstractMDArray::Read(
2137 : const GUInt64 *arrayStartIdx, const size_t *count,
2138 : const GInt64 *arrayStep, // step in elements
2139 : const GPtrDiff_t *bufferStride, // stride in elements
2140 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2141 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2142 : {
2143 2345 : if (!GetDataType().CanConvertTo(bufferDataType))
2144 : {
2145 0 : CPLError(CE_Failure, CPLE_AppDefined,
2146 : "Array data type is not convertible to buffer data type");
2147 0 : return false;
2148 : }
2149 :
2150 4690 : std::vector<GInt64> tmp_arrayStep;
2151 4690 : std::vector<GPtrDiff_t> tmp_bufferStride;
2152 2345 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2153 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2154 : nDstBufferAllocSize, tmp_arrayStep,
2155 : tmp_bufferStride))
2156 : {
2157 0 : return false;
2158 : }
2159 :
2160 2345 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2161 2345 : pDstBuffer);
2162 : }
2163 :
2164 : /************************************************************************/
2165 : /* IWrite() */
2166 : /************************************************************************/
2167 :
2168 : //! @cond Doxygen_Suppress
2169 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2170 : const GInt64 *, const GPtrDiff_t *,
2171 : const GDALExtendedDataType &, const void *)
2172 : {
2173 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2174 1 : return false;
2175 : }
2176 :
2177 : //! @endcond
2178 :
2179 : /************************************************************************/
2180 : /* Write() */
2181 : /************************************************************************/
2182 :
2183 : /** Write part or totality of a multidimensional array or attribute.
2184 : *
2185 : * This will set the content of a hyper-rectangle into the array from
2186 : * a user supplied buffer.
2187 : *
2188 : * If bufferDataType is of type string, the values read from pSrcBuffer
2189 : * will be char* pointers.
2190 : *
2191 : * This is the same as the C function GDALMDArrayWrite().
2192 : *
2193 : * @param arrayStartIdx Values representing the starting index to write
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 write 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 write 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 read from pSrcBuffer.
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 pSrcBuffer 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 pSrcBuffer.
2219 : *
2220 : * @param pSrcBuffer User buffer to read the values from. Should be big
2221 : * enough to store the number of values indicated by
2222 : * count[] and with the spacing of bufferStride[].
2223 : *
2224 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2225 : * validity of pSrcBuffer. pSrcBufferAllocStart
2226 : * should be the pointer returned by the malloc() or equivalent call used to
2227 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2228 : * bufferStride[] values are all positive), but not necessarily. If specified,
2229 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2230 : * validation is needed, nullptr can be passed.
2231 : *
2232 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2233 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2234 : * at pSrcBufferAllocStart. 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 1788 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2241 : const size_t *count, const GInt64 *arrayStep,
2242 : const GPtrDiff_t *bufferStride,
2243 : const GDALExtendedDataType &bufferDataType,
2244 : const void *pSrcBuffer,
2245 : const void *pSrcBufferAllocStart,
2246 : size_t nSrcBufferAllocSize)
2247 : {
2248 1788 : if (!bufferDataType.CanConvertTo(GetDataType()))
2249 : {
2250 0 : CPLError(CE_Failure, CPLE_AppDefined,
2251 : "Buffer data type is not convertible to array data type");
2252 0 : return false;
2253 : }
2254 :
2255 3576 : std::vector<GInt64> tmp_arrayStep;
2256 3576 : std::vector<GPtrDiff_t> tmp_bufferStride;
2257 1788 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2258 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2259 : nSrcBufferAllocSize, tmp_arrayStep,
2260 : tmp_bufferStride))
2261 : {
2262 0 : return false;
2263 : }
2264 :
2265 1788 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2266 1788 : pSrcBuffer);
2267 : }
2268 :
2269 : /************************************************************************/
2270 : /* GetTotalElementsCount() */
2271 : /************************************************************************/
2272 :
2273 : /** Return the total number of values in the array.
2274 : *
2275 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2276 : * and GDALAttributeGetTotalElementsCount().
2277 : *
2278 : */
2279 1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2280 : {
2281 1022 : const auto &dims = GetDimensions();
2282 1022 : if (dims.empty())
2283 504 : return 1;
2284 518 : GUInt64 nElts = 1;
2285 1146 : for (const auto &dim : dims)
2286 : {
2287 : try
2288 : {
2289 628 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2290 1884 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2291 628 : .v();
2292 : }
2293 0 : catch (...)
2294 : {
2295 0 : return 0;
2296 : }
2297 : }
2298 518 : return nElts;
2299 : }
2300 :
2301 : /************************************************************************/
2302 : /* GetBlockSize() */
2303 : /************************************************************************/
2304 :
2305 : /** Return the "natural" block size of the array along all dimensions.
2306 : *
2307 : * Some drivers might organize the array in tiles/blocks and reading/writing
2308 : * aligned on those tile/block boundaries will be more efficient.
2309 : *
2310 : * The returned number of elements in the vector is the same as
2311 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2312 : * the natural block size along the considered dimension.
2313 : * "Flat" arrays will typically return a vector of values set to 0.
2314 : *
2315 : * The default implementation will return a vector of values set to 0.
2316 : *
2317 : * This method is used by GetProcessingChunkSize().
2318 : *
2319 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2320 : * theoretical case of a 32-bit platform, this might exceed its size_t
2321 : * allocation capabilities.
2322 : *
2323 : * This is the same as the C function GDALMDArrayGetBlockSize().
2324 : *
2325 : * @return the block size, in number of elements along each dimension.
2326 : */
2327 221 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2328 : {
2329 221 : return std::vector<GUInt64>(GetDimensionCount());
2330 : }
2331 :
2332 : /************************************************************************/
2333 : /* GetProcessingChunkSize() */
2334 : /************************************************************************/
2335 :
2336 : /** \brief Return an optimal chunk size for read/write operations, given the
2337 : * natural block size and memory constraints specified.
2338 : *
2339 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2340 : * multiple of those returned by GetBlockSize() (unless the block define by
2341 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2342 : * returned by this method).
2343 : *
2344 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2345 : *
2346 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2347 : * chunk.
2348 : *
2349 : * @return the chunk size, in number of elements along each dimension.
2350 : */
2351 : std::vector<size_t>
2352 60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2353 : {
2354 60 : const auto &dims = GetDimensions();
2355 60 : const auto &nDTSize = GetDataType().GetSize();
2356 60 : std::vector<size_t> anChunkSize;
2357 120 : auto blockSize = GetBlockSize();
2358 60 : CPLAssert(blockSize.size() == dims.size());
2359 60 : size_t nChunkSize = nDTSize;
2360 60 : bool bOverflow = false;
2361 60 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2362 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2363 : // [1, min(sizet_max, dim_size[i])]
2364 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2365 173 : for (size_t i = 0; i < dims.size(); i++)
2366 : {
2367 : const auto sizeDimI =
2368 226 : std::max(static_cast<size_t>(1),
2369 226 : static_cast<size_t>(
2370 226 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2371 113 : std::min(blockSize[i], dims[i]->GetSize()))));
2372 113 : anChunkSize.push_back(sizeDimI);
2373 113 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2374 : {
2375 4 : bOverflow = true;
2376 : }
2377 : else
2378 : {
2379 109 : nChunkSize *= sizeDimI;
2380 : }
2381 : }
2382 60 : if (nChunkSize == 0)
2383 0 : return anChunkSize;
2384 :
2385 : // If the product of all anChunkSize[i] does not fit on size_t, then
2386 : // set lowest anChunkSize[i] to 1.
2387 60 : if (bOverflow)
2388 : {
2389 2 : nChunkSize = nDTSize;
2390 2 : bOverflow = false;
2391 8 : for (size_t i = dims.size(); i > 0;)
2392 : {
2393 6 : --i;
2394 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2395 : {
2396 4 : bOverflow = true;
2397 4 : anChunkSize[i] = 1;
2398 : }
2399 : else
2400 : {
2401 2 : nChunkSize *= anChunkSize[i];
2402 : }
2403 : }
2404 : }
2405 :
2406 60 : nChunkSize = nDTSize;
2407 120 : std::vector<size_t> anAccBlockSizeFromStart;
2408 173 : for (size_t i = 0; i < dims.size(); i++)
2409 : {
2410 113 : nChunkSize *= anChunkSize[i];
2411 113 : anAccBlockSizeFromStart.push_back(nChunkSize);
2412 : }
2413 60 : if (nChunkSize <= nMaxChunkMemory / 2)
2414 : {
2415 56 : size_t nVoxelsFromEnd = 1;
2416 161 : for (size_t i = dims.size(); i > 0;)
2417 : {
2418 105 : --i;
2419 : const auto nCurBlockSize =
2420 105 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2421 105 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2422 105 : if (nMul >= 2)
2423 : {
2424 97 : const auto nSizeThisDim(dims[i]->GetSize());
2425 : const auto nBlocksThisDim =
2426 97 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2427 97 : anChunkSize[i] = static_cast<size_t>(std::min(
2428 97 : anChunkSize[i] *
2429 194 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2430 97 : nSizeThisDim));
2431 : }
2432 105 : nVoxelsFromEnd *= anChunkSize[i];
2433 : }
2434 : }
2435 60 : return anChunkSize;
2436 : }
2437 :
2438 : /************************************************************************/
2439 : /* BaseRename() */
2440 : /************************************************************************/
2441 :
2442 : //! @cond Doxygen_Suppress
2443 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2444 : {
2445 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2446 18 : m_osFullName += osNewName;
2447 18 : m_osName = osNewName;
2448 :
2449 18 : NotifyChildrenOfRenaming();
2450 18 : }
2451 :
2452 : //! @endcond
2453 :
2454 : //! @cond Doxygen_Suppress
2455 : /************************************************************************/
2456 : /* ParentRenamed() */
2457 : /************************************************************************/
2458 :
2459 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2460 : {
2461 50 : m_osFullName = osNewParentFullName;
2462 50 : m_osFullName += "/";
2463 50 : m_osFullName += m_osName;
2464 :
2465 50 : NotifyChildrenOfRenaming();
2466 50 : }
2467 :
2468 : //! @endcond
2469 :
2470 : /************************************************************************/
2471 : /* Deleted() */
2472 : /************************************************************************/
2473 :
2474 : //! @cond Doxygen_Suppress
2475 52 : void GDALAbstractMDArray::Deleted()
2476 : {
2477 52 : m_bValid = false;
2478 :
2479 52 : NotifyChildrenOfDeletion();
2480 52 : }
2481 :
2482 : //! @endcond
2483 :
2484 : /************************************************************************/
2485 : /* ParentDeleted() */
2486 : /************************************************************************/
2487 :
2488 : //! @cond Doxygen_Suppress
2489 28 : void GDALAbstractMDArray::ParentDeleted()
2490 : {
2491 28 : Deleted();
2492 28 : }
2493 :
2494 : //! @endcond
2495 :
2496 : /************************************************************************/
2497 : /* CheckValidAndErrorOutIfNot() */
2498 : /************************************************************************/
2499 :
2500 : //! @cond Doxygen_Suppress
2501 5702 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2502 : {
2503 5702 : if (!m_bValid)
2504 : {
2505 26 : CPLError(CE_Failure, CPLE_AppDefined,
2506 : "This object has been deleted. No action on it is possible");
2507 : }
2508 5702 : return m_bValid;
2509 : }
2510 :
2511 : //! @endcond
2512 :
2513 : /************************************************************************/
2514 : /* SetUnit() */
2515 : /************************************************************************/
2516 :
2517 : /** Set the variable unit.
2518 : *
2519 : * Values should conform as much as possible with those allowed by
2520 : * the NetCDF CF conventions:
2521 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2522 : * but others might be returned.
2523 : *
2524 : * Few examples are "meter", "degrees", "second", ...
2525 : * Empty value means unknown.
2526 : *
2527 : * This is the same as the C function GDALMDArraySetUnit()
2528 : *
2529 : * @note Driver implementation: optionally implemented.
2530 : *
2531 : * @param osUnit unit name.
2532 : * @return true in case of success.
2533 : */
2534 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2535 : {
2536 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2537 0 : return false;
2538 : }
2539 :
2540 : /************************************************************************/
2541 : /* GetUnit() */
2542 : /************************************************************************/
2543 :
2544 : /** Return the array unit.
2545 : *
2546 : * Values should conform as much as possible with those allowed by
2547 : * the NetCDF CF conventions:
2548 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2549 : * but others might be returned.
2550 : *
2551 : * Few examples are "meter", "degrees", "second", ...
2552 : * Empty value means unknown.
2553 : *
2554 : * This is the same as the C function GDALMDArrayGetUnit()
2555 : */
2556 5 : const std::string &GDALMDArray::GetUnit() const
2557 : {
2558 5 : static const std::string emptyString;
2559 5 : return emptyString;
2560 : }
2561 :
2562 : /************************************************************************/
2563 : /* SetSpatialRef() */
2564 : /************************************************************************/
2565 :
2566 : /** Assign a spatial reference system object to the array.
2567 : *
2568 : * This is the same as the C function GDALMDArraySetSpatialRef().
2569 : */
2570 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2571 : {
2572 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2573 0 : return false;
2574 : }
2575 :
2576 : /************************************************************************/
2577 : /* GetSpatialRef() */
2578 : /************************************************************************/
2579 :
2580 : /** Return the spatial reference system object associated with the array.
2581 : *
2582 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2583 : */
2584 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2585 : {
2586 4 : return nullptr;
2587 : }
2588 :
2589 : /************************************************************************/
2590 : /* GetRawNoDataValue() */
2591 : /************************************************************************/
2592 :
2593 : /** Return the nodata value as a "raw" value.
2594 : *
2595 : * The value returned might be nullptr in case of no nodata value. When
2596 : * a nodata value is registered, a non-nullptr will be returned whose size in
2597 : * bytes is GetDataType().GetSize().
2598 : *
2599 : * The returned value should not be modified or freed. It is valid until
2600 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2601 : * SetRawNoDataValue(), or any similar methods.
2602 : *
2603 : * @note Driver implementation: this method shall be implemented if nodata
2604 : * is supported.
2605 : *
2606 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2607 : *
2608 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2609 : */
2610 5 : const void *GDALMDArray::GetRawNoDataValue() const
2611 : {
2612 5 : return nullptr;
2613 : }
2614 :
2615 : /************************************************************************/
2616 : /* GetNoDataValueAsDouble() */
2617 : /************************************************************************/
2618 :
2619 : /** Return the nodata value as a double.
2620 : *
2621 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2622 : *
2623 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2624 : * a nodata value exists and can be converted to double. Might be nullptr.
2625 : *
2626 : * @return the nodata value as a double. A 0.0 value might also indicate the
2627 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2628 : * set to false then).
2629 : */
2630 22420 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2631 : {
2632 22420 : const void *pNoData = GetRawNoDataValue();
2633 22420 : double dfNoData = 0.0;
2634 22420 : const auto &eDT = GetDataType();
2635 22420 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2636 22420 : if (ok)
2637 : {
2638 22183 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2639 : GDT_Float64, 0, 1);
2640 : }
2641 22420 : if (pbHasNoData)
2642 385 : *pbHasNoData = ok;
2643 22420 : return dfNoData;
2644 : }
2645 :
2646 : /************************************************************************/
2647 : /* GetNoDataValueAsInt64() */
2648 : /************************************************************************/
2649 :
2650 : /** Return the nodata value as a Int64.
2651 : *
2652 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2653 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2654 : *
2655 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2656 : *
2657 : * @return the nodata value as a Int64
2658 : *
2659 : * @since GDAL 3.5
2660 : */
2661 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2662 : {
2663 12 : const void *pNoData = GetRawNoDataValue();
2664 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2665 12 : const auto &eDT = GetDataType();
2666 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2667 12 : if (ok)
2668 : {
2669 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2670 : GDT_Int64, 0, 1);
2671 : }
2672 12 : if (pbHasNoData)
2673 12 : *pbHasNoData = ok;
2674 12 : return nNoData;
2675 : }
2676 :
2677 : /************************************************************************/
2678 : /* GetNoDataValueAsUInt64() */
2679 : /************************************************************************/
2680 :
2681 : /** Return the nodata value as a UInt64.
2682 : *
2683 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2684 :
2685 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2686 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2687 : *
2688 : * @return the nodata value as a UInt64
2689 : *
2690 : * @since GDAL 3.5
2691 : */
2692 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2693 : {
2694 8 : const void *pNoData = GetRawNoDataValue();
2695 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2696 8 : const auto &eDT = GetDataType();
2697 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2698 8 : if (ok)
2699 : {
2700 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2701 : GDT_UInt64, 0, 1);
2702 : }
2703 8 : if (pbHasNoData)
2704 8 : *pbHasNoData = ok;
2705 8 : return nNoData;
2706 : }
2707 :
2708 : /************************************************************************/
2709 : /* SetRawNoDataValue() */
2710 : /************************************************************************/
2711 :
2712 : /** Set the nodata value as a "raw" value.
2713 : *
2714 : * The value passed might be nullptr in case of no nodata value. When
2715 : * a nodata value is registered, a non-nullptr whose size in
2716 : * bytes is GetDataType().GetSize() must be passed.
2717 : *
2718 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2719 : *
2720 : * @note Driver implementation: this method shall be implemented if setting
2721 : nodata
2722 : * is supported.
2723 :
2724 : * @return true in case of success.
2725 : */
2726 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2727 : {
2728 0 : CPLError(CE_Failure, CPLE_NotSupported,
2729 : "SetRawNoDataValue() not implemented");
2730 0 : return false;
2731 : }
2732 :
2733 : /************************************************************************/
2734 : /* SetNoDataValue() */
2735 : /************************************************************************/
2736 :
2737 : /** Set the nodata value as a double.
2738 : *
2739 : * If the natural data type of the attribute/array is not double, type
2740 : * conversion will occur to the type returned by GetDataType().
2741 : *
2742 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2743 : *
2744 : * @return true in case of success.
2745 : */
2746 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2747 : {
2748 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2749 57 : bool bRet = false;
2750 57 : if (GDALExtendedDataType::CopyValue(
2751 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2752 57 : GetDataType()))
2753 : {
2754 57 : bRet = SetRawNoDataValue(pRawNoData);
2755 : }
2756 57 : CPLFree(pRawNoData);
2757 57 : return bRet;
2758 : }
2759 :
2760 : /************************************************************************/
2761 : /* SetNoDataValue() */
2762 : /************************************************************************/
2763 :
2764 : /** Set the nodata value as a Int64.
2765 : *
2766 : * If the natural data type of the attribute/array is not Int64, type conversion
2767 : * will occur to the type returned by GetDataType().
2768 : *
2769 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2770 : *
2771 : * @return true in case of success.
2772 : *
2773 : * @since GDAL 3.5
2774 : */
2775 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2776 : {
2777 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2778 3 : bool bRet = false;
2779 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2780 6 : GDALExtendedDataType::Create(GDT_Int64),
2781 3 : pRawNoData, GetDataType()))
2782 : {
2783 3 : bRet = SetRawNoDataValue(pRawNoData);
2784 : }
2785 3 : CPLFree(pRawNoData);
2786 3 : return bRet;
2787 : }
2788 :
2789 : /************************************************************************/
2790 : /* SetNoDataValue() */
2791 : /************************************************************************/
2792 :
2793 : /** Set the nodata value as a Int64.
2794 : *
2795 : * If the natural data type of the attribute/array is not Int64, type conversion
2796 : * will occur to the type returned by GetDataType().
2797 : *
2798 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2799 : *
2800 : * @return true in case of success.
2801 : *
2802 : * @since GDAL 3.5
2803 : */
2804 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2805 : {
2806 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2807 1 : bool bRet = false;
2808 1 : if (GDALExtendedDataType::CopyValue(
2809 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2810 1 : GetDataType()))
2811 : {
2812 1 : bRet = SetRawNoDataValue(pRawNoData);
2813 : }
2814 1 : CPLFree(pRawNoData);
2815 1 : return bRet;
2816 : }
2817 :
2818 : /************************************************************************/
2819 : /* Resize() */
2820 : /************************************************************************/
2821 :
2822 : /** Resize an array to new dimensions.
2823 : *
2824 : * Not all drivers may allow this operation, and with restrictions (e.g.
2825 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2826 : *
2827 : * Resizing a dimension used in other arrays will cause those other arrays
2828 : * to be resized.
2829 : *
2830 : * This is the same as the C function GDALMDArrayResize().
2831 : *
2832 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2833 : * new size of each indexing dimension.
2834 : * @param papszOptions Options. (Driver specific)
2835 : * @return true in case of success.
2836 : * @since GDAL 3.7
2837 : */
2838 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2839 : CPL_UNUSED CSLConstList papszOptions)
2840 : {
2841 0 : CPLError(CE_Failure, CPLE_NotSupported,
2842 : "Resize() is not supported for this array");
2843 0 : return false;
2844 : }
2845 :
2846 : /************************************************************************/
2847 : /* SetScale() */
2848 : /************************************************************************/
2849 :
2850 : /** Set the scale value to apply to raw values.
2851 : *
2852 : * unscaled_value = raw_value * GetScale() + GetOffset()
2853 : *
2854 : * This is the same as the C function GDALMDArraySetScale() /
2855 : * GDALMDArraySetScaleEx().
2856 : *
2857 : * @note Driver implementation: this method shall be implemented if setting
2858 : * scale is supported.
2859 : *
2860 : * @param dfScale scale
2861 : * @param eStorageType Data type to which create the potential attribute that
2862 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2863 : * implementation will decide automatically the data type. Note that changing
2864 : * the data type after initial setting might not be supported.
2865 : * @return true in case of success.
2866 : */
2867 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2868 : CPL_UNUSED GDALDataType eStorageType)
2869 : {
2870 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2871 0 : return false;
2872 : }
2873 :
2874 : /************************************************************************/
2875 : /* SetOffset) */
2876 : /************************************************************************/
2877 :
2878 : /** Set the offset value to apply to raw values.
2879 : *
2880 : * unscaled_value = raw_value * GetScale() + GetOffset()
2881 : *
2882 : * This is the same as the C function GDALMDArraySetOffset() /
2883 : * GDALMDArraySetOffsetEx().
2884 : *
2885 : * @note Driver implementation: this method shall be implemented if setting
2886 : * offset is supported.
2887 : *
2888 : * @param dfOffset Offset
2889 : * @param eStorageType Data type to which create the potential attribute that
2890 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2891 : * implementation will decide automatically the data type. Note that changing
2892 : * the data type after initial setting might not be supported.
2893 : * @return true in case of success.
2894 : */
2895 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2896 : CPL_UNUSED GDALDataType eStorageType)
2897 : {
2898 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2899 0 : return false;
2900 : }
2901 :
2902 : /************************************************************************/
2903 : /* GetScale() */
2904 : /************************************************************************/
2905 :
2906 : /** Get the scale value to apply to raw values.
2907 : *
2908 : * unscaled_value = raw_value * GetScale() + GetOffset()
2909 : *
2910 : * This is the same as the C function GDALMDArrayGetScale().
2911 : *
2912 : * @note Driver implementation: this method shall be implemented if gettings
2913 : * scale is supported.
2914 : *
2915 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2916 : * a scale value exists. Might be nullptr.
2917 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2918 : * the storage type of the scale value, when known/relevant. Otherwise will be
2919 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2920 : *
2921 : * @return the scale value. A 1.0 value might also indicate the
2922 : * absence of a scale value.
2923 : */
2924 13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
2925 : CPL_UNUSED GDALDataType *peStorageType) const
2926 : {
2927 13 : if (pbHasScale)
2928 13 : *pbHasScale = false;
2929 13 : return 1.0;
2930 : }
2931 :
2932 : /************************************************************************/
2933 : /* GetOffset() */
2934 : /************************************************************************/
2935 :
2936 : /** Get the offset value to apply to raw values.
2937 : *
2938 : * unscaled_value = raw_value * GetScale() + GetOffset()
2939 : *
2940 : * This is the same as the C function GDALMDArrayGetOffset().
2941 : *
2942 : * @note Driver implementation: this method shall be implemented if gettings
2943 : * offset is supported.
2944 : *
2945 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
2946 : * a offset value exists. Might be nullptr.
2947 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2948 : * the storage type of the offset value, when known/relevant. Otherwise will be
2949 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2950 : *
2951 : * @return the offset value. A 0.0 value might also indicate the
2952 : * absence of a offset value.
2953 : */
2954 13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
2955 : CPL_UNUSED GDALDataType *peStorageType) const
2956 : {
2957 13 : if (pbHasOffset)
2958 13 : *pbHasOffset = false;
2959 13 : return 0.0;
2960 : }
2961 :
2962 : /************************************************************************/
2963 : /* ProcessPerChunk() */
2964 : /************************************************************************/
2965 :
2966 : namespace
2967 : {
2968 : enum class Caller
2969 : {
2970 : CALLER_END_OF_LOOP,
2971 : CALLER_IN_LOOP,
2972 : };
2973 : }
2974 :
2975 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
2976 : *
2977 : * This method is to be used when doing operations on an array, or a subset of
2978 : * it, in a chunk by chunk way.
2979 : *
2980 : * @param arrayStartIdx Values representing the starting index to use
2981 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2982 : * Array of GetDimensionCount() values. Must not be
2983 : * nullptr, unless for a zero-dimensional array.
2984 : *
2985 : * @param count Values representing the number of values to use in
2986 : * each dimension.
2987 : * Array of GetDimensionCount() values. Must not be
2988 : * nullptr, unless for a zero-dimensional array.
2989 : *
2990 : * @param chunkSize Values representing the chunk size in each dimension.
2991 : * Might typically the output of GetProcessingChunkSize().
2992 : * Array of GetDimensionCount() values. Must not be
2993 : * nullptr, unless for a zero-dimensional array.
2994 : *
2995 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
2996 : * Must NOT be nullptr.
2997 : *
2998 : * @param pUserData Pointer to pass as the value of the pUserData argument
2999 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3000 : *
3001 : * @return true in case of success.
3002 : */
3003 58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3004 : const GUInt64 *count,
3005 : const size_t *chunkSize,
3006 : FuncProcessPerChunkType pfnFunc,
3007 : void *pUserData)
3008 : {
3009 58 : const auto &dims = GetDimensions();
3010 58 : if (dims.empty())
3011 : {
3012 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3013 : }
3014 :
3015 : // Sanity check
3016 56 : size_t nTotalChunkSize = 1;
3017 146 : for (size_t i = 0; i < dims.size(); i++)
3018 : {
3019 97 : const auto nSizeThisDim(dims[i]->GetSize());
3020 97 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3021 95 : arrayStartIdx[i] > nSizeThisDim - count[i])
3022 : {
3023 4 : CPLError(CE_Failure, CPLE_AppDefined,
3024 : "Inconsistent arrayStartIdx[] / count[] values "
3025 : "regarding array size");
3026 4 : return false;
3027 : }
3028 184 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3029 91 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3030 : {
3031 3 : CPLError(CE_Failure, CPLE_AppDefined,
3032 : "Inconsistent chunkSize[] values");
3033 3 : return false;
3034 : }
3035 90 : nTotalChunkSize *= chunkSize[i];
3036 : }
3037 :
3038 49 : size_t dimIdx = 0;
3039 98 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3040 98 : std::vector<size_t> chunkCount(dims.size());
3041 :
3042 : struct Stack
3043 : {
3044 : GUInt64 nBlockCounter = 0;
3045 : GUInt64 nBlocksMinusOne = 0;
3046 : size_t first_count = 0; // only used if nBlocks > 1
3047 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3048 : };
3049 :
3050 98 : std::vector<Stack> stack(dims.size());
3051 49 : GUInt64 iCurChunk = 0;
3052 49 : GUInt64 nChunkCount = 1;
3053 138 : for (size_t i = 0; i < dims.size(); i++)
3054 : {
3055 89 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3056 89 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3057 89 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3058 89 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3059 89 : if (stack[i].nBlocksMinusOne == 0)
3060 : {
3061 84 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3062 84 : chunkCount[i] = static_cast<size_t>(count[i]);
3063 : }
3064 : else
3065 : {
3066 5 : stack[i].first_count = static_cast<size_t>(
3067 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3068 : }
3069 : }
3070 :
3071 49 : lbl_next_depth:
3072 248 : if (dimIdx == dims.size())
3073 : {
3074 82 : ++iCurChunk;
3075 82 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3076 : iCurChunk, nChunkCount, pUserData))
3077 : {
3078 0 : return false;
3079 : }
3080 : }
3081 : else
3082 : {
3083 166 : if (stack[dimIdx].nBlocksMinusOne != 0)
3084 : {
3085 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3086 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3087 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3088 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3089 : while (true)
3090 : {
3091 33 : dimIdx++;
3092 33 : goto lbl_next_depth;
3093 33 : lbl_return_to_caller_in_loop:
3094 33 : --stack[dimIdx].nBlockCounter;
3095 33 : if (stack[dimIdx].nBlockCounter == 0)
3096 11 : break;
3097 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3098 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3099 : }
3100 :
3101 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3102 22 : chunkCount[dimIdx] =
3103 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3104 11 : chunkArrayStartIdx[dimIdx]);
3105 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3106 : }
3107 166 : dimIdx++;
3108 166 : goto lbl_next_depth;
3109 166 : lbl_return_to_caller_end_of_loop:
3110 166 : if (dimIdx == 0)
3111 49 : goto end;
3112 : }
3113 :
3114 199 : assert(dimIdx > 0);
3115 199 : dimIdx--;
3116 : // cppcheck-suppress negativeContainerIndex
3117 199 : switch (stack[dimIdx].return_point)
3118 : {
3119 166 : case Caller::CALLER_END_OF_LOOP:
3120 166 : goto lbl_return_to_caller_end_of_loop;
3121 33 : case Caller::CALLER_IN_LOOP:
3122 33 : goto lbl_return_to_caller_in_loop;
3123 : }
3124 49 : end:
3125 49 : return true;
3126 : }
3127 :
3128 : /************************************************************************/
3129 : /* GDALAttribute() */
3130 : /************************************************************************/
3131 :
3132 : //! @cond Doxygen_Suppress
3133 14005 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3134 0 : CPL_UNUSED const std::string &osName)
3135 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3136 14005 : : GDALAbstractMDArray(osParentName, osName)
3137 : #endif
3138 : {
3139 14005 : }
3140 :
3141 : //! @endcond
3142 :
3143 : /************************************************************************/
3144 : /* GetDimensionSize() */
3145 : /************************************************************************/
3146 :
3147 : /** Return the size of the dimensions of the attribute.
3148 : *
3149 : * This will be an empty array for a scalar (single value) attribute.
3150 : *
3151 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3152 : */
3153 362 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3154 : {
3155 362 : const auto &dims = GetDimensions();
3156 362 : std::vector<GUInt64> ret;
3157 362 : ret.reserve(dims.size());
3158 454 : for (const auto &dim : dims)
3159 92 : ret.push_back(dim->GetSize());
3160 362 : return ret;
3161 : }
3162 :
3163 : /************************************************************************/
3164 : /* GDALRawResult() */
3165 : /************************************************************************/
3166 :
3167 : //! @cond Doxygen_Suppress
3168 149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3169 149 : size_t nEltCount)
3170 298 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3171 149 : m_raw(raw)
3172 : {
3173 149 : }
3174 :
3175 : //! @endcond
3176 :
3177 : /************************************************************************/
3178 : /* GDALRawResult() */
3179 : /************************************************************************/
3180 :
3181 : /** Move constructor. */
3182 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3183 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3184 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3185 : {
3186 0 : other.m_nEltCount = 0;
3187 0 : other.m_nSize = 0;
3188 0 : other.m_raw = nullptr;
3189 0 : }
3190 :
3191 : /************************************************************************/
3192 : /* FreeMe() */
3193 : /************************************************************************/
3194 :
3195 149 : void GDALRawResult::FreeMe()
3196 : {
3197 149 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3198 : {
3199 47 : GByte *pabyPtr = m_raw;
3200 47 : const auto nDTSize(m_dt.GetSize());
3201 94 : for (size_t i = 0; i < m_nEltCount; ++i)
3202 : {
3203 47 : m_dt.FreeDynamicMemory(pabyPtr);
3204 47 : pabyPtr += nDTSize;
3205 : }
3206 : }
3207 149 : VSIFree(m_raw);
3208 149 : }
3209 :
3210 : /************************************************************************/
3211 : /* operator=() */
3212 : /************************************************************************/
3213 :
3214 : /** Move assignment. */
3215 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3216 : {
3217 0 : FreeMe();
3218 0 : m_dt = std::move(other.m_dt);
3219 0 : m_nEltCount = other.m_nEltCount;
3220 0 : m_nSize = other.m_nSize;
3221 0 : m_raw = other.m_raw;
3222 0 : other.m_nEltCount = 0;
3223 0 : other.m_nSize = 0;
3224 0 : other.m_raw = nullptr;
3225 0 : return *this;
3226 : }
3227 :
3228 : /************************************************************************/
3229 : /* ~GDALRawResult() */
3230 : /************************************************************************/
3231 :
3232 : /** Destructor. */
3233 149 : GDALRawResult::~GDALRawResult()
3234 : {
3235 149 : FreeMe();
3236 149 : }
3237 :
3238 : /************************************************************************/
3239 : /* StealData() */
3240 : /************************************************************************/
3241 :
3242 : //! @cond Doxygen_Suppress
3243 : /** Return buffer to caller which becomes owner of it.
3244 : * Only to be used by GDALAttributeReadAsRaw().
3245 : */
3246 6 : GByte *GDALRawResult::StealData()
3247 : {
3248 6 : GByte *ret = m_raw;
3249 6 : m_raw = nullptr;
3250 6 : m_nEltCount = 0;
3251 6 : m_nSize = 0;
3252 6 : return ret;
3253 : }
3254 :
3255 : //! @endcond
3256 :
3257 : /************************************************************************/
3258 : /* ReadAsRaw() */
3259 : /************************************************************************/
3260 :
3261 : /** Return the raw value of an attribute.
3262 : *
3263 : *
3264 : * This is the same as the C function GDALAttributeReadAsRaw().
3265 : */
3266 149 : GDALRawResult GDALAttribute::ReadAsRaw() const
3267 : {
3268 149 : const auto nEltCount(GetTotalElementsCount());
3269 149 : const auto &dt(GetDataType());
3270 149 : const auto nDTSize(dt.GetSize());
3271 : GByte *res = static_cast<GByte *>(
3272 149 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3273 149 : if (!res)
3274 0 : return GDALRawResult(nullptr, dt, 0);
3275 149 : const auto &dims = GetDimensions();
3276 149 : const auto nDims = GetDimensionCount();
3277 298 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3278 298 : std::vector<size_t> count(1 + nDims);
3279 168 : for (size_t i = 0; i < nDims; i++)
3280 : {
3281 19 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3282 : }
3283 149 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3284 149 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3285 : {
3286 0 : VSIFree(res);
3287 0 : return GDALRawResult(nullptr, dt, 0);
3288 : }
3289 149 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3290 : }
3291 :
3292 : /************************************************************************/
3293 : /* ReadAsString() */
3294 : /************************************************************************/
3295 :
3296 : /** Return the value of an attribute as a string.
3297 : *
3298 : * The returned string should not be freed, and its lifetime does not
3299 : * excess a next call to ReadAsString() on the same object, or the deletion
3300 : * of the object itself.
3301 : *
3302 : * This function will only return the first element if there are several.
3303 : *
3304 : * This is the same as the C function GDALAttributeReadAsString()
3305 : *
3306 : * @return a string, or nullptr.
3307 : */
3308 1303 : const char *GDALAttribute::ReadAsString() const
3309 : {
3310 1303 : const auto nDims = GetDimensionCount();
3311 2606 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3312 2606 : std::vector<size_t> count(1 + nDims, 1);
3313 1303 : char *szRet = nullptr;
3314 1303 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3315 1303 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3316 3908 : sizeof(szRet)) ||
3317 1302 : szRet == nullptr)
3318 : {
3319 4 : return nullptr;
3320 : }
3321 1299 : m_osCachedVal = szRet;
3322 1299 : CPLFree(szRet);
3323 1299 : return m_osCachedVal.c_str();
3324 : }
3325 :
3326 : /************************************************************************/
3327 : /* ReadAsInt() */
3328 : /************************************************************************/
3329 :
3330 : /** Return the value of an attribute as a integer.
3331 : *
3332 : * This function will only return the first element if there are several.
3333 : *
3334 : * It can fail if its value can not be converted to integer.
3335 : *
3336 : * This is the same as the C function GDALAttributeReadAsInt()
3337 : *
3338 : * @return a integer, or INT_MIN in case of error.
3339 : */
3340 218 : int GDALAttribute::ReadAsInt() const
3341 : {
3342 218 : const auto nDims = GetDimensionCount();
3343 436 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3344 218 : std::vector<size_t> count(1 + nDims, 1);
3345 218 : int nRet = INT_MIN;
3346 218 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3347 436 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3348 436 : return nRet;
3349 : }
3350 :
3351 : /************************************************************************/
3352 : /* ReadAsInt64() */
3353 : /************************************************************************/
3354 :
3355 : /** Return the value of an attribute as an int64_t.
3356 : *
3357 : * This function will only return the first element if there are several.
3358 : *
3359 : * It can fail if its value can not be converted to long.
3360 : *
3361 : * This is the same as the C function GDALAttributeReadAsInt64()
3362 : *
3363 : * @return an int64_t, or INT64_MIN in case of error.
3364 : */
3365 54 : int64_t GDALAttribute::ReadAsInt64() const
3366 : {
3367 54 : const auto nDims = GetDimensionCount();
3368 108 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3369 54 : std::vector<size_t> count(1 + nDims, 1);
3370 54 : int64_t nRet = INT64_MIN;
3371 54 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3372 108 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3373 108 : return nRet;
3374 : }
3375 :
3376 : /************************************************************************/
3377 : /* ReadAsDouble() */
3378 : /************************************************************************/
3379 :
3380 : /** Return the value of an attribute as a double.
3381 : *
3382 : * This function will only return the first element if there are several.
3383 : *
3384 : * It can fail if its value can not be converted to double.
3385 : *
3386 : * This is the same as the C function GDALAttributeReadAsInt()
3387 : *
3388 : * @return a double value.
3389 : */
3390 339 : double GDALAttribute::ReadAsDouble() const
3391 : {
3392 339 : const auto nDims = GetDimensionCount();
3393 678 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3394 339 : std::vector<size_t> count(1 + nDims, 1);
3395 339 : double dfRet = 0;
3396 339 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3397 339 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3398 339 : sizeof(dfRet));
3399 678 : return dfRet;
3400 : }
3401 :
3402 : /************************************************************************/
3403 : /* ReadAsStringArray() */
3404 : /************************************************************************/
3405 :
3406 : /** Return the value of an attribute as an array of strings.
3407 : *
3408 : * This is the same as the C function GDALAttributeReadAsStringArray()
3409 : */
3410 104 : CPLStringList GDALAttribute::ReadAsStringArray() const
3411 : {
3412 104 : const auto nElts = GetTotalElementsCount();
3413 104 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3414 0 : return CPLStringList();
3415 : char **papszList = static_cast<char **>(
3416 104 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3417 104 : const auto &dims = GetDimensions();
3418 104 : const auto nDims = GetDimensionCount();
3419 208 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3420 208 : std::vector<size_t> count(1 + nDims);
3421 157 : for (size_t i = 0; i < nDims; i++)
3422 : {
3423 53 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3424 : }
3425 104 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3426 104 : GDALExtendedDataType::CreateString(), papszList, papszList,
3427 104 : sizeof(char *) * static_cast<int>(nElts));
3428 269 : for (int i = 0; i < static_cast<int>(nElts); i++)
3429 : {
3430 165 : if (papszList[i] == nullptr)
3431 13 : papszList[i] = CPLStrdup("");
3432 : }
3433 104 : return CPLStringList(papszList);
3434 : }
3435 :
3436 : /************************************************************************/
3437 : /* ReadAsIntArray() */
3438 : /************************************************************************/
3439 :
3440 : /** Return the value of an attribute as an array of integers.
3441 : *
3442 : * This is the same as the C function GDALAttributeReadAsIntArray().
3443 : */
3444 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3445 : {
3446 15 : const auto nElts = GetTotalElementsCount();
3447 : #if SIZEOF_VOIDP == 4
3448 : if (nElts > static_cast<size_t>(nElts))
3449 : return {};
3450 : #endif
3451 15 : std::vector<int> res(static_cast<size_t>(nElts));
3452 15 : const auto &dims = GetDimensions();
3453 15 : const auto nDims = GetDimensionCount();
3454 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3455 30 : std::vector<size_t> count(1 + nDims);
3456 32 : for (size_t i = 0; i < nDims; i++)
3457 : {
3458 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3459 : }
3460 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3461 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3462 15 : res.size() * sizeof(res[0]));
3463 30 : return res;
3464 : }
3465 :
3466 : /************************************************************************/
3467 : /* ReadAsInt64Array() */
3468 : /************************************************************************/
3469 :
3470 : /** Return the value of an attribute as an array of int64_t.
3471 : *
3472 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3473 : */
3474 38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3475 : {
3476 38 : const auto nElts = GetTotalElementsCount();
3477 : #if SIZEOF_VOIDP == 4
3478 : if (nElts > static_cast<size_t>(nElts))
3479 : return {};
3480 : #endif
3481 38 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3482 38 : const auto &dims = GetDimensions();
3483 38 : const auto nDims = GetDimensionCount();
3484 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3485 76 : std::vector<size_t> count(1 + nDims);
3486 76 : for (size_t i = 0; i < nDims; i++)
3487 : {
3488 38 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3489 : }
3490 38 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3491 76 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3492 38 : res.size() * sizeof(res[0]));
3493 76 : return res;
3494 : }
3495 :
3496 : /************************************************************************/
3497 : /* ReadAsDoubleArray() */
3498 : /************************************************************************/
3499 :
3500 : /** Return the value of an attribute as an array of double.
3501 : *
3502 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3503 : */
3504 87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3505 : {
3506 87 : const auto nElts = GetTotalElementsCount();
3507 : #if SIZEOF_VOIDP == 4
3508 : if (nElts > static_cast<size_t>(nElts))
3509 : return {};
3510 : #endif
3511 87 : std::vector<double> res(static_cast<size_t>(nElts));
3512 87 : const auto &dims = GetDimensions();
3513 87 : const auto nDims = GetDimensionCount();
3514 174 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3515 174 : std::vector<size_t> count(1 + nDims);
3516 158 : for (size_t i = 0; i < nDims; i++)
3517 : {
3518 71 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3519 : }
3520 87 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3521 174 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3522 87 : res.size() * sizeof(res[0]));
3523 174 : return res;
3524 : }
3525 :
3526 : /************************************************************************/
3527 : /* Write() */
3528 : /************************************************************************/
3529 :
3530 : /** Write an attribute from raw values expressed in GetDataType()
3531 : *
3532 : * The values should be provided in the type of GetDataType() and there should
3533 : * be exactly GetTotalElementsCount() of them.
3534 : * If GetDataType() is a string, each value should be a char* pointer.
3535 : *
3536 : * This is the same as the C function GDALAttributeWriteRaw().
3537 : *
3538 : * @param pabyValue Buffer of nLen bytes.
3539 : * @param nLen Size of pabyValue in bytes. Should be equal to
3540 : * GetTotalElementsCount() * GetDataType().GetSize()
3541 : * @return true in case of success.
3542 : */
3543 91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3544 : {
3545 91 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3546 : {
3547 0 : CPLError(CE_Failure, CPLE_AppDefined,
3548 : "Length is not of expected value");
3549 0 : return false;
3550 : }
3551 91 : const auto &dims = GetDimensions();
3552 91 : const auto nDims = GetDimensionCount();
3553 182 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3554 182 : std::vector<size_t> count(1 + nDims);
3555 114 : for (size_t i = 0; i < nDims; i++)
3556 : {
3557 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3558 : }
3559 91 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3560 91 : pabyValue, pabyValue, nLen);
3561 : }
3562 :
3563 : /************************************************************************/
3564 : /* Write() */
3565 : /************************************************************************/
3566 :
3567 : /** Write an attribute from a string value.
3568 : *
3569 : * Type conversion will be performed if needed. If the attribute contains
3570 : * multiple values, only the first one will be updated.
3571 : *
3572 : * This is the same as the C function GDALAttributeWriteString().
3573 : *
3574 : * @param pszValue Pointer to a string.
3575 : * @return true in case of success.
3576 : */
3577 304 : bool GDALAttribute::Write(const char *pszValue)
3578 : {
3579 304 : const auto nDims = GetDimensionCount();
3580 608 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3581 304 : std::vector<size_t> count(1 + nDims, 1);
3582 304 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3583 608 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3584 608 : sizeof(pszValue));
3585 : }
3586 :
3587 : /************************************************************************/
3588 : /* WriteInt() */
3589 : /************************************************************************/
3590 :
3591 : /** Write an attribute from a integer value.
3592 : *
3593 : * Type conversion will be performed if needed. If the attribute contains
3594 : * multiple values, only the first one will be updated.
3595 : *
3596 : * This is the same as the C function GDALAttributeWriteInt().
3597 : *
3598 : * @param nVal Value.
3599 : * @return true in case of success.
3600 : */
3601 22 : bool GDALAttribute::WriteInt(int nVal)
3602 : {
3603 22 : const auto nDims = GetDimensionCount();
3604 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3605 22 : std::vector<size_t> count(1 + nDims, 1);
3606 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3607 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3608 44 : sizeof(nVal));
3609 : }
3610 :
3611 : /************************************************************************/
3612 : /* WriteInt64() */
3613 : /************************************************************************/
3614 :
3615 : /** Write an attribute from an int64_t value.
3616 : *
3617 : * Type conversion will be performed if needed. If the attribute contains
3618 : * multiple values, only the first one will be updated.
3619 : *
3620 : * This is the same as the C function GDALAttributeWriteInt().
3621 : *
3622 : * @param nVal Value.
3623 : * @return true in case of success.
3624 : */
3625 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3626 : {
3627 11 : const auto nDims = GetDimensionCount();
3628 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3629 11 : std::vector<size_t> count(1 + nDims, 1);
3630 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3631 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3632 22 : sizeof(nVal));
3633 : }
3634 :
3635 : /************************************************************************/
3636 : /* Write() */
3637 : /************************************************************************/
3638 :
3639 : /** Write an attribute from a double value.
3640 : *
3641 : * Type conversion will be performed if needed. If the attribute contains
3642 : * multiple values, only the first one will be updated.
3643 : *
3644 : * This is the same as the C function GDALAttributeWriteDouble().
3645 : *
3646 : * @param dfVal Value.
3647 : * @return true in case of success.
3648 : */
3649 36 : bool GDALAttribute::Write(double dfVal)
3650 : {
3651 36 : const auto nDims = GetDimensionCount();
3652 72 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3653 36 : std::vector<size_t> count(1 + nDims, 1);
3654 36 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3655 72 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3656 72 : sizeof(dfVal));
3657 : }
3658 :
3659 : /************************************************************************/
3660 : /* Write() */
3661 : /************************************************************************/
3662 :
3663 : /** Write an attribute from an array of strings.
3664 : *
3665 : * Type conversion will be performed if needed.
3666 : *
3667 : * Exactly GetTotalElementsCount() strings must be provided
3668 : *
3669 : * This is the same as the C function GDALAttributeWriteStringArray().
3670 : *
3671 : * @param vals Array of strings.
3672 : * @return true in case of success.
3673 : */
3674 8 : bool GDALAttribute::Write(CSLConstList vals)
3675 : {
3676 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3677 : {
3678 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3679 1 : return false;
3680 : }
3681 7 : const auto nDims = GetDimensionCount();
3682 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3683 7 : std::vector<size_t> count(1 + nDims);
3684 7 : const auto &dims = GetDimensions();
3685 15 : for (size_t i = 0; i < nDims; i++)
3686 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3687 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3688 7 : GDALExtendedDataType::CreateString(), vals, vals,
3689 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3690 : }
3691 :
3692 : /************************************************************************/
3693 : /* Write() */
3694 : /************************************************************************/
3695 :
3696 : /** Write an attribute from an array of int.
3697 : *
3698 : * Type conversion will be performed if needed.
3699 : *
3700 : * Exactly GetTotalElementsCount() strings must be provided
3701 : *
3702 : * This is the same as the C function GDALAttributeWriteIntArray()
3703 : *
3704 : * @param vals Array of int.
3705 : * @param nVals Should be equal to GetTotalElementsCount().
3706 : * @return true in case of success.
3707 : */
3708 9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3709 : {
3710 9 : if (nVals != GetTotalElementsCount())
3711 : {
3712 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3713 1 : return false;
3714 : }
3715 8 : const auto nDims = GetDimensionCount();
3716 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3717 8 : std::vector<size_t> count(1 + nDims);
3718 8 : const auto &dims = GetDimensions();
3719 16 : for (size_t i = 0; i < nDims; i++)
3720 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3721 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3722 8 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3723 16 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3724 : }
3725 :
3726 : /************************************************************************/
3727 : /* Write() */
3728 : /************************************************************************/
3729 :
3730 : /** Write an attribute from an array of int64_t.
3731 : *
3732 : * Type conversion will be performed if needed.
3733 : *
3734 : * Exactly GetTotalElementsCount() strings must be provided
3735 : *
3736 : * This is the same as the C function GDALAttributeWriteLongArray()
3737 : *
3738 : * @param vals Array of int64_t.
3739 : * @param nVals Should be equal to GetTotalElementsCount().
3740 : * @return true in case of success.
3741 : */
3742 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3743 : {
3744 10 : if (nVals != GetTotalElementsCount())
3745 : {
3746 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3747 0 : return false;
3748 : }
3749 10 : const auto nDims = GetDimensionCount();
3750 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3751 10 : std::vector<size_t> count(1 + nDims);
3752 10 : const auto &dims = GetDimensions();
3753 20 : for (size_t i = 0; i < nDims; i++)
3754 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3755 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3756 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3757 10 : static_cast<size_t>(GetTotalElementsCount()) *
3758 10 : sizeof(int64_t));
3759 : }
3760 :
3761 : /************************************************************************/
3762 : /* Write() */
3763 : /************************************************************************/
3764 :
3765 : /** Write an attribute from an array of double.
3766 : *
3767 : * Type conversion will be performed if needed.
3768 : *
3769 : * Exactly GetTotalElementsCount() strings must be provided
3770 : *
3771 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3772 : *
3773 : * @param vals Array of double.
3774 : * @param nVals Should be equal to GetTotalElementsCount().
3775 : * @return true in case of success.
3776 : */
3777 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3778 : {
3779 7 : if (nVals != GetTotalElementsCount())
3780 : {
3781 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3782 1 : return false;
3783 : }
3784 6 : const auto nDims = GetDimensionCount();
3785 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3786 6 : std::vector<size_t> count(1 + nDims);
3787 6 : const auto &dims = GetDimensions();
3788 13 : for (size_t i = 0; i < nDims; i++)
3789 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3790 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3791 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3792 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3793 : }
3794 :
3795 : /************************************************************************/
3796 : /* GDALMDArray() */
3797 : /************************************************************************/
3798 :
3799 : //! @cond Doxygen_Suppress
3800 6249 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3801 : CPL_UNUSED const std::string &osName,
3802 0 : const std::string &osContext)
3803 : :
3804 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3805 : GDALAbstractMDArray(osParentName, osName),
3806 : #endif
3807 6249 : m_osContext(osContext)
3808 : {
3809 6249 : }
3810 :
3811 : //! @endcond
3812 :
3813 : /************************************************************************/
3814 : /* GetTotalCopyCost() */
3815 : /************************************************************************/
3816 :
3817 : /** Return a total "cost" to copy the array.
3818 : *
3819 : * Used as a parameter for CopyFrom()
3820 : */
3821 43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3822 : {
3823 86 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3824 86 : GetTotalElementsCount() * GetDataType().GetSize();
3825 : }
3826 :
3827 : /************************************************************************/
3828 : /* CopyFromAllExceptValues() */
3829 : /************************************************************************/
3830 :
3831 : //! @cond Doxygen_Suppress
3832 :
3833 144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3834 : bool bStrict, GUInt64 &nCurCost,
3835 : const GUInt64 nTotalCost,
3836 : GDALProgressFunc pfnProgress,
3837 : void *pProgressData)
3838 : {
3839 : // Nodata setting must be one of the first things done for TileDB
3840 144 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3841 144 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3842 : {
3843 13 : SetRawNoDataValue(pNoData);
3844 : }
3845 :
3846 144 : const bool bThisIsUnscaledArray =
3847 144 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3848 288 : auto attrs = poSrcArray->GetAttributes();
3849 191 : for (const auto &attr : attrs)
3850 : {
3851 47 : const auto &osAttrName = attr->GetName();
3852 47 : if (bThisIsUnscaledArray)
3853 : {
3854 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3855 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3856 1 : osAttrName == "valid_range")
3857 : {
3858 1 : continue;
3859 : }
3860 : }
3861 :
3862 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3863 92 : attr->GetDataType());
3864 46 : if (!dstAttr)
3865 : {
3866 0 : if (bStrict)
3867 0 : return false;
3868 0 : continue;
3869 : }
3870 46 : auto raw = attr->ReadAsRaw();
3871 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3872 0 : return false;
3873 : }
3874 144 : if (!attrs.empty())
3875 : {
3876 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3877 46 : if (pfnProgress &&
3878 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3879 0 : return false;
3880 : }
3881 :
3882 144 : auto srcSRS = poSrcArray->GetSpatialRef();
3883 144 : if (srcSRS)
3884 : {
3885 11 : SetSpatialRef(srcSRS.get());
3886 : }
3887 :
3888 144 : const std::string &osUnit(poSrcArray->GetUnit());
3889 144 : if (!osUnit.empty())
3890 : {
3891 18 : SetUnit(osUnit);
3892 : }
3893 :
3894 144 : bool bGotValue = false;
3895 144 : GDALDataType eOffsetStorageType = GDT_Unknown;
3896 : const double dfOffset =
3897 144 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3898 144 : if (bGotValue)
3899 : {
3900 3 : SetOffset(dfOffset, eOffsetStorageType);
3901 : }
3902 :
3903 144 : bGotValue = false;
3904 144 : GDALDataType eScaleStorageType = GDT_Unknown;
3905 144 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3906 144 : if (bGotValue)
3907 : {
3908 3 : SetScale(dfScale, eScaleStorageType);
3909 : }
3910 :
3911 144 : return true;
3912 : }
3913 :
3914 : //! @endcond
3915 :
3916 : /************************************************************************/
3917 : /* CopyFrom() */
3918 : /************************************************************************/
3919 :
3920 : /** Copy the content of an array into a new (generally empty) array.
3921 : *
3922 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
3923 : * of some output drivers this is not recommended)
3924 : * @param poSrcArray Source array. Should NOT be nullptr.
3925 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
3926 : * stop the copy. In relaxed mode, the copy will be attempted to
3927 : * be pursued.
3928 : * @param nCurCost Should be provided as a variable initially set to 0.
3929 : * @param nTotalCost Total cost from GetTotalCopyCost().
3930 : * @param pfnProgress Progress callback, or nullptr.
3931 : * @param pProgressData Progress user data, or nulptr.
3932 : *
3933 : * @return true in case of success (or partial success if bStrict == false).
3934 : */
3935 41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
3936 : const GDALMDArray *poSrcArray, bool bStrict,
3937 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
3938 : GDALProgressFunc pfnProgress, void *pProgressData)
3939 : {
3940 41 : if (pfnProgress == nullptr)
3941 4 : pfnProgress = GDALDummyProgress;
3942 :
3943 41 : nCurCost += GDALMDArray::COPY_COST;
3944 :
3945 41 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
3946 : pfnProgress, pProgressData))
3947 : {
3948 0 : return false;
3949 : }
3950 :
3951 41 : const auto &dims = poSrcArray->GetDimensions();
3952 41 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
3953 41 : if (dims.empty())
3954 : {
3955 2 : std::vector<GByte> abyTmp(nDTSize);
3956 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
3957 2 : GetDataType(), &abyTmp[0]) &&
3958 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
3959 4 : &abyTmp[0])) &&
3960 : bStrict)
3961 : {
3962 0 : return false;
3963 : }
3964 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
3965 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3966 0 : return false;
3967 : }
3968 : else
3969 : {
3970 39 : std::vector<GUInt64> arrayStartIdx(dims.size());
3971 39 : std::vector<GUInt64> count(dims.size());
3972 106 : for (size_t i = 0; i < dims.size(); i++)
3973 : {
3974 67 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3975 : }
3976 :
3977 : struct CopyFunc
3978 : {
3979 : GDALMDArray *poDstArray = nullptr;
3980 : std::vector<GByte> abyTmp{};
3981 : GDALProgressFunc pfnProgress = nullptr;
3982 : void *pProgressData = nullptr;
3983 : GUInt64 nCurCost = 0;
3984 : GUInt64 nTotalCost = 0;
3985 : GUInt64 nTotalBytesThisArray = 0;
3986 : bool bStop = false;
3987 :
3988 57 : static bool f(GDALAbstractMDArray *l_poSrcArray,
3989 : const GUInt64 *chunkArrayStartIdx,
3990 : const size_t *chunkCount, GUInt64 iCurChunk,
3991 : GUInt64 nChunkCount, void *pUserData)
3992 : {
3993 57 : const auto &dt(l_poSrcArray->GetDataType());
3994 57 : auto data = static_cast<CopyFunc *>(pUserData);
3995 57 : auto poDstArray = data->poDstArray;
3996 57 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
3997 57 : nullptr, dt, &data->abyTmp[0]))
3998 : {
3999 0 : return false;
4000 : }
4001 : bool bRet =
4002 57 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4003 57 : nullptr, dt, &data->abyTmp[0]);
4004 57 : if (dt.NeedsFreeDynamicMemory())
4005 : {
4006 2 : const auto l_nDTSize = dt.GetSize();
4007 2 : GByte *ptr = &data->abyTmp[0];
4008 2 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4009 2 : size_t nEltCount = 1;
4010 4 : for (size_t i = 0; i < l_nDims; ++i)
4011 : {
4012 2 : nEltCount *= chunkCount[i];
4013 : }
4014 10 : for (size_t i = 0; i < nEltCount; i++)
4015 : {
4016 8 : dt.FreeDynamicMemory(ptr);
4017 8 : ptr += l_nDTSize;
4018 : }
4019 : }
4020 57 : if (!bRet)
4021 : {
4022 0 : return false;
4023 : }
4024 :
4025 57 : double dfCurCost =
4026 57 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4027 57 : data->nTotalBytesThisArray;
4028 57 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4029 : data->pProgressData))
4030 : {
4031 0 : data->bStop = true;
4032 0 : return false;
4033 : }
4034 :
4035 57 : return true;
4036 : }
4037 : };
4038 :
4039 39 : CopyFunc copyFunc;
4040 39 : copyFunc.poDstArray = this;
4041 39 : copyFunc.nCurCost = nCurCost;
4042 39 : copyFunc.nTotalCost = nTotalCost;
4043 39 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4044 39 : copyFunc.pfnProgress = pfnProgress;
4045 39 : copyFunc.pProgressData = pProgressData;
4046 : const char *pszSwathSize =
4047 39 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4048 : const size_t nMaxChunkSize =
4049 : pszSwathSize
4050 39 : ? static_cast<size_t>(
4051 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4052 1 : CPLAtoGIntBig(pszSwathSize)))
4053 : : static_cast<size_t>(
4054 38 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4055 38 : GDALGetCacheMax64() / 4));
4056 39 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4057 39 : size_t nRealChunkSize = nDTSize;
4058 106 : for (const auto &nChunkSize : anChunkSizes)
4059 : {
4060 67 : nRealChunkSize *= nChunkSize;
4061 : }
4062 : try
4063 : {
4064 39 : copyFunc.abyTmp.resize(nRealChunkSize);
4065 : }
4066 0 : catch (const std::exception &)
4067 : {
4068 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4069 : "Cannot allocate temporary buffer");
4070 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4071 0 : return false;
4072 : }
4073 116 : if (copyFunc.nTotalBytesThisArray != 0 &&
4074 38 : !const_cast<GDALMDArray *>(poSrcArray)
4075 38 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4076 : anChunkSizes.data(), CopyFunc::f,
4077 77 : ©Func) &&
4078 0 : (bStrict || copyFunc.bStop))
4079 : {
4080 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4081 0 : return false;
4082 : }
4083 39 : nCurCost += copyFunc.nTotalBytesThisArray;
4084 : }
4085 :
4086 41 : return true;
4087 : }
4088 :
4089 : /************************************************************************/
4090 : /* GetStructuralInfo() */
4091 : /************************************************************************/
4092 :
4093 : /** Return structural information on the array.
4094 : *
4095 : * This may be the compression, etc..
4096 : *
4097 : * The return value should not be freed and is valid until GDALMDArray is
4098 : * released or this function called again.
4099 : *
4100 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4101 : */
4102 51 : CSLConstList GDALMDArray::GetStructuralInfo() const
4103 : {
4104 51 : return nullptr;
4105 : }
4106 :
4107 : /************************************************************************/
4108 : /* AdviseRead() */
4109 : /************************************************************************/
4110 :
4111 : /** Advise driver of upcoming read requests.
4112 : *
4113 : * Some GDAL drivers operate more efficiently if they know in advance what
4114 : * set of upcoming read requests will be made. The AdviseRead() method allows
4115 : * an application to notify the driver of the region of interest.
4116 : *
4117 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4118 : * accelerate access via some drivers. One such case is when reading through
4119 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4120 : * with the region of interest defined by AdviseRead())
4121 : *
4122 : * This is the same as the C function GDALMDArrayAdviseRead().
4123 : *
4124 : * @param arrayStartIdx Values representing the starting index to read
4125 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4126 : * Array of GetDimensionCount() values.
4127 : * Can be nullptr as a synonymous for [0 for i in
4128 : * range(GetDimensionCount() ]
4129 : *
4130 : * @param count Values representing the number of values to extract in
4131 : * each dimension.
4132 : * Array of GetDimensionCount() values.
4133 : * Can be nullptr as a synonymous for
4134 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4135 : * range(GetDimensionCount() ]
4136 : *
4137 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4138 : * documentation.
4139 : *
4140 : * @return true in case of success (ignoring the advice is a success)
4141 : *
4142 : * @since GDAL 3.2
4143 : */
4144 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4145 : CSLConstList papszOptions) const
4146 : {
4147 25 : const auto nDimCount = GetDimensionCount();
4148 25 : if (nDimCount == 0)
4149 2 : return true;
4150 :
4151 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4152 23 : if (arrayStartIdx == nullptr)
4153 : {
4154 0 : tmp_arrayStartIdx.resize(nDimCount);
4155 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4156 : }
4157 :
4158 46 : std::vector<size_t> tmp_count;
4159 23 : if (count == nullptr)
4160 : {
4161 0 : tmp_count.resize(nDimCount);
4162 0 : const auto &dims = GetDimensions();
4163 0 : for (size_t i = 0; i < nDimCount; i++)
4164 : {
4165 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4166 : #if SIZEOF_VOIDP < 8
4167 : if (nSize != static_cast<size_t>(nSize))
4168 : {
4169 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4170 : return false;
4171 : }
4172 : #endif
4173 0 : tmp_count[i] = static_cast<size_t>(nSize);
4174 : }
4175 0 : count = tmp_count.data();
4176 : }
4177 :
4178 46 : std::vector<GInt64> tmp_arrayStep;
4179 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4180 23 : const GInt64 *arrayStep = nullptr;
4181 23 : const GPtrDiff_t *bufferStride = nullptr;
4182 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4183 46 : GDALExtendedDataType::Create(GDT_Unknown),
4184 : nullptr, nullptr, 0, tmp_arrayStep,
4185 : tmp_bufferStride))
4186 : {
4187 1 : return false;
4188 : }
4189 :
4190 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4191 : }
4192 :
4193 : /************************************************************************/
4194 : /* IAdviseRead() */
4195 : /************************************************************************/
4196 :
4197 : //! @cond Doxygen_Suppress
4198 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4199 : CSLConstList /* papszOptions*/) const
4200 : {
4201 3 : return true;
4202 : }
4203 :
4204 : //! @endcond
4205 :
4206 : /************************************************************************/
4207 : /* MassageName() */
4208 : /************************************************************************/
4209 :
4210 : //! @cond Doxygen_Suppress
4211 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4212 : {
4213 32 : std::string ret;
4214 604 : for (const char ch : inputName)
4215 : {
4216 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4217 138 : ret += '_';
4218 : else
4219 434 : ret += ch;
4220 : }
4221 32 : return ret;
4222 : }
4223 :
4224 : //! @endcond
4225 :
4226 : /************************************************************************/
4227 : /* GetCacheRootGroup() */
4228 : /************************************************************************/
4229 :
4230 : //! @cond Doxygen_Suppress
4231 : std::shared_ptr<GDALGroup>
4232 1358 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4233 : std::string &osCacheFilenameOut) const
4234 : {
4235 1358 : const auto &osFilename = GetFilename();
4236 1358 : if (osFilename.empty())
4237 : {
4238 1 : CPLError(CE_Failure, CPLE_AppDefined,
4239 : "Cannot cache an array with an empty filename");
4240 1 : return nullptr;
4241 : }
4242 :
4243 1357 : osCacheFilenameOut = osFilename + ".gmac";
4244 1357 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4245 : {
4246 0 : const auto nPosQuestionMark = osFilename.find('?');
4247 0 : if (nPosQuestionMark != std::string::npos)
4248 : {
4249 : osCacheFilenameOut =
4250 0 : osFilename.substr(0, nPosQuestionMark)
4251 0 : .append(".gmac")
4252 0 : .append(osFilename.substr(nPosQuestionMark));
4253 : }
4254 : }
4255 1357 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4256 1357 : if (pszProxy != nullptr)
4257 7 : osCacheFilenameOut = pszProxy;
4258 :
4259 1357 : std::unique_ptr<GDALDataset> poDS;
4260 : VSIStatBufL sStat;
4261 1357 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4262 : {
4263 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4264 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4265 : nullptr, nullptr, nullptr));
4266 : }
4267 1357 : if (poDS)
4268 : {
4269 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4270 28 : return poDS->GetRootGroup();
4271 : }
4272 :
4273 1329 : if (bCanCreate)
4274 : {
4275 4 : const char *pszDrvName = "netCDF";
4276 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4277 4 : if (poDrv == nullptr)
4278 : {
4279 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4280 : pszDrvName);
4281 0 : return nullptr;
4282 : }
4283 : {
4284 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4285 8 : CPLErrorStateBackuper oErrorStateBackuper;
4286 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4287 : nullptr, nullptr));
4288 : }
4289 4 : if (!poDS)
4290 : {
4291 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4292 1 : if (pszProxy)
4293 : {
4294 1 : osCacheFilenameOut = pszProxy;
4295 1 : poDS.reset(poDrv->CreateMultiDimensional(
4296 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4297 : }
4298 : }
4299 4 : if (poDS)
4300 : {
4301 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4302 4 : return poDS->GetRootGroup();
4303 : }
4304 : else
4305 : {
4306 0 : CPLError(CE_Failure, CPLE_AppDefined,
4307 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4308 : "configuration option to write the cache in "
4309 : "another directory",
4310 : osCacheFilenameOut.c_str());
4311 : }
4312 : }
4313 :
4314 1325 : return nullptr;
4315 : }
4316 :
4317 : //! @endcond
4318 :
4319 : /************************************************************************/
4320 : /* Cache() */
4321 : /************************************************************************/
4322 :
4323 : /** Cache the content of the array into an auxiliary filename.
4324 : *
4325 : * The main purpose of this method is to be able to cache views that are
4326 : * expensive to compute, such as transposed arrays.
4327 : *
4328 : * The array will be stored in a file whose name is the one of
4329 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4330 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4331 : *
4332 : * If the .gmac file cannot be written next to the dataset, the
4333 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4334 : * directory.
4335 : *
4336 : * The GDALMDArray::Read() method will automatically use the cache when it
4337 : * exists. There is no timestamp checks between the source array and the cached
4338 : * array. If the source arrays changes, the cache must be manually deleted.
4339 : *
4340 : * This is the same as the C function GDALMDArrayCache()
4341 : *
4342 : * @note Driver implementation: optionally implemented.
4343 : *
4344 : * @param papszOptions List of options, null terminated, or NULL. Currently
4345 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4346 : * to specify the block size of the cached array.
4347 : * @return true in case of success.
4348 : */
4349 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4350 : {
4351 14 : std::string osCacheFilename;
4352 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4353 7 : if (!poRG)
4354 1 : return false;
4355 :
4356 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4357 6 : if (poRG->OpenMDArray(osCachedArrayName))
4358 : {
4359 2 : CPLError(CE_Failure, CPLE_NotSupported,
4360 : "An array with same name %s already exists in %s",
4361 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4362 2 : return false;
4363 : }
4364 :
4365 8 : CPLStringList aosOptions;
4366 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4367 4 : const auto &aoDims = GetDimensions();
4368 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4369 4 : if (!aoDims.empty())
4370 : {
4371 : std::string osBlockSize(
4372 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4373 4 : if (osBlockSize.empty())
4374 : {
4375 6 : const auto anBlockSize = GetBlockSize();
4376 3 : int idxDim = 0;
4377 10 : for (auto nBlockSize : anBlockSize)
4378 : {
4379 7 : if (idxDim > 0)
4380 4 : osBlockSize += ',';
4381 7 : if (nBlockSize == 0)
4382 7 : nBlockSize = 256;
4383 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4384 : osBlockSize +=
4385 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4386 7 : idxDim++;
4387 : }
4388 : }
4389 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4390 :
4391 4 : int idxDim = 0;
4392 13 : for (const auto &poDim : aoDims)
4393 : {
4394 9 : auto poNewDim = poRG->CreateDimension(
4395 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4396 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4397 9 : if (!poNewDim)
4398 0 : return false;
4399 9 : aoNewDims.emplace_back(poNewDim);
4400 9 : idxDim++;
4401 : }
4402 : }
4403 :
4404 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4405 8 : GetDataType(), aosOptions.List());
4406 4 : if (!poCachedArray)
4407 : {
4408 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4409 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4410 0 : return false;
4411 : }
4412 :
4413 4 : GUInt64 nCost = 0;
4414 8 : return poCachedArray->CopyFrom(nullptr, this,
4415 : false, // strict
4416 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4417 : }
4418 :
4419 : /************************************************************************/
4420 : /* Read() */
4421 : /************************************************************************/
4422 :
4423 3741 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4424 : const GInt64 *arrayStep, // step in elements
4425 : const GPtrDiff_t *bufferStride, // stride in elements
4426 : const GDALExtendedDataType &bufferDataType,
4427 : void *pDstBuffer, const void *pDstBufferAllocStart,
4428 : size_t nDstBufferAllocSize) const
4429 : {
4430 3741 : if (!m_bHasTriedCachedArray)
4431 : {
4432 1649 : m_bHasTriedCachedArray = true;
4433 1649 : if (IsCacheable())
4434 : {
4435 1649 : const auto &osFilename = GetFilename();
4436 2804 : if (!osFilename.empty() &&
4437 2804 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4438 : {
4439 2290 : std::string osCacheFilename;
4440 2290 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4441 1145 : if (poRG)
4442 : {
4443 : const std::string osCachedArrayName(
4444 32 : MassageName(GetFullName()));
4445 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4446 16 : if (m_poCachedArray)
4447 : {
4448 6 : const auto &dims = GetDimensions();
4449 : const auto &cachedDims =
4450 6 : m_poCachedArray->GetDimensions();
4451 6 : const size_t nDims = dims.size();
4452 : bool ok =
4453 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4454 6 : cachedDims.size() == nDims;
4455 19 : for (size_t i = 0; ok && i < nDims; ++i)
4456 : {
4457 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4458 : }
4459 6 : if (ok)
4460 : {
4461 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4462 : osCachedArrayName.c_str(),
4463 : osCacheFilename.c_str());
4464 : }
4465 : else
4466 : {
4467 0 : CPLError(CE_Warning, CPLE_AppDefined,
4468 : "Cached array %s in %s has incompatible "
4469 : "characteristics with current array.",
4470 : osCachedArrayName.c_str(),
4471 : osCacheFilename.c_str());
4472 0 : m_poCachedArray.reset();
4473 : }
4474 : }
4475 : }
4476 : }
4477 : }
4478 : }
4479 :
4480 3741 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4481 3741 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4482 : {
4483 0 : CPLError(CE_Failure, CPLE_AppDefined,
4484 : "Array data type is not convertible to buffer data type");
4485 0 : return false;
4486 : }
4487 :
4488 7482 : std::vector<GInt64> tmp_arrayStep;
4489 7482 : std::vector<GPtrDiff_t> tmp_bufferStride;
4490 3741 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4491 : bufferStride, bufferDataType, pDstBuffer,
4492 : pDstBufferAllocStart, nDstBufferAllocSize,
4493 : tmp_arrayStep, tmp_bufferStride))
4494 : {
4495 9 : return false;
4496 : }
4497 :
4498 3732 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4499 3732 : bufferDataType, pDstBuffer);
4500 : }
4501 :
4502 : /************************************************************************/
4503 : /* GetRootGroup() */
4504 : /************************************************************************/
4505 :
4506 : /** Return the root group to which this arrays belongs too.
4507 : *
4508 : * Note that arrays may be free standing and some drivers may not implement
4509 : * this method, hence nullptr may be returned.
4510 : *
4511 : * It is used internally by the GetResampled() method to detect if GLT
4512 : * orthorectification is available.
4513 : *
4514 : * @return the root group, or nullptr.
4515 : * @since GDAL 3.8
4516 : */
4517 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4518 : {
4519 0 : return nullptr;
4520 : }
4521 :
4522 : //! @cond Doxygen_Suppress
4523 :
4524 : /************************************************************************/
4525 : /* IsTransposedRequest() */
4526 : /************************************************************************/
4527 :
4528 689 : bool GDALMDArray::IsTransposedRequest(
4529 : const size_t *count,
4530 : const GPtrDiff_t *bufferStride) const // stride in elements
4531 : {
4532 : /*
4533 : For example:
4534 : count = [2,3,4]
4535 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4536 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4537 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4538 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4539 : */
4540 689 : const size_t nDims(GetDimensionCount());
4541 689 : size_t nCurStrideForRowMajorStrides = 1;
4542 689 : bool bRowMajorStrides = true;
4543 689 : size_t nElts = 1;
4544 689 : size_t nLastIdx = 0;
4545 1954 : for (size_t i = nDims; i > 0;)
4546 : {
4547 1265 : --i;
4548 1265 : if (bufferStride[i] < 0)
4549 0 : return false;
4550 1265 : if (static_cast<size_t>(bufferStride[i]) !=
4551 : nCurStrideForRowMajorStrides)
4552 : {
4553 221 : bRowMajorStrides = false;
4554 : }
4555 : // Integer overflows have already been checked in CheckReadWriteParams()
4556 1265 : nCurStrideForRowMajorStrides *= count[i];
4557 1265 : nElts *= count[i];
4558 1265 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4559 : }
4560 689 : if (bRowMajorStrides)
4561 540 : return false;
4562 149 : return nLastIdx == nElts - 1;
4563 : }
4564 :
4565 : /************************************************************************/
4566 : /* CopyToFinalBufferSameDataType() */
4567 : /************************************************************************/
4568 :
4569 : template <size_t N>
4570 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4571 : size_t nDims, const size_t *count,
4572 : const GPtrDiff_t *bufferStride)
4573 : {
4574 120 : std::vector<size_t> anStackCount(nDims);
4575 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4576 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4577 : #if defined(__GNUC__)
4578 : #pragma GCC diagnostic push
4579 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4580 : #endif
4581 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4582 : #if defined(__GNUC__)
4583 : #pragma GCC diagnostic pop
4584 : #endif
4585 60 : size_t iDim = 0;
4586 :
4587 749 : lbl_next_depth:
4588 749 : if (iDim == nDims - 1)
4589 : {
4590 661 : size_t n = count[iDim];
4591 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4592 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4593 29186 : while (n > 0)
4594 : {
4595 28525 : --n;
4596 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4597 28525 : pabyDstBuffer += bufferStrideLastDim;
4598 28525 : pabySrcBuffer += N;
4599 : }
4600 : }
4601 : else
4602 : {
4603 88 : anStackCount[iDim] = count[iDim];
4604 : while (true)
4605 : {
4606 689 : ++iDim;
4607 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4608 689 : goto lbl_next_depth;
4609 689 : lbl_return_to_caller_in_loop:
4610 689 : --iDim;
4611 689 : --anStackCount[iDim];
4612 689 : if (anStackCount[iDim] == 0)
4613 88 : break;
4614 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4615 : }
4616 : }
4617 749 : if (iDim > 0)
4618 689 : goto lbl_return_to_caller_in_loop;
4619 60 : }
4620 :
4621 : /************************************************************************/
4622 : /* CopyToFinalBuffer() */
4623 : /************************************************************************/
4624 :
4625 129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4626 : const GDALExtendedDataType &eSrcDataType,
4627 : void *pDstBuffer,
4628 : const GDALExtendedDataType &eDstDataType,
4629 : size_t nDims, const size_t *count,
4630 : const GPtrDiff_t *bufferStride)
4631 : {
4632 129 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4633 : // Use specialized implementation for well-known data types when no
4634 : // type conversion is needed
4635 129 : if (eSrcDataType == eDstDataType)
4636 : {
4637 110 : if (nSrcDataTypeSize == 1)
4638 : {
4639 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4640 : count, bufferStride);
4641 60 : return;
4642 : }
4643 69 : else if (nSrcDataTypeSize == 2)
4644 : {
4645 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4646 : count, bufferStride);
4647 1 : return;
4648 : }
4649 68 : else if (nSrcDataTypeSize == 4)
4650 : {
4651 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4652 : count, bufferStride);
4653 14 : return;
4654 : }
4655 54 : else if (nSrcDataTypeSize == 8)
4656 : {
4657 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4658 : count, bufferStride);
4659 4 : return;
4660 : }
4661 : }
4662 :
4663 69 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4664 138 : std::vector<size_t> anStackCount(nDims);
4665 138 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4666 69 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4667 69 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4668 69 : size_t iDim = 0;
4669 :
4670 338 : lbl_next_depth:
4671 338 : if (iDim == nDims - 1)
4672 : {
4673 328 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4674 328 : pabyDstBufferStack[iDim], eDstDataType,
4675 328 : bufferStride[iDim], count[iDim]);
4676 328 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4677 : }
4678 : else
4679 : {
4680 10 : anStackCount[iDim] = count[iDim];
4681 : while (true)
4682 : {
4683 269 : ++iDim;
4684 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4685 269 : goto lbl_next_depth;
4686 269 : lbl_return_to_caller_in_loop:
4687 269 : --iDim;
4688 269 : --anStackCount[iDim];
4689 269 : if (anStackCount[iDim] == 0)
4690 10 : break;
4691 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4692 : }
4693 : }
4694 338 : if (iDim > 0)
4695 269 : goto lbl_return_to_caller_in_loop;
4696 : }
4697 :
4698 : /************************************************************************/
4699 : /* TransposeLast2Dims() */
4700 : /************************************************************************/
4701 :
4702 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4703 : const GDALExtendedDataType &eDT,
4704 : const size_t nDims, const size_t *count,
4705 : const size_t nEltsNonLast2Dims)
4706 : {
4707 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4708 19 : const auto nDTSize = eDT.GetSize();
4709 : void *pTempBufferForLast2DimsTranspose =
4710 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4711 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4712 0 : return false;
4713 :
4714 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4715 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4716 : {
4717 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4718 : pTempBufferForLast2DimsTranspose,
4719 39 : eDT.GetNumericDataType(), count[nDims - 1],
4720 39 : count[nDims - 2]);
4721 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4722 : nDTSize * nEltsLast2Dims);
4723 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4724 : }
4725 :
4726 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4727 :
4728 19 : return true;
4729 : }
4730 :
4731 : /************************************************************************/
4732 : /* ReadForTransposedRequest() */
4733 : /************************************************************************/
4734 :
4735 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4736 : // transposed view yield to extremely poor/unusable performance. This fixes
4737 : // this by using temporary memory to read in a contiguous buffer in a
4738 : // row-major order, and then do the transposition to the final buffer.
4739 :
4740 148 : bool GDALMDArray::ReadForTransposedRequest(
4741 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4742 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4743 : void *pDstBuffer) const
4744 : {
4745 148 : const size_t nDims(GetDimensionCount());
4746 148 : if (nDims == 0)
4747 : {
4748 0 : CPLAssert(false);
4749 : return false; // shouldn't happen
4750 : }
4751 148 : size_t nElts = 1;
4752 418 : for (size_t i = 0; i < nDims; ++i)
4753 270 : nElts *= count[i];
4754 :
4755 296 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4756 148 : tmpBufferStrides.back() = 1;
4757 270 : for (size_t i = nDims - 1; i > 0;)
4758 : {
4759 122 : --i;
4760 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4761 : }
4762 :
4763 148 : const auto &eDT = GetDataType();
4764 148 : const auto nDTSize = eDT.GetSize();
4765 277 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4766 293 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4767 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4768 : {
4769 : // Optimization of the optimization if only the last 2 dims are
4770 : // transposed that saves on temporary buffer allocation
4771 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4772 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4773 23 : bool bRowMajorStridesForNonLast2Dims = true;
4774 23 : size_t nEltsNonLast2Dims = 1;
4775 40 : for (size_t i = nDims - 2; i > 0;)
4776 : {
4777 17 : --i;
4778 17 : if (static_cast<size_t>(bufferStride[i]) !=
4779 : nCurStrideForRowMajorStrides)
4780 : {
4781 4 : bRowMajorStridesForNonLast2Dims = false;
4782 : }
4783 : // Integer overflows have already been checked in
4784 : // CheckReadWriteParams()
4785 17 : nCurStrideForRowMajorStrides *= count[i];
4786 17 : nEltsNonLast2Dims *= count[i];
4787 : }
4788 23 : if (bRowMajorStridesForNonLast2Dims)
4789 : {
4790 : // We read in the final buffer!
4791 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4792 19 : eDT, pDstBuffer))
4793 : {
4794 0 : return false;
4795 : }
4796 :
4797 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4798 19 : nEltsNonLast2Dims);
4799 : }
4800 : }
4801 :
4802 129 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4803 129 : if (pTempBuffer == nullptr)
4804 0 : return false;
4805 :
4806 129 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4807 129 : pTempBuffer))
4808 : {
4809 0 : VSIFree(pTempBuffer);
4810 0 : return false;
4811 : }
4812 129 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4813 : count, bufferStride);
4814 :
4815 129 : if (eDT.NeedsFreeDynamicMemory())
4816 : {
4817 58 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4818 116 : for (size_t i = 0; i < nElts; ++i)
4819 : {
4820 58 : eDT.FreeDynamicMemory(pabyPtr);
4821 58 : pabyPtr += nDTSize;
4822 : }
4823 : }
4824 :
4825 129 : VSIFree(pTempBuffer);
4826 129 : return true;
4827 : }
4828 :
4829 : /************************************************************************/
4830 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4831 : /************************************************************************/
4832 :
4833 : // Returns true if at all following conditions are met:
4834 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4835 : // defines a row-major ordered contiguous buffer.
4836 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4837 : const size_t *count, const GInt64 *arrayStep,
4838 : const GPtrDiff_t *bufferStride,
4839 : const GDALExtendedDataType &bufferDataType) const
4840 : {
4841 78 : if (bufferDataType != GetDataType())
4842 5 : return false;
4843 73 : size_t nExpectedStride = 1;
4844 166 : for (size_t i = GetDimensionCount(); i > 0;)
4845 : {
4846 96 : --i;
4847 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4848 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4849 : {
4850 3 : return false;
4851 : }
4852 93 : nExpectedStride *= count[i];
4853 : }
4854 70 : return true;
4855 : }
4856 :
4857 : /************************************************************************/
4858 : /* ReadUsingContiguousIRead() */
4859 : /************************************************************************/
4860 :
4861 : // Used for example by the TileDB driver when requesting it with
4862 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4863 : // not defining a row-major ordered contiguous buffer.
4864 : // Should only be called when at least one of the above conditions are true,
4865 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4866 : // returning none.
4867 : // This method will call IRead() again with arrayStep[] == 1,
4868 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4869 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4870 : // content of that temporary buffer onto pDstBuffer.
4871 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4872 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4873 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4874 : void *pDstBuffer) const
4875 : {
4876 7 : const size_t nDims(GetDimensionCount());
4877 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4878 14 : std::vector<size_t> anTmpCount(nDims);
4879 7 : const auto &oType = GetDataType();
4880 7 : size_t nMemArraySize = oType.GetSize();
4881 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4882 7 : GPtrDiff_t nStride = 1;
4883 18 : for (size_t i = nDims; i > 0;)
4884 : {
4885 11 : --i;
4886 11 : if (arrayStep[i] > 0)
4887 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4888 : else
4889 2 : anTmpStartIdx[i] =
4890 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4891 : const uint64_t nCount =
4892 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4893 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4894 : {
4895 0 : CPLError(CE_Failure, CPLE_AppDefined,
4896 : "Read() failed due to too large memory requirement");
4897 0 : return false;
4898 : }
4899 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4900 11 : nMemArraySize *= anTmpCount[i];
4901 11 : anTmpStride[i] = nStride;
4902 11 : nStride *= anTmpCount[i];
4903 : }
4904 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4905 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4906 7 : if (!pTmpBuffer)
4907 0 : return false;
4908 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4909 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4910 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4911 : {
4912 0 : return false;
4913 : }
4914 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
4915 18 : for (size_t i = 0; i < nDims; ++i)
4916 : {
4917 11 : if (arrayStep[i] > 0)
4918 9 : anTmpStartIdx[i] = 0;
4919 : else
4920 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
4921 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
4922 22 : std::string(), std::string(), std::string(), std::string(),
4923 22 : anTmpCount[i]);
4924 : }
4925 : auto poMEMArray =
4926 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
4927 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
4928 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
4929 7 : bufferStride, bufferDataType, pDstBuffer);
4930 : }
4931 :
4932 : //! @endcond
4933 :
4934 : /************************************************************************/
4935 : /* GDALSlicedMDArray */
4936 : /************************************************************************/
4937 :
4938 : class GDALSlicedMDArray final : public GDALPamMDArray
4939 : {
4940 : private:
4941 : std::shared_ptr<GDALMDArray> m_poParent{};
4942 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4943 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
4944 : std::vector<Range>
4945 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
4946 :
4947 : mutable std::vector<GUInt64> m_parentStart;
4948 : mutable std::vector<size_t> m_parentCount;
4949 : mutable std::vector<GInt64> m_parentStep;
4950 : mutable std::vector<GPtrDiff_t> m_parentStride;
4951 :
4952 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
4953 : const GInt64 *arrayStep,
4954 : const GPtrDiff_t *bufferStride) const;
4955 :
4956 : protected:
4957 574 : explicit GDALSlicedMDArray(
4958 : const std::shared_ptr<GDALMDArray> &poParent,
4959 : const std::string &viewExpr,
4960 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4961 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
4962 : std::vector<Range> &&parentRanges)
4963 1722 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
4964 1722 : poParent->GetFullName() +
4965 1148 : " (" + viewExpr + ")"),
4966 1148 : GDALPamMDArray(std::string(),
4967 1148 : "Sliced view of " + poParent->GetFullName() + " (" +
4968 1148 : viewExpr + ")",
4969 1148 : GDALPamMultiDim::GetPAM(poParent),
4970 : poParent->GetContext()),
4971 1148 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
4972 574 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
4973 574 : m_parentRanges(std::move(parentRanges)),
4974 574 : m_parentStart(m_poParent->GetDimensionCount()),
4975 574 : m_parentCount(m_poParent->GetDimensionCount(), 1),
4976 574 : m_parentStep(m_poParent->GetDimensionCount()),
4977 4592 : m_parentStride(m_poParent->GetDimensionCount())
4978 : {
4979 574 : }
4980 :
4981 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
4982 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4983 : const GDALExtendedDataType &bufferDataType,
4984 : void *pDstBuffer) const override;
4985 :
4986 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
4987 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4988 : const GDALExtendedDataType &bufferDataType,
4989 : const void *pSrcBuffer) override;
4990 :
4991 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4992 : CSLConstList papszOptions) const override;
4993 :
4994 : public:
4995 : static std::shared_ptr<GDALSlicedMDArray>
4996 574 : Create(const std::shared_ptr<GDALMDArray> &poParent,
4997 : const std::string &viewExpr,
4998 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4999 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5000 : std::vector<Range> &&parentRanges)
5001 : {
5002 574 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5003 574 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5004 :
5005 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5006 574 : poParent, viewExpr, std::move(dims),
5007 574 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5008 574 : newAr->SetSelf(newAr);
5009 574 : return newAr;
5010 : }
5011 :
5012 55 : bool IsWritable() const override
5013 : {
5014 55 : return m_poParent->IsWritable();
5015 : }
5016 :
5017 983 : const std::string &GetFilename() const override
5018 : {
5019 983 : return m_poParent->GetFilename();
5020 : }
5021 :
5022 : const std::vector<std::shared_ptr<GDALDimension>> &
5023 3646 : GetDimensions() const override
5024 : {
5025 3646 : return m_dims;
5026 : }
5027 :
5028 1383 : const GDALExtendedDataType &GetDataType() const override
5029 : {
5030 1383 : return m_poParent->GetDataType();
5031 : }
5032 :
5033 2 : const std::string &GetUnit() const override
5034 : {
5035 2 : return m_poParent->GetUnit();
5036 : }
5037 :
5038 : // bool SetUnit(const std::string& osUnit) override { return
5039 : // m_poParent->SetUnit(osUnit); }
5040 :
5041 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5042 : {
5043 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5044 2 : if (!poSrcSRS)
5045 1 : return nullptr;
5046 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5047 2 : std::vector<int> dstMapping;
5048 3 : for (int srcAxis : srcMapping)
5049 : {
5050 2 : bool bFound = false;
5051 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5052 : {
5053 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5054 3 : srcAxis - 1)
5055 : {
5056 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5057 2 : bFound = true;
5058 2 : break;
5059 : }
5060 : }
5061 2 : if (!bFound)
5062 : {
5063 0 : dstMapping.push_back(0);
5064 : }
5065 : }
5066 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5067 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5068 1 : return poClone;
5069 : }
5070 :
5071 55 : const void *GetRawNoDataValue() const override
5072 : {
5073 55 : return m_poParent->GetRawNoDataValue();
5074 : }
5075 :
5076 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5077 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5078 :
5079 2 : double GetOffset(bool *pbHasOffset,
5080 : GDALDataType *peStorageType) const override
5081 : {
5082 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5083 : }
5084 :
5085 2 : double GetScale(bool *pbHasScale,
5086 : GDALDataType *peStorageType) const override
5087 : {
5088 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5089 : }
5090 :
5091 : // bool SetOffset(double dfOffset) override { return
5092 : // m_poParent->SetOffset(dfOffset); }
5093 :
5094 : // bool SetScale(double dfScale) override { return
5095 : // m_poParent->SetScale(dfScale); }
5096 :
5097 197 : std::vector<GUInt64> GetBlockSize() const override
5098 : {
5099 197 : std::vector<GUInt64> ret(GetDimensionCount());
5100 394 : const auto parentBlockSize(m_poParent->GetBlockSize());
5101 595 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5102 : {
5103 398 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5104 398 : if (iOldAxis != static_cast<size_t>(-1))
5105 : {
5106 398 : ret[i] = parentBlockSize[iOldAxis];
5107 : }
5108 : }
5109 394 : return ret;
5110 : }
5111 :
5112 : std::shared_ptr<GDALAttribute>
5113 6 : GetAttribute(const std::string &osName) const override
5114 : {
5115 6 : return m_poParent->GetAttribute(osName);
5116 : }
5117 :
5118 : std::vector<std::shared_ptr<GDALAttribute>>
5119 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5120 : {
5121 24 : return m_poParent->GetAttributes(papszOptions);
5122 : }
5123 : };
5124 :
5125 : /************************************************************************/
5126 : /* PrepareParentArrays() */
5127 : /************************************************************************/
5128 :
5129 475 : void GDALSlicedMDArray::PrepareParentArrays(
5130 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5131 : const GPtrDiff_t *bufferStride) const
5132 : {
5133 475 : const size_t nParentDimCount = m_parentRanges.size();
5134 1481 : for (size_t i = 0; i < nParentDimCount; i++)
5135 : {
5136 : // For dimensions in parent that have no existence in sliced array
5137 1006 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5138 : }
5139 :
5140 1250 : for (size_t i = 0; i < m_dims.size(); i++)
5141 : {
5142 775 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5143 775 : if (iParent != static_cast<size_t>(-1))
5144 : {
5145 773 : m_parentStart[iParent] =
5146 773 : m_parentRanges[iParent].m_nIncr >= 0
5147 773 : ? m_parentRanges[iParent].m_nStartIdx +
5148 744 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5149 29 : : m_parentRanges[iParent].m_nStartIdx -
5150 58 : arrayStartIdx[i] *
5151 29 : static_cast<GUInt64>(
5152 29 : -m_parentRanges[iParent].m_nIncr);
5153 773 : m_parentCount[iParent] = count[i];
5154 773 : if (arrayStep)
5155 : {
5156 772 : m_parentStep[iParent] =
5157 772 : count[i] == 1 ? 1 :
5158 : // other checks should have ensured this does
5159 : // not overflow
5160 586 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5161 : }
5162 773 : if (bufferStride)
5163 : {
5164 772 : m_parentStride[iParent] = bufferStride[i];
5165 : }
5166 : }
5167 : }
5168 475 : }
5169 :
5170 : /************************************************************************/
5171 : /* IRead() */
5172 : /************************************************************************/
5173 :
5174 442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5175 : const GInt64 *arrayStep,
5176 : const GPtrDiff_t *bufferStride,
5177 : const GDALExtendedDataType &bufferDataType,
5178 : void *pDstBuffer) const
5179 : {
5180 442 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5181 884 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5182 442 : m_parentStep.data(), m_parentStride.data(),
5183 442 : bufferDataType, pDstBuffer);
5184 : }
5185 :
5186 : /************************************************************************/
5187 : /* IWrite() */
5188 : /************************************************************************/
5189 :
5190 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5191 : const size_t *count, const GInt64 *arrayStep,
5192 : const GPtrDiff_t *bufferStride,
5193 : const GDALExtendedDataType &bufferDataType,
5194 : const void *pSrcBuffer)
5195 : {
5196 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5197 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5198 32 : m_parentStep.data(), m_parentStride.data(),
5199 32 : bufferDataType, pSrcBuffer);
5200 : }
5201 :
5202 : /************************************************************************/
5203 : /* IAdviseRead() */
5204 : /************************************************************************/
5205 :
5206 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5207 : const size_t *count,
5208 : CSLConstList papszOptions) const
5209 : {
5210 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5211 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5212 1 : papszOptions);
5213 : }
5214 :
5215 : /************************************************************************/
5216 : /* CreateSlicedArray() */
5217 : /************************************************************************/
5218 :
5219 : static std::shared_ptr<GDALMDArray>
5220 592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5221 : const std::string &viewExpr, const std::string &activeSlice,
5222 : bool bRenameDimensions,
5223 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5224 : {
5225 592 : const auto &srcDims(self->GetDimensions());
5226 592 : if (srcDims.empty())
5227 : {
5228 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5229 2 : return nullptr;
5230 : }
5231 :
5232 1180 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5233 590 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5234 :
5235 1180 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5236 1180 : std::vector<size_t> mapDimIdxToParentDimIdx;
5237 1180 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5238 590 : newDims.reserve(nTokens);
5239 590 : mapDimIdxToParentDimIdx.reserve(nTokens);
5240 590 : parentRanges.reserve(nTokens);
5241 :
5242 590 : bool bGotEllipsis = false;
5243 590 : size_t nCurSrcDim = 0;
5244 1744 : for (size_t i = 0; i < nTokens; i++)
5245 : {
5246 1170 : const char *pszIdxSpec = aosTokens[i];
5247 1170 : if (EQUAL(pszIdxSpec, "..."))
5248 : {
5249 37 : if (bGotEllipsis)
5250 : {
5251 2 : CPLError(CE_Failure, CPLE_AppDefined,
5252 : "Only one single ellipsis is supported");
5253 2 : return nullptr;
5254 : }
5255 35 : bGotEllipsis = true;
5256 35 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5257 77 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5258 : {
5259 42 : parentRanges.emplace_back(0, 1);
5260 42 : newDims.push_back(srcDims[nCurSrcDim]);
5261 42 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5262 : }
5263 35 : continue;
5264 : }
5265 1133 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5266 1130 : EQUAL(pszIdxSpec, "np.newaxis"))
5267 : {
5268 3 : newDims.push_back(std::make_shared<GDALDimension>(
5269 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5270 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5271 3 : continue;
5272 : }
5273 1130 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5274 : {
5275 323 : if (nCurSrcDim >= srcDims.size())
5276 : {
5277 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5278 : activeSlice.c_str());
5279 7 : return nullptr;
5280 : }
5281 :
5282 321 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5283 321 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5284 321 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5285 317 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5286 : {
5287 5 : CPLError(CE_Failure, CPLE_AppDefined,
5288 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5289 5 : return nullptr;
5290 : }
5291 316 : if (nVal < 0)
5292 0 : nVal += nDimSize;
5293 316 : parentRanges.emplace_back(nVal, 0);
5294 : }
5295 : else
5296 : {
5297 807 : if (nCurSrcDim >= srcDims.size())
5298 : {
5299 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5300 : activeSlice.c_str());
5301 7 : return nullptr;
5302 : }
5303 :
5304 : CPLStringList aosRangeTokens(
5305 806 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5306 806 : int nRangeTokens = aosRangeTokens.size();
5307 806 : if (nRangeTokens > 3)
5308 : {
5309 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5310 : pszIdxSpec);
5311 1 : return nullptr;
5312 : }
5313 805 : if (nRangeTokens <= 1)
5314 : {
5315 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5316 : pszIdxSpec);
5317 1 : return nullptr;
5318 : }
5319 804 : const char *pszStart = aosRangeTokens[0];
5320 804 : const char *pszEnd = aosRangeTokens[1];
5321 804 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5322 804 : GDALSlicedMDArray::Range range;
5323 804 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5324 804 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5325 1607 : if (range.m_nIncr == 0 ||
5326 803 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5327 : {
5328 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5329 1 : return nullptr;
5330 : }
5331 803 : auto startIdx(CPLAtoGIntBig(pszStart));
5332 803 : if (startIdx < 0)
5333 : {
5334 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5335 0 : startIdx = 0;
5336 : else
5337 0 : startIdx = nDimSize + startIdx;
5338 : }
5339 803 : const bool bPosIncr = range.m_nIncr > 0;
5340 803 : range.m_nStartIdx = startIdx;
5341 1606 : range.m_nStartIdx = EQUAL(pszStart, "")
5342 803 : ? (bPosIncr ? 0 : nDimSize - 1)
5343 : : range.m_nStartIdx;
5344 803 : if (range.m_nStartIdx >= nDimSize - 1)
5345 185 : range.m_nStartIdx = nDimSize - 1;
5346 803 : auto endIdx(CPLAtoGIntBig(pszEnd));
5347 803 : if (endIdx < 0)
5348 : {
5349 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5350 1 : if (nDimSize < positiveEndIdx)
5351 0 : endIdx = 0;
5352 : else
5353 1 : endIdx = nDimSize - positiveEndIdx;
5354 : }
5355 803 : GUInt64 nEndIdx = endIdx;
5356 803 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5357 803 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5358 801 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5359 : {
5360 3 : CPLError(CE_Failure, CPLE_AppDefined,
5361 : "Output dimension of size 0 is not allowed");
5362 3 : return nullptr;
5363 : }
5364 800 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5365 800 : const auto nAbsIncr = std::abs(range.m_nIncr);
5366 800 : const GUInt64 newSize =
5367 : bPosIncr
5368 833 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5369 33 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5370 1322 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5371 522 : newSize == srcDims[nCurSrcDim]->GetSize())
5372 : {
5373 153 : newDims.push_back(srcDims[nCurSrcDim]);
5374 : }
5375 : else
5376 : {
5377 647 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5378 647 : if (bRenameDimensions)
5379 : {
5380 : osNewDimName =
5381 1210 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5382 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5383 : "_" CPL_FRMT_GUIB,
5384 605 : static_cast<GUIntBig>(range.m_nStartIdx),
5385 605 : static_cast<GIntBig>(range.m_nIncr),
5386 605 : static_cast<GUIntBig>(newSize));
5387 : }
5388 647 : newDims.push_back(std::make_shared<GDALDimension>(
5389 1294 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5390 1294 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5391 : : std::string(),
5392 : newSize));
5393 : }
5394 800 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5395 800 : parentRanges.emplace_back(range);
5396 : }
5397 :
5398 1116 : nCurSrcDim++;
5399 : }
5400 647 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5401 : {
5402 73 : parentRanges.emplace_back(0, 1);
5403 73 : newDims.push_back(srcDims[nCurSrcDim]);
5404 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5405 : }
5406 :
5407 574 : GDALMDArray::ViewSpec viewSpec;
5408 574 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5409 574 : viewSpec.m_parentRanges = parentRanges;
5410 574 : viewSpecs.emplace_back(std::move(viewSpec));
5411 :
5412 1148 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5413 574 : std::move(mapDimIdxToParentDimIdx),
5414 1148 : std::move(parentRanges));
5415 : }
5416 :
5417 : /************************************************************************/
5418 : /* GDALExtractFieldMDArray */
5419 : /************************************************************************/
5420 :
5421 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5422 : {
5423 : private:
5424 : std::shared_ptr<GDALMDArray> m_poParent{};
5425 : GDALExtendedDataType m_dt;
5426 : std::string m_srcCompName;
5427 : mutable std::vector<GByte> m_pabyNoData{};
5428 :
5429 : protected:
5430 62 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5431 : const std::string &fieldName,
5432 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5433 248 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5434 124 : " of " +
5435 62 : poParent->GetFullName()),
5436 : GDALPamMDArray(
5437 124 : std::string(),
5438 124 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5439 124 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5440 : m_poParent(poParent), m_dt(srcComp->GetType()),
5441 310 : m_srcCompName(srcComp->GetName())
5442 : {
5443 62 : m_pabyNoData.resize(m_dt.GetSize());
5444 62 : }
5445 :
5446 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5447 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5448 : const GDALExtendedDataType &bufferDataType,
5449 : void *pDstBuffer) const override;
5450 :
5451 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5452 : CSLConstList papszOptions) const override
5453 : {
5454 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5455 : }
5456 :
5457 : public:
5458 : static std::shared_ptr<GDALExtractFieldMDArray>
5459 62 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5460 : const std::string &fieldName,
5461 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5462 : {
5463 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5464 62 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5465 62 : newAr->SetSelf(newAr);
5466 62 : return newAr;
5467 : }
5468 :
5469 124 : ~GDALExtractFieldMDArray()
5470 62 : {
5471 62 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5472 124 : }
5473 :
5474 40 : bool IsWritable() const override
5475 : {
5476 40 : return m_poParent->IsWritable();
5477 : }
5478 :
5479 204 : const std::string &GetFilename() const override
5480 : {
5481 204 : return m_poParent->GetFilename();
5482 : }
5483 :
5484 : const std::vector<std::shared_ptr<GDALDimension>> &
5485 300 : GetDimensions() const override
5486 : {
5487 300 : return m_poParent->GetDimensions();
5488 : }
5489 :
5490 246 : const GDALExtendedDataType &GetDataType() const override
5491 : {
5492 246 : return m_dt;
5493 : }
5494 :
5495 2 : const std::string &GetUnit() const override
5496 : {
5497 2 : return m_poParent->GetUnit();
5498 : }
5499 :
5500 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5501 : {
5502 2 : return m_poParent->GetSpatialRef();
5503 : }
5504 :
5505 56 : const void *GetRawNoDataValue() const override
5506 : {
5507 56 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5508 56 : if (parentNoData == nullptr)
5509 1 : return nullptr;
5510 :
5511 55 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5512 55 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5513 :
5514 110 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5515 110 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5516 110 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5517 55 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5518 165 : std::move(comps)));
5519 :
5520 55 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5521 55 : &m_pabyNoData[0], tmpDT);
5522 :
5523 55 : return &m_pabyNoData[0];
5524 : }
5525 :
5526 2 : double GetOffset(bool *pbHasOffset,
5527 : GDALDataType *peStorageType) const override
5528 : {
5529 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5530 : }
5531 :
5532 2 : double GetScale(bool *pbHasScale,
5533 : GDALDataType *peStorageType) const override
5534 : {
5535 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5536 : }
5537 :
5538 41 : std::vector<GUInt64> GetBlockSize() const override
5539 : {
5540 41 : return m_poParent->GetBlockSize();
5541 : }
5542 : };
5543 :
5544 : /************************************************************************/
5545 : /* IRead() */
5546 : /************************************************************************/
5547 :
5548 46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5549 : const size_t *count,
5550 : const GInt64 *arrayStep,
5551 : const GPtrDiff_t *bufferStride,
5552 : const GDALExtendedDataType &bufferDataType,
5553 : void *pDstBuffer) const
5554 : {
5555 92 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5556 92 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5557 92 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5558 : auto tmpDT(GDALExtendedDataType::Create(
5559 92 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5560 :
5561 46 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5562 92 : tmpDT, pDstBuffer);
5563 : }
5564 :
5565 : /************************************************************************/
5566 : /* CreateFieldNameExtractArray() */
5567 : /************************************************************************/
5568 :
5569 : static std::shared_ptr<GDALMDArray>
5570 63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5571 : const std::string &fieldName)
5572 : {
5573 63 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5574 63 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5575 125 : for (const auto &comp : self->GetDataType().GetComponents())
5576 : {
5577 124 : if (comp->GetName() == fieldName)
5578 : {
5579 62 : srcComp = ∁
5580 62 : break;
5581 : }
5582 : }
5583 63 : if (srcComp == nullptr)
5584 : {
5585 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5586 : fieldName.c_str());
5587 1 : return nullptr;
5588 : }
5589 62 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5590 : }
5591 :
5592 : /************************************************************************/
5593 : /* GetView() */
5594 : /************************************************************************/
5595 :
5596 : // clang-format off
5597 : /** Return a view of the array using slicing or field access.
5598 : *
5599 : * The slice expression uses the same syntax as NumPy basic slicing and
5600 : * indexing. See
5601 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5602 : * Or it can use field access by name. See
5603 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5604 : *
5605 : * Multiple [] bracket elements can be concatenated, with a slice expression
5606 : * or field name inside each.
5607 : *
5608 : * For basic slicing and indexing, inside each [] bracket element, a list of
5609 : * indexes that apply to successive source dimensions, can be specified, using
5610 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5611 : * or newaxis, using a comma separator.
5612 : *
5613 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5614 : * <ul>
5615 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5616 : * at index 1 in the first dimension, and index 2 in the second dimension
5617 : * from the source array. That is 5</li>
5618 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5619 : * implemented internally doing this intermediate slicing approach.</li>
5620 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5621 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5622 : * first dimension. That is [4,5,6,7].</li>
5623 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5624 : * second dimension. That is [2,6].</li>
5625 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5626 : * the second dimension. That is [[2],[6]].</li>
5627 : * <li>GetView("[::,2]"): Same as
5628 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5629 : * ellipsis only expands to one dimension here.</li>
5630 : * <li>GetView("[:,::2]"):
5631 : * returns a 2-dimensional array, with even-indexed elements of the second
5632 : * dimension. That is [[0,2],[4,6]].</li>
5633 : * <li>GetView("[:,1::2]"): returns a
5634 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5635 : * is [[1,3],[5,7]].</li>
5636 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5637 : * array, with elements of the second dimension with index in the range [1,3[.
5638 : * That is [[1,2],[5,6]].</li>
5639 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5640 : * array, with the values in first dimension reversed. That is
5641 : * [[4,5,6,7],[0,1,2,3]].</li>
5642 : * <li>GetView("[newaxis,...]"): returns a
5643 : * 3-dimensional array, with an additional dimension of size 1 put at the
5644 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5645 : * </ul>
5646 : *
5647 : * One difference with NumPy behavior is that ranges that would result in
5648 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5649 : * GDAL multidimensional model).
5650 : *
5651 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5652 : * Multiple field specification is not supported currently.
5653 : *
5654 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5655 : *
5656 : * \note When using the GDAL Python bindings, natural Python syntax can be
5657 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5658 : * ar.GetView("[0,::,1]['foo']")
5659 : * \note When using the C++ API and integer indexing only, you may use the
5660 : * at(idx0, idx1, ...) method.
5661 : *
5662 : * The returned array holds a reference to the original one, and thus is
5663 : * a view of it (not a copy). If the content of the original array changes,
5664 : * the content of the view array too. When using basic slicing and indexing,
5665 : * the view can be written if the underlying array is writable.
5666 : *
5667 : * This is the same as the C function GDALMDArrayGetView()
5668 : *
5669 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5670 : * access.
5671 : * @return a new array, that holds a reference to the original one, and thus is
5672 : * a view of it (not a copy), or nullptr in case of error.
5673 : */
5674 : // clang-format on
5675 :
5676 : std::shared_ptr<GDALMDArray>
5677 598 : GDALMDArray::GetView(const std::string &viewExpr) const
5678 : {
5679 1196 : std::vector<ViewSpec> viewSpecs;
5680 1196 : return GetView(viewExpr, true, viewSpecs);
5681 : }
5682 :
5683 : //! @cond Doxygen_Suppress
5684 : std::shared_ptr<GDALMDArray>
5685 661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5686 : std::vector<ViewSpec> &viewSpecs) const
5687 : {
5688 1322 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5689 661 : if (!self)
5690 : {
5691 1 : CPLError(CE_Failure, CPLE_AppDefined,
5692 : "Driver implementation issue: m_pSelf not set !");
5693 1 : return nullptr;
5694 : }
5695 660 : std::string curExpr(viewExpr);
5696 : while (true)
5697 : {
5698 663 : if (curExpr.empty() || curExpr[0] != '[')
5699 : {
5700 2 : CPLError(CE_Failure, CPLE_AppDefined,
5701 : "Slice string should start with ['");
5702 660 : return nullptr;
5703 : }
5704 :
5705 661 : std::string fieldName;
5706 : size_t endExpr;
5707 661 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5708 : {
5709 67 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5710 : {
5711 2 : CPLError(CE_Failure, CPLE_AppDefined,
5712 : "Field access not allowed on non-compound data type");
5713 2 : return nullptr;
5714 : }
5715 65 : size_t idx = 2;
5716 572 : for (; idx < curExpr.size(); idx++)
5717 : {
5718 571 : const char ch = curExpr[idx];
5719 571 : if (ch == curExpr[1])
5720 64 : break;
5721 507 : if (ch == '\\' && idx + 1 < curExpr.size())
5722 : {
5723 1 : fieldName += curExpr[idx + 1];
5724 1 : idx++;
5725 : }
5726 : else
5727 : {
5728 506 : fieldName += ch;
5729 : }
5730 : }
5731 65 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5732 : {
5733 2 : CPLError(CE_Failure, CPLE_AppDefined,
5734 : "Invalid field access specification");
5735 2 : return nullptr;
5736 : }
5737 63 : endExpr = idx + 1;
5738 : }
5739 : else
5740 : {
5741 594 : endExpr = curExpr.find(']');
5742 : }
5743 657 : if (endExpr == std::string::npos)
5744 : {
5745 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5746 1 : return nullptr;
5747 : }
5748 656 : if (endExpr == 1)
5749 : {
5750 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5751 1 : return nullptr;
5752 : }
5753 655 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5754 :
5755 655 : if (!fieldName.empty())
5756 : {
5757 126 : ViewSpec viewSpec;
5758 63 : viewSpec.m_osFieldName = fieldName;
5759 63 : viewSpecs.emplace_back(std::move(viewSpec));
5760 : }
5761 :
5762 655 : auto newArray = !fieldName.empty()
5763 : ? CreateFieldNameExtractArray(self, fieldName)
5764 : : CreateSlicedArray(self, viewExpr, activeSlice,
5765 655 : bRenameDimensions, viewSpecs);
5766 :
5767 655 : if (endExpr == curExpr.size() - 1)
5768 : {
5769 652 : return newArray;
5770 : }
5771 3 : self = std::move(newArray);
5772 3 : curExpr = curExpr.substr(endExpr + 1);
5773 3 : }
5774 : }
5775 :
5776 : //! @endcond
5777 :
5778 : std::shared_ptr<GDALMDArray>
5779 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5780 : {
5781 19 : std::string osExpr("[");
5782 19 : bool bFirst = true;
5783 45 : for (const auto &idx : indices)
5784 : {
5785 26 : if (!bFirst)
5786 7 : osExpr += ',';
5787 26 : bFirst = false;
5788 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5789 : }
5790 57 : return GetView(osExpr + ']');
5791 : }
5792 :
5793 : /************************************************************************/
5794 : /* operator[] */
5795 : /************************************************************************/
5796 :
5797 : /** Return a view of the array using field access
5798 : *
5799 : * Equivalent of GetView("['fieldName']")
5800 : *
5801 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5802 : */
5803 : std::shared_ptr<GDALMDArray>
5804 2 : GDALMDArray::operator[](const std::string &fieldName) const
5805 : {
5806 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5807 4 : .replaceAll('\\', "\\\\")
5808 4 : .replaceAll('\'', "\\\'")
5809 6 : .c_str()));
5810 : }
5811 :
5812 : /************************************************************************/
5813 : /* GDALMDArrayTransposed */
5814 : /************************************************************************/
5815 :
5816 : class GDALMDArrayTransposed final : public GDALPamMDArray
5817 : {
5818 : private:
5819 : std::shared_ptr<GDALMDArray> m_poParent{};
5820 : std::vector<int> m_anMapNewAxisToOldAxis{};
5821 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5822 :
5823 : mutable std::vector<GUInt64> m_parentStart;
5824 : mutable std::vector<size_t> m_parentCount;
5825 : mutable std::vector<GInt64> m_parentStep;
5826 : mutable std::vector<GPtrDiff_t> m_parentStride;
5827 :
5828 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5829 : const GInt64 *arrayStep,
5830 : const GPtrDiff_t *bufferStride) const;
5831 :
5832 : static std::string
5833 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5834 : {
5835 84 : std::string ret;
5836 84 : ret += '[';
5837 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5838 : {
5839 228 : if (i > 0)
5840 144 : ret += ',';
5841 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5842 : }
5843 84 : ret += ']';
5844 84 : return ret;
5845 : }
5846 :
5847 : protected:
5848 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5849 : const std::vector<int> &anMapNewAxisToOldAxis,
5850 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5851 84 : : GDALAbstractMDArray(std::string(),
5852 84 : "Transposed view of " + poParent->GetFullName() +
5853 84 : " along " +
5854 42 : MappingToStr(anMapNewAxisToOldAxis)),
5855 84 : GDALPamMDArray(std::string(),
5856 84 : "Transposed view of " + poParent->GetFullName() +
5857 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5858 84 : GDALPamMultiDim::GetPAM(poParent),
5859 : poParent->GetContext()),
5860 42 : m_poParent(std::move(poParent)),
5861 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5862 42 : m_dims(std::move(dims)),
5863 42 : m_parentStart(m_poParent->GetDimensionCount()),
5864 42 : m_parentCount(m_poParent->GetDimensionCount()),
5865 42 : m_parentStep(m_poParent->GetDimensionCount()),
5866 336 : m_parentStride(m_poParent->GetDimensionCount())
5867 : {
5868 42 : }
5869 :
5870 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5871 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5872 : const GDALExtendedDataType &bufferDataType,
5873 : void *pDstBuffer) const override;
5874 :
5875 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5876 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5877 : const GDALExtendedDataType &bufferDataType,
5878 : const void *pSrcBuffer) override;
5879 :
5880 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5881 : CSLConstList papszOptions) const override;
5882 :
5883 : public:
5884 : static std::shared_ptr<GDALMDArrayTransposed>
5885 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5886 : const std::vector<int> &anMapNewAxisToOldAxis)
5887 : {
5888 42 : const auto &parentDims(poParent->GetDimensions());
5889 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5890 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5891 : {
5892 114 : if (iOldAxis < 0)
5893 : {
5894 1 : dims.push_back(std::make_shared<GDALDimension>(
5895 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5896 : }
5897 : else
5898 : {
5899 113 : dims.emplace_back(parentDims[iOldAxis]);
5900 : }
5901 : }
5902 :
5903 : auto newAr(
5904 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5905 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5906 42 : newAr->SetSelf(newAr);
5907 84 : return newAr;
5908 : }
5909 :
5910 1 : bool IsWritable() const override
5911 : {
5912 1 : return m_poParent->IsWritable();
5913 : }
5914 :
5915 84 : const std::string &GetFilename() const override
5916 : {
5917 84 : return m_poParent->GetFilename();
5918 : }
5919 :
5920 : const std::vector<std::shared_ptr<GDALDimension>> &
5921 358 : GetDimensions() const override
5922 : {
5923 358 : return m_dims;
5924 : }
5925 :
5926 141 : const GDALExtendedDataType &GetDataType() const override
5927 : {
5928 141 : return m_poParent->GetDataType();
5929 : }
5930 :
5931 4 : const std::string &GetUnit() const override
5932 : {
5933 4 : return m_poParent->GetUnit();
5934 : }
5935 :
5936 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5937 : {
5938 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
5939 5 : if (!poSrcSRS)
5940 2 : return nullptr;
5941 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5942 6 : std::vector<int> dstMapping;
5943 9 : for (int srcAxis : srcMapping)
5944 : {
5945 6 : bool bFound = false;
5946 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
5947 : {
5948 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
5949 : {
5950 6 : dstMapping.push_back(static_cast<int>(i) + 1);
5951 6 : bFound = true;
5952 6 : break;
5953 : }
5954 : }
5955 6 : if (!bFound)
5956 : {
5957 0 : dstMapping.push_back(0);
5958 : }
5959 : }
5960 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5961 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5962 3 : return poClone;
5963 : }
5964 :
5965 4 : const void *GetRawNoDataValue() const override
5966 : {
5967 4 : return m_poParent->GetRawNoDataValue();
5968 : }
5969 :
5970 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5971 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5972 :
5973 4 : double GetOffset(bool *pbHasOffset,
5974 : GDALDataType *peStorageType) const override
5975 : {
5976 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5977 : }
5978 :
5979 4 : double GetScale(bool *pbHasScale,
5980 : GDALDataType *peStorageType) const override
5981 : {
5982 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
5983 : }
5984 :
5985 : // bool SetOffset(double dfOffset) override { return
5986 : // m_poParent->SetOffset(dfOffset); }
5987 :
5988 : // bool SetScale(double dfScale) override { return
5989 : // m_poParent->SetScale(dfScale); }
5990 :
5991 3 : std::vector<GUInt64> GetBlockSize() const override
5992 : {
5993 3 : std::vector<GUInt64> ret(GetDimensionCount());
5994 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
5995 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
5996 : {
5997 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
5998 8 : if (iOldAxis >= 0)
5999 : {
6000 7 : ret[i] = parentBlockSize[iOldAxis];
6001 : }
6002 : }
6003 6 : return ret;
6004 : }
6005 :
6006 : std::shared_ptr<GDALAttribute>
6007 1 : GetAttribute(const std::string &osName) const override
6008 : {
6009 1 : return m_poParent->GetAttribute(osName);
6010 : }
6011 :
6012 : std::vector<std::shared_ptr<GDALAttribute>>
6013 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6014 : {
6015 6 : return m_poParent->GetAttributes(papszOptions);
6016 : }
6017 : };
6018 :
6019 : /************************************************************************/
6020 : /* PrepareParentArrays() */
6021 : /************************************************************************/
6022 :
6023 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6024 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6025 : const GPtrDiff_t *bufferStride) const
6026 : {
6027 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6028 : {
6029 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6030 129 : if (iOldAxis >= 0)
6031 : {
6032 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6033 128 : m_parentCount[iOldAxis] = count[i];
6034 128 : if (arrayStep) // only null when called from IAdviseRead()
6035 : {
6036 126 : m_parentStep[iOldAxis] = arrayStep[i];
6037 : }
6038 128 : if (bufferStride) // only null when called from IAdviseRead()
6039 : {
6040 126 : m_parentStride[iOldAxis] = bufferStride[i];
6041 : }
6042 : }
6043 : }
6044 47 : }
6045 :
6046 : /************************************************************************/
6047 : /* IRead() */
6048 : /************************************************************************/
6049 :
6050 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6051 : const size_t *count, const GInt64 *arrayStep,
6052 : const GPtrDiff_t *bufferStride,
6053 : const GDALExtendedDataType &bufferDataType,
6054 : void *pDstBuffer) const
6055 : {
6056 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6057 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6058 44 : m_parentStep.data(), m_parentStride.data(),
6059 44 : bufferDataType, pDstBuffer);
6060 : }
6061 :
6062 : /************************************************************************/
6063 : /* IWrite() */
6064 : /************************************************************************/
6065 :
6066 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6067 : const size_t *count, const GInt64 *arrayStep,
6068 : const GPtrDiff_t *bufferStride,
6069 : const GDALExtendedDataType &bufferDataType,
6070 : const void *pSrcBuffer)
6071 : {
6072 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6073 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6074 2 : m_parentStep.data(), m_parentStride.data(),
6075 2 : bufferDataType, pSrcBuffer);
6076 : }
6077 :
6078 : /************************************************************************/
6079 : /* IAdviseRead() */
6080 : /************************************************************************/
6081 :
6082 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6083 : const size_t *count,
6084 : CSLConstList papszOptions) const
6085 : {
6086 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6087 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6088 1 : papszOptions);
6089 : }
6090 :
6091 : /************************************************************************/
6092 : /* Transpose() */
6093 : /************************************************************************/
6094 :
6095 : /** Return a view of the array whose axis have been reordered.
6096 : *
6097 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6098 : * and GetDimensionCount() - 1, and each only once.
6099 : * -1 can be used as a special index value to ask for the insertion of a new
6100 : * axis of size 1.
6101 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6102 : * index of one of its dimension, it corresponds to the axis of index
6103 : * anMapNewAxisToOldAxis[i] from the current array.
6104 : *
6105 : * This is similar to the numpy.transpose() method
6106 : *
6107 : * The returned array holds a reference to the original one, and thus is
6108 : * a view of it (not a copy). If the content of the original array changes,
6109 : * the content of the view array too. The view can be written if the underlying
6110 : * array is writable.
6111 : *
6112 : * Note that I/O performance in such a transposed view might be poor.
6113 : *
6114 : * This is the same as the C function GDALMDArrayTranspose().
6115 : *
6116 : * @return a new array, that holds a reference to the original one, and thus is
6117 : * a view of it (not a copy), or nullptr in case of error.
6118 : */
6119 : std::shared_ptr<GDALMDArray>
6120 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6121 : {
6122 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6123 50 : if (!self)
6124 : {
6125 0 : CPLError(CE_Failure, CPLE_AppDefined,
6126 : "Driver implementation issue: m_pSelf not set !");
6127 0 : return nullptr;
6128 : }
6129 50 : const int nDims = static_cast<int>(GetDimensionCount());
6130 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6131 50 : int nCountOldAxis = 0;
6132 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6133 : {
6134 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6135 : {
6136 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6137 4 : return nullptr;
6138 : }
6139 130 : if (iOldAxis >= 0)
6140 : {
6141 128 : if (alreadyUsedOldAxis[iOldAxis])
6142 : {
6143 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6144 : iOldAxis);
6145 1 : return nullptr;
6146 : }
6147 127 : alreadyUsedOldAxis[iOldAxis] = true;
6148 127 : nCountOldAxis++;
6149 : }
6150 : }
6151 46 : if (nCountOldAxis != nDims)
6152 : {
6153 4 : CPLError(CE_Failure, CPLE_AppDefined,
6154 : "One or several original axis missing");
6155 4 : return nullptr;
6156 : }
6157 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6158 : }
6159 :
6160 : /************************************************************************/
6161 : /* IRead() */
6162 : /************************************************************************/
6163 :
6164 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6165 : const size_t *count, const GInt64 *arrayStep,
6166 : const GPtrDiff_t *bufferStride,
6167 : const GDALExtendedDataType &bufferDataType,
6168 : void *pDstBuffer) const
6169 : {
6170 16 : const double dfScale = m_dfScale;
6171 16 : const double dfOffset = m_dfOffset;
6172 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6173 : const auto dtDouble =
6174 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6175 16 : const size_t nDTSize = dtDouble.GetSize();
6176 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6177 :
6178 16 : double adfSrcNoData[2] = {0, 0};
6179 16 : if (m_bHasNoData)
6180 : {
6181 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6182 9 : m_poParent->GetDataType(),
6183 : &adfSrcNoData[0], dtDouble);
6184 : }
6185 :
6186 16 : const auto nDims = GetDimensions().size();
6187 16 : if (nDims == 0)
6188 : {
6189 : double adfVal[2];
6190 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6191 : dtDouble, &adfVal[0]))
6192 : {
6193 0 : return false;
6194 : }
6195 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6196 : {
6197 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6198 6 : if (bDTIsComplex)
6199 : {
6200 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6201 : }
6202 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6203 : bufferDataType);
6204 : }
6205 : else
6206 : {
6207 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6208 : pDstBuffer, bufferDataType);
6209 : }
6210 9 : return true;
6211 : }
6212 :
6213 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6214 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6215 7 : void *pTempBuffer = pDstBuffer;
6216 7 : if (bTempBufferNeeded)
6217 : {
6218 2 : size_t nElts = 1;
6219 2 : actualBufferStrideVector.resize(nDims);
6220 7 : for (size_t i = 0; i < nDims; i++)
6221 5 : nElts *= count[i];
6222 2 : actualBufferStrideVector.back() = 1;
6223 5 : for (size_t i = nDims - 1; i > 0;)
6224 : {
6225 3 : --i;
6226 3 : actualBufferStrideVector[i] =
6227 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6228 : }
6229 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6230 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6231 2 : if (!pTempBuffer)
6232 0 : return false;
6233 : }
6234 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6235 : actualBufferStridePtr, dtDouble, pTempBuffer))
6236 : {
6237 0 : if (bTempBufferNeeded)
6238 0 : VSIFree(pTempBuffer);
6239 0 : return false;
6240 : }
6241 :
6242 : struct Stack
6243 : {
6244 : size_t nIters = 0;
6245 : double *src_ptr = nullptr;
6246 : GByte *dst_ptr = nullptr;
6247 : GPtrDiff_t src_inc_offset = 0;
6248 : GPtrDiff_t dst_inc_offset = 0;
6249 : };
6250 :
6251 7 : std::vector<Stack> stack(nDims);
6252 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6253 23 : for (size_t i = 0; i < nDims; i++)
6254 : {
6255 32 : stack[i].src_inc_offset =
6256 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6257 16 : stack[i].dst_inc_offset =
6258 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6259 : }
6260 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6261 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6262 :
6263 7 : size_t dimIdx = 0;
6264 7 : const size_t nDimsMinus1 = nDims - 1;
6265 : GByte abyDstNoData[16];
6266 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6267 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6268 : bufferDataType);
6269 :
6270 37 : lbl_next_depth:
6271 37 : if (dimIdx == nDimsMinus1)
6272 : {
6273 25 : auto nIters = count[dimIdx];
6274 25 : double *padfVal = stack[dimIdx].src_ptr;
6275 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6276 : while (true)
6277 : {
6278 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6279 : {
6280 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6281 88 : if (bDTIsComplex)
6282 : {
6283 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6284 : }
6285 88 : if (bTempBufferNeeded)
6286 : {
6287 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6288 : dst_ptr, bufferDataType);
6289 : }
6290 : }
6291 : else
6292 : {
6293 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6294 : }
6295 :
6296 92 : if ((--nIters) == 0)
6297 25 : break;
6298 67 : padfVal += stack[dimIdx].src_inc_offset;
6299 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6300 : }
6301 : }
6302 : else
6303 : {
6304 12 : stack[dimIdx].nIters = count[dimIdx];
6305 : while (true)
6306 : {
6307 30 : dimIdx++;
6308 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6309 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6310 30 : goto lbl_next_depth;
6311 30 : lbl_return_to_caller:
6312 30 : dimIdx--;
6313 30 : if ((--stack[dimIdx].nIters) == 0)
6314 12 : break;
6315 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6316 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6317 : }
6318 : }
6319 37 : if (dimIdx > 0)
6320 30 : goto lbl_return_to_caller;
6321 :
6322 7 : if (bTempBufferNeeded)
6323 2 : VSIFree(pTempBuffer);
6324 7 : return true;
6325 : }
6326 :
6327 : /************************************************************************/
6328 : /* IWrite() */
6329 : /************************************************************************/
6330 :
6331 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6332 : const size_t *count, const GInt64 *arrayStep,
6333 : const GPtrDiff_t *bufferStride,
6334 : const GDALExtendedDataType &bufferDataType,
6335 : const void *pSrcBuffer)
6336 : {
6337 16 : const double dfScale = m_dfScale;
6338 16 : const double dfOffset = m_dfOffset;
6339 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6340 : const auto dtDouble =
6341 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6342 16 : const size_t nDTSize = dtDouble.GetSize();
6343 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6344 : const bool bSelfAndParentHaveNoData =
6345 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6346 16 : double dfNoData = 0;
6347 16 : if (m_bHasNoData)
6348 : {
6349 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6350 : &dfNoData, GDT_Float64, 0, 1);
6351 : }
6352 :
6353 16 : double adfSrcNoData[2] = {0, 0};
6354 16 : if (bSelfAndParentHaveNoData)
6355 : {
6356 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6357 7 : m_poParent->GetDataType(),
6358 : &adfSrcNoData[0], dtDouble);
6359 : }
6360 :
6361 16 : const auto nDims = GetDimensions().size();
6362 16 : if (nDims == 0)
6363 : {
6364 : double adfVal[2];
6365 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6366 : dtDouble);
6367 16 : if (bSelfAndParentHaveNoData &&
6368 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6369 : {
6370 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6371 2 : bufferStride, m_poParent->GetDataType(),
6372 4 : m_poParent->GetRawNoDataValue());
6373 : }
6374 : else
6375 : {
6376 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6377 8 : if (bDTIsComplex)
6378 : {
6379 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6380 : }
6381 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6382 8 : bufferStride, dtDouble, &adfVal[0]);
6383 : }
6384 : }
6385 :
6386 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6387 6 : size_t nElts = 1;
6388 6 : tmpBufferStrideVector.resize(nDims);
6389 20 : for (size_t i = 0; i < nDims; i++)
6390 14 : nElts *= count[i];
6391 6 : tmpBufferStrideVector.back() = 1;
6392 14 : for (size_t i = nDims - 1; i > 0;)
6393 : {
6394 8 : --i;
6395 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6396 : }
6397 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6398 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6399 6 : if (!pTempBuffer)
6400 0 : return false;
6401 :
6402 : struct Stack
6403 : {
6404 : size_t nIters = 0;
6405 : double *dst_ptr = nullptr;
6406 : const GByte *src_ptr = nullptr;
6407 : GPtrDiff_t src_inc_offset = 0;
6408 : GPtrDiff_t dst_inc_offset = 0;
6409 : };
6410 :
6411 6 : std::vector<Stack> stack(nDims);
6412 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6413 20 : for (size_t i = 0; i < nDims; i++)
6414 : {
6415 28 : stack[i].dst_inc_offset =
6416 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6417 14 : stack[i].src_inc_offset =
6418 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6419 : }
6420 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6421 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6422 :
6423 6 : size_t dimIdx = 0;
6424 6 : const size_t nDimsMinus1 = nDims - 1;
6425 :
6426 34 : lbl_next_depth:
6427 34 : if (dimIdx == nDimsMinus1)
6428 : {
6429 23 : auto nIters = count[dimIdx];
6430 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6431 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6432 : while (true)
6433 : {
6434 : double adfVal[2];
6435 : const double *padfSrcVal;
6436 86 : if (bIsBufferDataTypeNativeDataType)
6437 : {
6438 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6439 : }
6440 : else
6441 : {
6442 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6443 : &adfVal[0], dtDouble);
6444 36 : padfSrcVal = adfVal;
6445 : }
6446 :
6447 148 : if (bSelfAndParentHaveNoData &&
6448 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6449 : {
6450 3 : dst_ptr[0] = adfSrcNoData[0];
6451 3 : if (bDTIsComplex)
6452 : {
6453 1 : dst_ptr[1] = adfSrcNoData[1];
6454 : }
6455 : }
6456 : else
6457 : {
6458 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6459 83 : if (bDTIsComplex)
6460 : {
6461 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6462 : }
6463 : }
6464 :
6465 86 : if ((--nIters) == 0)
6466 23 : break;
6467 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6468 63 : src_ptr += stack[dimIdx].src_inc_offset;
6469 63 : }
6470 : }
6471 : else
6472 : {
6473 11 : stack[dimIdx].nIters = count[dimIdx];
6474 : while (true)
6475 : {
6476 28 : dimIdx++;
6477 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6478 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6479 28 : goto lbl_next_depth;
6480 28 : lbl_return_to_caller:
6481 28 : dimIdx--;
6482 28 : if ((--stack[dimIdx].nIters) == 0)
6483 11 : break;
6484 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6485 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6486 : }
6487 : }
6488 34 : if (dimIdx > 0)
6489 28 : goto lbl_return_to_caller;
6490 :
6491 : // If the parent array is not double/complex-double, then convert the
6492 : // values to it, before calling Write(), as some implementations can be
6493 : // very slow when doing the type conversion.
6494 6 : const auto &eParentDT = m_poParent->GetDataType();
6495 6 : const size_t nParentDTSize = eParentDT.GetSize();
6496 6 : if (nParentDTSize <= nDTSize / 2)
6497 : {
6498 : // Copy in-place by making sure that source and target do not overlap
6499 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6500 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6501 :
6502 : // Copy first element
6503 : {
6504 6 : std::vector<GByte> abyTemp(nParentDTSize);
6505 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6506 6 : static_cast<int>(nDTSize), &abyTemp[0],
6507 : eParentNumericDT, static_cast<int>(nParentDTSize),
6508 : 1);
6509 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6510 : }
6511 : // Remaining elements
6512 86 : for (size_t i = 1; i < nElts; ++i)
6513 : {
6514 80 : GDALCopyWords64(
6515 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6516 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6517 : eParentNumericDT, 0, 1);
6518 : }
6519 : }
6520 :
6521 : const bool ret =
6522 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6523 : eParentDT, pTempBuffer);
6524 :
6525 6 : VSIFree(pTempBuffer);
6526 6 : return ret;
6527 : }
6528 :
6529 : /************************************************************************/
6530 : /* GetUnscaled() */
6531 : /************************************************************************/
6532 :
6533 : /** Return an array that is the unscaled version of the current one.
6534 : *
6535 : * That is each value of the unscaled array will be
6536 : * unscaled_value = raw_value * GetScale() + GetOffset()
6537 : *
6538 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6539 : * from unscaled values to raw values.
6540 : *
6541 : * This is the same as the C function GDALMDArrayGetUnscaled().
6542 : *
6543 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6544 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6545 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6546 : * @return a new array, that holds a reference to the original one, and thus is
6547 : * a view of it (not a copy), or nullptr in case of error.
6548 : */
6549 : std::shared_ptr<GDALMDArray>
6550 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6551 : double dfOverriddenDstNodata) const
6552 : {
6553 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6554 17 : if (!self)
6555 : {
6556 0 : CPLError(CE_Failure, CPLE_AppDefined,
6557 : "Driver implementation issue: m_pSelf not set !");
6558 0 : return nullptr;
6559 : }
6560 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6561 : {
6562 0 : CPLError(CE_Failure, CPLE_AppDefined,
6563 : "GetUnscaled() only supports numeric data type");
6564 0 : return nullptr;
6565 : }
6566 : const double dfScale =
6567 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6568 : const double dfOffset =
6569 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6570 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6571 4 : return self;
6572 :
6573 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6574 13 : ? GDT_CFloat64
6575 13 : : GDT_Float64;
6576 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6577 : {
6578 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6579 0 : eDT = GDT_Float16;
6580 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6581 1 : eDT = GDT_Float32;
6582 : }
6583 :
6584 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6585 13 : dfOverriddenDstNodata, eDT);
6586 : }
6587 :
6588 : /************************************************************************/
6589 : /* GDALMDArrayMask */
6590 : /************************************************************************/
6591 :
6592 : class GDALMDArrayMask final : public GDALPamMDArray
6593 : {
6594 : private:
6595 : std::shared_ptr<GDALMDArray> m_poParent{};
6596 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6597 : double m_dfMissingValue = 0.0;
6598 : bool m_bHasMissingValue = false;
6599 : double m_dfFillValue = 0.0;
6600 : bool m_bHasFillValue = false;
6601 : double m_dfValidMin = 0.0;
6602 : bool m_bHasValidMin = false;
6603 : double m_dfValidMax = 0.0;
6604 : bool m_bHasValidMax = false;
6605 : std::vector<uint32_t> m_anValidFlagMasks{};
6606 : std::vector<uint32_t> m_anValidFlagValues{};
6607 :
6608 : bool Init(CSLConstList papszOptions);
6609 :
6610 : template <typename Type>
6611 : void
6612 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6613 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6614 : const void *pTempBuffer,
6615 : const GDALExtendedDataType &oTmpBufferDT,
6616 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6617 :
6618 : protected:
6619 45 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6620 90 : : GDALAbstractMDArray(std::string(),
6621 90 : "Mask of " + poParent->GetFullName()),
6622 90 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6623 90 : GDALPamMultiDim::GetPAM(poParent),
6624 : poParent->GetContext()),
6625 225 : m_poParent(std::move(poParent))
6626 : {
6627 45 : }
6628 :
6629 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6630 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6631 : const GDALExtendedDataType &bufferDataType,
6632 : void *pDstBuffer) const override;
6633 :
6634 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6635 : CSLConstList papszOptions) const override
6636 : {
6637 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6638 : }
6639 :
6640 : public:
6641 : static std::shared_ptr<GDALMDArrayMask>
6642 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6643 : CSLConstList papszOptions);
6644 :
6645 1 : bool IsWritable() const override
6646 : {
6647 1 : return false;
6648 : }
6649 :
6650 48 : const std::string &GetFilename() const override
6651 : {
6652 48 : return m_poParent->GetFilename();
6653 : }
6654 :
6655 : const std::vector<std::shared_ptr<GDALDimension>> &
6656 373 : GetDimensions() const override
6657 : {
6658 373 : return m_poParent->GetDimensions();
6659 : }
6660 :
6661 132 : const GDALExtendedDataType &GetDataType() const override
6662 : {
6663 132 : return m_dt;
6664 : }
6665 :
6666 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6667 : {
6668 1 : return m_poParent->GetSpatialRef();
6669 : }
6670 :
6671 2 : std::vector<GUInt64> GetBlockSize() const override
6672 : {
6673 2 : return m_poParent->GetBlockSize();
6674 : }
6675 : };
6676 :
6677 : /************************************************************************/
6678 : /* GDALMDArrayMask::Create() */
6679 : /************************************************************************/
6680 :
6681 : /* static */ std::shared_ptr<GDALMDArrayMask>
6682 45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6683 : CSLConstList papszOptions)
6684 : {
6685 90 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6686 45 : newAr->SetSelf(newAr);
6687 45 : if (!newAr->Init(papszOptions))
6688 6 : return nullptr;
6689 39 : return newAr;
6690 : }
6691 :
6692 : /************************************************************************/
6693 : /* GDALMDArrayMask::Init() */
6694 : /************************************************************************/
6695 :
6696 45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6697 : {
6698 : const auto GetSingleValNumericAttr =
6699 180 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6700 : {
6701 540 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6702 180 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6703 : {
6704 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6705 21 : if (anDimSizes.empty() ||
6706 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6707 : {
6708 11 : bHasVal = true;
6709 11 : dfVal = poAttr->ReadAsDouble();
6710 : }
6711 : }
6712 180 : };
6713 :
6714 45 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6715 45 : m_dfMissingValue);
6716 45 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6717 45 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6718 45 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6719 :
6720 : {
6721 135 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6722 50 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6723 55 : poValidRange->GetDimensionsSize()[0] == 2 &&
6724 5 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6725 : {
6726 5 : m_bHasValidMin = true;
6727 5 : m_bHasValidMax = true;
6728 5 : auto vals = poValidRange->ReadAsDoubleArray();
6729 5 : CPLAssert(vals.size() == 2);
6730 5 : m_dfValidMin = vals[0];
6731 5 : m_dfValidMax = vals[1];
6732 : }
6733 : }
6734 :
6735 : // Take into account
6736 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6737 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6738 : const char *pszUnmaskFlags =
6739 45 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6740 45 : if (pszUnmaskFlags)
6741 : {
6742 : const auto IsScalarStringAttr =
6743 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6744 : {
6745 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6746 26 : (poAttr->GetDimensionsSize().empty() ||
6747 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6748 26 : poAttr->GetDimensionsSize()[0] == 1));
6749 : };
6750 :
6751 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6752 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6753 : {
6754 1 : CPLError(CE_Failure, CPLE_AppDefined,
6755 : "UNMASK_FLAGS option specified but array has no "
6756 : "flag_meanings attribute");
6757 1 : return false;
6758 : }
6759 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6760 13 : if (!pszFlagMeanings)
6761 : {
6762 1 : CPLError(CE_Failure, CPLE_AppDefined,
6763 : "Cannot read flag_meanings attribute");
6764 1 : return false;
6765 : }
6766 :
6767 : const auto IsSingleDimNumericAttr =
6768 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6769 : {
6770 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6771 26 : poAttr->GetDimensionsSize().size() == 1;
6772 : };
6773 :
6774 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6775 : const bool bHasFlagValues =
6776 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6777 :
6778 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6779 : const bool bHasFlagMasks =
6780 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6781 :
6782 12 : if (!bHasFlagValues && !bHasFlagMasks)
6783 : {
6784 1 : CPLError(CE_Failure, CPLE_AppDefined,
6785 : "Cannot find flag_values and/or flag_masks attribute");
6786 1 : return false;
6787 : }
6788 :
6789 : const CPLStringList aosUnmaskFlags(
6790 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6791 : const CPLStringList aosFlagMeanings(
6792 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6793 :
6794 11 : if (bHasFlagValues)
6795 : {
6796 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6797 : // We could support Int64 or UInt64, but more work...
6798 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6799 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6800 : {
6801 0 : CPLError(CE_Failure, CPLE_NotSupported,
6802 : "Unsupported data type for flag_values attribute: %s",
6803 : GDALGetDataTypeName(eType));
6804 0 : return false;
6805 : }
6806 : }
6807 :
6808 11 : if (bHasFlagMasks)
6809 : {
6810 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6811 : // We could support Int64 or UInt64, but more work...
6812 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6813 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6814 : {
6815 0 : CPLError(CE_Failure, CPLE_NotSupported,
6816 : "Unsupported data type for flag_masks attribute: %s",
6817 : GDALGetDataTypeName(eType));
6818 0 : return false;
6819 : }
6820 : }
6821 :
6822 : const std::vector<double> adfValues(
6823 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6824 11 : : std::vector<double>());
6825 : const std::vector<double> adfMasks(
6826 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6827 11 : : std::vector<double>());
6828 :
6829 18 : if (bHasFlagValues &&
6830 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6831 : {
6832 1 : CPLError(CE_Failure, CPLE_AppDefined,
6833 : "Number of values in flag_values attribute is different "
6834 : "from the one in flag_meanings");
6835 1 : return false;
6836 : }
6837 :
6838 16 : if (bHasFlagMasks &&
6839 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6840 : {
6841 1 : CPLError(CE_Failure, CPLE_AppDefined,
6842 : "Number of values in flag_masks attribute is different "
6843 : "from the one in flag_meanings");
6844 1 : return false;
6845 : }
6846 :
6847 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6848 : {
6849 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6850 11 : if (nIdxFlag < 0)
6851 : {
6852 1 : CPLError(
6853 : CE_Failure, CPLE_AppDefined,
6854 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6855 : aosUnmaskFlags[i], pszFlagMeanings);
6856 1 : return false;
6857 : }
6858 :
6859 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6860 : {
6861 0 : CPLError(CE_Failure, CPLE_AppDefined,
6862 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6863 0 : adfValues[nIdxFlag]);
6864 0 : return false;
6865 : }
6866 :
6867 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6868 : {
6869 0 : CPLError(CE_Failure, CPLE_AppDefined,
6870 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6871 0 : adfMasks[nIdxFlag]);
6872 0 : return false;
6873 : }
6874 :
6875 10 : if (bHasFlagValues)
6876 : {
6877 12 : m_anValidFlagValues.push_back(
6878 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6879 : }
6880 :
6881 10 : if (bHasFlagMasks)
6882 : {
6883 12 : m_anValidFlagMasks.push_back(
6884 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6885 : }
6886 : }
6887 : }
6888 :
6889 39 : return true;
6890 : }
6891 :
6892 : /************************************************************************/
6893 : /* IRead() */
6894 : /************************************************************************/
6895 :
6896 48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6897 : const GInt64 *arrayStep,
6898 : const GPtrDiff_t *bufferStride,
6899 : const GDALExtendedDataType &bufferDataType,
6900 : void *pDstBuffer) const
6901 : {
6902 48 : size_t nElts = 1;
6903 48 : const size_t nDims = GetDimensionCount();
6904 96 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6905 132 : for (size_t i = 0; i < nDims; i++)
6906 84 : nElts *= count[i];
6907 48 : if (nDims > 0)
6908 : {
6909 43 : tmpBufferStrideVector.back() = 1;
6910 84 : for (size_t i = nDims - 1; i > 0;)
6911 : {
6912 41 : --i;
6913 41 : tmpBufferStrideVector[i] =
6914 41 : tmpBufferStrideVector[i + 1] * count[i + 1];
6915 : }
6916 : }
6917 :
6918 : /* Optimized case: if we are an integer data type and that there is no */
6919 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
6920 : /* directly */
6921 46 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
6922 70 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
6923 32 : m_anValidFlagMasks.empty() &&
6924 103 : m_poParent->GetRawNoDataValue() == nullptr &&
6925 9 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
6926 : {
6927 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
6928 7 : if (bBufferDataTypeIsByte) // Byte case
6929 : {
6930 4 : bool bContiguous = true;
6931 10 : for (size_t i = 0; i < nDims; i++)
6932 : {
6933 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
6934 : {
6935 1 : bContiguous = false;
6936 1 : break;
6937 : }
6938 : }
6939 4 : if (bContiguous)
6940 : {
6941 : // CPLDebug("GDAL", "GetMask(): contiguous case");
6942 3 : memset(pDstBuffer, 1, nElts);
6943 3 : return true;
6944 : }
6945 : }
6946 :
6947 : struct Stack
6948 : {
6949 : size_t nIters = 0;
6950 : GByte *dst_ptr = nullptr;
6951 : GPtrDiff_t dst_inc_offset = 0;
6952 : };
6953 :
6954 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
6955 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
6956 13 : for (size_t i = 0; i < nDims; i++)
6957 : {
6958 9 : stack[i].dst_inc_offset =
6959 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6960 : }
6961 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6962 :
6963 4 : size_t dimIdx = 0;
6964 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
6965 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
6966 4 : CPLAssert(nBufferDTSize <= 16);
6967 4 : const GByte flag = 1;
6968 : // Coverity misses that m_dt is of type Byte
6969 : // coverity[overrun-buffer-val]
6970 4 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
6971 :
6972 28 : lbl_next_depth:
6973 28 : if (dimIdx == nDimsMinus1)
6974 : {
6975 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
6976 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6977 :
6978 : while (true)
6979 : {
6980 : // cppcheck-suppress knownConditionTrueFalse
6981 73 : if (bBufferDataTypeIsByte)
6982 : {
6983 24 : *dst_ptr = flag;
6984 : }
6985 : else
6986 : {
6987 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
6988 : }
6989 :
6990 73 : if ((--nIters) == 0)
6991 19 : break;
6992 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
6993 : }
6994 : }
6995 : else
6996 : {
6997 9 : stack[dimIdx].nIters = count[dimIdx];
6998 : while (true)
6999 : {
7000 24 : dimIdx++;
7001 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7002 24 : goto lbl_next_depth;
7003 24 : lbl_return_to_caller:
7004 24 : dimIdx--;
7005 24 : if ((--stack[dimIdx].nIters) == 0)
7006 9 : break;
7007 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7008 : }
7009 : }
7010 28 : if (dimIdx > 0)
7011 24 : goto lbl_return_to_caller;
7012 :
7013 4 : return true;
7014 : }
7015 :
7016 : const auto oTmpBufferDT =
7017 41 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7018 : ? GDALExtendedDataType::Create(GDT_Float64)
7019 82 : : m_poParent->GetDataType();
7020 41 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7021 41 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7022 41 : if (!pTempBuffer)
7023 0 : return false;
7024 82 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7025 41 : tmpBufferStrideVector.data(), oTmpBufferDT,
7026 : pTempBuffer))
7027 : {
7028 0 : VSIFree(pTempBuffer);
7029 0 : return false;
7030 : }
7031 :
7032 41 : switch (oTmpBufferDT.GetNumericDataType())
7033 : {
7034 6 : case GDT_Byte:
7035 6 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7036 : pTempBuffer, oTmpBufferDT,
7037 : tmpBufferStrideVector);
7038 6 : break;
7039 :
7040 0 : case GDT_Int8:
7041 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7042 : pTempBuffer, oTmpBufferDT,
7043 : tmpBufferStrideVector);
7044 0 : break;
7045 :
7046 1 : case GDT_UInt16:
7047 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7048 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7049 : tmpBufferStrideVector);
7050 1 : break;
7051 :
7052 14 : case GDT_Int16:
7053 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7054 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7055 : tmpBufferStrideVector);
7056 14 : break;
7057 :
7058 1 : case GDT_UInt32:
7059 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7060 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7061 : tmpBufferStrideVector);
7062 1 : break;
7063 :
7064 5 : case GDT_Int32:
7065 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7066 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7067 : tmpBufferStrideVector);
7068 5 : break;
7069 :
7070 0 : case GDT_UInt64:
7071 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7072 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7073 : tmpBufferStrideVector);
7074 0 : break;
7075 :
7076 0 : case GDT_Int64:
7077 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7078 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7079 : tmpBufferStrideVector);
7080 0 : break;
7081 :
7082 0 : case GDT_Float16:
7083 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7084 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7085 : tmpBufferStrideVector);
7086 0 : break;
7087 :
7088 7 : case GDT_Float32:
7089 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7090 : pTempBuffer, oTmpBufferDT,
7091 : tmpBufferStrideVector);
7092 7 : break;
7093 :
7094 7 : case GDT_Float64:
7095 7 : ReadInternal<double>(count, bufferStride, bufferDataType,
7096 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7097 : tmpBufferStrideVector);
7098 7 : break;
7099 0 : case GDT_Unknown:
7100 : case GDT_CInt16:
7101 : case GDT_CInt32:
7102 : case GDT_CFloat16:
7103 : case GDT_CFloat32:
7104 : case GDT_CFloat64:
7105 : case GDT_TypeCount:
7106 0 : CPLAssert(false);
7107 : break;
7108 : }
7109 :
7110 41 : VSIFree(pTempBuffer);
7111 :
7112 41 : return true;
7113 : }
7114 :
7115 : /************************************************************************/
7116 : /* IsValidForDT() */
7117 : /************************************************************************/
7118 :
7119 38 : template <typename Type> static bool IsValidForDT(double dfVal)
7120 : {
7121 38 : if (std::isnan(dfVal))
7122 0 : return false;
7123 38 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7124 0 : return false;
7125 38 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7126 0 : return false;
7127 38 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7128 : }
7129 :
7130 9 : template <> bool IsValidForDT<double>(double)
7131 : {
7132 9 : return true;
7133 : }
7134 :
7135 : /************************************************************************/
7136 : /* IsNan() */
7137 : /************************************************************************/
7138 :
7139 1038 : template <typename Type> inline bool IsNan(Type)
7140 : {
7141 1038 : return false;
7142 : }
7143 :
7144 25 : template <> bool IsNan<double>(double val)
7145 : {
7146 25 : return std::isnan(val);
7147 : }
7148 :
7149 26 : template <> bool IsNan<float>(float val)
7150 : {
7151 26 : return std::isnan(val);
7152 : }
7153 :
7154 : /************************************************************************/
7155 : /* ReadInternal() */
7156 : /************************************************************************/
7157 :
7158 : template <typename Type>
7159 41 : void GDALMDArrayMask::ReadInternal(
7160 : const size_t *count, const GPtrDiff_t *bufferStride,
7161 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7162 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7163 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7164 : {
7165 41 : const size_t nDims = GetDimensionCount();
7166 :
7167 205 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7168 : {
7169 205 : if (bHasVal)
7170 : {
7171 47 : if (IsValidForDT<Type>(dfVal))
7172 : {
7173 47 : return static_cast<Type>(dfVal);
7174 : }
7175 : else
7176 : {
7177 0 : bHasVal = false;
7178 : }
7179 : }
7180 158 : return 0;
7181 : };
7182 :
7183 41 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7184 41 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7185 : const Type nNoDataValue =
7186 41 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7187 41 : bool bHasMissingValue = m_bHasMissingValue;
7188 41 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7189 41 : bool bHasFillValue = m_bHasFillValue;
7190 41 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7191 41 : bool bHasValidMin = m_bHasValidMin;
7192 41 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7193 41 : bool bHasValidMax = m_bHasValidMax;
7194 41 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7195 41 : const bool bHasValidFlags =
7196 41 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7197 :
7198 348 : const auto IsValidFlag = [this](Type v)
7199 : {
7200 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7201 : {
7202 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7203 : {
7204 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7205 : m_anValidFlagValues[i])
7206 : {
7207 4 : return true;
7208 : }
7209 : }
7210 : }
7211 42 : else if (!m_anValidFlagValues.empty())
7212 : {
7213 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7214 : {
7215 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7216 : {
7217 4 : return true;
7218 : }
7219 : }
7220 : }
7221 : else /* if( !m_anValidFlagMasks.empty() ) */
7222 : {
7223 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7224 : {
7225 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7226 : {
7227 9 : return true;
7228 : }
7229 : }
7230 : }
7231 37 : return false;
7232 : };
7233 :
7234 : #define GET_MASK_FOR_SAMPLE(v) \
7235 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7236 : !(bHasMissingValue && v == nMissingValue) && \
7237 : !(bHasFillValue && v == nFillValue) && \
7238 : !(bHasValidMin && v < nValidMin) && \
7239 : !(bHasValidMax && v > nValidMax) && \
7240 : (!bHasValidFlags || IsValidFlag(v)));
7241 :
7242 41 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7243 : /* Optimized case: Byte output and output buffer is contiguous */
7244 41 : if (bBufferDataTypeIsByte)
7245 : {
7246 37 : bool bContiguous = true;
7247 96 : for (size_t i = 0; i < nDims; i++)
7248 : {
7249 60 : if (bufferStride[i] != tmpBufferStrideVector[i])
7250 : {
7251 1 : bContiguous = false;
7252 1 : break;
7253 : }
7254 : }
7255 37 : if (bContiguous)
7256 : {
7257 36 : size_t nElts = 1;
7258 95 : for (size_t i = 0; i < nDims; i++)
7259 59 : nElts *= count[i];
7260 :
7261 670 : for (size_t i = 0; i < nElts; i++)
7262 : {
7263 634 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7264 634 : static_cast<GByte *>(pDstBuffer)[i] =
7265 634 : GET_MASK_FOR_SAMPLE(*pSrc);
7266 : }
7267 36 : return;
7268 : }
7269 : }
7270 :
7271 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7272 :
7273 : struct Stack
7274 : {
7275 : size_t nIters = 0;
7276 : const GByte *src_ptr = nullptr;
7277 : GByte *dst_ptr = nullptr;
7278 : GPtrDiff_t src_inc_offset = 0;
7279 : GPtrDiff_t dst_inc_offset = 0;
7280 : };
7281 :
7282 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7283 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7284 15 : for (size_t i = 0; i < nDims; i++)
7285 : {
7286 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7287 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7288 10 : stack[i].dst_inc_offset =
7289 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7290 : }
7291 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7292 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7293 :
7294 5 : size_t dimIdx = 0;
7295 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7296 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7297 5 : CPLAssert(nBufferDTSize <= 16);
7298 15 : for (GByte flag = 0; flag <= 1; flag++)
7299 : {
7300 : // Coverity misses that m_dt is of type Byte
7301 : // coverity[overrun-buffer-val]
7302 10 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
7303 : bufferDataType);
7304 : }
7305 :
7306 43 : lbl_next_depth:
7307 43 : if (dimIdx == nDimsMinus1)
7308 : {
7309 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7310 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7311 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7312 :
7313 420 : while (true)
7314 : {
7315 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7316 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7317 :
7318 455 : if (bBufferDataTypeIsByte)
7319 : {
7320 24 : *dst_ptr = flag;
7321 : }
7322 : else
7323 : {
7324 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7325 : }
7326 :
7327 455 : if ((--nIters) == 0)
7328 35 : break;
7329 420 : src_ptr += stack[dimIdx].src_inc_offset;
7330 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7331 : }
7332 : }
7333 : else
7334 : {
7335 8 : stack[dimIdx].nIters = count[dimIdx];
7336 : while (true)
7337 : {
7338 38 : dimIdx++;
7339 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7340 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7341 38 : goto lbl_next_depth;
7342 38 : lbl_return_to_caller:
7343 38 : dimIdx--;
7344 38 : if ((--stack[dimIdx].nIters) == 0)
7345 8 : break;
7346 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7347 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7348 : }
7349 : }
7350 43 : if (dimIdx > 0)
7351 38 : goto lbl_return_to_caller;
7352 : }
7353 :
7354 : /************************************************************************/
7355 : /* GetMask() */
7356 : /************************************************************************/
7357 :
7358 : /** Return an array that is a mask for the current array
7359 :
7360 : This array will be of type Byte, with values set to 0 to indicate invalid
7361 : pixels of the current array, and values set to 1 to indicate valid pixels.
7362 :
7363 : The generic implementation honours the NoDataValue, as well as various
7364 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7365 : and valid_range.
7366 :
7367 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7368 : can be used to specify strings of the "flag_meanings" attribute
7369 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7370 : for which pixels matching any of those flags will be set at 1 in the mask array,
7371 : and pixels matching none of those flags will be set at 0.
7372 : For example, let's consider the following netCDF variable defined with:
7373 : \verbatim
7374 : l2p_flags:valid_min = 0s ;
7375 : l2p_flags:valid_max = 256s ;
7376 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7377 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7378 : \endverbatim
7379 :
7380 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7381 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7382 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7383 : will be 1.
7384 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7385 : will be 0.
7386 :
7387 : This is the same as the C function GDALMDArrayGetMask().
7388 :
7389 : @param papszOptions NULL-terminated list of options, or NULL.
7390 :
7391 : @return a new array, that holds a reference to the original one, and thus is
7392 : a view of it (not a copy), or nullptr in case of error.
7393 : */
7394 : std::shared_ptr<GDALMDArray>
7395 46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7396 : {
7397 92 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7398 46 : if (!self)
7399 : {
7400 0 : CPLError(CE_Failure, CPLE_AppDefined,
7401 : "Driver implementation issue: m_pSelf not set !");
7402 0 : return nullptr;
7403 : }
7404 46 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7405 : {
7406 1 : CPLError(CE_Failure, CPLE_AppDefined,
7407 : "GetMask() only supports numeric data type");
7408 1 : return nullptr;
7409 : }
7410 45 : return GDALMDArrayMask::Create(self, papszOptions);
7411 : }
7412 :
7413 : /************************************************************************/
7414 : /* IsRegularlySpaced() */
7415 : /************************************************************************/
7416 :
7417 : /** Returns whether an array is a 1D regularly spaced array.
7418 : *
7419 : * @param[out] dfStart First value in the array
7420 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7421 : * @return true if the array is regularly spaced.
7422 : */
7423 183 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7424 : {
7425 183 : dfStart = 0;
7426 183 : dfIncrement = 0;
7427 183 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7428 0 : return false;
7429 183 : const auto nSize = GetDimensions()[0]->GetSize();
7430 183 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7431 2 : return false;
7432 :
7433 181 : size_t nCount = static_cast<size_t>(nSize);
7434 362 : std::vector<double> adfTmp;
7435 : try
7436 : {
7437 181 : adfTmp.resize(nCount);
7438 : }
7439 0 : catch (const std::exception &)
7440 : {
7441 0 : return false;
7442 : }
7443 :
7444 181 : GUInt64 anStart[1] = {0};
7445 181 : size_t anCount[1] = {nCount};
7446 :
7447 : const auto IsRegularlySpacedInternal =
7448 83542 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7449 : {
7450 253 : dfStart = adfTmp[0];
7451 253 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7452 253 : if (dfIncrement == 0)
7453 : {
7454 3 : return false;
7455 : }
7456 20820 : for (size_t i = 1; i < anCount[0]; i++)
7457 : {
7458 20570 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7459 20570 : 1e-3 * fabs(dfIncrement))
7460 : {
7461 0 : return false;
7462 : }
7463 : }
7464 250 : return true;
7465 181 : };
7466 :
7467 : // First try with the first block(s). This can avoid excessive processing
7468 : // time, for example with Zarr datasets.
7469 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7470 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7471 181 : const auto nBlockSize = GetBlockSize()[0];
7472 181 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7473 : {
7474 : size_t nReducedCount =
7475 75 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7476 436 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7477 361 : nReducedCount *= 2;
7478 75 : anCount[0] = nReducedCount;
7479 75 : if (!Read(anStart, anCount, nullptr, nullptr,
7480 150 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7481 : {
7482 0 : return false;
7483 : }
7484 75 : if (!IsRegularlySpacedInternal())
7485 : {
7486 3 : return false;
7487 : }
7488 :
7489 : // Get next values
7490 72 : anStart[0] = nReducedCount;
7491 72 : anCount[0] = nCount - nReducedCount;
7492 : }
7493 :
7494 178 : if (!Read(anStart, anCount, nullptr, nullptr,
7495 356 : GDALExtendedDataType::Create(GDT_Float64),
7496 178 : &adfTmp[static_cast<size_t>(anStart[0])]))
7497 : {
7498 0 : return false;
7499 : }
7500 :
7501 178 : return IsRegularlySpacedInternal();
7502 : }
7503 :
7504 : /************************************************************************/
7505 : /* GuessGeoTransform() */
7506 : /************************************************************************/
7507 :
7508 : /** Returns whether 2 specified dimensions form a geotransform
7509 : *
7510 : * @param nDimX Index of the X axis.
7511 : * @param nDimY Index of the Y axis.
7512 : * @param bPixelIsPoint Whether the geotransform should be returned
7513 : * with the pixel-is-point (pixel-center) convention
7514 : * (bPixelIsPoint = true), or with the pixel-is-area
7515 : * (top left corner convention)
7516 : * (bPixelIsPoint = false)
7517 : * @param[out] adfGeoTransform Computed geotransform
7518 : * @return true if a geotransform could be computed.
7519 : */
7520 215 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7521 : bool bPixelIsPoint,
7522 : double adfGeoTransform[6]) const
7523 : {
7524 215 : const auto &dims(GetDimensions());
7525 430 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7526 430 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7527 215 : double dfXStart = 0.0;
7528 215 : double dfXSpacing = 0.0;
7529 215 : double dfYStart = 0.0;
7530 215 : double dfYSpacing = 0.0;
7531 491 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7532 276 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7533 322 : poVarY && poVarY->GetDimensionCount() == 1 &&
7534 92 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7535 440 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7536 87 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7537 : {
7538 87 : adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7539 87 : adfGeoTransform[1] = dfXSpacing;
7540 87 : adfGeoTransform[2] = 0;
7541 87 : adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7542 87 : adfGeoTransform[4] = 0;
7543 87 : adfGeoTransform[5] = dfYSpacing;
7544 87 : return true;
7545 : }
7546 128 : return false;
7547 : }
7548 :
7549 : /************************************************************************/
7550 : /* GDALMDArrayResampled */
7551 : /************************************************************************/
7552 :
7553 : class GDALMDArrayResampledDataset;
7554 :
7555 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7556 : {
7557 : protected:
7558 : CPLErr IReadBlock(int, int, void *) override;
7559 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7560 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7561 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7562 : GSpacing nLineSpaceBuf,
7563 : GDALRasterIOExtraArg *psExtraArg) override;
7564 :
7565 : public:
7566 : explicit GDALMDArrayResampledDatasetRasterBand(
7567 : GDALMDArrayResampledDataset *poDSIn);
7568 :
7569 : double GetNoDataValue(int *pbHasNoData) override;
7570 : };
7571 :
7572 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7573 : {
7574 : friend class GDALMDArrayResampled;
7575 : friend class GDALMDArrayResampledDatasetRasterBand;
7576 :
7577 : std::shared_ptr<GDALMDArray> m_poArray;
7578 : const size_t m_iXDim;
7579 : const size_t m_iYDim;
7580 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7581 : bool m_bHasGT = false;
7582 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7583 :
7584 : std::vector<GUInt64> m_anOffset{};
7585 : std::vector<size_t> m_anCount{};
7586 : std::vector<GPtrDiff_t> m_anStride{};
7587 :
7588 : std::string m_osFilenameLong{};
7589 : std::string m_osFilenameLat{};
7590 :
7591 : public:
7592 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7593 : size_t iXDim, size_t iYDim)
7594 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7595 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7596 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7597 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7598 : {
7599 24 : const auto &dims(m_poArray->GetDimensions());
7600 :
7601 24 : nRasterYSize = static_cast<int>(
7602 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7603 24 : nRasterXSize = static_cast<int>(
7604 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7605 :
7606 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7607 24 : m_adfGeoTransform);
7608 :
7609 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7610 24 : }
7611 :
7612 48 : ~GDALMDArrayResampledDataset()
7613 24 : {
7614 24 : if (!m_osFilenameLong.empty())
7615 5 : VSIUnlink(m_osFilenameLong.c_str());
7616 24 : if (!m_osFilenameLat.empty())
7617 5 : VSIUnlink(m_osFilenameLat.c_str());
7618 48 : }
7619 :
7620 43 : CPLErr GetGeoTransform(double *padfGeoTransform) override
7621 : {
7622 43 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7623 43 : return m_bHasGT ? CE_None : CE_Failure;
7624 : }
7625 :
7626 105 : const OGRSpatialReference *GetSpatialRef() const override
7627 : {
7628 105 : m_poSRS = m_poArray->GetSpatialRef();
7629 105 : if (m_poSRS)
7630 : {
7631 79 : m_poSRS.reset(m_poSRS->Clone());
7632 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7633 237 : for (auto &m : axisMapping)
7634 : {
7635 158 : if (m == static_cast<int>(m_iXDim) + 1)
7636 79 : m = 1;
7637 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7638 79 : m = 2;
7639 : }
7640 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7641 : }
7642 105 : return m_poSRS.get();
7643 : }
7644 :
7645 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7646 : const std::string &osFilenameLat)
7647 : {
7648 5 : m_osFilenameLong = osFilenameLong;
7649 5 : m_osFilenameLat = osFilenameLat;
7650 10 : CPLStringList aosGeoLoc;
7651 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7652 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7653 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7654 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7655 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7656 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7657 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7658 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7659 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7660 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7661 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7662 5 : }
7663 : };
7664 :
7665 : /************************************************************************/
7666 : /* GDALMDArrayResampledDatasetRasterBand() */
7667 : /************************************************************************/
7668 :
7669 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7670 24 : GDALMDArrayResampledDataset *poDSIn)
7671 : {
7672 24 : const auto &poArray(poDSIn->m_poArray);
7673 24 : const auto blockSize(poArray->GetBlockSize());
7674 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7675 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7676 13 : blockSize[poDSIn->m_iYDim]))
7677 24 : : 1;
7678 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7679 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7680 13 : blockSize[poDSIn->m_iXDim]))
7681 24 : : poDSIn->GetRasterXSize();
7682 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7683 24 : eAccess = poDSIn->eAccess;
7684 24 : }
7685 :
7686 : /************************************************************************/
7687 : /* GetNoDataValue() */
7688 : /************************************************************************/
7689 :
7690 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7691 : {
7692 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7693 50 : const auto &poArray(l_poDS->m_poArray);
7694 50 : bool bHasNodata = false;
7695 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7696 50 : if (pbHasNoData)
7697 46 : *pbHasNoData = bHasNodata;
7698 50 : return dfRes;
7699 : }
7700 :
7701 : /************************************************************************/
7702 : /* IReadBlock() */
7703 : /************************************************************************/
7704 :
7705 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7706 : int nBlockYOff,
7707 : void *pImage)
7708 : {
7709 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7710 0 : const int nXOff = nBlockXOff * nBlockXSize;
7711 0 : const int nYOff = nBlockYOff * nBlockYSize;
7712 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7713 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7714 : GDALRasterIOExtraArg sExtraArg;
7715 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7716 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7717 : nReqXSize, nReqYSize, eDataType, nDTSize,
7718 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7719 : }
7720 :
7721 : /************************************************************************/
7722 : /* IRasterIO() */
7723 : /************************************************************************/
7724 :
7725 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7726 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7727 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7728 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7729 : GDALRasterIOExtraArg *psExtraArg)
7730 : {
7731 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7732 32 : const auto &poArray(l_poDS->m_poArray);
7733 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7734 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7735 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7736 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7737 : {
7738 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7739 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7740 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7741 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7742 :
7743 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7744 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7745 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7746 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7747 :
7748 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7749 32 : l_poDS->m_anCount.data(), nullptr,
7750 32 : l_poDS->m_anStride.data(),
7751 64 : GDALExtendedDataType::Create(eBufType), pData)
7752 32 : ? CE_None
7753 32 : : CE_Failure;
7754 : }
7755 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7756 : pData, nBufXSize, nBufYSize, eBufType,
7757 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7758 : }
7759 :
7760 : class GDALMDArrayResampled final : public GDALPamMDArray
7761 : {
7762 : private:
7763 : std::shared_ptr<GDALMDArray> m_poParent{};
7764 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7765 : std::vector<GUInt64> m_anBlockSize;
7766 : GDALExtendedDataType m_dt;
7767 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7768 : std::shared_ptr<GDALMDArray> m_poVarX{};
7769 : std::shared_ptr<GDALMDArray> m_poVarY{};
7770 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7771 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7772 :
7773 : protected:
7774 21 : GDALMDArrayResampled(
7775 : const std::shared_ptr<GDALMDArray> &poParent,
7776 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7777 : const std::vector<GUInt64> &anBlockSize)
7778 42 : : GDALAbstractMDArray(std::string(),
7779 42 : "Resampled view of " + poParent->GetFullName()),
7780 : GDALPamMDArray(
7781 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7782 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7783 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7784 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7785 : {
7786 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7787 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7788 21 : }
7789 :
7790 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7791 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7792 : const GDALExtendedDataType &bufferDataType,
7793 : void *pDstBuffer) const override;
7794 :
7795 : public:
7796 : static std::shared_ptr<GDALMDArray>
7797 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7798 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7799 : GDALRIOResampleAlg resampleAlg,
7800 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7801 :
7802 42 : ~GDALMDArrayResampled()
7803 21 : {
7804 : // First close the warped VRT
7805 21 : m_poReprojectedDS.reset();
7806 21 : m_poParentDS.reset();
7807 42 : }
7808 :
7809 11 : bool IsWritable() const override
7810 : {
7811 11 : return false;
7812 : }
7813 :
7814 74 : const std::string &GetFilename() const override
7815 : {
7816 74 : return m_poParent->GetFilename();
7817 : }
7818 :
7819 : const std::vector<std::shared_ptr<GDALDimension>> &
7820 257 : GetDimensions() const override
7821 : {
7822 257 : return m_apoDims;
7823 : }
7824 :
7825 109 : const GDALExtendedDataType &GetDataType() const override
7826 : {
7827 109 : return m_dt;
7828 : }
7829 :
7830 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7831 : {
7832 21 : return m_poSRS;
7833 : }
7834 :
7835 12 : std::vector<GUInt64> GetBlockSize() const override
7836 : {
7837 12 : return m_anBlockSize;
7838 : }
7839 :
7840 : std::shared_ptr<GDALAttribute>
7841 1 : GetAttribute(const std::string &osName) const override
7842 : {
7843 1 : return m_poParent->GetAttribute(osName);
7844 : }
7845 :
7846 : std::vector<std::shared_ptr<GDALAttribute>>
7847 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7848 : {
7849 12 : return m_poParent->GetAttributes(papszOptions);
7850 : }
7851 :
7852 1 : const std::string &GetUnit() const override
7853 : {
7854 1 : return m_poParent->GetUnit();
7855 : }
7856 :
7857 1 : const void *GetRawNoDataValue() const override
7858 : {
7859 1 : return m_poParent->GetRawNoDataValue();
7860 : }
7861 :
7862 1 : double GetOffset(bool *pbHasOffset,
7863 : GDALDataType *peStorageType) const override
7864 : {
7865 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7866 : }
7867 :
7868 1 : double GetScale(bool *pbHasScale,
7869 : GDALDataType *peStorageType) const override
7870 : {
7871 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7872 : }
7873 : };
7874 :
7875 : /************************************************************************/
7876 : /* GDALMDArrayResampled::Create() */
7877 : /************************************************************************/
7878 :
7879 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7880 : const std::shared_ptr<GDALMDArray> &poParent,
7881 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7882 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7883 : CSLConstList /* papszOptions */)
7884 : {
7885 29 : const char *pszResampleAlg = "nearest";
7886 29 : bool unsupported = false;
7887 29 : switch (resampleAlg)
7888 : {
7889 16 : case GRIORA_NearestNeighbour:
7890 16 : pszResampleAlg = "nearest";
7891 16 : break;
7892 2 : case GRIORA_Bilinear:
7893 2 : pszResampleAlg = "bilinear";
7894 2 : break;
7895 5 : case GRIORA_Cubic:
7896 5 : pszResampleAlg = "cubic";
7897 5 : break;
7898 1 : case GRIORA_CubicSpline:
7899 1 : pszResampleAlg = "cubicspline";
7900 1 : break;
7901 1 : case GRIORA_Lanczos:
7902 1 : pszResampleAlg = "lanczos";
7903 1 : break;
7904 1 : case GRIORA_Average:
7905 1 : pszResampleAlg = "average";
7906 1 : break;
7907 1 : case GRIORA_Mode:
7908 1 : pszResampleAlg = "mode";
7909 1 : break;
7910 1 : case GRIORA_Gauss:
7911 1 : unsupported = true;
7912 1 : break;
7913 0 : case GRIORA_RESERVED_START:
7914 0 : unsupported = true;
7915 0 : break;
7916 0 : case GRIORA_RESERVED_END:
7917 0 : unsupported = true;
7918 0 : break;
7919 1 : case GRIORA_RMS:
7920 1 : pszResampleAlg = "rms";
7921 1 : break;
7922 : }
7923 29 : if (unsupported)
7924 : {
7925 1 : CPLError(CE_Failure, CPLE_NotSupported,
7926 : "Unsupported resample method for GetResampled()");
7927 1 : return nullptr;
7928 : }
7929 :
7930 28 : if (poParent->GetDimensionCount() < 2)
7931 : {
7932 1 : CPLError(CE_Failure, CPLE_NotSupported,
7933 : "GetResampled() only supports 2 dimensions or more");
7934 1 : return nullptr;
7935 : }
7936 :
7937 27 : const auto &aoParentDims = poParent->GetDimensions();
7938 27 : if (apoNewDimsIn.size() != aoParentDims.size())
7939 : {
7940 2 : CPLError(CE_Failure, CPLE_AppDefined,
7941 : "GetResampled(): apoNewDims size should be the same as "
7942 : "GetDimensionCount()");
7943 2 : return nullptr;
7944 : }
7945 :
7946 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
7947 25 : apoNewDims.reserve(apoNewDimsIn.size());
7948 :
7949 50 : std::vector<GUInt64> anBlockSize;
7950 25 : anBlockSize.reserve(apoNewDimsIn.size());
7951 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
7952 :
7953 50 : auto apoParentDims = poParent->GetDimensions();
7954 : // Special case for NASA EMIT datasets
7955 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
7956 7 : apoParentDims[0]->GetName() == "downtrack" &&
7957 32 : apoParentDims[1]->GetName() == "crosstrack" &&
7958 2 : apoParentDims[2]->GetName() == "bands");
7959 :
7960 : const size_t iYDimParent =
7961 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
7962 : const size_t iXDimParent =
7963 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
7964 :
7965 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
7966 : {
7967 53 : if (i == iYDimParent || i == iXDimParent)
7968 48 : continue;
7969 5 : if (apoNewDimsIn[i] == nullptr)
7970 : {
7971 3 : apoNewDims.emplace_back(aoParentDims[i]);
7972 : }
7973 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
7974 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
7975 : {
7976 1 : CPLError(CE_Failure, CPLE_AppDefined,
7977 : "GetResampled(): apoNewDims[%u] should be the same "
7978 : "as its parent",
7979 : i);
7980 1 : return nullptr;
7981 : }
7982 : else
7983 : {
7984 1 : apoNewDims.emplace_back(aoParentDims[i]);
7985 : }
7986 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
7987 : }
7988 :
7989 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
7990 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
7991 :
7992 24 : double dfXStart = 0.0;
7993 24 : double dfXSpacing = 0.0;
7994 24 : bool gotXSpacing = false;
7995 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
7996 24 : if (poNewDimX)
7997 : {
7998 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
7999 : {
8000 0 : CPLError(CE_Failure, CPLE_NotSupported,
8001 : "Too big size for X dimension");
8002 0 : return nullptr;
8003 : }
8004 4 : auto var = poNewDimX->GetIndexingVariable();
8005 4 : if (var)
8006 : {
8007 2 : if (var->GetDimensionCount() != 1 ||
8008 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8009 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8010 : {
8011 0 : CPLError(CE_Failure, CPLE_NotSupported,
8012 : "New X dimension should be indexed by a regularly "
8013 : "spaced variable");
8014 0 : return nullptr;
8015 : }
8016 1 : gotXSpacing = true;
8017 : }
8018 : }
8019 :
8020 24 : double dfYStart = 0.0;
8021 24 : double dfYSpacing = 0.0;
8022 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8023 24 : bool gotYSpacing = false;
8024 24 : if (poNewDimY)
8025 : {
8026 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8027 : {
8028 0 : CPLError(CE_Failure, CPLE_NotSupported,
8029 : "Too big size for Y dimension");
8030 0 : return nullptr;
8031 : }
8032 4 : auto var = poNewDimY->GetIndexingVariable();
8033 4 : if (var)
8034 : {
8035 2 : if (var->GetDimensionCount() != 1 ||
8036 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8037 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8038 : {
8039 0 : CPLError(CE_Failure, CPLE_NotSupported,
8040 : "New Y dimension should be indexed by a regularly "
8041 : "spaced variable");
8042 0 : return nullptr;
8043 : }
8044 1 : gotYSpacing = true;
8045 : }
8046 : }
8047 :
8048 : // This limitation could probably be removed
8049 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8050 : {
8051 0 : CPLError(CE_Failure, CPLE_NotSupported,
8052 : "Either none of new X or Y dimension should have an indexing "
8053 : "variable, or both should both should have one.");
8054 0 : return nullptr;
8055 : }
8056 :
8057 48 : std::string osDstWKT;
8058 24 : if (poTargetSRS)
8059 : {
8060 2 : char *pszDstWKT = nullptr;
8061 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8062 : {
8063 0 : CPLFree(pszDstWKT);
8064 0 : return nullptr;
8065 : }
8066 2 : osDstWKT = pszDstWKT;
8067 2 : CPLFree(pszDstWKT);
8068 : }
8069 :
8070 : // Use coordinate variables for geolocation array
8071 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8072 24 : bool useGeolocationArray = false;
8073 24 : if (apoCoordinateVars.size() >= 2)
8074 : {
8075 0 : std::shared_ptr<GDALMDArray> poLongVar;
8076 0 : std::shared_ptr<GDALMDArray> poLatVar;
8077 15 : for (const auto &poCoordVar : apoCoordinateVars)
8078 : {
8079 10 : const auto &osName = poCoordVar->GetName();
8080 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8081 20 : std::string osStandardName;
8082 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8083 2 : poAttr->GetDimensionCount() == 0)
8084 : {
8085 2 : const char *pszStandardName = poAttr->ReadAsString();
8086 2 : if (pszStandardName)
8087 2 : osStandardName = pszStandardName;
8088 : }
8089 21 : if (osName == "lon" || osName == "longitude" ||
8090 21 : osName == "Longitude" || osStandardName == "longitude")
8091 : {
8092 5 : poLongVar = poCoordVar;
8093 : }
8094 6 : else if (osName == "lat" || osName == "latitude" ||
8095 6 : osName == "Latitude" || osStandardName == "latitude")
8096 : {
8097 5 : poLatVar = poCoordVar;
8098 : }
8099 : }
8100 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8101 : {
8102 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8103 5 : const auto &longDims = poLongVar->GetDimensions();
8104 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8105 5 : const auto &latDims = poLatVar->GetDimensions();
8106 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8107 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8108 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8109 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8110 : {
8111 : // Geolocation arrays are 1D, and of consistent size with
8112 : // the variable
8113 0 : useGeolocationArray = true;
8114 : }
8115 1 : else if ((longDimCount == 2 ||
8116 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8117 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8118 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8119 1 : (latDimCount == 2 ||
8120 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8121 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8122 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8123 :
8124 : {
8125 : // Geolocation arrays are 2D (or 3D with first dimension of
8126 : // size 1, as found in Sentinel 5P products), and of consistent
8127 : // size with the variable
8128 5 : useGeolocationArray = true;
8129 : }
8130 : else
8131 : {
8132 0 : CPLDebug(
8133 : "GDAL",
8134 : "Longitude and latitude coordinate variables found, "
8135 : "but their characteristics are not compatible of using "
8136 : "them as geolocation arrays");
8137 : }
8138 5 : if (useGeolocationArray)
8139 : {
8140 10 : CPLDebug("GDAL",
8141 : "Setting geolocation array from variables %s and %s",
8142 5 : poLongVar->GetName().c_str(),
8143 5 : poLatVar->GetName().c_str());
8144 : const std::string osFilenameLong =
8145 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8146 : const std::string osFilenameLat =
8147 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8148 : std::unique_ptr<GDALDataset> poTmpLongDS(
8149 : longDimCount == 1
8150 0 : ? poLongVar->AsClassicDataset(0, 0)
8151 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8152 15 : longDimCount - 2));
8153 5 : auto hTIFFLongDS = GDALTranslate(
8154 : osFilenameLong.c_str(),
8155 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8156 : std::unique_ptr<GDALDataset> poTmpLatDS(
8157 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8158 20 : : poLatVar->AsClassicDataset(
8159 15 : latDimCount - 1, latDimCount - 2));
8160 5 : auto hTIFFLatDS = GDALTranslate(
8161 : osFilenameLat.c_str(),
8162 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8163 5 : const bool bError =
8164 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8165 5 : GDALClose(hTIFFLongDS);
8166 5 : GDALClose(hTIFFLatDS);
8167 5 : if (bError)
8168 : {
8169 0 : VSIUnlink(osFilenameLong.c_str());
8170 0 : VSIUnlink(osFilenameLat.c_str());
8171 0 : return nullptr;
8172 : }
8173 :
8174 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8175 : }
8176 : }
8177 : else
8178 : {
8179 0 : CPLDebug("GDAL",
8180 : "Coordinate variables available for %s, but "
8181 : "longitude and/or latitude variables were not identified",
8182 0 : poParent->GetName().c_str());
8183 : }
8184 : }
8185 :
8186 : // Build gdalwarp arguments
8187 48 : CPLStringList aosArgv;
8188 :
8189 24 : aosArgv.AddString("-of");
8190 24 : aosArgv.AddString("VRT");
8191 :
8192 24 : aosArgv.AddString("-r");
8193 24 : aosArgv.AddString(pszResampleAlg);
8194 :
8195 24 : if (!osDstWKT.empty())
8196 : {
8197 2 : aosArgv.AddString("-t_srs");
8198 2 : aosArgv.AddString(osDstWKT.c_str());
8199 : }
8200 :
8201 24 : if (useGeolocationArray)
8202 5 : aosArgv.AddString("-geoloc");
8203 :
8204 24 : if (gotXSpacing && gotYSpacing)
8205 : {
8206 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8207 : const double dfXMax =
8208 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8209 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8210 : const double dfYMin =
8211 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8212 1 : aosArgv.AddString("-te");
8213 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8214 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8215 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8216 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8217 : }
8218 :
8219 24 : if (poNewDimX && poNewDimY)
8220 : {
8221 3 : aosArgv.AddString("-ts");
8222 : aosArgv.AddString(
8223 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8224 : aosArgv.AddString(
8225 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8226 : }
8227 21 : else if (poNewDimX)
8228 : {
8229 1 : aosArgv.AddString("-ts");
8230 : aosArgv.AddString(
8231 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8232 1 : aosArgv.AddString("0");
8233 : }
8234 20 : else if (poNewDimY)
8235 : {
8236 1 : aosArgv.AddString("-ts");
8237 1 : aosArgv.AddString("0");
8238 : aosArgv.AddString(
8239 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8240 : }
8241 :
8242 : // Create a warped VRT dataset
8243 : GDALWarpAppOptions *psOptions =
8244 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8245 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8246 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8247 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8248 24 : GDALWarpAppOptionsFree(psOptions);
8249 24 : if (poReprojectedDS == nullptr)
8250 3 : return nullptr;
8251 :
8252 : int nBlockXSize;
8253 : int nBlockYSize;
8254 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8255 21 : anBlockSize.emplace_back(nBlockYSize);
8256 21 : anBlockSize.emplace_back(nBlockXSize);
8257 :
8258 21 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8259 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8260 21 : CPLAssert(eErr == CE_None);
8261 21 : CPL_IGNORE_RET_VAL(eErr);
8262 :
8263 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8264 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8265 42 : poReprojectedDS->GetRasterYSize());
8266 : auto varY = GDALMDArrayRegularlySpaced::Create(
8267 63 : std::string(), poDimY->GetName(), poDimY,
8268 84 : adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8269 21 : poDimY->SetIndexingVariable(varY);
8270 :
8271 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8272 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8273 42 : poReprojectedDS->GetRasterXSize());
8274 : auto varX = GDALMDArrayRegularlySpaced::Create(
8275 63 : std::string(), poDimX->GetName(), poDimX,
8276 84 : adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8277 21 : poDimX->SetIndexingVariable(varX);
8278 :
8279 21 : apoNewDims.emplace_back(poDimY);
8280 21 : apoNewDims.emplace_back(poDimX);
8281 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8282 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8283 21 : newAr->SetSelf(newAr);
8284 21 : if (poTargetSRS)
8285 : {
8286 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8287 : }
8288 : else
8289 : {
8290 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8291 : }
8292 21 : newAr->m_poVarX = varX;
8293 21 : newAr->m_poVarY = varY;
8294 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8295 21 : newAr->m_poParentDS = std::move(poParentDS);
8296 :
8297 : // If the input array is y,x,band ordered, the above newAr is
8298 : // actually band,y,x ordered as it is more convenient for
8299 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8300 : // array to the order of the input array
8301 21 : if (bYXBandOrder)
8302 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8303 :
8304 19 : return newAr;
8305 : }
8306 :
8307 : /************************************************************************/
8308 : /* GDALMDArrayResampled::IRead() */
8309 : /************************************************************************/
8310 :
8311 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8312 : const size_t *count, const GInt64 *arrayStep,
8313 : const GPtrDiff_t *bufferStride,
8314 : const GDALExtendedDataType &bufferDataType,
8315 : void *pDstBuffer) const
8316 : {
8317 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8318 0 : return false;
8319 :
8320 : struct Stack
8321 : {
8322 : size_t nIters = 0;
8323 : GByte *dst_ptr = nullptr;
8324 : GPtrDiff_t dst_inc_offset = 0;
8325 : };
8326 :
8327 29 : const auto nDims = GetDimensionCount();
8328 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8329 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8330 92 : for (size_t i = 0; i < nDims; i++)
8331 : {
8332 63 : stack[i].dst_inc_offset =
8333 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8334 : }
8335 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8336 :
8337 29 : size_t dimIdx = 0;
8338 29 : const size_t iDimY = nDims - 2;
8339 29 : const size_t iDimX = nDims - 1;
8340 : // Use an array to avoid a false positive warning from CLang Static
8341 : // Analyzer about flushCaches being never read
8342 29 : bool flushCaches[] = {false};
8343 : const bool bYXBandOrder =
8344 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8345 :
8346 38 : lbl_next_depth:
8347 38 : if (dimIdx == iDimY)
8348 : {
8349 33 : if (flushCaches[0])
8350 : {
8351 5 : flushCaches[0] = false;
8352 : // When changing of 2D slice, flush GDAL 2D buffers
8353 5 : m_poParentDS->FlushCache(false);
8354 5 : m_poReprojectedDS->FlushCache(false);
8355 : }
8356 :
8357 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8358 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8359 : arrayStep, bufferStride, bufferDataType,
8360 33 : stack[dimIdx].dst_ptr))
8361 : {
8362 0 : return false;
8363 : }
8364 : }
8365 : else
8366 : {
8367 5 : stack[dimIdx].nIters = count[dimIdx];
8368 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8369 5 : arrayStartIdx[dimIdx])
8370 : {
8371 1 : flushCaches[0] = true;
8372 : }
8373 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8374 5 : arrayStartIdx[dimIdx];
8375 : while (true)
8376 : {
8377 9 : dimIdx++;
8378 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8379 9 : goto lbl_next_depth;
8380 9 : lbl_return_to_caller:
8381 9 : dimIdx--;
8382 9 : if ((--stack[dimIdx].nIters) == 0)
8383 5 : break;
8384 4 : flushCaches[0] = true;
8385 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8386 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8387 : }
8388 : }
8389 38 : if (dimIdx > 0)
8390 9 : goto lbl_return_to_caller;
8391 :
8392 29 : return true;
8393 : }
8394 :
8395 : /************************************************************************/
8396 : /* GetResampled() */
8397 : /************************************************************************/
8398 :
8399 : /** Return an array that is a resampled / reprojected view of the current array
8400 : *
8401 : * This is the same as the C function GDALMDArrayGetResampled().
8402 : *
8403 : * Currently this method can only resample along the last 2 dimensions, unless
8404 : * orthorectifying a NASA EMIT dataset.
8405 : *
8406 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8407 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8408 : *
8409 : * Options available are:
8410 : * <ul>
8411 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8412 : * Can be set to NO to use generic reprojection method.
8413 : * </li>
8414 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8415 : * orthorectification to take into account the value of the
8416 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8417 : * current array along the band dimension are valid.</li>
8418 : * </ul>
8419 : *
8420 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8421 : * apoNewDims[i] can be NULL to let the method automatically
8422 : * determine it.
8423 : * @param resampleAlg Resampling algorithm
8424 : * @param poTargetSRS Target SRS, or nullptr
8425 : * @param papszOptions NULL-terminated list of options, or NULL.
8426 : *
8427 : * @return a new array, that holds a reference to the original one, and thus is
8428 : * a view of it (not a copy), or nullptr in case of error.
8429 : *
8430 : * @since 3.4
8431 : */
8432 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8433 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8434 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8435 : CSLConstList papszOptions) const
8436 : {
8437 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8438 38 : if (!self)
8439 : {
8440 0 : CPLError(CE_Failure, CPLE_AppDefined,
8441 : "Driver implementation issue: m_pSelf not set !");
8442 0 : return nullptr;
8443 : }
8444 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8445 : {
8446 0 : CPLError(CE_Failure, CPLE_AppDefined,
8447 : "GetResampled() only supports numeric data type");
8448 0 : return nullptr;
8449 : }
8450 :
8451 : // Special case for NASA EMIT datasets
8452 76 : auto apoDims = GetDimensions();
8453 36 : if (poTargetSRS == nullptr &&
8454 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8455 20 : apoDims[1]->GetName() == "crosstrack" &&
8456 10 : apoDims[2]->GetName() == "bands" &&
8457 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8458 1 : apoNewDims ==
8459 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8460 30 : apoDims[2]})) ||
8461 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8462 3 : apoDims[1]->GetName() == "crosstrack" &&
8463 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8464 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8465 : "EMIT_ORTHORECTIFICATION", "YES")))
8466 : {
8467 9 : auto poRootGroup = GetRootGroup();
8468 9 : if (poRootGroup)
8469 : {
8470 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8471 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8472 9 : if (poAttrGeotransform &&
8473 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8474 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8475 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8476 9 : poLocationGroup)
8477 : {
8478 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8479 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8480 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8481 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8482 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8483 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8484 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8485 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8486 : {
8487 : return CreateGLTOrthorectified(
8488 : self, poRootGroup, poGLT_X, poGLT_Y,
8489 : /* nGLTIndexOffset = */ -1,
8490 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8491 : }
8492 : }
8493 : }
8494 : }
8495 :
8496 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8497 : "EMIT_ORTHORECTIFICATION", "NO")))
8498 : {
8499 0 : CPLError(CE_Failure, CPLE_AppDefined,
8500 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8501 : "parameters are not compatible with it");
8502 0 : return nullptr;
8503 : }
8504 :
8505 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8506 29 : poTargetSRS, papszOptions);
8507 : }
8508 :
8509 : /************************************************************************/
8510 : /* GDALDatasetFromArray() */
8511 : /************************************************************************/
8512 :
8513 : class GDALDatasetFromArray;
8514 :
8515 : namespace
8516 : {
8517 : struct MetadataItem
8518 : {
8519 : std::shared_ptr<GDALMDArray> poArray{};
8520 : std::string osName{};
8521 : std::string osDefinition{};
8522 : bool bDefinitionUsesPctForG = false;
8523 : };
8524 :
8525 : struct BandImageryMetadata
8526 : {
8527 : std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
8528 : double dfCentralWavelengthToMicrometer = 1.0;
8529 : std::shared_ptr<GDALMDArray> poFWHMArray{};
8530 : double dfFWHMToMicrometer = 1.0;
8531 : };
8532 :
8533 : } // namespace
8534 :
8535 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8536 : {
8537 : std::vector<GUInt64> m_anOffset{};
8538 : std::vector<size_t> m_anCount{};
8539 : std::vector<GPtrDiff_t> m_anStride{};
8540 :
8541 : protected:
8542 : CPLErr IReadBlock(int, int, void *) override;
8543 : CPLErr IWriteBlock(int, int, void *) override;
8544 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8545 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8546 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8547 : GSpacing nLineSpaceBuf,
8548 : GDALRasterIOExtraArg *psExtraArg) override;
8549 :
8550 : public:
8551 : explicit GDALRasterBandFromArray(
8552 : GDALDatasetFromArray *poDSIn,
8553 : const std::vector<GUInt64> &anOtherDimCoord,
8554 : const std::vector<std::vector<MetadataItem>>
8555 : &aoBandParameterMetadataItems,
8556 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8557 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8558 :
8559 : double GetNoDataValue(int *pbHasNoData) override;
8560 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8561 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8562 : double GetOffset(int *pbHasOffset) override;
8563 : double GetScale(int *pbHasScale) override;
8564 : const char *GetUnitType() override;
8565 : GDALColorInterp GetColorInterpretation() override;
8566 : };
8567 :
8568 : class GDALDatasetFromArray final : public GDALPamDataset
8569 : {
8570 : friend class GDALRasterBandFromArray;
8571 :
8572 : std::shared_ptr<GDALMDArray> m_poArray;
8573 : size_t m_iXDim;
8574 : size_t m_iYDim;
8575 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8576 : bool m_bHasGT = false;
8577 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8578 : GDALMultiDomainMetadata m_oMDD{};
8579 : std::string m_osOvrFilename{};
8580 :
8581 : public:
8582 191 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8583 : size_t iXDim, size_t iYDim)
8584 191 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8585 : {
8586 : // Initialize an overview filename from the filename of the array
8587 : // and its name.
8588 191 : const std::string &osFilename = m_poArray->GetFilename();
8589 191 : if (!osFilename.empty())
8590 : {
8591 170 : m_osOvrFilename = osFilename;
8592 170 : m_osOvrFilename += '.';
8593 6350 : for (char ch : m_poArray->GetName())
8594 : {
8595 6180 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8596 5481 : (ch >= 'a' && ch <= 'z') || ch == '_')
8597 : {
8598 4943 : m_osOvrFilename += ch;
8599 : }
8600 : else
8601 : {
8602 1237 : m_osOvrFilename += '_';
8603 : }
8604 : }
8605 170 : m_osOvrFilename += ".ovr";
8606 170 : oOvManager.Initialize(this);
8607 : }
8608 191 : }
8609 :
8610 : static GDALDatasetFromArray *
8611 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8612 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8613 : CSLConstList papszOptions);
8614 :
8615 382 : ~GDALDatasetFromArray()
8616 191 : {
8617 191 : GDALDatasetFromArray::Close();
8618 382 : }
8619 :
8620 314 : CPLErr Close() override
8621 : {
8622 314 : CPLErr eErr = CE_None;
8623 314 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8624 : {
8625 314 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8626 : CE_None)
8627 0 : eErr = CE_Failure;
8628 314 : m_poArray.reset();
8629 : }
8630 314 : return eErr;
8631 : }
8632 :
8633 49 : CPLErr GetGeoTransform(double *padfGeoTransform) override
8634 : {
8635 49 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8636 49 : return m_bHasGT ? CE_None : CE_Failure;
8637 : }
8638 :
8639 57 : const OGRSpatialReference *GetSpatialRef() const override
8640 : {
8641 57 : if (m_poArray->GetDimensionCount() < 2)
8642 3 : return nullptr;
8643 54 : m_poSRS = m_poArray->GetSpatialRef();
8644 54 : if (m_poSRS)
8645 : {
8646 20 : m_poSRS.reset(m_poSRS->Clone());
8647 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8648 60 : for (auto &m : axisMapping)
8649 : {
8650 40 : if (m == static_cast<int>(m_iXDim) + 1)
8651 20 : m = 1;
8652 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8653 20 : m = 2;
8654 : }
8655 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8656 : }
8657 54 : return m_poSRS.get();
8658 : }
8659 :
8660 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8661 : {
8662 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8663 : }
8664 :
8665 144 : char **GetMetadata(const char *pszDomain) override
8666 : {
8667 144 : return m_oMDD.GetMetadata(pszDomain);
8668 : }
8669 :
8670 204 : const char *GetMetadataItem(const char *pszName,
8671 : const char *pszDomain) override
8672 : {
8673 378 : if (!m_osOvrFilename.empty() && pszName &&
8674 390 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8675 12 : EQUAL(pszDomain, "OVERVIEWS"))
8676 : {
8677 12 : return m_osOvrFilename.c_str();
8678 : }
8679 192 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8680 : }
8681 : };
8682 :
8683 : /************************************************************************/
8684 : /* GDALRasterBandFromArray() */
8685 : /************************************************************************/
8686 :
8687 253 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8688 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8689 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8690 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8691 253 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8692 : {
8693 253 : const auto &poArray(poDSIn->m_poArray);
8694 253 : const auto &dims(poArray->GetDimensions());
8695 253 : const auto nDimCount(dims.size());
8696 506 : const auto blockSize(poArray->GetBlockSize());
8697 242 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8698 495 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8699 133 : blockSize[poDSIn->m_iYDim]))
8700 : : 1;
8701 253 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8702 144 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8703 144 : blockSize[poDSIn->m_iXDim]))
8704 253 : : poDSIn->GetRasterXSize();
8705 253 : eDataType = poArray->GetDataType().GetNumericDataType();
8706 253 : eAccess = poDSIn->eAccess;
8707 253 : m_anOffset.resize(nDimCount);
8708 253 : m_anCount.resize(nDimCount, 1);
8709 253 : m_anStride.resize(nDimCount);
8710 856 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8711 : {
8712 603 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8713 : {
8714 216 : std::string dimName(dims[i]->GetName());
8715 108 : GUInt64 nIndex = anOtherDimCoord[j];
8716 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8717 : // subsetted dimensions as generated by GetView()
8718 108 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8719 : {
8720 : CPLStringList aosTokens(
8721 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8722 6 : if (aosTokens.size() == 5)
8723 : {
8724 6 : dimName = aosTokens[1];
8725 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8726 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8727 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8728 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8729 0 : : nStartDim - (nIndex * -nIncrDim);
8730 : }
8731 : }
8732 108 : if (nDimCount != 3 || dimName != "Band")
8733 : {
8734 52 : SetMetadataItem(
8735 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8736 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8737 : }
8738 :
8739 108 : auto indexingVar = dims[i]->GetIndexingVariable();
8740 :
8741 : // If the indexing variable is also listed in band parameter arrays,
8742 : // then don't use our default formatting
8743 108 : if (indexingVar)
8744 : {
8745 38 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8746 : {
8747 12 : if (oItem.poArray->GetFullName() ==
8748 12 : indexingVar->GetFullName())
8749 : {
8750 12 : indexingVar.reset();
8751 12 : break;
8752 : }
8753 : }
8754 : }
8755 :
8756 134 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8757 26 : indexingVar->GetDimensions()[0]->GetSize() ==
8758 26 : dims[i]->GetSize())
8759 : {
8760 26 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8761 : {
8762 0 : if (!bHasWarned)
8763 : {
8764 0 : CPLError(
8765 : CE_Warning, CPLE_AppDefined,
8766 : "Maximum delay to load band metadata from "
8767 : "dimension indexing variables has expired. "
8768 : "Increase the value of the "
8769 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8770 : "option of GDALMDArray::AsClassicDataset() "
8771 : "(also accessible as the "
8772 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8773 : "configuration option), "
8774 : "or set it to 'unlimited' for unlimited delay. ");
8775 0 : bHasWarned = true;
8776 : }
8777 : }
8778 : else
8779 : {
8780 26 : size_t nCount = 1;
8781 26 : const auto &dt(indexingVar->GetDataType());
8782 52 : std::vector<GByte> abyTmp(dt.GetSize());
8783 52 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8784 26 : nullptr, nullptr, dt, &abyTmp[0]))
8785 : {
8786 26 : char *pszTmp = nullptr;
8787 26 : GDALExtendedDataType::CopyValue(
8788 26 : &abyTmp[0], dt, &pszTmp,
8789 52 : GDALExtendedDataType::CreateString());
8790 26 : if (pszTmp)
8791 : {
8792 26 : SetMetadataItem(
8793 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8794 : pszTmp);
8795 26 : CPLFree(pszTmp);
8796 : }
8797 :
8798 26 : const auto &unit(indexingVar->GetUnit());
8799 26 : if (!unit.empty())
8800 : {
8801 12 : SetMetadataItem(
8802 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8803 : unit.c_str());
8804 : }
8805 : }
8806 : }
8807 : }
8808 :
8809 124 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8810 : {
8811 32 : CPLString osVal;
8812 :
8813 16 : size_t nCount = 1;
8814 16 : const auto &dt(oItem.poArray->GetDataType());
8815 16 : if (oItem.bDefinitionUsesPctForG)
8816 : {
8817 : // There is one and only one %[x][.y]f|g in osDefinition
8818 12 : std::vector<GByte> abyTmp(dt.GetSize());
8819 12 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8820 6 : nullptr, nullptr, dt, &abyTmp[0]))
8821 : {
8822 6 : double dfVal = 0;
8823 6 : GDALExtendedDataType::CopyValue(
8824 6 : &abyTmp[0], dt, &dfVal,
8825 12 : GDALExtendedDataType::Create(GDT_Float64));
8826 6 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8827 : }
8828 : }
8829 : else
8830 : {
8831 : // There should be zero or one %s in osDefinition
8832 10 : char *pszValue = nullptr;
8833 10 : if (dt.GetClass() == GEDTC_STRING)
8834 : {
8835 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8836 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8837 : dt, &pszValue));
8838 : }
8839 : else
8840 : {
8841 16 : std::vector<GByte> abyTmp(dt.GetSize());
8842 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8843 : nullptr, nullptr, dt,
8844 8 : &abyTmp[0]))
8845 : {
8846 8 : GDALExtendedDataType::CopyValue(
8847 8 : &abyTmp[0], dt, &pszValue,
8848 16 : GDALExtendedDataType::CreateString());
8849 : }
8850 : }
8851 :
8852 10 : if (pszValue)
8853 : {
8854 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8855 10 : CPLFree(pszValue);
8856 : }
8857 : }
8858 16 : if (!osVal.empty())
8859 16 : SetMetadataItem(oItem.osName.c_str(), osVal);
8860 : }
8861 :
8862 108 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8863 : {
8864 : auto &poCentralWavelengthArray =
8865 2 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8866 2 : size_t nCount = 1;
8867 2 : const auto &dt(poCentralWavelengthArray->GetDataType());
8868 4 : std::vector<GByte> abyTmp(dt.GetSize());
8869 4 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8870 : &nCount, nullptr, nullptr,
8871 2 : dt, &abyTmp[0]))
8872 : {
8873 2 : double dfVal = 0;
8874 2 : GDALExtendedDataType::CopyValue(
8875 2 : &abyTmp[0], dt, &dfVal,
8876 4 : GDALExtendedDataType::Create(GDT_Float64));
8877 2 : SetMetadataItem(
8878 : "CENTRAL_WAVELENGTH_UM",
8879 : CPLSPrintf(
8880 2 : "%g", dfVal * aoBandImageryMetadata[j]
8881 2 : .dfCentralWavelengthToMicrometer),
8882 : "IMAGERY");
8883 : }
8884 : }
8885 :
8886 108 : if (aoBandImageryMetadata[j].poFWHMArray)
8887 : {
8888 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
8889 2 : size_t nCount = 1;
8890 2 : const auto &dt(poFWHMArray->GetDataType());
8891 4 : std::vector<GByte> abyTmp(dt.GetSize());
8892 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
8893 2 : nullptr, dt, &abyTmp[0]))
8894 : {
8895 2 : double dfVal = 0;
8896 2 : GDALExtendedDataType::CopyValue(
8897 2 : &abyTmp[0], dt, &dfVal,
8898 4 : GDALExtendedDataType::Create(GDT_Float64));
8899 2 : SetMetadataItem(
8900 : "FWHM_UM",
8901 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
8902 2 : .dfFWHMToMicrometer),
8903 : "IMAGERY");
8904 : }
8905 : }
8906 :
8907 108 : m_anOffset[i] = anOtherDimCoord[j];
8908 108 : j++;
8909 : }
8910 : }
8911 253 : }
8912 :
8913 : /************************************************************************/
8914 : /* GetNoDataValue() */
8915 : /************************************************************************/
8916 :
8917 93 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
8918 : {
8919 93 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8920 93 : const auto &poArray(l_poDS->m_poArray);
8921 93 : bool bHasNodata = false;
8922 93 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
8923 93 : if (pbHasNoData)
8924 81 : *pbHasNoData = bHasNodata;
8925 93 : return res;
8926 : }
8927 :
8928 : /************************************************************************/
8929 : /* GetNoDataValueAsInt64() */
8930 : /************************************************************************/
8931 :
8932 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
8933 : {
8934 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8935 1 : const auto &poArray(l_poDS->m_poArray);
8936 1 : bool bHasNodata = false;
8937 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
8938 1 : if (pbHasNoData)
8939 1 : *pbHasNoData = bHasNodata;
8940 1 : return nodata;
8941 : }
8942 :
8943 : /************************************************************************/
8944 : /* GetNoDataValueAsUInt64() */
8945 : /************************************************************************/
8946 :
8947 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
8948 : {
8949 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8950 1 : const auto &poArray(l_poDS->m_poArray);
8951 1 : bool bHasNodata = false;
8952 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
8953 1 : if (pbHasNoData)
8954 1 : *pbHasNoData = bHasNodata;
8955 1 : return nodata;
8956 : }
8957 :
8958 : /************************************************************************/
8959 : /* GetOffset() */
8960 : /************************************************************************/
8961 :
8962 29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
8963 : {
8964 29 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8965 29 : const auto &poArray(l_poDS->m_poArray);
8966 29 : bool bHasValue = false;
8967 29 : double dfRes = poArray->GetOffset(&bHasValue);
8968 29 : if (pbHasOffset)
8969 17 : *pbHasOffset = bHasValue;
8970 29 : return dfRes;
8971 : }
8972 :
8973 : /************************************************************************/
8974 : /* GetUnitType() */
8975 : /************************************************************************/
8976 :
8977 36 : const char *GDALRasterBandFromArray::GetUnitType()
8978 : {
8979 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8980 36 : const auto &poArray(l_poDS->m_poArray);
8981 36 : return poArray->GetUnit().c_str();
8982 : }
8983 :
8984 : /************************************************************************/
8985 : /* GetScale() */
8986 : /************************************************************************/
8987 :
8988 27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
8989 : {
8990 27 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8991 27 : const auto &poArray(l_poDS->m_poArray);
8992 27 : bool bHasValue = false;
8993 27 : double dfRes = poArray->GetScale(&bHasValue);
8994 27 : if (pbHasScale)
8995 15 : *pbHasScale = bHasValue;
8996 27 : return dfRes;
8997 : }
8998 :
8999 : /************************************************************************/
9000 : /* IReadBlock() */
9001 : /************************************************************************/
9002 :
9003 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9004 : void *pImage)
9005 : {
9006 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9007 68 : const int nXOff = nBlockXOff * nBlockXSize;
9008 68 : const int nYOff = nBlockYOff * nBlockYSize;
9009 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9010 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9011 : GDALRasterIOExtraArg sExtraArg;
9012 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9013 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9014 : nReqXSize, nReqYSize, eDataType, nDTSize,
9015 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9016 : }
9017 :
9018 : /************************************************************************/
9019 : /* IWriteBlock() */
9020 : /************************************************************************/
9021 :
9022 0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9023 : void *pImage)
9024 : {
9025 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9026 0 : const int nXOff = nBlockXOff * nBlockXSize;
9027 0 : const int nYOff = nBlockYOff * nBlockYSize;
9028 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9029 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9030 : GDALRasterIOExtraArg sExtraArg;
9031 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9032 0 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9033 : nReqXSize, nReqYSize, eDataType, nDTSize,
9034 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9035 : }
9036 :
9037 : /************************************************************************/
9038 : /* IRasterIO() */
9039 : /************************************************************************/
9040 :
9041 321 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9042 : int nYOff, int nXSize, int nYSize,
9043 : void *pData, int nBufXSize,
9044 : int nBufYSize, GDALDataType eBufType,
9045 : GSpacing nPixelSpaceBuf,
9046 : GSpacing nLineSpaceBuf,
9047 : GDALRasterIOExtraArg *psExtraArg)
9048 : {
9049 321 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9050 321 : const auto &poArray(l_poDS->m_poArray);
9051 321 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9052 321 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9053 321 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9054 321 : (nLineSpaceBuf % nBufferDTSize) == 0)
9055 : {
9056 321 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9057 321 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9058 642 : m_anStride[l_poDS->m_iXDim] =
9059 321 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9060 321 : if (poArray->GetDimensionCount() >= 2)
9061 : {
9062 312 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9063 312 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9064 312 : m_anStride[l_poDS->m_iYDim] =
9065 312 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9066 : }
9067 321 : if (eRWFlag == GF_Read)
9068 : {
9069 632 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9070 316 : m_anStride.data(),
9071 632 : GDALExtendedDataType::Create(eBufType), pData)
9072 316 : ? CE_None
9073 316 : : CE_Failure;
9074 : }
9075 : else
9076 : {
9077 10 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9078 5 : m_anStride.data(),
9079 10 : GDALExtendedDataType::Create(eBufType), pData)
9080 5 : ? CE_None
9081 5 : : CE_Failure;
9082 : }
9083 : }
9084 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9085 : pData, nBufXSize, nBufYSize, eBufType,
9086 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9087 : }
9088 :
9089 : /************************************************************************/
9090 : /* GetColorInterpretation() */
9091 : /************************************************************************/
9092 :
9093 45 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9094 : {
9095 45 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9096 45 : const auto &poArray(l_poDS->m_poArray);
9097 135 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9098 45 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9099 : {
9100 6 : bool bOK = false;
9101 6 : GUInt64 nStartIndex = 0;
9102 6 : if (poArray->GetDimensionCount() == 2 &&
9103 0 : poAttr->GetDimensionCount() == 0)
9104 : {
9105 0 : bOK = true;
9106 : }
9107 6 : else if (poArray->GetDimensionCount() == 3)
9108 : {
9109 6 : uint64_t nExtraDimSamples = 1;
9110 6 : const auto &apoDims = poArray->GetDimensions();
9111 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9112 : {
9113 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9114 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9115 : }
9116 6 : if (poAttr->GetDimensionsSize() ==
9117 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9118 : {
9119 6 : bOK = true;
9120 : }
9121 6 : nStartIndex = nBand - 1;
9122 : }
9123 6 : if (bOK)
9124 : {
9125 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9126 6 : const size_t nCount = 1;
9127 6 : const GInt64 arrayStep = 1;
9128 6 : const GPtrDiff_t bufferStride = 1;
9129 6 : char *pszValue = nullptr;
9130 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9131 6 : oStringDT, &pszValue);
9132 6 : if (pszValue)
9133 : {
9134 : const auto eColorInterp =
9135 6 : GDALGetColorInterpretationByName(pszValue);
9136 6 : CPLFree(pszValue);
9137 6 : return eColorInterp;
9138 : }
9139 : }
9140 : }
9141 39 : return GCI_Undefined;
9142 : }
9143 :
9144 : /************************************************************************/
9145 : /* GDALDatasetFromArray::Create() */
9146 : /************************************************************************/
9147 :
9148 224 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9149 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9150 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9151 :
9152 : {
9153 224 : const auto nDimCount(array->GetDimensionCount());
9154 224 : if (nDimCount == 0)
9155 : {
9156 1 : CPLError(CE_Failure, CPLE_NotSupported,
9157 : "Unsupported number of dimensions");
9158 1 : return nullptr;
9159 : }
9160 445 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9161 222 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9162 : {
9163 1 : CPLError(CE_Failure, CPLE_NotSupported,
9164 : "Only arrays with numeric data types "
9165 : "can be exposed as classic GDALDataset");
9166 1 : return nullptr;
9167 : }
9168 222 : if (iXDim >= nDimCount ||
9169 208 : (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
9170 : {
9171 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9172 6 : return nullptr;
9173 : }
9174 216 : GUInt64 nTotalBands = 1;
9175 216 : const auto &dims(array->GetDimensions());
9176 700 : for (size_t i = 0; i < nDimCount; ++i)
9177 : {
9178 485 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9179 : {
9180 66 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9181 : {
9182 1 : CPLError(CE_Failure, CPLE_AppDefined,
9183 : "Too many bands. Operate on a sliced view");
9184 1 : return nullptr;
9185 : }
9186 65 : nTotalBands *= dims[i]->GetSize();
9187 : }
9188 : }
9189 :
9190 430 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9191 699 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9192 : {
9193 484 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9194 : {
9195 65 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9196 65 : ++j;
9197 : }
9198 : }
9199 :
9200 215 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9201 :
9202 : const char *pszBandMetadata =
9203 215 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9204 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9205 430 : nNewDimCount);
9206 215 : if (pszBandMetadata)
9207 : {
9208 21 : if (!poRootGroup)
9209 : {
9210 1 : CPLError(CE_Failure, CPLE_AppDefined,
9211 : "Root group should be provided when BAND_METADATA is set");
9212 14 : return nullptr;
9213 : }
9214 20 : CPLJSONDocument oDoc;
9215 20 : if (!oDoc.LoadMemory(pszBandMetadata))
9216 : {
9217 1 : CPLError(CE_Failure, CPLE_AppDefined,
9218 : "Invalid JSON content for BAND_METADATA");
9219 1 : return nullptr;
9220 : }
9221 19 : auto oRoot = oDoc.GetRoot();
9222 19 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9223 : {
9224 1 : CPLError(CE_Failure, CPLE_AppDefined,
9225 : "Value of BAND_METADATA should be an array");
9226 1 : return nullptr;
9227 : }
9228 :
9229 18 : auto oArray = oRoot.ToArray();
9230 26 : for (int j = 0; j < oArray.Size(); ++j)
9231 : {
9232 19 : const auto oJsonItem = oArray[j];
9233 19 : MetadataItem oItem;
9234 :
9235 38 : auto osBandArrayFullname = oJsonItem.GetString("array");
9236 19 : if (osBandArrayFullname.empty())
9237 : {
9238 1 : CPLError(CE_Failure, CPLE_AppDefined,
9239 : "BAND_METADATA[%d][\"array\"] is missing", j);
9240 1 : return nullptr;
9241 : }
9242 : oItem.poArray =
9243 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9244 18 : if (!oItem.poArray)
9245 : {
9246 1 : CPLError(CE_Failure, CPLE_AppDefined,
9247 : "Array %s cannot be found",
9248 : osBandArrayFullname.c_str());
9249 1 : return nullptr;
9250 : }
9251 17 : if (oItem.poArray->GetDimensionCount() != 1)
9252 : {
9253 1 : CPLError(CE_Failure, CPLE_AppDefined,
9254 : "Array %s is not a 1D array",
9255 : osBandArrayFullname.c_str());
9256 1 : return nullptr;
9257 : }
9258 : const auto &osAuxArrayDimName =
9259 16 : oItem.poArray->GetDimensions()[0]->GetName();
9260 16 : auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9261 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9262 : {
9263 1 : CPLError(CE_Failure, CPLE_AppDefined,
9264 : "Dimension %s of array %s is not a non-X/Y dimension "
9265 : "of array %s",
9266 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9267 1 : array->GetName().c_str());
9268 1 : return nullptr;
9269 : }
9270 15 : const size_t iExtraDimIdx = oIter->second;
9271 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9272 :
9273 15 : oItem.osName = oJsonItem.GetString("item_name");
9274 15 : if (oItem.osName.empty())
9275 : {
9276 1 : CPLError(CE_Failure, CPLE_AppDefined,
9277 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9278 1 : return nullptr;
9279 : }
9280 :
9281 28 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9282 :
9283 : // Check correctness of definition
9284 14 : bool bFirstNumericFormatter = true;
9285 14 : std::string osModDefinition;
9286 14 : bool bDefinitionUsesPctForG = false;
9287 72 : for (size_t k = 0; k < osDefinition.size(); ++k)
9288 : {
9289 64 : if (osDefinition[k] == '%')
9290 : {
9291 13 : osModDefinition += osDefinition[k];
9292 13 : if (k + 1 == osDefinition.size())
9293 : {
9294 1 : CPLError(CE_Failure, CPLE_AppDefined,
9295 : "Value of "
9296 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9297 : "%s is invalid at offset %d",
9298 : osAuxArrayDimName.c_str(), j,
9299 : osDefinition.c_str(), int(k));
9300 1 : return nullptr;
9301 : }
9302 12 : ++k;
9303 12 : if (osDefinition[k] == '%')
9304 : {
9305 1 : osModDefinition += osDefinition[k];
9306 1 : continue;
9307 : }
9308 11 : if (!bFirstNumericFormatter)
9309 : {
9310 1 : CPLError(
9311 : CE_Failure, CPLE_AppDefined,
9312 : "Value of "
9313 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
9314 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9315 : "formatters should be specified at most once",
9316 : osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
9317 : int(k));
9318 1 : return nullptr;
9319 : }
9320 10 : bFirstNumericFormatter = false;
9321 13 : for (; k < osDefinition.size(); ++k)
9322 : {
9323 13 : osModDefinition += osDefinition[k];
9324 26 : if (!((osDefinition[k] >= '0' &&
9325 12 : osDefinition[k] <= '9') ||
9326 11 : osDefinition[k] == '.'))
9327 10 : break;
9328 : }
9329 20 : if (k == osDefinition.size() ||
9330 10 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9331 5 : osDefinition[k] != 's'))
9332 : {
9333 1 : CPLError(CE_Failure, CPLE_AppDefined,
9334 : "Value of "
9335 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9336 : "%s is invalid at offset %d: only "
9337 : "%%[x][.y]f|g or %%s formatters are accepted",
9338 : osAuxArrayDimName.c_str(), j,
9339 : osDefinition.c_str(), int(k));
9340 1 : return nullptr;
9341 : }
9342 9 : bDefinitionUsesPctForG =
9343 9 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9344 9 : if (bDefinitionUsesPctForG)
9345 : {
9346 5 : if (oItem.poArray->GetDataType().GetClass() !=
9347 : GEDTC_NUMERIC)
9348 : {
9349 1 : CPLError(CE_Failure, CPLE_AppDefined,
9350 : "Data type of %s array is not numeric",
9351 : osAuxArrayDimName.c_str());
9352 1 : return nullptr;
9353 : }
9354 : }
9355 : }
9356 56 : else if (osDefinition[k] == '$' &&
9357 56 : k + 1 < osDefinition.size() &&
9358 5 : osDefinition[k + 1] == '{')
9359 : {
9360 5 : const auto nPos = osDefinition.find('}', k);
9361 5 : if (nPos == std::string::npos)
9362 : {
9363 1 : CPLError(CE_Failure, CPLE_AppDefined,
9364 : "Value of "
9365 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9366 : "%s is invalid at offset %d",
9367 : osAuxArrayDimName.c_str(), j,
9368 : osDefinition.c_str(), int(k));
9369 2 : return nullptr;
9370 : }
9371 : const auto osAttrName =
9372 4 : osDefinition.substr(k + 2, nPos - (k + 2));
9373 4 : auto poAttr = oItem.poArray->GetAttribute(osAttrName);
9374 4 : if (!poAttr)
9375 : {
9376 1 : CPLError(CE_Failure, CPLE_AppDefined,
9377 : "Value of "
9378 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9379 : "%s is invalid: %s is not an attribute of %s",
9380 : osAuxArrayDimName.c_str(), j,
9381 : osDefinition.c_str(), osAttrName.c_str(),
9382 : osAuxArrayDimName.c_str());
9383 1 : return nullptr;
9384 : }
9385 3 : k = nPos;
9386 3 : const char *pszValue = poAttr->ReadAsString();
9387 3 : if (!pszValue)
9388 : {
9389 0 : CPLError(CE_Failure, CPLE_AppDefined,
9390 : "Cannot get value of attribute %s of %s as a "
9391 : "string",
9392 : osAttrName.c_str(), osAuxArrayDimName.c_str());
9393 0 : return nullptr;
9394 : }
9395 3 : osModDefinition += pszValue;
9396 : }
9397 : else
9398 : {
9399 46 : osModDefinition += osDefinition[k];
9400 : }
9401 : }
9402 :
9403 8 : oItem.osDefinition = std::move(osModDefinition);
9404 8 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9405 :
9406 8 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9407 8 : std::move(oItem));
9408 : }
9409 : }
9410 :
9411 402 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9412 : const char *pszBandImageryMetadata =
9413 201 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9414 201 : if (pszBandImageryMetadata)
9415 : {
9416 12 : if (!poRootGroup)
9417 : {
9418 1 : CPLError(CE_Failure, CPLE_AppDefined,
9419 : "Root group should be provided when BAND_IMAGERY_METADATA "
9420 : "is set");
9421 10 : return nullptr;
9422 : }
9423 11 : CPLJSONDocument oDoc;
9424 11 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9425 : {
9426 1 : CPLError(CE_Failure, CPLE_AppDefined,
9427 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9428 1 : return nullptr;
9429 : }
9430 10 : auto oRoot = oDoc.GetRoot();
9431 10 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9432 : {
9433 1 : CPLError(CE_Failure, CPLE_AppDefined,
9434 : "Value of BAND_IMAGERY_METADATA should be an object");
9435 1 : return nullptr;
9436 : }
9437 12 : for (const auto &oJsonItem : oRoot.GetChildren())
9438 : {
9439 22 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9440 12 : oJsonItem.GetName() == "FWHM_UM")
9441 : {
9442 18 : auto osBandArrayFullname = oJsonItem.GetString("array");
9443 9 : if (osBandArrayFullname.empty())
9444 : {
9445 1 : CPLError(
9446 : CE_Failure, CPLE_AppDefined,
9447 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
9448 2 : oJsonItem.GetName().c_str());
9449 1 : return nullptr;
9450 : }
9451 : auto poArray =
9452 8 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9453 8 : if (!poArray)
9454 : {
9455 1 : CPLError(CE_Failure, CPLE_AppDefined,
9456 : "Array %s cannot be found",
9457 : osBandArrayFullname.c_str());
9458 1 : return nullptr;
9459 : }
9460 7 : if (poArray->GetDimensionCount() != 1)
9461 : {
9462 1 : CPLError(CE_Failure, CPLE_AppDefined,
9463 : "Array %s is not a 1D array",
9464 : osBandArrayFullname.c_str());
9465 1 : return nullptr;
9466 : }
9467 : const auto &osAuxArrayDimName =
9468 6 : poArray->GetDimensions()[0]->GetName();
9469 : auto oIter =
9470 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9471 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9472 : {
9473 1 : CPLError(CE_Failure, CPLE_AppDefined,
9474 : "Dimension \"%s\" of array \"%s\" is not a "
9475 : "non-X/Y dimension of array \"%s\"",
9476 : osAuxArrayDimName.c_str(),
9477 : osBandArrayFullname.c_str(),
9478 1 : array->GetName().c_str());
9479 1 : return nullptr;
9480 : }
9481 5 : const size_t iExtraDimIdx = oIter->second;
9482 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9483 :
9484 10 : std::string osUnit = oJsonItem.GetString("unit", "um");
9485 5 : if (STARTS_WITH(osUnit.c_str(), "${"))
9486 : {
9487 3 : if (osUnit.back() != '}')
9488 : {
9489 2 : CPLError(CE_Failure, CPLE_AppDefined,
9490 : "Value of "
9491 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9492 : "%s is invalid",
9493 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9494 2 : return nullptr;
9495 : }
9496 2 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9497 2 : auto poAttr = poArray->GetAttribute(osAttrName);
9498 2 : if (!poAttr)
9499 : {
9500 2 : CPLError(CE_Failure, CPLE_AppDefined,
9501 : "Value of "
9502 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9503 : "%s is invalid: %s is not an attribute of %s",
9504 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9505 : osAttrName.c_str(),
9506 : osBandArrayFullname.c_str());
9507 1 : return nullptr;
9508 : }
9509 1 : const char *pszValue = poAttr->ReadAsString();
9510 1 : if (!pszValue)
9511 : {
9512 0 : CPLError(CE_Failure, CPLE_AppDefined,
9513 : "Cannot get value of attribute %s of %s as a "
9514 : "string",
9515 : osAttrName.c_str(),
9516 : osBandArrayFullname.c_str());
9517 0 : return nullptr;
9518 : }
9519 1 : osUnit = pszValue;
9520 : }
9521 3 : double dfConvToUM = 1.0;
9522 7 : if (osUnit == "nm" || osUnit == "nanometre" ||
9523 9 : osUnit == "nanometres" || osUnit == "nanometer" ||
9524 2 : osUnit == "nanometers")
9525 : {
9526 1 : dfConvToUM = 1e-3;
9527 : }
9528 3 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9529 1 : osUnit == "micrometres" || osUnit == "micrometer" ||
9530 1 : osUnit == "micrometers"))
9531 : {
9532 2 : CPLError(CE_Failure, CPLE_AppDefined,
9533 : "Unhandled value for "
9534 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9535 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9536 1 : return nullptr;
9537 : }
9538 :
9539 2 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9540 2 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9541 : {
9542 1 : item.poCentralWavelengthArray = std::move(poArray);
9543 1 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9544 : }
9545 : else
9546 : {
9547 1 : item.poFWHMArray = std::move(poArray);
9548 1 : item.dfFWHMToMicrometer = dfConvToUM;
9549 : }
9550 : }
9551 : else
9552 : {
9553 1 : CPLError(CE_Warning, CPLE_AppDefined,
9554 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9555 2 : oJsonItem.GetName().c_str());
9556 : }
9557 : }
9558 : }
9559 :
9560 382 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9561 :
9562 191 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9563 :
9564 191 : poDS->nRasterYSize =
9565 191 : nDimCount < 2 ? 1
9566 180 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9567 180 : dims[iYDim]->GetSize()));
9568 382 : poDS->nRasterXSize = static_cast<int>(
9569 191 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9570 :
9571 382 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9572 382 : std::vector<GUInt64> anStackIters(nDimCount);
9573 382 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9574 603 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9575 : {
9576 412 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9577 : {
9578 41 : anMapNewToOld[j] = i;
9579 41 : j++;
9580 : }
9581 : }
9582 :
9583 382 : poDS->m_bHasGT =
9584 191 : array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9585 :
9586 382 : const auto attrs(array->GetAttributes());
9587 270 : for (const auto &attr : attrs)
9588 : {
9589 79 : if (attr->GetName() != "COLOR_INTERPRETATION")
9590 : {
9591 144 : auto stringArray = attr->ReadAsStringArray();
9592 144 : std::string val;
9593 72 : if (stringArray.size() > 1)
9594 : {
9595 22 : val += '{';
9596 : }
9597 166 : for (int i = 0; i < stringArray.size(); ++i)
9598 : {
9599 94 : if (i > 0)
9600 22 : val += ',';
9601 94 : val += stringArray[i];
9602 : }
9603 72 : if (stringArray.size() > 1)
9604 : {
9605 22 : val += '}';
9606 : }
9607 72 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9608 : }
9609 : }
9610 :
9611 191 : const char *pszDelay = CSLFetchNameValueDef(
9612 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9613 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9614 : const double dfDelay =
9615 191 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9616 191 : const auto nStartTime = time(nullptr);
9617 191 : bool bHasWarned = false;
9618 : // Instantiate bands by iterating over non-XY variables
9619 191 : size_t iDim = 0;
9620 191 : int nCurBand = 1;
9621 296 : lbl_next_depth:
9622 296 : if (iDim < nNewDimCount)
9623 : {
9624 43 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9625 43 : anOtherDimCoord[iDim] = 0;
9626 : while (true)
9627 : {
9628 105 : ++iDim;
9629 105 : goto lbl_next_depth;
9630 105 : lbl_return_to_caller:
9631 105 : --iDim;
9632 105 : --anStackIters[iDim];
9633 105 : if (anStackIters[iDim] == 0)
9634 43 : break;
9635 62 : ++anOtherDimCoord[iDim];
9636 : }
9637 : }
9638 : else
9639 : {
9640 506 : poDS->SetBand(nCurBand,
9641 : new GDALRasterBandFromArray(
9642 253 : poDS.get(), anOtherDimCoord,
9643 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9644 253 : dfDelay, nStartTime, bHasWarned));
9645 253 : ++nCurBand;
9646 : }
9647 296 : if (iDim > 0)
9648 105 : goto lbl_return_to_caller;
9649 :
9650 191 : if (!array->GetFilename().empty())
9651 : {
9652 170 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9653 : std::string osDerivedDatasetName(
9654 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9655 340 : int(iYDim), array->GetFullName().c_str()));
9656 170 : if (!array->GetContext().empty())
9657 : {
9658 2 : osDerivedDatasetName += " with context ";
9659 2 : osDerivedDatasetName += array->GetContext();
9660 : }
9661 170 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9662 170 : poDS->TryLoadXML();
9663 :
9664 2 : for (const auto &[pszKey, pszValue] :
9665 : cpl::IterateNameValue(static_cast<CSLConstList>(
9666 172 : poDS->GDALPamDataset::GetMetadata())))
9667 : {
9668 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9669 : }
9670 : }
9671 :
9672 191 : return poDS.release();
9673 : }
9674 :
9675 : /************************************************************************/
9676 : /* AsClassicDataset() */
9677 : /************************************************************************/
9678 :
9679 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9680 : *
9681 : * In the case of > 2D arrays, additional dimensions will be represented as
9682 : * raster bands.
9683 : *
9684 : * The "reverse" method is GDALRasterBand::AsMDArray().
9685 : *
9686 : * This is the same as the C function GDALMDArrayAsClassicDataset().
9687 : *
9688 : * @param iXDim Index of the dimension that will be used as the X/width axis.
9689 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
9690 : * Ignored if the dimension count is 1.
9691 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9692 : * and BAND_IMAGERY_METADATA option.
9693 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9694 : * nullptr. Current supported options are:
9695 : * <ul>
9696 : * <li>BAND_METADATA: JSON serialized array defining which
9697 : * arrays of the poRootGroup, indexed by non-X and Y
9698 : * dimensions, should be mapped as band metadata items.
9699 : * Each array item should be an object with the
9700 : * following members:
9701 : * - "array": full name of a band parameter array.
9702 : * Such array must be a one
9703 : * dimensional array, and its dimension must be one of
9704 : * the dimensions of the array on which the method is
9705 : * called (excluding the X and Y dimensons).
9706 : * - "item_name": band metadata item name
9707 : * - "item_value": (optional) String, where "%[x][.y]f",
9708 : * "%[x][.y]g" or "%s" printf-like formatting can be
9709 : * used to format the corresponding value of the
9710 : * parameter array. The percentage character should be
9711 : * repeated: "%%"
9712 : * "${attribute_name}" can also be used to include the
9713 : * value of an attribute for the array.
9714 : * If "item_value" is not provided, a default formatting
9715 : * of the value will be applied.
9716 : *
9717 : * Example:
9718 : * [
9719 : * {
9720 : * "array": "/sensor_band_parameters/wavelengths",
9721 : * "item_name": "WAVELENGTH",
9722 : * "item_value": "%.1f ${units}"
9723 : * },
9724 : * {
9725 : * "array": "/sensor_band_parameters/fwhm",
9726 : * "item_name": "FWHM"
9727 : * },
9728 : * {
9729 : * "array": "/sensor_band_parameters/fwhm",
9730 : * "item_name": "FWHM_UNIT",
9731 : * "item_value": "${units}"
9732 : * }
9733 : * ]
9734 : * </li>
9735 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
9736 : * JSON serialized object defining which arrays of the
9737 : * poRootGroup, indexed by non-X and Y dimensions,
9738 : * should be mapped as band metadata items in the
9739 : * band IMAGERY domain.
9740 : * The object currently accepts 2 members:
9741 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
9742 : * micrometers.
9743 : * - "FWHM_UM": Full-width half-maximum
9744 : * in micrometers.
9745 : * The value of each member should be an object with the
9746 : * following members:
9747 : * - "array": (required) full name of a band parameter
9748 : * array.
9749 : * Such array must be a one dimensional array, and its
9750 : * dimension must be one of the dimensions of the
9751 : * array on which the method is called
9752 : * (excluding the X and Y dimensons).
9753 : * - "unit": (optional) unit of the values pointed in
9754 : * the above array.
9755 : * Can be a literal string or a string of the form
9756 : * "${attribute_name}" to point to an attribute for
9757 : * the array.
9758 : * Accepted values are "um", "micrometer"
9759 : * (with UK vs US spelling, singular or plural), "nm",
9760 : * "nanometer" (with UK vs US spelling, singular or
9761 : * plural)
9762 : * If not provided, micrometer is assumed.
9763 : *
9764 : * Example for EMIT datasets:
9765 : * {
9766 : * "CENTRAL_WAVELENGTH_UM": {
9767 : * "array": "/sensor_band_parameters/wavelengths",
9768 : * "unit": "${units}"
9769 : * },
9770 : * "FWHM_UM": {
9771 : * "array": "/sensor_band_parameters/fwhm",
9772 : * "unit": "${units}"
9773 : * }
9774 : * }
9775 : * </li>
9776 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
9777 : * seconds allowed to set the DIM_{dimname}_VALUE band
9778 : * metadata items from the indexing variable of the
9779 : * dimensions.
9780 : * Default value is 5. 'unlimited' can be used to mean
9781 : * unlimited delay. Can also be defined globally with
9782 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
9783 : * option.</li>
9784 : * </ul>
9785 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9786 : */
9787 : GDALDataset *
9788 224 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
9789 : const std::shared_ptr<GDALGroup> &poRootGroup,
9790 : CSLConstList papszOptions) const
9791 : {
9792 448 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
9793 224 : if (!self)
9794 : {
9795 0 : CPLError(CE_Failure, CPLE_AppDefined,
9796 : "Driver implementation issue: m_pSelf not set !");
9797 0 : return nullptr;
9798 : }
9799 224 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
9800 224 : papszOptions);
9801 : }
9802 :
9803 : /************************************************************************/
9804 : /* GetStatistics() */
9805 : /************************************************************************/
9806 :
9807 : /**
9808 : * \brief Fetch statistics.
9809 : *
9810 : * Returns the minimum, maximum, mean and standard deviation of all
9811 : * pixel values in this array.
9812 : *
9813 : * If bForce is FALSE results will only be returned if it can be done
9814 : * quickly (i.e. without scanning the data). If bForce is FALSE and
9815 : * results cannot be returned efficiently, the method will return CE_Warning
9816 : * but no warning will have been issued. This is a non-standard use of
9817 : * the CE_Warning return value to indicate "nothing done".
9818 : *
9819 : * When cached statistics are not available, and bForce is TRUE,
9820 : * ComputeStatistics() is called.
9821 : *
9822 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
9823 : * will generally cache statistics in the .aux.xml file allowing fast fetch
9824 : * after the first request.
9825 : *
9826 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9827 : *
9828 : * This method is the same as the C function GDALMDArrayGetStatistics().
9829 : *
9830 : * @param bApproxOK Currently ignored. In the future, should be set to true
9831 : * if statistics on the whole array are wished, or to false if a subset of it
9832 : * may be used.
9833 : *
9834 : * @param bForce If false statistics will only be returned if it can
9835 : * be done without rescanning the image.
9836 : *
9837 : * @param pdfMin Location into which to load image minimum (may be NULL).
9838 : *
9839 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9840 : *
9841 : * @param pdfMean Location into which to load image mean (may be NULL).
9842 : *
9843 : * @param pdfStdDev Location into which to load image standard deviation
9844 : * (may be NULL).
9845 : *
9846 : * @param pnValidCount Number of samples whose value is different from the
9847 : * nodata value. (may be NULL)
9848 : *
9849 : * @param pfnProgress a function to call to report progress, or NULL.
9850 : *
9851 : * @param pProgressData application data to pass to the progress function.
9852 : *
9853 : * @return CE_None on success, CE_Warning if no values returned,
9854 : * CE_Failure if an error occurs.
9855 : *
9856 : * @since GDAL 3.2
9857 : */
9858 :
9859 7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
9860 : double *pdfMax, double *pdfMean,
9861 : double *pdfStdDev, GUInt64 *pnValidCount,
9862 : GDALProgressFunc pfnProgress,
9863 : void *pProgressData)
9864 : {
9865 7 : if (!bForce)
9866 1 : return CE_Warning;
9867 :
9868 12 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
9869 6 : pnValidCount, pfnProgress, pProgressData, nullptr)
9870 6 : ? CE_None
9871 6 : : CE_Failure;
9872 : }
9873 :
9874 : /************************************************************************/
9875 : /* ComputeStatistics() */
9876 : /************************************************************************/
9877 :
9878 : /**
9879 : * \brief Compute statistics.
9880 : *
9881 : * Returns the minimum, maximum, mean and standard deviation of all
9882 : * pixel values in this array.
9883 : *
9884 : * Pixels taken into account in statistics are those whose mask value
9885 : * (as determined by GetMask()) is non-zero.
9886 : *
9887 : * Once computed, the statistics will generally be "set" back on the
9888 : * owing dataset.
9889 : *
9890 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9891 : *
9892 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
9893 : * and GDALMDArrayComputeStatisticsEx().
9894 : *
9895 : * @param bApproxOK Currently ignored. In the future, should be set to true
9896 : * if statistics on the whole array are wished, or to false if a subset of it
9897 : * may be used.
9898 : *
9899 : * @param pdfMin Location into which to load image minimum (may be NULL).
9900 : *
9901 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9902 : *
9903 : * @param pdfMean Location into which to load image mean (may be NULL).
9904 : *
9905 : * @param pdfStdDev Location into which to load image standard deviation
9906 : * (may be NULL).
9907 : *
9908 : * @param pnValidCount Number of samples whose value is different from the
9909 : * nodata value. (may be NULL)
9910 : *
9911 : * @param pfnProgress a function to call to report progress, or NULL.
9912 : *
9913 : * @param pProgressData application data to pass to the progress function.
9914 : *
9915 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
9916 : * Options are driver specific. For now the netCDF and Zarr
9917 : * drivers recognize UPDATE_METADATA=YES, whose effect is
9918 : * to add or update the actual_range attribute with the
9919 : * computed min/max, only if done on the full array, in non
9920 : * approximate mode, and the dataset is opened in update
9921 : * mode.
9922 : *
9923 : * @return true on success
9924 : *
9925 : * @since GDAL 3.2
9926 : */
9927 :
9928 10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
9929 : double *pdfMax, double *pdfMean,
9930 : double *pdfStdDev, GUInt64 *pnValidCount,
9931 : GDALProgressFunc pfnProgress,
9932 : void *pProgressData,
9933 : CSLConstList papszOptions)
9934 : {
9935 : struct StatsPerChunkType
9936 : {
9937 : const GDALMDArray *array = nullptr;
9938 : std::shared_ptr<GDALMDArray> poMask{};
9939 : double dfMin = cpl::NumericLimits<double>::max();
9940 : double dfMax = -cpl::NumericLimits<double>::max();
9941 : double dfMean = 0.0;
9942 : double dfM2 = 0.0;
9943 : GUInt64 nValidCount = 0;
9944 : std::vector<GByte> abyData{};
9945 : std::vector<double> adfData{};
9946 : std::vector<GByte> abyMaskData{};
9947 : GDALProgressFunc pfnProgress = nullptr;
9948 : void *pProgressData = nullptr;
9949 : };
9950 :
9951 10 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
9952 : const GUInt64 *chunkArrayStartIdx,
9953 : const size_t *chunkCount, GUInt64 iCurChunk,
9954 : GUInt64 nChunkCount, void *pUserData)
9955 : {
9956 10 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
9957 10 : const GDALMDArray *array = data->array;
9958 10 : const GDALMDArray *poMask = data->poMask.get();
9959 10 : const size_t nDims = array->GetDimensionCount();
9960 10 : size_t nVals = 1;
9961 27 : for (size_t i = 0; i < nDims; i++)
9962 17 : nVals *= chunkCount[i];
9963 :
9964 : // Get mask
9965 10 : data->abyMaskData.resize(nVals);
9966 10 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9967 10 : poMask->GetDataType(), &data->abyMaskData[0])))
9968 : {
9969 0 : return false;
9970 : }
9971 :
9972 : // Get data
9973 10 : const auto &oType = array->GetDataType();
9974 10 : if (oType.GetNumericDataType() == GDT_Float64)
9975 : {
9976 4 : data->adfData.resize(nVals);
9977 4 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9978 4 : oType, &data->adfData[0]))
9979 : {
9980 0 : return false;
9981 : }
9982 : }
9983 : else
9984 : {
9985 6 : data->abyData.resize(nVals * oType.GetSize());
9986 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9987 6 : oType, &data->abyData[0]))
9988 : {
9989 0 : return false;
9990 : }
9991 6 : data->adfData.resize(nVals);
9992 6 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
9993 6 : static_cast<int>(oType.GetSize()),
9994 6 : &data->adfData[0], GDT_Float64,
9995 : static_cast<int>(sizeof(double)),
9996 : static_cast<GPtrDiff_t>(nVals));
9997 : }
9998 469 : for (size_t i = 0; i < nVals; i++)
9999 : {
10000 459 : if (data->abyMaskData[i])
10001 : {
10002 454 : const double dfValue = data->adfData[i];
10003 454 : data->dfMin = std::min(data->dfMin, dfValue);
10004 454 : data->dfMax = std::max(data->dfMax, dfValue);
10005 454 : data->nValidCount++;
10006 454 : const double dfDelta = dfValue - data->dfMean;
10007 454 : data->dfMean += dfDelta / data->nValidCount;
10008 454 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10009 : }
10010 : }
10011 10 : if (data->pfnProgress &&
10012 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10013 : "", data->pProgressData))
10014 : {
10015 0 : return false;
10016 : }
10017 10 : return true;
10018 : };
10019 :
10020 10 : const auto &oType = GetDataType();
10021 20 : if (oType.GetClass() != GEDTC_NUMERIC ||
10022 10 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10023 : {
10024 0 : CPLError(
10025 : CE_Failure, CPLE_NotSupported,
10026 : "Statistics can only be computed on non-complex numeric data type");
10027 0 : return false;
10028 : }
10029 :
10030 10 : const size_t nDims = GetDimensionCount();
10031 20 : std::vector<GUInt64> arrayStartIdx(nDims);
10032 20 : std::vector<GUInt64> count(nDims);
10033 10 : const auto &poDims = GetDimensions();
10034 27 : for (size_t i = 0; i < nDims; i++)
10035 : {
10036 17 : count[i] = poDims[i]->GetSize();
10037 : }
10038 10 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10039 : const size_t nMaxChunkSize =
10040 : pszSwathSize
10041 10 : ? static_cast<size_t>(
10042 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10043 0 : CPLAtoGIntBig(pszSwathSize)))
10044 : : static_cast<size_t>(
10045 10 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10046 10 : GDALGetCacheMax64() / 4));
10047 20 : StatsPerChunkType sData;
10048 10 : sData.array = this;
10049 10 : sData.poMask = GetMask(nullptr);
10050 10 : if (sData.poMask == nullptr)
10051 : {
10052 0 : return false;
10053 : }
10054 10 : sData.pfnProgress = pfnProgress;
10055 10 : sData.pProgressData = pProgressData;
10056 10 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10057 20 : GetProcessingChunkSize(nMaxChunkSize).data(),
10058 10 : PerChunkFunc, &sData))
10059 : {
10060 0 : return false;
10061 : }
10062 :
10063 10 : if (pdfMin)
10064 10 : *pdfMin = sData.dfMin;
10065 :
10066 10 : if (pdfMax)
10067 10 : *pdfMax = sData.dfMax;
10068 :
10069 10 : if (pdfMean)
10070 8 : *pdfMean = sData.dfMean;
10071 :
10072 : const double dfStdDev =
10073 10 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10074 10 : if (pdfStdDev)
10075 8 : *pdfStdDev = dfStdDev;
10076 :
10077 10 : if (pnValidCount)
10078 8 : *pnValidCount = sData.nValidCount;
10079 :
10080 10 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10081 10 : sData.nValidCount, papszOptions);
10082 :
10083 10 : return true;
10084 : }
10085 :
10086 : /************************************************************************/
10087 : /* SetStatistics() */
10088 : /************************************************************************/
10089 : //! @cond Doxygen_Suppress
10090 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10091 : double /* dfMax */, double /* dfMean */,
10092 : double /* dfStdDev */,
10093 : GUInt64 /* nValidCount */,
10094 : CSLConstList /* papszOptions */)
10095 : {
10096 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10097 5 : return false;
10098 : }
10099 :
10100 : //! @endcond
10101 :
10102 : /************************************************************************/
10103 : /* ClearStatistics() */
10104 : /************************************************************************/
10105 :
10106 : /**
10107 : * \brief Clear statistics.
10108 : *
10109 : * @since GDAL 3.4
10110 : */
10111 0 : void GDALMDArray::ClearStatistics()
10112 : {
10113 0 : }
10114 :
10115 : /************************************************************************/
10116 : /* GetCoordinateVariables() */
10117 : /************************************************************************/
10118 :
10119 : /**
10120 : * \brief Return coordinate variables.
10121 : *
10122 : * Coordinate variables are an alternate way of indexing an array that can
10123 : * be sometimes used. For example, an array collected through remote sensing
10124 : * might be indexed by (scanline, pixel). But there can be
10125 : * a longitude and latitude arrays alongside that are also both indexed by
10126 : * (scanline, pixel), and are referenced from operational arrays for
10127 : * reprojection purposes.
10128 : *
10129 : * For netCDF, this will return the arrays referenced by the "coordinates"
10130 : * attribute.
10131 : *
10132 : * This method is the same as the C function
10133 : * GDALMDArrayGetCoordinateVariables().
10134 : *
10135 : * @return a vector of arrays
10136 : *
10137 : * @since GDAL 3.4
10138 : */
10139 :
10140 : std::vector<std::shared_ptr<GDALMDArray>>
10141 13 : GDALMDArray::GetCoordinateVariables() const
10142 : {
10143 13 : return {};
10144 : }
10145 :
10146 : /************************************************************************/
10147 : /* ~GDALExtendedDataType() */
10148 : /************************************************************************/
10149 :
10150 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10151 :
10152 : /************************************************************************/
10153 : /* GDALExtendedDataType() */
10154 : /************************************************************************/
10155 :
10156 8328 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10157 8328 : GDALExtendedDataTypeSubType eSubType)
10158 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10159 8328 : m_nMaxStringLength(nMaxStringLength)
10160 : {
10161 8328 : }
10162 :
10163 : /************************************************************************/
10164 : /* GDALExtendedDataType() */
10165 : /************************************************************************/
10166 :
10167 35854 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10168 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10169 35854 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10170 : {
10171 35854 : }
10172 :
10173 : /************************************************************************/
10174 : /* GDALExtendedDataType() */
10175 : /************************************************************************/
10176 :
10177 632 : GDALExtendedDataType::GDALExtendedDataType(
10178 : const std::string &osName, size_t nTotalSize,
10179 632 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10180 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10181 632 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10182 : {
10183 632 : }
10184 :
10185 : /************************************************************************/
10186 : /* GDALExtendedDataType() */
10187 : /************************************************************************/
10188 :
10189 : /** Copy constructor. */
10190 15843 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10191 31686 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10192 15843 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10193 15843 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
10194 : {
10195 15843 : if (m_eClass == GEDTC_COMPOUND)
10196 : {
10197 431 : for (const auto &elt : other.m_aoComponents)
10198 : {
10199 281 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10200 : }
10201 : }
10202 15843 : }
10203 :
10204 : /************************************************************************/
10205 : /* operator= () */
10206 : /************************************************************************/
10207 :
10208 : /** Copy assignment. */
10209 : GDALExtendedDataType &
10210 1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10211 : {
10212 1045 : if (this != &other)
10213 : {
10214 1045 : m_osName = other.m_osName;
10215 1045 : m_eClass = other.m_eClass;
10216 1045 : m_eSubType = other.m_eSubType;
10217 1045 : m_eNumericDT = other.m_eNumericDT;
10218 1045 : m_nSize = other.m_nSize;
10219 1045 : m_nMaxStringLength = other.m_nMaxStringLength;
10220 1045 : m_aoComponents.clear();
10221 1045 : if (m_eClass == GEDTC_COMPOUND)
10222 : {
10223 0 : for (const auto &elt : other.m_aoComponents)
10224 : {
10225 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10226 : }
10227 : }
10228 : }
10229 1045 : return *this;
10230 : }
10231 :
10232 : /************************************************************************/
10233 : /* operator= () */
10234 : /************************************************************************/
10235 :
10236 : /** Move assignment. */
10237 : GDALExtendedDataType &
10238 14731 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10239 : {
10240 14731 : m_osName = std::move(other.m_osName);
10241 14731 : m_eClass = other.m_eClass;
10242 14731 : m_eSubType = other.m_eSubType;
10243 14731 : m_eNumericDT = other.m_eNumericDT;
10244 14731 : m_nSize = other.m_nSize;
10245 14731 : m_nMaxStringLength = other.m_nMaxStringLength;
10246 14731 : m_aoComponents = std::move(other.m_aoComponents);
10247 14731 : other.m_eClass = GEDTC_NUMERIC;
10248 14731 : other.m_eNumericDT = GDT_Unknown;
10249 14731 : other.m_nSize = 0;
10250 14731 : other.m_nMaxStringLength = 0;
10251 14731 : return *this;
10252 : }
10253 :
10254 : /************************************************************************/
10255 : /* Create() */
10256 : /************************************************************************/
10257 :
10258 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10259 : *
10260 : * This is the same as the C function GDALExtendedDataTypeCreate()
10261 : *
10262 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10263 : * GDT_TypeCount
10264 : */
10265 35847 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10266 : {
10267 35847 : return GDALExtendedDataType(eType);
10268 : }
10269 :
10270 : /************************************************************************/
10271 : /* Create() */
10272 : /************************************************************************/
10273 :
10274 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10275 : *
10276 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10277 : *
10278 : * @param osName Type name.
10279 : * @param nTotalSize Total size of the type in bytes.
10280 : * Should be large enough to store all components.
10281 : * @param components Components of the compound type.
10282 : */
10283 639 : GDALExtendedDataType GDALExtendedDataType::Create(
10284 : const std::string &osName, size_t nTotalSize,
10285 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10286 : {
10287 639 : size_t nLastOffset = 0;
10288 : // Some arbitrary threshold to avoid potential integer overflows
10289 639 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10290 : {
10291 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10292 2 : return GDALExtendedDataType(GDT_Unknown);
10293 : }
10294 3155 : for (const auto &comp : components)
10295 : {
10296 : // Check alignment too ?
10297 2519 : if (comp->GetOffset() < nLastOffset)
10298 : {
10299 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10300 1 : return GDALExtendedDataType(GDT_Unknown);
10301 : }
10302 2518 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10303 : }
10304 636 : if (nTotalSize < nLastOffset)
10305 : {
10306 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10307 1 : return GDALExtendedDataType(GDT_Unknown);
10308 : }
10309 635 : if (nTotalSize == 0 || components.empty())
10310 : {
10311 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10312 3 : return GDALExtendedDataType(GDT_Unknown);
10313 : }
10314 632 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10315 : }
10316 :
10317 : /************************************************************************/
10318 : /* Create() */
10319 : /************************************************************************/
10320 :
10321 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10322 : *
10323 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10324 : *
10325 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10326 : * unknown/unlimited
10327 : * @param eSubType Subtype.
10328 : */
10329 : GDALExtendedDataType
10330 8328 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10331 : GDALExtendedDataTypeSubType eSubType)
10332 : {
10333 8328 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10334 : }
10335 :
10336 : /************************************************************************/
10337 : /* operator==() */
10338 : /************************************************************************/
10339 :
10340 : /** Equality operator.
10341 : *
10342 : * This is the same as the C function GDALExtendedDataTypeEquals().
10343 : */
10344 2242 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10345 : {
10346 2215 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10347 4457 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10348 : {
10349 177 : return false;
10350 : }
10351 2065 : if (m_eClass == GEDTC_NUMERIC)
10352 : {
10353 868 : return m_eNumericDT == other.m_eNumericDT;
10354 : }
10355 1197 : if (m_eClass == GEDTC_STRING)
10356 : {
10357 1016 : return true;
10358 : }
10359 181 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10360 181 : if (m_aoComponents.size() != other.m_aoComponents.size())
10361 : {
10362 2 : return false;
10363 : }
10364 806 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10365 : {
10366 627 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10367 : {
10368 0 : return false;
10369 : }
10370 : }
10371 179 : return true;
10372 : }
10373 :
10374 : /************************************************************************/
10375 : /* CanConvertTo() */
10376 : /************************************************************************/
10377 :
10378 : /** Return whether this data type can be converted to the other one.
10379 : *
10380 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10381 : *
10382 : * @param other Target data type for the conversion being considered.
10383 : */
10384 8161 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10385 : {
10386 8161 : if (m_eClass == GEDTC_NUMERIC)
10387 : {
10388 5802 : if (m_eNumericDT == GDT_Unknown)
10389 0 : return false;
10390 5802 : if (other.m_eClass == GEDTC_NUMERIC &&
10391 5715 : other.m_eNumericDT == GDT_Unknown)
10392 0 : return false;
10393 5889 : return other.m_eClass == GEDTC_NUMERIC ||
10394 5889 : other.m_eClass == GEDTC_STRING;
10395 : }
10396 2359 : if (m_eClass == GEDTC_STRING)
10397 : {
10398 2220 : return other.m_eClass == m_eClass;
10399 : }
10400 139 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10401 139 : if (other.m_eClass != GEDTC_COMPOUND)
10402 0 : return false;
10403 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10404 278 : srcComponents;
10405 568 : for (const auto &srcComp : m_aoComponents)
10406 : {
10407 429 : srcComponents[srcComp->GetName()] = &srcComp;
10408 : }
10409 419 : for (const auto &dstComp : other.m_aoComponents)
10410 : {
10411 281 : auto oIter = srcComponents.find(dstComp->GetName());
10412 281 : if (oIter == srcComponents.end())
10413 1 : return false;
10414 280 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10415 0 : return false;
10416 : }
10417 138 : return true;
10418 : }
10419 :
10420 : /************************************************************************/
10421 : /* NeedsFreeDynamicMemory() */
10422 : /************************************************************************/
10423 :
10424 : /** Return whether the data type holds dynamically allocated memory, that
10425 : * needs to be freed with FreeDynamicMemory().
10426 : *
10427 : */
10428 3539 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10429 : {
10430 3539 : switch (m_eClass)
10431 : {
10432 852 : case GEDTC_STRING:
10433 852 : return true;
10434 :
10435 2614 : case GEDTC_NUMERIC:
10436 2614 : return false;
10437 :
10438 73 : case GEDTC_COMPOUND:
10439 : {
10440 186 : for (const auto &comp : m_aoComponents)
10441 : {
10442 172 : if (comp->GetType().NeedsFreeDynamicMemory())
10443 59 : return true;
10444 : }
10445 : }
10446 : }
10447 14 : return false;
10448 : }
10449 :
10450 : /************************************************************************/
10451 : /* FreeDynamicMemory() */
10452 : /************************************************************************/
10453 :
10454 : /** Release the dynamic memory (strings typically) from a raw value.
10455 : *
10456 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10457 : *
10458 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10459 : */
10460 3298 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10461 : {
10462 3298 : switch (m_eClass)
10463 : {
10464 2306 : case GEDTC_STRING:
10465 : {
10466 : char *pszStr;
10467 2306 : memcpy(&pszStr, pBuffer, sizeof(char *));
10468 2306 : if (pszStr)
10469 : {
10470 1795 : VSIFree(pszStr);
10471 : }
10472 2306 : break;
10473 : }
10474 :
10475 853 : case GEDTC_NUMERIC:
10476 : {
10477 853 : break;
10478 : }
10479 :
10480 139 : case GEDTC_COMPOUND:
10481 : {
10482 139 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10483 605 : for (const auto &comp : m_aoComponents)
10484 : {
10485 932 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10486 466 : comp->GetOffset());
10487 : }
10488 139 : break;
10489 : }
10490 : }
10491 3298 : }
10492 :
10493 : /************************************************************************/
10494 : /* ~GDALEDTComponent() */
10495 : /************************************************************************/
10496 :
10497 : GDALEDTComponent::~GDALEDTComponent() = default;
10498 :
10499 : /************************************************************************/
10500 : /* GDALEDTComponent() */
10501 : /************************************************************************/
10502 :
10503 : /** constructor of a GDALEDTComponent
10504 : *
10505 : * This is the same as the C function GDALEDTComponendCreate()
10506 : *
10507 : * @param name Component name
10508 : * @param offset Offset in byte of the component in the compound data type.
10509 : * In case of nesting of compound data type, this should be
10510 : * the offset to the immediate belonging data type, not to the
10511 : * higher level one.
10512 : * @param type Component data type.
10513 : */
10514 2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10515 2510 : const GDALExtendedDataType &type)
10516 2510 : : m_osName(name), m_nOffset(offset), m_oType(type)
10517 : {
10518 2510 : }
10519 :
10520 : /************************************************************************/
10521 : /* GDALEDTComponent() */
10522 : /************************************************************************/
10523 :
10524 : /** Copy constructor. */
10525 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10526 :
10527 : /************************************************************************/
10528 : /* operator==() */
10529 : /************************************************************************/
10530 :
10531 : /** Equality operator.
10532 : */
10533 627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10534 : {
10535 1254 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10536 1254 : m_oType == other.m_oType;
10537 : }
10538 :
10539 : /************************************************************************/
10540 : /* ~GDALDimension() */
10541 : /************************************************************************/
10542 :
10543 : GDALDimension::~GDALDimension() = default;
10544 :
10545 : /************************************************************************/
10546 : /* GDALDimension() */
10547 : /************************************************************************/
10548 :
10549 : //! @cond Doxygen_Suppress
10550 : /** Constructor.
10551 : *
10552 : * @param osParentName Parent name
10553 : * @param osName name
10554 : * @param osType type. See GetType().
10555 : * @param osDirection direction. See GetDirection().
10556 : * @param nSize size.
10557 : */
10558 8175 : GDALDimension::GDALDimension(const std::string &osParentName,
10559 : const std::string &osName,
10560 : const std::string &osType,
10561 8175 : const std::string &osDirection, GUInt64 nSize)
10562 : : m_osName(osName),
10563 : m_osFullName(
10564 8175 : !osParentName.empty()
10565 12084 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10566 : : osName),
10567 28434 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10568 : {
10569 8175 : }
10570 :
10571 : //! @endcond
10572 :
10573 : /************************************************************************/
10574 : /* GetIndexingVariable() */
10575 : /************************************************************************/
10576 :
10577 : /** Return the variable that is used to index the dimension (if there is one).
10578 : *
10579 : * This is the array, typically one-dimensional, describing the values taken
10580 : * by the dimension.
10581 : */
10582 49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10583 : {
10584 49 : return nullptr;
10585 : }
10586 :
10587 : /************************************************************************/
10588 : /* SetIndexingVariable() */
10589 : /************************************************************************/
10590 :
10591 : /** Set the variable that is used to index the dimension.
10592 : *
10593 : * This is the array, typically one-dimensional, describing the values taken
10594 : * by the dimension.
10595 : *
10596 : * Optionally implemented by drivers.
10597 : *
10598 : * Drivers known to implement it: MEM.
10599 : *
10600 : * @param poArray Variable to use to index the dimension.
10601 : * @return true in case of success.
10602 : */
10603 3 : bool GDALDimension::SetIndexingVariable(
10604 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10605 : {
10606 3 : CPLError(CE_Failure, CPLE_NotSupported,
10607 : "SetIndexingVariable() not implemented");
10608 3 : return false;
10609 : }
10610 :
10611 : /************************************************************************/
10612 : /* Rename() */
10613 : /************************************************************************/
10614 :
10615 : /** Rename the dimension.
10616 : *
10617 : * This is not implemented by all drivers.
10618 : *
10619 : * Drivers known to implement it: MEM, netCDF, ZARR.
10620 : *
10621 : * This is the same as the C function GDALDimensionRename().
10622 : *
10623 : * @param osNewName New name.
10624 : *
10625 : * @return true in case of success
10626 : * @since GDAL 3.8
10627 : */
10628 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10629 : {
10630 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10631 0 : return false;
10632 : }
10633 :
10634 : /************************************************************************/
10635 : /* BaseRename() */
10636 : /************************************************************************/
10637 :
10638 : //! @cond Doxygen_Suppress
10639 8 : void GDALDimension::BaseRename(const std::string &osNewName)
10640 : {
10641 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
10642 8 : m_osFullName += osNewName;
10643 8 : m_osName = osNewName;
10644 8 : }
10645 :
10646 : //! @endcond
10647 :
10648 : //! @cond Doxygen_Suppress
10649 : /************************************************************************/
10650 : /* ParentRenamed() */
10651 : /************************************************************************/
10652 :
10653 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
10654 : {
10655 8 : m_osFullName = osNewParentFullName;
10656 8 : m_osFullName += "/";
10657 8 : m_osFullName += m_osName;
10658 8 : }
10659 :
10660 : //! @endcond
10661 :
10662 : //! @cond Doxygen_Suppress
10663 : /************************************************************************/
10664 : /* ParentDeleted() */
10665 : /************************************************************************/
10666 :
10667 4 : void GDALDimension::ParentDeleted()
10668 : {
10669 4 : }
10670 :
10671 : //! @endcond
10672 :
10673 : /************************************************************************/
10674 : /************************************************************************/
10675 : /************************************************************************/
10676 : /* C API */
10677 : /************************************************************************/
10678 : /************************************************************************/
10679 : /************************************************************************/
10680 :
10681 : /************************************************************************/
10682 : /* GDALExtendedDataTypeCreate() */
10683 : /************************************************************************/
10684 :
10685 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10686 : *
10687 : * This is the same as the C++ method GDALExtendedDataType::Create()
10688 : *
10689 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10690 : *
10691 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10692 : * GDT_TypeCount
10693 : *
10694 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10695 : */
10696 1992 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
10697 : {
10698 1992 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
10699 : {
10700 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10701 : "Illegal GDT_Unknown/GDT_TypeCount argument");
10702 0 : return nullptr;
10703 : }
10704 : return new GDALExtendedDataTypeHS(
10705 1992 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
10706 : }
10707 :
10708 : /************************************************************************/
10709 : /* GDALExtendedDataTypeCreateString() */
10710 : /************************************************************************/
10711 :
10712 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10713 : *
10714 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10715 : *
10716 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10717 : *
10718 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10719 : */
10720 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
10721 : {
10722 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10723 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
10724 : }
10725 :
10726 : /************************************************************************/
10727 : /* GDALExtendedDataTypeCreateStringEx() */
10728 : /************************************************************************/
10729 :
10730 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10731 : *
10732 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10733 : *
10734 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10735 : *
10736 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10737 : * @since GDAL 3.4
10738 : */
10739 : GDALExtendedDataTypeH
10740 188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
10741 : GDALExtendedDataTypeSubType eSubType)
10742 : {
10743 188 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10744 188 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
10745 : }
10746 :
10747 : /************************************************************************/
10748 : /* GDALExtendedDataTypeCreateCompound() */
10749 : /************************************************************************/
10750 :
10751 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10752 : *
10753 : * This is the same as the C++ method GDALExtendedDataType::Create(const
10754 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
10755 : *
10756 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10757 : *
10758 : * @param pszName Type name.
10759 : * @param nTotalSize Total size of the type in bytes.
10760 : * Should be large enough to store all components.
10761 : * @param nComponents Number of components in comps array.
10762 : * @param comps Components.
10763 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10764 : */
10765 : GDALExtendedDataTypeH
10766 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
10767 : size_t nComponents,
10768 : const GDALEDTComponentH *comps)
10769 : {
10770 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
10771 54 : for (size_t i = 0; i < nComponents; i++)
10772 : {
10773 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
10774 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
10775 : }
10776 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
10777 66 : std::move(compsCpp));
10778 22 : if (dt.GetClass() != GEDTC_COMPOUND)
10779 6 : return nullptr;
10780 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
10781 : }
10782 :
10783 : /************************************************************************/
10784 : /* GDALExtendedDataTypeRelease() */
10785 : /************************************************************************/
10786 :
10787 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
10788 : *
10789 : * Note: when applied on a object coming from a driver, this does not
10790 : * destroy the object in the file, database, etc...
10791 : */
10792 6541 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
10793 : {
10794 6541 : delete hEDT;
10795 6541 : }
10796 :
10797 : /************************************************************************/
10798 : /* GDALExtendedDataTypeGetName() */
10799 : /************************************************************************/
10800 :
10801 : /** Return type name.
10802 : *
10803 : * This is the same as the C++ method GDALExtendedDataType::GetName()
10804 : */
10805 7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
10806 : {
10807 7 : VALIDATE_POINTER1(hEDT, __func__, "");
10808 7 : return hEDT->m_poImpl->GetName().c_str();
10809 : }
10810 :
10811 : /************************************************************************/
10812 : /* GDALExtendedDataTypeGetClass() */
10813 : /************************************************************************/
10814 :
10815 : /** Return type class.
10816 : *
10817 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
10818 : */
10819 : GDALExtendedDataTypeClass
10820 9029 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
10821 : {
10822 9029 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
10823 9029 : return hEDT->m_poImpl->GetClass();
10824 : }
10825 :
10826 : /************************************************************************/
10827 : /* GDALExtendedDataTypeGetNumericDataType() */
10828 : /************************************************************************/
10829 :
10830 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
10831 : *
10832 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
10833 : */
10834 2010 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
10835 : {
10836 2010 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
10837 2010 : return hEDT->m_poImpl->GetNumericDataType();
10838 : }
10839 :
10840 : /************************************************************************/
10841 : /* GDALExtendedDataTypeGetSize() */
10842 : /************************************************************************/
10843 :
10844 : /** Return data type size in bytes.
10845 : *
10846 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
10847 : */
10848 2510 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
10849 : {
10850 2510 : VALIDATE_POINTER1(hEDT, __func__, 0);
10851 2510 : return hEDT->m_poImpl->GetSize();
10852 : }
10853 :
10854 : /************************************************************************/
10855 : /* GDALExtendedDataTypeGetMaxStringLength() */
10856 : /************************************************************************/
10857 :
10858 : /** Return the maximum length of a string in bytes.
10859 : *
10860 : * 0 indicates unknown/unlimited string.
10861 : *
10862 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
10863 : */
10864 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
10865 : {
10866 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
10867 3 : return hEDT->m_poImpl->GetMaxStringLength();
10868 : }
10869 :
10870 : /************************************************************************/
10871 : /* GDALExtendedDataTypeCanConvertTo() */
10872 : /************************************************************************/
10873 :
10874 : /** Return whether this data type can be converted to the other one.
10875 : *
10876 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
10877 : *
10878 : * @param hSourceEDT Source data type for the conversion being considered.
10879 : * @param hTargetEDT Target data type for the conversion being considered.
10880 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
10881 : */
10882 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
10883 : GDALExtendedDataTypeH hTargetEDT)
10884 : {
10885 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
10886 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
10887 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
10888 : }
10889 :
10890 : /************************************************************************/
10891 : /* GDALExtendedDataTypeEquals() */
10892 : /************************************************************************/
10893 :
10894 : /** Return whether this data type is equal to another one.
10895 : *
10896 : * This is the same as the C++ method GDALExtendedDataType::operator==()
10897 : *
10898 : * @param hFirstEDT First data type.
10899 : * @param hSecondEDT Second data type.
10900 : * @return TRUE if they are equal. FALSE otherwise.
10901 : */
10902 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
10903 : GDALExtendedDataTypeH hSecondEDT)
10904 : {
10905 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
10906 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
10907 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
10908 : }
10909 :
10910 : /************************************************************************/
10911 : /* GDALExtendedDataTypeGetSubType() */
10912 : /************************************************************************/
10913 :
10914 : /** Return the subtype of a type.
10915 : *
10916 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
10917 : *
10918 : * @param hEDT Data type.
10919 : * @return subtype.
10920 : * @since 3.4
10921 : */
10922 : GDALExtendedDataTypeSubType
10923 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
10924 : {
10925 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
10926 104 : return hEDT->m_poImpl->GetSubType();
10927 : }
10928 :
10929 : /************************************************************************/
10930 : /* GDALExtendedDataTypeGetComponents() */
10931 : /************************************************************************/
10932 :
10933 : /** Return the components of the data type (only valid when GetClass() ==
10934 : * GEDTC_COMPOUND)
10935 : *
10936 : * The returned array and its content must be freed with
10937 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
10938 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
10939 : * individual array members).
10940 : *
10941 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
10942 : *
10943 : * @param hEDT Data type
10944 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
10945 : * @return an array of *pnCount components.
10946 : */
10947 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
10948 : size_t *pnCount)
10949 : {
10950 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
10951 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
10952 44 : const auto &components = hEDT->m_poImpl->GetComponents();
10953 : auto ret = static_cast<GDALEDTComponentH *>(
10954 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
10955 131 : for (size_t i = 0; i < components.size(); i++)
10956 : {
10957 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
10958 : }
10959 44 : *pnCount = components.size();
10960 44 : return ret;
10961 : }
10962 :
10963 : /************************************************************************/
10964 : /* GDALExtendedDataTypeFreeComponents() */
10965 : /************************************************************************/
10966 :
10967 : /** Free the return of GDALExtendedDataTypeGetComponents().
10968 : *
10969 : * @param components return value of GDALExtendedDataTypeGetComponents()
10970 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
10971 : */
10972 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
10973 : size_t nCount)
10974 : {
10975 131 : for (size_t i = 0; i < nCount; i++)
10976 : {
10977 87 : delete components[i];
10978 : }
10979 44 : CPLFree(components);
10980 44 : }
10981 :
10982 : /************************************************************************/
10983 : /* GDALEDTComponentCreate() */
10984 : /************************************************************************/
10985 :
10986 : /** Create a new GDALEDTComponent.
10987 : *
10988 : * The returned value must be freed with GDALEDTComponentRelease().
10989 : *
10990 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
10991 : */
10992 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
10993 : GDALExtendedDataTypeH hType)
10994 : {
10995 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10996 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
10997 : return new GDALEDTComponentHS(
10998 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
10999 : }
11000 :
11001 : /************************************************************************/
11002 : /* GDALEDTComponentRelease() */
11003 : /************************************************************************/
11004 :
11005 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11006 : *
11007 : * Note: when applied on a object coming from a driver, this does not
11008 : * destroy the object in the file, database, etc...
11009 : */
11010 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11011 : {
11012 61 : delete hComp;
11013 61 : }
11014 :
11015 : /************************************************************************/
11016 : /* GDALEDTComponentGetName() */
11017 : /************************************************************************/
11018 :
11019 : /** Return the name.
11020 : *
11021 : * The returned pointer is valid until hComp is released.
11022 : *
11023 : * This is the same as the C++ method GDALEDTComponent::GetName().
11024 : */
11025 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11026 : {
11027 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11028 33 : return hComp->m_poImpl->GetName().c_str();
11029 : }
11030 :
11031 : /************************************************************************/
11032 : /* GDALEDTComponentGetOffset() */
11033 : /************************************************************************/
11034 :
11035 : /** Return the offset (in bytes) of the component in the compound data type.
11036 : *
11037 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11038 : */
11039 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11040 : {
11041 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11042 31 : return hComp->m_poImpl->GetOffset();
11043 : }
11044 :
11045 : /************************************************************************/
11046 : /* GDALEDTComponentGetType() */
11047 : /************************************************************************/
11048 :
11049 : /** Return the data type of the component.
11050 : *
11051 : * This is the same as the C++ method GDALEDTComponent::GetType().
11052 : */
11053 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11054 : {
11055 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11056 : return new GDALExtendedDataTypeHS(
11057 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11058 : }
11059 :
11060 : /************************************************************************/
11061 : /* GDALGroupRelease() */
11062 : /************************************************************************/
11063 :
11064 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11065 : *
11066 : * Note: when applied on a object coming from a driver, this does not
11067 : * destroy the object in the file, database, etc...
11068 : */
11069 1405 : void GDALGroupRelease(GDALGroupH hGroup)
11070 : {
11071 1405 : delete hGroup;
11072 1405 : }
11073 :
11074 : /************************************************************************/
11075 : /* GDALGroupGetName() */
11076 : /************************************************************************/
11077 :
11078 : /** Return the name of the group.
11079 : *
11080 : * The returned pointer is valid until hGroup is released.
11081 : *
11082 : * This is the same as the C++ method GDALGroup::GetName().
11083 : */
11084 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11085 : {
11086 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11087 95 : return hGroup->m_poImpl->GetName().c_str();
11088 : }
11089 :
11090 : /************************************************************************/
11091 : /* GDALGroupGetFullName() */
11092 : /************************************************************************/
11093 :
11094 : /** Return the full name of the group.
11095 : *
11096 : * The returned pointer is valid until hGroup is released.
11097 : *
11098 : * This is the same as the C++ method GDALGroup::GetFullName().
11099 : */
11100 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11101 : {
11102 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11103 47 : return hGroup->m_poImpl->GetFullName().c_str();
11104 : }
11105 :
11106 : /************************************************************************/
11107 : /* GDALGroupGetMDArrayNames() */
11108 : /************************************************************************/
11109 :
11110 : /** Return the list of multidimensional array names contained in this group.
11111 : *
11112 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11113 : *
11114 : * @return the array names, to be freed with CSLDestroy()
11115 : */
11116 325 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11117 : {
11118 325 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11119 650 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11120 650 : CPLStringList res;
11121 821 : for (const auto &name : names)
11122 : {
11123 496 : res.AddString(name.c_str());
11124 : }
11125 325 : return res.StealList();
11126 : }
11127 :
11128 : /************************************************************************/
11129 : /* GDALGroupOpenMDArray() */
11130 : /************************************************************************/
11131 :
11132 : /** Open and return a multidimensional array.
11133 : *
11134 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11135 : *
11136 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11137 : */
11138 777 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11139 : CSLConstList papszOptions)
11140 : {
11141 777 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11142 777 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11143 2331 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11144 2331 : papszOptions);
11145 777 : if (!array)
11146 30 : return nullptr;
11147 747 : return new GDALMDArrayHS(array);
11148 : }
11149 :
11150 : /************************************************************************/
11151 : /* GDALGroupOpenMDArrayFromFullname() */
11152 : /************************************************************************/
11153 :
11154 : /** Open and return a multidimensional array from its fully qualified name.
11155 : *
11156 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11157 : *
11158 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11159 : *
11160 : * @since GDAL 3.2
11161 : */
11162 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11163 : const char *pszFullname,
11164 : CSLConstList papszOptions)
11165 : {
11166 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11167 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11168 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11169 48 : std::string(pszFullname), papszOptions);
11170 16 : if (!array)
11171 2 : return nullptr;
11172 14 : return new GDALMDArrayHS(array);
11173 : }
11174 :
11175 : /************************************************************************/
11176 : /* GDALGroupResolveMDArray() */
11177 : /************************************************************************/
11178 :
11179 : /** Locate an array in a group and its subgroups by name.
11180 : *
11181 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11182 : * @since GDAL 3.2
11183 : */
11184 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11185 : const char *pszStartingPoint,
11186 : CSLConstList papszOptions)
11187 : {
11188 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11189 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11190 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11191 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11192 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11193 19 : if (!array)
11194 2 : return nullptr;
11195 17 : return new GDALMDArrayHS(array);
11196 : }
11197 :
11198 : /************************************************************************/
11199 : /* GDALGroupGetGroupNames() */
11200 : /************************************************************************/
11201 :
11202 : /** Return the list of sub-groups contained in this group.
11203 : *
11204 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11205 : *
11206 : * @return the group names, to be freed with CSLDestroy()
11207 : */
11208 97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11209 : {
11210 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11211 194 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11212 194 : CPLStringList res;
11213 219 : for (const auto &name : names)
11214 : {
11215 122 : res.AddString(name.c_str());
11216 : }
11217 97 : return res.StealList();
11218 : }
11219 :
11220 : /************************************************************************/
11221 : /* GDALGroupOpenGroup() */
11222 : /************************************************************************/
11223 :
11224 : /** Open and return a sub-group.
11225 : *
11226 : * This is the same as the C++ method GDALGroup::OpenGroup().
11227 : *
11228 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11229 : */
11230 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11231 : CSLConstList papszOptions)
11232 : {
11233 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11234 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11235 : auto subGroup =
11236 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11237 163 : if (!subGroup)
11238 30 : return nullptr;
11239 133 : return new GDALGroupHS(subGroup);
11240 : }
11241 :
11242 : /************************************************************************/
11243 : /* GDALGroupGetVectorLayerNames() */
11244 : /************************************************************************/
11245 :
11246 : /** Return the list of layer names contained in this group.
11247 : *
11248 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11249 : *
11250 : * @return the group names, to be freed with CSLDestroy()
11251 : * @since 3.4
11252 : */
11253 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11254 : CSLConstList papszOptions)
11255 : {
11256 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11257 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11258 16 : CPLStringList res;
11259 18 : for (const auto &name : names)
11260 : {
11261 10 : res.AddString(name.c_str());
11262 : }
11263 8 : return res.StealList();
11264 : }
11265 :
11266 : /************************************************************************/
11267 : /* GDALGroupOpenVectorLayer() */
11268 : /************************************************************************/
11269 :
11270 : /** Open and return a vector layer.
11271 : *
11272 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11273 : *
11274 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11275 : * the returned handled if only valid while the parent GDALDatasetH is kept
11276 : * opened.
11277 : *
11278 : * @return the vector layer, or nullptr.
11279 : * @since 3.4
11280 : */
11281 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11282 : const char *pszVectorLayerName,
11283 : CSLConstList papszOptions)
11284 : {
11285 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11286 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11287 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11288 24 : std::string(pszVectorLayerName), papszOptions));
11289 : }
11290 :
11291 : /************************************************************************/
11292 : /* GDALGroupOpenMDArrayFromFullname() */
11293 : /************************************************************************/
11294 :
11295 : /** Open and return a sub-group from its fully qualified name.
11296 : *
11297 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11298 : *
11299 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11300 : *
11301 : * @since GDAL 3.2
11302 : */
11303 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11304 : const char *pszFullname,
11305 : CSLConstList papszOptions)
11306 : {
11307 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11308 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11309 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11310 9 : std::string(pszFullname), papszOptions);
11311 3 : if (!subGroup)
11312 2 : return nullptr;
11313 1 : return new GDALGroupHS(subGroup);
11314 : }
11315 :
11316 : /************************************************************************/
11317 : /* GDALGroupGetDimensions() */
11318 : /************************************************************************/
11319 :
11320 : /** Return the list of dimensions contained in this group and used by its
11321 : * arrays.
11322 : *
11323 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11324 : * array itself needs to be freed, CPLFree() should be called (and
11325 : * GDALDimensionRelease() on individual array members).
11326 : *
11327 : * This is the same as the C++ method GDALGroup::GetDimensions().
11328 : *
11329 : * @param hGroup Group.
11330 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11331 : * @param papszOptions Driver specific options determining how dimensions
11332 : * should be retrieved. Pass nullptr for default behavior.
11333 : *
11334 : * @return an array of *pnCount dimensions.
11335 : */
11336 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11337 : CSLConstList papszOptions)
11338 : {
11339 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11340 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11341 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11342 : auto ret = static_cast<GDALDimensionH *>(
11343 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11344 230 : for (size_t i = 0; i < dims.size(); i++)
11345 : {
11346 157 : ret[i] = new GDALDimensionHS(dims[i]);
11347 : }
11348 73 : *pnCount = dims.size();
11349 73 : return ret;
11350 : }
11351 :
11352 : /************************************************************************/
11353 : /* GDALGroupGetAttribute() */
11354 : /************************************************************************/
11355 :
11356 : /** Return an attribute by its name.
11357 : *
11358 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11359 : *
11360 : * The returned attribute must be freed with GDALAttributeRelease().
11361 : */
11362 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11363 : {
11364 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11365 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11366 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11367 80 : if (attr)
11368 76 : return new GDALAttributeHS(attr);
11369 4 : return nullptr;
11370 : }
11371 :
11372 : /************************************************************************/
11373 : /* GDALGroupGetAttributes() */
11374 : /************************************************************************/
11375 :
11376 : /** Return the list of attributes contained in this group.
11377 : *
11378 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11379 : * array itself needs to be freed, CPLFree() should be called (and
11380 : * GDALAttributeRelease() on individual array members).
11381 : *
11382 : * This is the same as the C++ method GDALGroup::GetAttributes().
11383 : *
11384 : * @param hGroup Group.
11385 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11386 : * @param papszOptions Driver specific options determining how attributes
11387 : * should be retrieved. Pass nullptr for default behavior.
11388 : *
11389 : * @return an array of *pnCount attributes.
11390 : */
11391 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11392 : CSLConstList papszOptions)
11393 : {
11394 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11395 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11396 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11397 : auto ret = static_cast<GDALAttributeH *>(
11398 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11399 229 : for (size_t i = 0; i < attrs.size(); i++)
11400 : {
11401 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11402 : }
11403 71 : *pnCount = attrs.size();
11404 71 : return ret;
11405 : }
11406 :
11407 : /************************************************************************/
11408 : /* GDALGroupGetStructuralInfo() */
11409 : /************************************************************************/
11410 :
11411 : /** Return structural information on the group.
11412 : *
11413 : * This may be the compression, etc..
11414 : *
11415 : * The return value should not be freed and is valid until GDALGroup is
11416 : * released or this function called again.
11417 : *
11418 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11419 : */
11420 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11421 : {
11422 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11423 4 : return hGroup->m_poImpl->GetStructuralInfo();
11424 : }
11425 :
11426 : /************************************************************************/
11427 : /* GDALReleaseAttributes() */
11428 : /************************************************************************/
11429 :
11430 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11431 : *
11432 : * @param attributes return pointer of above methods
11433 : * @param nCount *pnCount value returned by above methods
11434 : */
11435 129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11436 : {
11437 416 : for (size_t i = 0; i < nCount; i++)
11438 : {
11439 287 : delete attributes[i];
11440 : }
11441 129 : CPLFree(attributes);
11442 129 : }
11443 :
11444 : /************************************************************************/
11445 : /* GDALGroupCreateGroup() */
11446 : /************************************************************************/
11447 :
11448 : /** Create a sub-group within a group.
11449 : *
11450 : * This is the same as the C++ method GDALGroup::CreateGroup().
11451 : *
11452 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11453 : */
11454 173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11455 : CSLConstList papszOptions)
11456 : {
11457 173 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11458 173 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11459 519 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11460 519 : papszOptions);
11461 173 : if (!ret)
11462 49 : return nullptr;
11463 124 : return new GDALGroupHS(ret);
11464 : }
11465 :
11466 : /************************************************************************/
11467 : /* GDALGroupDeleteGroup() */
11468 : /************************************************************************/
11469 :
11470 : /** Delete a sub-group from a group.
11471 : *
11472 : * After this call, if a previously obtained instance of the deleted object
11473 : * is still alive, no method other than for freeing it should be invoked.
11474 : *
11475 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11476 : *
11477 : * @return true in case of success.
11478 : * @since GDAL 3.8
11479 : */
11480 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11481 : CSLConstList papszOptions)
11482 : {
11483 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11484 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11485 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11486 20 : papszOptions);
11487 : }
11488 :
11489 : /************************************************************************/
11490 : /* GDALGroupCreateDimension() */
11491 : /************************************************************************/
11492 :
11493 : /** Create a dimension within a group.
11494 : *
11495 : * This is the same as the C++ method GDALGroup::CreateDimension().
11496 : *
11497 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11498 : */
11499 664 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11500 : const char *pszType,
11501 : const char *pszDirection, GUInt64 nSize,
11502 : CSLConstList papszOptions)
11503 : {
11504 664 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11505 664 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11506 664 : auto ret = hGroup->m_poImpl->CreateDimension(
11507 1328 : std::string(pszName), std::string(pszType ? pszType : ""),
11508 2656 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11509 664 : if (!ret)
11510 9 : return nullptr;
11511 655 : return new GDALDimensionHS(ret);
11512 : }
11513 :
11514 : /************************************************************************/
11515 : /* GDALGroupCreateMDArray() */
11516 : /************************************************************************/
11517 :
11518 : /** Create a multidimensional array within a group.
11519 : *
11520 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11521 : *
11522 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11523 : */
11524 603 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11525 : size_t nDimensions,
11526 : GDALDimensionH *pahDimensions,
11527 : GDALExtendedDataTypeH hEDT,
11528 : CSLConstList papszOptions)
11529 : {
11530 603 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11531 603 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11532 603 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11533 1206 : std::vector<std::shared_ptr<GDALDimension>> dims;
11534 603 : dims.reserve(nDimensions);
11535 1423 : for (size_t i = 0; i < nDimensions; i++)
11536 820 : dims.push_back(pahDimensions[i]->m_poImpl);
11537 1809 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11538 1809 : *(hEDT->m_poImpl), papszOptions);
11539 603 : if (!ret)
11540 65 : return nullptr;
11541 538 : return new GDALMDArrayHS(ret);
11542 : }
11543 :
11544 : /************************************************************************/
11545 : /* GDALGroupDeleteMDArray() */
11546 : /************************************************************************/
11547 :
11548 : /** Delete an array from a group.
11549 : *
11550 : * After this call, if a previously obtained instance of the deleted object
11551 : * is still alive, no method other than for freeing it should be invoked.
11552 : *
11553 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
11554 : *
11555 : * @return true in case of success.
11556 : * @since GDAL 3.8
11557 : */
11558 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
11559 : CSLConstList papszOptions)
11560 : {
11561 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11562 20 : VALIDATE_POINTER1(pszName, __func__, false);
11563 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
11564 : }
11565 :
11566 : /************************************************************************/
11567 : /* GDALGroupCreateAttribute() */
11568 : /************************************************************************/
11569 :
11570 : /** Create a attribute within a group.
11571 : *
11572 : * This is the same as the C++ method GDALGroup::CreateAttribute().
11573 : *
11574 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11575 : */
11576 120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
11577 : size_t nDimensions,
11578 : const GUInt64 *panDimensions,
11579 : GDALExtendedDataTypeH hEDT,
11580 : CSLConstList papszOptions)
11581 : {
11582 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11583 120 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11584 240 : std::vector<GUInt64> dims;
11585 120 : dims.reserve(nDimensions);
11586 166 : for (size_t i = 0; i < nDimensions; i++)
11587 46 : dims.push_back(panDimensions[i]);
11588 120 : auto ret = hGroup->m_poImpl->CreateAttribute(
11589 360 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11590 120 : if (!ret)
11591 14 : return nullptr;
11592 106 : return new GDALAttributeHS(ret);
11593 : }
11594 :
11595 : /************************************************************************/
11596 : /* GDALGroupDeleteAttribute() */
11597 : /************************************************************************/
11598 :
11599 : /** Delete an attribute from a group.
11600 : *
11601 : * After this call, if a previously obtained instance of the deleted object
11602 : * is still alive, no method other than for freeing it should be invoked.
11603 : *
11604 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
11605 : *
11606 : * @return true in case of success.
11607 : * @since GDAL 3.8
11608 : */
11609 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
11610 : CSLConstList papszOptions)
11611 : {
11612 25 : VALIDATE_POINTER1(hGroup, __func__, false);
11613 25 : VALIDATE_POINTER1(pszName, __func__, false);
11614 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
11615 25 : papszOptions);
11616 : }
11617 :
11618 : /************************************************************************/
11619 : /* GDALGroupRename() */
11620 : /************************************************************************/
11621 :
11622 : /** Rename the group.
11623 : *
11624 : * This is not implemented by all drivers.
11625 : *
11626 : * Drivers known to implement it: MEM, netCDF.
11627 : *
11628 : * This is the same as the C++ method GDALGroup::Rename()
11629 : *
11630 : * @return true in case of success
11631 : * @since GDAL 3.8
11632 : */
11633 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
11634 : {
11635 45 : VALIDATE_POINTER1(hGroup, __func__, false);
11636 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
11637 45 : return hGroup->m_poImpl->Rename(pszNewName);
11638 : }
11639 :
11640 : /************************************************************************/
11641 : /* GDALGroupSubsetDimensionFromSelection() */
11642 : /************************************************************************/
11643 :
11644 : /** Return a virtual group whose one dimension has been subset according to a
11645 : * selection.
11646 : *
11647 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
11648 : *
11649 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
11650 : */
11651 : GDALGroupH
11652 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
11653 : const char *pszSelection,
11654 : CPL_UNUSED CSLConstList papszOptions)
11655 : {
11656 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11657 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
11658 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
11659 42 : std::string(pszSelection));
11660 14 : if (!hNewGroup)
11661 8 : return nullptr;
11662 6 : return new GDALGroupHS(hNewGroup);
11663 : }
11664 :
11665 : /************************************************************************/
11666 : /* GDALMDArrayRelease() */
11667 : /************************************************************************/
11668 :
11669 : /** Release the GDAL in-memory object associated with a GDALMDArray.
11670 : *
11671 : * Note: when applied on a object coming from a driver, this does not
11672 : * destroy the object in the file, database, etc...
11673 : */
11674 1982 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
11675 : {
11676 1982 : delete hMDArray;
11677 1982 : }
11678 :
11679 : /************************************************************************/
11680 : /* GDALMDArrayGetName() */
11681 : /************************************************************************/
11682 :
11683 : /** Return array name.
11684 : *
11685 : * This is the same as the C++ method GDALMDArray::GetName()
11686 : */
11687 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
11688 : {
11689 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11690 83 : return hArray->m_poImpl->GetName().c_str();
11691 : }
11692 :
11693 : /************************************************************************/
11694 : /* GDALMDArrayGetFullName() */
11695 : /************************************************************************/
11696 :
11697 : /** Return array full name.
11698 : *
11699 : * This is the same as the C++ method GDALMDArray::GetFullName()
11700 : */
11701 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
11702 : {
11703 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11704 50 : return hArray->m_poImpl->GetFullName().c_str();
11705 : }
11706 :
11707 : /************************************************************************/
11708 : /* GDALMDArrayGetName() */
11709 : /************************************************************************/
11710 :
11711 : /** Return the total number of values in the array.
11712 : *
11713 : * This is the same as the C++ method
11714 : * GDALAbstractMDArray::GetTotalElementsCount()
11715 : */
11716 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
11717 : {
11718 6 : VALIDATE_POINTER1(hArray, __func__, 0);
11719 6 : return hArray->m_poImpl->GetTotalElementsCount();
11720 : }
11721 :
11722 : /************************************************************************/
11723 : /* GDALMDArrayGetDimensionCount() */
11724 : /************************************************************************/
11725 :
11726 : /** Return the number of dimensions.
11727 : *
11728 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
11729 : */
11730 10224 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
11731 : {
11732 10224 : VALIDATE_POINTER1(hArray, __func__, 0);
11733 10224 : return hArray->m_poImpl->GetDimensionCount();
11734 : }
11735 :
11736 : /************************************************************************/
11737 : /* GDALMDArrayGetDimensions() */
11738 : /************************************************************************/
11739 :
11740 : /** Return the dimensions of the array
11741 : *
11742 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11743 : * array itself needs to be freed, CPLFree() should be called (and
11744 : * GDALDimensionRelease() on individual array members).
11745 : *
11746 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
11747 : *
11748 : * @param hArray Array.
11749 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11750 : *
11751 : * @return an array of *pnCount dimensions.
11752 : */
11753 2270 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
11754 : {
11755 2270 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11756 2270 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11757 2270 : const auto &dims(hArray->m_poImpl->GetDimensions());
11758 : auto ret = static_cast<GDALDimensionH *>(
11759 2270 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11760 6409 : for (size_t i = 0; i < dims.size(); i++)
11761 : {
11762 4139 : ret[i] = new GDALDimensionHS(dims[i]);
11763 : }
11764 2270 : *pnCount = dims.size();
11765 2270 : return ret;
11766 : }
11767 :
11768 : /************************************************************************/
11769 : /* GDALReleaseDimensions() */
11770 : /************************************************************************/
11771 :
11772 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
11773 : *
11774 : * @param dims return pointer of above methods
11775 : * @param nCount *pnCount value returned by above methods
11776 : */
11777 2343 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
11778 : {
11779 6639 : for (size_t i = 0; i < nCount; i++)
11780 : {
11781 4296 : delete dims[i];
11782 : }
11783 2343 : CPLFree(dims);
11784 2343 : }
11785 :
11786 : /************************************************************************/
11787 : /* GDALMDArrayGetDataType() */
11788 : /************************************************************************/
11789 :
11790 : /** Return the data type
11791 : *
11792 : * The return must be freed with GDALExtendedDataTypeRelease().
11793 : */
11794 3850 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
11795 : {
11796 3850 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11797 : return new GDALExtendedDataTypeHS(
11798 3850 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
11799 : }
11800 :
11801 : /************************************************************************/
11802 : /* GDALMDArrayRead() */
11803 : /************************************************************************/
11804 :
11805 : /** Read part or totality of a multidimensional array.
11806 : *
11807 : * This is the same as the C++ method GDALAbstractMDArray::Read()
11808 : *
11809 : * @return TRUE in case of success.
11810 : */
11811 1927 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11812 : const size_t *count, const GInt64 *arrayStep,
11813 : const GPtrDiff_t *bufferStride,
11814 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
11815 : const void *pDstBufferAllocStart,
11816 : size_t nDstBufferAllocSize)
11817 : {
11818 1927 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11819 1927 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11820 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11821 : {
11822 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11823 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11824 : }
11825 1927 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11826 1927 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
11827 : // coverity[var_deref_model]
11828 3854 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
11829 1927 : *(bufferDataType->m_poImpl), pDstBuffer,
11830 1927 : pDstBufferAllocStart, nDstBufferAllocSize);
11831 : }
11832 :
11833 : /************************************************************************/
11834 : /* GDALMDArrayWrite() */
11835 : /************************************************************************/
11836 :
11837 : /** Write part or totality of a multidimensional array.
11838 : *
11839 : * This is the same as the C++ method GDALAbstractMDArray::Write()
11840 : *
11841 : * @return TRUE in case of success.
11842 : */
11843 558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11844 : const size_t *count, const GInt64 *arrayStep,
11845 : const GPtrDiff_t *bufferStride,
11846 : GDALExtendedDataTypeH bufferDataType,
11847 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
11848 : size_t nSrcBufferAllocSize)
11849 : {
11850 558 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11851 558 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11852 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11853 : {
11854 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11855 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11856 : }
11857 558 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11858 558 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
11859 : // coverity[var_deref_model]
11860 1116 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
11861 558 : bufferStride, *(bufferDataType->m_poImpl),
11862 : pSrcBuffer, pSrcBufferAllocStart,
11863 558 : nSrcBufferAllocSize);
11864 : }
11865 :
11866 : /************************************************************************/
11867 : /* GDALMDArrayAdviseRead() */
11868 : /************************************************************************/
11869 :
11870 : /** Advise driver of upcoming read requests.
11871 : *
11872 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11873 : *
11874 : * @return TRUE in case of success.
11875 : *
11876 : * @since GDAL 3.2
11877 : */
11878 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11879 : const size_t *count)
11880 : {
11881 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
11882 : }
11883 :
11884 : /************************************************************************/
11885 : /* GDALMDArrayAdviseReadEx() */
11886 : /************************************************************************/
11887 :
11888 : /** Advise driver of upcoming read requests.
11889 : *
11890 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11891 : *
11892 : * @return TRUE in case of success.
11893 : *
11894 : * @since GDAL 3.4
11895 : */
11896 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11897 : const size_t *count, CSLConstList papszOptions)
11898 : {
11899 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11900 : // coverity[var_deref_model]
11901 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
11902 : }
11903 :
11904 : /************************************************************************/
11905 : /* GDALMDArrayGetAttribute() */
11906 : /************************************************************************/
11907 :
11908 : /** Return an attribute by its name.
11909 : *
11910 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11911 : *
11912 : * The returned attribute must be freed with GDALAttributeRelease().
11913 : */
11914 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
11915 : {
11916 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11917 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11918 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
11919 119 : if (attr)
11920 110 : return new GDALAttributeHS(attr);
11921 9 : return nullptr;
11922 : }
11923 :
11924 : /************************************************************************/
11925 : /* GDALMDArrayGetAttributes() */
11926 : /************************************************************************/
11927 :
11928 : /** Return the list of attributes contained in this array.
11929 : *
11930 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11931 : * array itself needs to be freed, CPLFree() should be called (and
11932 : * GDALAttributeRelease() on individual array members).
11933 : *
11934 : * This is the same as the C++ method GDALMDArray::GetAttributes().
11935 : *
11936 : * @param hArray Array.
11937 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11938 : * @param papszOptions Driver specific options determining how attributes
11939 : * should be retrieved. Pass nullptr for default behavior.
11940 : *
11941 : * @return an array of *pnCount attributes.
11942 : */
11943 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
11944 : CSLConstList papszOptions)
11945 : {
11946 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11947 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11948 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
11949 : auto ret = static_cast<GDALAttributeH *>(
11950 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11951 187 : for (size_t i = 0; i < attrs.size(); i++)
11952 : {
11953 129 : ret[i] = new GDALAttributeHS(attrs[i]);
11954 : }
11955 58 : *pnCount = attrs.size();
11956 58 : return ret;
11957 : }
11958 :
11959 : /************************************************************************/
11960 : /* GDALMDArrayCreateAttribute() */
11961 : /************************************************************************/
11962 :
11963 : /** Create a attribute within an array.
11964 : *
11965 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
11966 : *
11967 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11968 : */
11969 150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
11970 : const char *pszName,
11971 : size_t nDimensions,
11972 : const GUInt64 *panDimensions,
11973 : GDALExtendedDataTypeH hEDT,
11974 : CSLConstList papszOptions)
11975 : {
11976 150 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11977 150 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11978 150 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11979 300 : std::vector<GUInt64> dims;
11980 150 : dims.reserve(nDimensions);
11981 175 : for (size_t i = 0; i < nDimensions; i++)
11982 25 : dims.push_back(panDimensions[i]);
11983 150 : auto ret = hArray->m_poImpl->CreateAttribute(
11984 450 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11985 150 : if (!ret)
11986 9 : return nullptr;
11987 141 : return new GDALAttributeHS(ret);
11988 : }
11989 :
11990 : /************************************************************************/
11991 : /* GDALMDArrayDeleteAttribute() */
11992 : /************************************************************************/
11993 :
11994 : /** Delete an attribute from an array.
11995 : *
11996 : * After this call, if a previously obtained instance of the deleted object
11997 : * is still alive, no method other than for freeing it should be invoked.
11998 : *
11999 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12000 : *
12001 : * @return true in case of success.
12002 : * @since GDAL 3.8
12003 : */
12004 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12005 : CSLConstList papszOptions)
12006 : {
12007 24 : VALIDATE_POINTER1(hArray, __func__, false);
12008 24 : VALIDATE_POINTER1(pszName, __func__, false);
12009 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12010 24 : papszOptions);
12011 : }
12012 :
12013 : /************************************************************************/
12014 : /* GDALMDArrayGetRawNoDataValue() */
12015 : /************************************************************************/
12016 :
12017 : /** Return the nodata value as a "raw" value.
12018 : *
12019 : * The value returned might be nullptr in case of no nodata value. When
12020 : * a nodata value is registered, a non-nullptr will be returned whose size in
12021 : * bytes is GetDataType().GetSize().
12022 : *
12023 : * The returned value should not be modified or freed.
12024 : *
12025 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12026 : *
12027 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12028 : */
12029 76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12030 : {
12031 76 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12032 76 : return hArray->m_poImpl->GetRawNoDataValue();
12033 : }
12034 :
12035 : /************************************************************************/
12036 : /* GDALMDArrayGetNoDataValueAsDouble() */
12037 : /************************************************************************/
12038 :
12039 : /** Return the nodata value as a double.
12040 : *
12041 : * The value returned might be nullptr in case of no nodata value. When
12042 : * a nodata value is registered, a non-nullptr will be returned whose size in
12043 : * bytes is GetDataType().GetSize().
12044 : *
12045 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12046 : *
12047 : * @param hArray Array handle.
12048 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12049 : * if a nodata value exists and can be converted to double. Might be nullptr.
12050 : *
12051 : * @return the nodata value as a double. A 0.0 value might also indicate the
12052 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12053 : * will be set to false then).
12054 : */
12055 119 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12056 : int *pbHasNoDataValue)
12057 : {
12058 119 : VALIDATE_POINTER1(hArray, __func__, 0);
12059 119 : bool bHasNodataValue = false;
12060 119 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12061 119 : if (pbHasNoDataValue)
12062 119 : *pbHasNoDataValue = bHasNodataValue;
12063 119 : return ret;
12064 : }
12065 :
12066 : /************************************************************************/
12067 : /* GDALMDArrayGetNoDataValueAsInt64() */
12068 : /************************************************************************/
12069 :
12070 : /** Return the nodata value as a Int64.
12071 : *
12072 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12073 : *
12074 : * @param hArray Array handle.
12075 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12076 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12077 : *
12078 : * @return the nodata value as a Int64.
12079 : * @since GDAL 3.5
12080 : */
12081 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12082 : int *pbHasNoDataValue)
12083 : {
12084 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12085 11 : bool bHasNodataValue = false;
12086 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12087 11 : if (pbHasNoDataValue)
12088 11 : *pbHasNoDataValue = bHasNodataValue;
12089 11 : return ret;
12090 : }
12091 :
12092 : /************************************************************************/
12093 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12094 : /************************************************************************/
12095 :
12096 : /** Return the nodata value as a UInt64.
12097 : *
12098 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12099 : *
12100 : * @param hArray Array handle.
12101 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12102 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12103 : *
12104 : * @return the nodata value as a UInt64.
12105 : * @since GDAL 3.5
12106 : */
12107 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12108 : int *pbHasNoDataValue)
12109 : {
12110 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12111 7 : bool bHasNodataValue = false;
12112 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12113 7 : if (pbHasNoDataValue)
12114 7 : *pbHasNoDataValue = bHasNodataValue;
12115 7 : return ret;
12116 : }
12117 :
12118 : /************************************************************************/
12119 : /* GDALMDArraySetRawNoDataValue() */
12120 : /************************************************************************/
12121 :
12122 : /** Set the nodata value as a "raw" value.
12123 : *
12124 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12125 : * void*).
12126 : *
12127 : * @return TRUE in case of success.
12128 : */
12129 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12130 : {
12131 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12132 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12133 : }
12134 :
12135 : /************************************************************************/
12136 : /* GDALMDArraySetNoDataValueAsDouble() */
12137 : /************************************************************************/
12138 :
12139 : /** Set the nodata value as a double.
12140 : *
12141 : * If the natural data type of the attribute/array is not double, type
12142 : * conversion will occur to the type returned by GetDataType().
12143 : *
12144 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12145 : *
12146 : * @return TRUE in case of success.
12147 : */
12148 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12149 : {
12150 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12151 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12152 : }
12153 :
12154 : /************************************************************************/
12155 : /* GDALMDArraySetNoDataValueAsInt64() */
12156 : /************************************************************************/
12157 :
12158 : /** Set the nodata value as a Int64.
12159 : *
12160 : * If the natural data type of the attribute/array is not Int64, type conversion
12161 : * will occur to the type returned by GetDataType().
12162 : *
12163 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12164 : *
12165 : * @return TRUE in case of success.
12166 : * @since GDAL 3.5
12167 : */
12168 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12169 : {
12170 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12171 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12172 : }
12173 :
12174 : /************************************************************************/
12175 : /* GDALMDArraySetNoDataValueAsUInt64() */
12176 : /************************************************************************/
12177 :
12178 : /** Set the nodata value as a UInt64.
12179 : *
12180 : * If the natural data type of the attribute/array is not UInt64, type
12181 : * conversion will occur to the type returned by GetDataType().
12182 : *
12183 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12184 : *
12185 : * @return TRUE in case of success.
12186 : * @since GDAL 3.5
12187 : */
12188 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12189 : uint64_t nNoDataValue)
12190 : {
12191 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12192 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12193 : }
12194 :
12195 : /************************************************************************/
12196 : /* GDALMDArrayResize() */
12197 : /************************************************************************/
12198 :
12199 : /** Resize an array to new dimensions.
12200 : *
12201 : * Not all drivers may allow this operation, and with restrictions (e.g.
12202 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12203 : *
12204 : * Resizing a dimension used in other arrays will cause those other arrays
12205 : * to be resized.
12206 : *
12207 : * This is the same as the C++ method GDALMDArray::Resize().
12208 : *
12209 : * @param hArray Array.
12210 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12211 : * new size of each indexing dimension.
12212 : * @param papszOptions Options. (Driver specific)
12213 : * @return true in case of success.
12214 : * @since GDAL 3.7
12215 : */
12216 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12217 : CSLConstList papszOptions)
12218 : {
12219 42 : VALIDATE_POINTER1(hArray, __func__, false);
12220 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12221 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12222 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12223 : {
12224 83 : anNewDimSizes[i] = panNewDimSizes[i];
12225 : }
12226 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12227 : }
12228 :
12229 : /************************************************************************/
12230 : /* GDALMDArraySetScale() */
12231 : /************************************************************************/
12232 :
12233 : /** Set the scale value to apply to raw values.
12234 : *
12235 : * unscaled_value = raw_value * GetScale() + GetOffset()
12236 : *
12237 : * This is the same as the C++ method GDALMDArray::SetScale().
12238 : *
12239 : * @return TRUE in case of success.
12240 : */
12241 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12242 : {
12243 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12244 0 : return hArray->m_poImpl->SetScale(dfScale);
12245 : }
12246 :
12247 : /************************************************************************/
12248 : /* GDALMDArraySetScaleEx() */
12249 : /************************************************************************/
12250 :
12251 : /** Set the scale value to apply to raw values.
12252 : *
12253 : * unscaled_value = raw_value * GetScale() + GetOffset()
12254 : *
12255 : * This is the same as the C++ method GDALMDArray::SetScale().
12256 : *
12257 : * @return TRUE in case of success.
12258 : * @since GDAL 3.3
12259 : */
12260 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12261 : GDALDataType eStorageType)
12262 : {
12263 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12264 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12265 : }
12266 :
12267 : /************************************************************************/
12268 : /* GDALMDArraySetOffset() */
12269 : /************************************************************************/
12270 :
12271 : /** Set the scale value to apply to raw values.
12272 : *
12273 : * unscaled_value = raw_value * GetScale() + GetOffset()
12274 : *
12275 : * This is the same as the C++ method GDALMDArray::SetOffset().
12276 : *
12277 : * @return TRUE in case of success.
12278 : */
12279 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12280 : {
12281 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12282 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12283 : }
12284 :
12285 : /************************************************************************/
12286 : /* GDALMDArraySetOffsetEx() */
12287 : /************************************************************************/
12288 :
12289 : /** Set the scale value to apply to raw values.
12290 : *
12291 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12292 : *
12293 : * This is the same as the C++ method GDALMDArray::SetOffset().
12294 : *
12295 : * @return TRUE in case of success.
12296 : * @since GDAL 3.3
12297 : */
12298 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12299 : GDALDataType eStorageType)
12300 : {
12301 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12302 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12303 : }
12304 :
12305 : /************************************************************************/
12306 : /* GDALMDArrayGetScale() */
12307 : /************************************************************************/
12308 :
12309 : /** Get the scale value to apply to raw values.
12310 : *
12311 : * unscaled_value = raw_value * GetScale() + GetOffset()
12312 : *
12313 : * This is the same as the C++ method GDALMDArray::GetScale().
12314 : *
12315 : * @return the scale value
12316 : */
12317 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12318 : {
12319 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12320 103 : bool bHasValue = false;
12321 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12322 103 : if (pbHasValue)
12323 103 : *pbHasValue = bHasValue;
12324 103 : return dfRet;
12325 : }
12326 :
12327 : /************************************************************************/
12328 : /* GDALMDArrayGetScaleEx() */
12329 : /************************************************************************/
12330 :
12331 : /** Get the scale value to apply to raw values.
12332 : *
12333 : * unscaled_value = raw_value * GetScale() + GetScale()
12334 : *
12335 : * This is the same as the C++ method GDALMDArray::GetScale().
12336 : *
12337 : * @return the scale value
12338 : * @since GDAL 3.3
12339 : */
12340 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12341 : GDALDataType *peStorageType)
12342 : {
12343 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12344 5 : bool bHasValue = false;
12345 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12346 5 : if (pbHasValue)
12347 5 : *pbHasValue = bHasValue;
12348 5 : return dfRet;
12349 : }
12350 :
12351 : /************************************************************************/
12352 : /* GDALMDArrayGetOffset() */
12353 : /************************************************************************/
12354 :
12355 : /** Get the scale value to apply to raw values.
12356 : *
12357 : * unscaled_value = raw_value * GetScale() + GetOffset()
12358 : *
12359 : * This is the same as the C++ method GDALMDArray::GetOffset().
12360 : *
12361 : * @return the scale value
12362 : */
12363 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12364 : {
12365 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12366 100 : bool bHasValue = false;
12367 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12368 100 : if (pbHasValue)
12369 100 : *pbHasValue = bHasValue;
12370 100 : return dfRet;
12371 : }
12372 :
12373 : /************************************************************************/
12374 : /* GDALMDArrayGetOffsetEx() */
12375 : /************************************************************************/
12376 :
12377 : /** Get the scale value to apply to raw values.
12378 : *
12379 : * unscaled_value = raw_value * GetScale() + GetOffset()
12380 : *
12381 : * This is the same as the C++ method GDALMDArray::GetOffset().
12382 : *
12383 : * @return the scale value
12384 : * @since GDAL 3.3
12385 : */
12386 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12387 : GDALDataType *peStorageType)
12388 : {
12389 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12390 5 : bool bHasValue = false;
12391 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12392 5 : if (pbHasValue)
12393 5 : *pbHasValue = bHasValue;
12394 5 : return dfRet;
12395 : }
12396 :
12397 : /************************************************************************/
12398 : /* GDALMDArrayGetBlockSize() */
12399 : /************************************************************************/
12400 :
12401 : /** Return the "natural" block size of the array along all dimensions.
12402 : *
12403 : * Some drivers might organize the array in tiles/blocks and reading/writing
12404 : * aligned on those tile/block boundaries will be more efficient.
12405 : *
12406 : * The returned number of elements in the vector is the same as
12407 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12408 : * the natural block size along the considered dimension.
12409 : * "Flat" arrays will typically return a vector of values set to 0.
12410 : *
12411 : * The default implementation will return a vector of values set to 0.
12412 : *
12413 : * This method is used by GetProcessingChunkSize().
12414 : *
12415 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12416 : * theoretical case of a 32-bit platform, this might exceed its size_t
12417 : * allocation capabilities.
12418 : *
12419 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12420 : *
12421 : * @return the block size, in number of elements along each dimension.
12422 : */
12423 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12424 : {
12425 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12426 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12427 93 : auto res = hArray->m_poImpl->GetBlockSize();
12428 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12429 285 : for (size_t i = 0; i < res.size(); i++)
12430 : {
12431 192 : ret[i] = res[i];
12432 : }
12433 93 : *pnCount = res.size();
12434 93 : return ret;
12435 : }
12436 :
12437 : /***********************************************************************/
12438 : /* GDALMDArrayGetProcessingChunkSize() */
12439 : /************************************************************************/
12440 :
12441 : /** \brief Return an optimal chunk size for read/write operations, given the
12442 : * natural block size and memory constraints specified.
12443 : *
12444 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12445 : * multiple of those returned by GetBlockSize() (unless the block define by
12446 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12447 : * returned by this method).
12448 : *
12449 : * This is the same as the C++ method
12450 : * GDALAbstractMDArray::GetProcessingChunkSize().
12451 : *
12452 : * @param hArray Array.
12453 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12454 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12455 : * chunk.
12456 : *
12457 : * @return the chunk size, in number of elements along each dimension.
12458 : */
12459 :
12460 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12461 : size_t nMaxChunkMemory)
12462 : {
12463 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12464 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12465 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12466 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12467 3 : for (size_t i = 0; i < res.size(); i++)
12468 : {
12469 2 : ret[i] = res[i];
12470 : }
12471 1 : *pnCount = res.size();
12472 1 : return ret;
12473 : }
12474 :
12475 : /************************************************************************/
12476 : /* GDALMDArrayGetStructuralInfo() */
12477 : /************************************************************************/
12478 :
12479 : /** Return structural information on the array.
12480 : *
12481 : * This may be the compression, etc..
12482 : *
12483 : * The return value should not be freed and is valid until GDALMDArray is
12484 : * released or this function called again.
12485 : *
12486 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12487 : */
12488 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12489 : {
12490 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12491 15 : return hArray->m_poImpl->GetStructuralInfo();
12492 : }
12493 :
12494 : /************************************************************************/
12495 : /* GDALMDArrayGetView() */
12496 : /************************************************************************/
12497 :
12498 : /** Return a view of the array using slicing or field access.
12499 : *
12500 : * The returned object should be released with GDALMDArrayRelease().
12501 : *
12502 : * This is the same as the C++ method GDALMDArray::GetView().
12503 : */
12504 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12505 : {
12506 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12507 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12508 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12509 430 : if (!sliced)
12510 22 : return nullptr;
12511 408 : return new GDALMDArrayHS(sliced);
12512 : }
12513 :
12514 : /************************************************************************/
12515 : /* GDALMDArrayTranspose() */
12516 : /************************************************************************/
12517 :
12518 : /** Return a view of the array whose axis have been reordered.
12519 : *
12520 : * The returned object should be released with GDALMDArrayRelease().
12521 : *
12522 : * This is the same as the C++ method GDALMDArray::Transpose().
12523 : */
12524 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12525 : const int *panMapNewAxisToOldAxis)
12526 : {
12527 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12528 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12529 44 : if (nNewAxisCount)
12530 : {
12531 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12532 : nNewAxisCount * sizeof(int));
12533 : }
12534 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12535 44 : if (!reordered)
12536 7 : return nullptr;
12537 37 : return new GDALMDArrayHS(reordered);
12538 : }
12539 :
12540 : /************************************************************************/
12541 : /* GDALMDArrayGetUnscaled() */
12542 : /************************************************************************/
12543 :
12544 : /** Return an array that is the unscaled version of the current one.
12545 : *
12546 : * That is each value of the unscaled array will be
12547 : * unscaled_value = raw_value * GetScale() + GetOffset()
12548 : *
12549 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
12550 : * from unscaled values to raw values.
12551 : *
12552 : * The returned object should be released with GDALMDArrayRelease().
12553 : *
12554 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
12555 : */
12556 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
12557 : {
12558 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12559 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
12560 13 : if (!unscaled)
12561 0 : return nullptr;
12562 13 : return new GDALMDArrayHS(unscaled);
12563 : }
12564 :
12565 : /************************************************************************/
12566 : /* GDALMDArrayGetMask() */
12567 : /************************************************************************/
12568 :
12569 : /** Return an array that is a mask for the current array
12570 : *
12571 : * This array will be of type Byte, with values set to 0 to indicate invalid
12572 : * pixels of the current array, and values set to 1 to indicate valid pixels.
12573 : *
12574 : * The returned object should be released with GDALMDArrayRelease().
12575 : *
12576 : * This is the same as the C++ method GDALMDArray::GetMask().
12577 : */
12578 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
12579 : {
12580 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12581 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
12582 35 : if (!unscaled)
12583 7 : return nullptr;
12584 28 : return new GDALMDArrayHS(unscaled);
12585 : }
12586 :
12587 : /************************************************************************/
12588 : /* GDALMDArrayGetResampled() */
12589 : /************************************************************************/
12590 :
12591 : /** Return an array that is a resampled / reprojected view of the current array
12592 : *
12593 : * This is the same as the C++ method GDALMDArray::GetResampled().
12594 : *
12595 : * Currently this method can only resample along the last 2 dimensions, unless
12596 : * orthorectifying a NASA EMIT dataset.
12597 : *
12598 : * The returned object should be released with GDALMDArrayRelease().
12599 : *
12600 : * @since 3.4
12601 : */
12602 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
12603 : const GDALDimensionH *pahNewDims,
12604 : GDALRIOResampleAlg resampleAlg,
12605 : OGRSpatialReferenceH hTargetSRS,
12606 : CSLConstList papszOptions)
12607 : {
12608 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12609 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
12610 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
12611 112 : for (size_t i = 0; i < nNewDimCount; ++i)
12612 : {
12613 78 : if (pahNewDims[i])
12614 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
12615 : }
12616 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
12617 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
12618 68 : papszOptions);
12619 34 : if (!poNewArray)
12620 8 : return nullptr;
12621 26 : return new GDALMDArrayHS(poNewArray);
12622 : }
12623 :
12624 : /************************************************************************/
12625 : /* GDALMDArraySetUnit() */
12626 : /************************************************************************/
12627 :
12628 : /** Set the variable unit.
12629 : *
12630 : * Values should conform as much as possible with those allowed by
12631 : * the NetCDF CF conventions:
12632 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12633 : * but others might be returned.
12634 : *
12635 : * Few examples are "meter", "degrees", "second", ...
12636 : * Empty value means unknown.
12637 : *
12638 : * This is the same as the C function GDALMDArraySetUnit()
12639 : *
12640 : * @param hArray array.
12641 : * @param pszUnit unit name.
12642 : * @return TRUE in case of success.
12643 : */
12644 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
12645 : {
12646 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12647 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
12648 : }
12649 :
12650 : /************************************************************************/
12651 : /* GDALMDArrayGetUnit() */
12652 : /************************************************************************/
12653 :
12654 : /** Return the array unit.
12655 : *
12656 : * Values should conform as much as possible with those allowed by
12657 : * the NetCDF CF conventions:
12658 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12659 : * but others might be returned.
12660 : *
12661 : * Few examples are "meter", "degrees", "second", ...
12662 : * Empty value means unknown.
12663 : *
12664 : * The return value should not be freed and is valid until GDALMDArray is
12665 : * released or this function called again.
12666 : *
12667 : * This is the same as the C++ method GDALMDArray::GetUnit().
12668 : */
12669 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
12670 : {
12671 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12672 111 : return hArray->m_poImpl->GetUnit().c_str();
12673 : }
12674 :
12675 : /************************************************************************/
12676 : /* GDALMDArrayGetSpatialRef() */
12677 : /************************************************************************/
12678 :
12679 : /** Assign a spatial reference system object to the array.
12680 : *
12681 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
12682 : * @return TRUE in case of success.
12683 : */
12684 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
12685 : {
12686 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12687 60 : return hArray->m_poImpl->SetSpatialRef(
12688 60 : OGRSpatialReference::FromHandle(hSRS));
12689 : }
12690 :
12691 : /************************************************************************/
12692 : /* GDALMDArrayGetSpatialRef() */
12693 : /************************************************************************/
12694 :
12695 : /** Return the spatial reference system object associated with the array.
12696 : *
12697 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
12698 : *
12699 : * The returned object must be freed with OSRDestroySpatialReference().
12700 : */
12701 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
12702 : {
12703 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12704 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
12705 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
12706 : }
12707 :
12708 : /************************************************************************/
12709 : /* GDALMDArrayGetStatistics() */
12710 : /************************************************************************/
12711 :
12712 : /**
12713 : * \brief Fetch statistics.
12714 : *
12715 : * This is the same as the C++ method GDALMDArray::GetStatistics().
12716 : *
12717 : * @since GDAL 3.2
12718 : */
12719 :
12720 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
12721 : int bApproxOK, int bForce, double *pdfMin,
12722 : double *pdfMax, double *pdfMean,
12723 : double *pdfStdDev, GUInt64 *pnValidCount,
12724 : GDALProgressFunc pfnProgress,
12725 : void *pProgressData)
12726 : {
12727 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
12728 30 : return hArray->m_poImpl->GetStatistics(
12729 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
12730 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
12731 : }
12732 :
12733 : /************************************************************************/
12734 : /* GDALMDArrayComputeStatistics() */
12735 : /************************************************************************/
12736 :
12737 : /**
12738 : * \brief Compute statistics.
12739 : *
12740 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12741 : *
12742 : * @since GDAL 3.2
12743 : * @see GDALMDArrayComputeStatisticsEx()
12744 : */
12745 :
12746 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12747 : int bApproxOK, double *pdfMin, double *pdfMax,
12748 : double *pdfMean, double *pdfStdDev,
12749 : GUInt64 *pnValidCount,
12750 : GDALProgressFunc pfnProgress,
12751 : void *pProgressData)
12752 : {
12753 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12754 0 : return hArray->m_poImpl->ComputeStatistics(
12755 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12756 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
12757 : }
12758 :
12759 : /************************************************************************/
12760 : /* GDALMDArrayComputeStatisticsEx() */
12761 : /************************************************************************/
12762 :
12763 : /**
12764 : * \brief Compute statistics.
12765 : *
12766 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
12767 : *
12768 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12769 : *
12770 : * @since GDAL 3.8
12771 : */
12772 :
12773 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12774 : int bApproxOK, double *pdfMin,
12775 : double *pdfMax, double *pdfMean,
12776 : double *pdfStdDev, GUInt64 *pnValidCount,
12777 : GDALProgressFunc pfnProgress,
12778 : void *pProgressData,
12779 : CSLConstList papszOptions)
12780 : {
12781 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12782 8 : return hArray->m_poImpl->ComputeStatistics(
12783 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12784 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
12785 : }
12786 :
12787 : /************************************************************************/
12788 : /* GDALMDArrayGetCoordinateVariables() */
12789 : /************************************************************************/
12790 :
12791 : /** Return coordinate variables.
12792 : *
12793 : * The returned array must be freed with GDALReleaseArrays(). If only the array
12794 : * itself needs to be freed, CPLFree() should be called (and
12795 : * GDALMDArrayRelease() on individual array members).
12796 : *
12797 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
12798 : *
12799 : * @param hArray Array.
12800 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12801 : *
12802 : * @return an array of *pnCount arrays.
12803 : * @since 3.4
12804 : */
12805 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
12806 : size_t *pnCount)
12807 : {
12808 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12809 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12810 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
12811 : auto ret = static_cast<GDALMDArrayH *>(
12812 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
12813 29 : for (size_t i = 0; i < coordinates.size(); i++)
12814 : {
12815 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
12816 : }
12817 13 : *pnCount = coordinates.size();
12818 13 : return ret;
12819 : }
12820 :
12821 : /************************************************************************/
12822 : /* GDALMDArrayGetGridded() */
12823 : /************************************************************************/
12824 :
12825 : /** Return a gridded array from scattered point data, that is from an array
12826 : * whose last dimension is the indexing variable of X and Y arrays.
12827 : *
12828 : * The returned object should be released with GDALMDArrayRelease().
12829 : *
12830 : * This is the same as the C++ method GDALMDArray::GetGridded().
12831 : *
12832 : * @since GDAL 3.7
12833 : */
12834 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
12835 : const char *pszGridOptions,
12836 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
12837 : CSLConstList papszOptions)
12838 : {
12839 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12840 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
12841 22 : auto gridded = hArray->m_poImpl->GetGridded(
12842 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
12843 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
12844 22 : if (!gridded)
12845 19 : return nullptr;
12846 3 : return new GDALMDArrayHS(gridded);
12847 : }
12848 :
12849 : /************************************************************************/
12850 : /* GDALMDArrayGetMeshGrid() */
12851 : /************************************************************************/
12852 :
12853 : /** Return a list of multidimensional arrays from a list of one-dimensional
12854 : * arrays.
12855 : *
12856 : * This is typically used to transform one-dimensional longitude, latitude
12857 : * arrays into 2D ones.
12858 : *
12859 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
12860 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
12861 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
12862 : * repeated to fill the matrix along the first dimension for x1, the second
12863 : * for x2 and so on.
12864 : *
12865 : * For example, if x = [1, 2], and y = [3, 4, 5],
12866 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
12867 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
12868 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
12869 : *
12870 : * and
12871 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
12872 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
12873 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
12874 : *
12875 : * The currently supported options are:
12876 : * <ul>
12877 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
12878 : * output.
12879 : * </li>
12880 : * </ul>
12881 : *
12882 : * This is the same as
12883 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
12884 : * function.
12885 : *
12886 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
12887 : * If only the array itself needs to be freed, CPLFree() should be called
12888 : * (and GDALMDArrayRelease() on individual array members).
12889 : *
12890 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
12891 : *
12892 : * @param pahInputArrays Input arrays
12893 : * @param nCountInputArrays Number of input arrays
12894 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
12895 : * @param papszOptions NULL, or NULL terminated list of options.
12896 : *
12897 : * @return an array of *pnCountOutputArrays arrays.
12898 : * @since 3.10
12899 : */
12900 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
12901 : size_t nCountInputArrays,
12902 : size_t *pnCountOutputArrays,
12903 : CSLConstList papszOptions)
12904 : {
12905 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
12906 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
12907 :
12908 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
12909 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
12910 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
12911 :
12912 : const auto apoOutputArrays =
12913 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
12914 : auto ret = static_cast<GDALMDArrayH *>(
12915 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
12916 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
12917 : {
12918 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
12919 : }
12920 7 : *pnCountOutputArrays = apoOutputArrays.size();
12921 7 : return ret;
12922 : }
12923 :
12924 : /************************************************************************/
12925 : /* GDALReleaseArrays() */
12926 : /************************************************************************/
12927 :
12928 : /** Free the return of GDALMDArrayGetCoordinateVariables()
12929 : *
12930 : * @param arrays return pointer of above methods
12931 : * @param nCount *pnCount value returned by above methods
12932 : */
12933 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
12934 : {
12935 46 : for (size_t i = 0; i < nCount; i++)
12936 : {
12937 26 : delete arrays[i];
12938 : }
12939 20 : CPLFree(arrays);
12940 20 : }
12941 :
12942 : /************************************************************************/
12943 : /* GDALMDArrayCache() */
12944 : /************************************************************************/
12945 :
12946 : /**
12947 : * \brief Cache the content of the array into an auxiliary filename.
12948 : *
12949 : * This is the same as the C++ method GDALMDArray::Cache().
12950 : *
12951 : * @since GDAL 3.4
12952 : */
12953 :
12954 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
12955 : {
12956 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12957 7 : return hArray->m_poImpl->Cache(papszOptions);
12958 : }
12959 :
12960 : /************************************************************************/
12961 : /* GDALMDArrayRename() */
12962 : /************************************************************************/
12963 :
12964 : /** Rename the array.
12965 : *
12966 : * This is not implemented by all drivers.
12967 : *
12968 : * Drivers known to implement it: MEM, netCDF, Zarr.
12969 : *
12970 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
12971 : *
12972 : * @return true in case of success
12973 : * @since GDAL 3.8
12974 : */
12975 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
12976 : {
12977 28 : VALIDATE_POINTER1(hArray, __func__, false);
12978 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
12979 28 : return hArray->m_poImpl->Rename(pszNewName);
12980 : }
12981 :
12982 : /************************************************************************/
12983 : /* GDALAttributeRelease() */
12984 : /************************************************************************/
12985 :
12986 : /** Release the GDAL in-memory object associated with a GDALAttribute.
12987 : *
12988 : * Note: when applied on a object coming from a driver, this does not
12989 : * destroy the object in the file, database, etc...
12990 : */
12991 720 : void GDALAttributeRelease(GDALAttributeH hAttr)
12992 : {
12993 720 : delete hAttr;
12994 720 : }
12995 :
12996 : /************************************************************************/
12997 : /* GDALAttributeGetName() */
12998 : /************************************************************************/
12999 :
13000 : /** Return the name of the attribute.
13001 : *
13002 : * The returned pointer is valid until hAttr is released.
13003 : *
13004 : * This is the same as the C++ method GDALAttribute::GetName().
13005 : */
13006 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13007 : {
13008 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13009 361 : return hAttr->m_poImpl->GetName().c_str();
13010 : }
13011 :
13012 : /************************************************************************/
13013 : /* GDALAttributeGetFullName() */
13014 : /************************************************************************/
13015 :
13016 : /** Return the full name of the attribute.
13017 : *
13018 : * The returned pointer is valid until hAttr is released.
13019 : *
13020 : * This is the same as the C++ method GDALAttribute::GetFullName().
13021 : */
13022 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13023 : {
13024 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13025 49 : return hAttr->m_poImpl->GetFullName().c_str();
13026 : }
13027 :
13028 : /************************************************************************/
13029 : /* GDALAttributeGetTotalElementsCount() */
13030 : /************************************************************************/
13031 :
13032 : /** Return the total number of values in the attribute.
13033 : *
13034 : * This is the same as the C++ method
13035 : * GDALAbstractMDArray::GetTotalElementsCount()
13036 : */
13037 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13038 : {
13039 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13040 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13041 : }
13042 :
13043 : /************************************************************************/
13044 : /* GDALAttributeGetDimensionCount() */
13045 : /************************************************************************/
13046 :
13047 : /** Return the number of dimensions.
13048 : *
13049 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13050 : */
13051 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13052 : {
13053 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13054 12 : return hAttr->m_poImpl->GetDimensionCount();
13055 : }
13056 :
13057 : /************************************************************************/
13058 : /* GDALAttributeGetDimensionsSize() */
13059 : /************************************************************************/
13060 :
13061 : /** Return the dimension sizes of the attribute.
13062 : *
13063 : * The returned array must be freed with CPLFree()
13064 : *
13065 : * @param hAttr Attribute.
13066 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13067 : *
13068 : * @return an array of *pnCount values.
13069 : */
13070 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13071 : {
13072 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13073 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13074 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13075 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13076 22 : for (size_t i = 0; i < dims.size(); i++)
13077 : {
13078 11 : ret[i] = dims[i]->GetSize();
13079 : }
13080 11 : *pnCount = dims.size();
13081 11 : return ret;
13082 : }
13083 :
13084 : /************************************************************************/
13085 : /* GDALAttributeGetDataType() */
13086 : /************************************************************************/
13087 :
13088 : /** Return the data type
13089 : *
13090 : * The return must be freed with GDALExtendedDataTypeRelease().
13091 : */
13092 427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13093 : {
13094 427 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13095 : return new GDALExtendedDataTypeHS(
13096 427 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13097 : }
13098 :
13099 : /************************************************************************/
13100 : /* GDALAttributeReadAsRaw() */
13101 : /************************************************************************/
13102 :
13103 : /** Return the raw value of an attribute.
13104 : *
13105 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13106 : *
13107 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13108 : *
13109 : * @param hAttr Attribute.
13110 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13111 : *
13112 : * @return a buffer of *pnSize bytes.
13113 : */
13114 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13115 : {
13116 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13117 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13118 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13119 6 : *pnSize = res.size();
13120 6 : auto ret = res.StealData();
13121 6 : if (!ret)
13122 : {
13123 0 : *pnSize = 0;
13124 0 : return nullptr;
13125 : }
13126 6 : return ret;
13127 : }
13128 :
13129 : /************************************************************************/
13130 : /* GDALAttributeFreeRawResult() */
13131 : /************************************************************************/
13132 :
13133 : /** Free the return of GDALAttributeAsRaw()
13134 : */
13135 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13136 : CPL_UNUSED size_t nSize)
13137 : {
13138 6 : VALIDATE_POINTER0(hAttr, __func__);
13139 6 : if (raw)
13140 : {
13141 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13142 6 : const auto nDTSize(dt.GetSize());
13143 6 : GByte *pabyPtr = raw;
13144 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13145 6 : CPLAssert(nSize == nDTSize * nEltCount);
13146 12 : for (size_t i = 0; i < nEltCount; ++i)
13147 : {
13148 6 : dt.FreeDynamicMemory(pabyPtr);
13149 6 : pabyPtr += nDTSize;
13150 : }
13151 6 : CPLFree(raw);
13152 : }
13153 : }
13154 :
13155 : /************************************************************************/
13156 : /* GDALAttributeReadAsString() */
13157 : /************************************************************************/
13158 :
13159 : /** Return the value of an attribute as a string.
13160 : *
13161 : * The returned string should not be freed, and its lifetime does not
13162 : * excess a next call to ReadAsString() on the same object, or the deletion
13163 : * of the object itself.
13164 : *
13165 : * This function will only return the first element if there are several.
13166 : *
13167 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13168 : *
13169 : * @return a string, or nullptr.
13170 : */
13171 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13172 : {
13173 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13174 107 : return hAttr->m_poImpl->ReadAsString();
13175 : }
13176 :
13177 : /************************************************************************/
13178 : /* GDALAttributeReadAsInt() */
13179 : /************************************************************************/
13180 :
13181 : /** Return the value of an attribute as a integer.
13182 : *
13183 : * This function will only return the first element if there are several.
13184 : *
13185 : * It can fail if its value can not be converted to integer.
13186 : *
13187 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13188 : *
13189 : * @return a integer, or INT_MIN in case of error.
13190 : */
13191 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13192 : {
13193 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13194 22 : return hAttr->m_poImpl->ReadAsInt();
13195 : }
13196 :
13197 : /************************************************************************/
13198 : /* GDALAttributeReadAsInt64() */
13199 : /************************************************************************/
13200 :
13201 : /** Return the value of an attribute as a int64_t.
13202 : *
13203 : * This function will only return the first element if there are several.
13204 : *
13205 : * It can fail if its value can not be converted to integer.
13206 : *
13207 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13208 : *
13209 : * @return an int64_t, or INT64_MIN in case of error.
13210 : */
13211 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13212 : {
13213 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13214 15 : return hAttr->m_poImpl->ReadAsInt64();
13215 : }
13216 :
13217 : /************************************************************************/
13218 : /* GDALAttributeReadAsDouble() */
13219 : /************************************************************************/
13220 :
13221 : /** Return the value of an attribute as a double.
13222 : *
13223 : * This function will only return the first element if there are several.
13224 : *
13225 : * It can fail if its value can not be converted to double.
13226 : *
13227 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13228 : *
13229 : * @return a double value.
13230 : */
13231 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13232 : {
13233 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13234 40 : return hAttr->m_poImpl->ReadAsDouble();
13235 : }
13236 :
13237 : /************************************************************************/
13238 : /* GDALAttributeReadAsStringArray() */
13239 : /************************************************************************/
13240 :
13241 : /** Return the value of an attribute as an array of strings.
13242 : *
13243 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13244 : *
13245 : * The return value must be freed with CSLDestroy().
13246 : */
13247 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13248 : {
13249 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13250 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13251 : }
13252 :
13253 : /************************************************************************/
13254 : /* GDALAttributeReadAsIntArray() */
13255 : /************************************************************************/
13256 :
13257 : /** Return the value of an attribute as an array of integers.
13258 : *
13259 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13260 : *
13261 : * @param hAttr Attribute
13262 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13263 : * @return array to be freed with CPLFree(), or nullptr.
13264 : */
13265 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13266 : {
13267 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13268 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13269 15 : *pnCount = 0;
13270 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13271 15 : if (tmp.empty())
13272 0 : return nullptr;
13273 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13274 15 : if (!ret)
13275 0 : return nullptr;
13276 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13277 15 : *pnCount = tmp.size();
13278 15 : return ret;
13279 : }
13280 :
13281 : /************************************************************************/
13282 : /* GDALAttributeReadAsInt64Array() */
13283 : /************************************************************************/
13284 :
13285 : /** Return the value of an attribute as an array of int64_t.
13286 : *
13287 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13288 : *
13289 : * @param hAttr Attribute
13290 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13291 : * @return array to be freed with CPLFree(), or nullptr.
13292 : */
13293 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13294 : {
13295 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13296 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13297 14 : *pnCount = 0;
13298 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13299 14 : if (tmp.empty())
13300 0 : return nullptr;
13301 : auto ret = static_cast<int64_t *>(
13302 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13303 14 : if (!ret)
13304 0 : return nullptr;
13305 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13306 14 : *pnCount = tmp.size();
13307 14 : return ret;
13308 : }
13309 :
13310 : /************************************************************************/
13311 : /* GDALAttributeReadAsDoubleArray() */
13312 : /************************************************************************/
13313 :
13314 : /** Return the value of an attribute as an array of doubles.
13315 : *
13316 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13317 : *
13318 : * @param hAttr Attribute
13319 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13320 : * @return array to be freed with CPLFree(), or nullptr.
13321 : */
13322 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13323 : {
13324 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13325 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13326 29 : *pnCount = 0;
13327 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13328 29 : if (tmp.empty())
13329 0 : return nullptr;
13330 : auto ret =
13331 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13332 29 : if (!ret)
13333 0 : return nullptr;
13334 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13335 29 : *pnCount = tmp.size();
13336 29 : return ret;
13337 : }
13338 :
13339 : /************************************************************************/
13340 : /* GDALAttributeWriteRaw() */
13341 : /************************************************************************/
13342 :
13343 : /** Write an attribute from raw values expressed in GetDataType()
13344 : *
13345 : * The values should be provided in the type of GetDataType() and there should
13346 : * be exactly GetTotalElementsCount() of them.
13347 : * If GetDataType() is a string, each value should be a char* pointer.
13348 : *
13349 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13350 : *
13351 : * @param hAttr Attribute
13352 : * @param pabyValue Buffer of nLen bytes.
13353 : * @param nLength Size of pabyValue in bytes. Should be equal to
13354 : * GetTotalElementsCount() * GetDataType().GetSize()
13355 : * @return TRUE in case of success.
13356 : */
13357 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13358 : size_t nLength)
13359 : {
13360 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13361 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13362 : }
13363 :
13364 : /************************************************************************/
13365 : /* GDALAttributeWriteString() */
13366 : /************************************************************************/
13367 :
13368 : /** Write an attribute from a string value.
13369 : *
13370 : * Type conversion will be performed if needed. If the attribute contains
13371 : * multiple values, only the first one will be updated.
13372 : *
13373 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13374 : *
13375 : * @param hAttr Attribute
13376 : * @param pszVal Pointer to a string.
13377 : * @return TRUE in case of success.
13378 : */
13379 175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13380 : {
13381 175 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13382 175 : return hAttr->m_poImpl->Write(pszVal);
13383 : }
13384 :
13385 : /************************************************************************/
13386 : /* GDALAttributeWriteInt() */
13387 : /************************************************************************/
13388 :
13389 : /** Write an attribute from a integer value.
13390 : *
13391 : * Type conversion will be performed if needed. If the attribute contains
13392 : * multiple values, only the first one will be updated.
13393 : *
13394 : * This is the same as the C++ method GDALAttribute::WriteInt()
13395 : *
13396 : * @param hAttr Attribute
13397 : * @param nVal Value.
13398 : * @return TRUE in case of success.
13399 : */
13400 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13401 : {
13402 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13403 22 : return hAttr->m_poImpl->WriteInt(nVal);
13404 : }
13405 :
13406 : /************************************************************************/
13407 : /* GDALAttributeWriteInt64() */
13408 : /************************************************************************/
13409 :
13410 : /** Write an attribute from an int64_t value.
13411 : *
13412 : * Type conversion will be performed if needed. If the attribute contains
13413 : * multiple values, only the first one will be updated.
13414 : *
13415 : * This is the same as the C++ method GDALAttribute::WriteLong()
13416 : *
13417 : * @param hAttr Attribute
13418 : * @param nVal Value.
13419 : * @return TRUE in case of success.
13420 : */
13421 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13422 : {
13423 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13424 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13425 : }
13426 :
13427 : /************************************************************************/
13428 : /* GDALAttributeWriteDouble() */
13429 : /************************************************************************/
13430 :
13431 : /** Write an attribute from a double value.
13432 : *
13433 : * Type conversion will be performed if needed. If the attribute contains
13434 : * multiple values, only the first one will be updated.
13435 : *
13436 : * This is the same as the C++ method GDALAttribute::Write(double);
13437 : *
13438 : * @param hAttr Attribute
13439 : * @param dfVal Value.
13440 : *
13441 : * @return TRUE in case of success.
13442 : */
13443 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13444 : {
13445 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13446 11 : return hAttr->m_poImpl->Write(dfVal);
13447 : }
13448 :
13449 : /************************************************************************/
13450 : /* GDALAttributeWriteStringArray() */
13451 : /************************************************************************/
13452 :
13453 : /** Write an attribute from an array of strings.
13454 : *
13455 : * Type conversion will be performed if needed.
13456 : *
13457 : * Exactly GetTotalElementsCount() strings must be provided
13458 : *
13459 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13460 : *
13461 : * @param hAttr Attribute
13462 : * @param papszValues Array of strings.
13463 : * @return TRUE in case of success.
13464 : */
13465 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13466 : CSLConstList papszValues)
13467 : {
13468 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13469 8 : return hAttr->m_poImpl->Write(papszValues);
13470 : }
13471 :
13472 : /************************************************************************/
13473 : /* GDALAttributeWriteIntArray() */
13474 : /************************************************************************/
13475 :
13476 : /** Write an attribute from an array of int.
13477 : *
13478 : * Type conversion will be performed if needed.
13479 : *
13480 : * Exactly GetTotalElementsCount() strings must be provided
13481 : *
13482 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13483 : * size_t)
13484 : *
13485 : * @param hAttr Attribute
13486 : * @param panValues Array of int.
13487 : * @param nCount Should be equal to GetTotalElementsCount().
13488 : * @return TRUE in case of success.
13489 : */
13490 9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13491 : size_t nCount)
13492 : {
13493 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13494 9 : return hAttr->m_poImpl->Write(panValues, nCount);
13495 : }
13496 :
13497 : /************************************************************************/
13498 : /* GDALAttributeWriteInt64Array() */
13499 : /************************************************************************/
13500 :
13501 : /** Write an attribute from an array of int64_t.
13502 : *
13503 : * Type conversion will be performed if needed.
13504 : *
13505 : * Exactly GetTotalElementsCount() strings must be provided
13506 : *
13507 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13508 : * size_t)
13509 : *
13510 : * @param hAttr Attribute
13511 : * @param panValues Array of int64_t.
13512 : * @param nCount Should be equal to GetTotalElementsCount().
13513 : * @return TRUE in case of success.
13514 : */
13515 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13516 : size_t nCount)
13517 : {
13518 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13519 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13520 : }
13521 :
13522 : /************************************************************************/
13523 : /* GDALAttributeWriteDoubleArray() */
13524 : /************************************************************************/
13525 :
13526 : /** Write an attribute from an array of double.
13527 : *
13528 : * Type conversion will be performed if needed.
13529 : *
13530 : * Exactly GetTotalElementsCount() strings must be provided
13531 : *
13532 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13533 : * size_t)
13534 : *
13535 : * @param hAttr Attribute
13536 : * @param padfValues Array of double.
13537 : * @param nCount Should be equal to GetTotalElementsCount().
13538 : * @return TRUE in case of success.
13539 : */
13540 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
13541 : const double *padfValues, size_t nCount)
13542 : {
13543 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13544 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
13545 : }
13546 :
13547 : /************************************************************************/
13548 : /* GDALAttributeRename() */
13549 : /************************************************************************/
13550 :
13551 : /** Rename the attribute.
13552 : *
13553 : * This is not implemented by all drivers.
13554 : *
13555 : * Drivers known to implement it: MEM, netCDF.
13556 : *
13557 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13558 : *
13559 : * @return true in case of success
13560 : * @since GDAL 3.8
13561 : */
13562 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
13563 : {
13564 27 : VALIDATE_POINTER1(hAttr, __func__, false);
13565 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
13566 27 : return hAttr->m_poImpl->Rename(pszNewName);
13567 : }
13568 :
13569 : /************************************************************************/
13570 : /* GDALDimensionRelease() */
13571 : /************************************************************************/
13572 :
13573 : /** Release the GDAL in-memory object associated with a GDALDimension.
13574 : *
13575 : * Note: when applied on a object coming from a driver, this does not
13576 : * destroy the object in the file, database, etc...
13577 : */
13578 4881 : void GDALDimensionRelease(GDALDimensionH hDim)
13579 : {
13580 4881 : delete hDim;
13581 4881 : }
13582 :
13583 : /************************************************************************/
13584 : /* GDALDimensionGetName() */
13585 : /************************************************************************/
13586 :
13587 : /** Return dimension name.
13588 : *
13589 : * This is the same as the C++ method GDALDimension::GetName()
13590 : */
13591 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
13592 : {
13593 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13594 284 : return hDim->m_poImpl->GetName().c_str();
13595 : }
13596 :
13597 : /************************************************************************/
13598 : /* GDALDimensionGetFullName() */
13599 : /************************************************************************/
13600 :
13601 : /** Return dimension full name.
13602 : *
13603 : * This is the same as the C++ method GDALDimension::GetFullName()
13604 : */
13605 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
13606 : {
13607 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13608 80 : return hDim->m_poImpl->GetFullName().c_str();
13609 : }
13610 :
13611 : /************************************************************************/
13612 : /* GDALDimensionGetType() */
13613 : /************************************************************************/
13614 :
13615 : /** Return dimension type.
13616 : *
13617 : * This is the same as the C++ method GDALDimension::GetType()
13618 : */
13619 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
13620 : {
13621 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13622 62 : return hDim->m_poImpl->GetType().c_str();
13623 : }
13624 :
13625 : /************************************************************************/
13626 : /* GDALDimensionGetDirection() */
13627 : /************************************************************************/
13628 :
13629 : /** Return dimension direction.
13630 : *
13631 : * This is the same as the C++ method GDALDimension::GetDirection()
13632 : */
13633 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
13634 : {
13635 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13636 32 : return hDim->m_poImpl->GetDirection().c_str();
13637 : }
13638 :
13639 : /************************************************************************/
13640 : /* GDALDimensionGetSize() */
13641 : /************************************************************************/
13642 :
13643 : /** Return the size, that is the number of values along the dimension.
13644 : *
13645 : * This is the same as the C++ method GDALDimension::GetSize()
13646 : */
13647 3654 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
13648 : {
13649 3654 : VALIDATE_POINTER1(hDim, __func__, 0);
13650 3654 : return hDim->m_poImpl->GetSize();
13651 : }
13652 :
13653 : /************************************************************************/
13654 : /* GDALDimensionGetIndexingVariable() */
13655 : /************************************************************************/
13656 :
13657 : /** Return the variable that is used to index the dimension (if there is one).
13658 : *
13659 : * This is the array, typically one-dimensional, describing the values taken
13660 : * by the dimension.
13661 : *
13662 : * The returned value should be freed with GDALMDArrayRelease().
13663 : *
13664 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
13665 : */
13666 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
13667 : {
13668 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13669 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
13670 118 : if (!var)
13671 10 : return nullptr;
13672 108 : return new GDALMDArrayHS(var);
13673 : }
13674 :
13675 : /************************************************************************/
13676 : /* GDALDimensionSetIndexingVariable() */
13677 : /************************************************************************/
13678 :
13679 : /** Set the variable that is used to index the dimension.
13680 : *
13681 : * This is the array, typically one-dimensional, describing the values taken
13682 : * by the dimension.
13683 : *
13684 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
13685 : *
13686 : * @return TRUE in case of success.
13687 : */
13688 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
13689 : {
13690 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
13691 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
13692 46 : : nullptr);
13693 : }
13694 :
13695 : /************************************************************************/
13696 : /* GDALDimensionRename() */
13697 : /************************************************************************/
13698 :
13699 : /** Rename the dimension.
13700 : *
13701 : * This is not implemented by all drivers.
13702 : *
13703 : * Drivers known to implement it: MEM, netCDF.
13704 : *
13705 : * This is the same as the C++ method GDALDimension::Rename()
13706 : *
13707 : * @return true in case of success
13708 : * @since GDAL 3.8
13709 : */
13710 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
13711 : {
13712 31 : VALIDATE_POINTER1(hDim, __func__, false);
13713 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
13714 31 : return hDim->m_poImpl->Rename(pszNewName);
13715 : }
13716 :
13717 : /************************************************************************/
13718 : /* GDALDatasetGetRootGroup() */
13719 : /************************************************************************/
13720 :
13721 : /** Return the root GDALGroup of this dataset.
13722 : *
13723 : * Only valid for multidimensional datasets.
13724 : *
13725 : * The returned value must be freed with GDALGroupRelease().
13726 : *
13727 : * This is the same as the C++ method GDALDataset::GetRootGroup().
13728 : *
13729 : * @since GDAL 3.1
13730 : */
13731 1145 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
13732 : {
13733 1145 : VALIDATE_POINTER1(hDS, __func__, nullptr);
13734 1145 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
13735 1145 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
13736 : }
13737 :
13738 : /************************************************************************/
13739 : /* GDALRasterBandAsMDArray() */
13740 : /************************************************************************/
13741 :
13742 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
13743 : *
13744 : * The band must be linked to a GDALDataset. If this dataset is not already
13745 : * marked as shared, it will be, so that the returned array holds a reference
13746 : * to it.
13747 : *
13748 : * If the dataset has a geotransform attached, the X and Y dimensions of the
13749 : * returned array will have an associated indexing variable.
13750 : *
13751 : * The returned pointer must be released with GDALMDArrayRelease().
13752 : *
13753 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
13754 : *
13755 : * @return a new array, or NULL.
13756 : *
13757 : * @since GDAL 3.1
13758 : */
13759 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
13760 : {
13761 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
13762 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
13763 21 : if (!poArray)
13764 0 : return nullptr;
13765 21 : return new GDALMDArrayHS(poArray);
13766 : }
13767 :
13768 : /************************************************************************/
13769 : /* GDALMDArrayAsClassicDataset() */
13770 : /************************************************************************/
13771 :
13772 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13773 : *
13774 : * Only 2D or more arrays are supported.
13775 : *
13776 : * In the case of > 2D arrays, additional dimensions will be represented as
13777 : * raster bands.
13778 : *
13779 : * The "reverse" method is GDALRasterBand::AsMDArray().
13780 : *
13781 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13782 : *
13783 : * @param hArray Array.
13784 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13785 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13786 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13787 : */
13788 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
13789 : size_t iYDim)
13790 : {
13791 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13792 0 : return GDALDataset::ToHandle(
13793 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
13794 : }
13795 :
13796 : /************************************************************************/
13797 : /* GDALMDArrayAsClassicDatasetEx() */
13798 : /************************************************************************/
13799 :
13800 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13801 : *
13802 : * Only 2D or more arrays are supported.
13803 : *
13804 : * In the case of > 2D arrays, additional dimensions will be represented as
13805 : * raster bands.
13806 : *
13807 : * The "reverse" method is GDALRasterBand::AsMDArray().
13808 : *
13809 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13810 : * @param hArray Array.
13811 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13812 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13813 : * Ignored if the dimension count is 1.
13814 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
13815 : * BAND_IMAGERY_METADATA option.
13816 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
13817 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13818 : * @since GDAL 3.8
13819 : */
13820 71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
13821 : size_t iYDim, GDALGroupH hRootGroup,
13822 : CSLConstList papszOptions)
13823 : {
13824 71 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13825 142 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
13826 142 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
13827 142 : papszOptions));
13828 : }
13829 :
13830 : //! @cond Doxygen_Suppress
13831 :
13832 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
13833 : const std::string &osName,
13834 : const std::string &osValue,
13835 180 : GDALExtendedDataTypeSubType eSubType)
13836 : : GDALAbstractMDArray(osParentName, osName),
13837 : GDALAttribute(osParentName, osName),
13838 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
13839 : {
13840 180 : }
13841 :
13842 : const std::vector<std::shared_ptr<GDALDimension>> &
13843 30 : GDALAttributeString::GetDimensions() const
13844 : {
13845 30 : return m_dims;
13846 : }
13847 :
13848 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
13849 : {
13850 21 : return m_dt;
13851 : }
13852 :
13853 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
13854 : const GPtrDiff_t *,
13855 : const GDALExtendedDataType &bufferDataType,
13856 : void *pDstBuffer) const
13857 : {
13858 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
13859 0 : return false;
13860 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
13861 10 : if (!pszStr)
13862 0 : return false;
13863 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
13864 10 : *static_cast<char **>(pDstBuffer) = pszStr;
13865 10 : return true;
13866 : }
13867 :
13868 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13869 : const std::string &osName,
13870 66 : double dfValue)
13871 : : GDALAbstractMDArray(osParentName, osName),
13872 : GDALAttribute(osParentName, osName),
13873 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
13874 : {
13875 66 : }
13876 :
13877 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13878 : const std::string &osName,
13879 27 : int nValue)
13880 : : GDALAbstractMDArray(osParentName, osName),
13881 : GDALAttribute(osParentName, osName),
13882 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
13883 : {
13884 27 : }
13885 :
13886 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13887 : const std::string &osName,
13888 7 : const std::vector<GUInt32> &anValues)
13889 : : GDALAbstractMDArray(osParentName, osName),
13890 : GDALAttribute(osParentName, osName),
13891 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
13892 : {
13893 7 : m_dims.push_back(std::make_shared<GDALDimension>(
13894 14 : std::string(), "dim0", std::string(), std::string(),
13895 7 : m_anValuesUInt32.size()));
13896 7 : }
13897 :
13898 : const std::vector<std::shared_ptr<GDALDimension>> &
13899 14 : GDALAttributeNumeric::GetDimensions() const
13900 : {
13901 14 : return m_dims;
13902 : }
13903 :
13904 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
13905 : {
13906 8 : return m_dt;
13907 : }
13908 :
13909 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
13910 : const size_t *count, const GInt64 *arrayStep,
13911 : const GPtrDiff_t *bufferStride,
13912 : const GDALExtendedDataType &bufferDataType,
13913 : void *pDstBuffer) const
13914 : {
13915 4 : if (m_dims.empty())
13916 : {
13917 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
13918 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
13919 : bufferDataType);
13920 : else
13921 : {
13922 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
13923 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
13924 : bufferDataType);
13925 : }
13926 : }
13927 : else
13928 : {
13929 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
13930 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13931 30 : for (size_t i = 0; i < count[0]; ++i)
13932 : {
13933 29 : GDALExtendedDataType::CopyValue(
13934 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
13935 29 : i * arrayStep[0])],
13936 29 : m_dt, pabyDstBuffer, bufferDataType);
13937 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
13938 : }
13939 : }
13940 4 : return true;
13941 : }
13942 :
13943 192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
13944 : const std::string &osParentName, const std::string &osName,
13945 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13946 192 : double dfIncrement, double dfOffsetInIncrement)
13947 : : GDALAbstractMDArray(osParentName, osName),
13948 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
13949 : m_dfIncrement(dfIncrement),
13950 384 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
13951 : {
13952 192 : }
13953 :
13954 192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
13955 : const std::string &osParentName, const std::string &osName,
13956 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13957 : double dfIncrement, double dfOffsetInIncrement)
13958 : {
13959 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
13960 192 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
13961 192 : poArray->SetSelf(poArray);
13962 192 : return poArray;
13963 : }
13964 :
13965 : const std::vector<std::shared_ptr<GDALDimension>> &
13966 786 : GDALMDArrayRegularlySpaced::GetDimensions() const
13967 : {
13968 786 : return m_dims;
13969 : }
13970 :
13971 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
13972 : {
13973 316 : return m_dt;
13974 : }
13975 :
13976 : std::vector<std::shared_ptr<GDALAttribute>>
13977 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
13978 : {
13979 4 : return m_attributes;
13980 : }
13981 :
13982 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
13983 : const std::shared_ptr<GDALAttribute> &poAttr)
13984 : {
13985 0 : m_attributes.emplace_back(poAttr);
13986 0 : }
13987 :
13988 188 : bool GDALMDArrayRegularlySpaced::IRead(
13989 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
13990 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
13991 : void *pDstBuffer) const
13992 : {
13993 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13994 14719 : for (size_t i = 0; i < count[0]; i++)
13995 : {
13996 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
13997 14531 : m_dfOffsetInIncrement) *
13998 14531 : m_dfIncrement;
13999 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14000 : bufferDataType);
14001 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14002 : }
14003 188 : return true;
14004 : }
14005 :
14006 2950 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14007 : const std::string &osParentName, const std::string &osName,
14008 2950 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14009 2950 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14010 : {
14011 2950 : }
14012 :
14013 : std::shared_ptr<GDALMDArray>
14014 706 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14015 : {
14016 706 : return m_poIndexingVariable.lock();
14017 : }
14018 :
14019 : // cppcheck-suppress passedByValue
14020 488 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14021 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14022 : {
14023 488 : m_poIndexingVariable = poIndexingVariable;
14024 488 : return true;
14025 : }
14026 :
14027 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14028 : {
14029 33 : m_nSize = nNewSize;
14030 33 : }
14031 :
14032 : /************************************************************************/
14033 : /* GDALPamMultiDim::Private */
14034 : /************************************************************************/
14035 :
14036 : struct GDALPamMultiDim::Private
14037 : {
14038 : std::string m_osFilename{};
14039 : std::string m_osPamFilename{};
14040 :
14041 : struct Statistics
14042 : {
14043 : bool bHasStats = false;
14044 : bool bApproxStats = false;
14045 : double dfMin = 0;
14046 : double dfMax = 0;
14047 : double dfMean = 0;
14048 : double dfStdDev = 0;
14049 : GUInt64 nValidCount = 0;
14050 : };
14051 :
14052 : struct ArrayInfo
14053 : {
14054 : std::shared_ptr<OGRSpatialReference> poSRS{};
14055 : // cppcheck-suppress unusedStructMember
14056 : Statistics stats{};
14057 : };
14058 :
14059 : typedef std::pair<std::string, std::string> NameContext;
14060 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14061 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14062 : bool m_bDirty = false;
14063 : bool m_bLoaded = false;
14064 : };
14065 :
14066 : /************************************************************************/
14067 : /* GDALPamMultiDim */
14068 : /************************************************************************/
14069 :
14070 1388 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14071 1388 : : d(new Private())
14072 : {
14073 1388 : d->m_osFilename = osFilename;
14074 1388 : }
14075 :
14076 : /************************************************************************/
14077 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14078 : /************************************************************************/
14079 :
14080 1388 : GDALPamMultiDim::~GDALPamMultiDim()
14081 : {
14082 1388 : if (d->m_bDirty)
14083 29 : Save();
14084 1388 : }
14085 :
14086 : /************************************************************************/
14087 : /* GDALPamMultiDim::Load() */
14088 : /************************************************************************/
14089 :
14090 101 : void GDALPamMultiDim::Load()
14091 : {
14092 101 : if (d->m_bLoaded)
14093 90 : return;
14094 44 : d->m_bLoaded = true;
14095 :
14096 44 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14097 44 : d->m_osPamFilename =
14098 88 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14099 44 : CPLXMLTreeCloser oTree(nullptr);
14100 : {
14101 88 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14102 44 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14103 : }
14104 44 : if (!oTree)
14105 : {
14106 33 : return;
14107 : }
14108 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14109 11 : if (!poPAMMultiDim)
14110 0 : return;
14111 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14112 24 : psIter = psIter->psNext)
14113 : {
14114 24 : if (psIter->eType == CXT_Element &&
14115 24 : strcmp(psIter->pszValue, "Array") == 0)
14116 : {
14117 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14118 13 : if (!pszName)
14119 0 : continue;
14120 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14121 : const auto oKey =
14122 26 : std::pair<std::string, std::string>(pszName, pszContext);
14123 :
14124 : /* --------------------------------------------------------------------
14125 : */
14126 : /* Check for an SRS node. */
14127 : /* --------------------------------------------------------------------
14128 : */
14129 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14130 13 : if (psSRSNode)
14131 : {
14132 : std::shared_ptr<OGRSpatialReference> poSRS =
14133 6 : std::make_shared<OGRSpatialReference>();
14134 3 : poSRS->SetFromUserInput(
14135 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14136 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14137 3 : const char *pszMapping = CPLGetXMLValue(
14138 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14139 3 : if (pszMapping)
14140 : {
14141 : char **papszTokens =
14142 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14143 6 : std::vector<int> anMapping;
14144 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14145 : {
14146 6 : anMapping.push_back(atoi(papszTokens[i]));
14147 : }
14148 3 : CSLDestroy(papszTokens);
14149 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14150 : }
14151 : else
14152 : {
14153 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14154 : }
14155 :
14156 : const char *pszCoordinateEpoch =
14157 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14158 3 : if (pszCoordinateEpoch)
14159 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14160 :
14161 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14162 : }
14163 :
14164 : const CPLXMLNode *psStatistics =
14165 13 : CPLGetXMLNode(psIter, "Statistics");
14166 13 : if (psStatistics)
14167 : {
14168 7 : Private::Statistics sStats;
14169 7 : sStats.bHasStats = true;
14170 7 : sStats.bApproxStats = CPLTestBool(
14171 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14172 7 : sStats.dfMin =
14173 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14174 7 : sStats.dfMax =
14175 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14176 7 : sStats.dfMean =
14177 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14178 7 : sStats.dfStdDev =
14179 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14180 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14181 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14182 7 : d->m_oMapArray[oKey].stats = sStats;
14183 13 : }
14184 : }
14185 : else
14186 : {
14187 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14188 11 : psIter->psNext = nullptr;
14189 11 : d->m_apoOtherNodes.emplace_back(
14190 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14191 11 : psIter->psNext = psNextBackup;
14192 : }
14193 : }
14194 : }
14195 :
14196 : /************************************************************************/
14197 : /* GDALPamMultiDim::Save() */
14198 : /************************************************************************/
14199 :
14200 29 : void GDALPamMultiDim::Save()
14201 : {
14202 : CPLXMLTreeCloser oTree(
14203 58 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14204 33 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14205 : {
14206 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14207 : }
14208 108 : for (const auto &kv : d->m_oMapArray)
14209 : {
14210 : CPLXMLNode *psArrayNode =
14211 79 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14212 79 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14213 79 : if (!kv.first.second.empty())
14214 : {
14215 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14216 : kv.first.second.c_str());
14217 : }
14218 79 : if (kv.second.poSRS)
14219 : {
14220 71 : char *pszWKT = nullptr;
14221 : {
14222 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14223 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14224 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14225 : }
14226 : CPLXMLNode *psSRSNode =
14227 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14228 71 : CPLFree(pszWKT);
14229 : const auto &mapping =
14230 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14231 142 : CPLString osMapping;
14232 213 : for (size_t i = 0; i < mapping.size(); ++i)
14233 : {
14234 142 : if (!osMapping.empty())
14235 71 : osMapping += ",";
14236 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14237 : }
14238 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14239 : osMapping.c_str());
14240 :
14241 : const double dfCoordinateEpoch =
14242 71 : kv.second.poSRS->GetCoordinateEpoch();
14243 71 : if (dfCoordinateEpoch > 0)
14244 : {
14245 : std::string osCoordinateEpoch =
14246 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14247 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14248 : {
14249 6 : while (osCoordinateEpoch.back() == '0')
14250 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14251 : }
14252 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14253 : osCoordinateEpoch.c_str());
14254 : }
14255 : }
14256 :
14257 79 : if (kv.second.stats.bHasStats)
14258 : {
14259 : CPLXMLNode *psMDArray =
14260 5 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14261 5 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14262 5 : kv.second.stats.bApproxStats ? "1"
14263 : : "0");
14264 5 : CPLCreateXMLElementAndValue(
14265 : psMDArray, "Minimum",
14266 5 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14267 5 : CPLCreateXMLElementAndValue(
14268 : psMDArray, "Maximum",
14269 5 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14270 5 : CPLCreateXMLElementAndValue(
14271 5 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14272 5 : CPLCreateXMLElementAndValue(
14273 : psMDArray, "StdDev",
14274 5 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14275 5 : CPLCreateXMLElementAndValue(
14276 : psMDArray, "ValidSampleCount",
14277 5 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14278 : }
14279 : }
14280 :
14281 : int bSaved;
14282 58 : CPLErrorAccumulator oErrorAccumulator;
14283 : {
14284 29 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14285 29 : CPL_IGNORE_RET_VAL(oAccumulator);
14286 : bSaved =
14287 29 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14288 : }
14289 :
14290 29 : const char *pszNewPam = nullptr;
14291 29 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14292 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14293 : {
14294 0 : CPLErrorReset();
14295 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14296 : }
14297 : else
14298 : {
14299 29 : oErrorAccumulator.ReplayErrors();
14300 : }
14301 29 : }
14302 :
14303 : /************************************************************************/
14304 : /* GDALPamMultiDim::GetSpatialRef() */
14305 : /************************************************************************/
14306 :
14307 : std::shared_ptr<OGRSpatialReference>
14308 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14309 : const std::string &osContext)
14310 : {
14311 10 : Load();
14312 : auto oIter =
14313 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14314 10 : if (oIter != d->m_oMapArray.end())
14315 2 : return oIter->second.poSRS;
14316 8 : return nullptr;
14317 : }
14318 :
14319 : /************************************************************************/
14320 : /* GDALPamMultiDim::SetSpatialRef() */
14321 : /************************************************************************/
14322 :
14323 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14324 : const std::string &osContext,
14325 : const OGRSpatialReference *poSRS)
14326 : {
14327 72 : Load();
14328 72 : d->m_bDirty = true;
14329 72 : if (poSRS && !poSRS->IsEmpty())
14330 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14331 : poSRS->Clone());
14332 : else
14333 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14334 1 : .poSRS.reset();
14335 72 : }
14336 :
14337 : /************************************************************************/
14338 : /* GetStatistics() */
14339 : /************************************************************************/
14340 :
14341 13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14342 : const std::string &osContext,
14343 : bool bApproxOK, double *pdfMin,
14344 : double *pdfMax, double *pdfMean,
14345 : double *pdfStdDev, GUInt64 *pnValidCount)
14346 : {
14347 13 : Load();
14348 : auto oIter =
14349 13 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14350 13 : if (oIter == d->m_oMapArray.end())
14351 6 : return CE_Failure;
14352 7 : const auto &stats = oIter->second.stats;
14353 7 : if (!stats.bHasStats)
14354 1 : return CE_Failure;
14355 6 : if (!bApproxOK && stats.bApproxStats)
14356 0 : return CE_Failure;
14357 6 : if (pdfMin)
14358 6 : *pdfMin = stats.dfMin;
14359 6 : if (pdfMax)
14360 6 : *pdfMax = stats.dfMax;
14361 6 : if (pdfMean)
14362 6 : *pdfMean = stats.dfMean;
14363 6 : if (pdfStdDev)
14364 6 : *pdfStdDev = stats.dfStdDev;
14365 6 : if (pnValidCount)
14366 6 : *pnValidCount = stats.nValidCount;
14367 6 : return CE_None;
14368 : }
14369 :
14370 : /************************************************************************/
14371 : /* SetStatistics() */
14372 : /************************************************************************/
14373 :
14374 5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14375 : const std::string &osContext,
14376 : bool bApproxStats, double dfMin,
14377 : double dfMax, double dfMean,
14378 : double dfStdDev, GUInt64 nValidCount)
14379 : {
14380 5 : Load();
14381 5 : d->m_bDirty = true;
14382 : auto &stats =
14383 5 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14384 5 : stats.bHasStats = true;
14385 5 : stats.bApproxStats = bApproxStats;
14386 5 : stats.dfMin = dfMin;
14387 5 : stats.dfMax = dfMax;
14388 5 : stats.dfMean = dfMean;
14389 5 : stats.dfStdDev = dfStdDev;
14390 5 : stats.nValidCount = nValidCount;
14391 5 : }
14392 :
14393 : /************************************************************************/
14394 : /* ClearStatistics() */
14395 : /************************************************************************/
14396 :
14397 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14398 : const std::string &osContext)
14399 : {
14400 0 : Load();
14401 0 : d->m_bDirty = true;
14402 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14403 : false;
14404 0 : }
14405 :
14406 : /************************************************************************/
14407 : /* ClearStatistics() */
14408 : /************************************************************************/
14409 :
14410 1 : void GDALPamMultiDim::ClearStatistics()
14411 : {
14412 1 : Load();
14413 1 : d->m_bDirty = true;
14414 3 : for (auto &kv : d->m_oMapArray)
14415 2 : kv.second.stats.bHasStats = false;
14416 1 : }
14417 :
14418 : /************************************************************************/
14419 : /* GetPAM() */
14420 : /************************************************************************/
14421 :
14422 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14423 784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14424 : {
14425 784 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14426 784 : if (poPamArray)
14427 563 : return poPamArray->GetPAM();
14428 221 : return nullptr;
14429 : }
14430 :
14431 : /************************************************************************/
14432 : /* GDALPamMDArray */
14433 : /************************************************************************/
14434 :
14435 3663 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14436 : const std::string &osName,
14437 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14438 0 : const std::string &osContext)
14439 : :
14440 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14441 : GDALAbstractMDArray(osParentName, osName),
14442 : #endif
14443 3663 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14444 : {
14445 3663 : }
14446 :
14447 : /************************************************************************/
14448 : /* GDALPamMDArray::SetSpatialRef() */
14449 : /************************************************************************/
14450 :
14451 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14452 : {
14453 72 : if (!m_poPam)
14454 0 : return false;
14455 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14456 72 : return true;
14457 : }
14458 :
14459 : /************************************************************************/
14460 : /* GDALPamMDArray::GetSpatialRef() */
14461 : /************************************************************************/
14462 :
14463 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14464 : {
14465 10 : if (!m_poPam)
14466 0 : return nullptr;
14467 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14468 : }
14469 :
14470 : /************************************************************************/
14471 : /* GetStatistics() */
14472 : /************************************************************************/
14473 :
14474 13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14475 : double *pdfMin, double *pdfMax,
14476 : double *pdfMean, double *pdfStdDev,
14477 : GUInt64 *pnValidCount,
14478 : GDALProgressFunc pfnProgress,
14479 : void *pProgressData)
14480 : {
14481 13 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14482 : bApproxOK, pdfMin, pdfMax, pdfMean,
14483 13 : pdfStdDev, pnValidCount) == CE_None)
14484 : {
14485 6 : return CE_None;
14486 : }
14487 7 : if (!bForce)
14488 4 : return CE_Warning;
14489 :
14490 3 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14491 : pdfMean, pdfStdDev, pnValidCount,
14492 3 : pfnProgress, pProgressData);
14493 : }
14494 :
14495 : /************************************************************************/
14496 : /* SetStatistics() */
14497 : /************************************************************************/
14498 :
14499 5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14500 : double dfMax, double dfMean, double dfStdDev,
14501 : GUInt64 nValidCount,
14502 : CSLConstList /* papszOptions */)
14503 : {
14504 5 : if (!m_poPam)
14505 0 : return false;
14506 5 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14507 : dfMax, dfMean, dfStdDev, nValidCount);
14508 5 : return true;
14509 : }
14510 :
14511 : /************************************************************************/
14512 : /* ClearStatistics() */
14513 : /************************************************************************/
14514 :
14515 0 : void GDALPamMDArray::ClearStatistics()
14516 : {
14517 0 : if (!m_poPam)
14518 0 : return;
14519 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14520 : }
14521 :
14522 : //! @endcond
|