Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Name: gdalmultidim.cpp
5 : * Project: GDAL Core
6 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
7 : * Author: Even Rouault <even.rouault at spatialys.com>
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include <assert.h>
16 : #include <algorithm>
17 : #include <limits>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "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 : GDALCopyWords(&dfOverriddenDstNodata, GDT_Float64, 0,
71 13 : m_abyRawNoData.data(), eNonComplexDT,
72 : 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 6670 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
334 6670 : const std::string &osContext)
335 6670 : : m_osName(osParentName.empty() ? "/" : osName),
336 : m_osFullName(
337 13340 : !osParentName.empty()
338 10244 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
339 : : "/"),
340 16914 : m_osContext(osContext)
341 : {
342 6670 : }
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 2 : (srcArrayType.GetNumericDataType() == GDT_Float32 ||
985 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
986 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
987 43 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
988 41 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
989 : {
990 2 : constexpr bool bApproxOK = false;
991 2 : constexpr bool bForce = true;
992 2 : double dfMin = 0.0;
993 2 : double dfMax = 0.0;
994 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
995 : nullptr, nullptr, nullptr, nullptr,
996 2 : nullptr) != CE_None)
997 : {
998 0 : CPLError(CE_Failure, CPLE_AppDefined,
999 : "Could not retrieve statistics for array %s",
1000 0 : srcArray->GetName().c_str());
1001 0 : return false;
1002 : }
1003 2 : double dfDTMin = 0;
1004 2 : double dfDTMax = 0;
1005 : #define setDTMinMax(ctype) \
1006 : do \
1007 : { \
1008 : dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min()); \
1009 : dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max()); \
1010 : } while (0)
1011 :
1012 2 : switch (eAutoScaleType)
1013 : {
1014 0 : case GDT_Byte:
1015 0 : setDTMinMax(GByte);
1016 0 : break;
1017 0 : case GDT_Int8:
1018 0 : setDTMinMax(GInt8);
1019 0 : break;
1020 1 : case GDT_UInt16:
1021 1 : setDTMinMax(GUInt16);
1022 1 : break;
1023 1 : case GDT_Int16:
1024 1 : setDTMinMax(GInt16);
1025 1 : break;
1026 0 : case GDT_UInt32:
1027 0 : setDTMinMax(GUInt32);
1028 0 : break;
1029 0 : case GDT_Int32:
1030 0 : setDTMinMax(GInt32);
1031 0 : break;
1032 0 : case GDT_UInt64:
1033 0 : setDTMinMax(std::uint64_t);
1034 0 : break;
1035 0 : case GDT_Int64:
1036 0 : setDTMinMax(std::int64_t);
1037 0 : break;
1038 0 : case GDT_Float32:
1039 : case GDT_Float64:
1040 : case GDT_Unknown:
1041 : case GDT_CInt16:
1042 : case GDT_CInt32:
1043 : case GDT_CFloat32:
1044 : case GDT_CFloat64:
1045 : case GDT_TypeCount:
1046 0 : CPLAssert(false);
1047 : }
1048 :
1049 : dstArray =
1050 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1051 4 : GDALExtendedDataType::Create(eAutoScaleType),
1052 4 : aosArrayCO.List());
1053 2 : if (!dstArray)
1054 0 : return !bStrict;
1055 :
1056 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1057 : {
1058 : // If there's a nodata value in the source array, reserve
1059 : // DTMax for that purpose in the target scaled array
1060 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1061 : {
1062 0 : CPLError(CE_Failure, CPLE_AppDefined,
1063 : "Cannot set nodata value");
1064 0 : return false;
1065 : }
1066 1 : dfDTMax--;
1067 : }
1068 2 : const double dfScale =
1069 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1070 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1071 :
1072 4 : if (!dstArray->SetOffset(dfOffset) ||
1073 2 : !dstArray->SetScale(dfScale))
1074 : {
1075 0 : CPLError(CE_Failure, CPLE_AppDefined,
1076 : "Cannot set scale/offset");
1077 0 : return false;
1078 : }
1079 :
1080 2 : auto poUnscaled = dstArray->GetUnscaled();
1081 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1082 : {
1083 1 : poUnscaled->SetNoDataValue(
1084 : srcArray->GetNoDataValueAsDouble());
1085 : }
1086 :
1087 : // Copy source array into unscaled array
1088 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1089 : nCurCost, nTotalCost, pfnProgress,
1090 2 : pProgressData))
1091 : {
1092 0 : return false;
1093 : }
1094 : }
1095 : else
1096 : {
1097 74 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1098 74 : srcArrayType, aosArrayCO.List());
1099 37 : if (!dstArray)
1100 0 : return !bStrict;
1101 :
1102 74 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1103 : nCurCost, nTotalCost, pfnProgress,
1104 37 : pProgressData))
1105 : {
1106 0 : return false;
1107 : }
1108 : }
1109 :
1110 : // If this array is the indexing variable of a dimension, link them
1111 : // together.
1112 39 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1113 : {
1114 : auto oCorrespondingDimIter =
1115 16 : mapExistingDstDims.find(oIterDimName->second);
1116 16 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1117 : {
1118 : CPLErrorStateBackuper oErrorStateBackuper(
1119 16 : CPLQuietErrorHandler);
1120 32 : oCorrespondingDimIter->second->SetIndexingVariable(
1121 16 : std::move(dstArray));
1122 : }
1123 : }
1124 :
1125 39 : return true;
1126 22 : };
1127 :
1128 44 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1129 :
1130 : // Start by copying arrays that are indexing variables of dimensions
1131 61 : for (const auto &name : arrayNames)
1132 : {
1133 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1134 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1135 :
1136 39 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1137 39 : srcArray->GetName()))
1138 : {
1139 16 : if (!CopyArray(srcArray))
1140 0 : return false;
1141 : }
1142 : }
1143 :
1144 : // Then copy regular arrays
1145 61 : for (const auto &name : arrayNames)
1146 : {
1147 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1148 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1149 :
1150 39 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1151 39 : srcArray->GetName()))
1152 : {
1153 23 : if (!CopyArray(srcArray))
1154 0 : return false;
1155 : }
1156 : }
1157 :
1158 44 : const auto groupNames = poSrcGroup->GetGroupNames();
1159 26 : for (const auto &name : groupNames)
1160 : {
1161 4 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1162 4 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1163 4 : auto dstSubGroup = CreateGroup(name);
1164 4 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1165 8 : if (!dstSubGroup->CopyFrom(
1166 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1167 4 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1168 0 : return false;
1169 : }
1170 :
1171 22 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1172 0 : return false;
1173 :
1174 22 : return true;
1175 : }
1176 0 : catch (const std::exception &e)
1177 : {
1178 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1179 0 : return false;
1180 : }
1181 : }
1182 :
1183 : /************************************************************************/
1184 : /* GetInnerMostGroup() */
1185 : /************************************************************************/
1186 :
1187 : //! @cond Doxygen_Suppress
1188 : const GDALGroup *
1189 1038 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1190 : std::shared_ptr<GDALGroup> &curGroupHolder,
1191 : std::string &osLastPart) const
1192 : {
1193 1038 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1194 6 : return nullptr;
1195 1032 : const GDALGroup *poCurGroup = this;
1196 : CPLStringList aosTokens(
1197 2064 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1198 1032 : if (aosTokens.size() == 0)
1199 : {
1200 0 : return nullptr;
1201 : }
1202 :
1203 1367 : for (int i = 0; i < aosTokens.size() - 1; i++)
1204 : {
1205 342 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1206 342 : if (!curGroupHolder)
1207 : {
1208 7 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1209 : aosTokens[i]);
1210 7 : return nullptr;
1211 : }
1212 335 : poCurGroup = curGroupHolder.get();
1213 : }
1214 1025 : osLastPart = aosTokens[aosTokens.size() - 1];
1215 1025 : return poCurGroup;
1216 : }
1217 :
1218 : //! @endcond
1219 :
1220 : /************************************************************************/
1221 : /* OpenMDArrayFromFullname() */
1222 : /************************************************************************/
1223 :
1224 : /** Get an array from its fully qualified name */
1225 : std::shared_ptr<GDALMDArray>
1226 352 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1227 : CSLConstList papszOptions) const
1228 : {
1229 704 : std::string osName;
1230 352 : std::shared_ptr<GDALGroup> curGroupHolder;
1231 352 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1232 352 : if (poGroup == nullptr)
1233 9 : return nullptr;
1234 343 : return poGroup->OpenMDArray(osName, papszOptions);
1235 : }
1236 :
1237 : /************************************************************************/
1238 : /* ResolveMDArray() */
1239 : /************************************************************************/
1240 :
1241 : /** Locate an array in a group and its subgroups by name.
1242 : *
1243 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1244 : * used
1245 : * Otherwise the search will start from the group identified by osStartingPath,
1246 : * and an array whose name is osName will be looked for in this group (if
1247 : * osStartingPath is empty or "/", then the current group is used). If there
1248 : * is no match, then a recursive descendent search will be made in its
1249 : * subgroups. If there is no match in the subgroups, then the parent (if
1250 : * existing) of the group pointed by osStartingPath will be used as the new
1251 : * starting point for the search.
1252 : *
1253 : * @param osName name, qualified or not
1254 : * @param osStartingPath fully qualified name of the (sub-)group from which
1255 : * the search should be started. If this is a non-empty
1256 : * string, the group on which this method is called should
1257 : * nominally be the root group (otherwise the path will
1258 : * be interpreted as from the current group)
1259 : * @param papszOptions options to pass to OpenMDArray()
1260 : * @since GDAL 3.2
1261 : */
1262 : std::shared_ptr<GDALMDArray>
1263 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1264 : const std::string &osStartingPath,
1265 : CSLConstList papszOptions) const
1266 : {
1267 19 : if (!osName.empty() && osName[0] == '/')
1268 : {
1269 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1270 1 : if (poArray)
1271 1 : return poArray;
1272 : }
1273 36 : std::string osPath(osStartingPath);
1274 36 : std::set<std::string> oSetAlreadyVisited;
1275 :
1276 : while (true)
1277 : {
1278 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1279 0 : std::shared_ptr<GDALGroup> poGroup;
1280 :
1281 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1282 22 : bool goOn = false;
1283 22 : if (osPath.empty() || osPath == "/")
1284 : {
1285 11 : goOn = true;
1286 : }
1287 : else
1288 : {
1289 22 : std::string osLastPart;
1290 : const GDALGroup *poGroupPtr =
1291 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1292 11 : if (poGroupPtr)
1293 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1294 22 : if (poGroup &&
1295 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1296 : {
1297 11 : oQueue.push(poGroup);
1298 11 : goOn = true;
1299 : }
1300 : }
1301 :
1302 22 : if (goOn)
1303 : {
1304 17 : do
1305 : {
1306 : const GDALGroup *groupPtr;
1307 39 : if (!oQueue.empty())
1308 : {
1309 28 : poGroup = oQueue.front();
1310 28 : oQueue.pop();
1311 28 : groupPtr = poGroup.get();
1312 : }
1313 : else
1314 : {
1315 11 : groupPtr = this;
1316 : }
1317 :
1318 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1319 39 : if (poArray)
1320 16 : return poArray;
1321 :
1322 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1323 47 : for (const auto &osGroupName : aosGroupNames)
1324 : {
1325 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1326 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1327 48 : poSubGroup->GetFullName()))
1328 : {
1329 24 : oQueue.push(poSubGroup);
1330 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1331 : }
1332 : }
1333 23 : } while (!oQueue.empty());
1334 : }
1335 :
1336 6 : if (osPath.empty() || osPath == "/")
1337 2 : break;
1338 :
1339 4 : const auto nPos = osPath.rfind('/');
1340 4 : if (nPos == 0)
1341 1 : osPath = "/";
1342 : else
1343 : {
1344 3 : if (nPos == std::string::npos)
1345 0 : break;
1346 3 : osPath.resize(nPos);
1347 : }
1348 4 : }
1349 2 : return nullptr;
1350 : }
1351 :
1352 : /************************************************************************/
1353 : /* OpenGroupFromFullname() */
1354 : /************************************************************************/
1355 :
1356 : /** Get a group from its fully qualified name.
1357 : * @since GDAL 3.2
1358 : */
1359 : std::shared_ptr<GDALGroup>
1360 550 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1361 : CSLConstList papszOptions) const
1362 : {
1363 1100 : std::string osName;
1364 550 : std::shared_ptr<GDALGroup> curGroupHolder;
1365 550 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1366 550 : if (poGroup == nullptr)
1367 2 : return nullptr;
1368 548 : return poGroup->OpenGroup(osName, papszOptions);
1369 : }
1370 :
1371 : /************************************************************************/
1372 : /* OpenDimensionFromFullname() */
1373 : /************************************************************************/
1374 :
1375 : /** Get a dimension from its fully qualified name */
1376 : std::shared_ptr<GDALDimension>
1377 125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1378 : {
1379 250 : std::string osName;
1380 125 : std::shared_ptr<GDALGroup> curGroupHolder;
1381 125 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1382 125 : if (poGroup == nullptr)
1383 2 : return nullptr;
1384 246 : auto dims(poGroup->GetDimensions());
1385 203 : for (auto &dim : dims)
1386 : {
1387 164 : if (dim->GetName() == osName)
1388 84 : return dim;
1389 : }
1390 39 : return nullptr;
1391 : }
1392 :
1393 : /************************************************************************/
1394 : /* ClearStatistics() */
1395 : /************************************************************************/
1396 :
1397 : /**
1398 : * \brief Clear statistics.
1399 : *
1400 : * @since GDAL 3.4
1401 : */
1402 0 : void GDALGroup::ClearStatistics()
1403 : {
1404 0 : auto groupNames = GetGroupNames();
1405 0 : for (const auto &name : groupNames)
1406 : {
1407 0 : auto subGroup = OpenGroup(name);
1408 0 : if (subGroup)
1409 : {
1410 0 : subGroup->ClearStatistics();
1411 : }
1412 : }
1413 :
1414 0 : auto arrayNames = GetMDArrayNames();
1415 0 : for (const auto &name : arrayNames)
1416 : {
1417 0 : auto array = OpenMDArray(name);
1418 0 : if (array)
1419 : {
1420 0 : array->ClearStatistics();
1421 : }
1422 : }
1423 0 : }
1424 :
1425 : /************************************************************************/
1426 : /* Rename() */
1427 : /************************************************************************/
1428 :
1429 : /** Rename the group.
1430 : *
1431 : * This is not implemented by all drivers.
1432 : *
1433 : * Drivers known to implement it: MEM, netCDF, ZARR.
1434 : *
1435 : * This is the same as the C function GDALGroupRename().
1436 : *
1437 : * @param osNewName New name.
1438 : *
1439 : * @return true in case of success
1440 : * @since GDAL 3.8
1441 : */
1442 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1443 : {
1444 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1445 0 : return false;
1446 : }
1447 :
1448 : /************************************************************************/
1449 : /* BaseRename() */
1450 : /************************************************************************/
1451 :
1452 : //! @cond Doxygen_Suppress
1453 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1454 : {
1455 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1456 8 : m_osFullName += osNewName;
1457 8 : m_osName = osNewName;
1458 :
1459 8 : NotifyChildrenOfRenaming();
1460 8 : }
1461 :
1462 : //! @endcond
1463 :
1464 : /************************************************************************/
1465 : /* ParentRenamed() */
1466 : /************************************************************************/
1467 :
1468 : //! @cond Doxygen_Suppress
1469 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1470 : {
1471 7 : m_osFullName = osNewParentFullName;
1472 7 : m_osFullName += "/";
1473 7 : m_osFullName += m_osName;
1474 :
1475 7 : NotifyChildrenOfRenaming();
1476 7 : }
1477 :
1478 : //! @endcond
1479 :
1480 : /************************************************************************/
1481 : /* Deleted() */
1482 : /************************************************************************/
1483 :
1484 : //! @cond Doxygen_Suppress
1485 22 : void GDALGroup::Deleted()
1486 : {
1487 22 : m_bValid = false;
1488 :
1489 22 : NotifyChildrenOfDeletion();
1490 22 : }
1491 :
1492 : //! @endcond
1493 :
1494 : /************************************************************************/
1495 : /* ParentDeleted() */
1496 : /************************************************************************/
1497 :
1498 : //! @cond Doxygen_Suppress
1499 3 : void GDALGroup::ParentDeleted()
1500 : {
1501 3 : Deleted();
1502 3 : }
1503 :
1504 : //! @endcond
1505 :
1506 : /************************************************************************/
1507 : /* CheckValidAndErrorOutIfNot() */
1508 : /************************************************************************/
1509 :
1510 : //! @cond Doxygen_Suppress
1511 11755 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1512 : {
1513 11755 : if (!m_bValid)
1514 : {
1515 14 : CPLError(CE_Failure, CPLE_AppDefined,
1516 : "This object has been deleted. No action on it is possible");
1517 : }
1518 11755 : return m_bValid;
1519 : }
1520 :
1521 : //! @endcond
1522 :
1523 : /************************************************************************/
1524 : /* ~GDALAbstractMDArray() */
1525 : /************************************************************************/
1526 :
1527 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1528 :
1529 : /************************************************************************/
1530 : /* GDALAbstractMDArray() */
1531 : /************************************************************************/
1532 :
1533 : //! @cond Doxygen_Suppress
1534 20196 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1535 20196 : const std::string &osName)
1536 : : m_osName(osName),
1537 : m_osFullName(
1538 20196 : !osParentName.empty()
1539 38754 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1540 79146 : : osName)
1541 : {
1542 20196 : }
1543 :
1544 : //! @endcond
1545 :
1546 : /************************************************************************/
1547 : /* GetDimensions() */
1548 : /************************************************************************/
1549 :
1550 : /** \fn GDALAbstractMDArray::GetDimensions() const
1551 : * \brief Return the dimensions of an attribute/array.
1552 : *
1553 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1554 : * similar to GDALAttributeGetDimensionsSize().
1555 : */
1556 :
1557 : /************************************************************************/
1558 : /* GetDataType() */
1559 : /************************************************************************/
1560 :
1561 : /** \fn GDALAbstractMDArray::GetDataType() const
1562 : * \brief Return the data type of an attribute/array.
1563 : *
1564 : * This is the same as the C functions GDALMDArrayGetDataType() and
1565 : * GDALAttributeGetDataType()
1566 : */
1567 :
1568 : /************************************************************************/
1569 : /* GetDimensionCount() */
1570 : /************************************************************************/
1571 :
1572 : /** Return the number of dimensions.
1573 : *
1574 : * Default implementation is GetDimensions().size(), and may be overridden by
1575 : * drivers if they have a faster / less expensive implementations.
1576 : *
1577 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1578 : * GDALAttributeGetDimensionCount().
1579 : *
1580 : */
1581 22267 : size_t GDALAbstractMDArray::GetDimensionCount() const
1582 : {
1583 22267 : return GetDimensions().size();
1584 : }
1585 :
1586 : /************************************************************************/
1587 : /* Rename() */
1588 : /************************************************************************/
1589 :
1590 : /** Rename the attribute/array.
1591 : *
1592 : * This is not implemented by all drivers.
1593 : *
1594 : * Drivers known to implement it: MEM, netCDF, Zarr.
1595 : *
1596 : * This is the same as the C functions GDALMDArrayRename() or
1597 : * GDALAttributeRename().
1598 : *
1599 : * @param osNewName New name.
1600 : *
1601 : * @return true in case of success
1602 : * @since GDAL 3.8
1603 : */
1604 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1605 : {
1606 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1607 0 : return false;
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* CopyValue() */
1612 : /************************************************************************/
1613 :
1614 : /** Convert a value from a source type to a destination type.
1615 : *
1616 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1617 : * that must be freed with CPLFree().
1618 : */
1619 78422 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1620 : const GDALExtendedDataType &srcType,
1621 : void *pDst,
1622 : const GDALExtendedDataType &dstType)
1623 : {
1624 153554 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1625 75132 : dstType.GetClass() == GEDTC_NUMERIC)
1626 : {
1627 74925 : GDALCopyWords(pSrc, srcType.GetNumericDataType(), 0, pDst,
1628 : dstType.GetNumericDataType(), 0, 1);
1629 74925 : return true;
1630 : }
1631 6612 : if (srcType.GetClass() == GEDTC_STRING &&
1632 3115 : dstType.GetClass() == GEDTC_STRING)
1633 : {
1634 : const char *srcStrPtr;
1635 2731 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1636 2731 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1637 2731 : *reinterpret_cast<void **>(pDst) = pszDup;
1638 2731 : return true;
1639 : }
1640 973 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1641 207 : dstType.GetClass() == GEDTC_STRING)
1642 : {
1643 207 : const char *str = nullptr;
1644 207 : switch (srcType.GetNumericDataType())
1645 : {
1646 0 : case GDT_Unknown:
1647 0 : break;
1648 0 : case GDT_Byte:
1649 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1650 0 : break;
1651 3 : case GDT_Int8:
1652 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1653 3 : break;
1654 48 : case GDT_UInt16:
1655 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1656 48 : break;
1657 0 : case GDT_Int16:
1658 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1659 0 : break;
1660 8 : case GDT_UInt32:
1661 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1662 8 : break;
1663 54 : case GDT_Int32:
1664 54 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1665 54 : break;
1666 0 : case GDT_UInt64:
1667 : str =
1668 0 : CPLSPrintf(CPL_FRMT_GUIB,
1669 : static_cast<GUIntBig>(
1670 : *static_cast<const std::uint64_t *>(pSrc)));
1671 0 : break;
1672 0 : case GDT_Int64:
1673 0 : str = CPLSPrintf(CPL_FRMT_GIB,
1674 : static_cast<GIntBig>(
1675 : *static_cast<const std::int64_t *>(pSrc)));
1676 0 : break;
1677 17 : case GDT_Float32:
1678 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1679 17 : break;
1680 75 : case GDT_Float64:
1681 75 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1682 75 : break;
1683 2 : case GDT_CInt16:
1684 : {
1685 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1686 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1687 2 : break;
1688 : }
1689 0 : case GDT_CInt32:
1690 : {
1691 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1692 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1693 0 : break;
1694 : }
1695 0 : case GDT_CFloat32:
1696 : {
1697 0 : const float *src = static_cast<const float *>(pSrc);
1698 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1699 0 : break;
1700 : }
1701 0 : case GDT_CFloat64:
1702 : {
1703 0 : const double *src = static_cast<const double *>(pSrc);
1704 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1705 0 : break;
1706 : }
1707 0 : case GDT_TypeCount:
1708 0 : CPLAssert(false);
1709 : break;
1710 : }
1711 207 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1712 207 : *reinterpret_cast<void **>(pDst) = pszDup;
1713 207 : return true;
1714 : }
1715 943 : if (srcType.GetClass() == GEDTC_STRING &&
1716 384 : dstType.GetClass() == GEDTC_NUMERIC)
1717 : {
1718 : const char *srcStrPtr;
1719 384 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1720 384 : if (dstType.GetNumericDataType() == GDT_Int64)
1721 : {
1722 2 : *(static_cast<int64_t *>(pDst)) =
1723 2 : srcStrPtr == nullptr ? 0
1724 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1725 : }
1726 382 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1727 : {
1728 2 : *(static_cast<uint64_t *>(pDst)) =
1729 2 : srcStrPtr == nullptr
1730 2 : ? 0
1731 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1732 : }
1733 : else
1734 : {
1735 : // FIXME GDT_UInt64
1736 380 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1737 380 : GDALCopyWords(&dfVal, GDT_Float64, 0, pDst,
1738 : dstType.GetNumericDataType(), 0, 1);
1739 : }
1740 384 : return true;
1741 : }
1742 350 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1743 175 : dstType.GetClass() == GEDTC_COMPOUND)
1744 : {
1745 175 : const auto &srcComponents = srcType.GetComponents();
1746 175 : const auto &dstComponents = dstType.GetComponents();
1747 175 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1748 175 : GByte *pabyDst = static_cast<GByte *>(pDst);
1749 :
1750 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1751 350 : srcComponentMap;
1752 688 : for (const auto &srcComp : srcComponents)
1753 : {
1754 513 : srcComponentMap[srcComp->GetName()] = &srcComp;
1755 : }
1756 504 : for (const auto &dstComp : dstComponents)
1757 : {
1758 329 : auto oIter = srcComponentMap.find(dstComp->GetName());
1759 329 : if (oIter == srcComponentMap.end())
1760 0 : return false;
1761 329 : const auto &srcComp = *(oIter->second);
1762 987 : if (!GDALExtendedDataType::CopyValue(
1763 329 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1764 329 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1765 : {
1766 0 : return false;
1767 : }
1768 : }
1769 175 : return true;
1770 : }
1771 :
1772 0 : return false;
1773 : }
1774 :
1775 : /************************************************************************/
1776 : /* CopyValues() */
1777 : /************************************************************************/
1778 :
1779 : /** Convert severals value from a source type to a destination type.
1780 : *
1781 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1782 : * that must be freed with CPLFree().
1783 : */
1784 328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1785 : const GDALExtendedDataType &srcType,
1786 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1787 : const GDALExtendedDataType &dstType,
1788 : GPtrDiff_t nDstStrideInElts,
1789 : size_t nValues)
1790 : {
1791 : const auto nSrcStrideInBytes =
1792 328 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1793 : const auto nDstStrideInBytes =
1794 328 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1795 594 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1796 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1797 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1798 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1799 860 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1800 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1801 : {
1802 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1803 : static_cast<int>(nSrcStrideInBytes), pDst,
1804 : dstType.GetNumericDataType(),
1805 : static_cast<int>(nDstStrideInBytes), nValues);
1806 : }
1807 : else
1808 : {
1809 62 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1810 62 : GByte *pabyDst = static_cast<GByte *>(pDst);
1811 124 : for (size_t i = 0; i < nValues; ++i)
1812 : {
1813 62 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1814 0 : return false;
1815 62 : pabySrc += nSrcStrideInBytes;
1816 62 : pabyDst += nDstStrideInBytes;
1817 : }
1818 : }
1819 328 : return true;
1820 : }
1821 :
1822 : /************************************************************************/
1823 : /* CheckReadWriteParams() */
1824 : /************************************************************************/
1825 : //! @cond Doxygen_Suppress
1826 7823 : bool GDALAbstractMDArray::CheckReadWriteParams(
1827 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1828 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1829 : const void *buffer, const void *buffer_alloc_start,
1830 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1831 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1832 : {
1833 0 : const auto lamda_error = []()
1834 : {
1835 0 : CPLError(CE_Failure, CPLE_AppDefined,
1836 : "Not all elements pointed by buffer will fit in "
1837 : "[buffer_alloc_start, "
1838 : "buffer_alloc_start + buffer_alloc_size]");
1839 0 : };
1840 :
1841 7823 : const auto &dims = GetDimensions();
1842 7823 : if (dims.empty())
1843 : {
1844 3032 : if (buffer_alloc_start)
1845 : {
1846 2665 : const size_t elementSize = bufferDataType.GetSize();
1847 2665 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1848 2665 : const GByte *paby_buffer_alloc_start =
1849 : static_cast<const GByte *>(buffer_alloc_start);
1850 2665 : const GByte *paby_buffer_alloc_end =
1851 : paby_buffer_alloc_start + buffer_alloc_size;
1852 :
1853 2665 : if (paby_buffer < paby_buffer_alloc_start ||
1854 2665 : paby_buffer + elementSize > paby_buffer_alloc_end)
1855 : {
1856 0 : lamda_error();
1857 0 : return false;
1858 : }
1859 : }
1860 3032 : return true;
1861 : }
1862 :
1863 4791 : if (arrayStep == nullptr)
1864 : {
1865 1265 : tmp_arrayStep.resize(dims.size(), 1);
1866 1265 : arrayStep = tmp_arrayStep.data();
1867 : }
1868 13508 : for (size_t i = 0; i < dims.size(); i++)
1869 : {
1870 8717 : if (count[i] == 0)
1871 : {
1872 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1873 : static_cast<unsigned>(i));
1874 0 : return false;
1875 : }
1876 : }
1877 4791 : bool bufferStride_all_positive = true;
1878 4791 : if (bufferStride == nullptr)
1879 : {
1880 979 : GPtrDiff_t stride = 1;
1881 : // To compute strides we must proceed from the fastest varying dimension
1882 : // (the last one), and then reverse the result
1883 2229 : for (size_t i = dims.size(); i != 0;)
1884 : {
1885 1250 : --i;
1886 1250 : tmp_bufferStride.push_back(stride);
1887 1250 : GUInt64 newStride = 0;
1888 : bool bOK;
1889 : try
1890 : {
1891 1250 : newStride = (CPLSM(static_cast<GUInt64>(stride)) *
1892 2500 : CPLSM(static_cast<GUInt64>(count[i])))
1893 1250 : .v();
1894 1250 : bOK = static_cast<size_t>(newStride) == newStride &&
1895 1250 : newStride < std::numeric_limits<size_t>::max() / 2;
1896 : }
1897 0 : catch (...)
1898 : {
1899 0 : bOK = false;
1900 : }
1901 1250 : if (!bOK)
1902 : {
1903 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1904 0 : return false;
1905 : }
1906 1250 : stride = static_cast<GPtrDiff_t>(newStride);
1907 : }
1908 979 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1909 979 : bufferStride = tmp_bufferStride.data();
1910 : }
1911 : else
1912 : {
1913 11277 : for (size_t i = 0; i < dims.size(); i++)
1914 : {
1915 7466 : if (bufferStride[i] < 0)
1916 : {
1917 1 : bufferStride_all_positive = false;
1918 1 : break;
1919 : }
1920 : }
1921 : }
1922 13479 : for (size_t i = 0; i < dims.size(); i++)
1923 : {
1924 8698 : if (arrayStartIdx[i] >= dims[i]->GetSize())
1925 : {
1926 2 : CPLError(CE_Failure, CPLE_AppDefined,
1927 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1928 : static_cast<unsigned>(i),
1929 2 : static_cast<GUInt64>(arrayStartIdx[i]),
1930 2 : static_cast<GUInt64>(dims[i]->GetSize()));
1931 2 : return false;
1932 : }
1933 : bool bOverflow;
1934 8696 : if (arrayStep[i] >= 0)
1935 : {
1936 : try
1937 : {
1938 8102 : bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
1939 8104 : CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1940 32411 : CPLSM(static_cast<GUInt64>(arrayStep[i])))
1941 8102 : .v() >= dims[i]->GetSize();
1942 : }
1943 1 : catch (...)
1944 : {
1945 1 : bOverflow = true;
1946 : }
1947 8103 : if (bOverflow)
1948 : {
1949 5 : CPLError(CE_Failure, CPLE_AppDefined,
1950 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
1951 : ">= " CPL_FRMT_GUIB,
1952 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1953 : static_cast<unsigned>(i),
1954 5 : static_cast<GUInt64>(dims[i]->GetSize()));
1955 5 : return false;
1956 : }
1957 : }
1958 : else
1959 : {
1960 : try
1961 : {
1962 593 : bOverflow =
1963 593 : arrayStartIdx[i] <
1964 593 : (CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1965 1186 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
1966 : ? (static_cast<GUInt64>(1) << 63)
1967 1186 : : static_cast<GUInt64>(-arrayStep[i])))
1968 593 : .v();
1969 : }
1970 0 : catch (...)
1971 : {
1972 0 : bOverflow = true;
1973 : }
1974 593 : if (bOverflow)
1975 : {
1976 3 : CPLError(
1977 : CE_Failure, CPLE_AppDefined,
1978 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
1979 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1980 : static_cast<unsigned>(i));
1981 3 : return false;
1982 : }
1983 : }
1984 : }
1985 :
1986 4781 : if (buffer_alloc_start)
1987 : {
1988 2485 : const size_t elementSize = bufferDataType.GetSize();
1989 2485 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1990 2485 : const GByte *paby_buffer_alloc_start =
1991 : static_cast<const GByte *>(buffer_alloc_start);
1992 2485 : const GByte *paby_buffer_alloc_end =
1993 : paby_buffer_alloc_start + buffer_alloc_size;
1994 2485 : if (bufferStride_all_positive)
1995 : {
1996 2485 : if (paby_buffer < paby_buffer_alloc_start)
1997 : {
1998 0 : lamda_error();
1999 0 : return false;
2000 : }
2001 2485 : GUInt64 nOffset = elementSize;
2002 7117 : for (size_t i = 0; i < dims.size(); i++)
2003 : {
2004 : try
2005 : {
2006 4632 : nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
2007 4632 : CPLSM(static_cast<GUInt64>(bufferStride[i])) *
2008 9264 : CPLSM(static_cast<GUInt64>(count[i] - 1)) *
2009 18528 : CPLSM(static_cast<GUInt64>(elementSize)))
2010 4632 : .v();
2011 : }
2012 0 : catch (...)
2013 : {
2014 0 : lamda_error();
2015 0 : return false;
2016 : }
2017 : }
2018 : #if SIZEOF_VOIDP == 4
2019 : if (static_cast<size_t>(nOffset) != nOffset)
2020 : {
2021 : lamda_error();
2022 : return false;
2023 : }
2024 : #endif
2025 2485 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2026 : {
2027 0 : lamda_error();
2028 0 : return false;
2029 : }
2030 : }
2031 0 : else if (dims.size() < 31)
2032 : {
2033 : // Check all corners of the hypercube
2034 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2035 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2036 : {
2037 0 : const GByte *paby = paby_buffer;
2038 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2039 : i++)
2040 : {
2041 0 : if (iCornerCode & (1U << i))
2042 : {
2043 : // We should check for integer overflows
2044 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2045 : }
2046 : }
2047 0 : if (paby < paby_buffer_alloc_start ||
2048 0 : paby + elementSize > paby_buffer_alloc_end)
2049 : {
2050 0 : lamda_error();
2051 0 : return false;
2052 : }
2053 : }
2054 : }
2055 : }
2056 :
2057 4781 : return true;
2058 : }
2059 :
2060 : //! @endcond
2061 :
2062 : /************************************************************************/
2063 : /* Read() */
2064 : /************************************************************************/
2065 :
2066 : /** Read part or totality of a multidimensional array or attribute.
2067 : *
2068 : * This will extract the content of a hyper-rectangle from the array into
2069 : * a user supplied buffer.
2070 : *
2071 : * If bufferDataType is of type string, the values written in pDstBuffer
2072 : * will be char* pointers and the strings should be freed with CPLFree().
2073 : *
2074 : * This is the same as the C function GDALMDArrayRead().
2075 : *
2076 : * @param arrayStartIdx Values representing the starting index to read
2077 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2078 : * Array of GetDimensionCount() values. Must not be
2079 : * nullptr, unless for a zero-dimensional array.
2080 : *
2081 : * @param count Values representing the number of values to extract in
2082 : * each dimension.
2083 : * Array of GetDimensionCount() values. Must not be
2084 : * nullptr, unless for a zero-dimensional array.
2085 : *
2086 : * @param arrayStep Spacing between values to extract in each dimension.
2087 : * The spacing is in number of array elements, not bytes.
2088 : * If provided, must contain GetDimensionCount() values.
2089 : * If set to nullptr, [1, 1, ... 1] will be used as a
2090 : * default to indicate consecutive elements.
2091 : *
2092 : * @param bufferStride Spacing between values to store in pDstBuffer.
2093 : * The spacing is in number of array elements, not bytes.
2094 : * If provided, must contain GetDimensionCount() values.
2095 : * Negative values are possible (for example to reorder
2096 : * from bottom-to-top to top-to-bottom).
2097 : * If set to nullptr, will be set so that pDstBuffer is
2098 : * written in a compact way, with elements of the last /
2099 : * fastest varying dimension being consecutive.
2100 : *
2101 : * @param bufferDataType Data type of values in pDstBuffer.
2102 : *
2103 : * @param pDstBuffer User buffer to store the values read. Should be big
2104 : * enough to store the number of values indicated by
2105 : * count[] and with the spacing of bufferStride[].
2106 : *
2107 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2108 : * validity of pDstBuffer. pDstBufferAllocStart
2109 : * should be the pointer returned by the malloc() or equivalent call used to
2110 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2111 : * bufferStride[] values are all positive), but not necessarily. If specified,
2112 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2113 : * validation is needed, nullptr can be passed.
2114 : *
2115 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2116 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2117 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2118 : * set to the appropriate value.
2119 : * If no validation is needed, 0 can be passed.
2120 : *
2121 : * @return true in case of success.
2122 : */
2123 2343 : bool GDALAbstractMDArray::Read(
2124 : const GUInt64 *arrayStartIdx, const size_t *count,
2125 : const GInt64 *arrayStep, // step in elements
2126 : const GPtrDiff_t *bufferStride, // stride in elements
2127 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2128 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2129 : {
2130 2343 : if (!GetDataType().CanConvertTo(bufferDataType))
2131 : {
2132 0 : CPLError(CE_Failure, CPLE_AppDefined,
2133 : "Array data type is not convertible to buffer data type");
2134 0 : return false;
2135 : }
2136 :
2137 4686 : std::vector<GInt64> tmp_arrayStep;
2138 4686 : std::vector<GPtrDiff_t> tmp_bufferStride;
2139 2343 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2140 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2141 : nDstBufferAllocSize, tmp_arrayStep,
2142 : tmp_bufferStride))
2143 : {
2144 0 : return false;
2145 : }
2146 :
2147 2343 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2148 2343 : pDstBuffer);
2149 : }
2150 :
2151 : /************************************************************************/
2152 : /* IWrite() */
2153 : /************************************************************************/
2154 :
2155 : //! @cond Doxygen_Suppress
2156 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2157 : const GInt64 *, const GPtrDiff_t *,
2158 : const GDALExtendedDataType &, const void *)
2159 : {
2160 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2161 1 : return false;
2162 : }
2163 :
2164 : //! @endcond
2165 :
2166 : /************************************************************************/
2167 : /* Write() */
2168 : /************************************************************************/
2169 :
2170 : /** Write part or totality of a multidimensional array or attribute.
2171 : *
2172 : * This will set the content of a hyper-rectangle into the array from
2173 : * a user supplied buffer.
2174 : *
2175 : * If bufferDataType is of type string, the values read from pSrcBuffer
2176 : * will be char* pointers.
2177 : *
2178 : * This is the same as the C function GDALMDArrayWrite().
2179 : *
2180 : * @param arrayStartIdx Values representing the starting index to write
2181 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2182 : * Array of GetDimensionCount() values. Must not be
2183 : * nullptr, unless for a zero-dimensional array.
2184 : *
2185 : * @param count Values representing the number of values to write in
2186 : * each dimension.
2187 : * Array of GetDimensionCount() values. Must not be
2188 : * nullptr, unless for a zero-dimensional array.
2189 : *
2190 : * @param arrayStep Spacing between values to write in each dimension.
2191 : * The spacing is in number of array elements, not bytes.
2192 : * If provided, must contain GetDimensionCount() values.
2193 : * If set to nullptr, [1, 1, ... 1] will be used as a
2194 : * default to indicate consecutive elements.
2195 : *
2196 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2197 : * The spacing is in number of array elements, not bytes.
2198 : * If provided, must contain GetDimensionCount() values.
2199 : * Negative values are possible (for example to reorder
2200 : * from bottom-to-top to top-to-bottom).
2201 : * If set to nullptr, will be set so that pSrcBuffer is
2202 : * written in a compact way, with elements of the last /
2203 : * fastest varying dimension being consecutive.
2204 : *
2205 : * @param bufferDataType Data type of values in pSrcBuffer.
2206 : *
2207 : * @param pSrcBuffer User buffer to read the values from. Should be big
2208 : * enough to store the number of values indicated by
2209 : * count[] and with the spacing of bufferStride[].
2210 : *
2211 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2212 : * validity of pSrcBuffer. pSrcBufferAllocStart
2213 : * should be the pointer returned by the malloc() or equivalent call used to
2214 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2215 : * bufferStride[] values are all positive), but not necessarily. If specified,
2216 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2217 : * validation is needed, nullptr can be passed.
2218 : *
2219 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2220 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2221 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2222 : * set to the appropriate value.
2223 : * If no validation is needed, 0 can be passed.
2224 : *
2225 : * @return true in case of success.
2226 : */
2227 1752 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2228 : const size_t *count, const GInt64 *arrayStep,
2229 : const GPtrDiff_t *bufferStride,
2230 : const GDALExtendedDataType &bufferDataType,
2231 : const void *pSrcBuffer,
2232 : const void *pSrcBufferAllocStart,
2233 : size_t nSrcBufferAllocSize)
2234 : {
2235 1752 : if (!bufferDataType.CanConvertTo(GetDataType()))
2236 : {
2237 0 : CPLError(CE_Failure, CPLE_AppDefined,
2238 : "Buffer data type is not convertible to array data type");
2239 0 : return false;
2240 : }
2241 :
2242 3504 : std::vector<GInt64> tmp_arrayStep;
2243 3504 : std::vector<GPtrDiff_t> tmp_bufferStride;
2244 1752 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2245 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2246 : nSrcBufferAllocSize, tmp_arrayStep,
2247 : tmp_bufferStride))
2248 : {
2249 0 : return false;
2250 : }
2251 :
2252 1752 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2253 1752 : pSrcBuffer);
2254 : }
2255 :
2256 : /************************************************************************/
2257 : /* GetTotalElementsCount() */
2258 : /************************************************************************/
2259 :
2260 : /** Return the total number of values in the array.
2261 : *
2262 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2263 : * and GDALAttributeGetTotalElementsCount().
2264 : *
2265 : */
2266 1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2267 : {
2268 1022 : const auto &dims = GetDimensions();
2269 1022 : if (dims.empty())
2270 504 : return 1;
2271 518 : GUInt64 nElts = 1;
2272 1146 : for (const auto &dim : dims)
2273 : {
2274 : try
2275 : {
2276 628 : nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
2277 1884 : CPLSM(static_cast<GUInt64>(dim->GetSize())))
2278 628 : .v();
2279 : }
2280 0 : catch (...)
2281 : {
2282 0 : return 0;
2283 : }
2284 : }
2285 518 : return nElts;
2286 : }
2287 :
2288 : /************************************************************************/
2289 : /* GetBlockSize() */
2290 : /************************************************************************/
2291 :
2292 : /** Return the "natural" block size of the array along all dimensions.
2293 : *
2294 : * Some drivers might organize the array in tiles/blocks and reading/writing
2295 : * aligned on those tile/block boundaries will be more efficient.
2296 : *
2297 : * The returned number of elements in the vector is the same as
2298 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2299 : * the natural block size along the considered dimension.
2300 : * "Flat" arrays will typically return a vector of values set to 0.
2301 : *
2302 : * The default implementation will return a vector of values set to 0.
2303 : *
2304 : * This method is used by GetProcessingChunkSize().
2305 : *
2306 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2307 : * theoretical case of a 32-bit platform, this might exceed its size_t
2308 : * allocation capabilities.
2309 : *
2310 : * This is the same as the C function GDALMDArrayGetBlockSize().
2311 : *
2312 : * @return the block size, in number of elements along each dimension.
2313 : */
2314 216 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2315 : {
2316 216 : return std::vector<GUInt64>(GetDimensionCount());
2317 : }
2318 :
2319 : /************************************************************************/
2320 : /* GetProcessingChunkSize() */
2321 : /************************************************************************/
2322 :
2323 : /** \brief Return an optimal chunk size for read/write operations, given the
2324 : * natural block size and memory constraints specified.
2325 : *
2326 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2327 : * multiple of those returned by GetBlockSize() (unless the block define by
2328 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2329 : * returned by this method).
2330 : *
2331 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2332 : *
2333 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2334 : * chunk.
2335 : *
2336 : * @return the chunk size, in number of elements along each dimension.
2337 : */
2338 : std::vector<size_t>
2339 60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2340 : {
2341 60 : const auto &dims = GetDimensions();
2342 60 : const auto &nDTSize = GetDataType().GetSize();
2343 60 : std::vector<size_t> anChunkSize;
2344 120 : auto blockSize = GetBlockSize();
2345 60 : CPLAssert(blockSize.size() == dims.size());
2346 60 : size_t nChunkSize = nDTSize;
2347 60 : bool bOverflow = false;
2348 60 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2349 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2350 : // [1, min(sizet_max, dim_size[i])]
2351 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2352 173 : for (size_t i = 0; i < dims.size(); i++)
2353 : {
2354 : const auto sizeDimI =
2355 226 : std::max(static_cast<size_t>(1),
2356 226 : static_cast<size_t>(
2357 226 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2358 113 : std::min(blockSize[i], dims[i]->GetSize()))));
2359 113 : anChunkSize.push_back(sizeDimI);
2360 113 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2361 : {
2362 4 : bOverflow = true;
2363 : }
2364 : else
2365 : {
2366 109 : nChunkSize *= sizeDimI;
2367 : }
2368 : }
2369 60 : if (nChunkSize == 0)
2370 0 : return anChunkSize;
2371 :
2372 : // If the product of all anChunkSize[i] does not fit on size_t, then
2373 : // set lowest anChunkSize[i] to 1.
2374 60 : if (bOverflow)
2375 : {
2376 2 : nChunkSize = nDTSize;
2377 2 : bOverflow = false;
2378 8 : for (size_t i = dims.size(); i > 0;)
2379 : {
2380 6 : --i;
2381 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2382 : {
2383 4 : bOverflow = true;
2384 4 : anChunkSize[i] = 1;
2385 : }
2386 : else
2387 : {
2388 2 : nChunkSize *= anChunkSize[i];
2389 : }
2390 : }
2391 : }
2392 :
2393 60 : nChunkSize = nDTSize;
2394 120 : std::vector<size_t> anAccBlockSizeFromStart;
2395 173 : for (size_t i = 0; i < dims.size(); i++)
2396 : {
2397 113 : nChunkSize *= anChunkSize[i];
2398 113 : anAccBlockSizeFromStart.push_back(nChunkSize);
2399 : }
2400 60 : if (nChunkSize <= nMaxChunkMemory / 2)
2401 : {
2402 56 : size_t nVoxelsFromEnd = 1;
2403 161 : for (size_t i = dims.size(); i > 0;)
2404 : {
2405 105 : --i;
2406 : const auto nCurBlockSize =
2407 105 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2408 105 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2409 105 : if (nMul >= 2)
2410 : {
2411 97 : const auto nSizeThisDim(dims[i]->GetSize());
2412 : const auto nBlocksThisDim =
2413 97 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2414 97 : anChunkSize[i] = static_cast<size_t>(std::min(
2415 97 : anChunkSize[i] *
2416 194 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2417 97 : nSizeThisDim));
2418 : }
2419 105 : nVoxelsFromEnd *= anChunkSize[i];
2420 : }
2421 : }
2422 60 : return anChunkSize;
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* BaseRename() */
2427 : /************************************************************************/
2428 :
2429 : //! @cond Doxygen_Suppress
2430 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2431 : {
2432 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2433 18 : m_osFullName += osNewName;
2434 18 : m_osName = osNewName;
2435 :
2436 18 : NotifyChildrenOfRenaming();
2437 18 : }
2438 :
2439 : //! @endcond
2440 :
2441 : //! @cond Doxygen_Suppress
2442 : /************************************************************************/
2443 : /* ParentRenamed() */
2444 : /************************************************************************/
2445 :
2446 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2447 : {
2448 50 : m_osFullName = osNewParentFullName;
2449 50 : m_osFullName += "/";
2450 50 : m_osFullName += m_osName;
2451 :
2452 50 : NotifyChildrenOfRenaming();
2453 50 : }
2454 :
2455 : //! @endcond
2456 :
2457 : /************************************************************************/
2458 : /* Deleted() */
2459 : /************************************************************************/
2460 :
2461 : //! @cond Doxygen_Suppress
2462 52 : void GDALAbstractMDArray::Deleted()
2463 : {
2464 52 : m_bValid = false;
2465 :
2466 52 : NotifyChildrenOfDeletion();
2467 52 : }
2468 :
2469 : //! @endcond
2470 :
2471 : /************************************************************************/
2472 : /* ParentDeleted() */
2473 : /************************************************************************/
2474 :
2475 : //! @cond Doxygen_Suppress
2476 28 : void GDALAbstractMDArray::ParentDeleted()
2477 : {
2478 28 : Deleted();
2479 28 : }
2480 :
2481 : //! @endcond
2482 :
2483 : /************************************************************************/
2484 : /* CheckValidAndErrorOutIfNot() */
2485 : /************************************************************************/
2486 :
2487 : //! @cond Doxygen_Suppress
2488 5617 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2489 : {
2490 5617 : if (!m_bValid)
2491 : {
2492 26 : CPLError(CE_Failure, CPLE_AppDefined,
2493 : "This object has been deleted. No action on it is possible");
2494 : }
2495 5617 : return m_bValid;
2496 : }
2497 :
2498 : //! @endcond
2499 :
2500 : /************************************************************************/
2501 : /* SetUnit() */
2502 : /************************************************************************/
2503 :
2504 : /** Set the variable unit.
2505 : *
2506 : * Values should conform as much as possible with those allowed by
2507 : * the NetCDF CF conventions:
2508 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2509 : * but others might be returned.
2510 : *
2511 : * Few examples are "meter", "degrees", "second", ...
2512 : * Empty value means unknown.
2513 : *
2514 : * This is the same as the C function GDALMDArraySetUnit()
2515 : *
2516 : * @note Driver implementation: optionally implemented.
2517 : *
2518 : * @param osUnit unit name.
2519 : * @return true in case of success.
2520 : */
2521 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2522 : {
2523 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2524 0 : return false;
2525 : }
2526 :
2527 : /************************************************************************/
2528 : /* GetUnit() */
2529 : /************************************************************************/
2530 :
2531 : /** Return the array unit.
2532 : *
2533 : * Values should conform as much as possible with those allowed by
2534 : * the NetCDF CF conventions:
2535 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2536 : * but others might be returned.
2537 : *
2538 : * Few examples are "meter", "degrees", "second", ...
2539 : * Empty value means unknown.
2540 : *
2541 : * This is the same as the C function GDALMDArrayGetUnit()
2542 : */
2543 5 : const std::string &GDALMDArray::GetUnit() const
2544 : {
2545 5 : static const std::string emptyString;
2546 5 : return emptyString;
2547 : }
2548 :
2549 : /************************************************************************/
2550 : /* SetSpatialRef() */
2551 : /************************************************************************/
2552 :
2553 : /** Assign a spatial reference system object to the array.
2554 : *
2555 : * This is the same as the C function GDALMDArraySetSpatialRef().
2556 : */
2557 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2558 : {
2559 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2560 0 : return false;
2561 : }
2562 :
2563 : /************************************************************************/
2564 : /* GetSpatialRef() */
2565 : /************************************************************************/
2566 :
2567 : /** Return the spatial reference system object associated with the array.
2568 : *
2569 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2570 : */
2571 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2572 : {
2573 4 : return nullptr;
2574 : }
2575 :
2576 : /************************************************************************/
2577 : /* GetRawNoDataValue() */
2578 : /************************************************************************/
2579 :
2580 : /** Return the nodata value as a "raw" value.
2581 : *
2582 : * The value returned might be nullptr in case of no nodata value. When
2583 : * a nodata value is registered, a non-nullptr will be returned whose size in
2584 : * bytes is GetDataType().GetSize().
2585 : *
2586 : * The returned value should not be modified or freed. It is valid until
2587 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2588 : * SetRawNoDataValue(), or any similar methods.
2589 : *
2590 : * @note Driver implementation: this method shall be implemented if nodata
2591 : * is supported.
2592 : *
2593 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2594 : *
2595 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2596 : */
2597 5 : const void *GDALMDArray::GetRawNoDataValue() const
2598 : {
2599 5 : return nullptr;
2600 : }
2601 :
2602 : /************************************************************************/
2603 : /* GetNoDataValueAsDouble() */
2604 : /************************************************************************/
2605 :
2606 : /** Return the nodata value as a double.
2607 : *
2608 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2609 : *
2610 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2611 : * a nodata value exists and can be converted to double. Might be nullptr.
2612 : *
2613 : * @return the nodata value as a double. A 0.0 value might also indicate the
2614 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2615 : * set to false then).
2616 : */
2617 22417 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2618 : {
2619 22417 : const void *pNoData = GetRawNoDataValue();
2620 22417 : double dfNoData = 0.0;
2621 22417 : const auto &eDT = GetDataType();
2622 22417 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2623 22417 : if (ok)
2624 : {
2625 22180 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2626 : GDT_Float64, 0, 1);
2627 : }
2628 22417 : if (pbHasNoData)
2629 384 : *pbHasNoData = ok;
2630 22417 : return dfNoData;
2631 : }
2632 :
2633 : /************************************************************************/
2634 : /* GetNoDataValueAsInt64() */
2635 : /************************************************************************/
2636 :
2637 : /** Return the nodata value as a Int64.
2638 : *
2639 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2640 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2641 : *
2642 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2643 : *
2644 : * @return the nodata value as a Int64
2645 : *
2646 : * @since GDAL 3.5
2647 : */
2648 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2649 : {
2650 12 : const void *pNoData = GetRawNoDataValue();
2651 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2652 12 : const auto &eDT = GetDataType();
2653 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2654 12 : if (ok)
2655 : {
2656 8 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData, GDT_Int64,
2657 : 0, 1);
2658 : }
2659 12 : if (pbHasNoData)
2660 12 : *pbHasNoData = ok;
2661 12 : return nNoData;
2662 : }
2663 :
2664 : /************************************************************************/
2665 : /* GetNoDataValueAsUInt64() */
2666 : /************************************************************************/
2667 :
2668 : /** Return the nodata value as a UInt64.
2669 : *
2670 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2671 :
2672 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2673 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2674 : *
2675 : * @return the nodata value as a UInt64
2676 : *
2677 : * @since GDAL 3.5
2678 : */
2679 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2680 : {
2681 8 : const void *pNoData = GetRawNoDataValue();
2682 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2683 8 : const auto &eDT = GetDataType();
2684 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2685 8 : if (ok)
2686 : {
2687 6 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2688 : GDT_UInt64, 0, 1);
2689 : }
2690 8 : if (pbHasNoData)
2691 8 : *pbHasNoData = ok;
2692 8 : return nNoData;
2693 : }
2694 :
2695 : /************************************************************************/
2696 : /* SetRawNoDataValue() */
2697 : /************************************************************************/
2698 :
2699 : /** Set the nodata value as a "raw" value.
2700 : *
2701 : * The value passed might be nullptr in case of no nodata value. When
2702 : * a nodata value is registered, a non-nullptr whose size in
2703 : * bytes is GetDataType().GetSize() must be passed.
2704 : *
2705 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2706 : *
2707 : * @note Driver implementation: this method shall be implemented if setting
2708 : nodata
2709 : * is supported.
2710 :
2711 : * @return true in case of success.
2712 : */
2713 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2714 : {
2715 0 : CPLError(CE_Failure, CPLE_NotSupported,
2716 : "SetRawNoDataValue() not implemented");
2717 0 : return false;
2718 : }
2719 :
2720 : /************************************************************************/
2721 : /* SetNoDataValue() */
2722 : /************************************************************************/
2723 :
2724 : /** Set the nodata value as a double.
2725 : *
2726 : * If the natural data type of the attribute/array is not double, type
2727 : * conversion will occur to the type returned by GetDataType().
2728 : *
2729 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2730 : *
2731 : * @return true in case of success.
2732 : */
2733 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2734 : {
2735 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2736 57 : bool bRet = false;
2737 57 : if (GDALExtendedDataType::CopyValue(
2738 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2739 57 : GetDataType()))
2740 : {
2741 57 : bRet = SetRawNoDataValue(pRawNoData);
2742 : }
2743 57 : CPLFree(pRawNoData);
2744 57 : return bRet;
2745 : }
2746 :
2747 : /************************************************************************/
2748 : /* SetNoDataValue() */
2749 : /************************************************************************/
2750 :
2751 : /** Set the nodata value as a Int64.
2752 : *
2753 : * If the natural data type of the attribute/array is not Int64, type conversion
2754 : * will occur to the type returned by GetDataType().
2755 : *
2756 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2757 : *
2758 : * @return true in case of success.
2759 : *
2760 : * @since GDAL 3.5
2761 : */
2762 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2763 : {
2764 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2765 3 : bool bRet = false;
2766 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2767 6 : GDALExtendedDataType::Create(GDT_Int64),
2768 3 : pRawNoData, GetDataType()))
2769 : {
2770 3 : bRet = SetRawNoDataValue(pRawNoData);
2771 : }
2772 3 : CPLFree(pRawNoData);
2773 3 : return bRet;
2774 : }
2775 :
2776 : /************************************************************************/
2777 : /* SetNoDataValue() */
2778 : /************************************************************************/
2779 :
2780 : /** Set the nodata value as a Int64.
2781 : *
2782 : * If the natural data type of the attribute/array is not Int64, type conversion
2783 : * will occur to the type returned by GetDataType().
2784 : *
2785 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2786 : *
2787 : * @return true in case of success.
2788 : *
2789 : * @since GDAL 3.5
2790 : */
2791 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2792 : {
2793 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2794 1 : bool bRet = false;
2795 1 : if (GDALExtendedDataType::CopyValue(
2796 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2797 1 : GetDataType()))
2798 : {
2799 1 : bRet = SetRawNoDataValue(pRawNoData);
2800 : }
2801 1 : CPLFree(pRawNoData);
2802 1 : return bRet;
2803 : }
2804 :
2805 : /************************************************************************/
2806 : /* Resize() */
2807 : /************************************************************************/
2808 :
2809 : /** Resize an array to new dimensions.
2810 : *
2811 : * Not all drivers may allow this operation, and with restrictions (e.g.
2812 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2813 : *
2814 : * Resizing a dimension used in other arrays will cause those other arrays
2815 : * to be resized.
2816 : *
2817 : * This is the same as the C function GDALMDArrayResize().
2818 : *
2819 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2820 : * new size of each indexing dimension.
2821 : * @param papszOptions Options. (Driver specific)
2822 : * @return true in case of success.
2823 : * @since GDAL 3.7
2824 : */
2825 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2826 : CPL_UNUSED CSLConstList papszOptions)
2827 : {
2828 0 : CPLError(CE_Failure, CPLE_NotSupported,
2829 : "Resize() is not supported for this array");
2830 0 : return false;
2831 : }
2832 :
2833 : /************************************************************************/
2834 : /* SetScale() */
2835 : /************************************************************************/
2836 :
2837 : /** Set the scale value to apply to raw values.
2838 : *
2839 : * unscaled_value = raw_value * GetScale() + GetOffset()
2840 : *
2841 : * This is the same as the C function GDALMDArraySetScale() /
2842 : * GDALMDArraySetScaleEx().
2843 : *
2844 : * @note Driver implementation: this method shall be implemented if setting
2845 : * scale is supported.
2846 : *
2847 : * @param dfScale scale
2848 : * @param eStorageType Data type to which create the potential attribute that
2849 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2850 : * implementation will decide automatically the data type. Note that changing
2851 : * the data type after initial setting might not be supported.
2852 : * @return true in case of success.
2853 : */
2854 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2855 : CPL_UNUSED GDALDataType eStorageType)
2856 : {
2857 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2858 0 : return false;
2859 : }
2860 :
2861 : /************************************************************************/
2862 : /* SetOffset) */
2863 : /************************************************************************/
2864 :
2865 : /** Set the offset value to apply to raw values.
2866 : *
2867 : * unscaled_value = raw_value * GetScale() + GetOffset()
2868 : *
2869 : * This is the same as the C function GDALMDArraySetOffset() /
2870 : * GDALMDArraySetOffsetEx().
2871 : *
2872 : * @note Driver implementation: this method shall be implemented if setting
2873 : * offset is supported.
2874 : *
2875 : * @param dfOffset Offset
2876 : * @param eStorageType Data type to which create the potential attribute that
2877 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2878 : * implementation will decide automatically the data type. Note that changing
2879 : * the data type after initial setting might not be supported.
2880 : * @return true in case of success.
2881 : */
2882 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2883 : CPL_UNUSED GDALDataType eStorageType)
2884 : {
2885 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2886 0 : return false;
2887 : }
2888 :
2889 : /************************************************************************/
2890 : /* GetScale() */
2891 : /************************************************************************/
2892 :
2893 : /** Get the scale value to apply to raw values.
2894 : *
2895 : * unscaled_value = raw_value * GetScale() + GetOffset()
2896 : *
2897 : * This is the same as the C function GDALMDArrayGetScale().
2898 : *
2899 : * @note Driver implementation: this method shall be implemented if gettings
2900 : * scale is supported.
2901 : *
2902 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2903 : * a scale value exists. Might be nullptr.
2904 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2905 : * the storage type of the scale value, when known/relevant. Otherwise will be
2906 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2907 : *
2908 : * @return the scale value. A 1.0 value might also indicate the
2909 : * absence of a scale value.
2910 : */
2911 13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
2912 : CPL_UNUSED GDALDataType *peStorageType) const
2913 : {
2914 13 : if (pbHasScale)
2915 13 : *pbHasScale = false;
2916 13 : return 1.0;
2917 : }
2918 :
2919 : /************************************************************************/
2920 : /* GetOffset() */
2921 : /************************************************************************/
2922 :
2923 : /** Get the offset value to apply to raw values.
2924 : *
2925 : * unscaled_value = raw_value * GetScale() + GetOffset()
2926 : *
2927 : * This is the same as the C function GDALMDArrayGetOffset().
2928 : *
2929 : * @note Driver implementation: this method shall be implemented if gettings
2930 : * offset is supported.
2931 : *
2932 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
2933 : * a offset value exists. Might be nullptr.
2934 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2935 : * the storage type of the offset value, when known/relevant. Otherwise will be
2936 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2937 : *
2938 : * @return the offset value. A 0.0 value might also indicate the
2939 : * absence of a offset value.
2940 : */
2941 13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
2942 : CPL_UNUSED GDALDataType *peStorageType) const
2943 : {
2944 13 : if (pbHasOffset)
2945 13 : *pbHasOffset = false;
2946 13 : return 0.0;
2947 : }
2948 :
2949 : /************************************************************************/
2950 : /* ProcessPerChunk() */
2951 : /************************************************************************/
2952 :
2953 : namespace
2954 : {
2955 : enum class Caller
2956 : {
2957 : CALLER_END_OF_LOOP,
2958 : CALLER_IN_LOOP,
2959 : };
2960 : }
2961 :
2962 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
2963 : *
2964 : * This method is to be used when doing operations on an array, or a subset of
2965 : * it, in a chunk by chunk way.
2966 : *
2967 : * @param arrayStartIdx Values representing the starting index to use
2968 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2969 : * Array of GetDimensionCount() values. Must not be
2970 : * nullptr, unless for a zero-dimensional array.
2971 : *
2972 : * @param count Values representing the number of values to use in
2973 : * each dimension.
2974 : * Array of GetDimensionCount() values. Must not be
2975 : * nullptr, unless for a zero-dimensional array.
2976 : *
2977 : * @param chunkSize Values representing the chunk size in each dimension.
2978 : * Might typically the output of GetProcessingChunkSize().
2979 : * Array of GetDimensionCount() values. Must not be
2980 : * nullptr, unless for a zero-dimensional array.
2981 : *
2982 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
2983 : * Must NOT be nullptr.
2984 : *
2985 : * @param pUserData Pointer to pass as the value of the pUserData argument
2986 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
2987 : *
2988 : * @return true in case of success.
2989 : */
2990 58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
2991 : const GUInt64 *count,
2992 : const size_t *chunkSize,
2993 : FuncProcessPerChunkType pfnFunc,
2994 : void *pUserData)
2995 : {
2996 58 : const auto &dims = GetDimensions();
2997 58 : if (dims.empty())
2998 : {
2999 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3000 : }
3001 :
3002 : // Sanity check
3003 56 : size_t nTotalChunkSize = 1;
3004 146 : for (size_t i = 0; i < dims.size(); i++)
3005 : {
3006 97 : const auto nSizeThisDim(dims[i]->GetSize());
3007 97 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3008 95 : arrayStartIdx[i] > nSizeThisDim - count[i])
3009 : {
3010 4 : CPLError(CE_Failure, CPLE_AppDefined,
3011 : "Inconsistent arrayStartIdx[] / count[] values "
3012 : "regarding array size");
3013 4 : return false;
3014 : }
3015 184 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3016 91 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3017 : {
3018 3 : CPLError(CE_Failure, CPLE_AppDefined,
3019 : "Inconsistent chunkSize[] values");
3020 3 : return false;
3021 : }
3022 90 : nTotalChunkSize *= chunkSize[i];
3023 : }
3024 :
3025 49 : size_t dimIdx = 0;
3026 98 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3027 98 : std::vector<size_t> chunkCount(dims.size());
3028 :
3029 : struct Stack
3030 : {
3031 : GUInt64 nBlockCounter = 0;
3032 : GUInt64 nBlocksMinusOne = 0;
3033 : size_t first_count = 0; // only used if nBlocks > 1
3034 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3035 : };
3036 :
3037 98 : std::vector<Stack> stack(dims.size());
3038 49 : GUInt64 iCurChunk = 0;
3039 49 : GUInt64 nChunkCount = 1;
3040 138 : for (size_t i = 0; i < dims.size(); i++)
3041 : {
3042 89 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3043 89 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3044 89 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3045 89 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3046 89 : if (stack[i].nBlocksMinusOne == 0)
3047 : {
3048 84 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3049 84 : chunkCount[i] = static_cast<size_t>(count[i]);
3050 : }
3051 : else
3052 : {
3053 5 : stack[i].first_count = static_cast<size_t>(
3054 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3055 : }
3056 : }
3057 :
3058 49 : lbl_next_depth:
3059 248 : if (dimIdx == dims.size())
3060 : {
3061 82 : ++iCurChunk;
3062 82 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3063 : iCurChunk, nChunkCount, pUserData))
3064 : {
3065 0 : return false;
3066 : }
3067 : }
3068 : else
3069 : {
3070 166 : if (stack[dimIdx].nBlocksMinusOne != 0)
3071 : {
3072 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3073 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3074 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3075 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3076 : while (true)
3077 : {
3078 33 : dimIdx++;
3079 33 : goto lbl_next_depth;
3080 33 : lbl_return_to_caller_in_loop:
3081 33 : --stack[dimIdx].nBlockCounter;
3082 33 : if (stack[dimIdx].nBlockCounter == 0)
3083 11 : break;
3084 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3085 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3086 : }
3087 :
3088 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3089 22 : chunkCount[dimIdx] =
3090 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3091 11 : chunkArrayStartIdx[dimIdx]);
3092 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3093 : }
3094 166 : dimIdx++;
3095 166 : goto lbl_next_depth;
3096 166 : lbl_return_to_caller_end_of_loop:
3097 166 : if (dimIdx == 0)
3098 49 : goto end;
3099 : }
3100 :
3101 199 : assert(dimIdx > 0);
3102 199 : dimIdx--;
3103 : // cppcheck-suppress negativeContainerIndex
3104 199 : switch (stack[dimIdx].return_point)
3105 : {
3106 166 : case Caller::CALLER_END_OF_LOOP:
3107 166 : goto lbl_return_to_caller_end_of_loop;
3108 33 : case Caller::CALLER_IN_LOOP:
3109 33 : goto lbl_return_to_caller_in_loop;
3110 : }
3111 49 : end:
3112 49 : return true;
3113 : }
3114 :
3115 : /************************************************************************/
3116 : /* GDALAttribute() */
3117 : /************************************************************************/
3118 :
3119 : //! @cond Doxygen_Suppress
3120 13997 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3121 0 : CPL_UNUSED const std::string &osName)
3122 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3123 13997 : : GDALAbstractMDArray(osParentName, osName)
3124 : #endif
3125 : {
3126 13997 : }
3127 :
3128 : //! @endcond
3129 :
3130 : /************************************************************************/
3131 : /* GetDimensionSize() */
3132 : /************************************************************************/
3133 :
3134 : /** Return the size of the dimensions of the attribute.
3135 : *
3136 : * This will be an empty array for a scalar (single value) attribute.
3137 : *
3138 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3139 : */
3140 361 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3141 : {
3142 361 : const auto &dims = GetDimensions();
3143 361 : std::vector<GUInt64> ret;
3144 361 : ret.reserve(dims.size());
3145 453 : for (const auto &dim : dims)
3146 92 : ret.push_back(dim->GetSize());
3147 361 : return ret;
3148 : }
3149 :
3150 : /************************************************************************/
3151 : /* GDALRawResult() */
3152 : /************************************************************************/
3153 :
3154 : //! @cond Doxygen_Suppress
3155 149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3156 149 : size_t nEltCount)
3157 298 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3158 149 : m_raw(raw)
3159 : {
3160 149 : }
3161 :
3162 : //! @endcond
3163 :
3164 : /************************************************************************/
3165 : /* GDALRawResult() */
3166 : /************************************************************************/
3167 :
3168 : /** Move constructor. */
3169 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3170 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3171 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3172 : {
3173 0 : other.m_nEltCount = 0;
3174 0 : other.m_nSize = 0;
3175 0 : other.m_raw = nullptr;
3176 0 : }
3177 :
3178 : /************************************************************************/
3179 : /* FreeMe() */
3180 : /************************************************************************/
3181 :
3182 149 : void GDALRawResult::FreeMe()
3183 : {
3184 149 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3185 : {
3186 47 : GByte *pabyPtr = m_raw;
3187 47 : const auto nDTSize(m_dt.GetSize());
3188 94 : for (size_t i = 0; i < m_nEltCount; ++i)
3189 : {
3190 47 : m_dt.FreeDynamicMemory(pabyPtr);
3191 47 : pabyPtr += nDTSize;
3192 : }
3193 : }
3194 149 : VSIFree(m_raw);
3195 149 : }
3196 :
3197 : /************************************************************************/
3198 : /* operator=() */
3199 : /************************************************************************/
3200 :
3201 : /** Move assignment. */
3202 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3203 : {
3204 0 : FreeMe();
3205 0 : m_dt = std::move(other.m_dt);
3206 0 : m_nEltCount = other.m_nEltCount;
3207 0 : m_nSize = other.m_nSize;
3208 0 : m_raw = other.m_raw;
3209 0 : other.m_nEltCount = 0;
3210 0 : other.m_nSize = 0;
3211 0 : other.m_raw = nullptr;
3212 0 : return *this;
3213 : }
3214 :
3215 : /************************************************************************/
3216 : /* ~GDALRawResult() */
3217 : /************************************************************************/
3218 :
3219 : /** Destructor. */
3220 149 : GDALRawResult::~GDALRawResult()
3221 : {
3222 149 : FreeMe();
3223 149 : }
3224 :
3225 : /************************************************************************/
3226 : /* StealData() */
3227 : /************************************************************************/
3228 :
3229 : //! @cond Doxygen_Suppress
3230 : /** Return buffer to caller which becomes owner of it.
3231 : * Only to be used by GDALAttributeReadAsRaw().
3232 : */
3233 6 : GByte *GDALRawResult::StealData()
3234 : {
3235 6 : GByte *ret = m_raw;
3236 6 : m_raw = nullptr;
3237 6 : m_nEltCount = 0;
3238 6 : m_nSize = 0;
3239 6 : return ret;
3240 : }
3241 :
3242 : //! @endcond
3243 :
3244 : /************************************************************************/
3245 : /* ReadAsRaw() */
3246 : /************************************************************************/
3247 :
3248 : /** Return the raw value of an attribute.
3249 : *
3250 : *
3251 : * This is the same as the C function GDALAttributeReadAsRaw().
3252 : */
3253 149 : GDALRawResult GDALAttribute::ReadAsRaw() const
3254 : {
3255 149 : const auto nEltCount(GetTotalElementsCount());
3256 149 : const auto &dt(GetDataType());
3257 149 : const auto nDTSize(dt.GetSize());
3258 : GByte *res = static_cast<GByte *>(
3259 149 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3260 149 : if (!res)
3261 0 : return GDALRawResult(nullptr, dt, 0);
3262 149 : const auto &dims = GetDimensions();
3263 149 : const auto nDims = GetDimensionCount();
3264 298 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3265 298 : std::vector<size_t> count(1 + nDims);
3266 168 : for (size_t i = 0; i < nDims; i++)
3267 : {
3268 19 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3269 : }
3270 149 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3271 149 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3272 : {
3273 0 : VSIFree(res);
3274 0 : return GDALRawResult(nullptr, dt, 0);
3275 : }
3276 149 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3277 : }
3278 :
3279 : /************************************************************************/
3280 : /* ReadAsString() */
3281 : /************************************************************************/
3282 :
3283 : /** Return the value of an attribute as a string.
3284 : *
3285 : * The returned string should not be freed, and its lifetime does not
3286 : * excess a next call to ReadAsString() on the same object, or the deletion
3287 : * of the object itself.
3288 : *
3289 : * This function will only return the first element if there are several.
3290 : *
3291 : * This is the same as the C function GDALAttributeReadAsString()
3292 : *
3293 : * @return a string, or nullptr.
3294 : */
3295 1301 : const char *GDALAttribute::ReadAsString() const
3296 : {
3297 1301 : const auto nDims = GetDimensionCount();
3298 2602 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3299 2602 : std::vector<size_t> count(1 + nDims, 1);
3300 1301 : char *szRet = nullptr;
3301 1301 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3302 1301 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3303 3902 : sizeof(szRet)) ||
3304 1300 : szRet == nullptr)
3305 : {
3306 4 : return nullptr;
3307 : }
3308 1297 : m_osCachedVal = szRet;
3309 1297 : CPLFree(szRet);
3310 1297 : return m_osCachedVal.c_str();
3311 : }
3312 :
3313 : /************************************************************************/
3314 : /* ReadAsInt() */
3315 : /************************************************************************/
3316 :
3317 : /** Return the value of an attribute as a integer.
3318 : *
3319 : * This function will only return the first element if there are several.
3320 : *
3321 : * It can fail if its value can not be converted to integer.
3322 : *
3323 : * This is the same as the C function GDALAttributeReadAsInt()
3324 : *
3325 : * @return a integer, or INT_MIN in case of error.
3326 : */
3327 218 : int GDALAttribute::ReadAsInt() const
3328 : {
3329 218 : const auto nDims = GetDimensionCount();
3330 436 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3331 218 : std::vector<size_t> count(1 + nDims, 1);
3332 218 : int nRet = INT_MIN;
3333 218 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3334 436 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3335 436 : return nRet;
3336 : }
3337 :
3338 : /************************************************************************/
3339 : /* ReadAsInt64() */
3340 : /************************************************************************/
3341 :
3342 : /** Return the value of an attribute as an int64_t.
3343 : *
3344 : * This function will only return the first element if there are several.
3345 : *
3346 : * It can fail if its value can not be converted to long.
3347 : *
3348 : * This is the same as the C function GDALAttributeReadAsInt64()
3349 : *
3350 : * @return an int64_t, or INT64_MIN in case of error.
3351 : */
3352 54 : int64_t GDALAttribute::ReadAsInt64() const
3353 : {
3354 54 : const auto nDims = GetDimensionCount();
3355 108 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3356 54 : std::vector<size_t> count(1 + nDims, 1);
3357 54 : int64_t nRet = INT64_MIN;
3358 54 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3359 108 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3360 108 : return nRet;
3361 : }
3362 :
3363 : /************************************************************************/
3364 : /* ReadAsDouble() */
3365 : /************************************************************************/
3366 :
3367 : /** Return the value of an attribute as a double.
3368 : *
3369 : * This function will only return the first element if there are several.
3370 : *
3371 : * It can fail if its value can not be converted to double.
3372 : *
3373 : * This is the same as the C function GDALAttributeReadAsInt()
3374 : *
3375 : * @return a double value.
3376 : */
3377 339 : double GDALAttribute::ReadAsDouble() const
3378 : {
3379 339 : const auto nDims = GetDimensionCount();
3380 678 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3381 339 : std::vector<size_t> count(1 + nDims, 1);
3382 339 : double dfRet = 0;
3383 339 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3384 339 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3385 339 : sizeof(dfRet));
3386 678 : return dfRet;
3387 : }
3388 :
3389 : /************************************************************************/
3390 : /* ReadAsStringArray() */
3391 : /************************************************************************/
3392 :
3393 : /** Return the value of an attribute as an array of strings.
3394 : *
3395 : * This is the same as the C function GDALAttributeReadAsStringArray()
3396 : */
3397 104 : CPLStringList GDALAttribute::ReadAsStringArray() const
3398 : {
3399 104 : const auto nElts = GetTotalElementsCount();
3400 104 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3401 0 : return CPLStringList();
3402 : char **papszList = static_cast<char **>(
3403 104 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3404 104 : const auto &dims = GetDimensions();
3405 104 : const auto nDims = GetDimensionCount();
3406 208 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3407 208 : std::vector<size_t> count(1 + nDims);
3408 157 : for (size_t i = 0; i < nDims; i++)
3409 : {
3410 53 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3411 : }
3412 104 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3413 104 : GDALExtendedDataType::CreateString(), papszList, papszList,
3414 104 : sizeof(char *) * static_cast<int>(nElts));
3415 269 : for (int i = 0; i < static_cast<int>(nElts); i++)
3416 : {
3417 165 : if (papszList[i] == nullptr)
3418 13 : papszList[i] = CPLStrdup("");
3419 : }
3420 104 : return CPLStringList(papszList);
3421 : }
3422 :
3423 : /************************************************************************/
3424 : /* ReadAsIntArray() */
3425 : /************************************************************************/
3426 :
3427 : /** Return the value of an attribute as an array of integers.
3428 : *
3429 : * This is the same as the C function GDALAttributeReadAsIntArray().
3430 : */
3431 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3432 : {
3433 15 : const auto nElts = GetTotalElementsCount();
3434 : #if SIZEOF_VOIDP == 4
3435 : if (nElts > static_cast<size_t>(nElts))
3436 : return {};
3437 : #endif
3438 15 : std::vector<int> res(static_cast<size_t>(nElts));
3439 15 : const auto &dims = GetDimensions();
3440 15 : const auto nDims = GetDimensionCount();
3441 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3442 30 : std::vector<size_t> count(1 + nDims);
3443 32 : for (size_t i = 0; i < nDims; i++)
3444 : {
3445 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3446 : }
3447 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3448 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3449 15 : res.size() * sizeof(res[0]));
3450 30 : return res;
3451 : }
3452 :
3453 : /************************************************************************/
3454 : /* ReadAsInt64Array() */
3455 : /************************************************************************/
3456 :
3457 : /** Return the value of an attribute as an array of int64_t.
3458 : *
3459 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3460 : */
3461 38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3462 : {
3463 38 : const auto nElts = GetTotalElementsCount();
3464 : #if SIZEOF_VOIDP == 4
3465 : if (nElts > static_cast<size_t>(nElts))
3466 : return {};
3467 : #endif
3468 38 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3469 38 : const auto &dims = GetDimensions();
3470 38 : const auto nDims = GetDimensionCount();
3471 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3472 76 : std::vector<size_t> count(1 + nDims);
3473 76 : for (size_t i = 0; i < nDims; i++)
3474 : {
3475 38 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3476 : }
3477 38 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3478 76 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3479 38 : res.size() * sizeof(res[0]));
3480 76 : return res;
3481 : }
3482 :
3483 : /************************************************************************/
3484 : /* ReadAsDoubleArray() */
3485 : /************************************************************************/
3486 :
3487 : /** Return the value of an attribute as an array of double.
3488 : *
3489 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3490 : */
3491 87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3492 : {
3493 87 : const auto nElts = GetTotalElementsCount();
3494 : #if SIZEOF_VOIDP == 4
3495 : if (nElts > static_cast<size_t>(nElts))
3496 : return {};
3497 : #endif
3498 87 : std::vector<double> res(static_cast<size_t>(nElts));
3499 87 : const auto &dims = GetDimensions();
3500 87 : const auto nDims = GetDimensionCount();
3501 174 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3502 174 : std::vector<size_t> count(1 + nDims);
3503 158 : for (size_t i = 0; i < nDims; i++)
3504 : {
3505 71 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3506 : }
3507 87 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3508 174 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3509 87 : res.size() * sizeof(res[0]));
3510 174 : return res;
3511 : }
3512 :
3513 : /************************************************************************/
3514 : /* Write() */
3515 : /************************************************************************/
3516 :
3517 : /** Write an attribute from raw values expressed in GetDataType()
3518 : *
3519 : * The values should be provided in the type of GetDataType() and there should
3520 : * be exactly GetTotalElementsCount() of them.
3521 : * If GetDataType() is a string, each value should be a char* pointer.
3522 : *
3523 : * This is the same as the C function GDALAttributeWriteRaw().
3524 : *
3525 : * @param pabyValue Buffer of nLen bytes.
3526 : * @param nLen Size of pabyValue in bytes. Should be equal to
3527 : * GetTotalElementsCount() * GetDataType().GetSize()
3528 : * @return true in case of success.
3529 : */
3530 91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3531 : {
3532 91 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3533 : {
3534 0 : CPLError(CE_Failure, CPLE_AppDefined,
3535 : "Length is not of expected value");
3536 0 : return false;
3537 : }
3538 91 : const auto &dims = GetDimensions();
3539 91 : const auto nDims = GetDimensionCount();
3540 182 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3541 182 : std::vector<size_t> count(1 + nDims);
3542 114 : for (size_t i = 0; i < nDims; i++)
3543 : {
3544 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3545 : }
3546 91 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3547 91 : pabyValue, pabyValue, nLen);
3548 : }
3549 :
3550 : /************************************************************************/
3551 : /* Write() */
3552 : /************************************************************************/
3553 :
3554 : /** Write an attribute from a string value.
3555 : *
3556 : * Type conversion will be performed if needed. If the attribute contains
3557 : * multiple values, only the first one will be updated.
3558 : *
3559 : * This is the same as the C function GDALAttributeWriteString().
3560 : *
3561 : * @param pszValue Pointer to a string.
3562 : * @return true in case of success.
3563 : */
3564 303 : bool GDALAttribute::Write(const char *pszValue)
3565 : {
3566 303 : const auto nDims = GetDimensionCount();
3567 606 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3568 303 : std::vector<size_t> count(1 + nDims, 1);
3569 303 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3570 606 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3571 606 : sizeof(pszValue));
3572 : }
3573 :
3574 : /************************************************************************/
3575 : /* WriteInt() */
3576 : /************************************************************************/
3577 :
3578 : /** Write an attribute from a integer value.
3579 : *
3580 : * Type conversion will be performed if needed. If the attribute contains
3581 : * multiple values, only the first one will be updated.
3582 : *
3583 : * This is the same as the C function GDALAttributeWriteInt().
3584 : *
3585 : * @param nVal Value.
3586 : * @return true in case of success.
3587 : */
3588 22 : bool GDALAttribute::WriteInt(int nVal)
3589 : {
3590 22 : const auto nDims = GetDimensionCount();
3591 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3592 22 : std::vector<size_t> count(1 + nDims, 1);
3593 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3594 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3595 44 : sizeof(nVal));
3596 : }
3597 :
3598 : /************************************************************************/
3599 : /* WriteInt64() */
3600 : /************************************************************************/
3601 :
3602 : /** Write an attribute from an int64_t value.
3603 : *
3604 : * Type conversion will be performed if needed. If the attribute contains
3605 : * multiple values, only the first one will be updated.
3606 : *
3607 : * This is the same as the C function GDALAttributeWriteInt().
3608 : *
3609 : * @param nVal Value.
3610 : * @return true in case of success.
3611 : */
3612 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3613 : {
3614 11 : const auto nDims = GetDimensionCount();
3615 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3616 11 : std::vector<size_t> count(1 + nDims, 1);
3617 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3618 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3619 22 : sizeof(nVal));
3620 : }
3621 :
3622 : /************************************************************************/
3623 : /* Write() */
3624 : /************************************************************************/
3625 :
3626 : /** Write an attribute from a double value.
3627 : *
3628 : * Type conversion will be performed if needed. If the attribute contains
3629 : * multiple values, only the first one will be updated.
3630 : *
3631 : * This is the same as the C function GDALAttributeWriteDouble().
3632 : *
3633 : * @param dfVal Value.
3634 : * @return true in case of success.
3635 : */
3636 36 : bool GDALAttribute::Write(double dfVal)
3637 : {
3638 36 : const auto nDims = GetDimensionCount();
3639 72 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3640 36 : std::vector<size_t> count(1 + nDims, 1);
3641 36 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3642 72 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3643 72 : sizeof(dfVal));
3644 : }
3645 :
3646 : /************************************************************************/
3647 : /* Write() */
3648 : /************************************************************************/
3649 :
3650 : /** Write an attribute from an array of strings.
3651 : *
3652 : * Type conversion will be performed if needed.
3653 : *
3654 : * Exactly GetTotalElementsCount() strings must be provided
3655 : *
3656 : * This is the same as the C function GDALAttributeWriteStringArray().
3657 : *
3658 : * @param vals Array of strings.
3659 : * @return true in case of success.
3660 : */
3661 8 : bool GDALAttribute::Write(CSLConstList vals)
3662 : {
3663 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3664 : {
3665 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3666 1 : return false;
3667 : }
3668 7 : const auto nDims = GetDimensionCount();
3669 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3670 7 : std::vector<size_t> count(1 + nDims);
3671 7 : const auto &dims = GetDimensions();
3672 15 : for (size_t i = 0; i < nDims; i++)
3673 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3674 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3675 7 : GDALExtendedDataType::CreateString(), vals, vals,
3676 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3677 : }
3678 :
3679 : /************************************************************************/
3680 : /* Write() */
3681 : /************************************************************************/
3682 :
3683 : /** Write an attribute from an array of int.
3684 : *
3685 : * Type conversion will be performed if needed.
3686 : *
3687 : * Exactly GetTotalElementsCount() strings must be provided
3688 : *
3689 : * This is the same as the C function GDALAttributeWriteIntArray()
3690 : *
3691 : * @param vals Array of int.
3692 : * @param nVals Should be equal to GetTotalElementsCount().
3693 : * @return true in case of success.
3694 : */
3695 9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3696 : {
3697 9 : if (nVals != GetTotalElementsCount())
3698 : {
3699 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3700 1 : return false;
3701 : }
3702 8 : const auto nDims = GetDimensionCount();
3703 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3704 8 : std::vector<size_t> count(1 + nDims);
3705 8 : const auto &dims = GetDimensions();
3706 16 : for (size_t i = 0; i < nDims; i++)
3707 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3708 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3709 8 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3710 16 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3711 : }
3712 :
3713 : /************************************************************************/
3714 : /* Write() */
3715 : /************************************************************************/
3716 :
3717 : /** Write an attribute from an array of int64_t.
3718 : *
3719 : * Type conversion will be performed if needed.
3720 : *
3721 : * Exactly GetTotalElementsCount() strings must be provided
3722 : *
3723 : * This is the same as the C function GDALAttributeWriteLongArray()
3724 : *
3725 : * @param vals Array of int64_t.
3726 : * @param nVals Should be equal to GetTotalElementsCount().
3727 : * @return true in case of success.
3728 : */
3729 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3730 : {
3731 10 : if (nVals != GetTotalElementsCount())
3732 : {
3733 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3734 0 : return false;
3735 : }
3736 10 : const auto nDims = GetDimensionCount();
3737 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3738 10 : std::vector<size_t> count(1 + nDims);
3739 10 : const auto &dims = GetDimensions();
3740 20 : for (size_t i = 0; i < nDims; i++)
3741 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3742 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3743 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3744 10 : static_cast<size_t>(GetTotalElementsCount()) *
3745 10 : sizeof(int64_t));
3746 : }
3747 :
3748 : /************************************************************************/
3749 : /* Write() */
3750 : /************************************************************************/
3751 :
3752 : /** Write an attribute from an array of double.
3753 : *
3754 : * Type conversion will be performed if needed.
3755 : *
3756 : * Exactly GetTotalElementsCount() strings must be provided
3757 : *
3758 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3759 : *
3760 : * @param vals Array of double.
3761 : * @param nVals Should be equal to GetTotalElementsCount().
3762 : * @return true in case of success.
3763 : */
3764 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3765 : {
3766 7 : if (nVals != GetTotalElementsCount())
3767 : {
3768 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3769 1 : return false;
3770 : }
3771 6 : const auto nDims = GetDimensionCount();
3772 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3773 6 : std::vector<size_t> count(1 + nDims);
3774 6 : const auto &dims = GetDimensions();
3775 13 : for (size_t i = 0; i < nDims; i++)
3776 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3777 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3778 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3779 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3780 : }
3781 :
3782 : /************************************************************************/
3783 : /* GDALMDArray() */
3784 : /************************************************************************/
3785 :
3786 : //! @cond Doxygen_Suppress
3787 6199 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3788 : CPL_UNUSED const std::string &osName,
3789 0 : const std::string &osContext)
3790 : :
3791 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3792 : GDALAbstractMDArray(osParentName, osName),
3793 : #endif
3794 6199 : m_osContext(osContext)
3795 : {
3796 6199 : }
3797 :
3798 : //! @endcond
3799 :
3800 : /************************************************************************/
3801 : /* GetTotalCopyCost() */
3802 : /************************************************************************/
3803 :
3804 : /** Return a total "cost" to copy the array.
3805 : *
3806 : * Used as a parameter for CopyFrom()
3807 : */
3808 43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3809 : {
3810 86 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3811 86 : GetTotalElementsCount() * GetDataType().GetSize();
3812 : }
3813 :
3814 : /************************************************************************/
3815 : /* CopyFromAllExceptValues() */
3816 : /************************************************************************/
3817 :
3818 : //! @cond Doxygen_Suppress
3819 :
3820 144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3821 : bool bStrict, GUInt64 &nCurCost,
3822 : const GUInt64 nTotalCost,
3823 : GDALProgressFunc pfnProgress,
3824 : void *pProgressData)
3825 : {
3826 : // Nodata setting must be one of the first things done for TileDB
3827 144 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3828 144 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3829 : {
3830 13 : SetRawNoDataValue(pNoData);
3831 : }
3832 :
3833 144 : const bool bThisIsUnscaledArray =
3834 144 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3835 288 : auto attrs = poSrcArray->GetAttributes();
3836 191 : for (const auto &attr : attrs)
3837 : {
3838 47 : const auto &osAttrName = attr->GetName();
3839 47 : if (bThisIsUnscaledArray)
3840 : {
3841 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3842 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3843 1 : osAttrName == "valid_range")
3844 : {
3845 1 : continue;
3846 : }
3847 : }
3848 :
3849 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3850 92 : attr->GetDataType());
3851 46 : if (!dstAttr)
3852 : {
3853 0 : if (bStrict)
3854 0 : return false;
3855 0 : continue;
3856 : }
3857 46 : auto raw = attr->ReadAsRaw();
3858 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3859 0 : return false;
3860 : }
3861 144 : if (!attrs.empty())
3862 : {
3863 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3864 46 : if (pfnProgress &&
3865 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3866 0 : return false;
3867 : }
3868 :
3869 144 : auto srcSRS = poSrcArray->GetSpatialRef();
3870 144 : if (srcSRS)
3871 : {
3872 11 : SetSpatialRef(srcSRS.get());
3873 : }
3874 :
3875 144 : const std::string &osUnit(poSrcArray->GetUnit());
3876 144 : if (!osUnit.empty())
3877 : {
3878 18 : SetUnit(osUnit);
3879 : }
3880 :
3881 144 : bool bGotValue = false;
3882 144 : GDALDataType eOffsetStorageType = GDT_Unknown;
3883 : const double dfOffset =
3884 144 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3885 144 : if (bGotValue)
3886 : {
3887 3 : SetOffset(dfOffset, eOffsetStorageType);
3888 : }
3889 :
3890 144 : bGotValue = false;
3891 144 : GDALDataType eScaleStorageType = GDT_Unknown;
3892 144 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3893 144 : if (bGotValue)
3894 : {
3895 3 : SetScale(dfScale, eScaleStorageType);
3896 : }
3897 :
3898 144 : return true;
3899 : }
3900 :
3901 : //! @endcond
3902 :
3903 : /************************************************************************/
3904 : /* CopyFrom() */
3905 : /************************************************************************/
3906 :
3907 : /** Copy the content of an array into a new (generally empty) array.
3908 : *
3909 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
3910 : * of some output drivers this is not recommended)
3911 : * @param poSrcArray Source array. Should NOT be nullptr.
3912 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
3913 : * stop the copy. In relaxed mode, the copy will be attempted to
3914 : * be pursued.
3915 : * @param nCurCost Should be provided as a variable initially set to 0.
3916 : * @param nTotalCost Total cost from GetTotalCopyCost().
3917 : * @param pfnProgress Progress callback, or nullptr.
3918 : * @param pProgressData Progress user data, or nulptr.
3919 : *
3920 : * @return true in case of success (or partial success if bStrict == false).
3921 : */
3922 41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
3923 : const GDALMDArray *poSrcArray, bool bStrict,
3924 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
3925 : GDALProgressFunc pfnProgress, void *pProgressData)
3926 : {
3927 41 : if (pfnProgress == nullptr)
3928 4 : pfnProgress = GDALDummyProgress;
3929 :
3930 41 : nCurCost += GDALMDArray::COPY_COST;
3931 :
3932 41 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
3933 : pfnProgress, pProgressData))
3934 : {
3935 0 : return false;
3936 : }
3937 :
3938 41 : const auto &dims = poSrcArray->GetDimensions();
3939 41 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
3940 41 : if (dims.empty())
3941 : {
3942 2 : std::vector<GByte> abyTmp(nDTSize);
3943 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
3944 2 : GetDataType(), &abyTmp[0]) &&
3945 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
3946 4 : &abyTmp[0])) &&
3947 : bStrict)
3948 : {
3949 0 : return false;
3950 : }
3951 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
3952 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3953 0 : return false;
3954 : }
3955 : else
3956 : {
3957 39 : std::vector<GUInt64> arrayStartIdx(dims.size());
3958 39 : std::vector<GUInt64> count(dims.size());
3959 106 : for (size_t i = 0; i < dims.size(); i++)
3960 : {
3961 67 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3962 : }
3963 :
3964 : struct CopyFunc
3965 : {
3966 : GDALMDArray *poDstArray = nullptr;
3967 : std::vector<GByte> abyTmp{};
3968 : GDALProgressFunc pfnProgress = nullptr;
3969 : void *pProgressData = nullptr;
3970 : GUInt64 nCurCost = 0;
3971 : GUInt64 nTotalCost = 0;
3972 : GUInt64 nTotalBytesThisArray = 0;
3973 : bool bStop = false;
3974 :
3975 57 : static bool f(GDALAbstractMDArray *l_poSrcArray,
3976 : const GUInt64 *chunkArrayStartIdx,
3977 : const size_t *chunkCount, GUInt64 iCurChunk,
3978 : GUInt64 nChunkCount, void *pUserData)
3979 : {
3980 57 : const auto &dt(l_poSrcArray->GetDataType());
3981 57 : auto data = static_cast<CopyFunc *>(pUserData);
3982 57 : auto poDstArray = data->poDstArray;
3983 57 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
3984 57 : nullptr, dt, &data->abyTmp[0]))
3985 : {
3986 0 : return false;
3987 : }
3988 : bool bRet =
3989 57 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
3990 57 : nullptr, dt, &data->abyTmp[0]);
3991 57 : if (dt.NeedsFreeDynamicMemory())
3992 : {
3993 2 : const auto l_nDTSize = dt.GetSize();
3994 2 : GByte *ptr = &data->abyTmp[0];
3995 2 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
3996 2 : size_t nEltCount = 1;
3997 4 : for (size_t i = 0; i < l_nDims; ++i)
3998 : {
3999 2 : nEltCount *= chunkCount[i];
4000 : }
4001 10 : for (size_t i = 0; i < nEltCount; i++)
4002 : {
4003 8 : dt.FreeDynamicMemory(ptr);
4004 8 : ptr += l_nDTSize;
4005 : }
4006 : }
4007 57 : if (!bRet)
4008 : {
4009 0 : return false;
4010 : }
4011 :
4012 57 : double dfCurCost =
4013 57 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4014 57 : data->nTotalBytesThisArray;
4015 57 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4016 : data->pProgressData))
4017 : {
4018 0 : data->bStop = true;
4019 0 : return false;
4020 : }
4021 :
4022 57 : return true;
4023 : }
4024 : };
4025 :
4026 39 : CopyFunc copyFunc;
4027 39 : copyFunc.poDstArray = this;
4028 39 : copyFunc.nCurCost = nCurCost;
4029 39 : copyFunc.nTotalCost = nTotalCost;
4030 39 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4031 39 : copyFunc.pfnProgress = pfnProgress;
4032 39 : copyFunc.pProgressData = pProgressData;
4033 : const char *pszSwathSize =
4034 39 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4035 : const size_t nMaxChunkSize =
4036 : pszSwathSize
4037 39 : ? static_cast<size_t>(
4038 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4039 1 : CPLAtoGIntBig(pszSwathSize)))
4040 : : static_cast<size_t>(
4041 38 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4042 38 : GDALGetCacheMax64() / 4));
4043 39 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4044 39 : size_t nRealChunkSize = nDTSize;
4045 106 : for (const auto &nChunkSize : anChunkSizes)
4046 : {
4047 67 : nRealChunkSize *= nChunkSize;
4048 : }
4049 : try
4050 : {
4051 39 : copyFunc.abyTmp.resize(nRealChunkSize);
4052 : }
4053 0 : catch (const std::exception &)
4054 : {
4055 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4056 : "Cannot allocate temporary buffer");
4057 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4058 0 : return false;
4059 : }
4060 116 : if (copyFunc.nTotalBytesThisArray != 0 &&
4061 38 : !const_cast<GDALMDArray *>(poSrcArray)
4062 38 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4063 : anChunkSizes.data(), CopyFunc::f,
4064 77 : ©Func) &&
4065 0 : (bStrict || copyFunc.bStop))
4066 : {
4067 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4068 0 : return false;
4069 : }
4070 39 : nCurCost += copyFunc.nTotalBytesThisArray;
4071 : }
4072 :
4073 41 : return true;
4074 : }
4075 :
4076 : /************************************************************************/
4077 : /* GetStructuralInfo() */
4078 : /************************************************************************/
4079 :
4080 : /** Return structural information on the array.
4081 : *
4082 : * This may be the compression, etc..
4083 : *
4084 : * The return value should not be freed and is valid until GDALMDArray is
4085 : * released or this function called again.
4086 : *
4087 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4088 : */
4089 55 : CSLConstList GDALMDArray::GetStructuralInfo() const
4090 : {
4091 55 : return nullptr;
4092 : }
4093 :
4094 : /************************************************************************/
4095 : /* AdviseRead() */
4096 : /************************************************************************/
4097 :
4098 : /** Advise driver of upcoming read requests.
4099 : *
4100 : * Some GDAL drivers operate more efficiently if they know in advance what
4101 : * set of upcoming read requests will be made. The AdviseRead() method allows
4102 : * an application to notify the driver of the region of interest.
4103 : *
4104 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4105 : * accelerate access via some drivers. One such case is when reading through
4106 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4107 : * with the region of interest defined by AdviseRead())
4108 : *
4109 : * This is the same as the C function GDALMDArrayAdviseRead().
4110 : *
4111 : * @param arrayStartIdx Values representing the starting index to read
4112 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4113 : * Array of GetDimensionCount() values.
4114 : * Can be nullptr as a synonymous for [0 for i in
4115 : * range(GetDimensionCount() ]
4116 : *
4117 : * @param count Values representing the number of values to extract in
4118 : * each dimension.
4119 : * Array of GetDimensionCount() values.
4120 : * Can be nullptr as a synonymous for
4121 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4122 : * range(GetDimensionCount() ]
4123 : *
4124 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4125 : * documentation.
4126 : *
4127 : * @return true in case of success (ignoring the advice is a success)
4128 : *
4129 : * @since GDAL 3.2
4130 : */
4131 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4132 : CSLConstList papszOptions) const
4133 : {
4134 25 : const auto nDimCount = GetDimensionCount();
4135 25 : if (nDimCount == 0)
4136 2 : return true;
4137 :
4138 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4139 23 : if (arrayStartIdx == nullptr)
4140 : {
4141 0 : tmp_arrayStartIdx.resize(nDimCount);
4142 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4143 : }
4144 :
4145 46 : std::vector<size_t> tmp_count;
4146 23 : if (count == nullptr)
4147 : {
4148 0 : tmp_count.resize(nDimCount);
4149 0 : const auto &dims = GetDimensions();
4150 0 : for (size_t i = 0; i < nDimCount; i++)
4151 : {
4152 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4153 : #if SIZEOF_VOIDP < 8
4154 : if (nSize != static_cast<size_t>(nSize))
4155 : {
4156 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4157 : return false;
4158 : }
4159 : #endif
4160 0 : tmp_count[i] = static_cast<size_t>(nSize);
4161 : }
4162 0 : count = tmp_count.data();
4163 : }
4164 :
4165 46 : std::vector<GInt64> tmp_arrayStep;
4166 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4167 23 : const GInt64 *arrayStep = nullptr;
4168 23 : const GPtrDiff_t *bufferStride = nullptr;
4169 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4170 46 : GDALExtendedDataType::Create(GDT_Unknown),
4171 : nullptr, nullptr, 0, tmp_arrayStep,
4172 : tmp_bufferStride))
4173 : {
4174 1 : return false;
4175 : }
4176 :
4177 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4178 : }
4179 :
4180 : /************************************************************************/
4181 : /* IAdviseRead() */
4182 : /************************************************************************/
4183 :
4184 : //! @cond Doxygen_Suppress
4185 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4186 : CSLConstList /* papszOptions*/) const
4187 : {
4188 3 : return true;
4189 : }
4190 :
4191 : //! @endcond
4192 :
4193 : /************************************************************************/
4194 : /* MassageName() */
4195 : /************************************************************************/
4196 :
4197 : //! @cond Doxygen_Suppress
4198 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4199 : {
4200 32 : std::string ret;
4201 604 : for (const char ch : inputName)
4202 : {
4203 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4204 138 : ret += '_';
4205 : else
4206 434 : ret += ch;
4207 : }
4208 32 : return ret;
4209 : }
4210 :
4211 : //! @endcond
4212 :
4213 : /************************************************************************/
4214 : /* GetCacheRootGroup() */
4215 : /************************************************************************/
4216 :
4217 : //! @cond Doxygen_Suppress
4218 : std::shared_ptr<GDALGroup>
4219 1345 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4220 : std::string &osCacheFilenameOut) const
4221 : {
4222 1345 : const auto &osFilename = GetFilename();
4223 1345 : if (osFilename.empty())
4224 : {
4225 1 : CPLError(CE_Failure, CPLE_AppDefined,
4226 : "Cannot cache an array with an empty filename");
4227 1 : return nullptr;
4228 : }
4229 :
4230 1344 : osCacheFilenameOut = osFilename + ".gmac";
4231 1344 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4232 : {
4233 0 : const auto nPosQuestionMark = osFilename.find('?');
4234 0 : if (nPosQuestionMark != std::string::npos)
4235 : {
4236 : osCacheFilenameOut =
4237 0 : osFilename.substr(0, nPosQuestionMark)
4238 0 : .append(".gmac")
4239 0 : .append(osFilename.substr(nPosQuestionMark));
4240 : }
4241 : }
4242 1344 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4243 1344 : if (pszProxy != nullptr)
4244 7 : osCacheFilenameOut = pszProxy;
4245 :
4246 1344 : std::unique_ptr<GDALDataset> poDS;
4247 : VSIStatBufL sStat;
4248 1344 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4249 : {
4250 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4251 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4252 : nullptr, nullptr, nullptr));
4253 : }
4254 1344 : if (poDS)
4255 : {
4256 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4257 28 : return poDS->GetRootGroup();
4258 : }
4259 :
4260 1316 : if (bCanCreate)
4261 : {
4262 4 : const char *pszDrvName = "netCDF";
4263 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4264 4 : if (poDrv == nullptr)
4265 : {
4266 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4267 : pszDrvName);
4268 0 : return nullptr;
4269 : }
4270 : {
4271 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4272 8 : CPLErrorStateBackuper oErrorStateBackuper;
4273 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4274 : nullptr, nullptr));
4275 : }
4276 4 : if (!poDS)
4277 : {
4278 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4279 1 : if (pszProxy)
4280 : {
4281 1 : osCacheFilenameOut = pszProxy;
4282 1 : poDS.reset(poDrv->CreateMultiDimensional(
4283 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4284 : }
4285 : }
4286 4 : if (poDS)
4287 : {
4288 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4289 4 : return poDS->GetRootGroup();
4290 : }
4291 : else
4292 : {
4293 0 : CPLError(CE_Failure, CPLE_AppDefined,
4294 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4295 : "configuration option to write the cache in "
4296 : "another directory",
4297 : osCacheFilenameOut.c_str());
4298 : }
4299 : }
4300 :
4301 1312 : return nullptr;
4302 : }
4303 :
4304 : //! @endcond
4305 :
4306 : /************************************************************************/
4307 : /* Cache() */
4308 : /************************************************************************/
4309 :
4310 : /** Cache the content of the array into an auxiliary filename.
4311 : *
4312 : * The main purpose of this method is to be able to cache views that are
4313 : * expensive to compute, such as transposed arrays.
4314 : *
4315 : * The array will be stored in a file whose name is the one of
4316 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4317 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4318 : *
4319 : * If the .gmac file cannot be written next to the dataset, the
4320 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4321 : * directory.
4322 : *
4323 : * The GDALMDArray::Read() method will automatically use the cache when it
4324 : * exists. There is no timestamp checks between the source array and the cached
4325 : * array. If the source arrays changes, the cache must be manually deleted.
4326 : *
4327 : * This is the same as the C function GDALMDArrayCache()
4328 : *
4329 : * @note Driver implementation: optionally implemented.
4330 : *
4331 : * @param papszOptions List of options, null terminated, or NULL. Currently
4332 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4333 : * to specify the block size of the cached array.
4334 : * @return true in case of success.
4335 : */
4336 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4337 : {
4338 14 : std::string osCacheFilename;
4339 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4340 7 : if (!poRG)
4341 1 : return false;
4342 :
4343 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4344 6 : if (poRG->OpenMDArray(osCachedArrayName))
4345 : {
4346 2 : CPLError(CE_Failure, CPLE_NotSupported,
4347 : "An array with same name %s already exists in %s",
4348 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4349 2 : return false;
4350 : }
4351 :
4352 8 : CPLStringList aosOptions;
4353 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4354 4 : const auto &aoDims = GetDimensions();
4355 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4356 4 : if (!aoDims.empty())
4357 : {
4358 : std::string osBlockSize(
4359 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4360 4 : if (osBlockSize.empty())
4361 : {
4362 6 : const auto anBlockSize = GetBlockSize();
4363 3 : int idxDim = 0;
4364 10 : for (auto nBlockSize : anBlockSize)
4365 : {
4366 7 : if (idxDim > 0)
4367 4 : osBlockSize += ',';
4368 7 : if (nBlockSize == 0)
4369 7 : nBlockSize = 256;
4370 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4371 : osBlockSize +=
4372 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4373 7 : idxDim++;
4374 : }
4375 : }
4376 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4377 :
4378 4 : int idxDim = 0;
4379 13 : for (const auto &poDim : aoDims)
4380 : {
4381 9 : auto poNewDim = poRG->CreateDimension(
4382 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4383 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4384 9 : if (!poNewDim)
4385 0 : return false;
4386 9 : aoNewDims.emplace_back(poNewDim);
4387 9 : idxDim++;
4388 : }
4389 : }
4390 :
4391 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4392 8 : GetDataType(), aosOptions.List());
4393 4 : if (!poCachedArray)
4394 : {
4395 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4396 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4397 0 : return false;
4398 : }
4399 :
4400 4 : GUInt64 nCost = 0;
4401 8 : return poCachedArray->CopyFrom(nullptr, this,
4402 : false, // strict
4403 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4404 : }
4405 :
4406 : /************************************************************************/
4407 : /* Read() */
4408 : /************************************************************************/
4409 :
4410 3705 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4411 : const GInt64 *arrayStep, // step in elements
4412 : const GPtrDiff_t *bufferStride, // stride in elements
4413 : const GDALExtendedDataType &bufferDataType,
4414 : void *pDstBuffer, const void *pDstBufferAllocStart,
4415 : size_t nDstBufferAllocSize) const
4416 : {
4417 3705 : if (!m_bHasTriedCachedArray)
4418 : {
4419 1629 : m_bHasTriedCachedArray = true;
4420 1629 : if (IsCacheable())
4421 : {
4422 1629 : const auto &osFilename = GetFilename();
4423 2771 : if (!osFilename.empty() &&
4424 1142 : !EQUAL(CPLGetExtension(osFilename.c_str()), "gmac"))
4425 : {
4426 2264 : std::string osCacheFilename;
4427 2264 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4428 1132 : if (poRG)
4429 : {
4430 : const std::string osCachedArrayName(
4431 32 : MassageName(GetFullName()));
4432 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4433 16 : if (m_poCachedArray)
4434 : {
4435 6 : const auto &dims = GetDimensions();
4436 : const auto &cachedDims =
4437 6 : m_poCachedArray->GetDimensions();
4438 6 : const size_t nDims = dims.size();
4439 : bool ok =
4440 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4441 6 : cachedDims.size() == nDims;
4442 19 : for (size_t i = 0; ok && i < nDims; ++i)
4443 : {
4444 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4445 : }
4446 6 : if (ok)
4447 : {
4448 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4449 : osCachedArrayName.c_str(),
4450 : osCacheFilename.c_str());
4451 : }
4452 : else
4453 : {
4454 0 : CPLError(CE_Warning, CPLE_AppDefined,
4455 : "Cached array %s in %s has incompatible "
4456 : "characteristics with current array.",
4457 : osCachedArrayName.c_str(),
4458 : osCacheFilename.c_str());
4459 0 : m_poCachedArray.reset();
4460 : }
4461 : }
4462 : }
4463 : }
4464 : }
4465 : }
4466 :
4467 3705 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4468 3705 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4469 : {
4470 0 : CPLError(CE_Failure, CPLE_AppDefined,
4471 : "Array data type is not convertible to buffer data type");
4472 0 : return false;
4473 : }
4474 :
4475 7410 : std::vector<GInt64> tmp_arrayStep;
4476 7410 : std::vector<GPtrDiff_t> tmp_bufferStride;
4477 3705 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4478 : bufferStride, bufferDataType, pDstBuffer,
4479 : pDstBufferAllocStart, nDstBufferAllocSize,
4480 : tmp_arrayStep, tmp_bufferStride))
4481 : {
4482 9 : return false;
4483 : }
4484 :
4485 3696 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4486 3696 : bufferDataType, pDstBuffer);
4487 : }
4488 :
4489 : /************************************************************************/
4490 : /* GetRootGroup() */
4491 : /************************************************************************/
4492 :
4493 : /** Return the root group to which this arrays belongs too.
4494 : *
4495 : * Note that arrays may be free standing and some drivers may not implement
4496 : * this method, hence nullptr may be returned.
4497 : *
4498 : * It is used internally by the GetResampled() method to detect if GLT
4499 : * orthorectification is available.
4500 : *
4501 : * @return the root group, or nullptr.
4502 : * @since GDAL 3.8
4503 : */
4504 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4505 : {
4506 0 : return nullptr;
4507 : }
4508 :
4509 : //! @cond Doxygen_Suppress
4510 :
4511 : /************************************************************************/
4512 : /* IsTransposedRequest() */
4513 : /************************************************************************/
4514 :
4515 689 : bool GDALMDArray::IsTransposedRequest(
4516 : const size_t *count,
4517 : const GPtrDiff_t *bufferStride) const // stride in elements
4518 : {
4519 : /*
4520 : For example:
4521 : count = [2,3,4]
4522 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4523 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4524 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4525 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4526 : */
4527 689 : const size_t nDims(GetDimensionCount());
4528 689 : size_t nCurStrideForRowMajorStrides = 1;
4529 689 : bool bRowMajorStrides = true;
4530 689 : size_t nElts = 1;
4531 689 : size_t nLastIdx = 0;
4532 1954 : for (size_t i = nDims; i > 0;)
4533 : {
4534 1265 : --i;
4535 1265 : if (bufferStride[i] < 0)
4536 0 : return false;
4537 1265 : if (static_cast<size_t>(bufferStride[i]) !=
4538 : nCurStrideForRowMajorStrides)
4539 : {
4540 221 : bRowMajorStrides = false;
4541 : }
4542 : // Integer overflows have already been checked in CheckReadWriteParams()
4543 1265 : nCurStrideForRowMajorStrides *= count[i];
4544 1265 : nElts *= count[i];
4545 1265 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4546 : }
4547 689 : if (bRowMajorStrides)
4548 540 : return false;
4549 149 : return nLastIdx == nElts - 1;
4550 : }
4551 :
4552 : /************************************************************************/
4553 : /* CopyToFinalBufferSameDataType() */
4554 : /************************************************************************/
4555 :
4556 : template <size_t N>
4557 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4558 : size_t nDims, const size_t *count,
4559 : const GPtrDiff_t *bufferStride)
4560 : {
4561 120 : std::vector<size_t> anStackCount(nDims);
4562 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4563 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4564 : #if defined(__GNUC__)
4565 : #pragma GCC diagnostic push
4566 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4567 : #endif
4568 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4569 : #if defined(__GNUC__)
4570 : #pragma GCC diagnostic pop
4571 : #endif
4572 60 : size_t iDim = 0;
4573 :
4574 749 : lbl_next_depth:
4575 749 : if (iDim == nDims - 1)
4576 : {
4577 661 : size_t n = count[iDim];
4578 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4579 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4580 29186 : while (n > 0)
4581 : {
4582 28525 : --n;
4583 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4584 28525 : pabyDstBuffer += bufferStrideLastDim;
4585 28525 : pabySrcBuffer += N;
4586 : }
4587 : }
4588 : else
4589 : {
4590 88 : anStackCount[iDim] = count[iDim];
4591 : while (true)
4592 : {
4593 689 : ++iDim;
4594 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4595 689 : goto lbl_next_depth;
4596 689 : lbl_return_to_caller_in_loop:
4597 689 : --iDim;
4598 689 : --anStackCount[iDim];
4599 689 : if (anStackCount[iDim] == 0)
4600 88 : break;
4601 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4602 : }
4603 : }
4604 749 : if (iDim > 0)
4605 689 : goto lbl_return_to_caller_in_loop;
4606 60 : }
4607 :
4608 : /************************************************************************/
4609 : /* CopyToFinalBuffer() */
4610 : /************************************************************************/
4611 :
4612 129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4613 : const GDALExtendedDataType &eSrcDataType,
4614 : void *pDstBuffer,
4615 : const GDALExtendedDataType &eDstDataType,
4616 : size_t nDims, const size_t *count,
4617 : const GPtrDiff_t *bufferStride)
4618 : {
4619 129 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4620 : // Use specialized implementation for well-known data types when no
4621 : // type conversion is needed
4622 129 : if (eSrcDataType == eDstDataType)
4623 : {
4624 110 : if (nSrcDataTypeSize == 1)
4625 : {
4626 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4627 : count, bufferStride);
4628 60 : return;
4629 : }
4630 69 : else if (nSrcDataTypeSize == 2)
4631 : {
4632 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4633 : count, bufferStride);
4634 1 : return;
4635 : }
4636 68 : else if (nSrcDataTypeSize == 4)
4637 : {
4638 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4639 : count, bufferStride);
4640 14 : return;
4641 : }
4642 54 : else if (nSrcDataTypeSize == 8)
4643 : {
4644 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4645 : count, bufferStride);
4646 4 : return;
4647 : }
4648 : }
4649 :
4650 69 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4651 138 : std::vector<size_t> anStackCount(nDims);
4652 138 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4653 69 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4654 69 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4655 69 : size_t iDim = 0;
4656 :
4657 338 : lbl_next_depth:
4658 338 : if (iDim == nDims - 1)
4659 : {
4660 328 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4661 328 : pabyDstBufferStack[iDim], eDstDataType,
4662 328 : bufferStride[iDim], count[iDim]);
4663 328 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4664 : }
4665 : else
4666 : {
4667 10 : anStackCount[iDim] = count[iDim];
4668 : while (true)
4669 : {
4670 269 : ++iDim;
4671 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4672 269 : goto lbl_next_depth;
4673 269 : lbl_return_to_caller_in_loop:
4674 269 : --iDim;
4675 269 : --anStackCount[iDim];
4676 269 : if (anStackCount[iDim] == 0)
4677 10 : break;
4678 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4679 : }
4680 : }
4681 338 : if (iDim > 0)
4682 269 : goto lbl_return_to_caller_in_loop;
4683 : }
4684 :
4685 : /************************************************************************/
4686 : /* Transpose2D() */
4687 : /************************************************************************/
4688 :
4689 : template <class T>
4690 39 : static void Transpose2D(T *dst, const T *src, size_t src_height,
4691 : size_t src_width)
4692 : {
4693 39 : constexpr size_t blocksize = 32;
4694 142 : for (size_t i = 0; i < src_height; i += blocksize)
4695 : {
4696 1264 : for (size_t j = 0; j < src_width; j += blocksize)
4697 : {
4698 : // transpose the block beginning at [i,j]
4699 1161 : const size_t max_k = std::min(i + blocksize, src_height);
4700 36699 : for (size_t k = i; k < max_k; ++k)
4701 : {
4702 35538 : const size_t max_l = std::min(j + blocksize, src_width);
4703 1135486 : for (size_t l = j; l < max_l; ++l)
4704 : {
4705 1099948 : dst[k + l * src_height] = src[l + k * src_width];
4706 : }
4707 : }
4708 : }
4709 : }
4710 39 : }
4711 :
4712 : /************************************************************************/
4713 : /* TransposeLast2Dims() */
4714 : /************************************************************************/
4715 :
4716 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4717 : const GDALExtendedDataType &eDT,
4718 : const size_t nDims, const size_t *count,
4719 : const size_t nEltsNonLast2Dims)
4720 : {
4721 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4722 19 : const auto nDTSize = eDT.GetSize();
4723 : void *pTempBufferForLast2DimsTranspose =
4724 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4725 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4726 0 : return false;
4727 :
4728 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4729 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4730 : {
4731 39 : if (nDTSize == 1)
4732 : {
4733 14 : Transpose2D(
4734 : static_cast<uint8_t *>(pTempBufferForLast2DimsTranspose),
4735 : reinterpret_cast<const uint8_t *>(pabyDstBuffer),
4736 14 : count[nDims - 2], count[nDims - 1]);
4737 : }
4738 25 : else if (nDTSize == 2)
4739 : {
4740 8 : Transpose2D(
4741 : static_cast<uint16_t *>(pTempBufferForLast2DimsTranspose),
4742 : reinterpret_cast<const uint16_t *>(pabyDstBuffer),
4743 8 : count[nDims - 2], count[nDims - 1]);
4744 : }
4745 17 : else if (nDTSize == 4)
4746 : {
4747 9 : Transpose2D(
4748 : static_cast<uint32_t *>(pTempBufferForLast2DimsTranspose),
4749 : reinterpret_cast<const uint32_t *>(pabyDstBuffer),
4750 9 : count[nDims - 2], count[nDims - 1]);
4751 : }
4752 8 : else if (nDTSize == 8)
4753 : {
4754 8 : Transpose2D(
4755 : static_cast<uint64_t *>(pTempBufferForLast2DimsTranspose),
4756 : reinterpret_cast<const uint64_t *>(pabyDstBuffer),
4757 8 : count[nDims - 2], count[nDims - 1]);
4758 : }
4759 : else
4760 : {
4761 0 : CPLAssert(false);
4762 : }
4763 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4764 : nDTSize * nEltsLast2Dims);
4765 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4766 : }
4767 :
4768 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4769 :
4770 19 : return true;
4771 : }
4772 :
4773 : /************************************************************************/
4774 : /* ReadForTransposedRequest() */
4775 : /************************************************************************/
4776 :
4777 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4778 : // transposed view yield to extremely poor/unusable performance. This fixes
4779 : // this by using temporary memory to read in a contiguous buffer in a
4780 : // row-major order, and then do the transposition to the final buffer.
4781 :
4782 148 : bool GDALMDArray::ReadForTransposedRequest(
4783 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4784 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4785 : void *pDstBuffer) const
4786 : {
4787 148 : const size_t nDims(GetDimensionCount());
4788 148 : if (nDims == 0)
4789 : {
4790 0 : CPLAssert(false);
4791 : return false; // shouldn't happen
4792 : }
4793 148 : size_t nElts = 1;
4794 418 : for (size_t i = 0; i < nDims; ++i)
4795 270 : nElts *= count[i];
4796 :
4797 296 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4798 148 : tmpBufferStrides.back() = 1;
4799 270 : for (size_t i = nDims - 1; i > 0;)
4800 : {
4801 122 : --i;
4802 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4803 : }
4804 :
4805 148 : const auto &eDT = GetDataType();
4806 148 : const auto nDTSize = eDT.GetSize();
4807 277 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4808 293 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4809 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4810 : {
4811 : // Optimization of the optimization if only the last 2 dims are
4812 : // transposed that saves on temporary buffer allocation
4813 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4814 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4815 23 : bool bRowMajorStridesForNonLast2Dims = true;
4816 23 : size_t nEltsNonLast2Dims = 1;
4817 40 : for (size_t i = nDims - 2; i > 0;)
4818 : {
4819 17 : --i;
4820 17 : if (static_cast<size_t>(bufferStride[i]) !=
4821 : nCurStrideForRowMajorStrides)
4822 : {
4823 4 : bRowMajorStridesForNonLast2Dims = false;
4824 : }
4825 : // Integer overflows have already been checked in
4826 : // CheckReadWriteParams()
4827 17 : nCurStrideForRowMajorStrides *= count[i];
4828 17 : nEltsNonLast2Dims *= count[i];
4829 : }
4830 23 : if (bRowMajorStridesForNonLast2Dims)
4831 : {
4832 : // We read in the final buffer!
4833 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4834 19 : eDT, pDstBuffer))
4835 : {
4836 0 : return false;
4837 : }
4838 :
4839 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4840 19 : nEltsNonLast2Dims);
4841 : }
4842 : }
4843 :
4844 129 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4845 129 : if (pTempBuffer == nullptr)
4846 0 : return false;
4847 :
4848 129 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4849 129 : pTempBuffer))
4850 : {
4851 0 : VSIFree(pTempBuffer);
4852 0 : return false;
4853 : }
4854 129 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4855 : count, bufferStride);
4856 :
4857 129 : if (eDT.NeedsFreeDynamicMemory())
4858 : {
4859 58 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4860 116 : for (size_t i = 0; i < nElts; ++i)
4861 : {
4862 58 : eDT.FreeDynamicMemory(pabyPtr);
4863 58 : pabyPtr += nDTSize;
4864 : }
4865 : }
4866 :
4867 129 : VSIFree(pTempBuffer);
4868 129 : return true;
4869 : }
4870 :
4871 : /************************************************************************/
4872 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4873 : /************************************************************************/
4874 :
4875 : // Returns true if at all following conditions are met:
4876 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4877 : // defines a row-major ordered contiguous buffer.
4878 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4879 : const size_t *count, const GInt64 *arrayStep,
4880 : const GPtrDiff_t *bufferStride,
4881 : const GDALExtendedDataType &bufferDataType) const
4882 : {
4883 78 : if (bufferDataType != GetDataType())
4884 5 : return false;
4885 73 : size_t nExpectedStride = 1;
4886 166 : for (size_t i = GetDimensionCount(); i > 0;)
4887 : {
4888 96 : --i;
4889 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4890 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4891 : {
4892 3 : return false;
4893 : }
4894 93 : nExpectedStride *= count[i];
4895 : }
4896 70 : return true;
4897 : }
4898 :
4899 : /************************************************************************/
4900 : /* ReadUsingContiguousIRead() */
4901 : /************************************************************************/
4902 :
4903 : // Used for example by the TileDB driver when requesting it with
4904 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4905 : // not defining a row-major ordered contiguous buffer.
4906 : // Should only be called when at least one of the above conditions are true,
4907 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4908 : // returning none.
4909 : // This method will call IRead() again with arrayStep[] == 1,
4910 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4911 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4912 : // content of that temporary buffer onto pDstBuffer.
4913 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4914 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4915 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4916 : void *pDstBuffer) const
4917 : {
4918 7 : const size_t nDims(GetDimensionCount());
4919 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4920 14 : std::vector<size_t> anTmpCount(nDims);
4921 7 : const auto &oType = GetDataType();
4922 7 : size_t nMemArraySize = oType.GetSize();
4923 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4924 7 : GPtrDiff_t nStride = 1;
4925 18 : for (size_t i = nDims; i > 0;)
4926 : {
4927 11 : --i;
4928 11 : if (arrayStep[i] > 0)
4929 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4930 : else
4931 2 : anTmpStartIdx[i] =
4932 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4933 : const uint64_t nCount =
4934 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4935 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4936 : {
4937 0 : CPLError(CE_Failure, CPLE_AppDefined,
4938 : "Read() failed due to too large memory requirement");
4939 0 : return false;
4940 : }
4941 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4942 11 : nMemArraySize *= anTmpCount[i];
4943 11 : anTmpStride[i] = nStride;
4944 11 : nStride *= anTmpCount[i];
4945 : }
4946 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4947 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4948 7 : if (!pTmpBuffer)
4949 0 : return false;
4950 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4951 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4952 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4953 : {
4954 0 : return false;
4955 : }
4956 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
4957 18 : for (size_t i = 0; i < nDims; ++i)
4958 : {
4959 11 : if (arrayStep[i] > 0)
4960 9 : anTmpStartIdx[i] = 0;
4961 : else
4962 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
4963 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
4964 22 : std::string(), std::string(), std::string(), std::string(),
4965 22 : anTmpCount[i]);
4966 : }
4967 : auto poMEMArray =
4968 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
4969 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
4970 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
4971 7 : bufferStride, bufferDataType, pDstBuffer);
4972 : }
4973 :
4974 : //! @endcond
4975 :
4976 : /************************************************************************/
4977 : /* GDALSlicedMDArray */
4978 : /************************************************************************/
4979 :
4980 : class GDALSlicedMDArray final : public GDALPamMDArray
4981 : {
4982 : private:
4983 : std::shared_ptr<GDALMDArray> m_poParent{};
4984 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4985 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
4986 : std::vector<Range>
4987 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
4988 :
4989 : mutable std::vector<GUInt64> m_parentStart;
4990 : mutable std::vector<size_t> m_parentCount;
4991 : mutable std::vector<GInt64> m_parentStep;
4992 : mutable std::vector<GPtrDiff_t> m_parentStride;
4993 :
4994 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
4995 : const GInt64 *arrayStep,
4996 : const GPtrDiff_t *bufferStride) const;
4997 :
4998 : protected:
4999 574 : explicit GDALSlicedMDArray(
5000 : const std::shared_ptr<GDALMDArray> &poParent,
5001 : const std::string &viewExpr,
5002 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5003 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5004 : std::vector<Range> &&parentRanges)
5005 1722 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5006 1722 : poParent->GetFullName() +
5007 1148 : " (" + viewExpr + ")"),
5008 1148 : GDALPamMDArray(std::string(),
5009 1148 : "Sliced view of " + poParent->GetFullName() + " (" +
5010 1148 : viewExpr + ")",
5011 1148 : GDALPamMultiDim::GetPAM(poParent),
5012 : poParent->GetContext()),
5013 1148 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5014 574 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5015 574 : m_parentRanges(std::move(parentRanges)),
5016 574 : m_parentStart(m_poParent->GetDimensionCount()),
5017 574 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5018 574 : m_parentStep(m_poParent->GetDimensionCount()),
5019 4592 : m_parentStride(m_poParent->GetDimensionCount())
5020 : {
5021 574 : }
5022 :
5023 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5024 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5025 : const GDALExtendedDataType &bufferDataType,
5026 : void *pDstBuffer) const override;
5027 :
5028 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5029 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5030 : const GDALExtendedDataType &bufferDataType,
5031 : const void *pSrcBuffer) override;
5032 :
5033 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5034 : CSLConstList papszOptions) const override;
5035 :
5036 : public:
5037 : static std::shared_ptr<GDALSlicedMDArray>
5038 574 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5039 : const std::string &viewExpr,
5040 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5041 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5042 : std::vector<Range> &&parentRanges)
5043 : {
5044 574 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5045 574 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5046 :
5047 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5048 574 : poParent, viewExpr, std::move(dims),
5049 574 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5050 574 : newAr->SetSelf(newAr);
5051 574 : return newAr;
5052 : }
5053 :
5054 55 : bool IsWritable() const override
5055 : {
5056 55 : return m_poParent->IsWritable();
5057 : }
5058 :
5059 983 : const std::string &GetFilename() const override
5060 : {
5061 983 : return m_poParent->GetFilename();
5062 : }
5063 :
5064 : const std::vector<std::shared_ptr<GDALDimension>> &
5065 3646 : GetDimensions() const override
5066 : {
5067 3646 : return m_dims;
5068 : }
5069 :
5070 1383 : const GDALExtendedDataType &GetDataType() const override
5071 : {
5072 1383 : return m_poParent->GetDataType();
5073 : }
5074 :
5075 2 : const std::string &GetUnit() const override
5076 : {
5077 2 : return m_poParent->GetUnit();
5078 : }
5079 :
5080 : // bool SetUnit(const std::string& osUnit) override { return
5081 : // m_poParent->SetUnit(osUnit); }
5082 :
5083 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5084 : {
5085 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5086 2 : if (!poSrcSRS)
5087 1 : return nullptr;
5088 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5089 2 : std::vector<int> dstMapping;
5090 3 : for (int srcAxis : srcMapping)
5091 : {
5092 2 : bool bFound = false;
5093 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5094 : {
5095 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5096 3 : srcAxis - 1)
5097 : {
5098 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5099 2 : bFound = true;
5100 2 : break;
5101 : }
5102 : }
5103 2 : if (!bFound)
5104 : {
5105 0 : dstMapping.push_back(0);
5106 : }
5107 : }
5108 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5109 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5110 1 : return poClone;
5111 : }
5112 :
5113 55 : const void *GetRawNoDataValue() const override
5114 : {
5115 55 : return m_poParent->GetRawNoDataValue();
5116 : }
5117 :
5118 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5119 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5120 :
5121 2 : double GetOffset(bool *pbHasOffset,
5122 : GDALDataType *peStorageType) const override
5123 : {
5124 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5125 : }
5126 :
5127 2 : double GetScale(bool *pbHasScale,
5128 : GDALDataType *peStorageType) const override
5129 : {
5130 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5131 : }
5132 :
5133 : // bool SetOffset(double dfOffset) override { return
5134 : // m_poParent->SetOffset(dfOffset); }
5135 :
5136 : // bool SetScale(double dfScale) override { return
5137 : // m_poParent->SetScale(dfScale); }
5138 :
5139 197 : std::vector<GUInt64> GetBlockSize() const override
5140 : {
5141 197 : std::vector<GUInt64> ret(GetDimensionCount());
5142 394 : const auto parentBlockSize(m_poParent->GetBlockSize());
5143 595 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5144 : {
5145 398 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5146 398 : if (iOldAxis != static_cast<size_t>(-1))
5147 : {
5148 398 : ret[i] = parentBlockSize[iOldAxis];
5149 : }
5150 : }
5151 394 : return ret;
5152 : }
5153 :
5154 : std::shared_ptr<GDALAttribute>
5155 6 : GetAttribute(const std::string &osName) const override
5156 : {
5157 6 : return m_poParent->GetAttribute(osName);
5158 : }
5159 :
5160 : std::vector<std::shared_ptr<GDALAttribute>>
5161 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5162 : {
5163 24 : return m_poParent->GetAttributes(papszOptions);
5164 : }
5165 : };
5166 :
5167 : /************************************************************************/
5168 : /* PrepareParentArrays() */
5169 : /************************************************************************/
5170 :
5171 475 : void GDALSlicedMDArray::PrepareParentArrays(
5172 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5173 : const GPtrDiff_t *bufferStride) const
5174 : {
5175 475 : const size_t nParentDimCount = m_parentRanges.size();
5176 1481 : for (size_t i = 0; i < nParentDimCount; i++)
5177 : {
5178 : // For dimensions in parent that have no existence in sliced array
5179 1006 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5180 : }
5181 :
5182 1250 : for (size_t i = 0; i < m_dims.size(); i++)
5183 : {
5184 775 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5185 775 : if (iParent != static_cast<size_t>(-1))
5186 : {
5187 773 : m_parentStart[iParent] =
5188 773 : m_parentRanges[iParent].m_nIncr >= 0
5189 773 : ? m_parentRanges[iParent].m_nStartIdx +
5190 744 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5191 29 : : m_parentRanges[iParent].m_nStartIdx -
5192 58 : arrayStartIdx[i] *
5193 29 : static_cast<GUInt64>(
5194 29 : -m_parentRanges[iParent].m_nIncr);
5195 773 : m_parentCount[iParent] = count[i];
5196 773 : if (arrayStep)
5197 : {
5198 772 : m_parentStep[iParent] =
5199 772 : count[i] == 1 ? 1 :
5200 : // other checks should have ensured this does
5201 : // not overflow
5202 586 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5203 : }
5204 773 : if (bufferStride)
5205 : {
5206 772 : m_parentStride[iParent] = bufferStride[i];
5207 : }
5208 : }
5209 : }
5210 475 : }
5211 :
5212 : /************************************************************************/
5213 : /* IRead() */
5214 : /************************************************************************/
5215 :
5216 442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5217 : const GInt64 *arrayStep,
5218 : const GPtrDiff_t *bufferStride,
5219 : const GDALExtendedDataType &bufferDataType,
5220 : void *pDstBuffer) const
5221 : {
5222 442 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5223 884 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5224 442 : m_parentStep.data(), m_parentStride.data(),
5225 442 : bufferDataType, pDstBuffer);
5226 : }
5227 :
5228 : /************************************************************************/
5229 : /* IWrite() */
5230 : /************************************************************************/
5231 :
5232 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5233 : const size_t *count, const GInt64 *arrayStep,
5234 : const GPtrDiff_t *bufferStride,
5235 : const GDALExtendedDataType &bufferDataType,
5236 : const void *pSrcBuffer)
5237 : {
5238 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5239 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5240 32 : m_parentStep.data(), m_parentStride.data(),
5241 32 : bufferDataType, pSrcBuffer);
5242 : }
5243 :
5244 : /************************************************************************/
5245 : /* IAdviseRead() */
5246 : /************************************************************************/
5247 :
5248 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5249 : const size_t *count,
5250 : CSLConstList papszOptions) const
5251 : {
5252 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5253 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5254 1 : papszOptions);
5255 : }
5256 :
5257 : /************************************************************************/
5258 : /* CreateSlicedArray() */
5259 : /************************************************************************/
5260 :
5261 : static std::shared_ptr<GDALMDArray>
5262 592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5263 : const std::string &viewExpr, const std::string &activeSlice,
5264 : bool bRenameDimensions,
5265 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5266 : {
5267 592 : const auto &srcDims(self->GetDimensions());
5268 592 : if (srcDims.empty())
5269 : {
5270 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5271 2 : return nullptr;
5272 : }
5273 :
5274 1180 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5275 590 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5276 :
5277 1180 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5278 1180 : std::vector<size_t> mapDimIdxToParentDimIdx;
5279 1180 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5280 590 : newDims.reserve(nTokens);
5281 590 : mapDimIdxToParentDimIdx.reserve(nTokens);
5282 590 : parentRanges.reserve(nTokens);
5283 :
5284 590 : bool bGotEllipsis = false;
5285 590 : size_t nCurSrcDim = 0;
5286 1744 : for (size_t i = 0; i < nTokens; i++)
5287 : {
5288 1170 : const char *pszIdxSpec = aosTokens[i];
5289 1170 : if (EQUAL(pszIdxSpec, "..."))
5290 : {
5291 37 : if (bGotEllipsis)
5292 : {
5293 2 : CPLError(CE_Failure, CPLE_AppDefined,
5294 : "Only one single ellipsis is supported");
5295 2 : return nullptr;
5296 : }
5297 35 : bGotEllipsis = true;
5298 35 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5299 77 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5300 : {
5301 42 : parentRanges.emplace_back(0, 1);
5302 42 : newDims.push_back(srcDims[nCurSrcDim]);
5303 42 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5304 : }
5305 35 : continue;
5306 : }
5307 1133 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5308 1130 : EQUAL(pszIdxSpec, "np.newaxis"))
5309 : {
5310 3 : newDims.push_back(std::make_shared<GDALDimension>(
5311 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5312 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5313 3 : continue;
5314 : }
5315 1130 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5316 : {
5317 323 : if (nCurSrcDim >= srcDims.size())
5318 : {
5319 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5320 : activeSlice.c_str());
5321 7 : return nullptr;
5322 : }
5323 :
5324 321 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5325 321 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5326 321 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5327 317 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5328 : {
5329 5 : CPLError(CE_Failure, CPLE_AppDefined,
5330 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5331 5 : return nullptr;
5332 : }
5333 316 : if (nVal < 0)
5334 0 : nVal += nDimSize;
5335 316 : parentRanges.emplace_back(nVal, 0);
5336 : }
5337 : else
5338 : {
5339 807 : if (nCurSrcDim >= srcDims.size())
5340 : {
5341 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5342 : activeSlice.c_str());
5343 7 : return nullptr;
5344 : }
5345 :
5346 : CPLStringList aosRangeTokens(
5347 806 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5348 806 : int nRangeTokens = aosRangeTokens.size();
5349 806 : if (nRangeTokens > 3)
5350 : {
5351 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5352 : pszIdxSpec);
5353 1 : return nullptr;
5354 : }
5355 805 : if (nRangeTokens <= 1)
5356 : {
5357 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5358 : pszIdxSpec);
5359 1 : return nullptr;
5360 : }
5361 804 : const char *pszStart = aosRangeTokens[0];
5362 804 : const char *pszEnd = aosRangeTokens[1];
5363 804 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5364 804 : GDALSlicedMDArray::Range range;
5365 804 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5366 804 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5367 1607 : if (range.m_nIncr == 0 ||
5368 803 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5369 : {
5370 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5371 1 : return nullptr;
5372 : }
5373 803 : auto startIdx(CPLAtoGIntBig(pszStart));
5374 803 : if (startIdx < 0)
5375 : {
5376 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5377 0 : startIdx = 0;
5378 : else
5379 0 : startIdx = nDimSize + startIdx;
5380 : }
5381 803 : const bool bPosIncr = range.m_nIncr > 0;
5382 803 : range.m_nStartIdx = startIdx;
5383 1606 : range.m_nStartIdx = EQUAL(pszStart, "")
5384 803 : ? (bPosIncr ? 0 : nDimSize - 1)
5385 : : range.m_nStartIdx;
5386 803 : if (range.m_nStartIdx >= nDimSize - 1)
5387 185 : range.m_nStartIdx = nDimSize - 1;
5388 803 : auto endIdx(CPLAtoGIntBig(pszEnd));
5389 803 : if (endIdx < 0)
5390 : {
5391 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5392 1 : if (nDimSize < positiveEndIdx)
5393 0 : endIdx = 0;
5394 : else
5395 1 : endIdx = nDimSize - positiveEndIdx;
5396 : }
5397 803 : GUInt64 nEndIdx = endIdx;
5398 803 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5399 803 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5400 801 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5401 : {
5402 3 : CPLError(CE_Failure, CPLE_AppDefined,
5403 : "Output dimension of size 0 is not allowed");
5404 3 : return nullptr;
5405 : }
5406 800 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5407 800 : const auto nAbsIncr = std::abs(range.m_nIncr);
5408 800 : const GUInt64 newSize =
5409 : bPosIncr
5410 833 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5411 33 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5412 1322 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5413 522 : newSize == srcDims[nCurSrcDim]->GetSize())
5414 : {
5415 153 : newDims.push_back(srcDims[nCurSrcDim]);
5416 : }
5417 : else
5418 : {
5419 647 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5420 647 : if (bRenameDimensions)
5421 : {
5422 : osNewDimName =
5423 1210 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5424 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5425 : "_" CPL_FRMT_GUIB,
5426 605 : static_cast<GUIntBig>(range.m_nStartIdx),
5427 605 : static_cast<GIntBig>(range.m_nIncr),
5428 605 : static_cast<GUIntBig>(newSize));
5429 : }
5430 647 : newDims.push_back(std::make_shared<GDALDimension>(
5431 1294 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5432 1294 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5433 : : std::string(),
5434 : newSize));
5435 : }
5436 800 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5437 800 : parentRanges.emplace_back(range);
5438 : }
5439 :
5440 1116 : nCurSrcDim++;
5441 : }
5442 647 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5443 : {
5444 73 : parentRanges.emplace_back(0, 1);
5445 73 : newDims.push_back(srcDims[nCurSrcDim]);
5446 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5447 : }
5448 :
5449 574 : GDALMDArray::ViewSpec viewSpec;
5450 574 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5451 574 : viewSpec.m_parentRanges = parentRanges;
5452 574 : viewSpecs.emplace_back(std::move(viewSpec));
5453 :
5454 1148 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5455 574 : std::move(mapDimIdxToParentDimIdx),
5456 1148 : std::move(parentRanges));
5457 : }
5458 :
5459 : /************************************************************************/
5460 : /* GDALExtractFieldMDArray */
5461 : /************************************************************************/
5462 :
5463 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5464 : {
5465 : private:
5466 : std::shared_ptr<GDALMDArray> m_poParent{};
5467 : GDALExtendedDataType m_dt;
5468 : std::string m_srcCompName;
5469 : mutable std::vector<GByte> m_pabyNoData{};
5470 :
5471 : protected:
5472 62 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5473 : const std::string &fieldName,
5474 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5475 248 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5476 124 : " of " +
5477 62 : poParent->GetFullName()),
5478 : GDALPamMDArray(
5479 124 : std::string(),
5480 124 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5481 124 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5482 : m_poParent(poParent), m_dt(srcComp->GetType()),
5483 310 : m_srcCompName(srcComp->GetName())
5484 : {
5485 62 : m_pabyNoData.resize(m_dt.GetSize());
5486 62 : }
5487 :
5488 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5489 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5490 : const GDALExtendedDataType &bufferDataType,
5491 : void *pDstBuffer) const override;
5492 :
5493 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5494 : CSLConstList papszOptions) const override
5495 : {
5496 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5497 : }
5498 :
5499 : public:
5500 : static std::shared_ptr<GDALExtractFieldMDArray>
5501 62 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5502 : const std::string &fieldName,
5503 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5504 : {
5505 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5506 62 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5507 62 : newAr->SetSelf(newAr);
5508 62 : return newAr;
5509 : }
5510 :
5511 124 : ~GDALExtractFieldMDArray()
5512 62 : {
5513 62 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5514 124 : }
5515 :
5516 40 : bool IsWritable() const override
5517 : {
5518 40 : return m_poParent->IsWritable();
5519 : }
5520 :
5521 204 : const std::string &GetFilename() const override
5522 : {
5523 204 : return m_poParent->GetFilename();
5524 : }
5525 :
5526 : const std::vector<std::shared_ptr<GDALDimension>> &
5527 300 : GetDimensions() const override
5528 : {
5529 300 : return m_poParent->GetDimensions();
5530 : }
5531 :
5532 245 : const GDALExtendedDataType &GetDataType() const override
5533 : {
5534 245 : return m_dt;
5535 : }
5536 :
5537 2 : const std::string &GetUnit() const override
5538 : {
5539 2 : return m_poParent->GetUnit();
5540 : }
5541 :
5542 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5543 : {
5544 2 : return m_poParent->GetSpatialRef();
5545 : }
5546 :
5547 56 : const void *GetRawNoDataValue() const override
5548 : {
5549 56 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5550 56 : if (parentNoData == nullptr)
5551 1 : return nullptr;
5552 :
5553 55 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5554 55 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5555 :
5556 110 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5557 110 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5558 110 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5559 55 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5560 165 : std::move(comps)));
5561 :
5562 55 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5563 55 : &m_pabyNoData[0], tmpDT);
5564 :
5565 55 : return &m_pabyNoData[0];
5566 : }
5567 :
5568 2 : double GetOffset(bool *pbHasOffset,
5569 : GDALDataType *peStorageType) const override
5570 : {
5571 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5572 : }
5573 :
5574 2 : double GetScale(bool *pbHasScale,
5575 : GDALDataType *peStorageType) const override
5576 : {
5577 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5578 : }
5579 :
5580 41 : std::vector<GUInt64> GetBlockSize() const override
5581 : {
5582 41 : return m_poParent->GetBlockSize();
5583 : }
5584 : };
5585 :
5586 : /************************************************************************/
5587 : /* IRead() */
5588 : /************************************************************************/
5589 :
5590 46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5591 : const size_t *count,
5592 : const GInt64 *arrayStep,
5593 : const GPtrDiff_t *bufferStride,
5594 : const GDALExtendedDataType &bufferDataType,
5595 : void *pDstBuffer) const
5596 : {
5597 92 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5598 92 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5599 92 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5600 : auto tmpDT(GDALExtendedDataType::Create(
5601 92 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5602 :
5603 46 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5604 92 : tmpDT, pDstBuffer);
5605 : }
5606 :
5607 : /************************************************************************/
5608 : /* CreateFieldNameExtractArray() */
5609 : /************************************************************************/
5610 :
5611 : static std::shared_ptr<GDALMDArray>
5612 63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5613 : const std::string &fieldName)
5614 : {
5615 63 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5616 63 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5617 125 : for (const auto &comp : self->GetDataType().GetComponents())
5618 : {
5619 124 : if (comp->GetName() == fieldName)
5620 : {
5621 62 : srcComp = ∁
5622 62 : break;
5623 : }
5624 : }
5625 63 : if (srcComp == nullptr)
5626 : {
5627 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5628 : fieldName.c_str());
5629 1 : return nullptr;
5630 : }
5631 62 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5632 : }
5633 :
5634 : /************************************************************************/
5635 : /* GetView() */
5636 : /************************************************************************/
5637 :
5638 : // clang-format off
5639 : /** Return a view of the array using slicing or field access.
5640 : *
5641 : * The slice expression uses the same syntax as NumPy basic slicing and
5642 : * indexing. See
5643 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5644 : * Or it can use field access by name. See
5645 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5646 : *
5647 : * Multiple [] bracket elements can be concatenated, with a slice expression
5648 : * or field name inside each.
5649 : *
5650 : * For basic slicing and indexing, inside each [] bracket element, a list of
5651 : * indexes that apply to successive source dimensions, can be specified, using
5652 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5653 : * or newaxis, using a comma separator.
5654 : *
5655 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5656 : * <ul>
5657 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5658 : * at index 1 in the first dimension, and index 2 in the second dimension
5659 : * from the source array. That is 5</li>
5660 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5661 : * implemented internally doing this intermediate slicing approach.</li>
5662 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5663 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5664 : * first dimension. That is [4,5,6,7].</li>
5665 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5666 : * second dimension. That is [2,6].</li>
5667 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5668 : * the second dimension. That is [[2],[6]].</li>
5669 : * <li>GetView("[::,2]"): Same as
5670 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5671 : * ellipsis only expands to one dimension here.</li>
5672 : * <li>GetView("[:,::2]"):
5673 : * returns a 2-dimensional array, with even-indexed elements of the second
5674 : * dimension. That is [[0,2],[4,6]].</li>
5675 : * <li>GetView("[:,1::2]"): returns a
5676 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5677 : * is [[1,3],[5,7]].</li>
5678 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5679 : * array, with elements of the second dimension with index in the range [1,3[.
5680 : * That is [[1,2],[5,6]].</li>
5681 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5682 : * array, with the values in first dimension reversed. That is
5683 : * [[4,5,6,7],[0,1,2,3]].</li>
5684 : * <li>GetView("[newaxis,...]"): returns a
5685 : * 3-dimensional array, with an additional dimension of size 1 put at the
5686 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5687 : * </ul>
5688 : *
5689 : * One difference with NumPy behavior is that ranges that would result in
5690 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5691 : * GDAL multidimensional model).
5692 : *
5693 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5694 : * Multiple field specification is not supported currently.
5695 : *
5696 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5697 : *
5698 : * \note When using the GDAL Python bindings, natural Python syntax can be
5699 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5700 : * ar.GetView("[0,::,1]['foo']")
5701 : * \note When using the C++ API and integer indexing only, you may use the
5702 : * at(idx0, idx1, ...) method.
5703 : *
5704 : * The returned array holds a reference to the original one, and thus is
5705 : * a view of it (not a copy). If the content of the original array changes,
5706 : * the content of the view array too. When using basic slicing and indexing,
5707 : * the view can be written if the underlying array is writable.
5708 : *
5709 : * This is the same as the C function GDALMDArrayGetView()
5710 : *
5711 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5712 : * access.
5713 : * @return a new array, that holds a reference to the original one, and thus is
5714 : * a view of it (not a copy), or nullptr in case of error.
5715 : */
5716 : // clang-format on
5717 :
5718 : std::shared_ptr<GDALMDArray>
5719 598 : GDALMDArray::GetView(const std::string &viewExpr) const
5720 : {
5721 1196 : std::vector<ViewSpec> viewSpecs;
5722 1196 : return GetView(viewExpr, true, viewSpecs);
5723 : }
5724 :
5725 : //! @cond Doxygen_Suppress
5726 : std::shared_ptr<GDALMDArray>
5727 661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5728 : std::vector<ViewSpec> &viewSpecs) const
5729 : {
5730 1322 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5731 661 : if (!self)
5732 : {
5733 1 : CPLError(CE_Failure, CPLE_AppDefined,
5734 : "Driver implementation issue: m_pSelf not set !");
5735 1 : return nullptr;
5736 : }
5737 660 : std::string curExpr(viewExpr);
5738 : while (true)
5739 : {
5740 663 : if (curExpr.empty() || curExpr[0] != '[')
5741 : {
5742 2 : CPLError(CE_Failure, CPLE_AppDefined,
5743 : "Slice string should start with ['");
5744 660 : return nullptr;
5745 : }
5746 :
5747 661 : std::string fieldName;
5748 : size_t endExpr;
5749 661 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5750 : {
5751 67 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5752 : {
5753 2 : CPLError(CE_Failure, CPLE_AppDefined,
5754 : "Field access not allowed on non-compound data type");
5755 2 : return nullptr;
5756 : }
5757 65 : size_t idx = 2;
5758 572 : for (; idx < curExpr.size(); idx++)
5759 : {
5760 571 : const char ch = curExpr[idx];
5761 571 : if (ch == curExpr[1])
5762 64 : break;
5763 507 : if (ch == '\\' && idx + 1 < curExpr.size())
5764 : {
5765 1 : fieldName += curExpr[idx + 1];
5766 1 : idx++;
5767 : }
5768 : else
5769 : {
5770 506 : fieldName += ch;
5771 : }
5772 : }
5773 65 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5774 : {
5775 2 : CPLError(CE_Failure, CPLE_AppDefined,
5776 : "Invalid field access specification");
5777 2 : return nullptr;
5778 : }
5779 63 : endExpr = idx + 1;
5780 : }
5781 : else
5782 : {
5783 594 : endExpr = curExpr.find(']');
5784 : }
5785 657 : if (endExpr == std::string::npos)
5786 : {
5787 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5788 1 : return nullptr;
5789 : }
5790 656 : if (endExpr == 1)
5791 : {
5792 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5793 1 : return nullptr;
5794 : }
5795 655 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5796 :
5797 655 : if (!fieldName.empty())
5798 : {
5799 126 : ViewSpec viewSpec;
5800 63 : viewSpec.m_osFieldName = fieldName;
5801 63 : viewSpecs.emplace_back(std::move(viewSpec));
5802 : }
5803 :
5804 655 : auto newArray = !fieldName.empty()
5805 : ? CreateFieldNameExtractArray(self, fieldName)
5806 : : CreateSlicedArray(self, viewExpr, activeSlice,
5807 655 : bRenameDimensions, viewSpecs);
5808 :
5809 655 : if (endExpr == curExpr.size() - 1)
5810 : {
5811 652 : return newArray;
5812 : }
5813 3 : self = std::move(newArray);
5814 3 : curExpr = curExpr.substr(endExpr + 1);
5815 3 : }
5816 : }
5817 :
5818 : //! @endcond
5819 :
5820 : std::shared_ptr<GDALMDArray>
5821 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5822 : {
5823 19 : std::string osExpr("[");
5824 19 : bool bFirst = true;
5825 45 : for (const auto &idx : indices)
5826 : {
5827 26 : if (!bFirst)
5828 7 : osExpr += ',';
5829 26 : bFirst = false;
5830 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5831 : }
5832 57 : return GetView(osExpr + ']');
5833 : }
5834 :
5835 : /************************************************************************/
5836 : /* operator[] */
5837 : /************************************************************************/
5838 :
5839 : /** Return a view of the array using field access
5840 : *
5841 : * Equivalent of GetView("['fieldName']")
5842 : *
5843 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5844 : */
5845 : std::shared_ptr<GDALMDArray>
5846 2 : GDALMDArray::operator[](const std::string &fieldName) const
5847 : {
5848 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5849 4 : .replaceAll('\\', "\\\\")
5850 4 : .replaceAll('\'', "\\\'")
5851 6 : .c_str()));
5852 : }
5853 :
5854 : /************************************************************************/
5855 : /* GDALMDArrayTransposed */
5856 : /************************************************************************/
5857 :
5858 : class GDALMDArrayTransposed final : public GDALPamMDArray
5859 : {
5860 : private:
5861 : std::shared_ptr<GDALMDArray> m_poParent{};
5862 : std::vector<int> m_anMapNewAxisToOldAxis{};
5863 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5864 :
5865 : mutable std::vector<GUInt64> m_parentStart;
5866 : mutable std::vector<size_t> m_parentCount;
5867 : mutable std::vector<GInt64> m_parentStep;
5868 : mutable std::vector<GPtrDiff_t> m_parentStride;
5869 :
5870 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5871 : const GInt64 *arrayStep,
5872 : const GPtrDiff_t *bufferStride) const;
5873 :
5874 : static std::string
5875 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5876 : {
5877 84 : std::string ret;
5878 84 : ret += '[';
5879 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5880 : {
5881 228 : if (i > 0)
5882 144 : ret += ',';
5883 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5884 : }
5885 84 : ret += ']';
5886 84 : return ret;
5887 : }
5888 :
5889 : protected:
5890 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5891 : const std::vector<int> &anMapNewAxisToOldAxis,
5892 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5893 84 : : GDALAbstractMDArray(std::string(),
5894 84 : "Transposed view of " + poParent->GetFullName() +
5895 84 : " along " +
5896 42 : MappingToStr(anMapNewAxisToOldAxis)),
5897 84 : GDALPamMDArray(std::string(),
5898 84 : "Transposed view of " + poParent->GetFullName() +
5899 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5900 84 : GDALPamMultiDim::GetPAM(poParent),
5901 : poParent->GetContext()),
5902 42 : m_poParent(std::move(poParent)),
5903 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5904 42 : m_dims(std::move(dims)),
5905 42 : m_parentStart(m_poParent->GetDimensionCount()),
5906 42 : m_parentCount(m_poParent->GetDimensionCount()),
5907 42 : m_parentStep(m_poParent->GetDimensionCount()),
5908 336 : m_parentStride(m_poParent->GetDimensionCount())
5909 : {
5910 42 : }
5911 :
5912 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5913 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5914 : const GDALExtendedDataType &bufferDataType,
5915 : void *pDstBuffer) const override;
5916 :
5917 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5918 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5919 : const GDALExtendedDataType &bufferDataType,
5920 : const void *pSrcBuffer) override;
5921 :
5922 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5923 : CSLConstList papszOptions) const override;
5924 :
5925 : public:
5926 : static std::shared_ptr<GDALMDArrayTransposed>
5927 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5928 : const std::vector<int> &anMapNewAxisToOldAxis)
5929 : {
5930 42 : const auto &parentDims(poParent->GetDimensions());
5931 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5932 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5933 : {
5934 114 : if (iOldAxis < 0)
5935 : {
5936 1 : dims.push_back(std::make_shared<GDALDimension>(
5937 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5938 : }
5939 : else
5940 : {
5941 113 : dims.emplace_back(parentDims[iOldAxis]);
5942 : }
5943 : }
5944 :
5945 : auto newAr(
5946 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5947 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5948 42 : newAr->SetSelf(newAr);
5949 84 : return newAr;
5950 : }
5951 :
5952 1 : bool IsWritable() const override
5953 : {
5954 1 : return m_poParent->IsWritable();
5955 : }
5956 :
5957 84 : const std::string &GetFilename() const override
5958 : {
5959 84 : return m_poParent->GetFilename();
5960 : }
5961 :
5962 : const std::vector<std::shared_ptr<GDALDimension>> &
5963 358 : GetDimensions() const override
5964 : {
5965 358 : return m_dims;
5966 : }
5967 :
5968 141 : const GDALExtendedDataType &GetDataType() const override
5969 : {
5970 141 : return m_poParent->GetDataType();
5971 : }
5972 :
5973 4 : const std::string &GetUnit() const override
5974 : {
5975 4 : return m_poParent->GetUnit();
5976 : }
5977 :
5978 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5979 : {
5980 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
5981 5 : if (!poSrcSRS)
5982 2 : return nullptr;
5983 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5984 6 : std::vector<int> dstMapping;
5985 9 : for (int srcAxis : srcMapping)
5986 : {
5987 6 : bool bFound = false;
5988 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
5989 : {
5990 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
5991 : {
5992 6 : dstMapping.push_back(static_cast<int>(i) + 1);
5993 6 : bFound = true;
5994 6 : break;
5995 : }
5996 : }
5997 6 : if (!bFound)
5998 : {
5999 0 : dstMapping.push_back(0);
6000 : }
6001 : }
6002 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6003 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6004 3 : return poClone;
6005 : }
6006 :
6007 4 : const void *GetRawNoDataValue() const override
6008 : {
6009 4 : return m_poParent->GetRawNoDataValue();
6010 : }
6011 :
6012 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6013 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6014 :
6015 4 : double GetOffset(bool *pbHasOffset,
6016 : GDALDataType *peStorageType) const override
6017 : {
6018 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6019 : }
6020 :
6021 4 : double GetScale(bool *pbHasScale,
6022 : GDALDataType *peStorageType) const override
6023 : {
6024 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6025 : }
6026 :
6027 : // bool SetOffset(double dfOffset) override { return
6028 : // m_poParent->SetOffset(dfOffset); }
6029 :
6030 : // bool SetScale(double dfScale) override { return
6031 : // m_poParent->SetScale(dfScale); }
6032 :
6033 3 : std::vector<GUInt64> GetBlockSize() const override
6034 : {
6035 3 : std::vector<GUInt64> ret(GetDimensionCount());
6036 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6037 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6038 : {
6039 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6040 8 : if (iOldAxis >= 0)
6041 : {
6042 7 : ret[i] = parentBlockSize[iOldAxis];
6043 : }
6044 : }
6045 6 : return ret;
6046 : }
6047 :
6048 : std::shared_ptr<GDALAttribute>
6049 1 : GetAttribute(const std::string &osName) const override
6050 : {
6051 1 : return m_poParent->GetAttribute(osName);
6052 : }
6053 :
6054 : std::vector<std::shared_ptr<GDALAttribute>>
6055 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6056 : {
6057 6 : return m_poParent->GetAttributes(papszOptions);
6058 : }
6059 : };
6060 :
6061 : /************************************************************************/
6062 : /* PrepareParentArrays() */
6063 : /************************************************************************/
6064 :
6065 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6066 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6067 : const GPtrDiff_t *bufferStride) const
6068 : {
6069 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6070 : {
6071 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6072 129 : if (iOldAxis >= 0)
6073 : {
6074 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6075 128 : m_parentCount[iOldAxis] = count[i];
6076 128 : if (arrayStep) // only null when called from IAdviseRead()
6077 : {
6078 126 : m_parentStep[iOldAxis] = arrayStep[i];
6079 : }
6080 128 : if (bufferStride) // only null when called from IAdviseRead()
6081 : {
6082 126 : m_parentStride[iOldAxis] = bufferStride[i];
6083 : }
6084 : }
6085 : }
6086 47 : }
6087 :
6088 : /************************************************************************/
6089 : /* IRead() */
6090 : /************************************************************************/
6091 :
6092 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6093 : const size_t *count, const GInt64 *arrayStep,
6094 : const GPtrDiff_t *bufferStride,
6095 : const GDALExtendedDataType &bufferDataType,
6096 : void *pDstBuffer) const
6097 : {
6098 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6099 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6100 44 : m_parentStep.data(), m_parentStride.data(),
6101 44 : bufferDataType, pDstBuffer);
6102 : }
6103 :
6104 : /************************************************************************/
6105 : /* IWrite() */
6106 : /************************************************************************/
6107 :
6108 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6109 : const size_t *count, const GInt64 *arrayStep,
6110 : const GPtrDiff_t *bufferStride,
6111 : const GDALExtendedDataType &bufferDataType,
6112 : const void *pSrcBuffer)
6113 : {
6114 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6115 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6116 2 : m_parentStep.data(), m_parentStride.data(),
6117 2 : bufferDataType, pSrcBuffer);
6118 : }
6119 :
6120 : /************************************************************************/
6121 : /* IAdviseRead() */
6122 : /************************************************************************/
6123 :
6124 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6125 : const size_t *count,
6126 : CSLConstList papszOptions) const
6127 : {
6128 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6129 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6130 1 : papszOptions);
6131 : }
6132 :
6133 : /************************************************************************/
6134 : /* Transpose() */
6135 : /************************************************************************/
6136 :
6137 : /** Return a view of the array whose axis have been reordered.
6138 : *
6139 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6140 : * and GetDimensionCount() - 1, and each only once.
6141 : * -1 can be used as a special index value to ask for the insertion of a new
6142 : * axis of size 1.
6143 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6144 : * index of one of its dimension, it corresponds to the axis of index
6145 : * anMapNewAxisToOldAxis[i] from the current array.
6146 : *
6147 : * This is similar to the numpy.transpose() method
6148 : *
6149 : * The returned array holds a reference to the original one, and thus is
6150 : * a view of it (not a copy). If the content of the original array changes,
6151 : * the content of the view array too. The view can be written if the underlying
6152 : * array is writable.
6153 : *
6154 : * Note that I/O performance in such a transposed view might be poor.
6155 : *
6156 : * This is the same as the C function GDALMDArrayTranspose().
6157 : *
6158 : * @return a new array, that holds a reference to the original one, and thus is
6159 : * a view of it (not a copy), or nullptr in case of error.
6160 : */
6161 : std::shared_ptr<GDALMDArray>
6162 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6163 : {
6164 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6165 50 : if (!self)
6166 : {
6167 0 : CPLError(CE_Failure, CPLE_AppDefined,
6168 : "Driver implementation issue: m_pSelf not set !");
6169 0 : return nullptr;
6170 : }
6171 50 : const int nDims = static_cast<int>(GetDimensionCount());
6172 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6173 50 : int nCountOldAxis = 0;
6174 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6175 : {
6176 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6177 : {
6178 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6179 4 : return nullptr;
6180 : }
6181 130 : if (iOldAxis >= 0)
6182 : {
6183 128 : if (alreadyUsedOldAxis[iOldAxis])
6184 : {
6185 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6186 : iOldAxis);
6187 1 : return nullptr;
6188 : }
6189 127 : alreadyUsedOldAxis[iOldAxis] = true;
6190 127 : nCountOldAxis++;
6191 : }
6192 : }
6193 46 : if (nCountOldAxis != nDims)
6194 : {
6195 4 : CPLError(CE_Failure, CPLE_AppDefined,
6196 : "One or several original axis missing");
6197 4 : return nullptr;
6198 : }
6199 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6200 : }
6201 :
6202 : /************************************************************************/
6203 : /* IRead() */
6204 : /************************************************************************/
6205 :
6206 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6207 : const size_t *count, const GInt64 *arrayStep,
6208 : const GPtrDiff_t *bufferStride,
6209 : const GDALExtendedDataType &bufferDataType,
6210 : void *pDstBuffer) const
6211 : {
6212 16 : const double dfScale = m_dfScale;
6213 16 : const double dfOffset = m_dfOffset;
6214 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6215 : const auto dtDouble =
6216 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6217 16 : const size_t nDTSize = dtDouble.GetSize();
6218 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6219 :
6220 16 : double adfSrcNoData[2] = {0, 0};
6221 16 : if (m_bHasNoData)
6222 : {
6223 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6224 9 : m_poParent->GetDataType(),
6225 : &adfSrcNoData[0], dtDouble);
6226 : }
6227 :
6228 16 : const auto nDims = GetDimensions().size();
6229 16 : if (nDims == 0)
6230 : {
6231 : double adfVal[2];
6232 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6233 : dtDouble, &adfVal[0]))
6234 : {
6235 0 : return false;
6236 : }
6237 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6238 : {
6239 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6240 6 : if (bDTIsComplex)
6241 : {
6242 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6243 : }
6244 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6245 : bufferDataType);
6246 : }
6247 : else
6248 : {
6249 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6250 : pDstBuffer, bufferDataType);
6251 : }
6252 9 : return true;
6253 : }
6254 :
6255 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6256 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6257 7 : void *pTempBuffer = pDstBuffer;
6258 7 : if (bTempBufferNeeded)
6259 : {
6260 2 : size_t nElts = 1;
6261 2 : actualBufferStrideVector.resize(nDims);
6262 7 : for (size_t i = 0; i < nDims; i++)
6263 5 : nElts *= count[i];
6264 2 : actualBufferStrideVector.back() = 1;
6265 5 : for (size_t i = nDims - 1; i > 0;)
6266 : {
6267 3 : --i;
6268 3 : actualBufferStrideVector[i] =
6269 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6270 : }
6271 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6272 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6273 2 : if (!pTempBuffer)
6274 0 : return false;
6275 : }
6276 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6277 : actualBufferStridePtr, dtDouble, pTempBuffer))
6278 : {
6279 0 : if (bTempBufferNeeded)
6280 0 : VSIFree(pTempBuffer);
6281 0 : return false;
6282 : }
6283 :
6284 : struct Stack
6285 : {
6286 : size_t nIters = 0;
6287 : double *src_ptr = nullptr;
6288 : GByte *dst_ptr = nullptr;
6289 : GPtrDiff_t src_inc_offset = 0;
6290 : GPtrDiff_t dst_inc_offset = 0;
6291 : };
6292 :
6293 7 : std::vector<Stack> stack(nDims);
6294 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6295 23 : for (size_t i = 0; i < nDims; i++)
6296 : {
6297 32 : stack[i].src_inc_offset =
6298 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6299 16 : stack[i].dst_inc_offset =
6300 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6301 : }
6302 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6303 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6304 :
6305 7 : size_t dimIdx = 0;
6306 7 : const size_t nDimsMinus1 = nDims - 1;
6307 : GByte abyDstNoData[16];
6308 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6309 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6310 : bufferDataType);
6311 :
6312 37 : lbl_next_depth:
6313 37 : if (dimIdx == nDimsMinus1)
6314 : {
6315 25 : auto nIters = count[dimIdx];
6316 25 : double *padfVal = stack[dimIdx].src_ptr;
6317 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6318 : while (true)
6319 : {
6320 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6321 : {
6322 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6323 88 : if (bDTIsComplex)
6324 : {
6325 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6326 : }
6327 88 : if (bTempBufferNeeded)
6328 : {
6329 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6330 : dst_ptr, bufferDataType);
6331 : }
6332 : }
6333 : else
6334 : {
6335 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6336 : }
6337 :
6338 92 : if ((--nIters) == 0)
6339 25 : break;
6340 67 : padfVal += stack[dimIdx].src_inc_offset;
6341 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6342 : }
6343 : }
6344 : else
6345 : {
6346 12 : stack[dimIdx].nIters = count[dimIdx];
6347 : while (true)
6348 : {
6349 30 : dimIdx++;
6350 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6351 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6352 30 : goto lbl_next_depth;
6353 30 : lbl_return_to_caller:
6354 30 : dimIdx--;
6355 30 : if ((--stack[dimIdx].nIters) == 0)
6356 12 : break;
6357 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6358 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6359 : }
6360 : }
6361 37 : if (dimIdx > 0)
6362 30 : goto lbl_return_to_caller;
6363 :
6364 7 : if (bTempBufferNeeded)
6365 2 : VSIFree(pTempBuffer);
6366 7 : return true;
6367 : }
6368 :
6369 : /************************************************************************/
6370 : /* IWrite() */
6371 : /************************************************************************/
6372 :
6373 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6374 : const size_t *count, const GInt64 *arrayStep,
6375 : const GPtrDiff_t *bufferStride,
6376 : const GDALExtendedDataType &bufferDataType,
6377 : const void *pSrcBuffer)
6378 : {
6379 16 : const double dfScale = m_dfScale;
6380 16 : const double dfOffset = m_dfOffset;
6381 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6382 : const auto dtDouble =
6383 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6384 16 : const size_t nDTSize = dtDouble.GetSize();
6385 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6386 : const bool bSelfAndParentHaveNoData =
6387 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6388 16 : double dfNoData = 0;
6389 16 : if (m_bHasNoData)
6390 : {
6391 7 : GDALCopyWords(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6392 : &dfNoData, GDT_Float64, 0, 1);
6393 : }
6394 :
6395 16 : double adfSrcNoData[2] = {0, 0};
6396 16 : if (bSelfAndParentHaveNoData)
6397 : {
6398 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6399 7 : m_poParent->GetDataType(),
6400 : &adfSrcNoData[0], dtDouble);
6401 : }
6402 :
6403 16 : const auto nDims = GetDimensions().size();
6404 16 : if (nDims == 0)
6405 : {
6406 : double adfVal[2];
6407 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6408 : dtDouble);
6409 16 : if (bSelfAndParentHaveNoData &&
6410 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6411 : {
6412 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6413 2 : bufferStride, m_poParent->GetDataType(),
6414 4 : m_poParent->GetRawNoDataValue());
6415 : }
6416 : else
6417 : {
6418 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6419 8 : if (bDTIsComplex)
6420 : {
6421 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6422 : }
6423 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6424 8 : bufferStride, dtDouble, &adfVal[0]);
6425 : }
6426 : }
6427 :
6428 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6429 6 : size_t nElts = 1;
6430 6 : tmpBufferStrideVector.resize(nDims);
6431 20 : for (size_t i = 0; i < nDims; i++)
6432 14 : nElts *= count[i];
6433 6 : tmpBufferStrideVector.back() = 1;
6434 14 : for (size_t i = nDims - 1; i > 0;)
6435 : {
6436 8 : --i;
6437 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6438 : }
6439 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6440 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6441 6 : if (!pTempBuffer)
6442 0 : return false;
6443 :
6444 : struct Stack
6445 : {
6446 : size_t nIters = 0;
6447 : double *dst_ptr = nullptr;
6448 : const GByte *src_ptr = nullptr;
6449 : GPtrDiff_t src_inc_offset = 0;
6450 : GPtrDiff_t dst_inc_offset = 0;
6451 : };
6452 :
6453 6 : std::vector<Stack> stack(nDims);
6454 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6455 20 : for (size_t i = 0; i < nDims; i++)
6456 : {
6457 28 : stack[i].dst_inc_offset =
6458 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6459 14 : stack[i].src_inc_offset =
6460 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6461 : }
6462 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6463 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6464 :
6465 6 : size_t dimIdx = 0;
6466 6 : const size_t nDimsMinus1 = nDims - 1;
6467 :
6468 34 : lbl_next_depth:
6469 34 : if (dimIdx == nDimsMinus1)
6470 : {
6471 23 : auto nIters = count[dimIdx];
6472 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6473 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6474 : while (true)
6475 : {
6476 : double adfVal[2];
6477 : const double *padfSrcVal;
6478 86 : if (bIsBufferDataTypeNativeDataType)
6479 : {
6480 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6481 : }
6482 : else
6483 : {
6484 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6485 : &adfVal[0], dtDouble);
6486 36 : padfSrcVal = adfVal;
6487 : }
6488 :
6489 148 : if (bSelfAndParentHaveNoData &&
6490 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6491 : {
6492 3 : dst_ptr[0] = adfSrcNoData[0];
6493 3 : if (bDTIsComplex)
6494 : {
6495 1 : dst_ptr[1] = adfSrcNoData[1];
6496 : }
6497 : }
6498 : else
6499 : {
6500 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6501 83 : if (bDTIsComplex)
6502 : {
6503 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6504 : }
6505 : }
6506 :
6507 86 : if ((--nIters) == 0)
6508 23 : break;
6509 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6510 63 : src_ptr += stack[dimIdx].src_inc_offset;
6511 63 : }
6512 : }
6513 : else
6514 : {
6515 11 : stack[dimIdx].nIters = count[dimIdx];
6516 : while (true)
6517 : {
6518 28 : dimIdx++;
6519 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6520 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6521 28 : goto lbl_next_depth;
6522 28 : lbl_return_to_caller:
6523 28 : dimIdx--;
6524 28 : if ((--stack[dimIdx].nIters) == 0)
6525 11 : break;
6526 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6527 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6528 : }
6529 : }
6530 34 : if (dimIdx > 0)
6531 28 : goto lbl_return_to_caller;
6532 :
6533 : // If the parent array is not double/complex-double, then convert the
6534 : // values to it, before calling Write(), as some implementations can be
6535 : // very slow when doing the type conversion.
6536 6 : const auto &eParentDT = m_poParent->GetDataType();
6537 6 : const size_t nParentDTSize = eParentDT.GetSize();
6538 6 : if (nParentDTSize <= nDTSize / 2)
6539 : {
6540 : // Copy in-place by making sure that source and target do not overlap
6541 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6542 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6543 :
6544 : // Copy first element
6545 : {
6546 6 : std::vector<GByte> abyTemp(nParentDTSize);
6547 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6548 6 : static_cast<int>(nDTSize), &abyTemp[0],
6549 : eParentNumericDT, static_cast<int>(nParentDTSize),
6550 : 1);
6551 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6552 : }
6553 : // Remaining elements
6554 86 : for (size_t i = 1; i < nElts; ++i)
6555 : {
6556 80 : GDALCopyWords(static_cast<GByte *>(pTempBuffer) + i * nDTSize,
6557 : eNumericDT, 0,
6558 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6559 : eParentNumericDT, 0, 1);
6560 : }
6561 : }
6562 :
6563 : const bool ret =
6564 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6565 : eParentDT, pTempBuffer);
6566 :
6567 6 : VSIFree(pTempBuffer);
6568 6 : return ret;
6569 : }
6570 :
6571 : /************************************************************************/
6572 : /* GetUnscaled() */
6573 : /************************************************************************/
6574 :
6575 : /** Return an array that is the unscaled version of the current one.
6576 : *
6577 : * That is each value of the unscaled array will be
6578 : * unscaled_value = raw_value * GetScale() + GetOffset()
6579 : *
6580 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6581 : * from unscaled values to raw values.
6582 : *
6583 : * This is the same as the C function GDALMDArrayGetUnscaled().
6584 : *
6585 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6586 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6587 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6588 : * @return a new array, that holds a reference to the original one, and thus is
6589 : * a view of it (not a copy), or nullptr in case of error.
6590 : */
6591 : std::shared_ptr<GDALMDArray>
6592 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6593 : double dfOverriddenDstNodata) const
6594 : {
6595 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6596 17 : if (!self)
6597 : {
6598 0 : CPLError(CE_Failure, CPLE_AppDefined,
6599 : "Driver implementation issue: m_pSelf not set !");
6600 0 : return nullptr;
6601 : }
6602 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6603 : {
6604 0 : CPLError(CE_Failure, CPLE_AppDefined,
6605 : "GetUnscaled() only supports numeric data type");
6606 0 : return nullptr;
6607 : }
6608 : const double dfScale =
6609 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6610 : const double dfOffset =
6611 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6612 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6613 4 : return self;
6614 :
6615 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6616 13 : ? GDT_CFloat64
6617 13 : : GDT_Float64;
6618 14 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
6619 1 : GetDataType().GetNumericDataType() == GDT_Float32)
6620 1 : eDT = GDT_Float32;
6621 :
6622 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6623 13 : dfOverriddenDstNodata, eDT);
6624 : }
6625 :
6626 : /************************************************************************/
6627 : /* GDALMDArrayMask */
6628 : /************************************************************************/
6629 :
6630 : class GDALMDArrayMask final : public GDALPamMDArray
6631 : {
6632 : private:
6633 : std::shared_ptr<GDALMDArray> m_poParent{};
6634 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6635 : double m_dfMissingValue = 0.0;
6636 : bool m_bHasMissingValue = false;
6637 : double m_dfFillValue = 0.0;
6638 : bool m_bHasFillValue = false;
6639 : double m_dfValidMin = 0.0;
6640 : bool m_bHasValidMin = false;
6641 : double m_dfValidMax = 0.0;
6642 : bool m_bHasValidMax = false;
6643 : std::vector<uint32_t> m_anValidFlagMasks{};
6644 : std::vector<uint32_t> m_anValidFlagValues{};
6645 :
6646 : bool Init(CSLConstList papszOptions);
6647 :
6648 : template <typename Type>
6649 : void
6650 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6651 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6652 : const void *pTempBuffer,
6653 : const GDALExtendedDataType &oTmpBufferDT,
6654 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6655 :
6656 : protected:
6657 45 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6658 90 : : GDALAbstractMDArray(std::string(),
6659 90 : "Mask of " + poParent->GetFullName()),
6660 90 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6661 90 : GDALPamMultiDim::GetPAM(poParent),
6662 : poParent->GetContext()),
6663 225 : m_poParent(std::move(poParent))
6664 : {
6665 45 : }
6666 :
6667 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6668 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6669 : const GDALExtendedDataType &bufferDataType,
6670 : void *pDstBuffer) const override;
6671 :
6672 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6673 : CSLConstList papszOptions) const override
6674 : {
6675 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6676 : }
6677 :
6678 : public:
6679 : static std::shared_ptr<GDALMDArrayMask>
6680 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6681 : CSLConstList papszOptions);
6682 :
6683 1 : bool IsWritable() const override
6684 : {
6685 1 : return false;
6686 : }
6687 :
6688 48 : const std::string &GetFilename() const override
6689 : {
6690 48 : return m_poParent->GetFilename();
6691 : }
6692 :
6693 : const std::vector<std::shared_ptr<GDALDimension>> &
6694 373 : GetDimensions() const override
6695 : {
6696 373 : return m_poParent->GetDimensions();
6697 : }
6698 :
6699 132 : const GDALExtendedDataType &GetDataType() const override
6700 : {
6701 132 : return m_dt;
6702 : }
6703 :
6704 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6705 : {
6706 1 : return m_poParent->GetSpatialRef();
6707 : }
6708 :
6709 2 : std::vector<GUInt64> GetBlockSize() const override
6710 : {
6711 2 : return m_poParent->GetBlockSize();
6712 : }
6713 : };
6714 :
6715 : /************************************************************************/
6716 : /* GDALMDArrayMask::Create() */
6717 : /************************************************************************/
6718 :
6719 : /* static */ std::shared_ptr<GDALMDArrayMask>
6720 45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6721 : CSLConstList papszOptions)
6722 : {
6723 90 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6724 45 : newAr->SetSelf(newAr);
6725 45 : if (!newAr->Init(papszOptions))
6726 6 : return nullptr;
6727 39 : return newAr;
6728 : }
6729 :
6730 : /************************************************************************/
6731 : /* GDALMDArrayMask::Init() */
6732 : /************************************************************************/
6733 :
6734 45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6735 : {
6736 : const auto GetSingleValNumericAttr =
6737 180 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6738 : {
6739 540 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6740 180 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6741 : {
6742 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6743 21 : if (anDimSizes.empty() ||
6744 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6745 : {
6746 11 : bHasVal = true;
6747 11 : dfVal = poAttr->ReadAsDouble();
6748 : }
6749 : }
6750 180 : };
6751 :
6752 45 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6753 45 : m_dfMissingValue);
6754 45 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6755 45 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6756 45 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6757 :
6758 : {
6759 135 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6760 50 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6761 55 : poValidRange->GetDimensionsSize()[0] == 2 &&
6762 5 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6763 : {
6764 5 : m_bHasValidMin = true;
6765 5 : m_bHasValidMax = true;
6766 5 : auto vals = poValidRange->ReadAsDoubleArray();
6767 5 : CPLAssert(vals.size() == 2);
6768 5 : m_dfValidMin = vals[0];
6769 5 : m_dfValidMax = vals[1];
6770 : }
6771 : }
6772 :
6773 : // Take into account
6774 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6775 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6776 : const char *pszUnmaskFlags =
6777 45 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6778 45 : if (pszUnmaskFlags)
6779 : {
6780 : const auto IsScalarStringAttr =
6781 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6782 : {
6783 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6784 26 : (poAttr->GetDimensionsSize().empty() ||
6785 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6786 26 : poAttr->GetDimensionsSize()[0] == 1));
6787 : };
6788 :
6789 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6790 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6791 : {
6792 1 : CPLError(CE_Failure, CPLE_AppDefined,
6793 : "UNMASK_FLAGS option specified but array has no "
6794 : "flag_meanings attribute");
6795 1 : return false;
6796 : }
6797 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6798 13 : if (!pszFlagMeanings)
6799 : {
6800 1 : CPLError(CE_Failure, CPLE_AppDefined,
6801 : "Cannot read flag_meanings attribute");
6802 1 : return false;
6803 : }
6804 :
6805 : const auto IsSingleDimNumericAttr =
6806 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6807 : {
6808 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6809 26 : poAttr->GetDimensionsSize().size() == 1;
6810 : };
6811 :
6812 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6813 : const bool bHasFlagValues =
6814 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6815 :
6816 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6817 : const bool bHasFlagMasks =
6818 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6819 :
6820 12 : if (!bHasFlagValues && !bHasFlagMasks)
6821 : {
6822 1 : CPLError(CE_Failure, CPLE_AppDefined,
6823 : "Cannot find flag_values and/or flag_masks attribute");
6824 1 : return false;
6825 : }
6826 :
6827 : const CPLStringList aosUnmaskFlags(
6828 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6829 : const CPLStringList aosFlagMeanings(
6830 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6831 :
6832 11 : if (bHasFlagValues)
6833 : {
6834 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6835 : // We could support Int64 or UInt64, but more work...
6836 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6837 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6838 : {
6839 0 : CPLError(CE_Failure, CPLE_NotSupported,
6840 : "Unsupported data type for flag_values attribute: %s",
6841 : GDALGetDataTypeName(eType));
6842 0 : return false;
6843 : }
6844 : }
6845 :
6846 11 : if (bHasFlagMasks)
6847 : {
6848 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6849 : // We could support Int64 or UInt64, but more work...
6850 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6851 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6852 : {
6853 0 : CPLError(CE_Failure, CPLE_NotSupported,
6854 : "Unsupported data type for flag_masks attribute: %s",
6855 : GDALGetDataTypeName(eType));
6856 0 : return false;
6857 : }
6858 : }
6859 :
6860 : const std::vector<double> adfValues(
6861 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6862 11 : : std::vector<double>());
6863 : const std::vector<double> adfMasks(
6864 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6865 11 : : std::vector<double>());
6866 :
6867 18 : if (bHasFlagValues &&
6868 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6869 : {
6870 1 : CPLError(CE_Failure, CPLE_AppDefined,
6871 : "Number of values in flag_values attribute is different "
6872 : "from the one in flag_meanings");
6873 1 : return false;
6874 : }
6875 :
6876 16 : if (bHasFlagMasks &&
6877 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6878 : {
6879 1 : CPLError(CE_Failure, CPLE_AppDefined,
6880 : "Number of values in flag_masks attribute is different "
6881 : "from the one in flag_meanings");
6882 1 : return false;
6883 : }
6884 :
6885 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6886 : {
6887 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6888 11 : if (nIdxFlag < 0)
6889 : {
6890 1 : CPLError(
6891 : CE_Failure, CPLE_AppDefined,
6892 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6893 : aosUnmaskFlags[i], pszFlagMeanings);
6894 1 : return false;
6895 : }
6896 :
6897 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6898 : {
6899 0 : CPLError(CE_Failure, CPLE_AppDefined,
6900 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6901 0 : adfValues[nIdxFlag]);
6902 0 : return false;
6903 : }
6904 :
6905 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6906 : {
6907 0 : CPLError(CE_Failure, CPLE_AppDefined,
6908 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6909 0 : adfMasks[nIdxFlag]);
6910 0 : return false;
6911 : }
6912 :
6913 10 : if (bHasFlagValues)
6914 : {
6915 12 : m_anValidFlagValues.push_back(
6916 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6917 : }
6918 :
6919 10 : if (bHasFlagMasks)
6920 : {
6921 12 : m_anValidFlagMasks.push_back(
6922 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6923 : }
6924 : }
6925 : }
6926 :
6927 39 : return true;
6928 : }
6929 :
6930 : /************************************************************************/
6931 : /* IRead() */
6932 : /************************************************************************/
6933 :
6934 48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6935 : const GInt64 *arrayStep,
6936 : const GPtrDiff_t *bufferStride,
6937 : const GDALExtendedDataType &bufferDataType,
6938 : void *pDstBuffer) const
6939 : {
6940 48 : size_t nElts = 1;
6941 48 : const size_t nDims = GetDimensionCount();
6942 96 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6943 132 : for (size_t i = 0; i < nDims; i++)
6944 84 : nElts *= count[i];
6945 48 : if (nDims > 0)
6946 : {
6947 43 : tmpBufferStrideVector.back() = 1;
6948 84 : for (size_t i = nDims - 1; i > 0;)
6949 : {
6950 41 : --i;
6951 41 : tmpBufferStrideVector[i] =
6952 41 : tmpBufferStrideVector[i + 1] * count[i + 1];
6953 : }
6954 : }
6955 :
6956 : /* Optimized case: if we are an integer data type and that there is no */
6957 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
6958 : /* directly */
6959 46 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
6960 70 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
6961 32 : m_anValidFlagMasks.empty() &&
6962 103 : m_poParent->GetRawNoDataValue() == nullptr &&
6963 9 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
6964 : {
6965 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
6966 7 : if (bBufferDataTypeIsByte) // Byte case
6967 : {
6968 4 : bool bContiguous = true;
6969 10 : for (size_t i = 0; i < nDims; i++)
6970 : {
6971 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
6972 : {
6973 1 : bContiguous = false;
6974 1 : break;
6975 : }
6976 : }
6977 4 : if (bContiguous)
6978 : {
6979 : // CPLDebug("GDAL", "GetMask(): contiguous case");
6980 3 : memset(pDstBuffer, 1, nElts);
6981 3 : return true;
6982 : }
6983 : }
6984 :
6985 : struct Stack
6986 : {
6987 : size_t nIters = 0;
6988 : GByte *dst_ptr = nullptr;
6989 : GPtrDiff_t dst_inc_offset = 0;
6990 : };
6991 :
6992 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
6993 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
6994 13 : for (size_t i = 0; i < nDims; i++)
6995 : {
6996 9 : stack[i].dst_inc_offset =
6997 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6998 : }
6999 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7000 :
7001 4 : size_t dimIdx = 0;
7002 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7003 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7004 4 : CPLAssert(nBufferDTSize <= 16);
7005 4 : const GByte flag = 1;
7006 : // Coverity misses that m_dt is of type Byte
7007 : // coverity[overrun-buffer-val]
7008 4 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
7009 :
7010 28 : lbl_next_depth:
7011 28 : if (dimIdx == nDimsMinus1)
7012 : {
7013 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7014 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7015 :
7016 : while (true)
7017 : {
7018 : // cppcheck-suppress knownConditionTrueFalse
7019 73 : if (bBufferDataTypeIsByte)
7020 : {
7021 24 : *dst_ptr = flag;
7022 : }
7023 : else
7024 : {
7025 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7026 : }
7027 :
7028 73 : if ((--nIters) == 0)
7029 19 : break;
7030 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7031 : }
7032 : }
7033 : else
7034 : {
7035 9 : stack[dimIdx].nIters = count[dimIdx];
7036 : while (true)
7037 : {
7038 24 : dimIdx++;
7039 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7040 24 : goto lbl_next_depth;
7041 24 : lbl_return_to_caller:
7042 24 : dimIdx--;
7043 24 : if ((--stack[dimIdx].nIters) == 0)
7044 9 : break;
7045 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7046 : }
7047 : }
7048 28 : if (dimIdx > 0)
7049 24 : goto lbl_return_to_caller;
7050 :
7051 4 : return true;
7052 : }
7053 :
7054 : const auto oTmpBufferDT =
7055 41 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7056 : ? GDALExtendedDataType::Create(GDT_Float64)
7057 82 : : m_poParent->GetDataType();
7058 41 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7059 41 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7060 41 : if (!pTempBuffer)
7061 0 : return false;
7062 82 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7063 41 : tmpBufferStrideVector.data(), oTmpBufferDT,
7064 : pTempBuffer))
7065 : {
7066 0 : VSIFree(pTempBuffer);
7067 0 : return false;
7068 : }
7069 :
7070 41 : switch (oTmpBufferDT.GetNumericDataType())
7071 : {
7072 6 : case GDT_Byte:
7073 6 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7074 : pTempBuffer, oTmpBufferDT,
7075 : tmpBufferStrideVector);
7076 6 : break;
7077 :
7078 0 : case GDT_Int8:
7079 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7080 : pTempBuffer, oTmpBufferDT,
7081 : tmpBufferStrideVector);
7082 0 : break;
7083 :
7084 1 : case GDT_UInt16:
7085 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7086 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7087 : tmpBufferStrideVector);
7088 1 : break;
7089 :
7090 14 : case GDT_Int16:
7091 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7092 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7093 : tmpBufferStrideVector);
7094 14 : break;
7095 :
7096 1 : case GDT_UInt32:
7097 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7098 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7099 : tmpBufferStrideVector);
7100 1 : break;
7101 :
7102 5 : case GDT_Int32:
7103 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7104 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7105 : tmpBufferStrideVector);
7106 5 : break;
7107 :
7108 0 : case GDT_UInt64:
7109 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7110 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7111 : tmpBufferStrideVector);
7112 0 : break;
7113 :
7114 0 : case GDT_Int64:
7115 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7116 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7117 : tmpBufferStrideVector);
7118 0 : break;
7119 :
7120 7 : case GDT_Float32:
7121 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7122 : pTempBuffer, oTmpBufferDT,
7123 : tmpBufferStrideVector);
7124 7 : break;
7125 :
7126 7 : case GDT_Float64:
7127 7 : ReadInternal<double>(count, bufferStride, bufferDataType,
7128 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7129 : tmpBufferStrideVector);
7130 7 : break;
7131 0 : case GDT_Unknown:
7132 : case GDT_CInt16:
7133 : case GDT_CInt32:
7134 : case GDT_CFloat32:
7135 : case GDT_CFloat64:
7136 : case GDT_TypeCount:
7137 0 : CPLAssert(false);
7138 : break;
7139 : }
7140 :
7141 41 : VSIFree(pTempBuffer);
7142 :
7143 41 : return true;
7144 : }
7145 :
7146 : /************************************************************************/
7147 : /* IsValidForDT() */
7148 : /************************************************************************/
7149 :
7150 38 : template <typename Type> static bool IsValidForDT(double dfVal)
7151 : {
7152 38 : if (std::isnan(dfVal))
7153 0 : return false;
7154 38 : if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
7155 0 : return false;
7156 38 : if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
7157 0 : return false;
7158 38 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7159 : }
7160 :
7161 9 : template <> bool IsValidForDT<double>(double)
7162 : {
7163 9 : return true;
7164 : }
7165 :
7166 : /************************************************************************/
7167 : /* IsNan() */
7168 : /************************************************************************/
7169 :
7170 1038 : template <typename Type> inline bool IsNan(Type)
7171 : {
7172 1038 : return false;
7173 : }
7174 :
7175 25 : template <> bool IsNan<double>(double val)
7176 : {
7177 25 : return std::isnan(val);
7178 : }
7179 :
7180 26 : template <> bool IsNan<float>(float val)
7181 : {
7182 26 : return std::isnan(val);
7183 : }
7184 :
7185 : /************************************************************************/
7186 : /* ReadInternal() */
7187 : /************************************************************************/
7188 :
7189 : template <typename Type>
7190 41 : void GDALMDArrayMask::ReadInternal(
7191 : const size_t *count, const GPtrDiff_t *bufferStride,
7192 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7193 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7194 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7195 : {
7196 41 : const size_t nDims = GetDimensionCount();
7197 :
7198 205 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7199 : {
7200 205 : if (bHasVal)
7201 : {
7202 47 : if (IsValidForDT<Type>(dfVal))
7203 : {
7204 47 : return static_cast<Type>(dfVal);
7205 : }
7206 : else
7207 : {
7208 0 : bHasVal = false;
7209 : }
7210 : }
7211 158 : return 0;
7212 : };
7213 :
7214 41 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7215 41 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7216 : const Type nNoDataValue =
7217 41 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7218 41 : bool bHasMissingValue = m_bHasMissingValue;
7219 41 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7220 41 : bool bHasFillValue = m_bHasFillValue;
7221 41 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7222 41 : bool bHasValidMin = m_bHasValidMin;
7223 41 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7224 41 : bool bHasValidMax = m_bHasValidMax;
7225 41 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7226 41 : const bool bHasValidFlags =
7227 41 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7228 :
7229 348 : const auto IsValidFlag = [this](Type v)
7230 : {
7231 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7232 : {
7233 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7234 : {
7235 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7236 : m_anValidFlagValues[i])
7237 : {
7238 4 : return true;
7239 : }
7240 : }
7241 : }
7242 42 : else if (!m_anValidFlagValues.empty())
7243 : {
7244 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7245 : {
7246 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7247 : {
7248 4 : return true;
7249 : }
7250 : }
7251 : }
7252 : else /* if( !m_anValidFlagMasks.empty() ) */
7253 : {
7254 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7255 : {
7256 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7257 : {
7258 9 : return true;
7259 : }
7260 : }
7261 : }
7262 37 : return false;
7263 : };
7264 :
7265 : #define GET_MASK_FOR_SAMPLE(v) \
7266 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7267 : !(bHasMissingValue && v == nMissingValue) && \
7268 : !(bHasFillValue && v == nFillValue) && \
7269 : !(bHasValidMin && v < nValidMin) && \
7270 : !(bHasValidMax && v > nValidMax) && \
7271 : (!bHasValidFlags || IsValidFlag(v)));
7272 :
7273 41 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7274 : /* Optimized case: Byte output and output buffer is contiguous */
7275 41 : if (bBufferDataTypeIsByte)
7276 : {
7277 37 : bool bContiguous = true;
7278 96 : for (size_t i = 0; i < nDims; i++)
7279 : {
7280 60 : if (bufferStride[i] != tmpBufferStrideVector[i])
7281 : {
7282 1 : bContiguous = false;
7283 1 : break;
7284 : }
7285 : }
7286 37 : if (bContiguous)
7287 : {
7288 36 : size_t nElts = 1;
7289 95 : for (size_t i = 0; i < nDims; i++)
7290 59 : nElts *= count[i];
7291 :
7292 670 : for (size_t i = 0; i < nElts; i++)
7293 : {
7294 634 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7295 634 : static_cast<GByte *>(pDstBuffer)[i] =
7296 634 : GET_MASK_FOR_SAMPLE(*pSrc);
7297 : }
7298 36 : return;
7299 : }
7300 : }
7301 :
7302 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7303 :
7304 : struct Stack
7305 : {
7306 : size_t nIters = 0;
7307 : const GByte *src_ptr = nullptr;
7308 : GByte *dst_ptr = nullptr;
7309 : GPtrDiff_t src_inc_offset = 0;
7310 : GPtrDiff_t dst_inc_offset = 0;
7311 : };
7312 :
7313 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7314 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7315 15 : for (size_t i = 0; i < nDims; i++)
7316 : {
7317 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7318 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7319 10 : stack[i].dst_inc_offset =
7320 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7321 : }
7322 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7323 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7324 :
7325 5 : size_t dimIdx = 0;
7326 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7327 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7328 5 : CPLAssert(nBufferDTSize <= 16);
7329 15 : for (GByte flag = 0; flag <= 1; flag++)
7330 : {
7331 : // Coverity misses that m_dt is of type Byte
7332 : // coverity[overrun-buffer-val]
7333 10 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
7334 : bufferDataType);
7335 : }
7336 :
7337 43 : lbl_next_depth:
7338 43 : if (dimIdx == nDimsMinus1)
7339 : {
7340 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7341 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7342 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7343 :
7344 420 : while (true)
7345 : {
7346 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7347 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7348 :
7349 455 : if (bBufferDataTypeIsByte)
7350 : {
7351 24 : *dst_ptr = flag;
7352 : }
7353 : else
7354 : {
7355 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7356 : }
7357 :
7358 455 : if ((--nIters) == 0)
7359 35 : break;
7360 420 : src_ptr += stack[dimIdx].src_inc_offset;
7361 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7362 : }
7363 : }
7364 : else
7365 : {
7366 8 : stack[dimIdx].nIters = count[dimIdx];
7367 : while (true)
7368 : {
7369 38 : dimIdx++;
7370 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7371 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7372 38 : goto lbl_next_depth;
7373 38 : lbl_return_to_caller:
7374 38 : dimIdx--;
7375 38 : if ((--stack[dimIdx].nIters) == 0)
7376 8 : break;
7377 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7378 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7379 : }
7380 : }
7381 43 : if (dimIdx > 0)
7382 38 : goto lbl_return_to_caller;
7383 : }
7384 :
7385 : /************************************************************************/
7386 : /* GetMask() */
7387 : /************************************************************************/
7388 :
7389 : /** Return an array that is a mask for the current array
7390 :
7391 : This array will be of type Byte, with values set to 0 to indicate invalid
7392 : pixels of the current array, and values set to 1 to indicate valid pixels.
7393 :
7394 : The generic implementation honours the NoDataValue, as well as various
7395 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7396 : and valid_range.
7397 :
7398 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7399 : can be used to specify strings of the "flag_meanings" attribute
7400 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7401 : for which pixels matching any of those flags will be set at 1 in the mask array,
7402 : and pixels matching none of those flags will be set at 0.
7403 : For example, let's consider the following netCDF variable defined with:
7404 : \verbatim
7405 : l2p_flags:valid_min = 0s ;
7406 : l2p_flags:valid_max = 256s ;
7407 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7408 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7409 : \endverbatim
7410 :
7411 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7412 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7413 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7414 : will be 1.
7415 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7416 : will be 0.
7417 :
7418 : This is the same as the C function GDALMDArrayGetMask().
7419 :
7420 : @param papszOptions NULL-terminated list of options, or NULL.
7421 :
7422 : @return a new array, that holds a reference to the original one, and thus is
7423 : a view of it (not a copy), or nullptr in case of error.
7424 : */
7425 : std::shared_ptr<GDALMDArray>
7426 46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7427 : {
7428 92 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7429 46 : if (!self)
7430 : {
7431 0 : CPLError(CE_Failure, CPLE_AppDefined,
7432 : "Driver implementation issue: m_pSelf not set !");
7433 0 : return nullptr;
7434 : }
7435 46 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7436 : {
7437 1 : CPLError(CE_Failure, CPLE_AppDefined,
7438 : "GetMask() only supports numeric data type");
7439 1 : return nullptr;
7440 : }
7441 45 : return GDALMDArrayMask::Create(self, papszOptions);
7442 : }
7443 :
7444 : /************************************************************************/
7445 : /* IsRegularlySpaced() */
7446 : /************************************************************************/
7447 :
7448 : /** Returns whether an array is a 1D regularly spaced array.
7449 : *
7450 : * @param[out] dfStart First value in the array
7451 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7452 : * @return true if the array is regularly spaced.
7453 : */
7454 181 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7455 : {
7456 181 : dfStart = 0;
7457 181 : dfIncrement = 0;
7458 181 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7459 0 : return false;
7460 181 : const auto nSize = GetDimensions()[0]->GetSize();
7461 181 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7462 2 : return false;
7463 :
7464 179 : size_t nCount = static_cast<size_t>(nSize);
7465 358 : std::vector<double> adfTmp;
7466 : try
7467 : {
7468 179 : adfTmp.resize(nCount);
7469 : }
7470 0 : catch (const std::exception &)
7471 : {
7472 0 : return false;
7473 : }
7474 :
7475 179 : GUInt64 anStart[1] = {0};
7476 179 : size_t anCount[1] = {nCount};
7477 :
7478 : const auto IsRegularlySpacedInternal =
7479 82516 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7480 : {
7481 251 : dfStart = adfTmp[0];
7482 251 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7483 251 : if (dfIncrement == 0)
7484 : {
7485 3 : return false;
7486 : }
7487 20564 : for (size_t i = 1; i < anCount[0]; i++)
7488 : {
7489 20316 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7490 20316 : 1e-3 * fabs(dfIncrement))
7491 : {
7492 0 : return false;
7493 : }
7494 : }
7495 248 : return true;
7496 179 : };
7497 :
7498 : // First try with the first block(s). This can avoid excessive processing
7499 : // time, for example with Zarr datasets.
7500 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7501 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7502 179 : const auto nBlockSize = GetBlockSize()[0];
7503 179 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7504 : {
7505 : size_t nReducedCount =
7506 75 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7507 436 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7508 361 : nReducedCount *= 2;
7509 75 : anCount[0] = nReducedCount;
7510 75 : if (!Read(anStart, anCount, nullptr, nullptr,
7511 150 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7512 : {
7513 0 : return false;
7514 : }
7515 75 : if (!IsRegularlySpacedInternal())
7516 : {
7517 3 : return false;
7518 : }
7519 :
7520 : // Get next values
7521 72 : anStart[0] = nReducedCount;
7522 72 : anCount[0] = nCount - nReducedCount;
7523 : }
7524 :
7525 176 : if (!Read(anStart, anCount, nullptr, nullptr,
7526 352 : GDALExtendedDataType::Create(GDT_Float64),
7527 176 : &adfTmp[static_cast<size_t>(anStart[0])]))
7528 : {
7529 0 : return false;
7530 : }
7531 :
7532 176 : return IsRegularlySpacedInternal();
7533 : }
7534 :
7535 : /************************************************************************/
7536 : /* GuessGeoTransform() */
7537 : /************************************************************************/
7538 :
7539 : /** Returns whether 2 specified dimensions form a geotransform
7540 : *
7541 : * @param nDimX Index of the X axis.
7542 : * @param nDimY Index of the Y axis.
7543 : * @param bPixelIsPoint Whether the geotransform should be returned
7544 : * with the pixel-is-point (pixel-center) convention
7545 : * (bPixelIsPoint = true), or with the pixel-is-area
7546 : * (top left corner convention)
7547 : * (bPixelIsPoint = false)
7548 : * @param[out] adfGeoTransform Computed geotransform
7549 : * @return true if a geotransform could be computed.
7550 : */
7551 207 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7552 : bool bPixelIsPoint,
7553 : double adfGeoTransform[6]) const
7554 : {
7555 207 : const auto &dims(GetDimensions());
7556 414 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7557 414 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7558 207 : double dfXStart = 0.0;
7559 207 : double dfXSpacing = 0.0;
7560 207 : double dfYStart = 0.0;
7561 207 : double dfYSpacing = 0.0;
7562 477 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7563 270 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7564 317 : poVarY && poVarY->GetDimensionCount() == 1 &&
7565 91 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7566 428 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7567 86 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7568 : {
7569 86 : adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7570 86 : adfGeoTransform[1] = dfXSpacing;
7571 86 : adfGeoTransform[2] = 0;
7572 86 : adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7573 86 : adfGeoTransform[4] = 0;
7574 86 : adfGeoTransform[5] = dfYSpacing;
7575 86 : return true;
7576 : }
7577 121 : return false;
7578 : }
7579 :
7580 : /************************************************************************/
7581 : /* GDALMDArrayResampled */
7582 : /************************************************************************/
7583 :
7584 : class GDALMDArrayResampledDataset;
7585 :
7586 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7587 : {
7588 : protected:
7589 : CPLErr IReadBlock(int, int, void *) override;
7590 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7591 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7592 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7593 : GSpacing nLineSpaceBuf,
7594 : GDALRasterIOExtraArg *psExtraArg) override;
7595 :
7596 : public:
7597 : explicit GDALMDArrayResampledDatasetRasterBand(
7598 : GDALMDArrayResampledDataset *poDSIn);
7599 :
7600 : double GetNoDataValue(int *pbHasNoData) override;
7601 : };
7602 :
7603 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7604 : {
7605 : friend class GDALMDArrayResampled;
7606 : friend class GDALMDArrayResampledDatasetRasterBand;
7607 :
7608 : std::shared_ptr<GDALMDArray> m_poArray;
7609 : const size_t m_iXDim;
7610 : const size_t m_iYDim;
7611 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7612 : bool m_bHasGT = false;
7613 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7614 :
7615 : std::vector<GUInt64> m_anOffset{};
7616 : std::vector<size_t> m_anCount{};
7617 : std::vector<GPtrDiff_t> m_anStride{};
7618 :
7619 : std::string m_osFilenameLong{};
7620 : std::string m_osFilenameLat{};
7621 :
7622 : public:
7623 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7624 : size_t iXDim, size_t iYDim)
7625 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7626 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7627 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7628 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7629 : {
7630 24 : const auto &dims(m_poArray->GetDimensions());
7631 :
7632 24 : nRasterYSize = static_cast<int>(
7633 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7634 24 : nRasterXSize = static_cast<int>(
7635 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7636 :
7637 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7638 24 : m_adfGeoTransform);
7639 :
7640 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7641 24 : }
7642 :
7643 48 : ~GDALMDArrayResampledDataset()
7644 24 : {
7645 24 : if (!m_osFilenameLong.empty())
7646 5 : VSIUnlink(m_osFilenameLong.c_str());
7647 24 : if (!m_osFilenameLat.empty())
7648 5 : VSIUnlink(m_osFilenameLat.c_str());
7649 48 : }
7650 :
7651 43 : CPLErr GetGeoTransform(double *padfGeoTransform) override
7652 : {
7653 43 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7654 43 : return m_bHasGT ? CE_None : CE_Failure;
7655 : }
7656 :
7657 105 : const OGRSpatialReference *GetSpatialRef() const override
7658 : {
7659 105 : m_poSRS = m_poArray->GetSpatialRef();
7660 105 : if (m_poSRS)
7661 : {
7662 79 : m_poSRS.reset(m_poSRS->Clone());
7663 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7664 237 : for (auto &m : axisMapping)
7665 : {
7666 158 : if (m == static_cast<int>(m_iXDim) + 1)
7667 79 : m = 1;
7668 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7669 79 : m = 2;
7670 : }
7671 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7672 : }
7673 105 : return m_poSRS.get();
7674 : }
7675 :
7676 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7677 : const std::string &osFilenameLat)
7678 : {
7679 5 : m_osFilenameLong = osFilenameLong;
7680 5 : m_osFilenameLat = osFilenameLat;
7681 10 : CPLStringList aosGeoLoc;
7682 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7683 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7684 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7685 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7686 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7687 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7688 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7689 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7690 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7691 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7692 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7693 5 : }
7694 : };
7695 :
7696 : /************************************************************************/
7697 : /* GDALMDArrayResampledDatasetRasterBand() */
7698 : /************************************************************************/
7699 :
7700 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7701 24 : GDALMDArrayResampledDataset *poDSIn)
7702 : {
7703 24 : const auto &poArray(poDSIn->m_poArray);
7704 24 : const auto blockSize(poArray->GetBlockSize());
7705 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7706 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7707 13 : blockSize[poDSIn->m_iYDim]))
7708 24 : : 1;
7709 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7710 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7711 13 : blockSize[poDSIn->m_iXDim]))
7712 24 : : poDSIn->GetRasterXSize();
7713 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7714 24 : eAccess = poDSIn->eAccess;
7715 24 : }
7716 :
7717 : /************************************************************************/
7718 : /* GetNoDataValue() */
7719 : /************************************************************************/
7720 :
7721 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7722 : {
7723 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7724 50 : const auto &poArray(l_poDS->m_poArray);
7725 50 : bool bHasNodata = false;
7726 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7727 50 : if (pbHasNoData)
7728 46 : *pbHasNoData = bHasNodata;
7729 50 : return dfRes;
7730 : }
7731 :
7732 : /************************************************************************/
7733 : /* IReadBlock() */
7734 : /************************************************************************/
7735 :
7736 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7737 : int nBlockYOff,
7738 : void *pImage)
7739 : {
7740 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7741 0 : const int nXOff = nBlockXOff * nBlockXSize;
7742 0 : const int nYOff = nBlockYOff * nBlockYSize;
7743 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7744 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7745 : GDALRasterIOExtraArg sExtraArg;
7746 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7747 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7748 : nReqXSize, nReqYSize, eDataType, nDTSize,
7749 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7750 : }
7751 :
7752 : /************************************************************************/
7753 : /* IRasterIO() */
7754 : /************************************************************************/
7755 :
7756 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7757 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7758 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7759 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7760 : GDALRasterIOExtraArg *psExtraArg)
7761 : {
7762 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7763 32 : const auto &poArray(l_poDS->m_poArray);
7764 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7765 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7766 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7767 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7768 : {
7769 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7770 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7771 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7772 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7773 :
7774 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7775 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7776 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7777 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7778 :
7779 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7780 32 : l_poDS->m_anCount.data(), nullptr,
7781 32 : l_poDS->m_anStride.data(),
7782 64 : GDALExtendedDataType::Create(eBufType), pData)
7783 32 : ? CE_None
7784 32 : : CE_Failure;
7785 : }
7786 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7787 : pData, nBufXSize, nBufYSize, eBufType,
7788 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7789 : }
7790 :
7791 : class GDALMDArrayResampled final : public GDALPamMDArray
7792 : {
7793 : private:
7794 : std::shared_ptr<GDALMDArray> m_poParent{};
7795 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7796 : std::vector<GUInt64> m_anBlockSize;
7797 : GDALExtendedDataType m_dt;
7798 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7799 : std::shared_ptr<GDALMDArray> m_poVarX{};
7800 : std::shared_ptr<GDALMDArray> m_poVarY{};
7801 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7802 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7803 :
7804 : protected:
7805 21 : GDALMDArrayResampled(
7806 : const std::shared_ptr<GDALMDArray> &poParent,
7807 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7808 : const std::vector<GUInt64> &anBlockSize)
7809 42 : : GDALAbstractMDArray(std::string(),
7810 42 : "Resampled view of " + poParent->GetFullName()),
7811 : GDALPamMDArray(
7812 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7813 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7814 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7815 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7816 : {
7817 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7818 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7819 21 : }
7820 :
7821 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7822 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7823 : const GDALExtendedDataType &bufferDataType,
7824 : void *pDstBuffer) const override;
7825 :
7826 : public:
7827 : static std::shared_ptr<GDALMDArray>
7828 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7829 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7830 : GDALRIOResampleAlg resampleAlg,
7831 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7832 :
7833 42 : ~GDALMDArrayResampled()
7834 21 : {
7835 : // First close the warped VRT
7836 21 : m_poReprojectedDS.reset();
7837 21 : m_poParentDS.reset();
7838 42 : }
7839 :
7840 11 : bool IsWritable() const override
7841 : {
7842 11 : return false;
7843 : }
7844 :
7845 74 : const std::string &GetFilename() const override
7846 : {
7847 74 : return m_poParent->GetFilename();
7848 : }
7849 :
7850 : const std::vector<std::shared_ptr<GDALDimension>> &
7851 257 : GetDimensions() const override
7852 : {
7853 257 : return m_apoDims;
7854 : }
7855 :
7856 109 : const GDALExtendedDataType &GetDataType() const override
7857 : {
7858 109 : return m_dt;
7859 : }
7860 :
7861 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7862 : {
7863 21 : return m_poSRS;
7864 : }
7865 :
7866 12 : std::vector<GUInt64> GetBlockSize() const override
7867 : {
7868 12 : return m_anBlockSize;
7869 : }
7870 :
7871 : std::shared_ptr<GDALAttribute>
7872 1 : GetAttribute(const std::string &osName) const override
7873 : {
7874 1 : return m_poParent->GetAttribute(osName);
7875 : }
7876 :
7877 : std::vector<std::shared_ptr<GDALAttribute>>
7878 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7879 : {
7880 12 : return m_poParent->GetAttributes(papszOptions);
7881 : }
7882 :
7883 1 : const std::string &GetUnit() const override
7884 : {
7885 1 : return m_poParent->GetUnit();
7886 : }
7887 :
7888 1 : const void *GetRawNoDataValue() const override
7889 : {
7890 1 : return m_poParent->GetRawNoDataValue();
7891 : }
7892 :
7893 1 : double GetOffset(bool *pbHasOffset,
7894 : GDALDataType *peStorageType) const override
7895 : {
7896 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7897 : }
7898 :
7899 1 : double GetScale(bool *pbHasScale,
7900 : GDALDataType *peStorageType) const override
7901 : {
7902 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7903 : }
7904 : };
7905 :
7906 : /************************************************************************/
7907 : /* GDALMDArrayResampled::Create() */
7908 : /************************************************************************/
7909 :
7910 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7911 : const std::shared_ptr<GDALMDArray> &poParent,
7912 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7913 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7914 : CSLConstList /* papszOptions */)
7915 : {
7916 29 : const char *pszResampleAlg = "nearest";
7917 29 : bool unsupported = false;
7918 29 : switch (resampleAlg)
7919 : {
7920 16 : case GRIORA_NearestNeighbour:
7921 16 : pszResampleAlg = "nearest";
7922 16 : break;
7923 2 : case GRIORA_Bilinear:
7924 2 : pszResampleAlg = "bilinear";
7925 2 : break;
7926 5 : case GRIORA_Cubic:
7927 5 : pszResampleAlg = "cubic";
7928 5 : break;
7929 1 : case GRIORA_CubicSpline:
7930 1 : pszResampleAlg = "cubicspline";
7931 1 : break;
7932 1 : case GRIORA_Lanczos:
7933 1 : pszResampleAlg = "lanczos";
7934 1 : break;
7935 1 : case GRIORA_Average:
7936 1 : pszResampleAlg = "average";
7937 1 : break;
7938 1 : case GRIORA_Mode:
7939 1 : pszResampleAlg = "mode";
7940 1 : break;
7941 1 : case GRIORA_Gauss:
7942 1 : unsupported = true;
7943 1 : break;
7944 0 : case GRIORA_RESERVED_START:
7945 0 : unsupported = true;
7946 0 : break;
7947 0 : case GRIORA_RESERVED_END:
7948 0 : unsupported = true;
7949 0 : break;
7950 1 : case GRIORA_RMS:
7951 1 : pszResampleAlg = "rms";
7952 1 : break;
7953 : }
7954 29 : if (unsupported)
7955 : {
7956 1 : CPLError(CE_Failure, CPLE_NotSupported,
7957 : "Unsupported resample method for GetResampled()");
7958 1 : return nullptr;
7959 : }
7960 :
7961 28 : if (poParent->GetDimensionCount() < 2)
7962 : {
7963 1 : CPLError(CE_Failure, CPLE_NotSupported,
7964 : "GetResampled() only supports 2 dimensions or more");
7965 1 : return nullptr;
7966 : }
7967 :
7968 27 : const auto &aoParentDims = poParent->GetDimensions();
7969 27 : if (apoNewDimsIn.size() != aoParentDims.size())
7970 : {
7971 2 : CPLError(CE_Failure, CPLE_AppDefined,
7972 : "GetResampled(): apoNewDims size should be the same as "
7973 : "GetDimensionCount()");
7974 2 : return nullptr;
7975 : }
7976 :
7977 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
7978 25 : apoNewDims.reserve(apoNewDimsIn.size());
7979 :
7980 50 : std::vector<GUInt64> anBlockSize;
7981 25 : anBlockSize.reserve(apoNewDimsIn.size());
7982 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
7983 :
7984 50 : auto apoParentDims = poParent->GetDimensions();
7985 : // Special case for NASA EMIT datasets
7986 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
7987 7 : apoParentDims[0]->GetName() == "downtrack" &&
7988 32 : apoParentDims[1]->GetName() == "crosstrack" &&
7989 2 : apoParentDims[2]->GetName() == "bands");
7990 :
7991 : const size_t iYDimParent =
7992 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
7993 : const size_t iXDimParent =
7994 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
7995 :
7996 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
7997 : {
7998 53 : if (i == iYDimParent || i == iXDimParent)
7999 48 : continue;
8000 5 : if (apoNewDimsIn[i] == nullptr)
8001 : {
8002 3 : apoNewDims.emplace_back(aoParentDims[i]);
8003 : }
8004 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8005 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8006 : {
8007 1 : CPLError(CE_Failure, CPLE_AppDefined,
8008 : "GetResampled(): apoNewDims[%u] should be the same "
8009 : "as its parent",
8010 : i);
8011 1 : return nullptr;
8012 : }
8013 : else
8014 : {
8015 1 : apoNewDims.emplace_back(aoParentDims[i]);
8016 : }
8017 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8018 : }
8019 :
8020 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8021 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8022 :
8023 24 : double dfXStart = 0.0;
8024 24 : double dfXSpacing = 0.0;
8025 24 : bool gotXSpacing = false;
8026 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8027 24 : if (poNewDimX)
8028 : {
8029 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8030 : {
8031 0 : CPLError(CE_Failure, CPLE_NotSupported,
8032 : "Too big size for X dimension");
8033 0 : return nullptr;
8034 : }
8035 4 : auto var = poNewDimX->GetIndexingVariable();
8036 4 : if (var)
8037 : {
8038 2 : if (var->GetDimensionCount() != 1 ||
8039 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8040 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8041 : {
8042 0 : CPLError(CE_Failure, CPLE_NotSupported,
8043 : "New X dimension should be indexed by a regularly "
8044 : "spaced variable");
8045 0 : return nullptr;
8046 : }
8047 1 : gotXSpacing = true;
8048 : }
8049 : }
8050 :
8051 24 : double dfYStart = 0.0;
8052 24 : double dfYSpacing = 0.0;
8053 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8054 24 : bool gotYSpacing = false;
8055 24 : if (poNewDimY)
8056 : {
8057 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8058 : {
8059 0 : CPLError(CE_Failure, CPLE_NotSupported,
8060 : "Too big size for Y dimension");
8061 0 : return nullptr;
8062 : }
8063 4 : auto var = poNewDimY->GetIndexingVariable();
8064 4 : if (var)
8065 : {
8066 2 : if (var->GetDimensionCount() != 1 ||
8067 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8068 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8069 : {
8070 0 : CPLError(CE_Failure, CPLE_NotSupported,
8071 : "New Y dimension should be indexed by a regularly "
8072 : "spaced variable");
8073 0 : return nullptr;
8074 : }
8075 1 : gotYSpacing = true;
8076 : }
8077 : }
8078 :
8079 : // This limitation could probably be removed
8080 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8081 : {
8082 0 : CPLError(CE_Failure, CPLE_NotSupported,
8083 : "Either none of new X or Y dimension should have an indexing "
8084 : "variable, or both should both should have one.");
8085 0 : return nullptr;
8086 : }
8087 :
8088 48 : std::string osDstWKT;
8089 24 : if (poTargetSRS)
8090 : {
8091 2 : char *pszDstWKT = nullptr;
8092 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8093 : {
8094 0 : CPLFree(pszDstWKT);
8095 0 : return nullptr;
8096 : }
8097 2 : osDstWKT = pszDstWKT;
8098 2 : CPLFree(pszDstWKT);
8099 : }
8100 :
8101 : // Use coordinate variables for geolocation array
8102 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8103 24 : bool useGeolocationArray = false;
8104 24 : if (apoCoordinateVars.size() >= 2)
8105 : {
8106 0 : std::shared_ptr<GDALMDArray> poLongVar;
8107 0 : std::shared_ptr<GDALMDArray> poLatVar;
8108 15 : for (const auto &poCoordVar : apoCoordinateVars)
8109 : {
8110 10 : const auto &osName = poCoordVar->GetName();
8111 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8112 20 : std::string osStandardName;
8113 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8114 2 : poAttr->GetDimensionCount() == 0)
8115 : {
8116 2 : const char *pszStandardName = poAttr->ReadAsString();
8117 2 : if (pszStandardName)
8118 2 : osStandardName = pszStandardName;
8119 : }
8120 21 : if (osName == "lon" || osName == "longitude" ||
8121 21 : osName == "Longitude" || osStandardName == "longitude")
8122 : {
8123 5 : poLongVar = poCoordVar;
8124 : }
8125 6 : else if (osName == "lat" || osName == "latitude" ||
8126 6 : osName == "Latitude" || osStandardName == "latitude")
8127 : {
8128 5 : poLatVar = poCoordVar;
8129 : }
8130 : }
8131 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8132 : {
8133 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8134 5 : const auto &longDims = poLongVar->GetDimensions();
8135 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8136 5 : const auto &latDims = poLatVar->GetDimensions();
8137 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8138 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8139 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8140 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8141 : {
8142 : // Geolocation arrays are 1D, and of consistent size with
8143 : // the variable
8144 0 : useGeolocationArray = true;
8145 : }
8146 1 : else if ((longDimCount == 2 ||
8147 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8148 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8149 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8150 1 : (latDimCount == 2 ||
8151 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8152 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8153 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8154 :
8155 : {
8156 : // Geolocation arrays are 2D (or 3D with first dimension of
8157 : // size 1, as found in Sentinel 5P products), and of consistent
8158 : // size with the variable
8159 5 : useGeolocationArray = true;
8160 : }
8161 : else
8162 : {
8163 0 : CPLDebug(
8164 : "GDAL",
8165 : "Longitude and latitude coordinate variables found, "
8166 : "but their characteristics are not compatible of using "
8167 : "them as geolocation arrays");
8168 : }
8169 5 : if (useGeolocationArray)
8170 : {
8171 10 : CPLDebug("GDAL",
8172 : "Setting geolocation array from variables %s and %s",
8173 5 : poLongVar->GetName().c_str(),
8174 5 : poLatVar->GetName().c_str());
8175 : const std::string osFilenameLong =
8176 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8177 : const std::string osFilenameLat =
8178 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8179 : std::unique_ptr<GDALDataset> poTmpLongDS(
8180 : longDimCount == 1
8181 0 : ? poLongVar->AsClassicDataset(0, 0)
8182 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8183 15 : longDimCount - 2));
8184 5 : auto hTIFFLongDS = GDALTranslate(
8185 : osFilenameLong.c_str(),
8186 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8187 : std::unique_ptr<GDALDataset> poTmpLatDS(
8188 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8189 20 : : poLatVar->AsClassicDataset(
8190 15 : latDimCount - 1, latDimCount - 2));
8191 5 : auto hTIFFLatDS = GDALTranslate(
8192 : osFilenameLat.c_str(),
8193 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8194 5 : const bool bError =
8195 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8196 5 : GDALClose(hTIFFLongDS);
8197 5 : GDALClose(hTIFFLatDS);
8198 5 : if (bError)
8199 : {
8200 0 : VSIUnlink(osFilenameLong.c_str());
8201 0 : VSIUnlink(osFilenameLat.c_str());
8202 0 : return nullptr;
8203 : }
8204 :
8205 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8206 : }
8207 : }
8208 : else
8209 : {
8210 0 : CPLDebug("GDAL",
8211 : "Coordinate variables available for %s, but "
8212 : "longitude and/or latitude variables were not identified",
8213 0 : poParent->GetName().c_str());
8214 : }
8215 : }
8216 :
8217 : // Build gdalwarp arguments
8218 48 : CPLStringList aosArgv;
8219 :
8220 24 : aosArgv.AddString("-of");
8221 24 : aosArgv.AddString("VRT");
8222 :
8223 24 : aosArgv.AddString("-r");
8224 24 : aosArgv.AddString(pszResampleAlg);
8225 :
8226 24 : if (!osDstWKT.empty())
8227 : {
8228 2 : aosArgv.AddString("-t_srs");
8229 2 : aosArgv.AddString(osDstWKT.c_str());
8230 : }
8231 :
8232 24 : if (useGeolocationArray)
8233 5 : aosArgv.AddString("-geoloc");
8234 :
8235 24 : if (gotXSpacing && gotYSpacing)
8236 : {
8237 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8238 : const double dfXMax =
8239 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8240 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8241 : const double dfYMin =
8242 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8243 1 : aosArgv.AddString("-te");
8244 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8245 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8246 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8247 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8248 : }
8249 :
8250 24 : if (poNewDimX && poNewDimY)
8251 : {
8252 3 : aosArgv.AddString("-ts");
8253 : aosArgv.AddString(
8254 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8255 : aosArgv.AddString(
8256 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8257 : }
8258 21 : else if (poNewDimX)
8259 : {
8260 1 : aosArgv.AddString("-ts");
8261 : aosArgv.AddString(
8262 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8263 1 : aosArgv.AddString("0");
8264 : }
8265 20 : else if (poNewDimY)
8266 : {
8267 1 : aosArgv.AddString("-ts");
8268 1 : aosArgv.AddString("0");
8269 : aosArgv.AddString(
8270 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8271 : }
8272 :
8273 : // Create a warped VRT dataset
8274 : GDALWarpAppOptions *psOptions =
8275 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8276 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8277 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8278 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8279 24 : GDALWarpAppOptionsFree(psOptions);
8280 24 : if (poReprojectedDS == nullptr)
8281 3 : return nullptr;
8282 :
8283 : int nBlockXSize;
8284 : int nBlockYSize;
8285 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8286 21 : anBlockSize.emplace_back(nBlockYSize);
8287 21 : anBlockSize.emplace_back(nBlockXSize);
8288 :
8289 21 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8290 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8291 21 : CPLAssert(eErr == CE_None);
8292 21 : CPL_IGNORE_RET_VAL(eErr);
8293 :
8294 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8295 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8296 42 : poReprojectedDS->GetRasterYSize());
8297 : auto varY = GDALMDArrayRegularlySpaced::Create(
8298 63 : std::string(), poDimY->GetName(), poDimY,
8299 84 : adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8300 21 : poDimY->SetIndexingVariable(varY);
8301 :
8302 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8303 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8304 42 : poReprojectedDS->GetRasterXSize());
8305 : auto varX = GDALMDArrayRegularlySpaced::Create(
8306 63 : std::string(), poDimX->GetName(), poDimX,
8307 84 : adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8308 21 : poDimX->SetIndexingVariable(varX);
8309 :
8310 21 : apoNewDims.emplace_back(poDimY);
8311 21 : apoNewDims.emplace_back(poDimX);
8312 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8313 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8314 21 : newAr->SetSelf(newAr);
8315 21 : if (poTargetSRS)
8316 : {
8317 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8318 : }
8319 : else
8320 : {
8321 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8322 : }
8323 21 : newAr->m_poVarX = varX;
8324 21 : newAr->m_poVarY = varY;
8325 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8326 21 : newAr->m_poParentDS = std::move(poParentDS);
8327 :
8328 : // If the input array is y,x,band ordered, the above newAr is
8329 : // actually band,y,x ordered as it is more convenient for
8330 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8331 : // array to the order of the input array
8332 21 : if (bYXBandOrder)
8333 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8334 :
8335 19 : return newAr;
8336 : }
8337 :
8338 : /************************************************************************/
8339 : /* GDALMDArrayResampled::IRead() */
8340 : /************************************************************************/
8341 :
8342 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8343 : const size_t *count, const GInt64 *arrayStep,
8344 : const GPtrDiff_t *bufferStride,
8345 : const GDALExtendedDataType &bufferDataType,
8346 : void *pDstBuffer) const
8347 : {
8348 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8349 0 : return false;
8350 :
8351 : struct Stack
8352 : {
8353 : size_t nIters = 0;
8354 : GByte *dst_ptr = nullptr;
8355 : GPtrDiff_t dst_inc_offset = 0;
8356 : };
8357 :
8358 29 : const auto nDims = GetDimensionCount();
8359 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8360 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8361 92 : for (size_t i = 0; i < nDims; i++)
8362 : {
8363 63 : stack[i].dst_inc_offset =
8364 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8365 : }
8366 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8367 :
8368 29 : size_t dimIdx = 0;
8369 29 : const size_t iDimY = nDims - 2;
8370 29 : const size_t iDimX = nDims - 1;
8371 : // Use an array to avoid a false positive warning from CLang Static
8372 : // Analyzer about flushCaches being never read
8373 29 : bool flushCaches[] = {false};
8374 : const bool bYXBandOrder =
8375 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8376 :
8377 38 : lbl_next_depth:
8378 38 : if (dimIdx == iDimY)
8379 : {
8380 33 : if (flushCaches[0])
8381 : {
8382 5 : flushCaches[0] = false;
8383 : // When changing of 2D slice, flush GDAL 2D buffers
8384 5 : m_poParentDS->FlushCache(false);
8385 5 : m_poReprojectedDS->FlushCache(false);
8386 : }
8387 :
8388 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8389 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8390 : arrayStep, bufferStride, bufferDataType,
8391 33 : stack[dimIdx].dst_ptr))
8392 : {
8393 0 : return false;
8394 : }
8395 : }
8396 : else
8397 : {
8398 5 : stack[dimIdx].nIters = count[dimIdx];
8399 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8400 5 : arrayStartIdx[dimIdx])
8401 : {
8402 1 : flushCaches[0] = true;
8403 : }
8404 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8405 5 : arrayStartIdx[dimIdx];
8406 : while (true)
8407 : {
8408 9 : dimIdx++;
8409 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8410 9 : goto lbl_next_depth;
8411 9 : lbl_return_to_caller:
8412 9 : dimIdx--;
8413 9 : if ((--stack[dimIdx].nIters) == 0)
8414 5 : break;
8415 4 : flushCaches[0] = true;
8416 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8417 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8418 : }
8419 : }
8420 38 : if (dimIdx > 0)
8421 9 : goto lbl_return_to_caller;
8422 :
8423 29 : return true;
8424 : }
8425 :
8426 : /************************************************************************/
8427 : /* GetResampled() */
8428 : /************************************************************************/
8429 :
8430 : /** Return an array that is a resampled / reprojected view of the current array
8431 : *
8432 : * This is the same as the C function GDALMDArrayGetResampled().
8433 : *
8434 : * Currently this method can only resample along the last 2 dimensions, unless
8435 : * orthorectifying a NASA EMIT dataset.
8436 : *
8437 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8438 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8439 : *
8440 : * Options available are:
8441 : * <ul>
8442 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8443 : * Can be set to NO to use generic reprojection method.
8444 : * </li>
8445 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8446 : * orthorectification to take into account the value of the
8447 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8448 : * current array along the band dimension are valid.</li>
8449 : * </ul>
8450 : *
8451 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8452 : * apoNewDims[i] can be NULL to let the method automatically
8453 : * determine it.
8454 : * @param resampleAlg Resampling algorithm
8455 : * @param poTargetSRS Target SRS, or nullptr
8456 : * @param papszOptions NULL-terminated list of options, or NULL.
8457 : *
8458 : * @return a new array, that holds a reference to the original one, and thus is
8459 : * a view of it (not a copy), or nullptr in case of error.
8460 : *
8461 : * @since 3.4
8462 : */
8463 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8464 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8465 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8466 : CSLConstList papszOptions) const
8467 : {
8468 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8469 38 : if (!self)
8470 : {
8471 0 : CPLError(CE_Failure, CPLE_AppDefined,
8472 : "Driver implementation issue: m_pSelf not set !");
8473 0 : return nullptr;
8474 : }
8475 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8476 : {
8477 0 : CPLError(CE_Failure, CPLE_AppDefined,
8478 : "GetResampled() only supports numeric data type");
8479 0 : return nullptr;
8480 : }
8481 :
8482 : // Special case for NASA EMIT datasets
8483 76 : auto apoDims = GetDimensions();
8484 36 : if (poTargetSRS == nullptr &&
8485 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8486 20 : apoDims[1]->GetName() == "crosstrack" &&
8487 10 : apoDims[2]->GetName() == "bands" &&
8488 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8489 1 : apoNewDims ==
8490 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8491 30 : apoDims[2]})) ||
8492 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8493 3 : apoDims[1]->GetName() == "crosstrack" &&
8494 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8495 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8496 : "EMIT_ORTHORECTIFICATION", "YES")))
8497 : {
8498 9 : auto poRootGroup = GetRootGroup();
8499 9 : if (poRootGroup)
8500 : {
8501 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8502 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8503 9 : if (poAttrGeotransform &&
8504 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8505 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8506 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8507 9 : poLocationGroup)
8508 : {
8509 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8510 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8511 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8512 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8513 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8514 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8515 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8516 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8517 : {
8518 : return CreateGLTOrthorectified(
8519 : self, poRootGroup, poGLT_X, poGLT_Y,
8520 : /* nGLTIndexOffset = */ -1,
8521 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8522 : }
8523 : }
8524 : }
8525 : }
8526 :
8527 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8528 : "EMIT_ORTHORECTIFICATION", "NO")))
8529 : {
8530 0 : CPLError(CE_Failure, CPLE_AppDefined,
8531 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8532 : "parameters are not compatible with it");
8533 0 : return nullptr;
8534 : }
8535 :
8536 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8537 29 : poTargetSRS, papszOptions);
8538 : }
8539 :
8540 : /************************************************************************/
8541 : /* GDALDatasetFromArray() */
8542 : /************************************************************************/
8543 :
8544 : class GDALDatasetFromArray;
8545 :
8546 : namespace
8547 : {
8548 : struct MetadataItem
8549 : {
8550 : std::shared_ptr<GDALMDArray> poArray{};
8551 : std::string osName{};
8552 : std::string osDefinition{};
8553 : bool bDefinitionUsesPctForG = false;
8554 : };
8555 : } // namespace
8556 :
8557 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8558 : {
8559 : std::vector<GUInt64> m_anOffset{};
8560 : std::vector<size_t> m_anCount{};
8561 : std::vector<GPtrDiff_t> m_anStride{};
8562 :
8563 : protected:
8564 : CPLErr IReadBlock(int, int, void *) override;
8565 : CPLErr IWriteBlock(int, int, void *) override;
8566 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8567 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8568 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8569 : GSpacing nLineSpaceBuf,
8570 : GDALRasterIOExtraArg *psExtraArg) override;
8571 :
8572 : public:
8573 : explicit GDALRasterBandFromArray(
8574 : GDALDatasetFromArray *poDSIn,
8575 : const std::vector<GUInt64> &anOtherDimCoord,
8576 : const std::vector<std::vector<MetadataItem>>
8577 : &aoBandParameterMetadataItems,
8578 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8579 :
8580 : double GetNoDataValue(int *pbHasNoData) override;
8581 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8582 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8583 : double GetOffset(int *pbHasOffset) override;
8584 : double GetScale(int *pbHasScale) override;
8585 : const char *GetUnitType() override;
8586 : GDALColorInterp GetColorInterpretation() override;
8587 : };
8588 :
8589 : class GDALDatasetFromArray final : public GDALPamDataset
8590 : {
8591 : friend class GDALRasterBandFromArray;
8592 :
8593 : std::shared_ptr<GDALMDArray> m_poArray;
8594 : size_t m_iXDim;
8595 : size_t m_iYDim;
8596 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8597 : bool m_bHasGT = false;
8598 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8599 : GDALMultiDomainMetadata m_oMDD{};
8600 : std::string m_osOvrFilename{};
8601 :
8602 : public:
8603 183 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8604 : size_t iXDim, size_t iYDim)
8605 183 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8606 : {
8607 : // Initialize an overview filename from the filename of the array
8608 : // and its name.
8609 183 : const std::string &osFilename = m_poArray->GetFilename();
8610 183 : if (!osFilename.empty())
8611 : {
8612 165 : m_osOvrFilename = osFilename;
8613 165 : m_osOvrFilename += '.';
8614 6327 : for (char ch : m_poArray->GetName())
8615 : {
8616 6162 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8617 5463 : (ch >= 'a' && ch <= 'z') || ch == '_')
8618 : {
8619 4925 : m_osOvrFilename += ch;
8620 : }
8621 : else
8622 : {
8623 1237 : m_osOvrFilename += '_';
8624 : }
8625 : }
8626 165 : m_osOvrFilename += ".ovr";
8627 165 : oOvManager.Initialize(this);
8628 : }
8629 183 : }
8630 :
8631 : static GDALDatasetFromArray *
8632 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8633 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8634 : CSLConstList papszOptions);
8635 :
8636 366 : ~GDALDatasetFromArray()
8637 183 : {
8638 183 : GDALDatasetFromArray::Close();
8639 366 : }
8640 :
8641 299 : CPLErr Close() override
8642 : {
8643 299 : CPLErr eErr = CE_None;
8644 299 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8645 : {
8646 299 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8647 : CE_None)
8648 0 : eErr = CE_Failure;
8649 299 : m_poArray.reset();
8650 : }
8651 299 : return eErr;
8652 : }
8653 :
8654 49 : CPLErr GetGeoTransform(double *padfGeoTransform) override
8655 : {
8656 49 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8657 49 : return m_bHasGT ? CE_None : CE_Failure;
8658 : }
8659 :
8660 53 : const OGRSpatialReference *GetSpatialRef() const override
8661 : {
8662 53 : if (m_poArray->GetDimensionCount() < 2)
8663 3 : return nullptr;
8664 50 : m_poSRS = m_poArray->GetSpatialRef();
8665 50 : if (m_poSRS)
8666 : {
8667 16 : m_poSRS.reset(m_poSRS->Clone());
8668 32 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8669 48 : for (auto &m : axisMapping)
8670 : {
8671 32 : if (m == static_cast<int>(m_iXDim) + 1)
8672 16 : m = 1;
8673 16 : else if (m == static_cast<int>(m_iYDim) + 1)
8674 16 : m = 2;
8675 : }
8676 16 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8677 : }
8678 50 : return m_poSRS.get();
8679 : }
8680 :
8681 4 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8682 : {
8683 4 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8684 : }
8685 :
8686 144 : char **GetMetadata(const char *pszDomain) override
8687 : {
8688 144 : return m_oMDD.GetMetadata(pszDomain);
8689 : }
8690 :
8691 204 : const char *GetMetadataItem(const char *pszName,
8692 : const char *pszDomain) override
8693 : {
8694 378 : if (!m_osOvrFilename.empty() && pszName &&
8695 390 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8696 12 : EQUAL(pszDomain, "OVERVIEWS"))
8697 : {
8698 12 : return m_osOvrFilename.c_str();
8699 : }
8700 192 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8701 : }
8702 : };
8703 :
8704 : /************************************************************************/
8705 : /* GDALRasterBandFromArray() */
8706 : /************************************************************************/
8707 :
8708 243 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8709 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8710 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8711 243 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8712 : {
8713 243 : const auto &poArray(poDSIn->m_poArray);
8714 243 : const auto &dims(poArray->GetDimensions());
8715 243 : const auto nDimCount(dims.size());
8716 486 : const auto blockSize(poArray->GetBlockSize());
8717 232 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8718 475 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8719 128 : blockSize[poDSIn->m_iYDim]))
8720 : : 1;
8721 243 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8722 139 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8723 139 : blockSize[poDSIn->m_iXDim]))
8724 243 : : poDSIn->GetRasterXSize();
8725 243 : eDataType = poArray->GetDataType().GetNumericDataType();
8726 243 : eAccess = poDSIn->eAccess;
8727 243 : m_anOffset.resize(nDimCount);
8728 243 : m_anCount.resize(nDimCount, 1);
8729 243 : m_anStride.resize(nDimCount);
8730 822 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8731 : {
8732 579 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8733 : {
8734 208 : std::string dimName(dims[i]->GetName());
8735 104 : GUInt64 nIndex = anOtherDimCoord[j];
8736 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8737 : // subsetted dimensions as generated by GetView()
8738 104 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8739 : {
8740 : CPLStringList aosTokens(
8741 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8742 6 : if (aosTokens.size() == 5)
8743 : {
8744 6 : dimName = aosTokens[1];
8745 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8746 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8747 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8748 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8749 0 : : nStartDim - (nIndex * -nIncrDim);
8750 : }
8751 : }
8752 104 : if (nDimCount != 3 || dimName != "Band")
8753 : {
8754 48 : SetMetadataItem(
8755 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8756 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8757 : }
8758 :
8759 104 : auto indexingVar = dims[i]->GetIndexingVariable();
8760 :
8761 : // If the indexing variable is also listed in band parameter arrays,
8762 : // then don't use our default formatting
8763 104 : if (indexingVar)
8764 : {
8765 38 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8766 : {
8767 12 : if (oItem.poArray->GetFullName() ==
8768 12 : indexingVar->GetFullName())
8769 : {
8770 12 : indexingVar.reset();
8771 12 : break;
8772 : }
8773 : }
8774 : }
8775 :
8776 130 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8777 26 : indexingVar->GetDimensions()[0]->GetSize() ==
8778 26 : dims[i]->GetSize())
8779 : {
8780 26 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8781 : {
8782 0 : if (!bHasWarned)
8783 : {
8784 0 : CPLError(
8785 : CE_Warning, CPLE_AppDefined,
8786 : "Maximum delay to load band metadata from "
8787 : "dimension indexing variables has expired. "
8788 : "Increase the value of the "
8789 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8790 : "option of GDALMDArray::AsClassicDataset() "
8791 : "(also accessible as the "
8792 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8793 : "configuration option), "
8794 : "or set it to 'unlimited' for unlimited delay. ");
8795 0 : bHasWarned = true;
8796 : }
8797 : }
8798 : else
8799 : {
8800 26 : size_t nCount = 1;
8801 26 : const auto &dt(indexingVar->GetDataType());
8802 52 : std::vector<GByte> abyTmp(dt.GetSize());
8803 52 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8804 26 : nullptr, nullptr, dt, &abyTmp[0]))
8805 : {
8806 26 : char *pszTmp = nullptr;
8807 26 : GDALExtendedDataType::CopyValue(
8808 26 : &abyTmp[0], dt, &pszTmp,
8809 52 : GDALExtendedDataType::CreateString());
8810 26 : if (pszTmp)
8811 : {
8812 26 : SetMetadataItem(
8813 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8814 : pszTmp);
8815 26 : CPLFree(pszTmp);
8816 : }
8817 :
8818 26 : const auto &unit(indexingVar->GetUnit());
8819 26 : if (!unit.empty())
8820 : {
8821 12 : SetMetadataItem(
8822 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8823 : unit.c_str());
8824 : }
8825 : }
8826 : }
8827 : }
8828 :
8829 120 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8830 : {
8831 32 : CPLString osVal;
8832 :
8833 16 : size_t nCount = 1;
8834 16 : const auto &dt(oItem.poArray->GetDataType());
8835 16 : if (oItem.bDefinitionUsesPctForG)
8836 : {
8837 : // There is one and only one %[x][.y]f|g in osDefinition
8838 12 : std::vector<GByte> abyTmp(dt.GetSize());
8839 12 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8840 6 : nullptr, nullptr, dt, &abyTmp[0]))
8841 : {
8842 6 : double dfVal = 0;
8843 6 : GDALExtendedDataType::CopyValue(
8844 6 : &abyTmp[0], dt, &dfVal,
8845 12 : GDALExtendedDataType::Create(GDT_Float64));
8846 6 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8847 : }
8848 : }
8849 : else
8850 : {
8851 : // There should be zero or one %s in osDefinition
8852 10 : char *pszValue = nullptr;
8853 10 : if (dt.GetClass() == GEDTC_STRING)
8854 : {
8855 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8856 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8857 : dt, &pszValue));
8858 : }
8859 : else
8860 : {
8861 16 : std::vector<GByte> abyTmp(dt.GetSize());
8862 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8863 : nullptr, nullptr, dt,
8864 8 : &abyTmp[0]))
8865 : {
8866 8 : GDALExtendedDataType::CopyValue(
8867 8 : &abyTmp[0], dt, &pszValue,
8868 16 : GDALExtendedDataType::CreateString());
8869 : }
8870 : }
8871 :
8872 10 : if (pszValue)
8873 : {
8874 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8875 10 : CPLFree(pszValue);
8876 : }
8877 : }
8878 16 : if (!osVal.empty())
8879 16 : SetMetadataItem(oItem.osName.c_str(), osVal);
8880 : }
8881 :
8882 104 : m_anOffset[i] = anOtherDimCoord[j];
8883 104 : j++;
8884 : }
8885 : }
8886 243 : }
8887 :
8888 : /************************************************************************/
8889 : /* GetNoDataValue() */
8890 : /************************************************************************/
8891 :
8892 93 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
8893 : {
8894 93 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8895 93 : const auto &poArray(l_poDS->m_poArray);
8896 93 : bool bHasNodata = false;
8897 93 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
8898 93 : if (pbHasNoData)
8899 81 : *pbHasNoData = bHasNodata;
8900 93 : return res;
8901 : }
8902 :
8903 : /************************************************************************/
8904 : /* GetNoDataValueAsInt64() */
8905 : /************************************************************************/
8906 :
8907 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
8908 : {
8909 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8910 1 : const auto &poArray(l_poDS->m_poArray);
8911 1 : bool bHasNodata = false;
8912 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
8913 1 : if (pbHasNoData)
8914 1 : *pbHasNoData = bHasNodata;
8915 1 : return nodata;
8916 : }
8917 :
8918 : /************************************************************************/
8919 : /* GetNoDataValueAsUInt64() */
8920 : /************************************************************************/
8921 :
8922 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
8923 : {
8924 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8925 1 : const auto &poArray(l_poDS->m_poArray);
8926 1 : bool bHasNodata = false;
8927 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
8928 1 : if (pbHasNoData)
8929 1 : *pbHasNoData = bHasNodata;
8930 1 : return nodata;
8931 : }
8932 :
8933 : /************************************************************************/
8934 : /* GetOffset() */
8935 : /************************************************************************/
8936 :
8937 29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
8938 : {
8939 29 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8940 29 : const auto &poArray(l_poDS->m_poArray);
8941 29 : bool bHasValue = false;
8942 29 : double dfRes = poArray->GetOffset(&bHasValue);
8943 29 : if (pbHasOffset)
8944 17 : *pbHasOffset = bHasValue;
8945 29 : return dfRes;
8946 : }
8947 :
8948 : /************************************************************************/
8949 : /* GetUnitType() */
8950 : /************************************************************************/
8951 :
8952 36 : const char *GDALRasterBandFromArray::GetUnitType()
8953 : {
8954 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8955 36 : const auto &poArray(l_poDS->m_poArray);
8956 36 : return poArray->GetUnit().c_str();
8957 : }
8958 :
8959 : /************************************************************************/
8960 : /* GetScale() */
8961 : /************************************************************************/
8962 :
8963 27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
8964 : {
8965 27 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8966 27 : const auto &poArray(l_poDS->m_poArray);
8967 27 : bool bHasValue = false;
8968 27 : double dfRes = poArray->GetScale(&bHasValue);
8969 27 : if (pbHasScale)
8970 15 : *pbHasScale = bHasValue;
8971 27 : return dfRes;
8972 : }
8973 :
8974 : /************************************************************************/
8975 : /* IReadBlock() */
8976 : /************************************************************************/
8977 :
8978 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
8979 : void *pImage)
8980 : {
8981 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
8982 68 : const int nXOff = nBlockXOff * nBlockXSize;
8983 68 : const int nYOff = nBlockYOff * nBlockYSize;
8984 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
8985 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
8986 : GDALRasterIOExtraArg sExtraArg;
8987 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
8988 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
8989 : nReqXSize, nReqYSize, eDataType, nDTSize,
8990 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
8991 : }
8992 :
8993 : /************************************************************************/
8994 : /* IWriteBlock() */
8995 : /************************************************************************/
8996 :
8997 0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
8998 : void *pImage)
8999 : {
9000 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9001 0 : const int nXOff = nBlockXOff * nBlockXSize;
9002 0 : const int nYOff = nBlockYOff * nBlockYSize;
9003 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9004 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9005 : GDALRasterIOExtraArg sExtraArg;
9006 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9007 0 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9008 : nReqXSize, nReqYSize, eDataType, nDTSize,
9009 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9010 : }
9011 :
9012 : /************************************************************************/
9013 : /* IRasterIO() */
9014 : /************************************************************************/
9015 :
9016 319 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9017 : int nYOff, int nXSize, int nYSize,
9018 : void *pData, int nBufXSize,
9019 : int nBufYSize, GDALDataType eBufType,
9020 : GSpacing nPixelSpaceBuf,
9021 : GSpacing nLineSpaceBuf,
9022 : GDALRasterIOExtraArg *psExtraArg)
9023 : {
9024 319 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9025 319 : const auto &poArray(l_poDS->m_poArray);
9026 319 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9027 319 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9028 319 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9029 319 : (nLineSpaceBuf % nBufferDTSize) == 0)
9030 : {
9031 319 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9032 319 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9033 638 : m_anStride[l_poDS->m_iXDim] =
9034 319 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9035 319 : if (poArray->GetDimensionCount() >= 2)
9036 : {
9037 310 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9038 310 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9039 310 : m_anStride[l_poDS->m_iYDim] =
9040 310 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9041 : }
9042 319 : if (eRWFlag == GF_Read)
9043 : {
9044 630 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9045 315 : m_anStride.data(),
9046 630 : GDALExtendedDataType::Create(eBufType), pData)
9047 315 : ? CE_None
9048 315 : : CE_Failure;
9049 : }
9050 : else
9051 : {
9052 8 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9053 4 : m_anStride.data(),
9054 8 : GDALExtendedDataType::Create(eBufType), pData)
9055 4 : ? CE_None
9056 4 : : CE_Failure;
9057 : }
9058 : }
9059 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9060 : pData, nBufXSize, nBufYSize, eBufType,
9061 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9062 : }
9063 :
9064 : /************************************************************************/
9065 : /* GetColorInterpretation() */
9066 : /************************************************************************/
9067 :
9068 45 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9069 : {
9070 45 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9071 45 : const auto &poArray(l_poDS->m_poArray);
9072 135 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9073 45 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9074 : {
9075 6 : bool bOK = false;
9076 6 : GUInt64 nStartIndex = 0;
9077 6 : if (poArray->GetDimensionCount() == 2 &&
9078 0 : poAttr->GetDimensionCount() == 0)
9079 : {
9080 0 : bOK = true;
9081 : }
9082 6 : else if (poArray->GetDimensionCount() == 3)
9083 : {
9084 6 : uint64_t nExtraDimSamples = 1;
9085 6 : const auto &apoDims = poArray->GetDimensions();
9086 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9087 : {
9088 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9089 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9090 : }
9091 6 : if (poAttr->GetDimensionsSize() ==
9092 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9093 : {
9094 6 : bOK = true;
9095 : }
9096 6 : nStartIndex = nBand - 1;
9097 : }
9098 6 : if (bOK)
9099 : {
9100 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9101 6 : const size_t nCount = 1;
9102 6 : const GInt64 arrayStep = 1;
9103 6 : const GPtrDiff_t bufferStride = 1;
9104 6 : char *pszValue = nullptr;
9105 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9106 6 : oStringDT, &pszValue);
9107 6 : if (pszValue)
9108 : {
9109 : const auto eColorInterp =
9110 6 : GDALGetColorInterpretationByName(pszValue);
9111 6 : CPLFree(pszValue);
9112 6 : return eColorInterp;
9113 : }
9114 : }
9115 : }
9116 39 : return GCI_Undefined;
9117 : }
9118 :
9119 : /************************************************************************/
9120 : /* GDALDatasetFromArray::Create() */
9121 : /************************************************************************/
9122 :
9123 206 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9124 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9125 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9126 :
9127 : {
9128 206 : const auto nDimCount(array->GetDimensionCount());
9129 206 : if (nDimCount == 0)
9130 : {
9131 1 : CPLError(CE_Failure, CPLE_NotSupported,
9132 : "Unsupported number of dimensions");
9133 1 : return nullptr;
9134 : }
9135 409 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9136 204 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9137 : {
9138 1 : CPLError(CE_Failure, CPLE_NotSupported,
9139 : "Only arrays with numeric data types "
9140 : "can be exposed as classic GDALDataset");
9141 1 : return nullptr;
9142 : }
9143 204 : if (iXDim >= nDimCount ||
9144 190 : (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
9145 : {
9146 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9147 6 : return nullptr;
9148 : }
9149 198 : GUInt64 nTotalBands = 1;
9150 198 : const auto &dims(array->GetDimensions());
9151 634 : for (size_t i = 0; i < nDimCount; ++i)
9152 : {
9153 437 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9154 : {
9155 54 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9156 : {
9157 1 : CPLError(CE_Failure, CPLE_AppDefined,
9158 : "Too many bands. Operate on a sliced view");
9159 1 : return nullptr;
9160 : }
9161 53 : nTotalBands *= dims[i]->GetSize();
9162 : }
9163 : }
9164 :
9165 : const char *pszBandMetadata =
9166 197 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9167 197 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9168 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9169 394 : nNewDimCount);
9170 :
9171 197 : if (pszBandMetadata)
9172 : {
9173 21 : if (!poRootGroup)
9174 : {
9175 1 : CPLError(CE_Failure, CPLE_AppDefined,
9176 : "Root group should be provided when BAND_METADATA is set");
9177 14 : return nullptr;
9178 : }
9179 20 : CPLJSONDocument oDoc;
9180 20 : if (!oDoc.LoadMemory(pszBandMetadata))
9181 : {
9182 1 : CPLError(CE_Failure, CPLE_AppDefined,
9183 : "Invalid JSON content for BAND_METADATA");
9184 1 : return nullptr;
9185 : }
9186 19 : auto oRoot = oDoc.GetRoot();
9187 19 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9188 : {
9189 1 : CPLError(CE_Failure, CPLE_AppDefined,
9190 : "Value of BAND_METADATA should be an array");
9191 1 : return nullptr;
9192 : }
9193 :
9194 18 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9195 72 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9196 : {
9197 54 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9198 : {
9199 18 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9200 18 : ++j;
9201 : }
9202 : }
9203 :
9204 18 : auto oArray = oRoot.ToArray();
9205 26 : for (int j = 0; j < oArray.Size(); ++j)
9206 : {
9207 19 : const auto oJsonItem = oArray[j];
9208 19 : MetadataItem oItem;
9209 :
9210 38 : auto osBandArrayFullname = oJsonItem.GetString("array");
9211 19 : if (osBandArrayFullname.empty())
9212 : {
9213 1 : CPLError(CE_Failure, CPLE_AppDefined,
9214 : "BAND_METADATA[%d][\"array\"] is missing", j);
9215 1 : return nullptr;
9216 : }
9217 : oItem.poArray =
9218 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9219 18 : if (!oItem.poArray)
9220 : {
9221 1 : CPLError(CE_Failure, CPLE_AppDefined,
9222 : "Array %s cannot be found",
9223 : osBandArrayFullname.c_str());
9224 1 : return nullptr;
9225 : }
9226 17 : if (oItem.poArray->GetDimensionCount() != 1)
9227 : {
9228 1 : CPLError(CE_Failure, CPLE_AppDefined,
9229 : "Array %s is not a 1D array",
9230 : osBandArrayFullname.c_str());
9231 1 : return nullptr;
9232 : }
9233 : const auto &osAuxArrayDimName =
9234 16 : oItem.poArray->GetDimensions()[0]->GetName();
9235 16 : auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9236 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9237 : {
9238 1 : CPLError(CE_Failure, CPLE_AppDefined,
9239 : "Dimension %s of array %s is not a non-X/Y dimension "
9240 : "of array %s",
9241 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9242 1 : array->GetName().c_str());
9243 1 : return nullptr;
9244 : }
9245 15 : const size_t iExtraDimIdx = oIter->second;
9246 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9247 :
9248 15 : oItem.osName = oJsonItem.GetString("item_name");
9249 15 : if (oItem.osName.empty())
9250 : {
9251 1 : CPLError(CE_Failure, CPLE_AppDefined,
9252 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9253 1 : return nullptr;
9254 : }
9255 :
9256 28 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9257 :
9258 : // Check correctness of definition
9259 14 : bool bFirstNumericFormatter = true;
9260 14 : std::string osModDefinition;
9261 14 : bool bDefinitionUsesPctForG = false;
9262 72 : for (size_t k = 0; k < osDefinition.size(); ++k)
9263 : {
9264 64 : if (osDefinition[k] == '%')
9265 : {
9266 13 : osModDefinition += osDefinition[k];
9267 13 : if (k + 1 == osDefinition.size())
9268 : {
9269 1 : CPLError(CE_Failure, CPLE_AppDefined,
9270 : "Value of "
9271 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9272 : "%s is invalid at offset %d",
9273 : osAuxArrayDimName.c_str(), j,
9274 : osDefinition.c_str(), int(k));
9275 1 : return nullptr;
9276 : }
9277 12 : ++k;
9278 12 : if (osDefinition[k] == '%')
9279 : {
9280 1 : osModDefinition += osDefinition[k];
9281 1 : continue;
9282 : }
9283 11 : if (!bFirstNumericFormatter)
9284 : {
9285 1 : CPLError(
9286 : CE_Failure, CPLE_AppDefined,
9287 : "Value of "
9288 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
9289 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9290 : "formatters should be specified at most once",
9291 : osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
9292 : int(k));
9293 1 : return nullptr;
9294 : }
9295 10 : bFirstNumericFormatter = false;
9296 13 : for (; k < osDefinition.size(); ++k)
9297 : {
9298 13 : osModDefinition += osDefinition[k];
9299 26 : if (!((osDefinition[k] >= '0' &&
9300 12 : osDefinition[k] <= '9') ||
9301 11 : osDefinition[k] == '.'))
9302 10 : break;
9303 : }
9304 20 : if (k == osDefinition.size() ||
9305 10 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9306 5 : osDefinition[k] != 's'))
9307 : {
9308 1 : CPLError(CE_Failure, CPLE_AppDefined,
9309 : "Value of "
9310 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9311 : "%s is invalid at offset %d: only "
9312 : "%%[x][.y]f|g or %%s formatters are accepted",
9313 : osAuxArrayDimName.c_str(), j,
9314 : osDefinition.c_str(), int(k));
9315 1 : return nullptr;
9316 : }
9317 9 : bDefinitionUsesPctForG =
9318 9 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9319 9 : if (bDefinitionUsesPctForG)
9320 : {
9321 5 : if (oItem.poArray->GetDataType().GetClass() !=
9322 : GEDTC_NUMERIC)
9323 : {
9324 1 : CPLError(CE_Failure, CPLE_AppDefined,
9325 : "Data type of %s array is not numeric",
9326 : osAuxArrayDimName.c_str());
9327 1 : return nullptr;
9328 : }
9329 : }
9330 : }
9331 56 : else if (osDefinition[k] == '$' &&
9332 56 : k + 1 < osDefinition.size() &&
9333 5 : osDefinition[k + 1] == '{')
9334 : {
9335 5 : const auto nPos = osDefinition.find('}', k);
9336 5 : if (nPos == std::string::npos)
9337 : {
9338 1 : CPLError(CE_Failure, CPLE_AppDefined,
9339 : "Value of "
9340 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9341 : "%s is invalid at offset %d",
9342 : osAuxArrayDimName.c_str(), j,
9343 : osDefinition.c_str(), int(k));
9344 2 : return nullptr;
9345 : }
9346 : const auto osAttrName =
9347 4 : osDefinition.substr(k + 2, nPos - (k + 2));
9348 4 : auto poAttr = oItem.poArray->GetAttribute(osAttrName);
9349 4 : if (!poAttr)
9350 : {
9351 1 : CPLError(CE_Failure, CPLE_AppDefined,
9352 : "Value of "
9353 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9354 : "%s is invalid: %s is not an attribute of %s",
9355 : osAuxArrayDimName.c_str(), j,
9356 : osDefinition.c_str(), osAttrName.c_str(),
9357 : osAuxArrayDimName.c_str());
9358 1 : return nullptr;
9359 : }
9360 3 : k = nPos;
9361 3 : const char *pszValue = poAttr->ReadAsString();
9362 3 : if (!pszValue)
9363 : {
9364 0 : CPLError(CE_Failure, CPLE_AppDefined,
9365 : "Cannot get value of attribute %s of %s as a "
9366 : "string",
9367 : osAttrName.c_str(), osAuxArrayDimName.c_str());
9368 0 : return nullptr;
9369 : }
9370 3 : osModDefinition += pszValue;
9371 : }
9372 : else
9373 : {
9374 46 : osModDefinition += osDefinition[k];
9375 : }
9376 : }
9377 :
9378 8 : oItem.osDefinition = std::move(osModDefinition);
9379 8 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9380 :
9381 8 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9382 8 : std::move(oItem));
9383 : }
9384 : }
9385 :
9386 366 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9387 :
9388 183 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9389 :
9390 183 : poDS->nRasterYSize =
9391 183 : nDimCount < 2 ? 1
9392 172 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9393 172 : dims[iYDim]->GetSize()));
9394 366 : poDS->nRasterXSize = static_cast<int>(
9395 183 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9396 :
9397 366 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9398 366 : std::vector<GUInt64> anStackIters(nDimCount);
9399 366 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9400 577 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9401 : {
9402 394 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9403 : {
9404 39 : anMapNewToOld[j] = i;
9405 39 : j++;
9406 : }
9407 : }
9408 :
9409 366 : poDS->m_bHasGT =
9410 183 : array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9411 :
9412 366 : const auto attrs(array->GetAttributes());
9413 261 : for (const auto &attr : attrs)
9414 : {
9415 78 : if (attr->GetName() != "COLOR_INTERPRETATION")
9416 : {
9417 144 : auto stringArray = attr->ReadAsStringArray();
9418 144 : std::string val;
9419 72 : if (stringArray.size() > 1)
9420 : {
9421 22 : val += '{';
9422 : }
9423 166 : for (int i = 0; i < stringArray.size(); ++i)
9424 : {
9425 94 : if (i > 0)
9426 22 : val += ',';
9427 94 : val += stringArray[i];
9428 : }
9429 72 : if (stringArray.size() > 1)
9430 : {
9431 22 : val += '}';
9432 : }
9433 72 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9434 : }
9435 : }
9436 :
9437 183 : const char *pszDelay = CSLFetchNameValueDef(
9438 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9439 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9440 : const double dfDelay =
9441 183 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9442 183 : const auto nStartTime = time(nullptr);
9443 183 : bool bHasWarned = false;
9444 : // Instantiate bands by iterating over non-XY variables
9445 183 : size_t iDim = 0;
9446 183 : int nCurBand = 1;
9447 284 : lbl_next_depth:
9448 284 : if (iDim < nNewDimCount)
9449 : {
9450 41 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9451 41 : anOtherDimCoord[iDim] = 0;
9452 : while (true)
9453 : {
9454 101 : ++iDim;
9455 101 : goto lbl_next_depth;
9456 101 : lbl_return_to_caller:
9457 101 : --iDim;
9458 101 : --anStackIters[iDim];
9459 101 : if (anStackIters[iDim] == 0)
9460 41 : break;
9461 60 : ++anOtherDimCoord[iDim];
9462 : }
9463 : }
9464 : else
9465 : {
9466 486 : poDS->SetBand(nCurBand, new GDALRasterBandFromArray(
9467 243 : poDS.get(), anOtherDimCoord,
9468 : aoBandParameterMetadataItems, dfDelay,
9469 243 : nStartTime, bHasWarned));
9470 243 : ++nCurBand;
9471 : }
9472 284 : if (iDim > 0)
9473 101 : goto lbl_return_to_caller;
9474 :
9475 183 : if (!array->GetFilename().empty())
9476 : {
9477 165 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9478 : std::string osDerivedDatasetName(
9479 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9480 330 : int(iYDim), array->GetFullName().c_str()));
9481 165 : if (!array->GetContext().empty())
9482 : {
9483 2 : osDerivedDatasetName += " with context ";
9484 2 : osDerivedDatasetName += array->GetContext();
9485 : }
9486 165 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9487 165 : poDS->TryLoadXML();
9488 :
9489 2 : for (const auto &[pszKey, pszValue] :
9490 : cpl::IterateNameValue(static_cast<CSLConstList>(
9491 167 : poDS->GDALPamDataset::GetMetadata())))
9492 : {
9493 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9494 : }
9495 : }
9496 :
9497 183 : return poDS.release();
9498 : }
9499 :
9500 : /************************************************************************/
9501 : /* AsClassicDataset() */
9502 : /************************************************************************/
9503 :
9504 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9505 : *
9506 : * In the case of > 2D arrays, additional dimensions will be represented as
9507 : * raster bands.
9508 : *
9509 : * The "reverse" method is GDALRasterBand::AsMDArray().
9510 : *
9511 : * This is the same as the C function GDALMDArrayAsClassicDataset().
9512 : *
9513 : * @param iXDim Index of the dimension that will be used as the X/width axis.
9514 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
9515 : * Ignored if the dimension count is 1.
9516 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9517 : * option.
9518 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9519 : * nullptr. Current supported options are:
9520 : * <ul>
9521 : * <li>BAND_METADATA: JSON serialized array defining which
9522 : * arrays of the poRootGroup, indexed by non-X and Y
9523 : * dimensions, should be mapped as band metadata items.
9524 : * Each array item should be an object with the
9525 : * following members:
9526 : * - "array": full name of a band parameter array.
9527 : * Such array must be a one
9528 : * dimensional array, and its dimension must be one of
9529 : * the dimensions of the array on which the method is
9530 : * called (excluding the X and Y dimensons).
9531 : * - "item_name": band metadata item name
9532 : * - "item_value": (optional) String, where "%[x][.y]f",
9533 : * "%[x][.y]g" or "%s" printf-like formatting can be
9534 : * used to format the corresponding value of the
9535 : * parameter array. The percentage character should be
9536 : * repeated: "%%"
9537 : * "${attribute_name}" can also be used to include the
9538 : * value of an attribute for the array.
9539 : * If "item_value" is not provided, a default formatting
9540 : * of the value will be applied.
9541 : *
9542 : * Example:
9543 : * [
9544 : * {
9545 : * "array": "/sensor_band_parameters/wavelengths",
9546 : * "item_name": "WAVELENGTH",
9547 : * "item_value": "%.1f ${units}"
9548 : * },
9549 : * {
9550 : * "array": "/sensor_band_parameters/fwhm",
9551 : * "item_name": "FWHM"
9552 : * },
9553 : * {
9554 : * "array": "/sensor_band_parameters/fwhm",
9555 : * "item_name": "FWHM_UNIT",
9556 : * "item_value": "${units}"
9557 : * }
9558 : * ]
9559 : * </li>
9560 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
9561 : * seconds allowed to set the DIM_{dimname}_VALUE band
9562 : * metadata items from the indexing variable of the
9563 : * dimensions.
9564 : * Default value is 5. 'unlimited' can be used to mean
9565 : * unlimited delay. Can also be defined globally with
9566 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
9567 : * option.</li>
9568 : * </ul>
9569 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9570 : */
9571 : GDALDataset *
9572 206 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
9573 : const std::shared_ptr<GDALGroup> &poRootGroup,
9574 : CSLConstList papszOptions) const
9575 : {
9576 412 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
9577 206 : if (!self)
9578 : {
9579 0 : CPLError(CE_Failure, CPLE_AppDefined,
9580 : "Driver implementation issue: m_pSelf not set !");
9581 0 : return nullptr;
9582 : }
9583 206 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
9584 206 : papszOptions);
9585 : }
9586 :
9587 : /************************************************************************/
9588 : /* GetStatistics() */
9589 : /************************************************************************/
9590 :
9591 : /**
9592 : * \brief Fetch statistics.
9593 : *
9594 : * Returns the minimum, maximum, mean and standard deviation of all
9595 : * pixel values in this array.
9596 : *
9597 : * If bForce is FALSE results will only be returned if it can be done
9598 : * quickly (i.e. without scanning the data). If bForce is FALSE and
9599 : * results cannot be returned efficiently, the method will return CE_Warning
9600 : * but no warning will have been issued. This is a non-standard use of
9601 : * the CE_Warning return value to indicate "nothing done".
9602 : *
9603 : * When cached statistics are not available, and bForce is TRUE,
9604 : * ComputeStatistics() is called.
9605 : *
9606 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
9607 : * will generally cache statistics in the .aux.xml file allowing fast fetch
9608 : * after the first request.
9609 : *
9610 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9611 : *
9612 : * This method is the same as the C function GDALMDArrayGetStatistics().
9613 : *
9614 : * @param bApproxOK Currently ignored. In the future, should be set to true
9615 : * if statistics on the whole array are wished, or to false if a subset of it
9616 : * may be used.
9617 : *
9618 : * @param bForce If false statistics will only be returned if it can
9619 : * be done without rescanning the image.
9620 : *
9621 : * @param pdfMin Location into which to load image minimum (may be NULL).
9622 : *
9623 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9624 : *
9625 : * @param pdfMean Location into which to load image mean (may be NULL).
9626 : *
9627 : * @param pdfStdDev Location into which to load image standard deviation
9628 : * (may be NULL).
9629 : *
9630 : * @param pnValidCount Number of samples whose value is different from the
9631 : * nodata value. (may be NULL)
9632 : *
9633 : * @param pfnProgress a function to call to report progress, or NULL.
9634 : *
9635 : * @param pProgressData application data to pass to the progress function.
9636 : *
9637 : * @return CE_None on success, CE_Warning if no values returned,
9638 : * CE_Failure if an error occurs.
9639 : *
9640 : * @since GDAL 3.2
9641 : */
9642 :
9643 7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
9644 : double *pdfMax, double *pdfMean,
9645 : double *pdfStdDev, GUInt64 *pnValidCount,
9646 : GDALProgressFunc pfnProgress,
9647 : void *pProgressData)
9648 : {
9649 7 : if (!bForce)
9650 1 : return CE_Warning;
9651 :
9652 12 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
9653 6 : pnValidCount, pfnProgress, pProgressData, nullptr)
9654 6 : ? CE_None
9655 6 : : CE_Failure;
9656 : }
9657 :
9658 : /************************************************************************/
9659 : /* ComputeStatistics() */
9660 : /************************************************************************/
9661 :
9662 : /**
9663 : * \brief Compute statistics.
9664 : *
9665 : * Returns the minimum, maximum, mean and standard deviation of all
9666 : * pixel values in this array.
9667 : *
9668 : * Pixels taken into account in statistics are those whose mask value
9669 : * (as determined by GetMask()) is non-zero.
9670 : *
9671 : * Once computed, the statistics will generally be "set" back on the
9672 : * owing dataset.
9673 : *
9674 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9675 : *
9676 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
9677 : * and GDALMDArrayComputeStatisticsEx().
9678 : *
9679 : * @param bApproxOK Currently ignored. In the future, should be set to true
9680 : * if statistics on the whole array are wished, or to false if a subset of it
9681 : * may be used.
9682 : *
9683 : * @param pdfMin Location into which to load image minimum (may be NULL).
9684 : *
9685 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9686 : *
9687 : * @param pdfMean Location into which to load image mean (may be NULL).
9688 : *
9689 : * @param pdfStdDev Location into which to load image standard deviation
9690 : * (may be NULL).
9691 : *
9692 : * @param pnValidCount Number of samples whose value is different from the
9693 : * nodata value. (may be NULL)
9694 : *
9695 : * @param pfnProgress a function to call to report progress, or NULL.
9696 : *
9697 : * @param pProgressData application data to pass to the progress function.
9698 : *
9699 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
9700 : * Options are driver specific. For now the netCDF and Zarr
9701 : * drivers recognize UPDATE_METADATA=YES, whose effect is
9702 : * to add or update the actual_range attribute with the
9703 : * computed min/max, only if done on the full array, in non
9704 : * approximate mode, and the dataset is opened in update
9705 : * mode.
9706 : *
9707 : * @return true on success
9708 : *
9709 : * @since GDAL 3.2
9710 : */
9711 :
9712 10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
9713 : double *pdfMax, double *pdfMean,
9714 : double *pdfStdDev, GUInt64 *pnValidCount,
9715 : GDALProgressFunc pfnProgress,
9716 : void *pProgressData,
9717 : CSLConstList papszOptions)
9718 : {
9719 : struct StatsPerChunkType
9720 : {
9721 : const GDALMDArray *array = nullptr;
9722 : std::shared_ptr<GDALMDArray> poMask{};
9723 : double dfMin = std::numeric_limits<double>::max();
9724 : double dfMax = -std::numeric_limits<double>::max();
9725 : double dfMean = 0.0;
9726 : double dfM2 = 0.0;
9727 : GUInt64 nValidCount = 0;
9728 : std::vector<GByte> abyData{};
9729 : std::vector<double> adfData{};
9730 : std::vector<GByte> abyMaskData{};
9731 : GDALProgressFunc pfnProgress = nullptr;
9732 : void *pProgressData = nullptr;
9733 : };
9734 :
9735 10 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
9736 : const GUInt64 *chunkArrayStartIdx,
9737 : const size_t *chunkCount, GUInt64 iCurChunk,
9738 : GUInt64 nChunkCount, void *pUserData)
9739 : {
9740 10 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
9741 10 : const GDALMDArray *array = data->array;
9742 10 : const GDALMDArray *poMask = data->poMask.get();
9743 10 : const size_t nDims = array->GetDimensionCount();
9744 10 : size_t nVals = 1;
9745 27 : for (size_t i = 0; i < nDims; i++)
9746 17 : nVals *= chunkCount[i];
9747 :
9748 : // Get mask
9749 10 : data->abyMaskData.resize(nVals);
9750 10 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9751 10 : poMask->GetDataType(), &data->abyMaskData[0])))
9752 : {
9753 0 : return false;
9754 : }
9755 :
9756 : // Get data
9757 10 : const auto &oType = array->GetDataType();
9758 10 : if (oType.GetNumericDataType() == GDT_Float64)
9759 : {
9760 4 : data->adfData.resize(nVals);
9761 4 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9762 4 : oType, &data->adfData[0]))
9763 : {
9764 0 : return false;
9765 : }
9766 : }
9767 : else
9768 : {
9769 6 : data->abyData.resize(nVals * oType.GetSize());
9770 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9771 6 : oType, &data->abyData[0]))
9772 : {
9773 0 : return false;
9774 : }
9775 6 : data->adfData.resize(nVals);
9776 6 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
9777 6 : static_cast<int>(oType.GetSize()),
9778 6 : &data->adfData[0], GDT_Float64,
9779 : static_cast<int>(sizeof(double)),
9780 : static_cast<GPtrDiff_t>(nVals));
9781 : }
9782 469 : for (size_t i = 0; i < nVals; i++)
9783 : {
9784 459 : if (data->abyMaskData[i])
9785 : {
9786 454 : const double dfValue = data->adfData[i];
9787 454 : data->dfMin = std::min(data->dfMin, dfValue);
9788 454 : data->dfMax = std::max(data->dfMax, dfValue);
9789 454 : data->nValidCount++;
9790 454 : const double dfDelta = dfValue - data->dfMean;
9791 454 : data->dfMean += dfDelta / data->nValidCount;
9792 454 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
9793 : }
9794 : }
9795 10 : if (data->pfnProgress &&
9796 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
9797 : "", data->pProgressData))
9798 : {
9799 0 : return false;
9800 : }
9801 10 : return true;
9802 : };
9803 :
9804 10 : const auto &oType = GetDataType();
9805 20 : if (oType.GetClass() != GEDTC_NUMERIC ||
9806 10 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
9807 : {
9808 0 : CPLError(
9809 : CE_Failure, CPLE_NotSupported,
9810 : "Statistics can only be computed on non-complex numeric data type");
9811 0 : return false;
9812 : }
9813 :
9814 10 : const size_t nDims = GetDimensionCount();
9815 20 : std::vector<GUInt64> arrayStartIdx(nDims);
9816 20 : std::vector<GUInt64> count(nDims);
9817 10 : const auto &poDims = GetDimensions();
9818 27 : for (size_t i = 0; i < nDims; i++)
9819 : {
9820 17 : count[i] = poDims[i]->GetSize();
9821 : }
9822 10 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
9823 : const size_t nMaxChunkSize =
9824 : pszSwathSize
9825 10 : ? static_cast<size_t>(
9826 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
9827 0 : CPLAtoGIntBig(pszSwathSize)))
9828 : : static_cast<size_t>(
9829 10 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
9830 10 : GDALGetCacheMax64() / 4));
9831 20 : StatsPerChunkType sData;
9832 10 : sData.array = this;
9833 10 : sData.poMask = GetMask(nullptr);
9834 10 : if (sData.poMask == nullptr)
9835 : {
9836 0 : return false;
9837 : }
9838 10 : sData.pfnProgress = pfnProgress;
9839 10 : sData.pProgressData = pProgressData;
9840 10 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
9841 20 : GetProcessingChunkSize(nMaxChunkSize).data(),
9842 10 : PerChunkFunc, &sData))
9843 : {
9844 0 : return false;
9845 : }
9846 :
9847 10 : if (pdfMin)
9848 10 : *pdfMin = sData.dfMin;
9849 :
9850 10 : if (pdfMax)
9851 10 : *pdfMax = sData.dfMax;
9852 :
9853 10 : if (pdfMean)
9854 8 : *pdfMean = sData.dfMean;
9855 :
9856 : const double dfStdDev =
9857 10 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
9858 10 : if (pdfStdDev)
9859 8 : *pdfStdDev = dfStdDev;
9860 :
9861 10 : if (pnValidCount)
9862 8 : *pnValidCount = sData.nValidCount;
9863 :
9864 10 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
9865 10 : sData.nValidCount, papszOptions);
9866 :
9867 10 : return true;
9868 : }
9869 :
9870 : /************************************************************************/
9871 : /* SetStatistics() */
9872 : /************************************************************************/
9873 : //! @cond Doxygen_Suppress
9874 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
9875 : double /* dfMax */, double /* dfMean */,
9876 : double /* dfStdDev */,
9877 : GUInt64 /* nValidCount */,
9878 : CSLConstList /* papszOptions */)
9879 : {
9880 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
9881 5 : return false;
9882 : }
9883 :
9884 : //! @endcond
9885 :
9886 : /************************************************************************/
9887 : /* ClearStatistics() */
9888 : /************************************************************************/
9889 :
9890 : /**
9891 : * \brief Clear statistics.
9892 : *
9893 : * @since GDAL 3.4
9894 : */
9895 0 : void GDALMDArray::ClearStatistics()
9896 : {
9897 0 : }
9898 :
9899 : /************************************************************************/
9900 : /* GetCoordinateVariables() */
9901 : /************************************************************************/
9902 :
9903 : /**
9904 : * \brief Return coordinate variables.
9905 : *
9906 : * Coordinate variables are an alternate way of indexing an array that can
9907 : * be sometimes used. For example, an array collected through remote sensing
9908 : * might be indexed by (scanline, pixel). But there can be
9909 : * a longitude and latitude arrays alongside that are also both indexed by
9910 : * (scanline, pixel), and are referenced from operational arrays for
9911 : * reprojection purposes.
9912 : *
9913 : * For netCDF, this will return the arrays referenced by the "coordinates"
9914 : * attribute.
9915 : *
9916 : * This method is the same as the C function
9917 : * GDALMDArrayGetCoordinateVariables().
9918 : *
9919 : * @return a vector of arrays
9920 : *
9921 : * @since GDAL 3.4
9922 : */
9923 :
9924 : std::vector<std::shared_ptr<GDALMDArray>>
9925 13 : GDALMDArray::GetCoordinateVariables() const
9926 : {
9927 13 : return {};
9928 : }
9929 :
9930 : /************************************************************************/
9931 : /* ~GDALExtendedDataType() */
9932 : /************************************************************************/
9933 :
9934 : GDALExtendedDataType::~GDALExtendedDataType() = default;
9935 :
9936 : /************************************************************************/
9937 : /* GDALExtendedDataType() */
9938 : /************************************************************************/
9939 :
9940 8303 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
9941 8303 : GDALExtendedDataTypeSubType eSubType)
9942 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
9943 8303 : m_nMaxStringLength(nMaxStringLength)
9944 : {
9945 8303 : }
9946 :
9947 : /************************************************************************/
9948 : /* GDALExtendedDataType() */
9949 : /************************************************************************/
9950 :
9951 35256 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
9952 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
9953 35256 : m_nSize(GDALGetDataTypeSizeBytes(eType))
9954 : {
9955 35256 : }
9956 :
9957 : /************************************************************************/
9958 : /* GDALExtendedDataType() */
9959 : /************************************************************************/
9960 :
9961 632 : GDALExtendedDataType::GDALExtendedDataType(
9962 : const std::string &osName, size_t nTotalSize,
9963 632 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
9964 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
9965 632 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
9966 : {
9967 632 : }
9968 :
9969 : /************************************************************************/
9970 : /* GDALExtendedDataType() */
9971 : /************************************************************************/
9972 :
9973 : /** Copy constructor. */
9974 15413 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
9975 30826 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
9976 15413 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
9977 15413 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
9978 : {
9979 15413 : if (m_eClass == GEDTC_COMPOUND)
9980 : {
9981 431 : for (const auto &elt : other.m_aoComponents)
9982 : {
9983 281 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
9984 : }
9985 : }
9986 15413 : }
9987 :
9988 : /************************************************************************/
9989 : /* operator= () */
9990 : /************************************************************************/
9991 :
9992 : /** Copy assignment. */
9993 : GDALExtendedDataType &
9994 606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
9995 : {
9996 606 : if (this != &other)
9997 : {
9998 606 : m_osName = other.m_osName;
9999 606 : m_eClass = other.m_eClass;
10000 606 : m_eSubType = other.m_eSubType;
10001 606 : m_eNumericDT = other.m_eNumericDT;
10002 606 : m_nSize = other.m_nSize;
10003 606 : m_nMaxStringLength = other.m_nMaxStringLength;
10004 606 : m_aoComponents.clear();
10005 606 : if (m_eClass == GEDTC_COMPOUND)
10006 : {
10007 0 : for (const auto &elt : other.m_aoComponents)
10008 : {
10009 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10010 : }
10011 : }
10012 : }
10013 606 : return *this;
10014 : }
10015 :
10016 : /************************************************************************/
10017 : /* operator= () */
10018 : /************************************************************************/
10019 :
10020 : /** Move assignment. */
10021 : GDALExtendedDataType &
10022 14541 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10023 : {
10024 14541 : m_osName = std::move(other.m_osName);
10025 14541 : m_eClass = other.m_eClass;
10026 14541 : m_eSubType = other.m_eSubType;
10027 14541 : m_eNumericDT = other.m_eNumericDT;
10028 14541 : m_nSize = other.m_nSize;
10029 14541 : m_nMaxStringLength = other.m_nMaxStringLength;
10030 14541 : m_aoComponents = std::move(other.m_aoComponents);
10031 14541 : other.m_eClass = GEDTC_NUMERIC;
10032 14541 : other.m_eNumericDT = GDT_Unknown;
10033 14541 : other.m_nSize = 0;
10034 14541 : other.m_nMaxStringLength = 0;
10035 14541 : return *this;
10036 : }
10037 :
10038 : /************************************************************************/
10039 : /* Create() */
10040 : /************************************************************************/
10041 :
10042 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10043 : *
10044 : * This is the same as the C function GDALExtendedDataTypeCreate()
10045 : *
10046 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10047 : * GDT_TypeCount
10048 : */
10049 35249 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10050 : {
10051 35249 : return GDALExtendedDataType(eType);
10052 : }
10053 :
10054 : /************************************************************************/
10055 : /* Create() */
10056 : /************************************************************************/
10057 :
10058 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10059 : *
10060 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10061 : *
10062 : * @param osName Type name.
10063 : * @param nTotalSize Total size of the type in bytes.
10064 : * Should be large enough to store all components.
10065 : * @param components Components of the compound type.
10066 : */
10067 639 : GDALExtendedDataType GDALExtendedDataType::Create(
10068 : const std::string &osName, size_t nTotalSize,
10069 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10070 : {
10071 639 : size_t nLastOffset = 0;
10072 : // Some arbitrary threshold to avoid potential integer overflows
10073 639 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10074 : {
10075 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10076 2 : return GDALExtendedDataType(GDT_Unknown);
10077 : }
10078 3155 : for (const auto &comp : components)
10079 : {
10080 : // Check alignment too ?
10081 2519 : if (comp->GetOffset() < nLastOffset)
10082 : {
10083 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10084 1 : return GDALExtendedDataType(GDT_Unknown);
10085 : }
10086 2518 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10087 : }
10088 636 : if (nTotalSize < nLastOffset)
10089 : {
10090 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10091 1 : return GDALExtendedDataType(GDT_Unknown);
10092 : }
10093 635 : if (nTotalSize == 0 || components.empty())
10094 : {
10095 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10096 3 : return GDALExtendedDataType(GDT_Unknown);
10097 : }
10098 632 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10099 : }
10100 :
10101 : /************************************************************************/
10102 : /* Create() */
10103 : /************************************************************************/
10104 :
10105 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10106 : *
10107 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10108 : *
10109 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10110 : * unknown/unlimited
10111 : * @param eSubType Subtype.
10112 : */
10113 : GDALExtendedDataType
10114 8303 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10115 : GDALExtendedDataTypeSubType eSubType)
10116 : {
10117 8303 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10118 : }
10119 :
10120 : /************************************************************************/
10121 : /* operator==() */
10122 : /************************************************************************/
10123 :
10124 : /** Equality operator.
10125 : *
10126 : * This is the same as the C function GDALExtendedDataTypeEquals().
10127 : */
10128 2240 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10129 : {
10130 2213 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10131 4453 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10132 : {
10133 177 : return false;
10134 : }
10135 2063 : if (m_eClass == GEDTC_NUMERIC)
10136 : {
10137 866 : return m_eNumericDT == other.m_eNumericDT;
10138 : }
10139 1197 : if (m_eClass == GEDTC_STRING)
10140 : {
10141 1016 : return true;
10142 : }
10143 181 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10144 181 : if (m_aoComponents.size() != other.m_aoComponents.size())
10145 : {
10146 2 : return false;
10147 : }
10148 806 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10149 : {
10150 627 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10151 : {
10152 0 : return false;
10153 : }
10154 : }
10155 179 : return true;
10156 : }
10157 :
10158 : /************************************************************************/
10159 : /* CanConvertTo() */
10160 : /************************************************************************/
10161 :
10162 : /** Return whether this data type can be converted to the other one.
10163 : *
10164 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10165 : *
10166 : * @param other Target data type for the conversion being considered.
10167 : */
10168 8087 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10169 : {
10170 8087 : if (m_eClass == GEDTC_NUMERIC)
10171 : {
10172 5737 : if (m_eNumericDT == GDT_Unknown)
10173 0 : return false;
10174 5737 : if (other.m_eClass == GEDTC_NUMERIC &&
10175 5650 : other.m_eNumericDT == GDT_Unknown)
10176 0 : return false;
10177 5824 : return other.m_eClass == GEDTC_NUMERIC ||
10178 5824 : other.m_eClass == GEDTC_STRING;
10179 : }
10180 2350 : if (m_eClass == GEDTC_STRING)
10181 : {
10182 2211 : return other.m_eClass == m_eClass;
10183 : }
10184 139 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10185 139 : if (other.m_eClass != GEDTC_COMPOUND)
10186 0 : return false;
10187 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10188 278 : srcComponents;
10189 568 : for (const auto &srcComp : m_aoComponents)
10190 : {
10191 429 : srcComponents[srcComp->GetName()] = &srcComp;
10192 : }
10193 419 : for (const auto &dstComp : other.m_aoComponents)
10194 : {
10195 281 : auto oIter = srcComponents.find(dstComp->GetName());
10196 281 : if (oIter == srcComponents.end())
10197 1 : return false;
10198 280 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10199 0 : return false;
10200 : }
10201 138 : return true;
10202 : }
10203 :
10204 : /************************************************************************/
10205 : /* NeedsFreeDynamicMemory() */
10206 : /************************************************************************/
10207 :
10208 : /** Return whether the data type holds dynamically allocated memory, that
10209 : * needs to be freed with FreeDynamicMemory().
10210 : *
10211 : */
10212 3501 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10213 : {
10214 3501 : switch (m_eClass)
10215 : {
10216 843 : case GEDTC_STRING:
10217 843 : return true;
10218 :
10219 2585 : case GEDTC_NUMERIC:
10220 2585 : return false;
10221 :
10222 73 : case GEDTC_COMPOUND:
10223 : {
10224 186 : for (const auto &comp : m_aoComponents)
10225 : {
10226 172 : if (comp->GetType().NeedsFreeDynamicMemory())
10227 59 : return true;
10228 : }
10229 : }
10230 : }
10231 14 : return false;
10232 : }
10233 :
10234 : /************************************************************************/
10235 : /* FreeDynamicMemory() */
10236 : /************************************************************************/
10237 :
10238 : /** Release the dynamic memory (strings typically) from a raw value.
10239 : *
10240 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10241 : *
10242 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10243 : */
10244 3258 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10245 : {
10246 3258 : switch (m_eClass)
10247 : {
10248 2284 : case GEDTC_STRING:
10249 : {
10250 : char *pszStr;
10251 2284 : memcpy(&pszStr, pBuffer, sizeof(char *));
10252 2284 : if (pszStr)
10253 : {
10254 1780 : VSIFree(pszStr);
10255 : }
10256 2284 : break;
10257 : }
10258 :
10259 835 : case GEDTC_NUMERIC:
10260 : {
10261 835 : break;
10262 : }
10263 :
10264 139 : case GEDTC_COMPOUND:
10265 : {
10266 139 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10267 605 : for (const auto &comp : m_aoComponents)
10268 : {
10269 932 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10270 466 : comp->GetOffset());
10271 : }
10272 139 : break;
10273 : }
10274 : }
10275 3258 : }
10276 :
10277 : /************************************************************************/
10278 : /* ~GDALEDTComponent() */
10279 : /************************************************************************/
10280 :
10281 : GDALEDTComponent::~GDALEDTComponent() = default;
10282 :
10283 : /************************************************************************/
10284 : /* GDALEDTComponent() */
10285 : /************************************************************************/
10286 :
10287 : /** constructor of a GDALEDTComponent
10288 : *
10289 : * This is the same as the C function GDALEDTComponendCreate()
10290 : *
10291 : * @param name Component name
10292 : * @param offset Offset in byte of the component in the compound data type.
10293 : * In case of nesting of compound data type, this should be
10294 : * the offset to the immediate belonging data type, not to the
10295 : * higher level one.
10296 : * @param type Component data type.
10297 : */
10298 2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10299 2510 : const GDALExtendedDataType &type)
10300 2510 : : m_osName(name), m_nOffset(offset), m_oType(type)
10301 : {
10302 2510 : }
10303 :
10304 : /************************************************************************/
10305 : /* GDALEDTComponent() */
10306 : /************************************************************************/
10307 :
10308 : /** Copy constructor. */
10309 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10310 :
10311 : /************************************************************************/
10312 : /* operator==() */
10313 : /************************************************************************/
10314 :
10315 : /** Equality operator.
10316 : */
10317 627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10318 : {
10319 1254 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10320 1254 : m_oType == other.m_oType;
10321 : }
10322 :
10323 : /************************************************************************/
10324 : /* ~GDALDimension() */
10325 : /************************************************************************/
10326 :
10327 : GDALDimension::~GDALDimension() = default;
10328 :
10329 : /************************************************************************/
10330 : /* GDALDimension() */
10331 : /************************************************************************/
10332 :
10333 : //! @cond Doxygen_Suppress
10334 : /** Constructor.
10335 : *
10336 : * @param osParentName Parent name
10337 : * @param osName name
10338 : * @param osType type. See GetType().
10339 : * @param osDirection direction. See GetDirection().
10340 : * @param nSize size.
10341 : */
10342 8092 : GDALDimension::GDALDimension(const std::string &osParentName,
10343 : const std::string &osName,
10344 : const std::string &osType,
10345 8092 : const std::string &osDirection, GUInt64 nSize)
10346 : : m_osName(osName),
10347 : m_osFullName(
10348 8092 : !osParentName.empty()
10349 11960 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10350 : : osName),
10351 28144 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10352 : {
10353 8092 : }
10354 :
10355 : //! @endcond
10356 :
10357 : /************************************************************************/
10358 : /* GetIndexingVariable() */
10359 : /************************************************************************/
10360 :
10361 : /** Return the variable that is used to index the dimension (if there is one).
10362 : *
10363 : * This is the array, typically one-dimensional, describing the values taken
10364 : * by the dimension.
10365 : */
10366 49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10367 : {
10368 49 : return nullptr;
10369 : }
10370 :
10371 : /************************************************************************/
10372 : /* SetIndexingVariable() */
10373 : /************************************************************************/
10374 :
10375 : /** Set the variable that is used to index the dimension.
10376 : *
10377 : * This is the array, typically one-dimensional, describing the values taken
10378 : * by the dimension.
10379 : *
10380 : * Optionally implemented by drivers.
10381 : *
10382 : * Drivers known to implement it: MEM.
10383 : *
10384 : * @param poArray Variable to use to index the dimension.
10385 : * @return true in case of success.
10386 : */
10387 3 : bool GDALDimension::SetIndexingVariable(
10388 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10389 : {
10390 3 : CPLError(CE_Failure, CPLE_NotSupported,
10391 : "SetIndexingVariable() not implemented");
10392 3 : return false;
10393 : }
10394 :
10395 : /************************************************************************/
10396 : /* Rename() */
10397 : /************************************************************************/
10398 :
10399 : /** Rename the dimension.
10400 : *
10401 : * This is not implemented by all drivers.
10402 : *
10403 : * Drivers known to implement it: MEM, netCDF, ZARR.
10404 : *
10405 : * This is the same as the C function GDALDimensionRename().
10406 : *
10407 : * @param osNewName New name.
10408 : *
10409 : * @return true in case of success
10410 : * @since GDAL 3.8
10411 : */
10412 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10413 : {
10414 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10415 0 : return false;
10416 : }
10417 :
10418 : /************************************************************************/
10419 : /* BaseRename() */
10420 : /************************************************************************/
10421 :
10422 : //! @cond Doxygen_Suppress
10423 8 : void GDALDimension::BaseRename(const std::string &osNewName)
10424 : {
10425 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
10426 8 : m_osFullName += osNewName;
10427 8 : m_osName = osNewName;
10428 8 : }
10429 :
10430 : //! @endcond
10431 :
10432 : //! @cond Doxygen_Suppress
10433 : /************************************************************************/
10434 : /* ParentRenamed() */
10435 : /************************************************************************/
10436 :
10437 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
10438 : {
10439 8 : m_osFullName = osNewParentFullName;
10440 8 : m_osFullName += "/";
10441 8 : m_osFullName += m_osName;
10442 8 : }
10443 :
10444 : //! @endcond
10445 :
10446 : //! @cond Doxygen_Suppress
10447 : /************************************************************************/
10448 : /* ParentDeleted() */
10449 : /************************************************************************/
10450 :
10451 4 : void GDALDimension::ParentDeleted()
10452 : {
10453 4 : }
10454 :
10455 : //! @endcond
10456 :
10457 : /************************************************************************/
10458 : /************************************************************************/
10459 : /************************************************************************/
10460 : /* C API */
10461 : /************************************************************************/
10462 : /************************************************************************/
10463 : /************************************************************************/
10464 :
10465 : /************************************************************************/
10466 : /* GDALExtendedDataTypeCreate() */
10467 : /************************************************************************/
10468 :
10469 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10470 : *
10471 : * This is the same as the C++ method GDALExtendedDataType::Create()
10472 : *
10473 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10474 : *
10475 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10476 : * GDT_TypeCount
10477 : *
10478 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10479 : */
10480 1937 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
10481 : {
10482 1937 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
10483 : {
10484 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10485 : "Illegal GDT_Unknown/GDT_TypeCount argument");
10486 0 : return nullptr;
10487 : }
10488 : return new GDALExtendedDataTypeHS(
10489 1937 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
10490 : }
10491 :
10492 : /************************************************************************/
10493 : /* GDALExtendedDataTypeCreateString() */
10494 : /************************************************************************/
10495 :
10496 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10497 : *
10498 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10499 : *
10500 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10501 : *
10502 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10503 : */
10504 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
10505 : {
10506 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10507 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
10508 : }
10509 :
10510 : /************************************************************************/
10511 : /* GDALExtendedDataTypeCreateStringEx() */
10512 : /************************************************************************/
10513 :
10514 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10515 : *
10516 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10517 : *
10518 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10519 : *
10520 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10521 : * @since GDAL 3.4
10522 : */
10523 : GDALExtendedDataTypeH
10524 187 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
10525 : GDALExtendedDataTypeSubType eSubType)
10526 : {
10527 187 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10528 187 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
10529 : }
10530 :
10531 : /************************************************************************/
10532 : /* GDALExtendedDataTypeCreateCompound() */
10533 : /************************************************************************/
10534 :
10535 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10536 : *
10537 : * This is the same as the C++ method GDALExtendedDataType::Create(const
10538 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
10539 : *
10540 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10541 : *
10542 : * @param pszName Type name.
10543 : * @param nTotalSize Total size of the type in bytes.
10544 : * Should be large enough to store all components.
10545 : * @param nComponents Number of components in comps array.
10546 : * @param comps Components.
10547 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10548 : */
10549 : GDALExtendedDataTypeH
10550 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
10551 : size_t nComponents,
10552 : const GDALEDTComponentH *comps)
10553 : {
10554 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
10555 54 : for (size_t i = 0; i < nComponents; i++)
10556 : {
10557 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
10558 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
10559 : }
10560 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
10561 66 : std::move(compsCpp));
10562 22 : if (dt.GetClass() != GEDTC_COMPOUND)
10563 6 : return nullptr;
10564 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
10565 : }
10566 :
10567 : /************************************************************************/
10568 : /* GDALExtendedDataTypeRelease() */
10569 : /************************************************************************/
10570 :
10571 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
10572 : *
10573 : * Note: when applied on a object coming from a driver, this does not
10574 : * destroy the object in the file, database, etc...
10575 : */
10576 6455 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
10577 : {
10578 6455 : delete hEDT;
10579 6455 : }
10580 :
10581 : /************************************************************************/
10582 : /* GDALExtendedDataTypeGetName() */
10583 : /************************************************************************/
10584 :
10585 : /** Return type name.
10586 : *
10587 : * This is the same as the C++ method GDALExtendedDataType::GetName()
10588 : */
10589 7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
10590 : {
10591 7 : VALIDATE_POINTER1(hEDT, __func__, "");
10592 7 : return hEDT->m_poImpl->GetName().c_str();
10593 : }
10594 :
10595 : /************************************************************************/
10596 : /* GDALExtendedDataTypeGetClass() */
10597 : /************************************************************************/
10598 :
10599 : /** Return type class.
10600 : *
10601 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
10602 : */
10603 : GDALExtendedDataTypeClass
10604 7493 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
10605 : {
10606 7493 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
10607 7493 : return hEDT->m_poImpl->GetClass();
10608 : }
10609 :
10610 : /************************************************************************/
10611 : /* GDALExtendedDataTypeGetNumericDataType() */
10612 : /************************************************************************/
10613 :
10614 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
10615 : *
10616 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
10617 : */
10618 584 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
10619 : {
10620 584 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
10621 584 : return hEDT->m_poImpl->GetNumericDataType();
10622 : }
10623 :
10624 : /************************************************************************/
10625 : /* GDALExtendedDataTypeGetSize() */
10626 : /************************************************************************/
10627 :
10628 : /** Return data type size in bytes.
10629 : *
10630 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
10631 : */
10632 2491 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
10633 : {
10634 2491 : VALIDATE_POINTER1(hEDT, __func__, 0);
10635 2491 : return hEDT->m_poImpl->GetSize();
10636 : }
10637 :
10638 : /************************************************************************/
10639 : /* GDALExtendedDataTypeGetMaxStringLength() */
10640 : /************************************************************************/
10641 :
10642 : /** Return the maximum length of a string in bytes.
10643 : *
10644 : * 0 indicates unknown/unlimited string.
10645 : *
10646 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
10647 : */
10648 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
10649 : {
10650 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
10651 3 : return hEDT->m_poImpl->GetMaxStringLength();
10652 : }
10653 :
10654 : /************************************************************************/
10655 : /* GDALExtendedDataTypeCanConvertTo() */
10656 : /************************************************************************/
10657 :
10658 : /** Return whether this data type can be converted to the other one.
10659 : *
10660 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
10661 : *
10662 : * @param hSourceEDT Source data type for the conversion being considered.
10663 : * @param hTargetEDT Target data type for the conversion being considered.
10664 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
10665 : */
10666 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
10667 : GDALExtendedDataTypeH hTargetEDT)
10668 : {
10669 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
10670 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
10671 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
10672 : }
10673 :
10674 : /************************************************************************/
10675 : /* GDALExtendedDataTypeEquals() */
10676 : /************************************************************************/
10677 :
10678 : /** Return whether this data type is equal to another one.
10679 : *
10680 : * This is the same as the C++ method GDALExtendedDataType::operator==()
10681 : *
10682 : * @param hFirstEDT First data type.
10683 : * @param hSecondEDT Second data type.
10684 : * @return TRUE if they are equal. FALSE otherwise.
10685 : */
10686 98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
10687 : GDALExtendedDataTypeH hSecondEDT)
10688 : {
10689 98 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
10690 98 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
10691 98 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
10692 : }
10693 :
10694 : /************************************************************************/
10695 : /* GDALExtendedDataTypeGetSubType() */
10696 : /************************************************************************/
10697 :
10698 : /** Return the subtype of a type.
10699 : *
10700 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
10701 : *
10702 : * @param hEDT Data type.
10703 : * @return subtype.
10704 : * @since 3.4
10705 : */
10706 : GDALExtendedDataTypeSubType
10707 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
10708 : {
10709 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
10710 104 : return hEDT->m_poImpl->GetSubType();
10711 : }
10712 :
10713 : /************************************************************************/
10714 : /* GDALExtendedDataTypeGetComponents() */
10715 : /************************************************************************/
10716 :
10717 : /** Return the components of the data type (only valid when GetClass() ==
10718 : * GEDTC_COMPOUND)
10719 : *
10720 : * The returned array and its content must be freed with
10721 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
10722 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
10723 : * individual array members).
10724 : *
10725 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
10726 : *
10727 : * @param hEDT Data type
10728 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
10729 : * @return an array of *pnCount components.
10730 : */
10731 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
10732 : size_t *pnCount)
10733 : {
10734 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
10735 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
10736 44 : const auto &components = hEDT->m_poImpl->GetComponents();
10737 : auto ret = static_cast<GDALEDTComponentH *>(
10738 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
10739 131 : for (size_t i = 0; i < components.size(); i++)
10740 : {
10741 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
10742 : }
10743 44 : *pnCount = components.size();
10744 44 : return ret;
10745 : }
10746 :
10747 : /************************************************************************/
10748 : /* GDALExtendedDataTypeFreeComponents() */
10749 : /************************************************************************/
10750 :
10751 : /** Free the return of GDALExtendedDataTypeGetComponents().
10752 : *
10753 : * @param components return value of GDALExtendedDataTypeGetComponents()
10754 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
10755 : */
10756 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
10757 : size_t nCount)
10758 : {
10759 131 : for (size_t i = 0; i < nCount; i++)
10760 : {
10761 87 : delete components[i];
10762 : }
10763 44 : CPLFree(components);
10764 44 : }
10765 :
10766 : /************************************************************************/
10767 : /* GDALEDTComponentCreate() */
10768 : /************************************************************************/
10769 :
10770 : /** Create a new GDALEDTComponent.
10771 : *
10772 : * The returned value must be freed with GDALEDTComponentRelease().
10773 : *
10774 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
10775 : */
10776 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
10777 : GDALExtendedDataTypeH hType)
10778 : {
10779 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10780 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
10781 : return new GDALEDTComponentHS(
10782 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
10783 : }
10784 :
10785 : /************************************************************************/
10786 : /* GDALEDTComponentRelease() */
10787 : /************************************************************************/
10788 :
10789 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
10790 : *
10791 : * Note: when applied on a object coming from a driver, this does not
10792 : * destroy the object in the file, database, etc...
10793 : */
10794 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
10795 : {
10796 61 : delete hComp;
10797 61 : }
10798 :
10799 : /************************************************************************/
10800 : /* GDALEDTComponentGetName() */
10801 : /************************************************************************/
10802 :
10803 : /** Return the name.
10804 : *
10805 : * The returned pointer is valid until hComp is released.
10806 : *
10807 : * This is the same as the C++ method GDALEDTComponent::GetName().
10808 : */
10809 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
10810 : {
10811 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
10812 33 : return hComp->m_poImpl->GetName().c_str();
10813 : }
10814 :
10815 : /************************************************************************/
10816 : /* GDALEDTComponentGetOffset() */
10817 : /************************************************************************/
10818 :
10819 : /** Return the offset (in bytes) of the component in the compound data type.
10820 : *
10821 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
10822 : */
10823 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
10824 : {
10825 31 : VALIDATE_POINTER1(hComp, __func__, 0);
10826 31 : return hComp->m_poImpl->GetOffset();
10827 : }
10828 :
10829 : /************************************************************************/
10830 : /* GDALEDTComponentGetType() */
10831 : /************************************************************************/
10832 :
10833 : /** Return the data type of the component.
10834 : *
10835 : * This is the same as the C++ method GDALEDTComponent::GetType().
10836 : */
10837 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
10838 : {
10839 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
10840 : return new GDALExtendedDataTypeHS(
10841 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
10842 : }
10843 :
10844 : /************************************************************************/
10845 : /* GDALGroupRelease() */
10846 : /************************************************************************/
10847 :
10848 : /** Release the GDAL in-memory object associated with a GDALGroupH.
10849 : *
10850 : * Note: when applied on a object coming from a driver, this does not
10851 : * destroy the object in the file, database, etc...
10852 : */
10853 1384 : void GDALGroupRelease(GDALGroupH hGroup)
10854 : {
10855 1384 : delete hGroup;
10856 1384 : }
10857 :
10858 : /************************************************************************/
10859 : /* GDALGroupGetName() */
10860 : /************************************************************************/
10861 :
10862 : /** Return the name of the group.
10863 : *
10864 : * The returned pointer is valid until hGroup is released.
10865 : *
10866 : * This is the same as the C++ method GDALGroup::GetName().
10867 : */
10868 87 : const char *GDALGroupGetName(GDALGroupH hGroup)
10869 : {
10870 87 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10871 87 : return hGroup->m_poImpl->GetName().c_str();
10872 : }
10873 :
10874 : /************************************************************************/
10875 : /* GDALGroupGetFullName() */
10876 : /************************************************************************/
10877 :
10878 : /** Return the full name of the group.
10879 : *
10880 : * The returned pointer is valid until hGroup is released.
10881 : *
10882 : * This is the same as the C++ method GDALGroup::GetFullName().
10883 : */
10884 41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
10885 : {
10886 41 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10887 41 : return hGroup->m_poImpl->GetFullName().c_str();
10888 : }
10889 :
10890 : /************************************************************************/
10891 : /* GDALGroupGetMDArrayNames() */
10892 : /************************************************************************/
10893 :
10894 : /** Return the list of multidimensional array names contained in this group.
10895 : *
10896 : * This is the same as the C++ method GDALGroup::GetGroupNames().
10897 : *
10898 : * @return the array names, to be freed with CSLDestroy()
10899 : */
10900 317 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
10901 : {
10902 317 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10903 634 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
10904 634 : CPLStringList res;
10905 805 : for (const auto &name : names)
10906 : {
10907 488 : res.AddString(name.c_str());
10908 : }
10909 317 : return res.StealList();
10910 : }
10911 :
10912 : /************************************************************************/
10913 : /* GDALGroupOpenMDArray() */
10914 : /************************************************************************/
10915 :
10916 : /** Open and return a multidimensional array.
10917 : *
10918 : * This is the same as the C++ method GDALGroup::OpenMDArray().
10919 : *
10920 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
10921 : */
10922 761 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
10923 : CSLConstList papszOptions)
10924 : {
10925 761 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10926 761 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
10927 2283 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
10928 2283 : papszOptions);
10929 761 : if (!array)
10930 28 : return nullptr;
10931 733 : return new GDALMDArrayHS(array);
10932 : }
10933 :
10934 : /************************************************************************/
10935 : /* GDALGroupOpenMDArrayFromFullname() */
10936 : /************************************************************************/
10937 :
10938 : /** Open and return a multidimensional array from its fully qualified name.
10939 : *
10940 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
10941 : *
10942 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
10943 : *
10944 : * @since GDAL 3.2
10945 : */
10946 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
10947 : const char *pszFullname,
10948 : CSLConstList papszOptions)
10949 : {
10950 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10951 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
10952 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
10953 48 : std::string(pszFullname), papszOptions);
10954 16 : if (!array)
10955 2 : return nullptr;
10956 14 : return new GDALMDArrayHS(array);
10957 : }
10958 :
10959 : /************************************************************************/
10960 : /* GDALGroupResolveMDArray() */
10961 : /************************************************************************/
10962 :
10963 : /** Locate an array in a group and its subgroups by name.
10964 : *
10965 : * See GDALGroup::ResolveMDArray() for description of the behavior.
10966 : * @since GDAL 3.2
10967 : */
10968 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
10969 : const char *pszStartingPoint,
10970 : CSLConstList papszOptions)
10971 : {
10972 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10973 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10974 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
10975 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
10976 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
10977 19 : if (!array)
10978 2 : return nullptr;
10979 17 : return new GDALMDArrayHS(array);
10980 : }
10981 :
10982 : /************************************************************************/
10983 : /* GDALGroupGetGroupNames() */
10984 : /************************************************************************/
10985 :
10986 : /** Return the list of sub-groups contained in this group.
10987 : *
10988 : * This is the same as the C++ method GDALGroup::GetGroupNames().
10989 : *
10990 : * @return the group names, to be freed with CSLDestroy()
10991 : */
10992 95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
10993 : {
10994 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10995 190 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
10996 190 : CPLStringList res;
10997 215 : for (const auto &name : names)
10998 : {
10999 120 : res.AddString(name.c_str());
11000 : }
11001 95 : return res.StealList();
11002 : }
11003 :
11004 : /************************************************************************/
11005 : /* GDALGroupOpenGroup() */
11006 : /************************************************************************/
11007 :
11008 : /** Open and return a sub-group.
11009 : *
11010 : * This is the same as the C++ method GDALGroup::OpenGroup().
11011 : *
11012 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11013 : */
11014 157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11015 : CSLConstList papszOptions)
11016 : {
11017 157 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11018 157 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11019 : auto subGroup =
11020 471 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11021 157 : if (!subGroup)
11022 28 : return nullptr;
11023 129 : return new GDALGroupHS(subGroup);
11024 : }
11025 :
11026 : /************************************************************************/
11027 : /* GDALGroupGetVectorLayerNames() */
11028 : /************************************************************************/
11029 :
11030 : /** Return the list of layer names contained in this group.
11031 : *
11032 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11033 : *
11034 : * @return the group names, to be freed with CSLDestroy()
11035 : * @since 3.4
11036 : */
11037 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11038 : CSLConstList papszOptions)
11039 : {
11040 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11041 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11042 16 : CPLStringList res;
11043 18 : for (const auto &name : names)
11044 : {
11045 10 : res.AddString(name.c_str());
11046 : }
11047 8 : return res.StealList();
11048 : }
11049 :
11050 : /************************************************************************/
11051 : /* GDALGroupOpenVectorLayer() */
11052 : /************************************************************************/
11053 :
11054 : /** Open and return a vector layer.
11055 : *
11056 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11057 : *
11058 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11059 : * the returned handled if only valid while the parent GDALDatasetH is kept
11060 : * opened.
11061 : *
11062 : * @return the vector layer, or nullptr.
11063 : * @since 3.4
11064 : */
11065 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11066 : const char *pszVectorLayerName,
11067 : CSLConstList papszOptions)
11068 : {
11069 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11070 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11071 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11072 24 : std::string(pszVectorLayerName), papszOptions));
11073 : }
11074 :
11075 : /************************************************************************/
11076 : /* GDALGroupOpenMDArrayFromFullname() */
11077 : /************************************************************************/
11078 :
11079 : /** Open and return a sub-group from its fully qualified name.
11080 : *
11081 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11082 : *
11083 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11084 : *
11085 : * @since GDAL 3.2
11086 : */
11087 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11088 : const char *pszFullname,
11089 : CSLConstList papszOptions)
11090 : {
11091 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11092 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11093 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11094 9 : std::string(pszFullname), papszOptions);
11095 3 : if (!subGroup)
11096 2 : return nullptr;
11097 1 : return new GDALGroupHS(subGroup);
11098 : }
11099 :
11100 : /************************************************************************/
11101 : /* GDALGroupGetDimensions() */
11102 : /************************************************************************/
11103 :
11104 : /** Return the list of dimensions contained in this group and used by its
11105 : * arrays.
11106 : *
11107 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11108 : * array itself needs to be freed, CPLFree() should be called (and
11109 : * GDALDimensionRelease() on individual array members).
11110 : *
11111 : * This is the same as the C++ method GDALGroup::GetDimensions().
11112 : *
11113 : * @param hGroup Group.
11114 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11115 : * @param papszOptions Driver specific options determining how dimensions
11116 : * should be retrieved. Pass nullptr for default behavior.
11117 : *
11118 : * @return an array of *pnCount dimensions.
11119 : */
11120 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11121 : CSLConstList papszOptions)
11122 : {
11123 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11124 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11125 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11126 : auto ret = static_cast<GDALDimensionH *>(
11127 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11128 230 : for (size_t i = 0; i < dims.size(); i++)
11129 : {
11130 157 : ret[i] = new GDALDimensionHS(dims[i]);
11131 : }
11132 73 : *pnCount = dims.size();
11133 73 : return ret;
11134 : }
11135 :
11136 : /************************************************************************/
11137 : /* GDALGroupGetAttribute() */
11138 : /************************************************************************/
11139 :
11140 : /** Return an attribute by its name.
11141 : *
11142 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11143 : *
11144 : * The returned attribute must be freed with GDALAttributeRelease().
11145 : */
11146 78 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11147 : {
11148 78 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11149 78 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11150 234 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11151 78 : if (attr)
11152 74 : return new GDALAttributeHS(attr);
11153 4 : return nullptr;
11154 : }
11155 :
11156 : /************************************************************************/
11157 : /* GDALGroupGetAttributes() */
11158 : /************************************************************************/
11159 :
11160 : /** Return the list of attributes contained in this group.
11161 : *
11162 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11163 : * array itself needs to be freed, CPLFree() should be called (and
11164 : * GDALAttributeRelease() on individual array members).
11165 : *
11166 : * This is the same as the C++ method GDALGroup::GetAttributes().
11167 : *
11168 : * @param hGroup Group.
11169 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11170 : * @param papszOptions Driver specific options determining how attributes
11171 : * should be retrieved. Pass nullptr for default behavior.
11172 : *
11173 : * @return an array of *pnCount attributes.
11174 : */
11175 67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11176 : CSLConstList papszOptions)
11177 : {
11178 67 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11179 67 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11180 67 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11181 : auto ret = static_cast<GDALAttributeH *>(
11182 67 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11183 221 : for (size_t i = 0; i < attrs.size(); i++)
11184 : {
11185 154 : ret[i] = new GDALAttributeHS(attrs[i]);
11186 : }
11187 67 : *pnCount = attrs.size();
11188 67 : return ret;
11189 : }
11190 :
11191 : /************************************************************************/
11192 : /* GDALGroupGetStructuralInfo() */
11193 : /************************************************************************/
11194 :
11195 : /** Return structural information on the group.
11196 : *
11197 : * This may be the compression, etc..
11198 : *
11199 : * The return value should not be freed and is valid until GDALGroup is
11200 : * released or this function called again.
11201 : *
11202 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11203 : */
11204 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11205 : {
11206 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11207 4 : return hGroup->m_poImpl->GetStructuralInfo();
11208 : }
11209 :
11210 : /************************************************************************/
11211 : /* GDALReleaseAttributes() */
11212 : /************************************************************************/
11213 :
11214 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11215 : *
11216 : * @param attributes return pointer of above methods
11217 : * @param nCount *pnCount value returned by above methods
11218 : */
11219 125 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11220 : {
11221 408 : for (size_t i = 0; i < nCount; i++)
11222 : {
11223 283 : delete attributes[i];
11224 : }
11225 125 : CPLFree(attributes);
11226 125 : }
11227 :
11228 : /************************************************************************/
11229 : /* GDALGroupCreateGroup() */
11230 : /************************************************************************/
11231 :
11232 : /** Create a sub-group within a group.
11233 : *
11234 : * This is the same as the C++ method GDALGroup::CreateGroup().
11235 : *
11236 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11237 : */
11238 173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11239 : CSLConstList papszOptions)
11240 : {
11241 173 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11242 173 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11243 519 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11244 519 : papszOptions);
11245 173 : if (!ret)
11246 49 : return nullptr;
11247 124 : return new GDALGroupHS(ret);
11248 : }
11249 :
11250 : /************************************************************************/
11251 : /* GDALGroupDeleteGroup() */
11252 : /************************************************************************/
11253 :
11254 : /** Delete a sub-group from a group.
11255 : *
11256 : * After this call, if a previously obtained instance of the deleted object
11257 : * is still alive, no method other than for freeing it should be invoked.
11258 : *
11259 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11260 : *
11261 : * @return true in case of success.
11262 : * @since GDAL 3.8
11263 : */
11264 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11265 : CSLConstList papszOptions)
11266 : {
11267 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11268 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11269 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11270 20 : papszOptions);
11271 : }
11272 :
11273 : /************************************************************************/
11274 : /* GDALGroupCreateDimension() */
11275 : /************************************************************************/
11276 :
11277 : /** Create a dimension within a group.
11278 : *
11279 : * This is the same as the C++ method GDALGroup::CreateDimension().
11280 : *
11281 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11282 : */
11283 645 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11284 : const char *pszType,
11285 : const char *pszDirection, GUInt64 nSize,
11286 : CSLConstList papszOptions)
11287 : {
11288 645 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11289 645 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11290 645 : auto ret = hGroup->m_poImpl->CreateDimension(
11291 1290 : std::string(pszName), std::string(pszType ? pszType : ""),
11292 2580 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11293 645 : if (!ret)
11294 9 : return nullptr;
11295 636 : return new GDALDimensionHS(ret);
11296 : }
11297 :
11298 : /************************************************************************/
11299 : /* GDALGroupCreateMDArray() */
11300 : /************************************************************************/
11301 :
11302 : /** Create a multidimensional array within a group.
11303 : *
11304 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11305 : *
11306 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11307 : */
11308 591 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11309 : size_t nDimensions,
11310 : GDALDimensionH *pahDimensions,
11311 : GDALExtendedDataTypeH hEDT,
11312 : CSLConstList papszOptions)
11313 : {
11314 591 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11315 591 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11316 591 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11317 1182 : std::vector<std::shared_ptr<GDALDimension>> dims;
11318 591 : dims.reserve(nDimensions);
11319 1389 : for (size_t i = 0; i < nDimensions; i++)
11320 798 : dims.push_back(pahDimensions[i]->m_poImpl);
11321 1773 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11322 1773 : *(hEDT->m_poImpl), papszOptions);
11323 591 : if (!ret)
11324 65 : return nullptr;
11325 526 : return new GDALMDArrayHS(ret);
11326 : }
11327 :
11328 : /************************************************************************/
11329 : /* GDALGroupDeleteMDArray() */
11330 : /************************************************************************/
11331 :
11332 : /** Delete an array from a group.
11333 : *
11334 : * After this call, if a previously obtained instance of the deleted object
11335 : * is still alive, no method other than for freeing it should be invoked.
11336 : *
11337 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
11338 : *
11339 : * @return true in case of success.
11340 : * @since GDAL 3.8
11341 : */
11342 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
11343 : CSLConstList papszOptions)
11344 : {
11345 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11346 20 : VALIDATE_POINTER1(pszName, __func__, false);
11347 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
11348 : }
11349 :
11350 : /************************************************************************/
11351 : /* GDALGroupCreateAttribute() */
11352 : /************************************************************************/
11353 :
11354 : /** Create a attribute within a group.
11355 : *
11356 : * This is the same as the C++ method GDALGroup::CreateAttribute().
11357 : *
11358 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11359 : */
11360 120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
11361 : size_t nDimensions,
11362 : const GUInt64 *panDimensions,
11363 : GDALExtendedDataTypeH hEDT,
11364 : CSLConstList papszOptions)
11365 : {
11366 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11367 120 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11368 240 : std::vector<GUInt64> dims;
11369 120 : dims.reserve(nDimensions);
11370 166 : for (size_t i = 0; i < nDimensions; i++)
11371 46 : dims.push_back(panDimensions[i]);
11372 120 : auto ret = hGroup->m_poImpl->CreateAttribute(
11373 360 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11374 120 : if (!ret)
11375 14 : return nullptr;
11376 106 : return new GDALAttributeHS(ret);
11377 : }
11378 :
11379 : /************************************************************************/
11380 : /* GDALGroupDeleteAttribute() */
11381 : /************************************************************************/
11382 :
11383 : /** Delete an attribute from a group.
11384 : *
11385 : * After this call, if a previously obtained instance of the deleted object
11386 : * is still alive, no method other than for freeing it should be invoked.
11387 : *
11388 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
11389 : *
11390 : * @return true in case of success.
11391 : * @since GDAL 3.8
11392 : */
11393 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
11394 : CSLConstList papszOptions)
11395 : {
11396 25 : VALIDATE_POINTER1(hGroup, __func__, false);
11397 25 : VALIDATE_POINTER1(pszName, __func__, false);
11398 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
11399 25 : papszOptions);
11400 : }
11401 :
11402 : /************************************************************************/
11403 : /* GDALGroupRename() */
11404 : /************************************************************************/
11405 :
11406 : /** Rename the group.
11407 : *
11408 : * This is not implemented by all drivers.
11409 : *
11410 : * Drivers known to implement it: MEM, netCDF.
11411 : *
11412 : * This is the same as the C++ method GDALGroup::Rename()
11413 : *
11414 : * @return true in case of success
11415 : * @since GDAL 3.8
11416 : */
11417 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
11418 : {
11419 45 : VALIDATE_POINTER1(hGroup, __func__, false);
11420 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
11421 45 : return hGroup->m_poImpl->Rename(pszNewName);
11422 : }
11423 :
11424 : /************************************************************************/
11425 : /* GDALGroupSubsetDimensionFromSelection() */
11426 : /************************************************************************/
11427 :
11428 : /** Return a virtual group whose one dimension has been subset according to a
11429 : * selection.
11430 : *
11431 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
11432 : *
11433 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
11434 : */
11435 : GDALGroupH
11436 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
11437 : const char *pszSelection,
11438 : CPL_UNUSED CSLConstList papszOptions)
11439 : {
11440 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11441 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
11442 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
11443 42 : std::string(pszSelection));
11444 14 : if (!hNewGroup)
11445 8 : return nullptr;
11446 6 : return new GDALGroupHS(hNewGroup);
11447 : }
11448 :
11449 : /************************************************************************/
11450 : /* GDALMDArrayRelease() */
11451 : /************************************************************************/
11452 :
11453 : /** Release the GDAL in-memory object associated with a GDALMDArray.
11454 : *
11455 : * Note: when applied on a object coming from a driver, this does not
11456 : * destroy the object in the file, database, etc...
11457 : */
11458 1960 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
11459 : {
11460 1960 : delete hMDArray;
11461 1960 : }
11462 :
11463 : /************************************************************************/
11464 : /* GDALMDArrayGetName() */
11465 : /************************************************************************/
11466 :
11467 : /** Return array name.
11468 : *
11469 : * This is the same as the C++ method GDALMDArray::GetName()
11470 : */
11471 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
11472 : {
11473 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11474 83 : return hArray->m_poImpl->GetName().c_str();
11475 : }
11476 :
11477 : /************************************************************************/
11478 : /* GDALMDArrayGetFullName() */
11479 : /************************************************************************/
11480 :
11481 : /** Return array full name.
11482 : *
11483 : * This is the same as the C++ method GDALMDArray::GetFullName()
11484 : */
11485 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
11486 : {
11487 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11488 50 : return hArray->m_poImpl->GetFullName().c_str();
11489 : }
11490 :
11491 : /************************************************************************/
11492 : /* GDALMDArrayGetName() */
11493 : /************************************************************************/
11494 :
11495 : /** Return the total number of values in the array.
11496 : *
11497 : * This is the same as the C++ method
11498 : * GDALAbstractMDArray::GetTotalElementsCount()
11499 : */
11500 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
11501 : {
11502 6 : VALIDATE_POINTER1(hArray, __func__, 0);
11503 6 : return hArray->m_poImpl->GetTotalElementsCount();
11504 : }
11505 :
11506 : /************************************************************************/
11507 : /* GDALMDArrayGetDimensionCount() */
11508 : /************************************************************************/
11509 :
11510 : /** Return the number of dimensions.
11511 : *
11512 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
11513 : */
11514 10104 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
11515 : {
11516 10104 : VALIDATE_POINTER1(hArray, __func__, 0);
11517 10104 : return hArray->m_poImpl->GetDimensionCount();
11518 : }
11519 :
11520 : /************************************************************************/
11521 : /* GDALMDArrayGetDimensions() */
11522 : /************************************************************************/
11523 :
11524 : /** Return the dimensions of the array
11525 : *
11526 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11527 : * array itself needs to be freed, CPLFree() should be called (and
11528 : * GDALDimensionRelease() on individual array members).
11529 : *
11530 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
11531 : *
11532 : * @param hArray Array.
11533 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11534 : *
11535 : * @return an array of *pnCount dimensions.
11536 : */
11537 2226 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
11538 : {
11539 2226 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11540 2226 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11541 2226 : const auto &dims(hArray->m_poImpl->GetDimensions());
11542 : auto ret = static_cast<GDALDimensionH *>(
11543 2226 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11544 6269 : for (size_t i = 0; i < dims.size(); i++)
11545 : {
11546 4043 : ret[i] = new GDALDimensionHS(dims[i]);
11547 : }
11548 2226 : *pnCount = dims.size();
11549 2226 : return ret;
11550 : }
11551 :
11552 : /************************************************************************/
11553 : /* GDALReleaseDimensions() */
11554 : /************************************************************************/
11555 :
11556 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
11557 : *
11558 : * @param dims return pointer of above methods
11559 : * @param nCount *pnCount value returned by above methods
11560 : */
11561 2299 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
11562 : {
11563 6499 : for (size_t i = 0; i < nCount; i++)
11564 : {
11565 4200 : delete dims[i];
11566 : }
11567 2299 : CPLFree(dims);
11568 2299 : }
11569 :
11570 : /************************************************************************/
11571 : /* GDALMDArrayGetDataType() */
11572 : /************************************************************************/
11573 :
11574 : /** Return the data type
11575 : *
11576 : * The return must be freed with GDALExtendedDataTypeRelease().
11577 : */
11578 3816 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
11579 : {
11580 3816 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11581 : return new GDALExtendedDataTypeHS(
11582 3816 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
11583 : }
11584 :
11585 : /************************************************************************/
11586 : /* GDALMDArrayRead() */
11587 : /************************************************************************/
11588 :
11589 : /** Read part or totality of a multidimensional array.
11590 : *
11591 : * This is the same as the C++ method GDALAbstractMDArray::Read()
11592 : *
11593 : * @return TRUE in case of success.
11594 : */
11595 1898 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11596 : const size_t *count, const GInt64 *arrayStep,
11597 : const GPtrDiff_t *bufferStride,
11598 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
11599 : const void *pDstBufferAllocStart,
11600 : size_t nDstBufferAllocSize)
11601 : {
11602 1898 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11603 1898 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11604 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11605 : {
11606 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11607 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11608 : }
11609 1898 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11610 1898 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
11611 : // coverity[var_deref_model]
11612 3796 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
11613 1898 : *(bufferDataType->m_poImpl), pDstBuffer,
11614 1898 : pDstBufferAllocStart, nDstBufferAllocSize);
11615 : }
11616 :
11617 : /************************************************************************/
11618 : /* GDALMDArrayWrite() */
11619 : /************************************************************************/
11620 :
11621 : /** Write part or totality of a multidimensional array.
11622 : *
11623 : * This is the same as the C++ method GDALAbstractMDArray::Write()
11624 : *
11625 : * @return TRUE in case of success.
11626 : */
11627 533 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11628 : const size_t *count, const GInt64 *arrayStep,
11629 : const GPtrDiff_t *bufferStride,
11630 : GDALExtendedDataTypeH bufferDataType,
11631 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
11632 : size_t nSrcBufferAllocSize)
11633 : {
11634 533 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11635 533 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11636 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11637 : {
11638 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11639 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11640 : }
11641 533 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11642 533 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
11643 : // coverity[var_deref_model]
11644 1066 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
11645 533 : bufferStride, *(bufferDataType->m_poImpl),
11646 : pSrcBuffer, pSrcBufferAllocStart,
11647 533 : nSrcBufferAllocSize);
11648 : }
11649 :
11650 : /************************************************************************/
11651 : /* GDALMDArrayAdviseRead() */
11652 : /************************************************************************/
11653 :
11654 : /** Advise driver of upcoming read requests.
11655 : *
11656 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11657 : *
11658 : * @return TRUE in case of success.
11659 : *
11660 : * @since GDAL 3.2
11661 : */
11662 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11663 : const size_t *count)
11664 : {
11665 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
11666 : }
11667 :
11668 : /************************************************************************/
11669 : /* GDALMDArrayAdviseReadEx() */
11670 : /************************************************************************/
11671 :
11672 : /** Advise driver of upcoming read requests.
11673 : *
11674 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11675 : *
11676 : * @return TRUE in case of success.
11677 : *
11678 : * @since GDAL 3.4
11679 : */
11680 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11681 : const size_t *count, CSLConstList papszOptions)
11682 : {
11683 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11684 : // coverity[var_deref_model]
11685 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
11686 : }
11687 :
11688 : /************************************************************************/
11689 : /* GDALMDArrayGetAttribute() */
11690 : /************************************************************************/
11691 :
11692 : /** Return an attribute by its name.
11693 : *
11694 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11695 : *
11696 : * The returned attribute must be freed with GDALAttributeRelease().
11697 : */
11698 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
11699 : {
11700 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11701 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11702 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
11703 119 : if (attr)
11704 110 : return new GDALAttributeHS(attr);
11705 9 : return nullptr;
11706 : }
11707 :
11708 : /************************************************************************/
11709 : /* GDALMDArrayGetAttributes() */
11710 : /************************************************************************/
11711 :
11712 : /** Return the list of attributes contained in this array.
11713 : *
11714 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11715 : * array itself needs to be freed, CPLFree() should be called (and
11716 : * GDALAttributeRelease() on individual array members).
11717 : *
11718 : * This is the same as the C++ method GDALMDArray::GetAttributes().
11719 : *
11720 : * @param hArray Array.
11721 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11722 : * @param papszOptions Driver specific options determining how attributes
11723 : * should be retrieved. Pass nullptr for default behavior.
11724 : *
11725 : * @return an array of *pnCount attributes.
11726 : */
11727 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
11728 : CSLConstList papszOptions)
11729 : {
11730 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11731 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11732 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
11733 : auto ret = static_cast<GDALAttributeH *>(
11734 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11735 187 : for (size_t i = 0; i < attrs.size(); i++)
11736 : {
11737 129 : ret[i] = new GDALAttributeHS(attrs[i]);
11738 : }
11739 58 : *pnCount = attrs.size();
11740 58 : return ret;
11741 : }
11742 :
11743 : /************************************************************************/
11744 : /* GDALMDArrayCreateAttribute() */
11745 : /************************************************************************/
11746 :
11747 : /** Create a attribute within an array.
11748 : *
11749 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
11750 : *
11751 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11752 : */
11753 149 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
11754 : const char *pszName,
11755 : size_t nDimensions,
11756 : const GUInt64 *panDimensions,
11757 : GDALExtendedDataTypeH hEDT,
11758 : CSLConstList papszOptions)
11759 : {
11760 149 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11761 149 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11762 149 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11763 298 : std::vector<GUInt64> dims;
11764 149 : dims.reserve(nDimensions);
11765 174 : for (size_t i = 0; i < nDimensions; i++)
11766 25 : dims.push_back(panDimensions[i]);
11767 149 : auto ret = hArray->m_poImpl->CreateAttribute(
11768 447 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11769 149 : if (!ret)
11770 9 : return nullptr;
11771 140 : return new GDALAttributeHS(ret);
11772 : }
11773 :
11774 : /************************************************************************/
11775 : /* GDALMDArrayDeleteAttribute() */
11776 : /************************************************************************/
11777 :
11778 : /** Delete an attribute from an array.
11779 : *
11780 : * After this call, if a previously obtained instance of the deleted object
11781 : * is still alive, no method other than for freeing it should be invoked.
11782 : *
11783 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
11784 : *
11785 : * @return true in case of success.
11786 : * @since GDAL 3.8
11787 : */
11788 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
11789 : CSLConstList papszOptions)
11790 : {
11791 24 : VALIDATE_POINTER1(hArray, __func__, false);
11792 24 : VALIDATE_POINTER1(pszName, __func__, false);
11793 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
11794 24 : papszOptions);
11795 : }
11796 :
11797 : /************************************************************************/
11798 : /* GDALMDArrayGetRawNoDataValue() */
11799 : /************************************************************************/
11800 :
11801 : /** Return the nodata value as a "raw" value.
11802 : *
11803 : * The value returned might be nullptr in case of no nodata value. When
11804 : * a nodata value is registered, a non-nullptr will be returned whose size in
11805 : * bytes is GetDataType().GetSize().
11806 : *
11807 : * The returned value should not be modified or freed.
11808 : *
11809 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
11810 : *
11811 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
11812 : */
11813 74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
11814 : {
11815 74 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11816 74 : return hArray->m_poImpl->GetRawNoDataValue();
11817 : }
11818 :
11819 : /************************************************************************/
11820 : /* GDALMDArrayGetNoDataValueAsDouble() */
11821 : /************************************************************************/
11822 :
11823 : /** Return the nodata value as a double.
11824 : *
11825 : * The value returned might be nullptr in case of no nodata value. When
11826 : * a nodata value is registered, a non-nullptr will be returned whose size in
11827 : * bytes is GetDataType().GetSize().
11828 : *
11829 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
11830 : *
11831 : * @param hArray Array handle.
11832 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11833 : * if a nodata value exists and can be converted to double. Might be nullptr.
11834 : *
11835 : * @return the nodata value as a double. A 0.0 value might also indicate the
11836 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
11837 : * will be set to false then).
11838 : */
11839 118 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
11840 : int *pbHasNoDataValue)
11841 : {
11842 118 : VALIDATE_POINTER1(hArray, __func__, 0);
11843 118 : bool bHasNodataValue = false;
11844 118 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
11845 118 : if (pbHasNoDataValue)
11846 118 : *pbHasNoDataValue = bHasNodataValue;
11847 118 : return ret;
11848 : }
11849 :
11850 : /************************************************************************/
11851 : /* GDALMDArrayGetNoDataValueAsInt64() */
11852 : /************************************************************************/
11853 :
11854 : /** Return the nodata value as a Int64.
11855 : *
11856 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
11857 : *
11858 : * @param hArray Array handle.
11859 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11860 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
11861 : *
11862 : * @return the nodata value as a Int64.
11863 : * @since GDAL 3.5
11864 : */
11865 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
11866 : int *pbHasNoDataValue)
11867 : {
11868 11 : VALIDATE_POINTER1(hArray, __func__, 0);
11869 11 : bool bHasNodataValue = false;
11870 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
11871 11 : if (pbHasNoDataValue)
11872 11 : *pbHasNoDataValue = bHasNodataValue;
11873 11 : return ret;
11874 : }
11875 :
11876 : /************************************************************************/
11877 : /* GDALMDArrayGetNoDataValueAsUInt64() */
11878 : /************************************************************************/
11879 :
11880 : /** Return the nodata value as a UInt64.
11881 : *
11882 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
11883 : *
11884 : * @param hArray Array handle.
11885 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11886 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
11887 : *
11888 : * @return the nodata value as a UInt64.
11889 : * @since GDAL 3.5
11890 : */
11891 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
11892 : int *pbHasNoDataValue)
11893 : {
11894 7 : VALIDATE_POINTER1(hArray, __func__, 0);
11895 7 : bool bHasNodataValue = false;
11896 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
11897 7 : if (pbHasNoDataValue)
11898 7 : *pbHasNoDataValue = bHasNodataValue;
11899 7 : return ret;
11900 : }
11901 :
11902 : /************************************************************************/
11903 : /* GDALMDArraySetRawNoDataValue() */
11904 : /************************************************************************/
11905 :
11906 : /** Set the nodata value as a "raw" value.
11907 : *
11908 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
11909 : * void*).
11910 : *
11911 : * @return TRUE in case of success.
11912 : */
11913 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
11914 : {
11915 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11916 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
11917 : }
11918 :
11919 : /************************************************************************/
11920 : /* GDALMDArraySetNoDataValueAsDouble() */
11921 : /************************************************************************/
11922 :
11923 : /** Set the nodata value as a double.
11924 : *
11925 : * If the natural data type of the attribute/array is not double, type
11926 : * conversion will occur to the type returned by GetDataType().
11927 : *
11928 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
11929 : *
11930 : * @return TRUE in case of success.
11931 : */
11932 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
11933 : {
11934 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11935 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
11936 : }
11937 :
11938 : /************************************************************************/
11939 : /* GDALMDArraySetNoDataValueAsInt64() */
11940 : /************************************************************************/
11941 :
11942 : /** Set the nodata value as a Int64.
11943 : *
11944 : * If the natural data type of the attribute/array is not Int64, type conversion
11945 : * will occur to the type returned by GetDataType().
11946 : *
11947 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
11948 : *
11949 : * @return TRUE in case of success.
11950 : * @since GDAL 3.5
11951 : */
11952 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
11953 : {
11954 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11955 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
11956 : }
11957 :
11958 : /************************************************************************/
11959 : /* GDALMDArraySetNoDataValueAsUInt64() */
11960 : /************************************************************************/
11961 :
11962 : /** Set the nodata value as a UInt64.
11963 : *
11964 : * If the natural data type of the attribute/array is not UInt64, type
11965 : * conversion will occur to the type returned by GetDataType().
11966 : *
11967 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
11968 : *
11969 : * @return TRUE in case of success.
11970 : * @since GDAL 3.5
11971 : */
11972 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
11973 : uint64_t nNoDataValue)
11974 : {
11975 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11976 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
11977 : }
11978 :
11979 : /************************************************************************/
11980 : /* GDALMDArrayResize() */
11981 : /************************************************************************/
11982 :
11983 : /** Resize an array to new dimensions.
11984 : *
11985 : * Not all drivers may allow this operation, and with restrictions (e.g.
11986 : * for netCDF, this is limited to growing of "unlimited" dimensions)
11987 : *
11988 : * Resizing a dimension used in other arrays will cause those other arrays
11989 : * to be resized.
11990 : *
11991 : * This is the same as the C++ method GDALMDArray::Resize().
11992 : *
11993 : * @param hArray Array.
11994 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
11995 : * new size of each indexing dimension.
11996 : * @param papszOptions Options. (Driver specific)
11997 : * @return true in case of success.
11998 : * @since GDAL 3.7
11999 : */
12000 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12001 : CSLConstList papszOptions)
12002 : {
12003 42 : VALIDATE_POINTER1(hArray, __func__, false);
12004 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12005 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12006 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12007 : {
12008 83 : anNewDimSizes[i] = panNewDimSizes[i];
12009 : }
12010 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12011 : }
12012 :
12013 : /************************************************************************/
12014 : /* GDALMDArraySetScale() */
12015 : /************************************************************************/
12016 :
12017 : /** Set the scale value to apply to raw values.
12018 : *
12019 : * unscaled_value = raw_value * GetScale() + GetOffset()
12020 : *
12021 : * This is the same as the C++ method GDALMDArray::SetScale().
12022 : *
12023 : * @return TRUE in case of success.
12024 : */
12025 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12026 : {
12027 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12028 0 : return hArray->m_poImpl->SetScale(dfScale);
12029 : }
12030 :
12031 : /************************************************************************/
12032 : /* GDALMDArraySetScaleEx() */
12033 : /************************************************************************/
12034 :
12035 : /** Set the scale value to apply to raw values.
12036 : *
12037 : * unscaled_value = raw_value * GetScale() + GetOffset()
12038 : *
12039 : * This is the same as the C++ method GDALMDArray::SetScale().
12040 : *
12041 : * @return TRUE in case of success.
12042 : * @since GDAL 3.3
12043 : */
12044 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12045 : GDALDataType eStorageType)
12046 : {
12047 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12048 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12049 : }
12050 :
12051 : /************************************************************************/
12052 : /* GDALMDArraySetOffset() */
12053 : /************************************************************************/
12054 :
12055 : /** Set the scale value to apply to raw values.
12056 : *
12057 : * unscaled_value = raw_value * GetScale() + GetOffset()
12058 : *
12059 : * This is the same as the C++ method GDALMDArray::SetOffset().
12060 : *
12061 : * @return TRUE in case of success.
12062 : */
12063 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12064 : {
12065 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12066 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12067 : }
12068 :
12069 : /************************************************************************/
12070 : /* GDALMDArraySetOffsetEx() */
12071 : /************************************************************************/
12072 :
12073 : /** Set the scale value to apply to raw values.
12074 : *
12075 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12076 : *
12077 : * This is the same as the C++ method GDALMDArray::SetOffset().
12078 : *
12079 : * @return TRUE in case of success.
12080 : * @since GDAL 3.3
12081 : */
12082 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12083 : GDALDataType eStorageType)
12084 : {
12085 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12086 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12087 : }
12088 :
12089 : /************************************************************************/
12090 : /* GDALMDArrayGetScale() */
12091 : /************************************************************************/
12092 :
12093 : /** Get the scale value to apply to raw values.
12094 : *
12095 : * unscaled_value = raw_value * GetScale() + GetOffset()
12096 : *
12097 : * This is the same as the C++ method GDALMDArray::GetScale().
12098 : *
12099 : * @return the scale value
12100 : */
12101 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12102 : {
12103 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12104 103 : bool bHasValue = false;
12105 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12106 103 : if (pbHasValue)
12107 103 : *pbHasValue = bHasValue;
12108 103 : return dfRet;
12109 : }
12110 :
12111 : /************************************************************************/
12112 : /* GDALMDArrayGetScaleEx() */
12113 : /************************************************************************/
12114 :
12115 : /** Get the scale value to apply to raw values.
12116 : *
12117 : * unscaled_value = raw_value * GetScale() + GetScale()
12118 : *
12119 : * This is the same as the C++ method GDALMDArray::GetScale().
12120 : *
12121 : * @return the scale value
12122 : * @since GDAL 3.3
12123 : */
12124 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12125 : GDALDataType *peStorageType)
12126 : {
12127 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12128 5 : bool bHasValue = false;
12129 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12130 5 : if (pbHasValue)
12131 5 : *pbHasValue = bHasValue;
12132 5 : return dfRet;
12133 : }
12134 :
12135 : /************************************************************************/
12136 : /* GDALMDArrayGetOffset() */
12137 : /************************************************************************/
12138 :
12139 : /** Get the scale value to apply to raw values.
12140 : *
12141 : * unscaled_value = raw_value * GetScale() + GetOffset()
12142 : *
12143 : * This is the same as the C++ method GDALMDArray::GetOffset().
12144 : *
12145 : * @return the scale value
12146 : */
12147 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12148 : {
12149 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12150 100 : bool bHasValue = false;
12151 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12152 100 : if (pbHasValue)
12153 100 : *pbHasValue = bHasValue;
12154 100 : return dfRet;
12155 : }
12156 :
12157 : /************************************************************************/
12158 : /* GDALMDArrayGetOffsetEx() */
12159 : /************************************************************************/
12160 :
12161 : /** Get the scale value to apply to raw values.
12162 : *
12163 : * unscaled_value = raw_value * GetScale() + GetOffset()
12164 : *
12165 : * This is the same as the C++ method GDALMDArray::GetOffset().
12166 : *
12167 : * @return the scale value
12168 : * @since GDAL 3.3
12169 : */
12170 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12171 : GDALDataType *peStorageType)
12172 : {
12173 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12174 5 : bool bHasValue = false;
12175 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12176 5 : if (pbHasValue)
12177 5 : *pbHasValue = bHasValue;
12178 5 : return dfRet;
12179 : }
12180 :
12181 : /************************************************************************/
12182 : /* GDALMDArrayGetBlockSize() */
12183 : /************************************************************************/
12184 :
12185 : /** Return the "natural" block size of the array along all dimensions.
12186 : *
12187 : * Some drivers might organize the array in tiles/blocks and reading/writing
12188 : * aligned on those tile/block boundaries will be more efficient.
12189 : *
12190 : * The returned number of elements in the vector is the same as
12191 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12192 : * the natural block size along the considered dimension.
12193 : * "Flat" arrays will typically return a vector of values set to 0.
12194 : *
12195 : * The default implementation will return a vector of values set to 0.
12196 : *
12197 : * This method is used by GetProcessingChunkSize().
12198 : *
12199 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12200 : * theoretical case of a 32-bit platform, this might exceed its size_t
12201 : * allocation capabilities.
12202 : *
12203 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12204 : *
12205 : * @return the block size, in number of elements along each dimension.
12206 : */
12207 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12208 : {
12209 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12210 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12211 93 : auto res = hArray->m_poImpl->GetBlockSize();
12212 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12213 285 : for (size_t i = 0; i < res.size(); i++)
12214 : {
12215 192 : ret[i] = res[i];
12216 : }
12217 93 : *pnCount = res.size();
12218 93 : return ret;
12219 : }
12220 :
12221 : /***********************************************************************/
12222 : /* GDALMDArrayGetProcessingChunkSize() */
12223 : /************************************************************************/
12224 :
12225 : /** \brief Return an optimal chunk size for read/write operations, given the
12226 : * natural block size and memory constraints specified.
12227 : *
12228 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12229 : * multiple of those returned by GetBlockSize() (unless the block define by
12230 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12231 : * returned by this method).
12232 : *
12233 : * This is the same as the C++ method
12234 : * GDALAbstractMDArray::GetProcessingChunkSize().
12235 : *
12236 : * @param hArray Array.
12237 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12238 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12239 : * chunk.
12240 : *
12241 : * @return the chunk size, in number of elements along each dimension.
12242 : */
12243 :
12244 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12245 : size_t nMaxChunkMemory)
12246 : {
12247 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12248 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12249 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12250 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12251 3 : for (size_t i = 0; i < res.size(); i++)
12252 : {
12253 2 : ret[i] = res[i];
12254 : }
12255 1 : *pnCount = res.size();
12256 1 : return ret;
12257 : }
12258 :
12259 : /************************************************************************/
12260 : /* GDALMDArrayGetStructuralInfo() */
12261 : /************************************************************************/
12262 :
12263 : /** Return structural information on the array.
12264 : *
12265 : * This may be the compression, etc..
12266 : *
12267 : * The return value should not be freed and is valid until GDALMDArray is
12268 : * released or this function called again.
12269 : *
12270 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12271 : */
12272 6 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12273 : {
12274 6 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12275 6 : return hArray->m_poImpl->GetStructuralInfo();
12276 : }
12277 :
12278 : /************************************************************************/
12279 : /* GDALMDArrayGetView() */
12280 : /************************************************************************/
12281 :
12282 : /** Return a view of the array using slicing or field access.
12283 : *
12284 : * The returned object should be released with GDALMDArrayRelease().
12285 : *
12286 : * This is the same as the C++ method GDALMDArray::GetView().
12287 : */
12288 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12289 : {
12290 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12291 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12292 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12293 430 : if (!sliced)
12294 22 : return nullptr;
12295 408 : return new GDALMDArrayHS(sliced);
12296 : }
12297 :
12298 : /************************************************************************/
12299 : /* GDALMDArrayTranspose() */
12300 : /************************************************************************/
12301 :
12302 : /** Return a view of the array whose axis have been reordered.
12303 : *
12304 : * The returned object should be released with GDALMDArrayRelease().
12305 : *
12306 : * This is the same as the C++ method GDALMDArray::Transpose().
12307 : */
12308 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12309 : const int *panMapNewAxisToOldAxis)
12310 : {
12311 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12312 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12313 44 : if (nNewAxisCount)
12314 : {
12315 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12316 : nNewAxisCount * sizeof(int));
12317 : }
12318 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12319 44 : if (!reordered)
12320 7 : return nullptr;
12321 37 : return new GDALMDArrayHS(reordered);
12322 : }
12323 :
12324 : /************************************************************************/
12325 : /* GDALMDArrayGetUnscaled() */
12326 : /************************************************************************/
12327 :
12328 : /** Return an array that is the unscaled version of the current one.
12329 : *
12330 : * That is each value of the unscaled array will be
12331 : * unscaled_value = raw_value * GetScale() + GetOffset()
12332 : *
12333 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
12334 : * from unscaled values to raw values.
12335 : *
12336 : * The returned object should be released with GDALMDArrayRelease().
12337 : *
12338 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
12339 : */
12340 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
12341 : {
12342 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12343 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
12344 13 : if (!unscaled)
12345 0 : return nullptr;
12346 13 : return new GDALMDArrayHS(unscaled);
12347 : }
12348 :
12349 : /************************************************************************/
12350 : /* GDALMDArrayGetMask() */
12351 : /************************************************************************/
12352 :
12353 : /** Return an array that is a mask for the current array
12354 : *
12355 : * This array will be of type Byte, with values set to 0 to indicate invalid
12356 : * pixels of the current array, and values set to 1 to indicate valid pixels.
12357 : *
12358 : * The returned object should be released with GDALMDArrayRelease().
12359 : *
12360 : * This is the same as the C++ method GDALMDArray::GetMask().
12361 : */
12362 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
12363 : {
12364 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12365 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
12366 35 : if (!unscaled)
12367 7 : return nullptr;
12368 28 : return new GDALMDArrayHS(unscaled);
12369 : }
12370 :
12371 : /************************************************************************/
12372 : /* GDALMDArrayGetResampled() */
12373 : /************************************************************************/
12374 :
12375 : /** Return an array that is a resampled / reprojected view of the current array
12376 : *
12377 : * This is the same as the C++ method GDALMDArray::GetResampled().
12378 : *
12379 : * Currently this method can only resample along the last 2 dimensions, unless
12380 : * orthorectifying a NASA EMIT dataset.
12381 : *
12382 : * The returned object should be released with GDALMDArrayRelease().
12383 : *
12384 : * @since 3.4
12385 : */
12386 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
12387 : const GDALDimensionH *pahNewDims,
12388 : GDALRIOResampleAlg resampleAlg,
12389 : OGRSpatialReferenceH hTargetSRS,
12390 : CSLConstList papszOptions)
12391 : {
12392 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12393 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
12394 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
12395 112 : for (size_t i = 0; i < nNewDimCount; ++i)
12396 : {
12397 78 : if (pahNewDims[i])
12398 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
12399 : }
12400 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
12401 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
12402 68 : papszOptions);
12403 34 : if (!poNewArray)
12404 8 : return nullptr;
12405 26 : return new GDALMDArrayHS(poNewArray);
12406 : }
12407 :
12408 : /************************************************************************/
12409 : /* GDALMDArraySetUnit() */
12410 : /************************************************************************/
12411 :
12412 : /** Set the variable unit.
12413 : *
12414 : * Values should conform as much as possible with those allowed by
12415 : * the NetCDF CF conventions:
12416 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12417 : * but others might be returned.
12418 : *
12419 : * Few examples are "meter", "degrees", "second", ...
12420 : * Empty value means unknown.
12421 : *
12422 : * This is the same as the C function GDALMDArraySetUnit()
12423 : *
12424 : * @param hArray array.
12425 : * @param pszUnit unit name.
12426 : * @return TRUE in case of success.
12427 : */
12428 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
12429 : {
12430 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12431 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
12432 : }
12433 :
12434 : /************************************************************************/
12435 : /* GDALMDArrayGetUnit() */
12436 : /************************************************************************/
12437 :
12438 : /** Return the array unit.
12439 : *
12440 : * Values should conform as much as possible with those allowed by
12441 : * the NetCDF CF conventions:
12442 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12443 : * but others might be returned.
12444 : *
12445 : * Few examples are "meter", "degrees", "second", ...
12446 : * Empty value means unknown.
12447 : *
12448 : * The return value should not be freed and is valid until GDALMDArray is
12449 : * released or this function called again.
12450 : *
12451 : * This is the same as the C++ method GDALMDArray::GetUnit().
12452 : */
12453 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
12454 : {
12455 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12456 111 : return hArray->m_poImpl->GetUnit().c_str();
12457 : }
12458 :
12459 : /************************************************************************/
12460 : /* GDALMDArrayGetSpatialRef() */
12461 : /************************************************************************/
12462 :
12463 : /** Assign a spatial reference system object to the array.
12464 : *
12465 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
12466 : * @return TRUE in case of success.
12467 : */
12468 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
12469 : {
12470 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12471 60 : return hArray->m_poImpl->SetSpatialRef(
12472 60 : OGRSpatialReference::FromHandle(hSRS));
12473 : }
12474 :
12475 : /************************************************************************/
12476 : /* GDALMDArrayGetSpatialRef() */
12477 : /************************************************************************/
12478 :
12479 : /** Return the spatial reference system object associated with the array.
12480 : *
12481 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
12482 : *
12483 : * The returned object must be freed with OSRDestroySpatialReference().
12484 : */
12485 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
12486 : {
12487 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12488 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
12489 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
12490 : }
12491 :
12492 : /************************************************************************/
12493 : /* GDALMDArrayGetStatistics() */
12494 : /************************************************************************/
12495 :
12496 : /**
12497 : * \brief Fetch statistics.
12498 : *
12499 : * This is the same as the C++ method GDALMDArray::GetStatistics().
12500 : *
12501 : * @since GDAL 3.2
12502 : */
12503 :
12504 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
12505 : int bApproxOK, int bForce, double *pdfMin,
12506 : double *pdfMax, double *pdfMean,
12507 : double *pdfStdDev, GUInt64 *pnValidCount,
12508 : GDALProgressFunc pfnProgress,
12509 : void *pProgressData)
12510 : {
12511 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
12512 30 : return hArray->m_poImpl->GetStatistics(
12513 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
12514 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
12515 : }
12516 :
12517 : /************************************************************************/
12518 : /* GDALMDArrayComputeStatistics() */
12519 : /************************************************************************/
12520 :
12521 : /**
12522 : * \brief Compute statistics.
12523 : *
12524 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12525 : *
12526 : * @since GDAL 3.2
12527 : * @see GDALMDArrayComputeStatisticsEx()
12528 : */
12529 :
12530 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12531 : int bApproxOK, double *pdfMin, double *pdfMax,
12532 : double *pdfMean, double *pdfStdDev,
12533 : GUInt64 *pnValidCount,
12534 : GDALProgressFunc pfnProgress,
12535 : void *pProgressData)
12536 : {
12537 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12538 0 : return hArray->m_poImpl->ComputeStatistics(
12539 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12540 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
12541 : }
12542 :
12543 : /************************************************************************/
12544 : /* GDALMDArrayComputeStatisticsEx() */
12545 : /************************************************************************/
12546 :
12547 : /**
12548 : * \brief Compute statistics.
12549 : *
12550 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
12551 : *
12552 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12553 : *
12554 : * @since GDAL 3.8
12555 : */
12556 :
12557 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12558 : int bApproxOK, double *pdfMin,
12559 : double *pdfMax, double *pdfMean,
12560 : double *pdfStdDev, GUInt64 *pnValidCount,
12561 : GDALProgressFunc pfnProgress,
12562 : void *pProgressData,
12563 : CSLConstList papszOptions)
12564 : {
12565 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12566 8 : return hArray->m_poImpl->ComputeStatistics(
12567 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12568 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
12569 : }
12570 :
12571 : /************************************************************************/
12572 : /* GDALMDArrayGetCoordinateVariables() */
12573 : /************************************************************************/
12574 :
12575 : /** Return coordinate variables.
12576 : *
12577 : * The returned array must be freed with GDALReleaseArrays(). If only the array
12578 : * itself needs to be freed, CPLFree() should be called (and
12579 : * GDALMDArrayRelease() on individual array members).
12580 : *
12581 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
12582 : *
12583 : * @param hArray Array.
12584 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12585 : *
12586 : * @return an array of *pnCount arrays.
12587 : * @since 3.4
12588 : */
12589 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
12590 : size_t *pnCount)
12591 : {
12592 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12593 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12594 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
12595 : auto ret = static_cast<GDALMDArrayH *>(
12596 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
12597 29 : for (size_t i = 0; i < coordinates.size(); i++)
12598 : {
12599 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
12600 : }
12601 13 : *pnCount = coordinates.size();
12602 13 : return ret;
12603 : }
12604 :
12605 : /************************************************************************/
12606 : /* GDALMDArrayGetGridded() */
12607 : /************************************************************************/
12608 :
12609 : /** Return a gridded array from scattered point data, that is from an array
12610 : * whose last dimension is the indexing variable of X and Y arrays.
12611 : *
12612 : * The returned object should be released with GDALMDArrayRelease().
12613 : *
12614 : * This is the same as the C++ method GDALMDArray::GetGridded().
12615 : *
12616 : * @since GDAL 3.7
12617 : */
12618 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
12619 : const char *pszGridOptions,
12620 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
12621 : CSLConstList papszOptions)
12622 : {
12623 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12624 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
12625 22 : auto gridded = hArray->m_poImpl->GetGridded(
12626 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
12627 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
12628 22 : if (!gridded)
12629 19 : return nullptr;
12630 3 : return new GDALMDArrayHS(gridded);
12631 : }
12632 :
12633 : /************************************************************************/
12634 : /* GDALMDArrayGetMeshGrid() */
12635 : /************************************************************************/
12636 :
12637 : /** Return a list of multidimensional arrays from a list of one-dimensional
12638 : * arrays.
12639 : *
12640 : * This is typically used to transform one-dimensional longitude, latitude
12641 : * arrays into 2D ones.
12642 : *
12643 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
12644 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
12645 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
12646 : * repeated to fill the matrix along the first dimension for x1, the second
12647 : * for x2 and so on.
12648 : *
12649 : * For example, if x = [1, 2], and y = [3, 4, 5],
12650 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
12651 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
12652 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
12653 : *
12654 : * and
12655 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
12656 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
12657 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
12658 : *
12659 : * The currently supported options are:
12660 : * <ul>
12661 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
12662 : * output.
12663 : * </li>
12664 : * </ul>
12665 : *
12666 : * This is the same as
12667 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
12668 : * function.
12669 : *
12670 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
12671 : * If only the array itself needs to be freed, CPLFree() should be called
12672 : * (and GDALMDArrayRelease() on individual array members).
12673 : *
12674 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
12675 : *
12676 : * @param pahInputArrays Input arrays
12677 : * @param nCountInputArrays Number of input arrays
12678 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
12679 : * @param papszOptions NULL, or NULL terminated list of options.
12680 : *
12681 : * @return an array of *pnCountOutputArrays arrays.
12682 : * @since 3.10
12683 : */
12684 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
12685 : size_t nCountInputArrays,
12686 : size_t *pnCountOutputArrays,
12687 : CSLConstList papszOptions)
12688 : {
12689 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
12690 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
12691 :
12692 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
12693 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
12694 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
12695 :
12696 : const auto apoOutputArrays =
12697 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
12698 : auto ret = static_cast<GDALMDArrayH *>(
12699 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
12700 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
12701 : {
12702 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
12703 : }
12704 7 : *pnCountOutputArrays = apoOutputArrays.size();
12705 7 : return ret;
12706 : }
12707 :
12708 : /************************************************************************/
12709 : /* GDALReleaseArrays() */
12710 : /************************************************************************/
12711 :
12712 : /** Free the return of GDALMDArrayGetCoordinateVariables()
12713 : *
12714 : * @param arrays return pointer of above methods
12715 : * @param nCount *pnCount value returned by above methods
12716 : */
12717 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
12718 : {
12719 46 : for (size_t i = 0; i < nCount; i++)
12720 : {
12721 26 : delete arrays[i];
12722 : }
12723 20 : CPLFree(arrays);
12724 20 : }
12725 :
12726 : /************************************************************************/
12727 : /* GDALMDArrayCache() */
12728 : /************************************************************************/
12729 :
12730 : /**
12731 : * \brief Cache the content of the array into an auxiliary filename.
12732 : *
12733 : * This is the same as the C++ method GDALMDArray::Cache().
12734 : *
12735 : * @since GDAL 3.4
12736 : */
12737 :
12738 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
12739 : {
12740 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12741 7 : return hArray->m_poImpl->Cache(papszOptions);
12742 : }
12743 :
12744 : /************************************************************************/
12745 : /* GDALMDArrayRename() */
12746 : /************************************************************************/
12747 :
12748 : /** Rename the array.
12749 : *
12750 : * This is not implemented by all drivers.
12751 : *
12752 : * Drivers known to implement it: MEM, netCDF, Zarr.
12753 : *
12754 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
12755 : *
12756 : * @return true in case of success
12757 : * @since GDAL 3.8
12758 : */
12759 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
12760 : {
12761 28 : VALIDATE_POINTER1(hArray, __func__, false);
12762 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
12763 28 : return hArray->m_poImpl->Rename(pszNewName);
12764 : }
12765 :
12766 : /************************************************************************/
12767 : /* GDALAttributeRelease() */
12768 : /************************************************************************/
12769 :
12770 : /** Release the GDAL in-memory object associated with a GDALAttribute.
12771 : *
12772 : * Note: when applied on a object coming from a driver, this does not
12773 : * destroy the object in the file, database, etc...
12774 : */
12775 713 : void GDALAttributeRelease(GDALAttributeH hAttr)
12776 : {
12777 713 : delete hAttr;
12778 713 : }
12779 :
12780 : /************************************************************************/
12781 : /* GDALAttributeGetName() */
12782 : /************************************************************************/
12783 :
12784 : /** Return the name of the attribute.
12785 : *
12786 : * The returned pointer is valid until hAttr is released.
12787 : *
12788 : * This is the same as the C++ method GDALAttribute::GetName().
12789 : */
12790 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
12791 : {
12792 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12793 361 : return hAttr->m_poImpl->GetName().c_str();
12794 : }
12795 :
12796 : /************************************************************************/
12797 : /* GDALAttributeGetFullName() */
12798 : /************************************************************************/
12799 :
12800 : /** Return the full name of the attribute.
12801 : *
12802 : * The returned pointer is valid until hAttr is released.
12803 : *
12804 : * This is the same as the C++ method GDALAttribute::GetFullName().
12805 : */
12806 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
12807 : {
12808 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12809 49 : return hAttr->m_poImpl->GetFullName().c_str();
12810 : }
12811 :
12812 : /************************************************************************/
12813 : /* GDALAttributeGetTotalElementsCount() */
12814 : /************************************************************************/
12815 :
12816 : /** Return the total number of values in the attribute.
12817 : *
12818 : * This is the same as the C++ method
12819 : * GDALAbstractMDArray::GetTotalElementsCount()
12820 : */
12821 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
12822 : {
12823 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
12824 176 : return hAttr->m_poImpl->GetTotalElementsCount();
12825 : }
12826 :
12827 : /************************************************************************/
12828 : /* GDALAttributeGetDimensionCount() */
12829 : /************************************************************************/
12830 :
12831 : /** Return the number of dimensions.
12832 : *
12833 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12834 : */
12835 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
12836 : {
12837 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
12838 12 : return hAttr->m_poImpl->GetDimensionCount();
12839 : }
12840 :
12841 : /************************************************************************/
12842 : /* GDALAttributeGetDimensionsSize() */
12843 : /************************************************************************/
12844 :
12845 : /** Return the dimension sizes of the attribute.
12846 : *
12847 : * The returned array must be freed with CPLFree()
12848 : *
12849 : * @param hAttr Attribute.
12850 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12851 : *
12852 : * @return an array of *pnCount values.
12853 : */
12854 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
12855 : {
12856 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12857 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12858 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
12859 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
12860 22 : for (size_t i = 0; i < dims.size(); i++)
12861 : {
12862 11 : ret[i] = dims[i]->GetSize();
12863 : }
12864 11 : *pnCount = dims.size();
12865 11 : return ret;
12866 : }
12867 :
12868 : /************************************************************************/
12869 : /* GDALAttributeGetDataType() */
12870 : /************************************************************************/
12871 :
12872 : /** Return the data type
12873 : *
12874 : * The return must be freed with GDALExtendedDataTypeRelease().
12875 : */
12876 426 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
12877 : {
12878 426 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12879 : return new GDALExtendedDataTypeHS(
12880 426 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
12881 : }
12882 :
12883 : /************************************************************************/
12884 : /* GDALAttributeReadAsRaw() */
12885 : /************************************************************************/
12886 :
12887 : /** Return the raw value of an attribute.
12888 : *
12889 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
12890 : *
12891 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
12892 : *
12893 : * @param hAttr Attribute.
12894 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
12895 : *
12896 : * @return a buffer of *pnSize bytes.
12897 : */
12898 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
12899 : {
12900 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12901 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
12902 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
12903 6 : *pnSize = res.size();
12904 6 : auto ret = res.StealData();
12905 6 : if (!ret)
12906 : {
12907 0 : *pnSize = 0;
12908 0 : return nullptr;
12909 : }
12910 6 : return ret;
12911 : }
12912 :
12913 : /************************************************************************/
12914 : /* GDALAttributeFreeRawResult() */
12915 : /************************************************************************/
12916 :
12917 : /** Free the return of GDALAttributeAsRaw()
12918 : */
12919 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
12920 : CPL_UNUSED size_t nSize)
12921 : {
12922 6 : VALIDATE_POINTER0(hAttr, __func__);
12923 6 : if (raw)
12924 : {
12925 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
12926 6 : const auto nDTSize(dt.GetSize());
12927 6 : GByte *pabyPtr = raw;
12928 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
12929 6 : CPLAssert(nSize == nDTSize * nEltCount);
12930 12 : for (size_t i = 0; i < nEltCount; ++i)
12931 : {
12932 6 : dt.FreeDynamicMemory(pabyPtr);
12933 6 : pabyPtr += nDTSize;
12934 : }
12935 6 : CPLFree(raw);
12936 : }
12937 : }
12938 :
12939 : /************************************************************************/
12940 : /* GDALAttributeReadAsString() */
12941 : /************************************************************************/
12942 :
12943 : /** Return the value of an attribute as a string.
12944 : *
12945 : * The returned string should not be freed, and its lifetime does not
12946 : * excess a next call to ReadAsString() on the same object, or the deletion
12947 : * of the object itself.
12948 : *
12949 : * This function will only return the first element if there are several.
12950 : *
12951 : * This is the same as the C++ method GDALAttribute::ReadAsString()
12952 : *
12953 : * @return a string, or nullptr.
12954 : */
12955 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
12956 : {
12957 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12958 107 : return hAttr->m_poImpl->ReadAsString();
12959 : }
12960 :
12961 : /************************************************************************/
12962 : /* GDALAttributeReadAsInt() */
12963 : /************************************************************************/
12964 :
12965 : /** Return the value of an attribute as a integer.
12966 : *
12967 : * This function will only return the first element if there are several.
12968 : *
12969 : * It can fail if its value can not be converted to integer.
12970 : *
12971 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
12972 : *
12973 : * @return a integer, or INT_MIN in case of error.
12974 : */
12975 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
12976 : {
12977 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
12978 22 : return hAttr->m_poImpl->ReadAsInt();
12979 : }
12980 :
12981 : /************************************************************************/
12982 : /* GDALAttributeReadAsInt64() */
12983 : /************************************************************************/
12984 :
12985 : /** Return the value of an attribute as a int64_t.
12986 : *
12987 : * This function will only return the first element if there are several.
12988 : *
12989 : * It can fail if its value can not be converted to integer.
12990 : *
12991 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
12992 : *
12993 : * @return an int64_t, or INT64_MIN in case of error.
12994 : */
12995 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
12996 : {
12997 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
12998 15 : return hAttr->m_poImpl->ReadAsInt64();
12999 : }
13000 :
13001 : /************************************************************************/
13002 : /* GDALAttributeReadAsDouble() */
13003 : /************************************************************************/
13004 :
13005 : /** Return the value of an attribute as a double.
13006 : *
13007 : * This function will only return the first element if there are several.
13008 : *
13009 : * It can fail if its value can not be converted to double.
13010 : *
13011 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13012 : *
13013 : * @return a double value.
13014 : */
13015 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13016 : {
13017 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13018 40 : return hAttr->m_poImpl->ReadAsDouble();
13019 : }
13020 :
13021 : /************************************************************************/
13022 : /* GDALAttributeReadAsStringArray() */
13023 : /************************************************************************/
13024 :
13025 : /** Return the value of an attribute as an array of strings.
13026 : *
13027 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13028 : *
13029 : * The return value must be freed with CSLDestroy().
13030 : */
13031 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13032 : {
13033 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13034 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13035 : }
13036 :
13037 : /************************************************************************/
13038 : /* GDALAttributeReadAsIntArray() */
13039 : /************************************************************************/
13040 :
13041 : /** Return the value of an attribute as an array of integers.
13042 : *
13043 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13044 : *
13045 : * @param hAttr Attribute
13046 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13047 : * @return array to be freed with CPLFree(), or nullptr.
13048 : */
13049 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13050 : {
13051 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13052 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13053 15 : *pnCount = 0;
13054 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13055 15 : if (tmp.empty())
13056 0 : return nullptr;
13057 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13058 15 : if (!ret)
13059 0 : return nullptr;
13060 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13061 15 : *pnCount = tmp.size();
13062 15 : return ret;
13063 : }
13064 :
13065 : /************************************************************************/
13066 : /* GDALAttributeReadAsInt64Array() */
13067 : /************************************************************************/
13068 :
13069 : /** Return the value of an attribute as an array of int64_t.
13070 : *
13071 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13072 : *
13073 : * @param hAttr Attribute
13074 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13075 : * @return array to be freed with CPLFree(), or nullptr.
13076 : */
13077 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13078 : {
13079 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13080 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13081 14 : *pnCount = 0;
13082 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13083 14 : if (tmp.empty())
13084 0 : return nullptr;
13085 : auto ret = static_cast<int64_t *>(
13086 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13087 14 : if (!ret)
13088 0 : return nullptr;
13089 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13090 14 : *pnCount = tmp.size();
13091 14 : return ret;
13092 : }
13093 :
13094 : /************************************************************************/
13095 : /* GDALAttributeReadAsDoubleArray() */
13096 : /************************************************************************/
13097 :
13098 : /** Return the value of an attribute as an array of doubles.
13099 : *
13100 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13101 : *
13102 : * @param hAttr Attribute
13103 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13104 : * @return array to be freed with CPLFree(), or nullptr.
13105 : */
13106 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13107 : {
13108 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13109 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13110 29 : *pnCount = 0;
13111 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13112 29 : if (tmp.empty())
13113 0 : return nullptr;
13114 : auto ret =
13115 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13116 29 : if (!ret)
13117 0 : return nullptr;
13118 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13119 29 : *pnCount = tmp.size();
13120 29 : return ret;
13121 : }
13122 :
13123 : /************************************************************************/
13124 : /* GDALAttributeWriteRaw() */
13125 : /************************************************************************/
13126 :
13127 : /** Write an attribute from raw values expressed in GetDataType()
13128 : *
13129 : * The values should be provided in the type of GetDataType() and there should
13130 : * be exactly GetTotalElementsCount() of them.
13131 : * If GetDataType() is a string, each value should be a char* pointer.
13132 : *
13133 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13134 : *
13135 : * @param hAttr Attribute
13136 : * @param pabyValue Buffer of nLen bytes.
13137 : * @param nLength Size of pabyValue in bytes. Should be equal to
13138 : * GetTotalElementsCount() * GetDataType().GetSize()
13139 : * @return TRUE in case of success.
13140 : */
13141 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13142 : size_t nLength)
13143 : {
13144 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13145 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13146 : }
13147 :
13148 : /************************************************************************/
13149 : /* GDALAttributeWriteString() */
13150 : /************************************************************************/
13151 :
13152 : /** Write an attribute from a string value.
13153 : *
13154 : * Type conversion will be performed if needed. If the attribute contains
13155 : * multiple values, only the first one will be updated.
13156 : *
13157 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13158 : *
13159 : * @param hAttr Attribute
13160 : * @param pszVal Pointer to a string.
13161 : * @return TRUE in case of success.
13162 : */
13163 174 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13164 : {
13165 174 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13166 174 : return hAttr->m_poImpl->Write(pszVal);
13167 : }
13168 :
13169 : /************************************************************************/
13170 : /* GDALAttributeWriteInt() */
13171 : /************************************************************************/
13172 :
13173 : /** Write an attribute from a integer value.
13174 : *
13175 : * Type conversion will be performed if needed. If the attribute contains
13176 : * multiple values, only the first one will be updated.
13177 : *
13178 : * This is the same as the C++ method GDALAttribute::WriteInt()
13179 : *
13180 : * @param hAttr Attribute
13181 : * @param nVal Value.
13182 : * @return TRUE in case of success.
13183 : */
13184 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13185 : {
13186 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13187 22 : return hAttr->m_poImpl->WriteInt(nVal);
13188 : }
13189 :
13190 : /************************************************************************/
13191 : /* GDALAttributeWriteInt64() */
13192 : /************************************************************************/
13193 :
13194 : /** Write an attribute from an int64_t value.
13195 : *
13196 : * Type conversion will be performed if needed. If the attribute contains
13197 : * multiple values, only the first one will be updated.
13198 : *
13199 : * This is the same as the C++ method GDALAttribute::WriteLong()
13200 : *
13201 : * @param hAttr Attribute
13202 : * @param nVal Value.
13203 : * @return TRUE in case of success.
13204 : */
13205 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13206 : {
13207 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13208 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13209 : }
13210 :
13211 : /************************************************************************/
13212 : /* GDALAttributeWriteDouble() */
13213 : /************************************************************************/
13214 :
13215 : /** Write an attribute from a double value.
13216 : *
13217 : * Type conversion will be performed if needed. If the attribute contains
13218 : * multiple values, only the first one will be updated.
13219 : *
13220 : * This is the same as the C++ method GDALAttribute::Write(double);
13221 : *
13222 : * @param hAttr Attribute
13223 : * @param dfVal Value.
13224 : *
13225 : * @return TRUE in case of success.
13226 : */
13227 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13228 : {
13229 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13230 11 : return hAttr->m_poImpl->Write(dfVal);
13231 : }
13232 :
13233 : /************************************************************************/
13234 : /* GDALAttributeWriteStringArray() */
13235 : /************************************************************************/
13236 :
13237 : /** Write an attribute from an array of strings.
13238 : *
13239 : * Type conversion will be performed if needed.
13240 : *
13241 : * Exactly GetTotalElementsCount() strings must be provided
13242 : *
13243 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13244 : *
13245 : * @param hAttr Attribute
13246 : * @param papszValues Array of strings.
13247 : * @return TRUE in case of success.
13248 : */
13249 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13250 : CSLConstList papszValues)
13251 : {
13252 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13253 8 : return hAttr->m_poImpl->Write(papszValues);
13254 : }
13255 :
13256 : /************************************************************************/
13257 : /* GDALAttributeWriteIntArray() */
13258 : /************************************************************************/
13259 :
13260 : /** Write an attribute from an array of int.
13261 : *
13262 : * Type conversion will be performed if needed.
13263 : *
13264 : * Exactly GetTotalElementsCount() strings must be provided
13265 : *
13266 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13267 : * size_t)
13268 : *
13269 : * @param hAttr Attribute
13270 : * @param panValues Array of int.
13271 : * @param nCount Should be equal to GetTotalElementsCount().
13272 : * @return TRUE in case of success.
13273 : */
13274 9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13275 : size_t nCount)
13276 : {
13277 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13278 9 : return hAttr->m_poImpl->Write(panValues, nCount);
13279 : }
13280 :
13281 : /************************************************************************/
13282 : /* GDALAttributeWriteInt64Array() */
13283 : /************************************************************************/
13284 :
13285 : /** Write an attribute from an array of int64_t.
13286 : *
13287 : * Type conversion will be performed if needed.
13288 : *
13289 : * Exactly GetTotalElementsCount() strings must be provided
13290 : *
13291 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13292 : * size_t)
13293 : *
13294 : * @param hAttr Attribute
13295 : * @param panValues Array of int64_t.
13296 : * @param nCount Should be equal to GetTotalElementsCount().
13297 : * @return TRUE in case of success.
13298 : */
13299 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13300 : size_t nCount)
13301 : {
13302 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13303 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13304 : }
13305 :
13306 : /************************************************************************/
13307 : /* GDALAttributeWriteDoubleArray() */
13308 : /************************************************************************/
13309 :
13310 : /** Write an attribute from an array of double.
13311 : *
13312 : * Type conversion will be performed if needed.
13313 : *
13314 : * Exactly GetTotalElementsCount() strings must be provided
13315 : *
13316 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13317 : * size_t)
13318 : *
13319 : * @param hAttr Attribute
13320 : * @param padfValues Array of double.
13321 : * @param nCount Should be equal to GetTotalElementsCount().
13322 : * @return TRUE in case of success.
13323 : */
13324 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
13325 : const double *padfValues, size_t nCount)
13326 : {
13327 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13328 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
13329 : }
13330 :
13331 : /************************************************************************/
13332 : /* GDALAttributeRename() */
13333 : /************************************************************************/
13334 :
13335 : /** Rename the attribute.
13336 : *
13337 : * This is not implemented by all drivers.
13338 : *
13339 : * Drivers known to implement it: MEM, netCDF.
13340 : *
13341 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13342 : *
13343 : * @return true in case of success
13344 : * @since GDAL 3.8
13345 : */
13346 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
13347 : {
13348 27 : VALIDATE_POINTER1(hAttr, __func__, false);
13349 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
13350 27 : return hAttr->m_poImpl->Rename(pszNewName);
13351 : }
13352 :
13353 : /************************************************************************/
13354 : /* GDALDimensionRelease() */
13355 : /************************************************************************/
13356 :
13357 : /** Release the GDAL in-memory object associated with a GDALDimension.
13358 : *
13359 : * Note: when applied on a object coming from a driver, this does not
13360 : * destroy the object in the file, database, etc...
13361 : */
13362 4836 : void GDALDimensionRelease(GDALDimensionH hDim)
13363 : {
13364 4836 : delete hDim;
13365 4836 : }
13366 :
13367 : /************************************************************************/
13368 : /* GDALDimensionGetName() */
13369 : /************************************************************************/
13370 :
13371 : /** Return dimension name.
13372 : *
13373 : * This is the same as the C++ method GDALDimension::GetName()
13374 : */
13375 274 : const char *GDALDimensionGetName(GDALDimensionH hDim)
13376 : {
13377 274 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13378 274 : return hDim->m_poImpl->GetName().c_str();
13379 : }
13380 :
13381 : /************************************************************************/
13382 : /* GDALDimensionGetFullName() */
13383 : /************************************************************************/
13384 :
13385 : /** Return dimension full name.
13386 : *
13387 : * This is the same as the C++ method GDALDimension::GetFullName()
13388 : */
13389 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
13390 : {
13391 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13392 80 : return hDim->m_poImpl->GetFullName().c_str();
13393 : }
13394 :
13395 : /************************************************************************/
13396 : /* GDALDimensionGetType() */
13397 : /************************************************************************/
13398 :
13399 : /** Return dimension type.
13400 : *
13401 : * This is the same as the C++ method GDALDimension::GetType()
13402 : */
13403 52 : const char *GDALDimensionGetType(GDALDimensionH hDim)
13404 : {
13405 52 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13406 52 : return hDim->m_poImpl->GetType().c_str();
13407 : }
13408 :
13409 : /************************************************************************/
13410 : /* GDALDimensionGetDirection() */
13411 : /************************************************************************/
13412 :
13413 : /** Return dimension direction.
13414 : *
13415 : * This is the same as the C++ method GDALDimension::GetDirection()
13416 : */
13417 22 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
13418 : {
13419 22 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13420 22 : return hDim->m_poImpl->GetDirection().c_str();
13421 : }
13422 :
13423 : /************************************************************************/
13424 : /* GDALDimensionGetSize() */
13425 : /************************************************************************/
13426 :
13427 : /** Return the size, that is the number of values along the dimension.
13428 : *
13429 : * This is the same as the C++ method GDALDimension::GetSize()
13430 : */
13431 3616 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
13432 : {
13433 3616 : VALIDATE_POINTER1(hDim, __func__, 0);
13434 3616 : return hDim->m_poImpl->GetSize();
13435 : }
13436 :
13437 : /************************************************************************/
13438 : /* GDALDimensionGetIndexingVariable() */
13439 : /************************************************************************/
13440 :
13441 : /** Return the variable that is used to index the dimension (if there is one).
13442 : *
13443 : * This is the array, typically one-dimensional, describing the values taken
13444 : * by the dimension.
13445 : *
13446 : * The returned value should be freed with GDALMDArrayRelease().
13447 : *
13448 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
13449 : */
13450 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
13451 : {
13452 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13453 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
13454 118 : if (!var)
13455 10 : return nullptr;
13456 108 : return new GDALMDArrayHS(var);
13457 : }
13458 :
13459 : /************************************************************************/
13460 : /* GDALDimensionSetIndexingVariable() */
13461 : /************************************************************************/
13462 :
13463 : /** Set the variable that is used to index the dimension.
13464 : *
13465 : * This is the array, typically one-dimensional, describing the values taken
13466 : * by the dimension.
13467 : *
13468 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
13469 : *
13470 : * @return TRUE in case of success.
13471 : */
13472 22 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
13473 : {
13474 22 : VALIDATE_POINTER1(hDim, __func__, FALSE);
13475 66 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
13476 44 : : nullptr);
13477 : }
13478 :
13479 : /************************************************************************/
13480 : /* GDALDimensionRename() */
13481 : /************************************************************************/
13482 :
13483 : /** Rename the dimension.
13484 : *
13485 : * This is not implemented by all drivers.
13486 : *
13487 : * Drivers known to implement it: MEM, netCDF.
13488 : *
13489 : * This is the same as the C++ method GDALDimension::Rename()
13490 : *
13491 : * @return true in case of success
13492 : * @since GDAL 3.8
13493 : */
13494 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
13495 : {
13496 31 : VALIDATE_POINTER1(hDim, __func__, false);
13497 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
13498 31 : return hDim->m_poImpl->Rename(pszNewName);
13499 : }
13500 :
13501 : /************************************************************************/
13502 : /* GDALDatasetGetRootGroup() */
13503 : /************************************************************************/
13504 :
13505 : /** Return the root GDALGroup of this dataset.
13506 : *
13507 : * Only valid for multidimensional datasets.
13508 : *
13509 : * The returned value must be freed with GDALGroupRelease().
13510 : *
13511 : * This is the same as the C++ method GDALDataset::GetRootGroup().
13512 : *
13513 : * @since GDAL 3.1
13514 : */
13515 1124 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
13516 : {
13517 1124 : VALIDATE_POINTER1(hDS, __func__, nullptr);
13518 1124 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
13519 1124 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
13520 : }
13521 :
13522 : /************************************************************************/
13523 : /* GDALRasterBandAsMDArray() */
13524 : /************************************************************************/
13525 :
13526 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
13527 : *
13528 : * The band must be linked to a GDALDataset. If this dataset is not already
13529 : * marked as shared, it will be, so that the returned array holds a reference
13530 : * to it.
13531 : *
13532 : * If the dataset has a geotransform attached, the X and Y dimensions of the
13533 : * returned array will have an associated indexing variable.
13534 : *
13535 : * The returned pointer must be released with GDALMDArrayRelease().
13536 : *
13537 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
13538 : *
13539 : * @return a new array, or NULL.
13540 : *
13541 : * @since GDAL 3.1
13542 : */
13543 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
13544 : {
13545 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
13546 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
13547 21 : if (!poArray)
13548 0 : return nullptr;
13549 21 : return new GDALMDArrayHS(poArray);
13550 : }
13551 :
13552 : /************************************************************************/
13553 : /* GDALMDArrayAsClassicDataset() */
13554 : /************************************************************************/
13555 :
13556 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13557 : *
13558 : * Only 2D or more arrays are supported.
13559 : *
13560 : * In the case of > 2D arrays, additional dimensions will be represented as
13561 : * raster bands.
13562 : *
13563 : * The "reverse" method is GDALRasterBand::AsMDArray().
13564 : *
13565 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13566 : *
13567 : * @param hArray Array.
13568 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13569 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13570 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13571 : */
13572 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
13573 : size_t iYDim)
13574 : {
13575 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13576 0 : return GDALDataset::ToHandle(
13577 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
13578 : }
13579 :
13580 : /************************************************************************/
13581 : /* GDALMDArrayAsClassicDatasetEx() */
13582 : /************************************************************************/
13583 :
13584 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13585 : *
13586 : * Only 2D or more arrays are supported.
13587 : *
13588 : * In the case of > 2D arrays, additional dimensions will be represented as
13589 : * raster bands.
13590 : *
13591 : * The "reverse" method is GDALRasterBand::AsMDArray().
13592 : *
13593 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13594 : * @param hArray Array.
13595 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13596 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13597 : * Ignored if the dimension count is 1.
13598 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA option.
13599 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
13600 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13601 : * @since GDAL 3.8
13602 : */
13603 59 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
13604 : size_t iYDim, GDALGroupH hRootGroup,
13605 : CSLConstList papszOptions)
13606 : {
13607 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13608 118 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
13609 118 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
13610 118 : papszOptions));
13611 : }
13612 :
13613 : //! @cond Doxygen_Suppress
13614 :
13615 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
13616 : const std::string &osName,
13617 : const std::string &osValue,
13618 180 : GDALExtendedDataTypeSubType eSubType)
13619 : : GDALAbstractMDArray(osParentName, osName),
13620 : GDALAttribute(osParentName, osName),
13621 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
13622 : {
13623 180 : }
13624 :
13625 : const std::vector<std::shared_ptr<GDALDimension>> &
13626 30 : GDALAttributeString::GetDimensions() const
13627 : {
13628 30 : return m_dims;
13629 : }
13630 :
13631 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
13632 : {
13633 21 : return m_dt;
13634 : }
13635 :
13636 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
13637 : const GPtrDiff_t *,
13638 : const GDALExtendedDataType &bufferDataType,
13639 : void *pDstBuffer) const
13640 : {
13641 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
13642 0 : return false;
13643 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
13644 10 : if (!pszStr)
13645 0 : return false;
13646 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
13647 10 : *static_cast<char **>(pDstBuffer) = pszStr;
13648 10 : return true;
13649 : }
13650 :
13651 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13652 : const std::string &osName,
13653 66 : double dfValue)
13654 : : GDALAbstractMDArray(osParentName, osName),
13655 : GDALAttribute(osParentName, osName),
13656 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
13657 : {
13658 66 : }
13659 :
13660 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13661 : const std::string &osName,
13662 27 : int nValue)
13663 : : GDALAbstractMDArray(osParentName, osName),
13664 : GDALAttribute(osParentName, osName),
13665 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
13666 : {
13667 27 : }
13668 :
13669 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13670 : const std::string &osName,
13671 7 : const std::vector<GUInt32> &anValues)
13672 : : GDALAbstractMDArray(osParentName, osName),
13673 : GDALAttribute(osParentName, osName),
13674 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
13675 : {
13676 7 : m_dims.push_back(std::make_shared<GDALDimension>(
13677 14 : std::string(), "dim0", std::string(), std::string(),
13678 7 : m_anValuesUInt32.size()));
13679 7 : }
13680 :
13681 : const std::vector<std::shared_ptr<GDALDimension>> &
13682 14 : GDALAttributeNumeric::GetDimensions() const
13683 : {
13684 14 : return m_dims;
13685 : }
13686 :
13687 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
13688 : {
13689 8 : return m_dt;
13690 : }
13691 :
13692 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
13693 : const size_t *count, const GInt64 *arrayStep,
13694 : const GPtrDiff_t *bufferStride,
13695 : const GDALExtendedDataType &bufferDataType,
13696 : void *pDstBuffer) const
13697 : {
13698 4 : if (m_dims.empty())
13699 : {
13700 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
13701 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
13702 : bufferDataType);
13703 : else
13704 : {
13705 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
13706 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
13707 : bufferDataType);
13708 : }
13709 : }
13710 : else
13711 : {
13712 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
13713 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13714 30 : for (size_t i = 0; i < count[0]; ++i)
13715 : {
13716 29 : GDALExtendedDataType::CopyValue(
13717 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
13718 29 : i * arrayStep[0])],
13719 29 : m_dt, pabyDstBuffer, bufferDataType);
13720 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
13721 : }
13722 : }
13723 4 : return true;
13724 : }
13725 :
13726 192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
13727 : const std::string &osParentName, const std::string &osName,
13728 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13729 192 : double dfIncrement, double dfOffsetInIncrement)
13730 : : GDALAbstractMDArray(osParentName, osName),
13731 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
13732 : m_dfIncrement(dfIncrement),
13733 384 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
13734 : {
13735 192 : }
13736 :
13737 192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
13738 : const std::string &osParentName, const std::string &osName,
13739 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13740 : double dfIncrement, double dfOffsetInIncrement)
13741 : {
13742 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
13743 192 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
13744 192 : poArray->SetSelf(poArray);
13745 192 : return poArray;
13746 : }
13747 :
13748 : const std::vector<std::shared_ptr<GDALDimension>> &
13749 786 : GDALMDArrayRegularlySpaced::GetDimensions() const
13750 : {
13751 786 : return m_dims;
13752 : }
13753 :
13754 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
13755 : {
13756 316 : return m_dt;
13757 : }
13758 :
13759 : std::vector<std::shared_ptr<GDALAttribute>>
13760 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
13761 : {
13762 4 : return m_attributes;
13763 : }
13764 :
13765 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
13766 : const std::shared_ptr<GDALAttribute> &poAttr)
13767 : {
13768 0 : m_attributes.emplace_back(poAttr);
13769 0 : }
13770 :
13771 188 : bool GDALMDArrayRegularlySpaced::IRead(
13772 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
13773 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
13774 : void *pDstBuffer) const
13775 : {
13776 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13777 14719 : for (size_t i = 0; i < count[0]; i++)
13778 : {
13779 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
13780 14531 : m_dfOffsetInIncrement) *
13781 14531 : m_dfIncrement;
13782 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
13783 : bufferDataType);
13784 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
13785 : }
13786 188 : return true;
13787 : }
13788 :
13789 2873 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
13790 : const std::string &osParentName, const std::string &osName,
13791 2873 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
13792 2873 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
13793 : {
13794 2873 : }
13795 :
13796 : std::shared_ptr<GDALMDArray>
13797 686 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
13798 : {
13799 686 : return m_poIndexingVariable.lock();
13800 : }
13801 :
13802 : // cppcheck-suppress passedByValue
13803 483 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
13804 : std::shared_ptr<GDALMDArray> poIndexingVariable)
13805 : {
13806 483 : m_poIndexingVariable = poIndexingVariable;
13807 483 : return true;
13808 : }
13809 :
13810 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
13811 : {
13812 33 : m_nSize = nNewSize;
13813 33 : }
13814 :
13815 : /************************************************************************/
13816 : /* GDALPamMultiDim::Private */
13817 : /************************************************************************/
13818 :
13819 : struct GDALPamMultiDim::Private
13820 : {
13821 : std::string m_osFilename{};
13822 : std::string m_osPamFilename{};
13823 :
13824 : struct Statistics
13825 : {
13826 : bool bHasStats = false;
13827 : bool bApproxStats = false;
13828 : double dfMin = 0;
13829 : double dfMax = 0;
13830 : double dfMean = 0;
13831 : double dfStdDev = 0;
13832 : GUInt64 nValidCount = 0;
13833 : };
13834 :
13835 : struct ArrayInfo
13836 : {
13837 : std::shared_ptr<OGRSpatialReference> poSRS{};
13838 : // cppcheck-suppress unusedStructMember
13839 : Statistics stats{};
13840 : };
13841 :
13842 : typedef std::pair<std::string, std::string> NameContext;
13843 : std::map<NameContext, ArrayInfo> m_oMapArray{};
13844 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
13845 : bool m_bDirty = false;
13846 : bool m_bLoaded = false;
13847 : };
13848 :
13849 : /************************************************************************/
13850 : /* GDALPamMultiDim */
13851 : /************************************************************************/
13852 :
13853 1363 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
13854 1363 : : d(new Private())
13855 : {
13856 1363 : d->m_osFilename = osFilename;
13857 1363 : }
13858 :
13859 : /************************************************************************/
13860 : /* GDALPamMultiDim::~GDALPamMultiDim() */
13861 : /************************************************************************/
13862 :
13863 1363 : GDALPamMultiDim::~GDALPamMultiDim()
13864 : {
13865 1363 : if (d->m_bDirty)
13866 29 : Save();
13867 1363 : }
13868 :
13869 : /************************************************************************/
13870 : /* GDALPamMultiDim::Load() */
13871 : /************************************************************************/
13872 :
13873 101 : void GDALPamMultiDim::Load()
13874 : {
13875 101 : if (d->m_bLoaded)
13876 90 : return;
13877 44 : d->m_bLoaded = true;
13878 :
13879 44 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
13880 44 : d->m_osPamFilename =
13881 88 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
13882 44 : CPLXMLTreeCloser oTree(nullptr);
13883 : {
13884 88 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
13885 44 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
13886 : }
13887 44 : if (!oTree)
13888 : {
13889 33 : return;
13890 : }
13891 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
13892 11 : if (!poPAMMultiDim)
13893 0 : return;
13894 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
13895 24 : psIter = psIter->psNext)
13896 : {
13897 24 : if (psIter->eType == CXT_Element &&
13898 24 : strcmp(psIter->pszValue, "Array") == 0)
13899 : {
13900 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
13901 13 : if (!pszName)
13902 0 : continue;
13903 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
13904 : const auto oKey =
13905 26 : std::pair<std::string, std::string>(pszName, pszContext);
13906 :
13907 : /* --------------------------------------------------------------------
13908 : */
13909 : /* Check for an SRS node. */
13910 : /* --------------------------------------------------------------------
13911 : */
13912 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
13913 13 : if (psSRSNode)
13914 : {
13915 : std::shared_ptr<OGRSpatialReference> poSRS =
13916 6 : std::make_shared<OGRSpatialReference>();
13917 3 : poSRS->SetFromUserInput(
13918 : CPLGetXMLValue(psSRSNode, nullptr, ""),
13919 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
13920 3 : const char *pszMapping = CPLGetXMLValue(
13921 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
13922 3 : if (pszMapping)
13923 : {
13924 : char **papszTokens =
13925 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
13926 6 : std::vector<int> anMapping;
13927 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
13928 : {
13929 6 : anMapping.push_back(atoi(papszTokens[i]));
13930 : }
13931 3 : CSLDestroy(papszTokens);
13932 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
13933 : }
13934 : else
13935 : {
13936 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
13937 : }
13938 :
13939 : const char *pszCoordinateEpoch =
13940 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
13941 3 : if (pszCoordinateEpoch)
13942 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
13943 :
13944 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
13945 : }
13946 :
13947 : const CPLXMLNode *psStatistics =
13948 13 : CPLGetXMLNode(psIter, "Statistics");
13949 13 : if (psStatistics)
13950 : {
13951 7 : Private::Statistics sStats;
13952 7 : sStats.bHasStats = true;
13953 7 : sStats.bApproxStats = CPLTestBool(
13954 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
13955 7 : sStats.dfMin =
13956 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
13957 7 : sStats.dfMax =
13958 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
13959 7 : sStats.dfMean =
13960 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
13961 7 : sStats.dfStdDev =
13962 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
13963 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
13964 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
13965 7 : d->m_oMapArray[oKey].stats = sStats;
13966 13 : }
13967 : }
13968 : else
13969 : {
13970 11 : CPLXMLNode *psNextBackup = psIter->psNext;
13971 11 : psIter->psNext = nullptr;
13972 11 : d->m_apoOtherNodes.emplace_back(
13973 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
13974 11 : psIter->psNext = psNextBackup;
13975 : }
13976 : }
13977 : }
13978 :
13979 : /************************************************************************/
13980 : /* GDALPamMultiDim::Save() */
13981 : /************************************************************************/
13982 :
13983 29 : void GDALPamMultiDim::Save()
13984 : {
13985 : CPLXMLTreeCloser oTree(
13986 58 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
13987 33 : for (const auto &poOtherNode : d->m_apoOtherNodes)
13988 : {
13989 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
13990 : }
13991 108 : for (const auto &kv : d->m_oMapArray)
13992 : {
13993 : CPLXMLNode *psArrayNode =
13994 79 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
13995 79 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
13996 79 : if (!kv.first.second.empty())
13997 : {
13998 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
13999 : kv.first.second.c_str());
14000 : }
14001 79 : if (kv.second.poSRS)
14002 : {
14003 71 : char *pszWKT = nullptr;
14004 : {
14005 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14006 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14007 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14008 : }
14009 : CPLXMLNode *psSRSNode =
14010 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14011 71 : CPLFree(pszWKT);
14012 : const auto &mapping =
14013 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14014 142 : CPLString osMapping;
14015 213 : for (size_t i = 0; i < mapping.size(); ++i)
14016 : {
14017 142 : if (!osMapping.empty())
14018 71 : osMapping += ",";
14019 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14020 : }
14021 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14022 : osMapping.c_str());
14023 :
14024 : const double dfCoordinateEpoch =
14025 71 : kv.second.poSRS->GetCoordinateEpoch();
14026 71 : if (dfCoordinateEpoch > 0)
14027 : {
14028 : std::string osCoordinateEpoch =
14029 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14030 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14031 : {
14032 6 : while (osCoordinateEpoch.back() == '0')
14033 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14034 : }
14035 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14036 : osCoordinateEpoch.c_str());
14037 : }
14038 : }
14039 :
14040 79 : if (kv.second.stats.bHasStats)
14041 : {
14042 : CPLXMLNode *psMDArray =
14043 5 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14044 5 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14045 5 : kv.second.stats.bApproxStats ? "1"
14046 : : "0");
14047 5 : CPLCreateXMLElementAndValue(
14048 : psMDArray, "Minimum",
14049 5 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14050 5 : CPLCreateXMLElementAndValue(
14051 : psMDArray, "Maximum",
14052 5 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14053 5 : CPLCreateXMLElementAndValue(
14054 5 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14055 5 : CPLCreateXMLElementAndValue(
14056 : psMDArray, "StdDev",
14057 5 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14058 5 : CPLCreateXMLElementAndValue(
14059 : psMDArray, "ValidSampleCount",
14060 5 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14061 : }
14062 : }
14063 :
14064 58 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
14065 29 : CPLInstallErrorHandlerAccumulator(aoErrors);
14066 : const int bSaved =
14067 29 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14068 29 : CPLUninstallErrorHandlerAccumulator();
14069 :
14070 29 : const char *pszNewPam = nullptr;
14071 29 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14072 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14073 : {
14074 0 : CPLErrorReset();
14075 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14076 : }
14077 : else
14078 : {
14079 29 : for (const auto &oError : aoErrors)
14080 : {
14081 0 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
14082 : }
14083 : }
14084 29 : }
14085 :
14086 : /************************************************************************/
14087 : /* GDALPamMultiDim::GetSpatialRef() */
14088 : /************************************************************************/
14089 :
14090 : std::shared_ptr<OGRSpatialReference>
14091 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14092 : const std::string &osContext)
14093 : {
14094 10 : Load();
14095 : auto oIter =
14096 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14097 10 : if (oIter != d->m_oMapArray.end())
14098 2 : return oIter->second.poSRS;
14099 8 : return nullptr;
14100 : }
14101 :
14102 : /************************************************************************/
14103 : /* GDALPamMultiDim::SetSpatialRef() */
14104 : /************************************************************************/
14105 :
14106 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14107 : const std::string &osContext,
14108 : const OGRSpatialReference *poSRS)
14109 : {
14110 72 : Load();
14111 72 : d->m_bDirty = true;
14112 72 : if (poSRS && !poSRS->IsEmpty())
14113 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14114 : poSRS->Clone());
14115 : else
14116 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14117 1 : .poSRS.reset();
14118 72 : }
14119 :
14120 : /************************************************************************/
14121 : /* GetStatistics() */
14122 : /************************************************************************/
14123 :
14124 13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14125 : const std::string &osContext,
14126 : bool bApproxOK, double *pdfMin,
14127 : double *pdfMax, double *pdfMean,
14128 : double *pdfStdDev, GUInt64 *pnValidCount)
14129 : {
14130 13 : Load();
14131 : auto oIter =
14132 13 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14133 13 : if (oIter == d->m_oMapArray.end())
14134 6 : return CE_Failure;
14135 7 : const auto &stats = oIter->second.stats;
14136 7 : if (!stats.bHasStats)
14137 1 : return CE_Failure;
14138 6 : if (!bApproxOK && stats.bApproxStats)
14139 0 : return CE_Failure;
14140 6 : if (pdfMin)
14141 6 : *pdfMin = stats.dfMin;
14142 6 : if (pdfMax)
14143 6 : *pdfMax = stats.dfMax;
14144 6 : if (pdfMean)
14145 6 : *pdfMean = stats.dfMean;
14146 6 : if (pdfStdDev)
14147 6 : *pdfStdDev = stats.dfStdDev;
14148 6 : if (pnValidCount)
14149 6 : *pnValidCount = stats.nValidCount;
14150 6 : return CE_None;
14151 : }
14152 :
14153 : /************************************************************************/
14154 : /* SetStatistics() */
14155 : /************************************************************************/
14156 :
14157 5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14158 : const std::string &osContext,
14159 : bool bApproxStats, double dfMin,
14160 : double dfMax, double dfMean,
14161 : double dfStdDev, GUInt64 nValidCount)
14162 : {
14163 5 : Load();
14164 5 : d->m_bDirty = true;
14165 : auto &stats =
14166 5 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14167 5 : stats.bHasStats = true;
14168 5 : stats.bApproxStats = bApproxStats;
14169 5 : stats.dfMin = dfMin;
14170 5 : stats.dfMax = dfMax;
14171 5 : stats.dfMean = dfMean;
14172 5 : stats.dfStdDev = dfStdDev;
14173 5 : stats.nValidCount = nValidCount;
14174 5 : }
14175 :
14176 : /************************************************************************/
14177 : /* ClearStatistics() */
14178 : /************************************************************************/
14179 :
14180 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14181 : const std::string &osContext)
14182 : {
14183 0 : Load();
14184 0 : d->m_bDirty = true;
14185 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14186 : false;
14187 0 : }
14188 :
14189 : /************************************************************************/
14190 : /* ClearStatistics() */
14191 : /************************************************************************/
14192 :
14193 1 : void GDALPamMultiDim::ClearStatistics()
14194 : {
14195 1 : Load();
14196 1 : d->m_bDirty = true;
14197 3 : for (auto &kv : d->m_oMapArray)
14198 2 : kv.second.stats.bHasStats = false;
14199 1 : }
14200 :
14201 : /************************************************************************/
14202 : /* GetPAM() */
14203 : /************************************************************************/
14204 :
14205 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14206 784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14207 : {
14208 784 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14209 784 : if (poPamArray)
14210 563 : return poPamArray->GetPAM();
14211 221 : return nullptr;
14212 : }
14213 :
14214 : /************************************************************************/
14215 : /* GDALPamMDArray */
14216 : /************************************************************************/
14217 :
14218 3629 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14219 : const std::string &osName,
14220 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14221 0 : const std::string &osContext)
14222 : :
14223 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14224 : GDALAbstractMDArray(osParentName, osName),
14225 : #endif
14226 3629 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14227 : {
14228 3629 : }
14229 :
14230 : /************************************************************************/
14231 : /* GDALPamMDArray::SetSpatialRef() */
14232 : /************************************************************************/
14233 :
14234 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14235 : {
14236 72 : if (!m_poPam)
14237 0 : return false;
14238 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14239 72 : return true;
14240 : }
14241 :
14242 : /************************************************************************/
14243 : /* GDALPamMDArray::GetSpatialRef() */
14244 : /************************************************************************/
14245 :
14246 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14247 : {
14248 10 : if (!m_poPam)
14249 0 : return nullptr;
14250 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14251 : }
14252 :
14253 : /************************************************************************/
14254 : /* GetStatistics() */
14255 : /************************************************************************/
14256 :
14257 13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14258 : double *pdfMin, double *pdfMax,
14259 : double *pdfMean, double *pdfStdDev,
14260 : GUInt64 *pnValidCount,
14261 : GDALProgressFunc pfnProgress,
14262 : void *pProgressData)
14263 : {
14264 13 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14265 : bApproxOK, pdfMin, pdfMax, pdfMean,
14266 13 : pdfStdDev, pnValidCount) == CE_None)
14267 : {
14268 6 : return CE_None;
14269 : }
14270 7 : if (!bForce)
14271 4 : return CE_Warning;
14272 :
14273 3 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14274 : pdfMean, pdfStdDev, pnValidCount,
14275 3 : pfnProgress, pProgressData);
14276 : }
14277 :
14278 : /************************************************************************/
14279 : /* SetStatistics() */
14280 : /************************************************************************/
14281 :
14282 5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14283 : double dfMax, double dfMean, double dfStdDev,
14284 : GUInt64 nValidCount,
14285 : CSLConstList /* papszOptions */)
14286 : {
14287 5 : if (!m_poPam)
14288 0 : return false;
14289 5 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14290 : dfMax, dfMean, dfStdDev, nValidCount);
14291 5 : return true;
14292 : }
14293 :
14294 : /************************************************************************/
14295 : /* ClearStatistics() */
14296 : /************************************************************************/
14297 :
14298 0 : void GDALPamMDArray::ClearStatistics()
14299 : {
14300 0 : if (!m_poPam)
14301 0 : return;
14302 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14303 : }
14304 :
14305 : //! @endcond
|