Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <queue>
18 : #include <set>
19 : #include <utility>
20 : #include <time.h>
21 :
22 : #include <cmath>
23 : #include <ctype.h> // isalnum
24 :
25 : #include "cpl_error_internal.h"
26 : #include "gdal_priv.h"
27 : #include "gdal_pam.h"
28 : #include "gdal_utils.h"
29 : #include "cpl_safemaths.hpp"
30 : #include "memmultidim.h"
31 : #include "ogrsf_frmts.h"
32 : #include "gdalmultidim_priv.h"
33 :
34 : #if defined(__clang__) || defined(_MSC_VER)
35 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
36 : #endif
37 :
38 : /************************************************************************/
39 : /* GDALMDArrayUnscaled */
40 : /************************************************************************/
41 :
42 : class GDALMDArrayUnscaled final : public GDALPamMDArray
43 : {
44 : private:
45 : std::shared_ptr<GDALMDArray> m_poParent{};
46 : const GDALExtendedDataType m_dt;
47 : bool m_bHasNoData;
48 : const double m_dfScale;
49 : const double m_dfOffset;
50 : std::vector<GByte> m_abyRawNoData{};
51 :
52 : protected:
53 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
54 : double dfScale, double dfOffset,
55 : double dfOverriddenDstNodata, GDALDataType eDT)
56 26 : : GDALAbstractMDArray(std::string(),
57 26 : "Unscaled view of " + poParent->GetFullName()),
58 : GDALPamMDArray(
59 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
60 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
61 13 : m_poParent(std::move(poParent)),
62 : m_dt(GDALExtendedDataType::Create(eDT)),
63 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
64 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
65 : {
66 13 : m_abyRawNoData.resize(m_dt.GetSize());
67 : const auto eNonComplexDT =
68 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
69 26 : GDALCopyWords64(
70 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
71 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
72 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
73 13 : }
74 :
75 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
76 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
77 : const GDALExtendedDataType &bufferDataType,
78 : void *pDstBuffer) const override;
79 :
80 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
81 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
82 : const GDALExtendedDataType &bufferDataType,
83 : const void *pSrcBuffer) override;
84 :
85 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
86 : CSLConstList papszOptions) const override
87 : {
88 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
89 : }
90 :
91 : public:
92 : static std::shared_ptr<GDALMDArrayUnscaled>
93 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
94 : double dfOffset, double dfDstNodata, GDALDataType eDT)
95 : {
96 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
97 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
98 13 : newAr->SetSelf(newAr);
99 13 : return newAr;
100 : }
101 :
102 1 : bool IsWritable() const override
103 : {
104 1 : return m_poParent->IsWritable();
105 : }
106 :
107 15 : const std::string &GetFilename() const override
108 : {
109 15 : return m_poParent->GetFilename();
110 : }
111 :
112 : const std::vector<std::shared_ptr<GDALDimension>> &
113 220 : GetDimensions() const override
114 : {
115 220 : return m_poParent->GetDimensions();
116 : }
117 :
118 103 : const GDALExtendedDataType &GetDataType() const override
119 : {
120 103 : return m_dt;
121 : }
122 :
123 1 : const std::string &GetUnit() const override
124 : {
125 1 : return m_poParent->GetUnit();
126 : }
127 :
128 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
129 : {
130 1 : return m_poParent->GetSpatialRef();
131 : }
132 :
133 6 : const void *GetRawNoDataValue() const override
134 : {
135 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
136 : }
137 :
138 1 : bool SetRawNoDataValue(const void *pRawNoData) override
139 : {
140 1 : m_bHasNoData = true;
141 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
142 1 : return true;
143 : }
144 :
145 4 : std::vector<GUInt64> GetBlockSize() const override
146 : {
147 4 : return m_poParent->GetBlockSize();
148 : }
149 :
150 : std::shared_ptr<GDALAttribute>
151 0 : GetAttribute(const std::string &osName) const override
152 : {
153 0 : return m_poParent->GetAttribute(osName);
154 : }
155 :
156 : std::vector<std::shared_ptr<GDALAttribute>>
157 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
158 : {
159 1 : return m_poParent->GetAttributes(papszOptions);
160 : }
161 :
162 0 : bool SetUnit(const std::string &osUnit) override
163 : {
164 0 : return m_poParent->SetUnit(osUnit);
165 : }
166 :
167 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
168 : {
169 0 : return m_poParent->SetSpatialRef(poSRS);
170 : }
171 :
172 : std::shared_ptr<GDALAttribute>
173 1 : CreateAttribute(const std::string &osName,
174 : const std::vector<GUInt64> &anDimensions,
175 : const GDALExtendedDataType &oDataType,
176 : CSLConstList papszOptions = nullptr) override
177 : {
178 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
179 1 : papszOptions);
180 : }
181 : };
182 :
183 : /************************************************************************/
184 : /* ~GDALIHasAttribute() */
185 : /************************************************************************/
186 :
187 : GDALIHasAttribute::~GDALIHasAttribute() = default;
188 :
189 : /************************************************************************/
190 : /* GetAttribute() */
191 : /************************************************************************/
192 :
193 : /** Return an attribute by its name.
194 : *
195 : * If the attribute does not exist, nullptr should be silently returned.
196 : *
197 : * @note Driver implementation: this method will fallback to
198 : * GetAttributeFromAttributes() is not explicitly implemented
199 : *
200 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
201 : *
202 : * This is the same as the C function GDALGroupGetAttribute() or
203 : * GDALMDArrayGetAttribute().
204 : *
205 : * @param osName Attribute name
206 : * @return the attribute, or nullptr if it does not exist or an error occurred.
207 : */
208 : std::shared_ptr<GDALAttribute>
209 1022 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
210 : {
211 1022 : return GetAttributeFromAttributes(osName);
212 : }
213 :
214 : /************************************************************************/
215 : /* GetAttributeFromAttributes() */
216 : /************************************************************************/
217 :
218 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
219 : */
220 : std::shared_ptr<GDALAttribute>
221 1022 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
222 : {
223 2044 : auto attrs(GetAttributes());
224 5357 : for (const auto &attr : attrs)
225 : {
226 5051 : if (attr->GetName() == osName)
227 716 : return attr;
228 : }
229 306 : return nullptr;
230 : }
231 :
232 : /************************************************************************/
233 : /* GetAttributes() */
234 : /************************************************************************/
235 :
236 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
237 : *
238 : * If the attribute does not exist, nullptr should be silently returned.
239 : *
240 : * @note Driver implementation: optionally implemented. If implemented,
241 : * GetAttribute() should also be implemented.
242 : *
243 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
244 : *
245 : * This is the same as the C function GDALGroupGetAttributes() or
246 : * GDALMDArrayGetAttributes().
247 :
248 : * @param papszOptions Driver specific options determining how attributes
249 : * should be retrieved. Pass nullptr for default behavior.
250 : *
251 : * @return the attributes.
252 : */
253 : std::vector<std::shared_ptr<GDALAttribute>>
254 41 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
255 : {
256 41 : return {};
257 : }
258 :
259 : /************************************************************************/
260 : /* CreateAttribute() */
261 : /************************************************************************/
262 :
263 : /** Create an attribute within a GDALMDArray or GDALGroup.
264 : *
265 : * The attribute might not be "physically" created until a value is written
266 : * into it.
267 : *
268 : * Optionally implemented.
269 : *
270 : * Drivers known to implement it: MEM, netCDF
271 : *
272 : * This is the same as the C function GDALGroupCreateAttribute() or
273 : * GDALMDArrayCreateAttribute()
274 : *
275 : * @param osName Attribute name.
276 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
277 : * dimension first to the fastest varying dimension last.
278 : * Empty for a scalar attribute (common case)
279 : * @param oDataType Attribute data type.
280 : * @param papszOptions Driver specific options determining how the attribute.
281 : * should be created.
282 : *
283 : * @return the new attribute, or nullptr if case of error
284 : */
285 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
286 : CPL_UNUSED const std::string &osName,
287 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
288 : CPL_UNUSED const GDALExtendedDataType &oDataType,
289 : CPL_UNUSED CSLConstList papszOptions)
290 : {
291 0 : CPLError(CE_Failure, CPLE_NotSupported,
292 : "CreateAttribute() not implemented");
293 0 : return nullptr;
294 : }
295 :
296 : /************************************************************************/
297 : /* DeleteAttribute() */
298 : /************************************************************************/
299 :
300 : /** Delete an attribute from a GDALMDArray or GDALGroup.
301 : *
302 : * Optionally implemented.
303 : *
304 : * After this call, if a previously obtained instance of the deleted object
305 : * is still alive, no method other than for freeing it should be invoked.
306 : *
307 : * Drivers known to implement it: MEM, netCDF
308 : *
309 : * This is the same as the C function GDALGroupDeleteAttribute() or
310 : * GDALMDArrayDeleteAttribute()
311 : *
312 : * @param osName Attribute name.
313 : * @param papszOptions Driver specific options determining how the attribute.
314 : * should be deleted.
315 : *
316 : * @return true in case of success
317 : * @since GDAL 3.8
318 : */
319 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
320 : CPL_UNUSED CSLConstList papszOptions)
321 : {
322 0 : CPLError(CE_Failure, CPLE_NotSupported,
323 : "DeleteAttribute() not implemented");
324 0 : return false;
325 : }
326 :
327 : /************************************************************************/
328 : /* GDALGroup() */
329 : /************************************************************************/
330 :
331 : //! @cond Doxygen_Suppress
332 6694 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
333 6694 : const std::string &osContext)
334 6694 : : m_osName(osParentName.empty() ? "/" : osName),
335 : m_osFullName(
336 13388 : !osParentName.empty()
337 10278 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
338 : : "/"),
339 16972 : m_osContext(osContext)
340 : {
341 6694 : }
342 :
343 : //! @endcond
344 :
345 : /************************************************************************/
346 : /* ~GDALGroup() */
347 : /************************************************************************/
348 :
349 : GDALGroup::~GDALGroup() = default;
350 :
351 : /************************************************************************/
352 : /* GetMDArrayNames() */
353 : /************************************************************************/
354 :
355 : /** Return the list of multidimensional array names contained in this group.
356 : *
357 : * @note Driver implementation: optionally implemented. If implemented,
358 : * OpenMDArray() should also be implemented.
359 : *
360 : * Drivers known to implement it: MEM, netCDF.
361 : *
362 : * This is the same as the C function GDALGroupGetMDArrayNames().
363 : *
364 : * @param papszOptions Driver specific options determining how arrays
365 : * should be retrieved. Pass nullptr for default behavior.
366 : *
367 : * @return the array names.
368 : */
369 : std::vector<std::string>
370 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
371 : {
372 0 : return {};
373 : }
374 :
375 : /************************************************************************/
376 : /* OpenMDArray() */
377 : /************************************************************************/
378 :
379 : /** Open and return a multidimensional array.
380 : *
381 : * @note Driver implementation: optionally implemented. If implemented,
382 : * GetMDArrayNames() should also be implemented.
383 : *
384 : * Drivers known to implement it: MEM, netCDF.
385 : *
386 : * This is the same as the C function GDALGroupOpenMDArray().
387 : *
388 : * @param osName Array name.
389 : * @param papszOptions Driver specific options determining how the array should
390 : * be opened. Pass nullptr for default behavior.
391 : *
392 : * @return the array, or nullptr.
393 : */
394 : std::shared_ptr<GDALMDArray>
395 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
396 : CPL_UNUSED CSLConstList papszOptions) const
397 : {
398 0 : return nullptr;
399 : }
400 :
401 : /************************************************************************/
402 : /* GetGroupNames() */
403 : /************************************************************************/
404 :
405 : /** Return the list of sub-groups contained in this group.
406 : *
407 : * @note Driver implementation: optionally implemented. If implemented,
408 : * OpenGroup() should also be implemented.
409 : *
410 : * Drivers known to implement it: MEM, netCDF.
411 : *
412 : * This is the same as the C function GDALGroupGetGroupNames().
413 : *
414 : * @param papszOptions Driver specific options determining how groups
415 : * should be retrieved. Pass nullptr for default behavior.
416 : *
417 : * @return the group names.
418 : */
419 : std::vector<std::string>
420 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
421 : {
422 4 : return {};
423 : }
424 :
425 : /************************************************************************/
426 : /* OpenGroup() */
427 : /************************************************************************/
428 :
429 : /** Open and return a sub-group.
430 : *
431 : * @note Driver implementation: optionally implemented. If implemented,
432 : * GetGroupNames() should also be implemented.
433 : *
434 : * Drivers known to implement it: MEM, netCDF.
435 : *
436 : * This is the same as the C function GDALGroupOpenGroup().
437 : *
438 : * @param osName Sub-group name.
439 : * @param papszOptions Driver specific options determining how the sub-group
440 : * should be opened. Pass nullptr for default behavior.
441 : *
442 : * @return the group, or nullptr.
443 : */
444 : std::shared_ptr<GDALGroup>
445 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
446 : CPL_UNUSED CSLConstList papszOptions) const
447 : {
448 4 : return nullptr;
449 : }
450 :
451 : /************************************************************************/
452 : /* GetVectorLayerNames() */
453 : /************************************************************************/
454 :
455 : /** Return the list of layer names contained in this group.
456 : *
457 : * @note Driver implementation: optionally implemented. If implemented,
458 : * OpenVectorLayer() should also be implemented.
459 : *
460 : * Drivers known to implement it: OpenFileGDB, FileGDB
461 : *
462 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
463 : * GDALDataset::GetLayer() should then be used.
464 : *
465 : * This is the same as the C function GDALGroupGetVectorLayerNames().
466 : *
467 : * @param papszOptions Driver specific options determining how layers
468 : * should be retrieved. Pass nullptr for default behavior.
469 : *
470 : * @return the vector layer names.
471 : * @since GDAL 3.4
472 : */
473 : std::vector<std::string>
474 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
475 : {
476 1 : return {};
477 : }
478 :
479 : /************************************************************************/
480 : /* OpenVectorLayer() */
481 : /************************************************************************/
482 :
483 : /** Open and return a vector layer.
484 : *
485 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
486 : * lifetime of the returned OGRLayer* is linked to the one of the owner
487 : * dataset (contrary to the general design of this class where objects can be
488 : * used independently of the object that returned them)
489 : *
490 : * @note Driver implementation: optionally implemented. If implemented,
491 : * GetVectorLayerNames() should also be implemented.
492 : *
493 : * Drivers known to implement it: MEM, netCDF.
494 : *
495 : * This is the same as the C function GDALGroupOpenVectorLayer().
496 : *
497 : * @param osName Vector layer name.
498 : * @param papszOptions Driver specific options determining how the layer should
499 : * be opened. Pass nullptr for default behavior.
500 : *
501 : * @return the group, or nullptr.
502 : */
503 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
504 : CPL_UNUSED CSLConstList papszOptions) const
505 : {
506 2 : return nullptr;
507 : }
508 :
509 : /************************************************************************/
510 : /* GetDimensions() */
511 : /************************************************************************/
512 :
513 : /** Return the list of dimensions contained in this group and used by its
514 : * arrays.
515 : *
516 : * This is for dimensions that can potentially be used by several arrays.
517 : * Not all drivers might implement this. To retrieve the dimensions used by
518 : * a specific array, use GDALMDArray::GetDimensions().
519 : *
520 : * Drivers known to implement it: MEM, netCDF
521 : *
522 : * This is the same as the C function GDALGroupGetDimensions().
523 : *
524 : * @param papszOptions Driver specific options determining how groups
525 : * should be retrieved. Pass nullptr for default behavior.
526 : *
527 : * @return the dimensions.
528 : */
529 : std::vector<std::shared_ptr<GDALDimension>>
530 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 11 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* GetStructuralInfo() */
537 : /************************************************************************/
538 :
539 : /** Return structural information on the group.
540 : *
541 : * This may be the compression, etc..
542 : *
543 : * The return value should not be freed and is valid until GDALGroup is
544 : * released or this function called again.
545 : *
546 : * This is the same as the C function GDALGroupGetStructuralInfo().
547 : */
548 29 : CSLConstList GDALGroup::GetStructuralInfo() const
549 : {
550 29 : return nullptr;
551 : }
552 :
553 : /************************************************************************/
554 : /* CreateGroup() */
555 : /************************************************************************/
556 :
557 : /** Create a sub-group within a group.
558 : *
559 : * Optionally implemented by drivers.
560 : *
561 : * Drivers known to implement it: MEM, netCDF
562 : *
563 : * This is the same as the C function GDALGroupCreateGroup().
564 : *
565 : * @param osName Sub-group name.
566 : * @param papszOptions Driver specific options determining how the sub-group
567 : * should be created.
568 : *
569 : * @return the new sub-group, or nullptr in case of error.
570 : */
571 : std::shared_ptr<GDALGroup>
572 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
573 : CPL_UNUSED CSLConstList papszOptions)
574 : {
575 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
576 0 : return nullptr;
577 : }
578 :
579 : /************************************************************************/
580 : /* DeleteGroup() */
581 : /************************************************************************/
582 :
583 : /** Delete a sub-group from a group.
584 : *
585 : * Optionally implemented.
586 : *
587 : * After this call, if a previously obtained instance of the deleted object
588 : * is still alive, no method other than for freeing it should be invoked.
589 : *
590 : * Drivers known to implement it: MEM, Zarr
591 : *
592 : * This is the same as the C function GDALGroupDeleteGroup().
593 : *
594 : * @param osName Sub-group name.
595 : * @param papszOptions Driver specific options determining how the group.
596 : * should be deleted.
597 : *
598 : * @return true in case of success
599 : * @since GDAL 3.8
600 : */
601 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
602 : CPL_UNUSED CSLConstList papszOptions)
603 : {
604 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
605 0 : return false;
606 : }
607 :
608 : /************************************************************************/
609 : /* CreateDimension() */
610 : /************************************************************************/
611 :
612 : /** Create a dimension within a group.
613 : *
614 : * @note Driver implementation: drivers supporting CreateDimension() should
615 : * implement this method, but do not have necessarily to implement
616 : * GDALGroup::GetDimensions().
617 : *
618 : * Drivers known to implement it: MEM, netCDF
619 : *
620 : * This is the same as the C function GDALGroupCreateDimension().
621 : *
622 : * @param osName Dimension name.
623 : * @param osType Dimension type (might be empty, and ignored by drivers)
624 : * @param osDirection Dimension direction (might be empty, and ignored by
625 : * drivers)
626 : * @param nSize Number of values indexed by this dimension. Should be > 0.
627 : * @param papszOptions Driver specific options determining how the dimension
628 : * should be created.
629 : *
630 : * @return the new dimension, or nullptr if case of error
631 : */
632 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
633 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
634 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
635 : CPL_UNUSED CSLConstList papszOptions)
636 : {
637 0 : CPLError(CE_Failure, CPLE_NotSupported,
638 : "CreateDimension() not implemented");
639 0 : return nullptr;
640 : }
641 :
642 : /************************************************************************/
643 : /* CreateMDArray() */
644 : /************************************************************************/
645 :
646 : /** Create a multidimensional array within a group.
647 : *
648 : * It is recommended that the GDALDimension objects passed in aoDimensions
649 : * belong to this group, either by retrieving them with GetDimensions()
650 : * or creating a new one with CreateDimension().
651 : *
652 : * Optionally implemented.
653 : *
654 : * Drivers known to implement it: MEM, netCDF
655 : *
656 : * This is the same as the C function GDALGroupCreateMDArray().
657 : *
658 : * @note Driver implementation: drivers should take into account the possibility
659 : * that GDALDimension object passed in aoDimensions might belong to a different
660 : * group / dataset / driver and act accordingly.
661 : *
662 : * @param osName Array name.
663 : * @param aoDimensions List of dimensions, ordered from the slowest varying
664 : * dimension first to the fastest varying dimension last.
665 : * Might be empty for a scalar array (if supported by
666 : * driver)
667 : * @param oDataType Array data type.
668 : * @param papszOptions Driver specific options determining how the array
669 : * should be created.
670 : *
671 : * @return the new array, or nullptr if case of error
672 : */
673 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
674 : CPL_UNUSED const std::string &osName,
675 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
676 : CPL_UNUSED const GDALExtendedDataType &oDataType,
677 : CPL_UNUSED CSLConstList papszOptions)
678 : {
679 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
680 0 : return nullptr;
681 : }
682 :
683 : /************************************************************************/
684 : /* DeleteMDArray() */
685 : /************************************************************************/
686 :
687 : /** Delete an array from a group.
688 : *
689 : * Optionally implemented.
690 : *
691 : * After this call, if a previously obtained instance of the deleted object
692 : * is still alive, no method other than for freeing it should be invoked.
693 : *
694 : * Drivers known to implement it: MEM, Zarr
695 : *
696 : * This is the same as the C function GDALGroupDeleteMDArray().
697 : *
698 : * @param osName Arrayname.
699 : * @param papszOptions Driver specific options determining how the array.
700 : * should be deleted.
701 : *
702 : * @return true in case of success
703 : * @since GDAL 3.8
704 : */
705 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
706 : CPL_UNUSED CSLConstList papszOptions)
707 : {
708 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
709 0 : return false;
710 : }
711 :
712 : /************************************************************************/
713 : /* GetTotalCopyCost() */
714 : /************************************************************************/
715 :
716 : /** Return a total "cost" to copy the group.
717 : *
718 : * Used as a parameter for CopFrom()
719 : */
720 22 : GUInt64 GDALGroup::GetTotalCopyCost() const
721 : {
722 22 : GUInt64 nCost = COPY_COST;
723 22 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
724 :
725 44 : auto groupNames = GetGroupNames();
726 26 : for (const auto &name : groupNames)
727 : {
728 8 : auto subGroup = OpenGroup(name);
729 4 : if (subGroup)
730 : {
731 4 : nCost += subGroup->GetTotalCopyCost();
732 : }
733 : }
734 :
735 22 : auto arrayNames = GetMDArrayNames();
736 61 : for (const auto &name : arrayNames)
737 : {
738 78 : auto array = OpenMDArray(name);
739 39 : if (array)
740 : {
741 39 : nCost += array->GetTotalCopyCost();
742 : }
743 : }
744 44 : return nCost;
745 : }
746 :
747 : /************************************************************************/
748 : /* CopyFrom() */
749 : /************************************************************************/
750 :
751 : /** Copy the content of a group into a new (generally empty) group.
752 : *
753 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
754 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
755 : * of some output drivers this is not recommended)
756 : * @param poSrcGroup Source group. Must NOT be nullptr.
757 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
758 : * stop the copy. In relaxed mode, the copy will be attempted to
759 : * be pursued.
760 : * @param nCurCost Should be provided as a variable initially set to 0.
761 : * @param nTotalCost Total cost from GetTotalCopyCost().
762 : * @param pfnProgress Progress callback, or nullptr.
763 : * @param pProgressData Progress user data, or nulptr.
764 : * @param papszOptions Creation options. Currently, only array creation
765 : * options are supported. They must be prefixed with
766 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
767 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
768 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
769 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
770 : * Restriction to arrays of a given name is done with adding
771 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
772 : * a full qualified name.
773 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
774 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
775 : * scaled to UInt16 with scale and offset values being computed from the minimum
776 : * and maximum of the source array. The integer data type used can be set with
777 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
778 : *
779 : * @return true in case of success (or partial success if bStrict == false).
780 : */
781 22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
782 : GDALDataset *poSrcDS,
783 : const std::shared_ptr<GDALGroup> &poSrcGroup,
784 : bool bStrict, GUInt64 &nCurCost,
785 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
786 : void *pProgressData, CSLConstList papszOptions)
787 : {
788 22 : if (pfnProgress == nullptr)
789 0 : pfnProgress = GDALDummyProgress;
790 :
791 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
792 : if (!(x)) \
793 : { \
794 : if (bStrict) \
795 : return false; \
796 : continue; \
797 : } \
798 : (void)0
799 :
800 : try
801 : {
802 22 : nCurCost += GDALGroup::COPY_COST;
803 :
804 44 : const auto srcDims = poSrcGroup->GetDimensions();
805 : std::map<std::string, std::shared_ptr<GDALDimension>>
806 44 : mapExistingDstDims;
807 44 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
808 56 : for (const auto &dim : srcDims)
809 : {
810 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
811 34 : dim->GetDirection(), dim->GetSize());
812 34 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
813 34 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
814 68 : auto poIndexingVarSrc(dim->GetIndexingVariable());
815 34 : if (poIndexingVarSrc)
816 : {
817 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
818 16 : ->GetName()] =
819 32 : dim->GetName();
820 : }
821 : }
822 :
823 44 : auto attrs = poSrcGroup->GetAttributes();
824 28 : for (const auto &attr : attrs)
825 : {
826 : auto dstAttr =
827 6 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
828 12 : attr->GetDataType());
829 6 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
830 6 : auto raw(attr->ReadAsRaw());
831 6 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
832 0 : return false;
833 : }
834 22 : if (!attrs.empty())
835 : {
836 4 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
837 4 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
838 0 : return false;
839 : }
840 :
841 : const auto CopyArray =
842 39 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
843 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
844 : papszOptions, bStrict, &nCurCost,
845 355 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
846 : {
847 : // Map source dimensions to target dimensions
848 78 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
849 39 : const auto &srcArrayDims(srcArray->GetDimensions());
850 99 : for (const auto &dim : srcArrayDims)
851 : {
852 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
853 60 : dim->GetFullName());
854 60 : if (dstDim && dstDim->GetSize() == dim->GetSize())
855 : {
856 50 : dstArrayDims.emplace_back(dstDim);
857 : }
858 : else
859 : {
860 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
861 19 : if (oIter != mapExistingDstDims.end() &&
862 9 : oIter->second->GetSize() == dim->GetSize())
863 : {
864 8 : dstArrayDims.emplace_back(oIter->second);
865 : }
866 : else
867 : {
868 2 : std::string newDimName;
869 2 : if (oIter == mapExistingDstDims.end())
870 : {
871 1 : newDimName = dim->GetName();
872 : }
873 : else
874 : {
875 1 : std::string newDimNamePrefix(srcArray->GetName() +
876 3 : '_' + dim->GetName());
877 1 : newDimName = newDimNamePrefix;
878 1 : int nIterCount = 2;
879 0 : while (
880 1 : cpl::contains(mapExistingDstDims, newDimName))
881 : {
882 0 : newDimName = newDimNamePrefix +
883 0 : CPLSPrintf("_%d", nIterCount);
884 0 : nIterCount++;
885 : }
886 : }
887 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
888 : dim->GetDirection(),
889 4 : dim->GetSize());
890 2 : if (!dstDim)
891 0 : return false;
892 2 : mapExistingDstDims[newDimName] = dstDim;
893 2 : dstArrayDims.emplace_back(dstDim);
894 : }
895 : }
896 : }
897 :
898 78 : CPLStringList aosArrayCO;
899 39 : bool bAutoScale = false;
900 39 : GDALDataType eAutoScaleType = GDT_UInt16;
901 46 : for (const char *pszItem : cpl::Iterate(papszOptions))
902 : {
903 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
904 : {
905 7 : const char *pszOption = pszItem + strlen("ARRAY:");
906 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
907 : {
908 1 : const char *pszNext = strchr(pszOption, ':');
909 1 : if (pszNext != nullptr)
910 : {
911 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
912 1 : if (static_cast<size_t>(nDim) ==
913 1 : dstArrayDims.size())
914 : {
915 1 : pszOption = pszNext + 1;
916 : }
917 : else
918 : {
919 0 : pszOption = nullptr;
920 : }
921 : }
922 : }
923 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
924 : {
925 2 : const char *pszName = pszOption + strlen("IF(NAME=");
926 2 : const char *pszNext = strchr(pszName, ':');
927 2 : if (pszNext != nullptr && pszNext > pszName &&
928 2 : pszNext[-1] == ')')
929 : {
930 4 : CPLString osName;
931 2 : osName.assign(pszName, pszNext - pszName - 1);
932 3 : if (osName == srcArray->GetName() ||
933 1 : osName == srcArray->GetFullName())
934 : {
935 2 : pszOption = pszNext + 1;
936 : }
937 : else
938 : {
939 0 : pszOption = nullptr;
940 : }
941 : }
942 : }
943 7 : if (pszOption)
944 : {
945 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
946 : {
947 : bAutoScale =
948 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
949 : }
950 5 : else if (STARTS_WITH_CI(pszOption,
951 : "AUTOSCALE_DATA_TYPE="))
952 : {
953 1 : const char *pszDataType =
954 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
955 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
956 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
957 1 : GDALDataTypeIsFloating(eAutoScaleType))
958 : {
959 0 : CPLError(CE_Failure, CPLE_NotSupported,
960 : "Unsupported value for "
961 : "AUTOSCALE_DATA_TYPE");
962 0 : return false;
963 : }
964 : }
965 : else
966 : {
967 4 : aosArrayCO.AddString(pszOption);
968 : }
969 : }
970 : }
971 : }
972 :
973 : auto oIterDimName =
974 39 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
975 39 : const auto &srcArrayType = srcArray->GetDataType();
976 :
977 39 : std::shared_ptr<GDALMDArray> dstArray;
978 :
979 : // Only autoscale non-indexing variables
980 39 : bool bHasOffset = false;
981 39 : bool bHasScale = false;
982 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
983 2 : (srcArrayType.GetNumericDataType() == GDT_Float32 ||
984 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
985 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
986 43 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
987 41 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
988 : {
989 2 : constexpr bool bApproxOK = false;
990 2 : constexpr bool bForce = true;
991 2 : double dfMin = 0.0;
992 2 : double dfMax = 0.0;
993 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
994 : nullptr, nullptr, nullptr, nullptr,
995 2 : nullptr) != CE_None)
996 : {
997 0 : CPLError(CE_Failure, CPLE_AppDefined,
998 : "Could not retrieve statistics for array %s",
999 0 : srcArray->GetName().c_str());
1000 0 : return false;
1001 : }
1002 2 : double dfDTMin = 0;
1003 2 : double dfDTMax = 0;
1004 : #define setDTMinMax(ctype) \
1005 : do \
1006 : { \
1007 : dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min()); \
1008 : dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max()); \
1009 : } while (0)
1010 :
1011 2 : switch (eAutoScaleType)
1012 : {
1013 0 : case GDT_Byte:
1014 0 : setDTMinMax(GByte);
1015 0 : break;
1016 0 : case GDT_Int8:
1017 0 : setDTMinMax(GInt8);
1018 0 : break;
1019 1 : case GDT_UInt16:
1020 1 : setDTMinMax(GUInt16);
1021 1 : break;
1022 1 : case GDT_Int16:
1023 1 : setDTMinMax(GInt16);
1024 1 : break;
1025 0 : case GDT_UInt32:
1026 0 : setDTMinMax(GUInt32);
1027 0 : break;
1028 0 : case GDT_Int32:
1029 0 : setDTMinMax(GInt32);
1030 0 : break;
1031 0 : case GDT_UInt64:
1032 0 : setDTMinMax(std::uint64_t);
1033 0 : break;
1034 0 : case GDT_Int64:
1035 0 : setDTMinMax(std::int64_t);
1036 0 : break;
1037 0 : case GDT_Float32:
1038 : case GDT_Float64:
1039 : case GDT_Unknown:
1040 : case GDT_CInt16:
1041 : case GDT_CInt32:
1042 : case GDT_CFloat32:
1043 : case GDT_CFloat64:
1044 : case GDT_TypeCount:
1045 0 : CPLAssert(false);
1046 : }
1047 :
1048 : dstArray =
1049 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1050 4 : GDALExtendedDataType::Create(eAutoScaleType),
1051 4 : aosArrayCO.List());
1052 2 : if (!dstArray)
1053 0 : return !bStrict;
1054 :
1055 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1056 : {
1057 : // If there's a nodata value in the source array, reserve
1058 : // DTMax for that purpose in the target scaled array
1059 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1060 : {
1061 0 : CPLError(CE_Failure, CPLE_AppDefined,
1062 : "Cannot set nodata value");
1063 0 : return false;
1064 : }
1065 1 : dfDTMax--;
1066 : }
1067 2 : const double dfScale =
1068 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1069 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1070 :
1071 4 : if (!dstArray->SetOffset(dfOffset) ||
1072 2 : !dstArray->SetScale(dfScale))
1073 : {
1074 0 : CPLError(CE_Failure, CPLE_AppDefined,
1075 : "Cannot set scale/offset");
1076 0 : return false;
1077 : }
1078 :
1079 2 : auto poUnscaled = dstArray->GetUnscaled();
1080 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1081 : {
1082 1 : poUnscaled->SetNoDataValue(
1083 : srcArray->GetNoDataValueAsDouble());
1084 : }
1085 :
1086 : // Copy source array into unscaled array
1087 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1088 : nCurCost, nTotalCost, pfnProgress,
1089 2 : pProgressData))
1090 : {
1091 0 : return false;
1092 : }
1093 : }
1094 : else
1095 : {
1096 74 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1097 74 : srcArrayType, aosArrayCO.List());
1098 37 : if (!dstArray)
1099 0 : return !bStrict;
1100 :
1101 74 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1102 : nCurCost, nTotalCost, pfnProgress,
1103 37 : pProgressData))
1104 : {
1105 0 : return false;
1106 : }
1107 : }
1108 :
1109 : // If this array is the indexing variable of a dimension, link them
1110 : // together.
1111 39 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1112 : {
1113 : auto oCorrespondingDimIter =
1114 16 : mapExistingDstDims.find(oIterDimName->second);
1115 16 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1116 : {
1117 : CPLErrorStateBackuper oErrorStateBackuper(
1118 16 : CPLQuietErrorHandler);
1119 32 : oCorrespondingDimIter->second->SetIndexingVariable(
1120 16 : std::move(dstArray));
1121 : }
1122 : }
1123 :
1124 39 : return true;
1125 22 : };
1126 :
1127 44 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1128 :
1129 : // Start by copying arrays that are indexing variables of dimensions
1130 61 : for (const auto &name : arrayNames)
1131 : {
1132 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1133 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1134 :
1135 39 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1136 39 : srcArray->GetName()))
1137 : {
1138 16 : if (!CopyArray(srcArray))
1139 0 : return false;
1140 : }
1141 : }
1142 :
1143 : // Then copy regular arrays
1144 61 : for (const auto &name : arrayNames)
1145 : {
1146 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1147 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1148 :
1149 39 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1150 39 : srcArray->GetName()))
1151 : {
1152 23 : if (!CopyArray(srcArray))
1153 0 : return false;
1154 : }
1155 : }
1156 :
1157 44 : const auto groupNames = poSrcGroup->GetGroupNames();
1158 26 : for (const auto &name : groupNames)
1159 : {
1160 4 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1161 4 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1162 4 : auto dstSubGroup = CreateGroup(name);
1163 4 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1164 8 : if (!dstSubGroup->CopyFrom(
1165 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1166 4 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1167 0 : return false;
1168 : }
1169 :
1170 22 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1171 0 : return false;
1172 :
1173 22 : return true;
1174 : }
1175 0 : catch (const std::exception &e)
1176 : {
1177 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1178 0 : return false;
1179 : }
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* GetInnerMostGroup() */
1184 : /************************************************************************/
1185 :
1186 : //! @cond Doxygen_Suppress
1187 : const GDALGroup *
1188 1051 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1189 : std::shared_ptr<GDALGroup> &curGroupHolder,
1190 : std::string &osLastPart) const
1191 : {
1192 1051 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1193 6 : return nullptr;
1194 1045 : const GDALGroup *poCurGroup = this;
1195 : CPLStringList aosTokens(
1196 2090 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1197 1045 : if (aosTokens.size() == 0)
1198 : {
1199 0 : return nullptr;
1200 : }
1201 :
1202 1380 : for (int i = 0; i < aosTokens.size() - 1; i++)
1203 : {
1204 343 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1205 343 : if (!curGroupHolder)
1206 : {
1207 8 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1208 : aosTokens[i]);
1209 8 : return nullptr;
1210 : }
1211 335 : poCurGroup = curGroupHolder.get();
1212 : }
1213 1037 : osLastPart = aosTokens[aosTokens.size() - 1];
1214 1037 : return poCurGroup;
1215 : }
1216 :
1217 : //! @endcond
1218 :
1219 : /************************************************************************/
1220 : /* OpenMDArrayFromFullname() */
1221 : /************************************************************************/
1222 :
1223 : /** Get an array from its fully qualified name */
1224 : std::shared_ptr<GDALMDArray>
1225 364 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1226 : CSLConstList papszOptions) const
1227 : {
1228 728 : std::string osName;
1229 364 : std::shared_ptr<GDALGroup> curGroupHolder;
1230 364 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1231 364 : if (poGroup == nullptr)
1232 10 : return nullptr;
1233 354 : return poGroup->OpenMDArray(osName, papszOptions);
1234 : }
1235 :
1236 : /************************************************************************/
1237 : /* ResolveMDArray() */
1238 : /************************************************************************/
1239 :
1240 : /** Locate an array in a group and its subgroups by name.
1241 : *
1242 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1243 : * used
1244 : * Otherwise the search will start from the group identified by osStartingPath,
1245 : * and an array whose name is osName will be looked for in this group (if
1246 : * osStartingPath is empty or "/", then the current group is used). If there
1247 : * is no match, then a recursive descendent search will be made in its
1248 : * subgroups. If there is no match in the subgroups, then the parent (if
1249 : * existing) of the group pointed by osStartingPath will be used as the new
1250 : * starting point for the search.
1251 : *
1252 : * @param osName name, qualified or not
1253 : * @param osStartingPath fully qualified name of the (sub-)group from which
1254 : * the search should be started. If this is a non-empty
1255 : * string, the group on which this method is called should
1256 : * nominally be the root group (otherwise the path will
1257 : * be interpreted as from the current group)
1258 : * @param papszOptions options to pass to OpenMDArray()
1259 : * @since GDAL 3.2
1260 : */
1261 : std::shared_ptr<GDALMDArray>
1262 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1263 : const std::string &osStartingPath,
1264 : CSLConstList papszOptions) const
1265 : {
1266 19 : if (!osName.empty() && osName[0] == '/')
1267 : {
1268 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1269 1 : if (poArray)
1270 1 : return poArray;
1271 : }
1272 36 : std::string osPath(osStartingPath);
1273 36 : std::set<std::string> oSetAlreadyVisited;
1274 :
1275 : while (true)
1276 : {
1277 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1278 0 : std::shared_ptr<GDALGroup> poGroup;
1279 :
1280 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1281 22 : bool goOn = false;
1282 22 : if (osPath.empty() || osPath == "/")
1283 : {
1284 11 : goOn = true;
1285 : }
1286 : else
1287 : {
1288 22 : std::string osLastPart;
1289 : const GDALGroup *poGroupPtr =
1290 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1291 11 : if (poGroupPtr)
1292 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1293 22 : if (poGroup &&
1294 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1295 : {
1296 11 : oQueue.push(poGroup);
1297 11 : goOn = true;
1298 : }
1299 : }
1300 :
1301 22 : if (goOn)
1302 : {
1303 17 : do
1304 : {
1305 : const GDALGroup *groupPtr;
1306 39 : if (!oQueue.empty())
1307 : {
1308 28 : poGroup = oQueue.front();
1309 28 : oQueue.pop();
1310 28 : groupPtr = poGroup.get();
1311 : }
1312 : else
1313 : {
1314 11 : groupPtr = this;
1315 : }
1316 :
1317 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1318 39 : if (poArray)
1319 16 : return poArray;
1320 :
1321 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1322 47 : for (const auto &osGroupName : aosGroupNames)
1323 : {
1324 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1325 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1326 48 : poSubGroup->GetFullName()))
1327 : {
1328 24 : oQueue.push(poSubGroup);
1329 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1330 : }
1331 : }
1332 23 : } while (!oQueue.empty());
1333 : }
1334 :
1335 6 : if (osPath.empty() || osPath == "/")
1336 2 : break;
1337 :
1338 4 : const auto nPos = osPath.rfind('/');
1339 4 : if (nPos == 0)
1340 1 : osPath = "/";
1341 : else
1342 : {
1343 3 : if (nPos == std::string::npos)
1344 0 : break;
1345 3 : osPath.resize(nPos);
1346 : }
1347 4 : }
1348 2 : return nullptr;
1349 : }
1350 :
1351 : /************************************************************************/
1352 : /* OpenGroupFromFullname() */
1353 : /************************************************************************/
1354 :
1355 : /** Get a group from its fully qualified name.
1356 : * @since GDAL 3.2
1357 : */
1358 : std::shared_ptr<GDALGroup>
1359 551 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1360 : CSLConstList papszOptions) const
1361 : {
1362 1102 : std::string osName;
1363 551 : std::shared_ptr<GDALGroup> curGroupHolder;
1364 551 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1365 551 : if (poGroup == nullptr)
1366 2 : return nullptr;
1367 549 : return poGroup->OpenGroup(osName, papszOptions);
1368 : }
1369 :
1370 : /************************************************************************/
1371 : /* OpenDimensionFromFullname() */
1372 : /************************************************************************/
1373 :
1374 : /** Get a dimension from its fully qualified name */
1375 : std::shared_ptr<GDALDimension>
1376 125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1377 : {
1378 250 : std::string osName;
1379 125 : std::shared_ptr<GDALGroup> curGroupHolder;
1380 125 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1381 125 : if (poGroup == nullptr)
1382 2 : return nullptr;
1383 246 : auto dims(poGroup->GetDimensions());
1384 203 : for (auto &dim : dims)
1385 : {
1386 164 : if (dim->GetName() == osName)
1387 84 : return dim;
1388 : }
1389 39 : return nullptr;
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* ClearStatistics() */
1394 : /************************************************************************/
1395 :
1396 : /**
1397 : * \brief Clear statistics.
1398 : *
1399 : * @since GDAL 3.4
1400 : */
1401 0 : void GDALGroup::ClearStatistics()
1402 : {
1403 0 : auto groupNames = GetGroupNames();
1404 0 : for (const auto &name : groupNames)
1405 : {
1406 0 : auto subGroup = OpenGroup(name);
1407 0 : if (subGroup)
1408 : {
1409 0 : subGroup->ClearStatistics();
1410 : }
1411 : }
1412 :
1413 0 : auto arrayNames = GetMDArrayNames();
1414 0 : for (const auto &name : arrayNames)
1415 : {
1416 0 : auto array = OpenMDArray(name);
1417 0 : if (array)
1418 : {
1419 0 : array->ClearStatistics();
1420 : }
1421 : }
1422 0 : }
1423 :
1424 : /************************************************************************/
1425 : /* Rename() */
1426 : /************************************************************************/
1427 :
1428 : /** Rename the group.
1429 : *
1430 : * This is not implemented by all drivers.
1431 : *
1432 : * Drivers known to implement it: MEM, netCDF, ZARR.
1433 : *
1434 : * This is the same as the C function GDALGroupRename().
1435 : *
1436 : * @param osNewName New name.
1437 : *
1438 : * @return true in case of success
1439 : * @since GDAL 3.8
1440 : */
1441 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1442 : {
1443 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1444 0 : return false;
1445 : }
1446 :
1447 : /************************************************************************/
1448 : /* BaseRename() */
1449 : /************************************************************************/
1450 :
1451 : //! @cond Doxygen_Suppress
1452 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1453 : {
1454 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1455 8 : m_osFullName += osNewName;
1456 8 : m_osName = osNewName;
1457 :
1458 8 : NotifyChildrenOfRenaming();
1459 8 : }
1460 :
1461 : //! @endcond
1462 :
1463 : /************************************************************************/
1464 : /* ParentRenamed() */
1465 : /************************************************************************/
1466 :
1467 : //! @cond Doxygen_Suppress
1468 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1469 : {
1470 7 : m_osFullName = osNewParentFullName;
1471 7 : m_osFullName += "/";
1472 7 : m_osFullName += m_osName;
1473 :
1474 7 : NotifyChildrenOfRenaming();
1475 7 : }
1476 :
1477 : //! @endcond
1478 :
1479 : /************************************************************************/
1480 : /* Deleted() */
1481 : /************************************************************************/
1482 :
1483 : //! @cond Doxygen_Suppress
1484 22 : void GDALGroup::Deleted()
1485 : {
1486 22 : m_bValid = false;
1487 :
1488 22 : NotifyChildrenOfDeletion();
1489 22 : }
1490 :
1491 : //! @endcond
1492 :
1493 : /************************************************************************/
1494 : /* ParentDeleted() */
1495 : /************************************************************************/
1496 :
1497 : //! @cond Doxygen_Suppress
1498 3 : void GDALGroup::ParentDeleted()
1499 : {
1500 3 : Deleted();
1501 3 : }
1502 :
1503 : //! @endcond
1504 :
1505 : /************************************************************************/
1506 : /* CheckValidAndErrorOutIfNot() */
1507 : /************************************************************************/
1508 :
1509 : //! @cond Doxygen_Suppress
1510 11819 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1511 : {
1512 11819 : if (!m_bValid)
1513 : {
1514 14 : CPLError(CE_Failure, CPLE_AppDefined,
1515 : "This object has been deleted. No action on it is possible");
1516 : }
1517 11819 : return m_bValid;
1518 : }
1519 :
1520 : //! @endcond
1521 :
1522 : /************************************************************************/
1523 : /* ~GDALAbstractMDArray() */
1524 : /************************************************************************/
1525 :
1526 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1527 :
1528 : /************************************************************************/
1529 : /* GDALAbstractMDArray() */
1530 : /************************************************************************/
1531 :
1532 : //! @cond Doxygen_Suppress
1533 20211 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1534 20211 : const std::string &osName)
1535 : : m_osName(osName),
1536 : m_osFullName(
1537 20211 : !osParentName.empty()
1538 38783 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1539 79205 : : osName)
1540 : {
1541 20211 : }
1542 :
1543 : //! @endcond
1544 :
1545 : /************************************************************************/
1546 : /* GetDimensions() */
1547 : /************************************************************************/
1548 :
1549 : /** \fn GDALAbstractMDArray::GetDimensions() const
1550 : * \brief Return the dimensions of an attribute/array.
1551 : *
1552 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1553 : * similar to GDALAttributeGetDimensionsSize().
1554 : */
1555 :
1556 : /************************************************************************/
1557 : /* GetDataType() */
1558 : /************************************************************************/
1559 :
1560 : /** \fn GDALAbstractMDArray::GetDataType() const
1561 : * \brief Return the data type of an attribute/array.
1562 : *
1563 : * This is the same as the C functions GDALMDArrayGetDataType() and
1564 : * GDALAttributeGetDataType()
1565 : */
1566 :
1567 : /************************************************************************/
1568 : /* GetDimensionCount() */
1569 : /************************************************************************/
1570 :
1571 : /** Return the number of dimensions.
1572 : *
1573 : * Default implementation is GetDimensions().size(), and may be overridden by
1574 : * drivers if they have a faster / less expensive implementations.
1575 : *
1576 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1577 : * GDALAttributeGetDimensionCount().
1578 : *
1579 : */
1580 22383 : size_t GDALAbstractMDArray::GetDimensionCount() const
1581 : {
1582 22383 : return GetDimensions().size();
1583 : }
1584 :
1585 : /************************************************************************/
1586 : /* Rename() */
1587 : /************************************************************************/
1588 :
1589 : /** Rename the attribute/array.
1590 : *
1591 : * This is not implemented by all drivers.
1592 : *
1593 : * Drivers known to implement it: MEM, netCDF, Zarr.
1594 : *
1595 : * This is the same as the C functions GDALMDArrayRename() or
1596 : * GDALAttributeRename().
1597 : *
1598 : * @param osNewName New name.
1599 : *
1600 : * @return true in case of success
1601 : * @since GDAL 3.8
1602 : */
1603 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1604 : {
1605 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1606 0 : return false;
1607 : }
1608 :
1609 : /************************************************************************/
1610 : /* CopyValue() */
1611 : /************************************************************************/
1612 :
1613 : /** Convert a value from a source type to a destination type.
1614 : *
1615 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1616 : * that must be freed with CPLFree().
1617 : */
1618 78829 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1619 : const GDALExtendedDataType &srcType,
1620 : void *pDst,
1621 : const GDALExtendedDataType &dstType)
1622 : {
1623 154365 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1624 75536 : dstType.GetClass() == GEDTC_NUMERIC)
1625 : {
1626 75329 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1627 : dstType.GetNumericDataType(), 0, 1);
1628 75329 : return true;
1629 : }
1630 6618 : if (srcType.GetClass() == GEDTC_STRING &&
1631 3118 : dstType.GetClass() == GEDTC_STRING)
1632 : {
1633 : const char *srcStrPtr;
1634 2733 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1635 2733 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1636 2733 : *reinterpret_cast<void **>(pDst) = pszDup;
1637 2733 : return true;
1638 : }
1639 974 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1640 207 : dstType.GetClass() == GEDTC_STRING)
1641 : {
1642 207 : const char *str = nullptr;
1643 207 : switch (srcType.GetNumericDataType())
1644 : {
1645 0 : case GDT_Unknown:
1646 0 : break;
1647 0 : case GDT_Byte:
1648 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1649 0 : break;
1650 3 : case GDT_Int8:
1651 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1652 3 : break;
1653 48 : case GDT_UInt16:
1654 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1655 48 : break;
1656 0 : case GDT_Int16:
1657 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1658 0 : break;
1659 8 : case GDT_UInt32:
1660 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1661 8 : break;
1662 54 : case GDT_Int32:
1663 54 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1664 54 : break;
1665 0 : case GDT_UInt64:
1666 : str =
1667 0 : CPLSPrintf(CPL_FRMT_GUIB,
1668 : static_cast<GUIntBig>(
1669 : *static_cast<const std::uint64_t *>(pSrc)));
1670 0 : break;
1671 0 : case GDT_Int64:
1672 0 : str = CPLSPrintf(CPL_FRMT_GIB,
1673 : static_cast<GIntBig>(
1674 : *static_cast<const std::int64_t *>(pSrc)));
1675 0 : break;
1676 17 : case GDT_Float32:
1677 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1678 17 : break;
1679 75 : case GDT_Float64:
1680 75 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1681 75 : break;
1682 2 : case GDT_CInt16:
1683 : {
1684 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1685 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1686 2 : break;
1687 : }
1688 0 : case GDT_CInt32:
1689 : {
1690 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1691 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1692 0 : break;
1693 : }
1694 0 : case GDT_CFloat32:
1695 : {
1696 0 : const float *src = static_cast<const float *>(pSrc);
1697 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1698 0 : break;
1699 : }
1700 0 : case GDT_CFloat64:
1701 : {
1702 0 : const double *src = static_cast<const double *>(pSrc);
1703 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1704 0 : break;
1705 : }
1706 0 : case GDT_TypeCount:
1707 0 : CPLAssert(false);
1708 : break;
1709 : }
1710 207 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1711 207 : *reinterpret_cast<void **>(pDst) = pszDup;
1712 207 : return true;
1713 : }
1714 945 : if (srcType.GetClass() == GEDTC_STRING &&
1715 385 : dstType.GetClass() == GEDTC_NUMERIC)
1716 : {
1717 : const char *srcStrPtr;
1718 385 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1719 385 : if (dstType.GetNumericDataType() == GDT_Int64)
1720 : {
1721 2 : *(static_cast<int64_t *>(pDst)) =
1722 2 : srcStrPtr == nullptr ? 0
1723 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1724 : }
1725 383 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1726 : {
1727 2 : *(static_cast<uint64_t *>(pDst)) =
1728 2 : srcStrPtr == nullptr
1729 2 : ? 0
1730 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1731 : }
1732 : else
1733 : {
1734 : // FIXME GDT_UInt64
1735 381 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1736 381 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1737 : dstType.GetNumericDataType(), 0, 1);
1738 : }
1739 385 : return true;
1740 : }
1741 350 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1742 175 : dstType.GetClass() == GEDTC_COMPOUND)
1743 : {
1744 175 : const auto &srcComponents = srcType.GetComponents();
1745 175 : const auto &dstComponents = dstType.GetComponents();
1746 175 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1747 175 : GByte *pabyDst = static_cast<GByte *>(pDst);
1748 :
1749 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1750 350 : srcComponentMap;
1751 688 : for (const auto &srcComp : srcComponents)
1752 : {
1753 513 : srcComponentMap[srcComp->GetName()] = &srcComp;
1754 : }
1755 504 : for (const auto &dstComp : dstComponents)
1756 : {
1757 329 : auto oIter = srcComponentMap.find(dstComp->GetName());
1758 329 : if (oIter == srcComponentMap.end())
1759 0 : return false;
1760 329 : const auto &srcComp = *(oIter->second);
1761 987 : if (!GDALExtendedDataType::CopyValue(
1762 329 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1763 329 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1764 : {
1765 0 : return false;
1766 : }
1767 : }
1768 175 : return true;
1769 : }
1770 :
1771 0 : return false;
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* CopyValues() */
1776 : /************************************************************************/
1777 :
1778 : /** Convert severals value from a source type to a destination type.
1779 : *
1780 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1781 : * that must be freed with CPLFree().
1782 : */
1783 328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1784 : const GDALExtendedDataType &srcType,
1785 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1786 : const GDALExtendedDataType &dstType,
1787 : GPtrDiff_t nDstStrideInElts,
1788 : size_t nValues)
1789 : {
1790 : const auto nSrcStrideInBytes =
1791 328 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1792 : const auto nDstStrideInBytes =
1793 328 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1794 594 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1795 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1796 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1797 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1798 860 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1799 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1800 : {
1801 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1802 : static_cast<int>(nSrcStrideInBytes), pDst,
1803 : dstType.GetNumericDataType(),
1804 : static_cast<int>(nDstStrideInBytes), nValues);
1805 : }
1806 : else
1807 : {
1808 62 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1809 62 : GByte *pabyDst = static_cast<GByte *>(pDst);
1810 124 : for (size_t i = 0; i < nValues; ++i)
1811 : {
1812 62 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1813 0 : return false;
1814 62 : pabySrc += nSrcStrideInBytes;
1815 62 : pabyDst += nDstStrideInBytes;
1816 : }
1817 : }
1818 328 : return true;
1819 : }
1820 :
1821 : /************************************************************************/
1822 : /* CheckReadWriteParams() */
1823 : /************************************************************************/
1824 : //! @cond Doxygen_Suppress
1825 7869 : bool GDALAbstractMDArray::CheckReadWriteParams(
1826 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1827 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1828 : const void *buffer, const void *buffer_alloc_start,
1829 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1830 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1831 : {
1832 0 : const auto lamda_error = []()
1833 : {
1834 0 : CPLError(CE_Failure, CPLE_AppDefined,
1835 : "Not all elements pointed by buffer will fit in "
1836 : "[buffer_alloc_start, "
1837 : "buffer_alloc_start + buffer_alloc_size]");
1838 0 : };
1839 :
1840 7869 : const auto &dims = GetDimensions();
1841 7869 : if (dims.empty())
1842 : {
1843 3034 : if (buffer_alloc_start)
1844 : {
1845 2667 : const size_t elementSize = bufferDataType.GetSize();
1846 2667 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1847 2667 : const GByte *paby_buffer_alloc_start =
1848 : static_cast<const GByte *>(buffer_alloc_start);
1849 2667 : const GByte *paby_buffer_alloc_end =
1850 : paby_buffer_alloc_start + buffer_alloc_size;
1851 :
1852 2667 : if (paby_buffer < paby_buffer_alloc_start ||
1853 2667 : paby_buffer + elementSize > paby_buffer_alloc_end)
1854 : {
1855 0 : lamda_error();
1856 0 : return false;
1857 : }
1858 : }
1859 3034 : return true;
1860 : }
1861 :
1862 4835 : if (arrayStep == nullptr)
1863 : {
1864 1270 : tmp_arrayStep.resize(dims.size(), 1);
1865 1270 : arrayStep = tmp_arrayStep.data();
1866 : }
1867 13691 : for (size_t i = 0; i < dims.size(); i++)
1868 : {
1869 8856 : if (count[i] == 0)
1870 : {
1871 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1872 : static_cast<unsigned>(i));
1873 0 : return false;
1874 : }
1875 : }
1876 4835 : bool bufferStride_all_positive = true;
1877 4835 : if (bufferStride == nullptr)
1878 : {
1879 983 : GPtrDiff_t stride = 1;
1880 : // To compute strides we must proceed from the fastest varying dimension
1881 : // (the last one), and then reverse the result
1882 2237 : for (size_t i = dims.size(); i != 0;)
1883 : {
1884 1254 : --i;
1885 1254 : tmp_bufferStride.push_back(stride);
1886 1254 : GUInt64 newStride = 0;
1887 : bool bOK;
1888 : try
1889 : {
1890 1254 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1891 2508 : CPLSM(static_cast<uint64_t>(count[i])))
1892 1254 : .v();
1893 1254 : bOK = static_cast<size_t>(newStride) == newStride &&
1894 1254 : newStride < std::numeric_limits<size_t>::max() / 2;
1895 : }
1896 0 : catch (...)
1897 : {
1898 0 : bOK = false;
1899 : }
1900 1254 : if (!bOK)
1901 : {
1902 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1903 0 : return false;
1904 : }
1905 1254 : stride = static_cast<GPtrDiff_t>(newStride);
1906 : }
1907 983 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1908 983 : bufferStride = tmp_bufferStride.data();
1909 : }
1910 : else
1911 : {
1912 11452 : for (size_t i = 0; i < dims.size(); i++)
1913 : {
1914 7601 : if (bufferStride[i] < 0)
1915 : {
1916 1 : bufferStride_all_positive = false;
1917 1 : break;
1918 : }
1919 : }
1920 : }
1921 13662 : for (size_t i = 0; i < dims.size(); i++)
1922 : {
1923 8837 : if (arrayStartIdx[i] >= dims[i]->GetSize())
1924 : {
1925 2 : CPLError(CE_Failure, CPLE_AppDefined,
1926 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1927 : static_cast<unsigned>(i),
1928 2 : static_cast<GUInt64>(arrayStartIdx[i]),
1929 2 : static_cast<GUInt64>(dims[i]->GetSize()));
1930 2 : return false;
1931 : }
1932 : bool bOverflow;
1933 8835 : if (arrayStep[i] >= 0)
1934 : {
1935 : try
1936 : {
1937 8241 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
1938 8243 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
1939 32967 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
1940 8241 : .v() >= dims[i]->GetSize();
1941 : }
1942 1 : catch (...)
1943 : {
1944 1 : bOverflow = true;
1945 : }
1946 8242 : if (bOverflow)
1947 : {
1948 5 : CPLError(CE_Failure, CPLE_AppDefined,
1949 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
1950 : ">= " CPL_FRMT_GUIB,
1951 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1952 : static_cast<unsigned>(i),
1953 5 : static_cast<GUInt64>(dims[i]->GetSize()));
1954 5 : return false;
1955 : }
1956 : }
1957 : else
1958 : {
1959 : try
1960 : {
1961 593 : bOverflow =
1962 593 : arrayStartIdx[i] <
1963 593 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
1964 1186 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
1965 : ? (static_cast<uint64_t>(1) << 63)
1966 1186 : : static_cast<uint64_t>(-arrayStep[i])))
1967 593 : .v();
1968 : }
1969 0 : catch (...)
1970 : {
1971 0 : bOverflow = true;
1972 : }
1973 593 : if (bOverflow)
1974 : {
1975 3 : CPLError(
1976 : CE_Failure, CPLE_AppDefined,
1977 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
1978 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1979 : static_cast<unsigned>(i));
1980 3 : return false;
1981 : }
1982 : }
1983 : }
1984 :
1985 4825 : if (buffer_alloc_start)
1986 : {
1987 2524 : const size_t elementSize = bufferDataType.GetSize();
1988 2524 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1989 2524 : const GByte *paby_buffer_alloc_start =
1990 : static_cast<const GByte *>(buffer_alloc_start);
1991 2524 : const GByte *paby_buffer_alloc_end =
1992 : paby_buffer_alloc_start + buffer_alloc_size;
1993 2524 : if (bufferStride_all_positive)
1994 : {
1995 2524 : if (paby_buffer < paby_buffer_alloc_start)
1996 : {
1997 0 : lamda_error();
1998 0 : return false;
1999 : }
2000 2524 : GUInt64 nOffset = elementSize;
2001 7289 : for (size_t i = 0; i < dims.size(); i++)
2002 : {
2003 : try
2004 : {
2005 4765 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2006 4765 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2007 9530 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2008 19060 : CPLSM(static_cast<uint64_t>(elementSize)))
2009 4765 : .v();
2010 : }
2011 0 : catch (...)
2012 : {
2013 0 : lamda_error();
2014 0 : return false;
2015 : }
2016 : }
2017 : #if SIZEOF_VOIDP == 4
2018 : if (static_cast<size_t>(nOffset) != nOffset)
2019 : {
2020 : lamda_error();
2021 : return false;
2022 : }
2023 : #endif
2024 2524 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2025 : {
2026 0 : lamda_error();
2027 0 : return false;
2028 : }
2029 : }
2030 0 : else if (dims.size() < 31)
2031 : {
2032 : // Check all corners of the hypercube
2033 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2034 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2035 : {
2036 0 : const GByte *paby = paby_buffer;
2037 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2038 : i++)
2039 : {
2040 0 : if (iCornerCode & (1U << i))
2041 : {
2042 : // We should check for integer overflows
2043 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2044 : }
2045 : }
2046 0 : if (paby < paby_buffer_alloc_start ||
2047 0 : paby + elementSize > paby_buffer_alloc_end)
2048 : {
2049 0 : lamda_error();
2050 0 : return false;
2051 : }
2052 : }
2053 : }
2054 : }
2055 :
2056 4825 : return true;
2057 : }
2058 :
2059 : //! @endcond
2060 :
2061 : /************************************************************************/
2062 : /* Read() */
2063 : /************************************************************************/
2064 :
2065 : /** Read part or totality of a multidimensional array or attribute.
2066 : *
2067 : * This will extract the content of a hyper-rectangle from the array into
2068 : * a user supplied buffer.
2069 : *
2070 : * If bufferDataType is of type string, the values written in pDstBuffer
2071 : * will be char* pointers and the strings should be freed with CPLFree().
2072 : *
2073 : * This is the same as the C function GDALMDArrayRead().
2074 : *
2075 : * @param arrayStartIdx Values representing the starting index to read
2076 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2077 : * Array of GetDimensionCount() values. Must not be
2078 : * nullptr, unless for a zero-dimensional array.
2079 : *
2080 : * @param count Values representing the number of values to extract in
2081 : * each dimension.
2082 : * Array of GetDimensionCount() values. Must not be
2083 : * nullptr, unless for a zero-dimensional array.
2084 : *
2085 : * @param arrayStep Spacing between values to extract in each dimension.
2086 : * The spacing is in number of array elements, not bytes.
2087 : * If provided, must contain GetDimensionCount() values.
2088 : * If set to nullptr, [1, 1, ... 1] will be used as a
2089 : * default to indicate consecutive elements.
2090 : *
2091 : * @param bufferStride Spacing between values to store in pDstBuffer.
2092 : * The spacing is in number of array elements, not bytes.
2093 : * If provided, must contain GetDimensionCount() values.
2094 : * Negative values are possible (for example to reorder
2095 : * from bottom-to-top to top-to-bottom).
2096 : * If set to nullptr, will be set so that pDstBuffer is
2097 : * written in a compact way, with elements of the last /
2098 : * fastest varying dimension being consecutive.
2099 : *
2100 : * @param bufferDataType Data type of values in pDstBuffer.
2101 : *
2102 : * @param pDstBuffer User buffer to store the values read. Should be big
2103 : * enough to store the number of values indicated by
2104 : * count[] and with the spacing of bufferStride[].
2105 : *
2106 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2107 : * validity of pDstBuffer. pDstBufferAllocStart
2108 : * should be the pointer returned by the malloc() or equivalent call used to
2109 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2110 : * bufferStride[] values are all positive), but not necessarily. If specified,
2111 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2112 : * validation is needed, nullptr can be passed.
2113 : *
2114 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2115 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2116 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2117 : * set to the appropriate value.
2118 : * If no validation is needed, 0 can be passed.
2119 : *
2120 : * @return true in case of success.
2121 : */
2122 2344 : bool GDALAbstractMDArray::Read(
2123 : const GUInt64 *arrayStartIdx, const size_t *count,
2124 : const GInt64 *arrayStep, // step in elements
2125 : const GPtrDiff_t *bufferStride, // stride in elements
2126 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2127 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2128 : {
2129 2344 : if (!GetDataType().CanConvertTo(bufferDataType))
2130 : {
2131 0 : CPLError(CE_Failure, CPLE_AppDefined,
2132 : "Array data type is not convertible to buffer data type");
2133 0 : return false;
2134 : }
2135 :
2136 4688 : std::vector<GInt64> tmp_arrayStep;
2137 4688 : std::vector<GPtrDiff_t> tmp_bufferStride;
2138 2344 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2139 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2140 : nDstBufferAllocSize, tmp_arrayStep,
2141 : tmp_bufferStride))
2142 : {
2143 0 : return false;
2144 : }
2145 :
2146 2344 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2147 2344 : pDstBuffer);
2148 : }
2149 :
2150 : /************************************************************************/
2151 : /* IWrite() */
2152 : /************************************************************************/
2153 :
2154 : //! @cond Doxygen_Suppress
2155 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2156 : const GInt64 *, const GPtrDiff_t *,
2157 : const GDALExtendedDataType &, const void *)
2158 : {
2159 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2160 1 : return false;
2161 : }
2162 :
2163 : //! @endcond
2164 :
2165 : /************************************************************************/
2166 : /* Write() */
2167 : /************************************************************************/
2168 :
2169 : /** Write part or totality of a multidimensional array or attribute.
2170 : *
2171 : * This will set the content of a hyper-rectangle into the array from
2172 : * a user supplied buffer.
2173 : *
2174 : * If bufferDataType is of type string, the values read from pSrcBuffer
2175 : * will be char* pointers.
2176 : *
2177 : * This is the same as the C function GDALMDArrayWrite().
2178 : *
2179 : * @param arrayStartIdx Values representing the starting index to write
2180 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2181 : * Array of GetDimensionCount() values. Must not be
2182 : * nullptr, unless for a zero-dimensional array.
2183 : *
2184 : * @param count Values representing the number of values to write in
2185 : * each dimension.
2186 : * Array of GetDimensionCount() values. Must not be
2187 : * nullptr, unless for a zero-dimensional array.
2188 : *
2189 : * @param arrayStep Spacing between values to write in each dimension.
2190 : * The spacing is in number of array elements, not bytes.
2191 : * If provided, must contain GetDimensionCount() values.
2192 : * If set to nullptr, [1, 1, ... 1] will be used as a
2193 : * default to indicate consecutive elements.
2194 : *
2195 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2196 : * The spacing is in number of array elements, not bytes.
2197 : * If provided, must contain GetDimensionCount() values.
2198 : * Negative values are possible (for example to reorder
2199 : * from bottom-to-top to top-to-bottom).
2200 : * If set to nullptr, will be set so that pSrcBuffer is
2201 : * written in a compact way, with elements of the last /
2202 : * fastest varying dimension being consecutive.
2203 : *
2204 : * @param bufferDataType Data type of values in pSrcBuffer.
2205 : *
2206 : * @param pSrcBuffer User buffer to read the values from. Should be big
2207 : * enough to store the number of values indicated by
2208 : * count[] and with the spacing of bufferStride[].
2209 : *
2210 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2211 : * validity of pSrcBuffer. pSrcBufferAllocStart
2212 : * should be the pointer returned by the malloc() or equivalent call used to
2213 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2214 : * bufferStride[] values are all positive), but not necessarily. If specified,
2215 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2216 : * validation is needed, nullptr can be passed.
2217 : *
2218 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2219 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2220 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2221 : * set to the appropriate value.
2222 : * If no validation is needed, 0 can be passed.
2223 : *
2224 : * @return true in case of success.
2225 : */
2226 1774 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2227 : const size_t *count, const GInt64 *arrayStep,
2228 : const GPtrDiff_t *bufferStride,
2229 : const GDALExtendedDataType &bufferDataType,
2230 : const void *pSrcBuffer,
2231 : const void *pSrcBufferAllocStart,
2232 : size_t nSrcBufferAllocSize)
2233 : {
2234 1774 : if (!bufferDataType.CanConvertTo(GetDataType()))
2235 : {
2236 0 : CPLError(CE_Failure, CPLE_AppDefined,
2237 : "Buffer data type is not convertible to array data type");
2238 0 : return false;
2239 : }
2240 :
2241 3548 : std::vector<GInt64> tmp_arrayStep;
2242 3548 : std::vector<GPtrDiff_t> tmp_bufferStride;
2243 1774 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2244 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2245 : nSrcBufferAllocSize, tmp_arrayStep,
2246 : tmp_bufferStride))
2247 : {
2248 0 : return false;
2249 : }
2250 :
2251 1774 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2252 1774 : pSrcBuffer);
2253 : }
2254 :
2255 : /************************************************************************/
2256 : /* GetTotalElementsCount() */
2257 : /************************************************************************/
2258 :
2259 : /** Return the total number of values in the array.
2260 : *
2261 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2262 : * and GDALAttributeGetTotalElementsCount().
2263 : *
2264 : */
2265 1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2266 : {
2267 1022 : const auto &dims = GetDimensions();
2268 1022 : if (dims.empty())
2269 504 : return 1;
2270 518 : GUInt64 nElts = 1;
2271 1146 : for (const auto &dim : dims)
2272 : {
2273 : try
2274 : {
2275 628 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2276 1884 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2277 628 : .v();
2278 : }
2279 0 : catch (...)
2280 : {
2281 0 : return 0;
2282 : }
2283 : }
2284 518 : return nElts;
2285 : }
2286 :
2287 : /************************************************************************/
2288 : /* GetBlockSize() */
2289 : /************************************************************************/
2290 :
2291 : /** Return the "natural" block size of the array along all dimensions.
2292 : *
2293 : * Some drivers might organize the array in tiles/blocks and reading/writing
2294 : * aligned on those tile/block boundaries will be more efficient.
2295 : *
2296 : * The returned number of elements in the vector is the same as
2297 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2298 : * the natural block size along the considered dimension.
2299 : * "Flat" arrays will typically return a vector of values set to 0.
2300 : *
2301 : * The default implementation will return a vector of values set to 0.
2302 : *
2303 : * This method is used by GetProcessingChunkSize().
2304 : *
2305 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2306 : * theoretical case of a 32-bit platform, this might exceed its size_t
2307 : * allocation capabilities.
2308 : *
2309 : * This is the same as the C function GDALMDArrayGetBlockSize().
2310 : *
2311 : * @return the block size, in number of elements along each dimension.
2312 : */
2313 221 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2314 : {
2315 221 : return std::vector<GUInt64>(GetDimensionCount());
2316 : }
2317 :
2318 : /************************************************************************/
2319 : /* GetProcessingChunkSize() */
2320 : /************************************************************************/
2321 :
2322 : /** \brief Return an optimal chunk size for read/write operations, given the
2323 : * natural block size and memory constraints specified.
2324 : *
2325 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2326 : * multiple of those returned by GetBlockSize() (unless the block define by
2327 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2328 : * returned by this method).
2329 : *
2330 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2331 : *
2332 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2333 : * chunk.
2334 : *
2335 : * @return the chunk size, in number of elements along each dimension.
2336 : */
2337 : std::vector<size_t>
2338 60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2339 : {
2340 60 : const auto &dims = GetDimensions();
2341 60 : const auto &nDTSize = GetDataType().GetSize();
2342 60 : std::vector<size_t> anChunkSize;
2343 120 : auto blockSize = GetBlockSize();
2344 60 : CPLAssert(blockSize.size() == dims.size());
2345 60 : size_t nChunkSize = nDTSize;
2346 60 : bool bOverflow = false;
2347 60 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2348 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2349 : // [1, min(sizet_max, dim_size[i])]
2350 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2351 173 : for (size_t i = 0; i < dims.size(); i++)
2352 : {
2353 : const auto sizeDimI =
2354 226 : std::max(static_cast<size_t>(1),
2355 226 : static_cast<size_t>(
2356 226 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2357 113 : std::min(blockSize[i], dims[i]->GetSize()))));
2358 113 : anChunkSize.push_back(sizeDimI);
2359 113 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2360 : {
2361 4 : bOverflow = true;
2362 : }
2363 : else
2364 : {
2365 109 : nChunkSize *= sizeDimI;
2366 : }
2367 : }
2368 60 : if (nChunkSize == 0)
2369 0 : return anChunkSize;
2370 :
2371 : // If the product of all anChunkSize[i] does not fit on size_t, then
2372 : // set lowest anChunkSize[i] to 1.
2373 60 : if (bOverflow)
2374 : {
2375 2 : nChunkSize = nDTSize;
2376 2 : bOverflow = false;
2377 8 : for (size_t i = dims.size(); i > 0;)
2378 : {
2379 6 : --i;
2380 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2381 : {
2382 4 : bOverflow = true;
2383 4 : anChunkSize[i] = 1;
2384 : }
2385 : else
2386 : {
2387 2 : nChunkSize *= anChunkSize[i];
2388 : }
2389 : }
2390 : }
2391 :
2392 60 : nChunkSize = nDTSize;
2393 120 : std::vector<size_t> anAccBlockSizeFromStart;
2394 173 : for (size_t i = 0; i < dims.size(); i++)
2395 : {
2396 113 : nChunkSize *= anChunkSize[i];
2397 113 : anAccBlockSizeFromStart.push_back(nChunkSize);
2398 : }
2399 60 : if (nChunkSize <= nMaxChunkMemory / 2)
2400 : {
2401 56 : size_t nVoxelsFromEnd = 1;
2402 161 : for (size_t i = dims.size(); i > 0;)
2403 : {
2404 105 : --i;
2405 : const auto nCurBlockSize =
2406 105 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2407 105 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2408 105 : if (nMul >= 2)
2409 : {
2410 97 : const auto nSizeThisDim(dims[i]->GetSize());
2411 : const auto nBlocksThisDim =
2412 97 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2413 97 : anChunkSize[i] = static_cast<size_t>(std::min(
2414 97 : anChunkSize[i] *
2415 194 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2416 97 : nSizeThisDim));
2417 : }
2418 105 : nVoxelsFromEnd *= anChunkSize[i];
2419 : }
2420 : }
2421 60 : return anChunkSize;
2422 : }
2423 :
2424 : /************************************************************************/
2425 : /* BaseRename() */
2426 : /************************************************************************/
2427 :
2428 : //! @cond Doxygen_Suppress
2429 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2430 : {
2431 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2432 18 : m_osFullName += osNewName;
2433 18 : m_osName = osNewName;
2434 :
2435 18 : NotifyChildrenOfRenaming();
2436 18 : }
2437 :
2438 : //! @endcond
2439 :
2440 : //! @cond Doxygen_Suppress
2441 : /************************************************************************/
2442 : /* ParentRenamed() */
2443 : /************************************************************************/
2444 :
2445 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2446 : {
2447 50 : m_osFullName = osNewParentFullName;
2448 50 : m_osFullName += "/";
2449 50 : m_osFullName += m_osName;
2450 :
2451 50 : NotifyChildrenOfRenaming();
2452 50 : }
2453 :
2454 : //! @endcond
2455 :
2456 : /************************************************************************/
2457 : /* Deleted() */
2458 : /************************************************************************/
2459 :
2460 : //! @cond Doxygen_Suppress
2461 52 : void GDALAbstractMDArray::Deleted()
2462 : {
2463 52 : m_bValid = false;
2464 :
2465 52 : NotifyChildrenOfDeletion();
2466 52 : }
2467 :
2468 : //! @endcond
2469 :
2470 : /************************************************************************/
2471 : /* ParentDeleted() */
2472 : /************************************************************************/
2473 :
2474 : //! @cond Doxygen_Suppress
2475 28 : void GDALAbstractMDArray::ParentDeleted()
2476 : {
2477 28 : Deleted();
2478 28 : }
2479 :
2480 : //! @endcond
2481 :
2482 : /************************************************************************/
2483 : /* CheckValidAndErrorOutIfNot() */
2484 : /************************************************************************/
2485 :
2486 : //! @cond Doxygen_Suppress
2487 5672 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2488 : {
2489 5672 : if (!m_bValid)
2490 : {
2491 26 : CPLError(CE_Failure, CPLE_AppDefined,
2492 : "This object has been deleted. No action on it is possible");
2493 : }
2494 5672 : return m_bValid;
2495 : }
2496 :
2497 : //! @endcond
2498 :
2499 : /************************************************************************/
2500 : /* SetUnit() */
2501 : /************************************************************************/
2502 :
2503 : /** Set the variable unit.
2504 : *
2505 : * Values should conform as much as possible with those allowed by
2506 : * the NetCDF CF conventions:
2507 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2508 : * but others might be returned.
2509 : *
2510 : * Few examples are "meter", "degrees", "second", ...
2511 : * Empty value means unknown.
2512 : *
2513 : * This is the same as the C function GDALMDArraySetUnit()
2514 : *
2515 : * @note Driver implementation: optionally implemented.
2516 : *
2517 : * @param osUnit unit name.
2518 : * @return true in case of success.
2519 : */
2520 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2521 : {
2522 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2523 0 : return false;
2524 : }
2525 :
2526 : /************************************************************************/
2527 : /* GetUnit() */
2528 : /************************************************************************/
2529 :
2530 : /** Return the array unit.
2531 : *
2532 : * Values should conform as much as possible with those allowed by
2533 : * the NetCDF CF conventions:
2534 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2535 : * but others might be returned.
2536 : *
2537 : * Few examples are "meter", "degrees", "second", ...
2538 : * Empty value means unknown.
2539 : *
2540 : * This is the same as the C function GDALMDArrayGetUnit()
2541 : */
2542 5 : const std::string &GDALMDArray::GetUnit() const
2543 : {
2544 5 : static const std::string emptyString;
2545 5 : return emptyString;
2546 : }
2547 :
2548 : /************************************************************************/
2549 : /* SetSpatialRef() */
2550 : /************************************************************************/
2551 :
2552 : /** Assign a spatial reference system object to the array.
2553 : *
2554 : * This is the same as the C function GDALMDArraySetSpatialRef().
2555 : */
2556 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2557 : {
2558 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2559 0 : return false;
2560 : }
2561 :
2562 : /************************************************************************/
2563 : /* GetSpatialRef() */
2564 : /************************************************************************/
2565 :
2566 : /** Return the spatial reference system object associated with the array.
2567 : *
2568 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2569 : */
2570 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2571 : {
2572 4 : return nullptr;
2573 : }
2574 :
2575 : /************************************************************************/
2576 : /* GetRawNoDataValue() */
2577 : /************************************************************************/
2578 :
2579 : /** Return the nodata value as a "raw" value.
2580 : *
2581 : * The value returned might be nullptr in case of no nodata value. When
2582 : * a nodata value is registered, a non-nullptr will be returned whose size in
2583 : * bytes is GetDataType().GetSize().
2584 : *
2585 : * The returned value should not be modified or freed. It is valid until
2586 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2587 : * SetRawNoDataValue(), or any similar methods.
2588 : *
2589 : * @note Driver implementation: this method shall be implemented if nodata
2590 : * is supported.
2591 : *
2592 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2593 : *
2594 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2595 : */
2596 5 : const void *GDALMDArray::GetRawNoDataValue() const
2597 : {
2598 5 : return nullptr;
2599 : }
2600 :
2601 : /************************************************************************/
2602 : /* GetNoDataValueAsDouble() */
2603 : /************************************************************************/
2604 :
2605 : /** Return the nodata value as a double.
2606 : *
2607 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2608 : *
2609 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2610 : * a nodata value exists and can be converted to double. Might be nullptr.
2611 : *
2612 : * @return the nodata value as a double. A 0.0 value might also indicate the
2613 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2614 : * set to false then).
2615 : */
2616 22417 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2617 : {
2618 22417 : const void *pNoData = GetRawNoDataValue();
2619 22417 : double dfNoData = 0.0;
2620 22417 : const auto &eDT = GetDataType();
2621 22417 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2622 22417 : if (ok)
2623 : {
2624 22180 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2625 : GDT_Float64, 0, 1);
2626 : }
2627 22417 : if (pbHasNoData)
2628 384 : *pbHasNoData = ok;
2629 22417 : return dfNoData;
2630 : }
2631 :
2632 : /************************************************************************/
2633 : /* GetNoDataValueAsInt64() */
2634 : /************************************************************************/
2635 :
2636 : /** Return the nodata value as a Int64.
2637 : *
2638 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2639 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2640 : *
2641 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2642 : *
2643 : * @return the nodata value as a Int64
2644 : *
2645 : * @since GDAL 3.5
2646 : */
2647 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2648 : {
2649 12 : const void *pNoData = GetRawNoDataValue();
2650 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2651 12 : const auto &eDT = GetDataType();
2652 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2653 12 : if (ok)
2654 : {
2655 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2656 : GDT_Int64, 0, 1);
2657 : }
2658 12 : if (pbHasNoData)
2659 12 : *pbHasNoData = ok;
2660 12 : return nNoData;
2661 : }
2662 :
2663 : /************************************************************************/
2664 : /* GetNoDataValueAsUInt64() */
2665 : /************************************************************************/
2666 :
2667 : /** Return the nodata value as a UInt64.
2668 : *
2669 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2670 :
2671 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2672 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2673 : *
2674 : * @return the nodata value as a UInt64
2675 : *
2676 : * @since GDAL 3.5
2677 : */
2678 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2679 : {
2680 8 : const void *pNoData = GetRawNoDataValue();
2681 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2682 8 : const auto &eDT = GetDataType();
2683 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2684 8 : if (ok)
2685 : {
2686 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2687 : GDT_UInt64, 0, 1);
2688 : }
2689 8 : if (pbHasNoData)
2690 8 : *pbHasNoData = ok;
2691 8 : return nNoData;
2692 : }
2693 :
2694 : /************************************************************************/
2695 : /* SetRawNoDataValue() */
2696 : /************************************************************************/
2697 :
2698 : /** Set the nodata value as a "raw" value.
2699 : *
2700 : * The value passed might be nullptr in case of no nodata value. When
2701 : * a nodata value is registered, a non-nullptr whose size in
2702 : * bytes is GetDataType().GetSize() must be passed.
2703 : *
2704 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2705 : *
2706 : * @note Driver implementation: this method shall be implemented if setting
2707 : nodata
2708 : * is supported.
2709 :
2710 : * @return true in case of success.
2711 : */
2712 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2713 : {
2714 0 : CPLError(CE_Failure, CPLE_NotSupported,
2715 : "SetRawNoDataValue() not implemented");
2716 0 : return false;
2717 : }
2718 :
2719 : /************************************************************************/
2720 : /* SetNoDataValue() */
2721 : /************************************************************************/
2722 :
2723 : /** Set the nodata value as a double.
2724 : *
2725 : * If the natural data type of the attribute/array is not double, type
2726 : * conversion will occur to the type returned by GetDataType().
2727 : *
2728 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2729 : *
2730 : * @return true in case of success.
2731 : */
2732 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2733 : {
2734 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2735 57 : bool bRet = false;
2736 57 : if (GDALExtendedDataType::CopyValue(
2737 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2738 57 : GetDataType()))
2739 : {
2740 57 : bRet = SetRawNoDataValue(pRawNoData);
2741 : }
2742 57 : CPLFree(pRawNoData);
2743 57 : return bRet;
2744 : }
2745 :
2746 : /************************************************************************/
2747 : /* SetNoDataValue() */
2748 : /************************************************************************/
2749 :
2750 : /** Set the nodata value as a Int64.
2751 : *
2752 : * If the natural data type of the attribute/array is not Int64, type conversion
2753 : * will occur to the type returned by GetDataType().
2754 : *
2755 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2756 : *
2757 : * @return true in case of success.
2758 : *
2759 : * @since GDAL 3.5
2760 : */
2761 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2762 : {
2763 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2764 3 : bool bRet = false;
2765 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2766 6 : GDALExtendedDataType::Create(GDT_Int64),
2767 3 : pRawNoData, GetDataType()))
2768 : {
2769 3 : bRet = SetRawNoDataValue(pRawNoData);
2770 : }
2771 3 : CPLFree(pRawNoData);
2772 3 : return bRet;
2773 : }
2774 :
2775 : /************************************************************************/
2776 : /* SetNoDataValue() */
2777 : /************************************************************************/
2778 :
2779 : /** Set the nodata value as a Int64.
2780 : *
2781 : * If the natural data type of the attribute/array is not Int64, type conversion
2782 : * will occur to the type returned by GetDataType().
2783 : *
2784 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2785 : *
2786 : * @return true in case of success.
2787 : *
2788 : * @since GDAL 3.5
2789 : */
2790 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2791 : {
2792 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2793 1 : bool bRet = false;
2794 1 : if (GDALExtendedDataType::CopyValue(
2795 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2796 1 : GetDataType()))
2797 : {
2798 1 : bRet = SetRawNoDataValue(pRawNoData);
2799 : }
2800 1 : CPLFree(pRawNoData);
2801 1 : return bRet;
2802 : }
2803 :
2804 : /************************************************************************/
2805 : /* Resize() */
2806 : /************************************************************************/
2807 :
2808 : /** Resize an array to new dimensions.
2809 : *
2810 : * Not all drivers may allow this operation, and with restrictions (e.g.
2811 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2812 : *
2813 : * Resizing a dimension used in other arrays will cause those other arrays
2814 : * to be resized.
2815 : *
2816 : * This is the same as the C function GDALMDArrayResize().
2817 : *
2818 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2819 : * new size of each indexing dimension.
2820 : * @param papszOptions Options. (Driver specific)
2821 : * @return true in case of success.
2822 : * @since GDAL 3.7
2823 : */
2824 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2825 : CPL_UNUSED CSLConstList papszOptions)
2826 : {
2827 0 : CPLError(CE_Failure, CPLE_NotSupported,
2828 : "Resize() is not supported for this array");
2829 0 : return false;
2830 : }
2831 :
2832 : /************************************************************************/
2833 : /* SetScale() */
2834 : /************************************************************************/
2835 :
2836 : /** Set the scale value to apply to raw values.
2837 : *
2838 : * unscaled_value = raw_value * GetScale() + GetOffset()
2839 : *
2840 : * This is the same as the C function GDALMDArraySetScale() /
2841 : * GDALMDArraySetScaleEx().
2842 : *
2843 : * @note Driver implementation: this method shall be implemented if setting
2844 : * scale is supported.
2845 : *
2846 : * @param dfScale scale
2847 : * @param eStorageType Data type to which create the potential attribute that
2848 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2849 : * implementation will decide automatically the data type. Note that changing
2850 : * the data type after initial setting might not be supported.
2851 : * @return true in case of success.
2852 : */
2853 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2854 : CPL_UNUSED GDALDataType eStorageType)
2855 : {
2856 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2857 0 : return false;
2858 : }
2859 :
2860 : /************************************************************************/
2861 : /* SetOffset) */
2862 : /************************************************************************/
2863 :
2864 : /** Set the offset value to apply to raw values.
2865 : *
2866 : * unscaled_value = raw_value * GetScale() + GetOffset()
2867 : *
2868 : * This is the same as the C function GDALMDArraySetOffset() /
2869 : * GDALMDArraySetOffsetEx().
2870 : *
2871 : * @note Driver implementation: this method shall be implemented if setting
2872 : * offset is supported.
2873 : *
2874 : * @param dfOffset Offset
2875 : * @param eStorageType Data type to which create the potential attribute that
2876 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2877 : * implementation will decide automatically the data type. Note that changing
2878 : * the data type after initial setting might not be supported.
2879 : * @return true in case of success.
2880 : */
2881 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2882 : CPL_UNUSED GDALDataType eStorageType)
2883 : {
2884 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2885 0 : return false;
2886 : }
2887 :
2888 : /************************************************************************/
2889 : /* GetScale() */
2890 : /************************************************************************/
2891 :
2892 : /** Get the scale value to apply to raw values.
2893 : *
2894 : * unscaled_value = raw_value * GetScale() + GetOffset()
2895 : *
2896 : * This is the same as the C function GDALMDArrayGetScale().
2897 : *
2898 : * @note Driver implementation: this method shall be implemented if gettings
2899 : * scale is supported.
2900 : *
2901 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2902 : * a scale value exists. Might be nullptr.
2903 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2904 : * the storage type of the scale value, when known/relevant. Otherwise will be
2905 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2906 : *
2907 : * @return the scale value. A 1.0 value might also indicate the
2908 : * absence of a scale value.
2909 : */
2910 13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
2911 : CPL_UNUSED GDALDataType *peStorageType) const
2912 : {
2913 13 : if (pbHasScale)
2914 13 : *pbHasScale = false;
2915 13 : return 1.0;
2916 : }
2917 :
2918 : /************************************************************************/
2919 : /* GetOffset() */
2920 : /************************************************************************/
2921 :
2922 : /** Get the offset value to apply to raw values.
2923 : *
2924 : * unscaled_value = raw_value * GetScale() + GetOffset()
2925 : *
2926 : * This is the same as the C function GDALMDArrayGetOffset().
2927 : *
2928 : * @note Driver implementation: this method shall be implemented if gettings
2929 : * offset is supported.
2930 : *
2931 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
2932 : * a offset value exists. Might be nullptr.
2933 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2934 : * the storage type of the offset value, when known/relevant. Otherwise will be
2935 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2936 : *
2937 : * @return the offset value. A 0.0 value might also indicate the
2938 : * absence of a offset value.
2939 : */
2940 13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
2941 : CPL_UNUSED GDALDataType *peStorageType) const
2942 : {
2943 13 : if (pbHasOffset)
2944 13 : *pbHasOffset = false;
2945 13 : return 0.0;
2946 : }
2947 :
2948 : /************************************************************************/
2949 : /* ProcessPerChunk() */
2950 : /************************************************************************/
2951 :
2952 : namespace
2953 : {
2954 : enum class Caller
2955 : {
2956 : CALLER_END_OF_LOOP,
2957 : CALLER_IN_LOOP,
2958 : };
2959 : }
2960 :
2961 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
2962 : *
2963 : * This method is to be used when doing operations on an array, or a subset of
2964 : * it, in a chunk by chunk way.
2965 : *
2966 : * @param arrayStartIdx Values representing the starting index to use
2967 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2968 : * Array of GetDimensionCount() values. Must not be
2969 : * nullptr, unless for a zero-dimensional array.
2970 : *
2971 : * @param count Values representing the number of values to use in
2972 : * each dimension.
2973 : * Array of GetDimensionCount() values. Must not be
2974 : * nullptr, unless for a zero-dimensional array.
2975 : *
2976 : * @param chunkSize Values representing the chunk size in each dimension.
2977 : * Might typically the output of GetProcessingChunkSize().
2978 : * Array of GetDimensionCount() values. Must not be
2979 : * nullptr, unless for a zero-dimensional array.
2980 : *
2981 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
2982 : * Must NOT be nullptr.
2983 : *
2984 : * @param pUserData Pointer to pass as the value of the pUserData argument
2985 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
2986 : *
2987 : * @return true in case of success.
2988 : */
2989 58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
2990 : const GUInt64 *count,
2991 : const size_t *chunkSize,
2992 : FuncProcessPerChunkType pfnFunc,
2993 : void *pUserData)
2994 : {
2995 58 : const auto &dims = GetDimensions();
2996 58 : if (dims.empty())
2997 : {
2998 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
2999 : }
3000 :
3001 : // Sanity check
3002 56 : size_t nTotalChunkSize = 1;
3003 146 : for (size_t i = 0; i < dims.size(); i++)
3004 : {
3005 97 : const auto nSizeThisDim(dims[i]->GetSize());
3006 97 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3007 95 : arrayStartIdx[i] > nSizeThisDim - count[i])
3008 : {
3009 4 : CPLError(CE_Failure, CPLE_AppDefined,
3010 : "Inconsistent arrayStartIdx[] / count[] values "
3011 : "regarding array size");
3012 4 : return false;
3013 : }
3014 184 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3015 91 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3016 : {
3017 3 : CPLError(CE_Failure, CPLE_AppDefined,
3018 : "Inconsistent chunkSize[] values");
3019 3 : return false;
3020 : }
3021 90 : nTotalChunkSize *= chunkSize[i];
3022 : }
3023 :
3024 49 : size_t dimIdx = 0;
3025 98 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3026 98 : std::vector<size_t> chunkCount(dims.size());
3027 :
3028 : struct Stack
3029 : {
3030 : GUInt64 nBlockCounter = 0;
3031 : GUInt64 nBlocksMinusOne = 0;
3032 : size_t first_count = 0; // only used if nBlocks > 1
3033 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3034 : };
3035 :
3036 98 : std::vector<Stack> stack(dims.size());
3037 49 : GUInt64 iCurChunk = 0;
3038 49 : GUInt64 nChunkCount = 1;
3039 138 : for (size_t i = 0; i < dims.size(); i++)
3040 : {
3041 89 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3042 89 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3043 89 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3044 89 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3045 89 : if (stack[i].nBlocksMinusOne == 0)
3046 : {
3047 84 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3048 84 : chunkCount[i] = static_cast<size_t>(count[i]);
3049 : }
3050 : else
3051 : {
3052 5 : stack[i].first_count = static_cast<size_t>(
3053 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3054 : }
3055 : }
3056 :
3057 49 : lbl_next_depth:
3058 248 : if (dimIdx == dims.size())
3059 : {
3060 82 : ++iCurChunk;
3061 82 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3062 : iCurChunk, nChunkCount, pUserData))
3063 : {
3064 0 : return false;
3065 : }
3066 : }
3067 : else
3068 : {
3069 166 : if (stack[dimIdx].nBlocksMinusOne != 0)
3070 : {
3071 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3072 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3073 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3074 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3075 : while (true)
3076 : {
3077 33 : dimIdx++;
3078 33 : goto lbl_next_depth;
3079 33 : lbl_return_to_caller_in_loop:
3080 33 : --stack[dimIdx].nBlockCounter;
3081 33 : if (stack[dimIdx].nBlockCounter == 0)
3082 11 : break;
3083 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3084 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3085 : }
3086 :
3087 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3088 22 : chunkCount[dimIdx] =
3089 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3090 11 : chunkArrayStartIdx[dimIdx]);
3091 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3092 : }
3093 166 : dimIdx++;
3094 166 : goto lbl_next_depth;
3095 166 : lbl_return_to_caller_end_of_loop:
3096 166 : if (dimIdx == 0)
3097 49 : goto end;
3098 : }
3099 :
3100 199 : assert(dimIdx > 0);
3101 199 : dimIdx--;
3102 : // cppcheck-suppress negativeContainerIndex
3103 199 : switch (stack[dimIdx].return_point)
3104 : {
3105 166 : case Caller::CALLER_END_OF_LOOP:
3106 166 : goto lbl_return_to_caller_end_of_loop;
3107 33 : case Caller::CALLER_IN_LOOP:
3108 33 : goto lbl_return_to_caller_in_loop;
3109 : }
3110 49 : end:
3111 49 : return true;
3112 : }
3113 :
3114 : /************************************************************************/
3115 : /* GDALAttribute() */
3116 : /************************************************************************/
3117 :
3118 : //! @cond Doxygen_Suppress
3119 13998 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3120 0 : CPL_UNUSED const std::string &osName)
3121 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3122 13998 : : GDALAbstractMDArray(osParentName, osName)
3123 : #endif
3124 : {
3125 13998 : }
3126 :
3127 : //! @endcond
3128 :
3129 : /************************************************************************/
3130 : /* GetDimensionSize() */
3131 : /************************************************************************/
3132 :
3133 : /** Return the size of the dimensions of the attribute.
3134 : *
3135 : * This will be an empty array for a scalar (single value) attribute.
3136 : *
3137 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3138 : */
3139 361 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3140 : {
3141 361 : const auto &dims = GetDimensions();
3142 361 : std::vector<GUInt64> ret;
3143 361 : ret.reserve(dims.size());
3144 453 : for (const auto &dim : dims)
3145 92 : ret.push_back(dim->GetSize());
3146 361 : return ret;
3147 : }
3148 :
3149 : /************************************************************************/
3150 : /* GDALRawResult() */
3151 : /************************************************************************/
3152 :
3153 : //! @cond Doxygen_Suppress
3154 149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3155 149 : size_t nEltCount)
3156 298 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3157 149 : m_raw(raw)
3158 : {
3159 149 : }
3160 :
3161 : //! @endcond
3162 :
3163 : /************************************************************************/
3164 : /* GDALRawResult() */
3165 : /************************************************************************/
3166 :
3167 : /** Move constructor. */
3168 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3169 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3170 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3171 : {
3172 0 : other.m_nEltCount = 0;
3173 0 : other.m_nSize = 0;
3174 0 : other.m_raw = nullptr;
3175 0 : }
3176 :
3177 : /************************************************************************/
3178 : /* FreeMe() */
3179 : /************************************************************************/
3180 :
3181 149 : void GDALRawResult::FreeMe()
3182 : {
3183 149 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3184 : {
3185 47 : GByte *pabyPtr = m_raw;
3186 47 : const auto nDTSize(m_dt.GetSize());
3187 94 : for (size_t i = 0; i < m_nEltCount; ++i)
3188 : {
3189 47 : m_dt.FreeDynamicMemory(pabyPtr);
3190 47 : pabyPtr += nDTSize;
3191 : }
3192 : }
3193 149 : VSIFree(m_raw);
3194 149 : }
3195 :
3196 : /************************************************************************/
3197 : /* operator=() */
3198 : /************************************************************************/
3199 :
3200 : /** Move assignment. */
3201 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3202 : {
3203 0 : FreeMe();
3204 0 : m_dt = std::move(other.m_dt);
3205 0 : m_nEltCount = other.m_nEltCount;
3206 0 : m_nSize = other.m_nSize;
3207 0 : m_raw = other.m_raw;
3208 0 : other.m_nEltCount = 0;
3209 0 : other.m_nSize = 0;
3210 0 : other.m_raw = nullptr;
3211 0 : return *this;
3212 : }
3213 :
3214 : /************************************************************************/
3215 : /* ~GDALRawResult() */
3216 : /************************************************************************/
3217 :
3218 : /** Destructor. */
3219 149 : GDALRawResult::~GDALRawResult()
3220 : {
3221 149 : FreeMe();
3222 149 : }
3223 :
3224 : /************************************************************************/
3225 : /* StealData() */
3226 : /************************************************************************/
3227 :
3228 : //! @cond Doxygen_Suppress
3229 : /** Return buffer to caller which becomes owner of it.
3230 : * Only to be used by GDALAttributeReadAsRaw().
3231 : */
3232 6 : GByte *GDALRawResult::StealData()
3233 : {
3234 6 : GByte *ret = m_raw;
3235 6 : m_raw = nullptr;
3236 6 : m_nEltCount = 0;
3237 6 : m_nSize = 0;
3238 6 : return ret;
3239 : }
3240 :
3241 : //! @endcond
3242 :
3243 : /************************************************************************/
3244 : /* ReadAsRaw() */
3245 : /************************************************************************/
3246 :
3247 : /** Return the raw value of an attribute.
3248 : *
3249 : *
3250 : * This is the same as the C function GDALAttributeReadAsRaw().
3251 : */
3252 149 : GDALRawResult GDALAttribute::ReadAsRaw() const
3253 : {
3254 149 : const auto nEltCount(GetTotalElementsCount());
3255 149 : const auto &dt(GetDataType());
3256 149 : const auto nDTSize(dt.GetSize());
3257 : GByte *res = static_cast<GByte *>(
3258 149 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3259 149 : if (!res)
3260 0 : return GDALRawResult(nullptr, dt, 0);
3261 149 : const auto &dims = GetDimensions();
3262 149 : const auto nDims = GetDimensionCount();
3263 298 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3264 298 : std::vector<size_t> count(1 + nDims);
3265 168 : for (size_t i = 0; i < nDims; i++)
3266 : {
3267 19 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3268 : }
3269 149 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3270 149 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3271 : {
3272 0 : VSIFree(res);
3273 0 : return GDALRawResult(nullptr, dt, 0);
3274 : }
3275 149 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3276 : }
3277 :
3278 : /************************************************************************/
3279 : /* ReadAsString() */
3280 : /************************************************************************/
3281 :
3282 : /** Return the value of an attribute as a string.
3283 : *
3284 : * The returned string should not be freed, and its lifetime does not
3285 : * excess a next call to ReadAsString() on the same object, or the deletion
3286 : * of the object itself.
3287 : *
3288 : * This function will only return the first element if there are several.
3289 : *
3290 : * This is the same as the C function GDALAttributeReadAsString()
3291 : *
3292 : * @return a string, or nullptr.
3293 : */
3294 1302 : const char *GDALAttribute::ReadAsString() const
3295 : {
3296 1302 : const auto nDims = GetDimensionCount();
3297 2604 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3298 2604 : std::vector<size_t> count(1 + nDims, 1);
3299 1302 : char *szRet = nullptr;
3300 1302 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3301 1302 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3302 3905 : sizeof(szRet)) ||
3303 1301 : szRet == nullptr)
3304 : {
3305 4 : return nullptr;
3306 : }
3307 1298 : m_osCachedVal = szRet;
3308 1298 : CPLFree(szRet);
3309 1298 : return m_osCachedVal.c_str();
3310 : }
3311 :
3312 : /************************************************************************/
3313 : /* ReadAsInt() */
3314 : /************************************************************************/
3315 :
3316 : /** Return the value of an attribute as a integer.
3317 : *
3318 : * This function will only return the first element if there are several.
3319 : *
3320 : * It can fail if its value can not be converted to integer.
3321 : *
3322 : * This is the same as the C function GDALAttributeReadAsInt()
3323 : *
3324 : * @return a integer, or INT_MIN in case of error.
3325 : */
3326 218 : int GDALAttribute::ReadAsInt() const
3327 : {
3328 218 : const auto nDims = GetDimensionCount();
3329 436 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3330 218 : std::vector<size_t> count(1 + nDims, 1);
3331 218 : int nRet = INT_MIN;
3332 218 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3333 436 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3334 436 : return nRet;
3335 : }
3336 :
3337 : /************************************************************************/
3338 : /* ReadAsInt64() */
3339 : /************************************************************************/
3340 :
3341 : /** Return the value of an attribute as an int64_t.
3342 : *
3343 : * This function will only return the first element if there are several.
3344 : *
3345 : * It can fail if its value can not be converted to long.
3346 : *
3347 : * This is the same as the C function GDALAttributeReadAsInt64()
3348 : *
3349 : * @return an int64_t, or INT64_MIN in case of error.
3350 : */
3351 54 : int64_t GDALAttribute::ReadAsInt64() const
3352 : {
3353 54 : const auto nDims = GetDimensionCount();
3354 108 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3355 54 : std::vector<size_t> count(1 + nDims, 1);
3356 54 : int64_t nRet = INT64_MIN;
3357 54 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3358 108 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3359 108 : return nRet;
3360 : }
3361 :
3362 : /************************************************************************/
3363 : /* ReadAsDouble() */
3364 : /************************************************************************/
3365 :
3366 : /** Return the value of an attribute as a double.
3367 : *
3368 : * This function will only return the first element if there are several.
3369 : *
3370 : * It can fail if its value can not be converted to double.
3371 : *
3372 : * This is the same as the C function GDALAttributeReadAsInt()
3373 : *
3374 : * @return a double value.
3375 : */
3376 339 : double GDALAttribute::ReadAsDouble() const
3377 : {
3378 339 : const auto nDims = GetDimensionCount();
3379 678 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3380 339 : std::vector<size_t> count(1 + nDims, 1);
3381 339 : double dfRet = 0;
3382 339 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3383 339 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3384 339 : sizeof(dfRet));
3385 678 : return dfRet;
3386 : }
3387 :
3388 : /************************************************************************/
3389 : /* ReadAsStringArray() */
3390 : /************************************************************************/
3391 :
3392 : /** Return the value of an attribute as an array of strings.
3393 : *
3394 : * This is the same as the C function GDALAttributeReadAsStringArray()
3395 : */
3396 104 : CPLStringList GDALAttribute::ReadAsStringArray() const
3397 : {
3398 104 : const auto nElts = GetTotalElementsCount();
3399 104 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3400 0 : return CPLStringList();
3401 : char **papszList = static_cast<char **>(
3402 104 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3403 104 : const auto &dims = GetDimensions();
3404 104 : const auto nDims = GetDimensionCount();
3405 208 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3406 208 : std::vector<size_t> count(1 + nDims);
3407 157 : for (size_t i = 0; i < nDims; i++)
3408 : {
3409 53 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3410 : }
3411 104 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3412 104 : GDALExtendedDataType::CreateString(), papszList, papszList,
3413 104 : sizeof(char *) * static_cast<int>(nElts));
3414 269 : for (int i = 0; i < static_cast<int>(nElts); i++)
3415 : {
3416 165 : if (papszList[i] == nullptr)
3417 13 : papszList[i] = CPLStrdup("");
3418 : }
3419 104 : return CPLStringList(papszList);
3420 : }
3421 :
3422 : /************************************************************************/
3423 : /* ReadAsIntArray() */
3424 : /************************************************************************/
3425 :
3426 : /** Return the value of an attribute as an array of integers.
3427 : *
3428 : * This is the same as the C function GDALAttributeReadAsIntArray().
3429 : */
3430 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3431 : {
3432 15 : const auto nElts = GetTotalElementsCount();
3433 : #if SIZEOF_VOIDP == 4
3434 : if (nElts > static_cast<size_t>(nElts))
3435 : return {};
3436 : #endif
3437 15 : std::vector<int> res(static_cast<size_t>(nElts));
3438 15 : const auto &dims = GetDimensions();
3439 15 : const auto nDims = GetDimensionCount();
3440 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3441 30 : std::vector<size_t> count(1 + nDims);
3442 32 : for (size_t i = 0; i < nDims; i++)
3443 : {
3444 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3445 : }
3446 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3447 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3448 15 : res.size() * sizeof(res[0]));
3449 30 : return res;
3450 : }
3451 :
3452 : /************************************************************************/
3453 : /* ReadAsInt64Array() */
3454 : /************************************************************************/
3455 :
3456 : /** Return the value of an attribute as an array of int64_t.
3457 : *
3458 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3459 : */
3460 38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3461 : {
3462 38 : const auto nElts = GetTotalElementsCount();
3463 : #if SIZEOF_VOIDP == 4
3464 : if (nElts > static_cast<size_t>(nElts))
3465 : return {};
3466 : #endif
3467 38 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3468 38 : const auto &dims = GetDimensions();
3469 38 : const auto nDims = GetDimensionCount();
3470 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3471 76 : std::vector<size_t> count(1 + nDims);
3472 76 : for (size_t i = 0; i < nDims; i++)
3473 : {
3474 38 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3475 : }
3476 38 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3477 76 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3478 38 : res.size() * sizeof(res[0]));
3479 76 : return res;
3480 : }
3481 :
3482 : /************************************************************************/
3483 : /* ReadAsDoubleArray() */
3484 : /************************************************************************/
3485 :
3486 : /** Return the value of an attribute as an array of double.
3487 : *
3488 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3489 : */
3490 87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3491 : {
3492 87 : const auto nElts = GetTotalElementsCount();
3493 : #if SIZEOF_VOIDP == 4
3494 : if (nElts > static_cast<size_t>(nElts))
3495 : return {};
3496 : #endif
3497 87 : std::vector<double> res(static_cast<size_t>(nElts));
3498 87 : const auto &dims = GetDimensions();
3499 87 : const auto nDims = GetDimensionCount();
3500 174 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3501 174 : std::vector<size_t> count(1 + nDims);
3502 158 : for (size_t i = 0; i < nDims; i++)
3503 : {
3504 71 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3505 : }
3506 87 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3507 174 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3508 87 : res.size() * sizeof(res[0]));
3509 174 : return res;
3510 : }
3511 :
3512 : /************************************************************************/
3513 : /* Write() */
3514 : /************************************************************************/
3515 :
3516 : /** Write an attribute from raw values expressed in GetDataType()
3517 : *
3518 : * The values should be provided in the type of GetDataType() and there should
3519 : * be exactly GetTotalElementsCount() of them.
3520 : * If GetDataType() is a string, each value should be a char* pointer.
3521 : *
3522 : * This is the same as the C function GDALAttributeWriteRaw().
3523 : *
3524 : * @param pabyValue Buffer of nLen bytes.
3525 : * @param nLen Size of pabyValue in bytes. Should be equal to
3526 : * GetTotalElementsCount() * GetDataType().GetSize()
3527 : * @return true in case of success.
3528 : */
3529 91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3530 : {
3531 91 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3532 : {
3533 0 : CPLError(CE_Failure, CPLE_AppDefined,
3534 : "Length is not of expected value");
3535 0 : return false;
3536 : }
3537 91 : const auto &dims = GetDimensions();
3538 91 : const auto nDims = GetDimensionCount();
3539 182 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3540 182 : std::vector<size_t> count(1 + nDims);
3541 114 : for (size_t i = 0; i < nDims; i++)
3542 : {
3543 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3544 : }
3545 91 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3546 91 : pabyValue, pabyValue, nLen);
3547 : }
3548 :
3549 : /************************************************************************/
3550 : /* Write() */
3551 : /************************************************************************/
3552 :
3553 : /** Write an attribute from a string value.
3554 : *
3555 : * Type conversion will be performed if needed. If the attribute contains
3556 : * multiple values, only the first one will be updated.
3557 : *
3558 : * This is the same as the C function GDALAttributeWriteString().
3559 : *
3560 : * @param pszValue Pointer to a string.
3561 : * @return true in case of success.
3562 : */
3563 304 : bool GDALAttribute::Write(const char *pszValue)
3564 : {
3565 304 : const auto nDims = GetDimensionCount();
3566 608 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3567 304 : std::vector<size_t> count(1 + nDims, 1);
3568 304 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3569 608 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3570 608 : sizeof(pszValue));
3571 : }
3572 :
3573 : /************************************************************************/
3574 : /* WriteInt() */
3575 : /************************************************************************/
3576 :
3577 : /** Write an attribute from a integer value.
3578 : *
3579 : * Type conversion will be performed if needed. If the attribute contains
3580 : * multiple values, only the first one will be updated.
3581 : *
3582 : * This is the same as the C function GDALAttributeWriteInt().
3583 : *
3584 : * @param nVal Value.
3585 : * @return true in case of success.
3586 : */
3587 22 : bool GDALAttribute::WriteInt(int nVal)
3588 : {
3589 22 : const auto nDims = GetDimensionCount();
3590 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3591 22 : std::vector<size_t> count(1 + nDims, 1);
3592 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3593 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3594 44 : sizeof(nVal));
3595 : }
3596 :
3597 : /************************************************************************/
3598 : /* WriteInt64() */
3599 : /************************************************************************/
3600 :
3601 : /** Write an attribute from an int64_t value.
3602 : *
3603 : * Type conversion will be performed if needed. If the attribute contains
3604 : * multiple values, only the first one will be updated.
3605 : *
3606 : * This is the same as the C function GDALAttributeWriteInt().
3607 : *
3608 : * @param nVal Value.
3609 : * @return true in case of success.
3610 : */
3611 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3612 : {
3613 11 : const auto nDims = GetDimensionCount();
3614 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3615 11 : std::vector<size_t> count(1 + nDims, 1);
3616 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3617 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3618 22 : sizeof(nVal));
3619 : }
3620 :
3621 : /************************************************************************/
3622 : /* Write() */
3623 : /************************************************************************/
3624 :
3625 : /** Write an attribute from a double value.
3626 : *
3627 : * Type conversion will be performed if needed. If the attribute contains
3628 : * multiple values, only the first one will be updated.
3629 : *
3630 : * This is the same as the C function GDALAttributeWriteDouble().
3631 : *
3632 : * @param dfVal Value.
3633 : * @return true in case of success.
3634 : */
3635 36 : bool GDALAttribute::Write(double dfVal)
3636 : {
3637 36 : const auto nDims = GetDimensionCount();
3638 72 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3639 36 : std::vector<size_t> count(1 + nDims, 1);
3640 36 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3641 72 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3642 72 : sizeof(dfVal));
3643 : }
3644 :
3645 : /************************************************************************/
3646 : /* Write() */
3647 : /************************************************************************/
3648 :
3649 : /** Write an attribute from an array of strings.
3650 : *
3651 : * Type conversion will be performed if needed.
3652 : *
3653 : * Exactly GetTotalElementsCount() strings must be provided
3654 : *
3655 : * This is the same as the C function GDALAttributeWriteStringArray().
3656 : *
3657 : * @param vals Array of strings.
3658 : * @return true in case of success.
3659 : */
3660 8 : bool GDALAttribute::Write(CSLConstList vals)
3661 : {
3662 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3663 : {
3664 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3665 1 : return false;
3666 : }
3667 7 : const auto nDims = GetDimensionCount();
3668 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3669 7 : std::vector<size_t> count(1 + nDims);
3670 7 : const auto &dims = GetDimensions();
3671 15 : for (size_t i = 0; i < nDims; i++)
3672 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3673 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3674 7 : GDALExtendedDataType::CreateString(), vals, vals,
3675 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3676 : }
3677 :
3678 : /************************************************************************/
3679 : /* Write() */
3680 : /************************************************************************/
3681 :
3682 : /** Write an attribute from an array of int.
3683 : *
3684 : * Type conversion will be performed if needed.
3685 : *
3686 : * Exactly GetTotalElementsCount() strings must be provided
3687 : *
3688 : * This is the same as the C function GDALAttributeWriteIntArray()
3689 : *
3690 : * @param vals Array of int.
3691 : * @param nVals Should be equal to GetTotalElementsCount().
3692 : * @return true in case of success.
3693 : */
3694 9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3695 : {
3696 9 : if (nVals != GetTotalElementsCount())
3697 : {
3698 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3699 1 : return false;
3700 : }
3701 8 : const auto nDims = GetDimensionCount();
3702 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3703 8 : std::vector<size_t> count(1 + nDims);
3704 8 : const auto &dims = GetDimensions();
3705 16 : for (size_t i = 0; i < nDims; i++)
3706 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3707 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3708 8 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3709 16 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3710 : }
3711 :
3712 : /************************************************************************/
3713 : /* Write() */
3714 : /************************************************************************/
3715 :
3716 : /** Write an attribute from an array of int64_t.
3717 : *
3718 : * Type conversion will be performed if needed.
3719 : *
3720 : * Exactly GetTotalElementsCount() strings must be provided
3721 : *
3722 : * This is the same as the C function GDALAttributeWriteLongArray()
3723 : *
3724 : * @param vals Array of int64_t.
3725 : * @param nVals Should be equal to GetTotalElementsCount().
3726 : * @return true in case of success.
3727 : */
3728 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3729 : {
3730 10 : if (nVals != GetTotalElementsCount())
3731 : {
3732 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3733 0 : return false;
3734 : }
3735 10 : const auto nDims = GetDimensionCount();
3736 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3737 10 : std::vector<size_t> count(1 + nDims);
3738 10 : const auto &dims = GetDimensions();
3739 20 : for (size_t i = 0; i < nDims; i++)
3740 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3741 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3742 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3743 10 : static_cast<size_t>(GetTotalElementsCount()) *
3744 10 : sizeof(int64_t));
3745 : }
3746 :
3747 : /************************************************************************/
3748 : /* Write() */
3749 : /************************************************************************/
3750 :
3751 : /** Write an attribute from an array of double.
3752 : *
3753 : * Type conversion will be performed if needed.
3754 : *
3755 : * Exactly GetTotalElementsCount() strings must be provided
3756 : *
3757 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3758 : *
3759 : * @param vals Array of double.
3760 : * @param nVals Should be equal to GetTotalElementsCount().
3761 : * @return true in case of success.
3762 : */
3763 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3764 : {
3765 7 : if (nVals != GetTotalElementsCount())
3766 : {
3767 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3768 1 : return false;
3769 : }
3770 6 : const auto nDims = GetDimensionCount();
3771 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3772 6 : std::vector<size_t> count(1 + nDims);
3773 6 : const auto &dims = GetDimensions();
3774 13 : for (size_t i = 0; i < nDims; i++)
3775 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3776 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3777 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3778 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3779 : }
3780 :
3781 : /************************************************************************/
3782 : /* GDALMDArray() */
3783 : /************************************************************************/
3784 :
3785 : //! @cond Doxygen_Suppress
3786 6213 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3787 : CPL_UNUSED const std::string &osName,
3788 0 : const std::string &osContext)
3789 : :
3790 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3791 : GDALAbstractMDArray(osParentName, osName),
3792 : #endif
3793 6213 : m_osContext(osContext)
3794 : {
3795 6213 : }
3796 :
3797 : //! @endcond
3798 :
3799 : /************************************************************************/
3800 : /* GetTotalCopyCost() */
3801 : /************************************************************************/
3802 :
3803 : /** Return a total "cost" to copy the array.
3804 : *
3805 : * Used as a parameter for CopyFrom()
3806 : */
3807 43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3808 : {
3809 86 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3810 86 : GetTotalElementsCount() * GetDataType().GetSize();
3811 : }
3812 :
3813 : /************************************************************************/
3814 : /* CopyFromAllExceptValues() */
3815 : /************************************************************************/
3816 :
3817 : //! @cond Doxygen_Suppress
3818 :
3819 144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3820 : bool bStrict, GUInt64 &nCurCost,
3821 : const GUInt64 nTotalCost,
3822 : GDALProgressFunc pfnProgress,
3823 : void *pProgressData)
3824 : {
3825 : // Nodata setting must be one of the first things done for TileDB
3826 144 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3827 144 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3828 : {
3829 13 : SetRawNoDataValue(pNoData);
3830 : }
3831 :
3832 144 : const bool bThisIsUnscaledArray =
3833 144 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3834 288 : auto attrs = poSrcArray->GetAttributes();
3835 191 : for (const auto &attr : attrs)
3836 : {
3837 47 : const auto &osAttrName = attr->GetName();
3838 47 : if (bThisIsUnscaledArray)
3839 : {
3840 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3841 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3842 1 : osAttrName == "valid_range")
3843 : {
3844 1 : continue;
3845 : }
3846 : }
3847 :
3848 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3849 92 : attr->GetDataType());
3850 46 : if (!dstAttr)
3851 : {
3852 0 : if (bStrict)
3853 0 : return false;
3854 0 : continue;
3855 : }
3856 46 : auto raw = attr->ReadAsRaw();
3857 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3858 0 : return false;
3859 : }
3860 144 : if (!attrs.empty())
3861 : {
3862 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3863 46 : if (pfnProgress &&
3864 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3865 0 : return false;
3866 : }
3867 :
3868 144 : auto srcSRS = poSrcArray->GetSpatialRef();
3869 144 : if (srcSRS)
3870 : {
3871 11 : SetSpatialRef(srcSRS.get());
3872 : }
3873 :
3874 144 : const std::string &osUnit(poSrcArray->GetUnit());
3875 144 : if (!osUnit.empty())
3876 : {
3877 18 : SetUnit(osUnit);
3878 : }
3879 :
3880 144 : bool bGotValue = false;
3881 144 : GDALDataType eOffsetStorageType = GDT_Unknown;
3882 : const double dfOffset =
3883 144 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3884 144 : if (bGotValue)
3885 : {
3886 3 : SetOffset(dfOffset, eOffsetStorageType);
3887 : }
3888 :
3889 144 : bGotValue = false;
3890 144 : GDALDataType eScaleStorageType = GDT_Unknown;
3891 144 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3892 144 : if (bGotValue)
3893 : {
3894 3 : SetScale(dfScale, eScaleStorageType);
3895 : }
3896 :
3897 144 : return true;
3898 : }
3899 :
3900 : //! @endcond
3901 :
3902 : /************************************************************************/
3903 : /* CopyFrom() */
3904 : /************************************************************************/
3905 :
3906 : /** Copy the content of an array into a new (generally empty) array.
3907 : *
3908 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
3909 : * of some output drivers this is not recommended)
3910 : * @param poSrcArray Source array. Should NOT be nullptr.
3911 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
3912 : * stop the copy. In relaxed mode, the copy will be attempted to
3913 : * be pursued.
3914 : * @param nCurCost Should be provided as a variable initially set to 0.
3915 : * @param nTotalCost Total cost from GetTotalCopyCost().
3916 : * @param pfnProgress Progress callback, or nullptr.
3917 : * @param pProgressData Progress user data, or nulptr.
3918 : *
3919 : * @return true in case of success (or partial success if bStrict == false).
3920 : */
3921 41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
3922 : const GDALMDArray *poSrcArray, bool bStrict,
3923 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
3924 : GDALProgressFunc pfnProgress, void *pProgressData)
3925 : {
3926 41 : if (pfnProgress == nullptr)
3927 4 : pfnProgress = GDALDummyProgress;
3928 :
3929 41 : nCurCost += GDALMDArray::COPY_COST;
3930 :
3931 41 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
3932 : pfnProgress, pProgressData))
3933 : {
3934 0 : return false;
3935 : }
3936 :
3937 41 : const auto &dims = poSrcArray->GetDimensions();
3938 41 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
3939 41 : if (dims.empty())
3940 : {
3941 2 : std::vector<GByte> abyTmp(nDTSize);
3942 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
3943 2 : GetDataType(), &abyTmp[0]) &&
3944 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
3945 4 : &abyTmp[0])) &&
3946 : bStrict)
3947 : {
3948 0 : return false;
3949 : }
3950 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
3951 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3952 0 : return false;
3953 : }
3954 : else
3955 : {
3956 39 : std::vector<GUInt64> arrayStartIdx(dims.size());
3957 39 : std::vector<GUInt64> count(dims.size());
3958 106 : for (size_t i = 0; i < dims.size(); i++)
3959 : {
3960 67 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3961 : }
3962 :
3963 : struct CopyFunc
3964 : {
3965 : GDALMDArray *poDstArray = nullptr;
3966 : std::vector<GByte> abyTmp{};
3967 : GDALProgressFunc pfnProgress = nullptr;
3968 : void *pProgressData = nullptr;
3969 : GUInt64 nCurCost = 0;
3970 : GUInt64 nTotalCost = 0;
3971 : GUInt64 nTotalBytesThisArray = 0;
3972 : bool bStop = false;
3973 :
3974 57 : static bool f(GDALAbstractMDArray *l_poSrcArray,
3975 : const GUInt64 *chunkArrayStartIdx,
3976 : const size_t *chunkCount, GUInt64 iCurChunk,
3977 : GUInt64 nChunkCount, void *pUserData)
3978 : {
3979 57 : const auto &dt(l_poSrcArray->GetDataType());
3980 57 : auto data = static_cast<CopyFunc *>(pUserData);
3981 57 : auto poDstArray = data->poDstArray;
3982 57 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
3983 57 : nullptr, dt, &data->abyTmp[0]))
3984 : {
3985 0 : return false;
3986 : }
3987 : bool bRet =
3988 57 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
3989 57 : nullptr, dt, &data->abyTmp[0]);
3990 57 : if (dt.NeedsFreeDynamicMemory())
3991 : {
3992 2 : const auto l_nDTSize = dt.GetSize();
3993 2 : GByte *ptr = &data->abyTmp[0];
3994 2 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
3995 2 : size_t nEltCount = 1;
3996 4 : for (size_t i = 0; i < l_nDims; ++i)
3997 : {
3998 2 : nEltCount *= chunkCount[i];
3999 : }
4000 10 : for (size_t i = 0; i < nEltCount; i++)
4001 : {
4002 8 : dt.FreeDynamicMemory(ptr);
4003 8 : ptr += l_nDTSize;
4004 : }
4005 : }
4006 57 : if (!bRet)
4007 : {
4008 0 : return false;
4009 : }
4010 :
4011 57 : double dfCurCost =
4012 57 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4013 57 : data->nTotalBytesThisArray;
4014 57 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4015 : data->pProgressData))
4016 : {
4017 0 : data->bStop = true;
4018 0 : return false;
4019 : }
4020 :
4021 57 : return true;
4022 : }
4023 : };
4024 :
4025 39 : CopyFunc copyFunc;
4026 39 : copyFunc.poDstArray = this;
4027 39 : copyFunc.nCurCost = nCurCost;
4028 39 : copyFunc.nTotalCost = nTotalCost;
4029 39 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4030 39 : copyFunc.pfnProgress = pfnProgress;
4031 39 : copyFunc.pProgressData = pProgressData;
4032 : const char *pszSwathSize =
4033 39 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4034 : const size_t nMaxChunkSize =
4035 : pszSwathSize
4036 39 : ? static_cast<size_t>(
4037 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4038 1 : CPLAtoGIntBig(pszSwathSize)))
4039 : : static_cast<size_t>(
4040 38 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4041 38 : GDALGetCacheMax64() / 4));
4042 39 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4043 39 : size_t nRealChunkSize = nDTSize;
4044 106 : for (const auto &nChunkSize : anChunkSizes)
4045 : {
4046 67 : nRealChunkSize *= nChunkSize;
4047 : }
4048 : try
4049 : {
4050 39 : copyFunc.abyTmp.resize(nRealChunkSize);
4051 : }
4052 0 : catch (const std::exception &)
4053 : {
4054 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4055 : "Cannot allocate temporary buffer");
4056 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4057 0 : return false;
4058 : }
4059 116 : if (copyFunc.nTotalBytesThisArray != 0 &&
4060 38 : !const_cast<GDALMDArray *>(poSrcArray)
4061 38 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4062 : anChunkSizes.data(), CopyFunc::f,
4063 77 : ©Func) &&
4064 0 : (bStrict || copyFunc.bStop))
4065 : {
4066 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4067 0 : return false;
4068 : }
4069 39 : nCurCost += copyFunc.nTotalBytesThisArray;
4070 : }
4071 :
4072 41 : return true;
4073 : }
4074 :
4075 : /************************************************************************/
4076 : /* GetStructuralInfo() */
4077 : /************************************************************************/
4078 :
4079 : /** Return structural information on the array.
4080 : *
4081 : * This may be the compression, etc..
4082 : *
4083 : * The return value should not be freed and is valid until GDALMDArray is
4084 : * released or this function called again.
4085 : *
4086 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4087 : */
4088 55 : CSLConstList GDALMDArray::GetStructuralInfo() const
4089 : {
4090 55 : return nullptr;
4091 : }
4092 :
4093 : /************************************************************************/
4094 : /* AdviseRead() */
4095 : /************************************************************************/
4096 :
4097 : /** Advise driver of upcoming read requests.
4098 : *
4099 : * Some GDAL drivers operate more efficiently if they know in advance what
4100 : * set of upcoming read requests will be made. The AdviseRead() method allows
4101 : * an application to notify the driver of the region of interest.
4102 : *
4103 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4104 : * accelerate access via some drivers. One such case is when reading through
4105 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4106 : * with the region of interest defined by AdviseRead())
4107 : *
4108 : * This is the same as the C function GDALMDArrayAdviseRead().
4109 : *
4110 : * @param arrayStartIdx Values representing the starting index to read
4111 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4112 : * Array of GetDimensionCount() values.
4113 : * Can be nullptr as a synonymous for [0 for i in
4114 : * range(GetDimensionCount() ]
4115 : *
4116 : * @param count Values representing the number of values to extract in
4117 : * each dimension.
4118 : * Array of GetDimensionCount() values.
4119 : * Can be nullptr as a synonymous for
4120 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4121 : * range(GetDimensionCount() ]
4122 : *
4123 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4124 : * documentation.
4125 : *
4126 : * @return true in case of success (ignoring the advice is a success)
4127 : *
4128 : * @since GDAL 3.2
4129 : */
4130 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4131 : CSLConstList papszOptions) const
4132 : {
4133 25 : const auto nDimCount = GetDimensionCount();
4134 25 : if (nDimCount == 0)
4135 2 : return true;
4136 :
4137 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4138 23 : if (arrayStartIdx == nullptr)
4139 : {
4140 0 : tmp_arrayStartIdx.resize(nDimCount);
4141 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4142 : }
4143 :
4144 46 : std::vector<size_t> tmp_count;
4145 23 : if (count == nullptr)
4146 : {
4147 0 : tmp_count.resize(nDimCount);
4148 0 : const auto &dims = GetDimensions();
4149 0 : for (size_t i = 0; i < nDimCount; i++)
4150 : {
4151 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4152 : #if SIZEOF_VOIDP < 8
4153 : if (nSize != static_cast<size_t>(nSize))
4154 : {
4155 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4156 : return false;
4157 : }
4158 : #endif
4159 0 : tmp_count[i] = static_cast<size_t>(nSize);
4160 : }
4161 0 : count = tmp_count.data();
4162 : }
4163 :
4164 46 : std::vector<GInt64> tmp_arrayStep;
4165 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4166 23 : const GInt64 *arrayStep = nullptr;
4167 23 : const GPtrDiff_t *bufferStride = nullptr;
4168 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4169 46 : GDALExtendedDataType::Create(GDT_Unknown),
4170 : nullptr, nullptr, 0, tmp_arrayStep,
4171 : tmp_bufferStride))
4172 : {
4173 1 : return false;
4174 : }
4175 :
4176 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4177 : }
4178 :
4179 : /************************************************************************/
4180 : /* IAdviseRead() */
4181 : /************************************************************************/
4182 :
4183 : //! @cond Doxygen_Suppress
4184 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4185 : CSLConstList /* papszOptions*/) const
4186 : {
4187 3 : return true;
4188 : }
4189 :
4190 : //! @endcond
4191 :
4192 : /************************************************************************/
4193 : /* MassageName() */
4194 : /************************************************************************/
4195 :
4196 : //! @cond Doxygen_Suppress
4197 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4198 : {
4199 32 : std::string ret;
4200 604 : for (const char ch : inputName)
4201 : {
4202 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4203 138 : ret += '_';
4204 : else
4205 434 : ret += ch;
4206 : }
4207 32 : return ret;
4208 : }
4209 :
4210 : //! @endcond
4211 :
4212 : /************************************************************************/
4213 : /* GetCacheRootGroup() */
4214 : /************************************************************************/
4215 :
4216 : //! @cond Doxygen_Suppress
4217 : std::shared_ptr<GDALGroup>
4218 1345 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4219 : std::string &osCacheFilenameOut) const
4220 : {
4221 1345 : const auto &osFilename = GetFilename();
4222 1345 : if (osFilename.empty())
4223 : {
4224 1 : CPLError(CE_Failure, CPLE_AppDefined,
4225 : "Cannot cache an array with an empty filename");
4226 1 : return nullptr;
4227 : }
4228 :
4229 1344 : osCacheFilenameOut = osFilename + ".gmac";
4230 1344 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4231 : {
4232 0 : const auto nPosQuestionMark = osFilename.find('?');
4233 0 : if (nPosQuestionMark != std::string::npos)
4234 : {
4235 : osCacheFilenameOut =
4236 0 : osFilename.substr(0, nPosQuestionMark)
4237 0 : .append(".gmac")
4238 0 : .append(osFilename.substr(nPosQuestionMark));
4239 : }
4240 : }
4241 1344 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4242 1344 : if (pszProxy != nullptr)
4243 7 : osCacheFilenameOut = pszProxy;
4244 :
4245 1344 : std::unique_ptr<GDALDataset> poDS;
4246 : VSIStatBufL sStat;
4247 1344 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4248 : {
4249 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4250 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4251 : nullptr, nullptr, nullptr));
4252 : }
4253 1344 : if (poDS)
4254 : {
4255 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4256 28 : return poDS->GetRootGroup();
4257 : }
4258 :
4259 1316 : if (bCanCreate)
4260 : {
4261 4 : const char *pszDrvName = "netCDF";
4262 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4263 4 : if (poDrv == nullptr)
4264 : {
4265 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4266 : pszDrvName);
4267 0 : return nullptr;
4268 : }
4269 : {
4270 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4271 8 : CPLErrorStateBackuper oErrorStateBackuper;
4272 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4273 : nullptr, nullptr));
4274 : }
4275 4 : if (!poDS)
4276 : {
4277 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4278 1 : if (pszProxy)
4279 : {
4280 1 : osCacheFilenameOut = pszProxy;
4281 1 : poDS.reset(poDrv->CreateMultiDimensional(
4282 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4283 : }
4284 : }
4285 4 : if (poDS)
4286 : {
4287 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4288 4 : return poDS->GetRootGroup();
4289 : }
4290 : else
4291 : {
4292 0 : CPLError(CE_Failure, CPLE_AppDefined,
4293 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4294 : "configuration option to write the cache in "
4295 : "another directory",
4296 : osCacheFilenameOut.c_str());
4297 : }
4298 : }
4299 :
4300 1312 : return nullptr;
4301 : }
4302 :
4303 : //! @endcond
4304 :
4305 : /************************************************************************/
4306 : /* Cache() */
4307 : /************************************************************************/
4308 :
4309 : /** Cache the content of the array into an auxiliary filename.
4310 : *
4311 : * The main purpose of this method is to be able to cache views that are
4312 : * expensive to compute, such as transposed arrays.
4313 : *
4314 : * The array will be stored in a file whose name is the one of
4315 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4316 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4317 : *
4318 : * If the .gmac file cannot be written next to the dataset, the
4319 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4320 : * directory.
4321 : *
4322 : * The GDALMDArray::Read() method will automatically use the cache when it
4323 : * exists. There is no timestamp checks between the source array and the cached
4324 : * array. If the source arrays changes, the cache must be manually deleted.
4325 : *
4326 : * This is the same as the C function GDALMDArrayCache()
4327 : *
4328 : * @note Driver implementation: optionally implemented.
4329 : *
4330 : * @param papszOptions List of options, null terminated, or NULL. Currently
4331 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4332 : * to specify the block size of the cached array.
4333 : * @return true in case of success.
4334 : */
4335 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4336 : {
4337 14 : std::string osCacheFilename;
4338 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4339 7 : if (!poRG)
4340 1 : return false;
4341 :
4342 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4343 6 : if (poRG->OpenMDArray(osCachedArrayName))
4344 : {
4345 2 : CPLError(CE_Failure, CPLE_NotSupported,
4346 : "An array with same name %s already exists in %s",
4347 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4348 2 : return false;
4349 : }
4350 :
4351 8 : CPLStringList aosOptions;
4352 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4353 4 : const auto &aoDims = GetDimensions();
4354 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4355 4 : if (!aoDims.empty())
4356 : {
4357 : std::string osBlockSize(
4358 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4359 4 : if (osBlockSize.empty())
4360 : {
4361 6 : const auto anBlockSize = GetBlockSize();
4362 3 : int idxDim = 0;
4363 10 : for (auto nBlockSize : anBlockSize)
4364 : {
4365 7 : if (idxDim > 0)
4366 4 : osBlockSize += ',';
4367 7 : if (nBlockSize == 0)
4368 7 : nBlockSize = 256;
4369 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4370 : osBlockSize +=
4371 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4372 7 : idxDim++;
4373 : }
4374 : }
4375 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4376 :
4377 4 : int idxDim = 0;
4378 13 : for (const auto &poDim : aoDims)
4379 : {
4380 9 : auto poNewDim = poRG->CreateDimension(
4381 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4382 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4383 9 : if (!poNewDim)
4384 0 : return false;
4385 9 : aoNewDims.emplace_back(poNewDim);
4386 9 : idxDim++;
4387 : }
4388 : }
4389 :
4390 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4391 8 : GetDataType(), aosOptions.List());
4392 4 : if (!poCachedArray)
4393 : {
4394 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4395 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4396 0 : return false;
4397 : }
4398 :
4399 4 : GUInt64 nCost = 0;
4400 8 : return poCachedArray->CopyFrom(nullptr, this,
4401 : false, // strict
4402 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4403 : }
4404 :
4405 : /************************************************************************/
4406 : /* Read() */
4407 : /************************************************************************/
4408 :
4409 3728 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4410 : const GInt64 *arrayStep, // step in elements
4411 : const GPtrDiff_t *bufferStride, // stride in elements
4412 : const GDALExtendedDataType &bufferDataType,
4413 : void *pDstBuffer, const void *pDstBufferAllocStart,
4414 : size_t nDstBufferAllocSize) const
4415 : {
4416 3728 : if (!m_bHasTriedCachedArray)
4417 : {
4418 1636 : m_bHasTriedCachedArray = true;
4419 1636 : if (IsCacheable())
4420 : {
4421 1636 : const auto &osFilename = GetFilename();
4422 2778 : if (!osFilename.empty() &&
4423 2778 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4424 : {
4425 2264 : std::string osCacheFilename;
4426 2264 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4427 1132 : if (poRG)
4428 : {
4429 : const std::string osCachedArrayName(
4430 32 : MassageName(GetFullName()));
4431 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4432 16 : if (m_poCachedArray)
4433 : {
4434 6 : const auto &dims = GetDimensions();
4435 : const auto &cachedDims =
4436 6 : m_poCachedArray->GetDimensions();
4437 6 : const size_t nDims = dims.size();
4438 : bool ok =
4439 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4440 6 : cachedDims.size() == nDims;
4441 19 : for (size_t i = 0; ok && i < nDims; ++i)
4442 : {
4443 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4444 : }
4445 6 : if (ok)
4446 : {
4447 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4448 : osCachedArrayName.c_str(),
4449 : osCacheFilename.c_str());
4450 : }
4451 : else
4452 : {
4453 0 : CPLError(CE_Warning, CPLE_AppDefined,
4454 : "Cached array %s in %s has incompatible "
4455 : "characteristics with current array.",
4456 : osCachedArrayName.c_str(),
4457 : osCacheFilename.c_str());
4458 0 : m_poCachedArray.reset();
4459 : }
4460 : }
4461 : }
4462 : }
4463 : }
4464 : }
4465 :
4466 3728 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4467 3728 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4468 : {
4469 0 : CPLError(CE_Failure, CPLE_AppDefined,
4470 : "Array data type is not convertible to buffer data type");
4471 0 : return false;
4472 : }
4473 :
4474 7456 : std::vector<GInt64> tmp_arrayStep;
4475 7456 : std::vector<GPtrDiff_t> tmp_bufferStride;
4476 3728 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4477 : bufferStride, bufferDataType, pDstBuffer,
4478 : pDstBufferAllocStart, nDstBufferAllocSize,
4479 : tmp_arrayStep, tmp_bufferStride))
4480 : {
4481 9 : return false;
4482 : }
4483 :
4484 3719 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4485 3719 : bufferDataType, pDstBuffer);
4486 : }
4487 :
4488 : /************************************************************************/
4489 : /* GetRootGroup() */
4490 : /************************************************************************/
4491 :
4492 : /** Return the root group to which this arrays belongs too.
4493 : *
4494 : * Note that arrays may be free standing and some drivers may not implement
4495 : * this method, hence nullptr may be returned.
4496 : *
4497 : * It is used internally by the GetResampled() method to detect if GLT
4498 : * orthorectification is available.
4499 : *
4500 : * @return the root group, or nullptr.
4501 : * @since GDAL 3.8
4502 : */
4503 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4504 : {
4505 0 : return nullptr;
4506 : }
4507 :
4508 : //! @cond Doxygen_Suppress
4509 :
4510 : /************************************************************************/
4511 : /* IsTransposedRequest() */
4512 : /************************************************************************/
4513 :
4514 689 : bool GDALMDArray::IsTransposedRequest(
4515 : const size_t *count,
4516 : const GPtrDiff_t *bufferStride) const // stride in elements
4517 : {
4518 : /*
4519 : For example:
4520 : count = [2,3,4]
4521 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4522 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4523 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4524 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4525 : */
4526 689 : const size_t nDims(GetDimensionCount());
4527 689 : size_t nCurStrideForRowMajorStrides = 1;
4528 689 : bool bRowMajorStrides = true;
4529 689 : size_t nElts = 1;
4530 689 : size_t nLastIdx = 0;
4531 1954 : for (size_t i = nDims; i > 0;)
4532 : {
4533 1265 : --i;
4534 1265 : if (bufferStride[i] < 0)
4535 0 : return false;
4536 1265 : if (static_cast<size_t>(bufferStride[i]) !=
4537 : nCurStrideForRowMajorStrides)
4538 : {
4539 221 : bRowMajorStrides = false;
4540 : }
4541 : // Integer overflows have already been checked in CheckReadWriteParams()
4542 1265 : nCurStrideForRowMajorStrides *= count[i];
4543 1265 : nElts *= count[i];
4544 1265 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4545 : }
4546 689 : if (bRowMajorStrides)
4547 540 : return false;
4548 149 : return nLastIdx == nElts - 1;
4549 : }
4550 :
4551 : /************************************************************************/
4552 : /* CopyToFinalBufferSameDataType() */
4553 : /************************************************************************/
4554 :
4555 : template <size_t N>
4556 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4557 : size_t nDims, const size_t *count,
4558 : const GPtrDiff_t *bufferStride)
4559 : {
4560 120 : std::vector<size_t> anStackCount(nDims);
4561 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4562 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4563 : #if defined(__GNUC__)
4564 : #pragma GCC diagnostic push
4565 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4566 : #endif
4567 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4568 : #if defined(__GNUC__)
4569 : #pragma GCC diagnostic pop
4570 : #endif
4571 60 : size_t iDim = 0;
4572 :
4573 749 : lbl_next_depth:
4574 749 : if (iDim == nDims - 1)
4575 : {
4576 661 : size_t n = count[iDim];
4577 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4578 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4579 29186 : while (n > 0)
4580 : {
4581 28525 : --n;
4582 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4583 28525 : pabyDstBuffer += bufferStrideLastDim;
4584 28525 : pabySrcBuffer += N;
4585 : }
4586 : }
4587 : else
4588 : {
4589 88 : anStackCount[iDim] = count[iDim];
4590 : while (true)
4591 : {
4592 689 : ++iDim;
4593 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4594 689 : goto lbl_next_depth;
4595 689 : lbl_return_to_caller_in_loop:
4596 689 : --iDim;
4597 689 : --anStackCount[iDim];
4598 689 : if (anStackCount[iDim] == 0)
4599 88 : break;
4600 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4601 : }
4602 : }
4603 749 : if (iDim > 0)
4604 689 : goto lbl_return_to_caller_in_loop;
4605 60 : }
4606 :
4607 : /************************************************************************/
4608 : /* CopyToFinalBuffer() */
4609 : /************************************************************************/
4610 :
4611 129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4612 : const GDALExtendedDataType &eSrcDataType,
4613 : void *pDstBuffer,
4614 : const GDALExtendedDataType &eDstDataType,
4615 : size_t nDims, const size_t *count,
4616 : const GPtrDiff_t *bufferStride)
4617 : {
4618 129 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4619 : // Use specialized implementation for well-known data types when no
4620 : // type conversion is needed
4621 129 : if (eSrcDataType == eDstDataType)
4622 : {
4623 110 : if (nSrcDataTypeSize == 1)
4624 : {
4625 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4626 : count, bufferStride);
4627 60 : return;
4628 : }
4629 69 : else if (nSrcDataTypeSize == 2)
4630 : {
4631 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4632 : count, bufferStride);
4633 1 : return;
4634 : }
4635 68 : else if (nSrcDataTypeSize == 4)
4636 : {
4637 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4638 : count, bufferStride);
4639 14 : return;
4640 : }
4641 54 : else if (nSrcDataTypeSize == 8)
4642 : {
4643 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4644 : count, bufferStride);
4645 4 : return;
4646 : }
4647 : }
4648 :
4649 69 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4650 138 : std::vector<size_t> anStackCount(nDims);
4651 138 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4652 69 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4653 69 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4654 69 : size_t iDim = 0;
4655 :
4656 338 : lbl_next_depth:
4657 338 : if (iDim == nDims - 1)
4658 : {
4659 328 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4660 328 : pabyDstBufferStack[iDim], eDstDataType,
4661 328 : bufferStride[iDim], count[iDim]);
4662 328 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4663 : }
4664 : else
4665 : {
4666 10 : anStackCount[iDim] = count[iDim];
4667 : while (true)
4668 : {
4669 269 : ++iDim;
4670 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4671 269 : goto lbl_next_depth;
4672 269 : lbl_return_to_caller_in_loop:
4673 269 : --iDim;
4674 269 : --anStackCount[iDim];
4675 269 : if (anStackCount[iDim] == 0)
4676 10 : break;
4677 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4678 : }
4679 : }
4680 338 : if (iDim > 0)
4681 269 : goto lbl_return_to_caller_in_loop;
4682 : }
4683 :
4684 : /************************************************************************/
4685 : /* TransposeLast2Dims() */
4686 : /************************************************************************/
4687 :
4688 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4689 : const GDALExtendedDataType &eDT,
4690 : const size_t nDims, const size_t *count,
4691 : const size_t nEltsNonLast2Dims)
4692 : {
4693 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4694 19 : const auto nDTSize = eDT.GetSize();
4695 : void *pTempBufferForLast2DimsTranspose =
4696 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4697 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4698 0 : return false;
4699 :
4700 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4701 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4702 : {
4703 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4704 : pTempBufferForLast2DimsTranspose,
4705 39 : eDT.GetNumericDataType(), count[nDims - 1],
4706 39 : count[nDims - 2]);
4707 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4708 : nDTSize * nEltsLast2Dims);
4709 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4710 : }
4711 :
4712 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4713 :
4714 19 : return true;
4715 : }
4716 :
4717 : /************************************************************************/
4718 : /* ReadForTransposedRequest() */
4719 : /************************************************************************/
4720 :
4721 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4722 : // transposed view yield to extremely poor/unusable performance. This fixes
4723 : // this by using temporary memory to read in a contiguous buffer in a
4724 : // row-major order, and then do the transposition to the final buffer.
4725 :
4726 148 : bool GDALMDArray::ReadForTransposedRequest(
4727 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4728 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4729 : void *pDstBuffer) const
4730 : {
4731 148 : const size_t nDims(GetDimensionCount());
4732 148 : if (nDims == 0)
4733 : {
4734 0 : CPLAssert(false);
4735 : return false; // shouldn't happen
4736 : }
4737 148 : size_t nElts = 1;
4738 418 : for (size_t i = 0; i < nDims; ++i)
4739 270 : nElts *= count[i];
4740 :
4741 296 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4742 148 : tmpBufferStrides.back() = 1;
4743 270 : for (size_t i = nDims - 1; i > 0;)
4744 : {
4745 122 : --i;
4746 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4747 : }
4748 :
4749 148 : const auto &eDT = GetDataType();
4750 148 : const auto nDTSize = eDT.GetSize();
4751 277 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4752 293 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4753 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4754 : {
4755 : // Optimization of the optimization if only the last 2 dims are
4756 : // transposed that saves on temporary buffer allocation
4757 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4758 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4759 23 : bool bRowMajorStridesForNonLast2Dims = true;
4760 23 : size_t nEltsNonLast2Dims = 1;
4761 40 : for (size_t i = nDims - 2; i > 0;)
4762 : {
4763 17 : --i;
4764 17 : if (static_cast<size_t>(bufferStride[i]) !=
4765 : nCurStrideForRowMajorStrides)
4766 : {
4767 4 : bRowMajorStridesForNonLast2Dims = false;
4768 : }
4769 : // Integer overflows have already been checked in
4770 : // CheckReadWriteParams()
4771 17 : nCurStrideForRowMajorStrides *= count[i];
4772 17 : nEltsNonLast2Dims *= count[i];
4773 : }
4774 23 : if (bRowMajorStridesForNonLast2Dims)
4775 : {
4776 : // We read in the final buffer!
4777 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4778 19 : eDT, pDstBuffer))
4779 : {
4780 0 : return false;
4781 : }
4782 :
4783 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4784 19 : nEltsNonLast2Dims);
4785 : }
4786 : }
4787 :
4788 129 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4789 129 : if (pTempBuffer == nullptr)
4790 0 : return false;
4791 :
4792 129 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4793 129 : pTempBuffer))
4794 : {
4795 0 : VSIFree(pTempBuffer);
4796 0 : return false;
4797 : }
4798 129 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4799 : count, bufferStride);
4800 :
4801 129 : if (eDT.NeedsFreeDynamicMemory())
4802 : {
4803 58 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4804 116 : for (size_t i = 0; i < nElts; ++i)
4805 : {
4806 58 : eDT.FreeDynamicMemory(pabyPtr);
4807 58 : pabyPtr += nDTSize;
4808 : }
4809 : }
4810 :
4811 129 : VSIFree(pTempBuffer);
4812 129 : return true;
4813 : }
4814 :
4815 : /************************************************************************/
4816 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4817 : /************************************************************************/
4818 :
4819 : // Returns true if at all following conditions are met:
4820 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4821 : // defines a row-major ordered contiguous buffer.
4822 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4823 : const size_t *count, const GInt64 *arrayStep,
4824 : const GPtrDiff_t *bufferStride,
4825 : const GDALExtendedDataType &bufferDataType) const
4826 : {
4827 78 : if (bufferDataType != GetDataType())
4828 5 : return false;
4829 73 : size_t nExpectedStride = 1;
4830 166 : for (size_t i = GetDimensionCount(); i > 0;)
4831 : {
4832 96 : --i;
4833 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4834 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4835 : {
4836 3 : return false;
4837 : }
4838 93 : nExpectedStride *= count[i];
4839 : }
4840 70 : return true;
4841 : }
4842 :
4843 : /************************************************************************/
4844 : /* ReadUsingContiguousIRead() */
4845 : /************************************************************************/
4846 :
4847 : // Used for example by the TileDB driver when requesting it with
4848 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4849 : // not defining a row-major ordered contiguous buffer.
4850 : // Should only be called when at least one of the above conditions are true,
4851 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4852 : // returning none.
4853 : // This method will call IRead() again with arrayStep[] == 1,
4854 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4855 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4856 : // content of that temporary buffer onto pDstBuffer.
4857 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4858 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4859 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4860 : void *pDstBuffer) const
4861 : {
4862 7 : const size_t nDims(GetDimensionCount());
4863 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4864 14 : std::vector<size_t> anTmpCount(nDims);
4865 7 : const auto &oType = GetDataType();
4866 7 : size_t nMemArraySize = oType.GetSize();
4867 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4868 7 : GPtrDiff_t nStride = 1;
4869 18 : for (size_t i = nDims; i > 0;)
4870 : {
4871 11 : --i;
4872 11 : if (arrayStep[i] > 0)
4873 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4874 : else
4875 2 : anTmpStartIdx[i] =
4876 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4877 : const uint64_t nCount =
4878 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4879 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4880 : {
4881 0 : CPLError(CE_Failure, CPLE_AppDefined,
4882 : "Read() failed due to too large memory requirement");
4883 0 : return false;
4884 : }
4885 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4886 11 : nMemArraySize *= anTmpCount[i];
4887 11 : anTmpStride[i] = nStride;
4888 11 : nStride *= anTmpCount[i];
4889 : }
4890 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4891 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4892 7 : if (!pTmpBuffer)
4893 0 : return false;
4894 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4895 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4896 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4897 : {
4898 0 : return false;
4899 : }
4900 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
4901 18 : for (size_t i = 0; i < nDims; ++i)
4902 : {
4903 11 : if (arrayStep[i] > 0)
4904 9 : anTmpStartIdx[i] = 0;
4905 : else
4906 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
4907 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
4908 22 : std::string(), std::string(), std::string(), std::string(),
4909 22 : anTmpCount[i]);
4910 : }
4911 : auto poMEMArray =
4912 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
4913 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
4914 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
4915 7 : bufferStride, bufferDataType, pDstBuffer);
4916 : }
4917 :
4918 : //! @endcond
4919 :
4920 : /************************************************************************/
4921 : /* GDALSlicedMDArray */
4922 : /************************************************************************/
4923 :
4924 : class GDALSlicedMDArray final : public GDALPamMDArray
4925 : {
4926 : private:
4927 : std::shared_ptr<GDALMDArray> m_poParent{};
4928 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4929 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
4930 : std::vector<Range>
4931 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
4932 :
4933 : mutable std::vector<GUInt64> m_parentStart;
4934 : mutable std::vector<size_t> m_parentCount;
4935 : mutable std::vector<GInt64> m_parentStep;
4936 : mutable std::vector<GPtrDiff_t> m_parentStride;
4937 :
4938 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
4939 : const GInt64 *arrayStep,
4940 : const GPtrDiff_t *bufferStride) const;
4941 :
4942 : protected:
4943 574 : explicit GDALSlicedMDArray(
4944 : const std::shared_ptr<GDALMDArray> &poParent,
4945 : const std::string &viewExpr,
4946 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4947 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
4948 : std::vector<Range> &&parentRanges)
4949 1722 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
4950 1722 : poParent->GetFullName() +
4951 1148 : " (" + viewExpr + ")"),
4952 1148 : GDALPamMDArray(std::string(),
4953 1148 : "Sliced view of " + poParent->GetFullName() + " (" +
4954 1148 : viewExpr + ")",
4955 1148 : GDALPamMultiDim::GetPAM(poParent),
4956 : poParent->GetContext()),
4957 1148 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
4958 574 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
4959 574 : m_parentRanges(std::move(parentRanges)),
4960 574 : m_parentStart(m_poParent->GetDimensionCount()),
4961 574 : m_parentCount(m_poParent->GetDimensionCount(), 1),
4962 574 : m_parentStep(m_poParent->GetDimensionCount()),
4963 4592 : m_parentStride(m_poParent->GetDimensionCount())
4964 : {
4965 574 : }
4966 :
4967 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
4968 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4969 : const GDALExtendedDataType &bufferDataType,
4970 : void *pDstBuffer) const override;
4971 :
4972 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
4973 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4974 : const GDALExtendedDataType &bufferDataType,
4975 : const void *pSrcBuffer) override;
4976 :
4977 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4978 : CSLConstList papszOptions) const override;
4979 :
4980 : public:
4981 : static std::shared_ptr<GDALSlicedMDArray>
4982 574 : Create(const std::shared_ptr<GDALMDArray> &poParent,
4983 : const std::string &viewExpr,
4984 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4985 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
4986 : std::vector<Range> &&parentRanges)
4987 : {
4988 574 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
4989 574 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
4990 :
4991 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
4992 574 : poParent, viewExpr, std::move(dims),
4993 574 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
4994 574 : newAr->SetSelf(newAr);
4995 574 : return newAr;
4996 : }
4997 :
4998 55 : bool IsWritable() const override
4999 : {
5000 55 : return m_poParent->IsWritable();
5001 : }
5002 :
5003 983 : const std::string &GetFilename() const override
5004 : {
5005 983 : return m_poParent->GetFilename();
5006 : }
5007 :
5008 : const std::vector<std::shared_ptr<GDALDimension>> &
5009 3646 : GetDimensions() const override
5010 : {
5011 3646 : return m_dims;
5012 : }
5013 :
5014 1383 : const GDALExtendedDataType &GetDataType() const override
5015 : {
5016 1383 : return m_poParent->GetDataType();
5017 : }
5018 :
5019 2 : const std::string &GetUnit() const override
5020 : {
5021 2 : return m_poParent->GetUnit();
5022 : }
5023 :
5024 : // bool SetUnit(const std::string& osUnit) override { return
5025 : // m_poParent->SetUnit(osUnit); }
5026 :
5027 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5028 : {
5029 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5030 2 : if (!poSrcSRS)
5031 1 : return nullptr;
5032 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5033 2 : std::vector<int> dstMapping;
5034 3 : for (int srcAxis : srcMapping)
5035 : {
5036 2 : bool bFound = false;
5037 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5038 : {
5039 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5040 3 : srcAxis - 1)
5041 : {
5042 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5043 2 : bFound = true;
5044 2 : break;
5045 : }
5046 : }
5047 2 : if (!bFound)
5048 : {
5049 0 : dstMapping.push_back(0);
5050 : }
5051 : }
5052 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5053 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5054 1 : return poClone;
5055 : }
5056 :
5057 55 : const void *GetRawNoDataValue() const override
5058 : {
5059 55 : return m_poParent->GetRawNoDataValue();
5060 : }
5061 :
5062 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5063 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5064 :
5065 2 : double GetOffset(bool *pbHasOffset,
5066 : GDALDataType *peStorageType) const override
5067 : {
5068 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5069 : }
5070 :
5071 2 : double GetScale(bool *pbHasScale,
5072 : GDALDataType *peStorageType) const override
5073 : {
5074 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5075 : }
5076 :
5077 : // bool SetOffset(double dfOffset) override { return
5078 : // m_poParent->SetOffset(dfOffset); }
5079 :
5080 : // bool SetScale(double dfScale) override { return
5081 : // m_poParent->SetScale(dfScale); }
5082 :
5083 197 : std::vector<GUInt64> GetBlockSize() const override
5084 : {
5085 197 : std::vector<GUInt64> ret(GetDimensionCount());
5086 394 : const auto parentBlockSize(m_poParent->GetBlockSize());
5087 595 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5088 : {
5089 398 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5090 398 : if (iOldAxis != static_cast<size_t>(-1))
5091 : {
5092 398 : ret[i] = parentBlockSize[iOldAxis];
5093 : }
5094 : }
5095 394 : return ret;
5096 : }
5097 :
5098 : std::shared_ptr<GDALAttribute>
5099 6 : GetAttribute(const std::string &osName) const override
5100 : {
5101 6 : return m_poParent->GetAttribute(osName);
5102 : }
5103 :
5104 : std::vector<std::shared_ptr<GDALAttribute>>
5105 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5106 : {
5107 24 : return m_poParent->GetAttributes(papszOptions);
5108 : }
5109 : };
5110 :
5111 : /************************************************************************/
5112 : /* PrepareParentArrays() */
5113 : /************************************************************************/
5114 :
5115 475 : void GDALSlicedMDArray::PrepareParentArrays(
5116 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5117 : const GPtrDiff_t *bufferStride) const
5118 : {
5119 475 : const size_t nParentDimCount = m_parentRanges.size();
5120 1481 : for (size_t i = 0; i < nParentDimCount; i++)
5121 : {
5122 : // For dimensions in parent that have no existence in sliced array
5123 1006 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5124 : }
5125 :
5126 1250 : for (size_t i = 0; i < m_dims.size(); i++)
5127 : {
5128 775 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5129 775 : if (iParent != static_cast<size_t>(-1))
5130 : {
5131 773 : m_parentStart[iParent] =
5132 773 : m_parentRanges[iParent].m_nIncr >= 0
5133 773 : ? m_parentRanges[iParent].m_nStartIdx +
5134 744 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5135 29 : : m_parentRanges[iParent].m_nStartIdx -
5136 58 : arrayStartIdx[i] *
5137 29 : static_cast<GUInt64>(
5138 29 : -m_parentRanges[iParent].m_nIncr);
5139 773 : m_parentCount[iParent] = count[i];
5140 773 : if (arrayStep)
5141 : {
5142 772 : m_parentStep[iParent] =
5143 772 : count[i] == 1 ? 1 :
5144 : // other checks should have ensured this does
5145 : // not overflow
5146 586 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5147 : }
5148 773 : if (bufferStride)
5149 : {
5150 772 : m_parentStride[iParent] = bufferStride[i];
5151 : }
5152 : }
5153 : }
5154 475 : }
5155 :
5156 : /************************************************************************/
5157 : /* IRead() */
5158 : /************************************************************************/
5159 :
5160 442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5161 : const GInt64 *arrayStep,
5162 : const GPtrDiff_t *bufferStride,
5163 : const GDALExtendedDataType &bufferDataType,
5164 : void *pDstBuffer) const
5165 : {
5166 442 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5167 884 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5168 442 : m_parentStep.data(), m_parentStride.data(),
5169 442 : bufferDataType, pDstBuffer);
5170 : }
5171 :
5172 : /************************************************************************/
5173 : /* IWrite() */
5174 : /************************************************************************/
5175 :
5176 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5177 : const size_t *count, const GInt64 *arrayStep,
5178 : const GPtrDiff_t *bufferStride,
5179 : const GDALExtendedDataType &bufferDataType,
5180 : const void *pSrcBuffer)
5181 : {
5182 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5183 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5184 32 : m_parentStep.data(), m_parentStride.data(),
5185 32 : bufferDataType, pSrcBuffer);
5186 : }
5187 :
5188 : /************************************************************************/
5189 : /* IAdviseRead() */
5190 : /************************************************************************/
5191 :
5192 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5193 : const size_t *count,
5194 : CSLConstList papszOptions) const
5195 : {
5196 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5197 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5198 1 : papszOptions);
5199 : }
5200 :
5201 : /************************************************************************/
5202 : /* CreateSlicedArray() */
5203 : /************************************************************************/
5204 :
5205 : static std::shared_ptr<GDALMDArray>
5206 592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5207 : const std::string &viewExpr, const std::string &activeSlice,
5208 : bool bRenameDimensions,
5209 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5210 : {
5211 592 : const auto &srcDims(self->GetDimensions());
5212 592 : if (srcDims.empty())
5213 : {
5214 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5215 2 : return nullptr;
5216 : }
5217 :
5218 1180 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5219 590 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5220 :
5221 1180 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5222 1180 : std::vector<size_t> mapDimIdxToParentDimIdx;
5223 1180 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5224 590 : newDims.reserve(nTokens);
5225 590 : mapDimIdxToParentDimIdx.reserve(nTokens);
5226 590 : parentRanges.reserve(nTokens);
5227 :
5228 590 : bool bGotEllipsis = false;
5229 590 : size_t nCurSrcDim = 0;
5230 1744 : for (size_t i = 0; i < nTokens; i++)
5231 : {
5232 1170 : const char *pszIdxSpec = aosTokens[i];
5233 1170 : if (EQUAL(pszIdxSpec, "..."))
5234 : {
5235 37 : if (bGotEllipsis)
5236 : {
5237 2 : CPLError(CE_Failure, CPLE_AppDefined,
5238 : "Only one single ellipsis is supported");
5239 2 : return nullptr;
5240 : }
5241 35 : bGotEllipsis = true;
5242 35 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5243 77 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5244 : {
5245 42 : parentRanges.emplace_back(0, 1);
5246 42 : newDims.push_back(srcDims[nCurSrcDim]);
5247 42 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5248 : }
5249 35 : continue;
5250 : }
5251 1133 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5252 1130 : EQUAL(pszIdxSpec, "np.newaxis"))
5253 : {
5254 3 : newDims.push_back(std::make_shared<GDALDimension>(
5255 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5256 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5257 3 : continue;
5258 : }
5259 1130 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5260 : {
5261 323 : if (nCurSrcDim >= srcDims.size())
5262 : {
5263 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5264 : activeSlice.c_str());
5265 7 : return nullptr;
5266 : }
5267 :
5268 321 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5269 321 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5270 321 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5271 317 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5272 : {
5273 5 : CPLError(CE_Failure, CPLE_AppDefined,
5274 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5275 5 : return nullptr;
5276 : }
5277 316 : if (nVal < 0)
5278 0 : nVal += nDimSize;
5279 316 : parentRanges.emplace_back(nVal, 0);
5280 : }
5281 : else
5282 : {
5283 807 : if (nCurSrcDim >= srcDims.size())
5284 : {
5285 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5286 : activeSlice.c_str());
5287 7 : return nullptr;
5288 : }
5289 :
5290 : CPLStringList aosRangeTokens(
5291 806 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5292 806 : int nRangeTokens = aosRangeTokens.size();
5293 806 : if (nRangeTokens > 3)
5294 : {
5295 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5296 : pszIdxSpec);
5297 1 : return nullptr;
5298 : }
5299 805 : if (nRangeTokens <= 1)
5300 : {
5301 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5302 : pszIdxSpec);
5303 1 : return nullptr;
5304 : }
5305 804 : const char *pszStart = aosRangeTokens[0];
5306 804 : const char *pszEnd = aosRangeTokens[1];
5307 804 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5308 804 : GDALSlicedMDArray::Range range;
5309 804 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5310 804 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5311 1607 : if (range.m_nIncr == 0 ||
5312 803 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5313 : {
5314 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5315 1 : return nullptr;
5316 : }
5317 803 : auto startIdx(CPLAtoGIntBig(pszStart));
5318 803 : if (startIdx < 0)
5319 : {
5320 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5321 0 : startIdx = 0;
5322 : else
5323 0 : startIdx = nDimSize + startIdx;
5324 : }
5325 803 : const bool bPosIncr = range.m_nIncr > 0;
5326 803 : range.m_nStartIdx = startIdx;
5327 1606 : range.m_nStartIdx = EQUAL(pszStart, "")
5328 803 : ? (bPosIncr ? 0 : nDimSize - 1)
5329 : : range.m_nStartIdx;
5330 803 : if (range.m_nStartIdx >= nDimSize - 1)
5331 185 : range.m_nStartIdx = nDimSize - 1;
5332 803 : auto endIdx(CPLAtoGIntBig(pszEnd));
5333 803 : if (endIdx < 0)
5334 : {
5335 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5336 1 : if (nDimSize < positiveEndIdx)
5337 0 : endIdx = 0;
5338 : else
5339 1 : endIdx = nDimSize - positiveEndIdx;
5340 : }
5341 803 : GUInt64 nEndIdx = endIdx;
5342 803 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5343 803 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5344 801 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5345 : {
5346 3 : CPLError(CE_Failure, CPLE_AppDefined,
5347 : "Output dimension of size 0 is not allowed");
5348 3 : return nullptr;
5349 : }
5350 800 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5351 800 : const auto nAbsIncr = std::abs(range.m_nIncr);
5352 800 : const GUInt64 newSize =
5353 : bPosIncr
5354 833 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5355 33 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5356 1322 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5357 522 : newSize == srcDims[nCurSrcDim]->GetSize())
5358 : {
5359 153 : newDims.push_back(srcDims[nCurSrcDim]);
5360 : }
5361 : else
5362 : {
5363 647 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5364 647 : if (bRenameDimensions)
5365 : {
5366 : osNewDimName =
5367 1210 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5368 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5369 : "_" CPL_FRMT_GUIB,
5370 605 : static_cast<GUIntBig>(range.m_nStartIdx),
5371 605 : static_cast<GIntBig>(range.m_nIncr),
5372 605 : static_cast<GUIntBig>(newSize));
5373 : }
5374 647 : newDims.push_back(std::make_shared<GDALDimension>(
5375 1294 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5376 1294 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5377 : : std::string(),
5378 : newSize));
5379 : }
5380 800 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5381 800 : parentRanges.emplace_back(range);
5382 : }
5383 :
5384 1116 : nCurSrcDim++;
5385 : }
5386 647 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5387 : {
5388 73 : parentRanges.emplace_back(0, 1);
5389 73 : newDims.push_back(srcDims[nCurSrcDim]);
5390 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5391 : }
5392 :
5393 574 : GDALMDArray::ViewSpec viewSpec;
5394 574 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5395 574 : viewSpec.m_parentRanges = parentRanges;
5396 574 : viewSpecs.emplace_back(std::move(viewSpec));
5397 :
5398 1148 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5399 574 : std::move(mapDimIdxToParentDimIdx),
5400 1148 : std::move(parentRanges));
5401 : }
5402 :
5403 : /************************************************************************/
5404 : /* GDALExtractFieldMDArray */
5405 : /************************************************************************/
5406 :
5407 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5408 : {
5409 : private:
5410 : std::shared_ptr<GDALMDArray> m_poParent{};
5411 : GDALExtendedDataType m_dt;
5412 : std::string m_srcCompName;
5413 : mutable std::vector<GByte> m_pabyNoData{};
5414 :
5415 : protected:
5416 62 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5417 : const std::string &fieldName,
5418 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5419 248 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5420 124 : " of " +
5421 62 : poParent->GetFullName()),
5422 : GDALPamMDArray(
5423 124 : std::string(),
5424 124 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5425 124 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5426 : m_poParent(poParent), m_dt(srcComp->GetType()),
5427 310 : m_srcCompName(srcComp->GetName())
5428 : {
5429 62 : m_pabyNoData.resize(m_dt.GetSize());
5430 62 : }
5431 :
5432 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5433 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5434 : const GDALExtendedDataType &bufferDataType,
5435 : void *pDstBuffer) const override;
5436 :
5437 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5438 : CSLConstList papszOptions) const override
5439 : {
5440 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5441 : }
5442 :
5443 : public:
5444 : static std::shared_ptr<GDALExtractFieldMDArray>
5445 62 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5446 : const std::string &fieldName,
5447 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5448 : {
5449 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5450 62 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5451 62 : newAr->SetSelf(newAr);
5452 62 : return newAr;
5453 : }
5454 :
5455 124 : ~GDALExtractFieldMDArray()
5456 62 : {
5457 62 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5458 124 : }
5459 :
5460 40 : bool IsWritable() const override
5461 : {
5462 40 : return m_poParent->IsWritable();
5463 : }
5464 :
5465 204 : const std::string &GetFilename() const override
5466 : {
5467 204 : return m_poParent->GetFilename();
5468 : }
5469 :
5470 : const std::vector<std::shared_ptr<GDALDimension>> &
5471 300 : GetDimensions() const override
5472 : {
5473 300 : return m_poParent->GetDimensions();
5474 : }
5475 :
5476 245 : const GDALExtendedDataType &GetDataType() const override
5477 : {
5478 245 : return m_dt;
5479 : }
5480 :
5481 2 : const std::string &GetUnit() const override
5482 : {
5483 2 : return m_poParent->GetUnit();
5484 : }
5485 :
5486 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5487 : {
5488 2 : return m_poParent->GetSpatialRef();
5489 : }
5490 :
5491 56 : const void *GetRawNoDataValue() const override
5492 : {
5493 56 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5494 56 : if (parentNoData == nullptr)
5495 1 : return nullptr;
5496 :
5497 55 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5498 55 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5499 :
5500 110 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5501 110 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5502 110 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5503 55 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5504 165 : std::move(comps)));
5505 :
5506 55 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5507 55 : &m_pabyNoData[0], tmpDT);
5508 :
5509 55 : return &m_pabyNoData[0];
5510 : }
5511 :
5512 2 : double GetOffset(bool *pbHasOffset,
5513 : GDALDataType *peStorageType) const override
5514 : {
5515 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5516 : }
5517 :
5518 2 : double GetScale(bool *pbHasScale,
5519 : GDALDataType *peStorageType) const override
5520 : {
5521 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5522 : }
5523 :
5524 41 : std::vector<GUInt64> GetBlockSize() const override
5525 : {
5526 41 : return m_poParent->GetBlockSize();
5527 : }
5528 : };
5529 :
5530 : /************************************************************************/
5531 : /* IRead() */
5532 : /************************************************************************/
5533 :
5534 46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5535 : const size_t *count,
5536 : const GInt64 *arrayStep,
5537 : const GPtrDiff_t *bufferStride,
5538 : const GDALExtendedDataType &bufferDataType,
5539 : void *pDstBuffer) const
5540 : {
5541 92 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5542 92 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5543 92 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5544 : auto tmpDT(GDALExtendedDataType::Create(
5545 92 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5546 :
5547 46 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5548 92 : tmpDT, pDstBuffer);
5549 : }
5550 :
5551 : /************************************************************************/
5552 : /* CreateFieldNameExtractArray() */
5553 : /************************************************************************/
5554 :
5555 : static std::shared_ptr<GDALMDArray>
5556 63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5557 : const std::string &fieldName)
5558 : {
5559 63 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5560 63 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5561 125 : for (const auto &comp : self->GetDataType().GetComponents())
5562 : {
5563 124 : if (comp->GetName() == fieldName)
5564 : {
5565 62 : srcComp = ∁
5566 62 : break;
5567 : }
5568 : }
5569 63 : if (srcComp == nullptr)
5570 : {
5571 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5572 : fieldName.c_str());
5573 1 : return nullptr;
5574 : }
5575 62 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5576 : }
5577 :
5578 : /************************************************************************/
5579 : /* GetView() */
5580 : /************************************************************************/
5581 :
5582 : // clang-format off
5583 : /** Return a view of the array using slicing or field access.
5584 : *
5585 : * The slice expression uses the same syntax as NumPy basic slicing and
5586 : * indexing. See
5587 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5588 : * Or it can use field access by name. See
5589 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5590 : *
5591 : * Multiple [] bracket elements can be concatenated, with a slice expression
5592 : * or field name inside each.
5593 : *
5594 : * For basic slicing and indexing, inside each [] bracket element, a list of
5595 : * indexes that apply to successive source dimensions, can be specified, using
5596 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5597 : * or newaxis, using a comma separator.
5598 : *
5599 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5600 : * <ul>
5601 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5602 : * at index 1 in the first dimension, and index 2 in the second dimension
5603 : * from the source array. That is 5</li>
5604 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5605 : * implemented internally doing this intermediate slicing approach.</li>
5606 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5607 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5608 : * first dimension. That is [4,5,6,7].</li>
5609 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5610 : * second dimension. That is [2,6].</li>
5611 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5612 : * the second dimension. That is [[2],[6]].</li>
5613 : * <li>GetView("[::,2]"): Same as
5614 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5615 : * ellipsis only expands to one dimension here.</li>
5616 : * <li>GetView("[:,::2]"):
5617 : * returns a 2-dimensional array, with even-indexed elements of the second
5618 : * dimension. That is [[0,2],[4,6]].</li>
5619 : * <li>GetView("[:,1::2]"): returns a
5620 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5621 : * is [[1,3],[5,7]].</li>
5622 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5623 : * array, with elements of the second dimension with index in the range [1,3[.
5624 : * That is [[1,2],[5,6]].</li>
5625 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5626 : * array, with the values in first dimension reversed. That is
5627 : * [[4,5,6,7],[0,1,2,3]].</li>
5628 : * <li>GetView("[newaxis,...]"): returns a
5629 : * 3-dimensional array, with an additional dimension of size 1 put at the
5630 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5631 : * </ul>
5632 : *
5633 : * One difference with NumPy behavior is that ranges that would result in
5634 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5635 : * GDAL multidimensional model).
5636 : *
5637 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5638 : * Multiple field specification is not supported currently.
5639 : *
5640 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5641 : *
5642 : * \note When using the GDAL Python bindings, natural Python syntax can be
5643 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5644 : * ar.GetView("[0,::,1]['foo']")
5645 : * \note When using the C++ API and integer indexing only, you may use the
5646 : * at(idx0, idx1, ...) method.
5647 : *
5648 : * The returned array holds a reference to the original one, and thus is
5649 : * a view of it (not a copy). If the content of the original array changes,
5650 : * the content of the view array too. When using basic slicing and indexing,
5651 : * the view can be written if the underlying array is writable.
5652 : *
5653 : * This is the same as the C function GDALMDArrayGetView()
5654 : *
5655 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5656 : * access.
5657 : * @return a new array, that holds a reference to the original one, and thus is
5658 : * a view of it (not a copy), or nullptr in case of error.
5659 : */
5660 : // clang-format on
5661 :
5662 : std::shared_ptr<GDALMDArray>
5663 598 : GDALMDArray::GetView(const std::string &viewExpr) const
5664 : {
5665 1196 : std::vector<ViewSpec> viewSpecs;
5666 1196 : return GetView(viewExpr, true, viewSpecs);
5667 : }
5668 :
5669 : //! @cond Doxygen_Suppress
5670 : std::shared_ptr<GDALMDArray>
5671 661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5672 : std::vector<ViewSpec> &viewSpecs) const
5673 : {
5674 1322 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5675 661 : if (!self)
5676 : {
5677 1 : CPLError(CE_Failure, CPLE_AppDefined,
5678 : "Driver implementation issue: m_pSelf not set !");
5679 1 : return nullptr;
5680 : }
5681 660 : std::string curExpr(viewExpr);
5682 : while (true)
5683 : {
5684 663 : if (curExpr.empty() || curExpr[0] != '[')
5685 : {
5686 2 : CPLError(CE_Failure, CPLE_AppDefined,
5687 : "Slice string should start with ['");
5688 660 : return nullptr;
5689 : }
5690 :
5691 661 : std::string fieldName;
5692 : size_t endExpr;
5693 661 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5694 : {
5695 67 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5696 : {
5697 2 : CPLError(CE_Failure, CPLE_AppDefined,
5698 : "Field access not allowed on non-compound data type");
5699 2 : return nullptr;
5700 : }
5701 65 : size_t idx = 2;
5702 572 : for (; idx < curExpr.size(); idx++)
5703 : {
5704 571 : const char ch = curExpr[idx];
5705 571 : if (ch == curExpr[1])
5706 64 : break;
5707 507 : if (ch == '\\' && idx + 1 < curExpr.size())
5708 : {
5709 1 : fieldName += curExpr[idx + 1];
5710 1 : idx++;
5711 : }
5712 : else
5713 : {
5714 506 : fieldName += ch;
5715 : }
5716 : }
5717 65 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5718 : {
5719 2 : CPLError(CE_Failure, CPLE_AppDefined,
5720 : "Invalid field access specification");
5721 2 : return nullptr;
5722 : }
5723 63 : endExpr = idx + 1;
5724 : }
5725 : else
5726 : {
5727 594 : endExpr = curExpr.find(']');
5728 : }
5729 657 : if (endExpr == std::string::npos)
5730 : {
5731 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5732 1 : return nullptr;
5733 : }
5734 656 : if (endExpr == 1)
5735 : {
5736 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5737 1 : return nullptr;
5738 : }
5739 655 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5740 :
5741 655 : if (!fieldName.empty())
5742 : {
5743 126 : ViewSpec viewSpec;
5744 63 : viewSpec.m_osFieldName = fieldName;
5745 63 : viewSpecs.emplace_back(std::move(viewSpec));
5746 : }
5747 :
5748 655 : auto newArray = !fieldName.empty()
5749 : ? CreateFieldNameExtractArray(self, fieldName)
5750 : : CreateSlicedArray(self, viewExpr, activeSlice,
5751 655 : bRenameDimensions, viewSpecs);
5752 :
5753 655 : if (endExpr == curExpr.size() - 1)
5754 : {
5755 652 : return newArray;
5756 : }
5757 3 : self = std::move(newArray);
5758 3 : curExpr = curExpr.substr(endExpr + 1);
5759 3 : }
5760 : }
5761 :
5762 : //! @endcond
5763 :
5764 : std::shared_ptr<GDALMDArray>
5765 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5766 : {
5767 19 : std::string osExpr("[");
5768 19 : bool bFirst = true;
5769 45 : for (const auto &idx : indices)
5770 : {
5771 26 : if (!bFirst)
5772 7 : osExpr += ',';
5773 26 : bFirst = false;
5774 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5775 : }
5776 57 : return GetView(osExpr + ']');
5777 : }
5778 :
5779 : /************************************************************************/
5780 : /* operator[] */
5781 : /************************************************************************/
5782 :
5783 : /** Return a view of the array using field access
5784 : *
5785 : * Equivalent of GetView("['fieldName']")
5786 : *
5787 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5788 : */
5789 : std::shared_ptr<GDALMDArray>
5790 2 : GDALMDArray::operator[](const std::string &fieldName) const
5791 : {
5792 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5793 4 : .replaceAll('\\', "\\\\")
5794 4 : .replaceAll('\'', "\\\'")
5795 6 : .c_str()));
5796 : }
5797 :
5798 : /************************************************************************/
5799 : /* GDALMDArrayTransposed */
5800 : /************************************************************************/
5801 :
5802 : class GDALMDArrayTransposed final : public GDALPamMDArray
5803 : {
5804 : private:
5805 : std::shared_ptr<GDALMDArray> m_poParent{};
5806 : std::vector<int> m_anMapNewAxisToOldAxis{};
5807 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5808 :
5809 : mutable std::vector<GUInt64> m_parentStart;
5810 : mutable std::vector<size_t> m_parentCount;
5811 : mutable std::vector<GInt64> m_parentStep;
5812 : mutable std::vector<GPtrDiff_t> m_parentStride;
5813 :
5814 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5815 : const GInt64 *arrayStep,
5816 : const GPtrDiff_t *bufferStride) const;
5817 :
5818 : static std::string
5819 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5820 : {
5821 84 : std::string ret;
5822 84 : ret += '[';
5823 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5824 : {
5825 228 : if (i > 0)
5826 144 : ret += ',';
5827 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5828 : }
5829 84 : ret += ']';
5830 84 : return ret;
5831 : }
5832 :
5833 : protected:
5834 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5835 : const std::vector<int> &anMapNewAxisToOldAxis,
5836 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5837 84 : : GDALAbstractMDArray(std::string(),
5838 84 : "Transposed view of " + poParent->GetFullName() +
5839 84 : " along " +
5840 42 : MappingToStr(anMapNewAxisToOldAxis)),
5841 84 : GDALPamMDArray(std::string(),
5842 84 : "Transposed view of " + poParent->GetFullName() +
5843 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5844 84 : GDALPamMultiDim::GetPAM(poParent),
5845 : poParent->GetContext()),
5846 42 : m_poParent(std::move(poParent)),
5847 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5848 42 : m_dims(std::move(dims)),
5849 42 : m_parentStart(m_poParent->GetDimensionCount()),
5850 42 : m_parentCount(m_poParent->GetDimensionCount()),
5851 42 : m_parentStep(m_poParent->GetDimensionCount()),
5852 336 : m_parentStride(m_poParent->GetDimensionCount())
5853 : {
5854 42 : }
5855 :
5856 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5857 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5858 : const GDALExtendedDataType &bufferDataType,
5859 : void *pDstBuffer) const override;
5860 :
5861 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5862 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5863 : const GDALExtendedDataType &bufferDataType,
5864 : const void *pSrcBuffer) override;
5865 :
5866 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5867 : CSLConstList papszOptions) const override;
5868 :
5869 : public:
5870 : static std::shared_ptr<GDALMDArrayTransposed>
5871 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5872 : const std::vector<int> &anMapNewAxisToOldAxis)
5873 : {
5874 42 : const auto &parentDims(poParent->GetDimensions());
5875 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5876 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5877 : {
5878 114 : if (iOldAxis < 0)
5879 : {
5880 1 : dims.push_back(std::make_shared<GDALDimension>(
5881 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5882 : }
5883 : else
5884 : {
5885 113 : dims.emplace_back(parentDims[iOldAxis]);
5886 : }
5887 : }
5888 :
5889 : auto newAr(
5890 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5891 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5892 42 : newAr->SetSelf(newAr);
5893 84 : return newAr;
5894 : }
5895 :
5896 1 : bool IsWritable() const override
5897 : {
5898 1 : return m_poParent->IsWritable();
5899 : }
5900 :
5901 84 : const std::string &GetFilename() const override
5902 : {
5903 84 : return m_poParent->GetFilename();
5904 : }
5905 :
5906 : const std::vector<std::shared_ptr<GDALDimension>> &
5907 358 : GetDimensions() const override
5908 : {
5909 358 : return m_dims;
5910 : }
5911 :
5912 141 : const GDALExtendedDataType &GetDataType() const override
5913 : {
5914 141 : return m_poParent->GetDataType();
5915 : }
5916 :
5917 4 : const std::string &GetUnit() const override
5918 : {
5919 4 : return m_poParent->GetUnit();
5920 : }
5921 :
5922 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5923 : {
5924 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
5925 5 : if (!poSrcSRS)
5926 2 : return nullptr;
5927 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5928 6 : std::vector<int> dstMapping;
5929 9 : for (int srcAxis : srcMapping)
5930 : {
5931 6 : bool bFound = false;
5932 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
5933 : {
5934 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
5935 : {
5936 6 : dstMapping.push_back(static_cast<int>(i) + 1);
5937 6 : bFound = true;
5938 6 : break;
5939 : }
5940 : }
5941 6 : if (!bFound)
5942 : {
5943 0 : dstMapping.push_back(0);
5944 : }
5945 : }
5946 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5947 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5948 3 : return poClone;
5949 : }
5950 :
5951 4 : const void *GetRawNoDataValue() const override
5952 : {
5953 4 : return m_poParent->GetRawNoDataValue();
5954 : }
5955 :
5956 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5957 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5958 :
5959 4 : double GetOffset(bool *pbHasOffset,
5960 : GDALDataType *peStorageType) const override
5961 : {
5962 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5963 : }
5964 :
5965 4 : double GetScale(bool *pbHasScale,
5966 : GDALDataType *peStorageType) const override
5967 : {
5968 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
5969 : }
5970 :
5971 : // bool SetOffset(double dfOffset) override { return
5972 : // m_poParent->SetOffset(dfOffset); }
5973 :
5974 : // bool SetScale(double dfScale) override { return
5975 : // m_poParent->SetScale(dfScale); }
5976 :
5977 3 : std::vector<GUInt64> GetBlockSize() const override
5978 : {
5979 3 : std::vector<GUInt64> ret(GetDimensionCount());
5980 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
5981 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
5982 : {
5983 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
5984 8 : if (iOldAxis >= 0)
5985 : {
5986 7 : ret[i] = parentBlockSize[iOldAxis];
5987 : }
5988 : }
5989 6 : return ret;
5990 : }
5991 :
5992 : std::shared_ptr<GDALAttribute>
5993 1 : GetAttribute(const std::string &osName) const override
5994 : {
5995 1 : return m_poParent->GetAttribute(osName);
5996 : }
5997 :
5998 : std::vector<std::shared_ptr<GDALAttribute>>
5999 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6000 : {
6001 6 : return m_poParent->GetAttributes(papszOptions);
6002 : }
6003 : };
6004 :
6005 : /************************************************************************/
6006 : /* PrepareParentArrays() */
6007 : /************************************************************************/
6008 :
6009 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6010 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6011 : const GPtrDiff_t *bufferStride) const
6012 : {
6013 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6014 : {
6015 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6016 129 : if (iOldAxis >= 0)
6017 : {
6018 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6019 128 : m_parentCount[iOldAxis] = count[i];
6020 128 : if (arrayStep) // only null when called from IAdviseRead()
6021 : {
6022 126 : m_parentStep[iOldAxis] = arrayStep[i];
6023 : }
6024 128 : if (bufferStride) // only null when called from IAdviseRead()
6025 : {
6026 126 : m_parentStride[iOldAxis] = bufferStride[i];
6027 : }
6028 : }
6029 : }
6030 47 : }
6031 :
6032 : /************************************************************************/
6033 : /* IRead() */
6034 : /************************************************************************/
6035 :
6036 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6037 : const size_t *count, const GInt64 *arrayStep,
6038 : const GPtrDiff_t *bufferStride,
6039 : const GDALExtendedDataType &bufferDataType,
6040 : void *pDstBuffer) const
6041 : {
6042 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6043 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6044 44 : m_parentStep.data(), m_parentStride.data(),
6045 44 : bufferDataType, pDstBuffer);
6046 : }
6047 :
6048 : /************************************************************************/
6049 : /* IWrite() */
6050 : /************************************************************************/
6051 :
6052 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6053 : const size_t *count, const GInt64 *arrayStep,
6054 : const GPtrDiff_t *bufferStride,
6055 : const GDALExtendedDataType &bufferDataType,
6056 : const void *pSrcBuffer)
6057 : {
6058 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6059 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6060 2 : m_parentStep.data(), m_parentStride.data(),
6061 2 : bufferDataType, pSrcBuffer);
6062 : }
6063 :
6064 : /************************************************************************/
6065 : /* IAdviseRead() */
6066 : /************************************************************************/
6067 :
6068 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6069 : const size_t *count,
6070 : CSLConstList papszOptions) const
6071 : {
6072 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6073 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6074 1 : papszOptions);
6075 : }
6076 :
6077 : /************************************************************************/
6078 : /* Transpose() */
6079 : /************************************************************************/
6080 :
6081 : /** Return a view of the array whose axis have been reordered.
6082 : *
6083 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6084 : * and GetDimensionCount() - 1, and each only once.
6085 : * -1 can be used as a special index value to ask for the insertion of a new
6086 : * axis of size 1.
6087 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6088 : * index of one of its dimension, it corresponds to the axis of index
6089 : * anMapNewAxisToOldAxis[i] from the current array.
6090 : *
6091 : * This is similar to the numpy.transpose() method
6092 : *
6093 : * The returned array holds a reference to the original one, and thus is
6094 : * a view of it (not a copy). If the content of the original array changes,
6095 : * the content of the view array too. The view can be written if the underlying
6096 : * array is writable.
6097 : *
6098 : * Note that I/O performance in such a transposed view might be poor.
6099 : *
6100 : * This is the same as the C function GDALMDArrayTranspose().
6101 : *
6102 : * @return a new array, that holds a reference to the original one, and thus is
6103 : * a view of it (not a copy), or nullptr in case of error.
6104 : */
6105 : std::shared_ptr<GDALMDArray>
6106 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6107 : {
6108 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6109 50 : if (!self)
6110 : {
6111 0 : CPLError(CE_Failure, CPLE_AppDefined,
6112 : "Driver implementation issue: m_pSelf not set !");
6113 0 : return nullptr;
6114 : }
6115 50 : const int nDims = static_cast<int>(GetDimensionCount());
6116 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6117 50 : int nCountOldAxis = 0;
6118 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6119 : {
6120 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6121 : {
6122 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6123 4 : return nullptr;
6124 : }
6125 130 : if (iOldAxis >= 0)
6126 : {
6127 128 : if (alreadyUsedOldAxis[iOldAxis])
6128 : {
6129 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6130 : iOldAxis);
6131 1 : return nullptr;
6132 : }
6133 127 : alreadyUsedOldAxis[iOldAxis] = true;
6134 127 : nCountOldAxis++;
6135 : }
6136 : }
6137 46 : if (nCountOldAxis != nDims)
6138 : {
6139 4 : CPLError(CE_Failure, CPLE_AppDefined,
6140 : "One or several original axis missing");
6141 4 : return nullptr;
6142 : }
6143 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6144 : }
6145 :
6146 : /************************************************************************/
6147 : /* IRead() */
6148 : /************************************************************************/
6149 :
6150 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6151 : const size_t *count, const GInt64 *arrayStep,
6152 : const GPtrDiff_t *bufferStride,
6153 : const GDALExtendedDataType &bufferDataType,
6154 : void *pDstBuffer) const
6155 : {
6156 16 : const double dfScale = m_dfScale;
6157 16 : const double dfOffset = m_dfOffset;
6158 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6159 : const auto dtDouble =
6160 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6161 16 : const size_t nDTSize = dtDouble.GetSize();
6162 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6163 :
6164 16 : double adfSrcNoData[2] = {0, 0};
6165 16 : if (m_bHasNoData)
6166 : {
6167 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6168 9 : m_poParent->GetDataType(),
6169 : &adfSrcNoData[0], dtDouble);
6170 : }
6171 :
6172 16 : const auto nDims = GetDimensions().size();
6173 16 : if (nDims == 0)
6174 : {
6175 : double adfVal[2];
6176 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6177 : dtDouble, &adfVal[0]))
6178 : {
6179 0 : return false;
6180 : }
6181 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6182 : {
6183 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6184 6 : if (bDTIsComplex)
6185 : {
6186 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6187 : }
6188 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6189 : bufferDataType);
6190 : }
6191 : else
6192 : {
6193 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6194 : pDstBuffer, bufferDataType);
6195 : }
6196 9 : return true;
6197 : }
6198 :
6199 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6200 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6201 7 : void *pTempBuffer = pDstBuffer;
6202 7 : if (bTempBufferNeeded)
6203 : {
6204 2 : size_t nElts = 1;
6205 2 : actualBufferStrideVector.resize(nDims);
6206 7 : for (size_t i = 0; i < nDims; i++)
6207 5 : nElts *= count[i];
6208 2 : actualBufferStrideVector.back() = 1;
6209 5 : for (size_t i = nDims - 1; i > 0;)
6210 : {
6211 3 : --i;
6212 3 : actualBufferStrideVector[i] =
6213 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6214 : }
6215 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6216 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6217 2 : if (!pTempBuffer)
6218 0 : return false;
6219 : }
6220 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6221 : actualBufferStridePtr, dtDouble, pTempBuffer))
6222 : {
6223 0 : if (bTempBufferNeeded)
6224 0 : VSIFree(pTempBuffer);
6225 0 : return false;
6226 : }
6227 :
6228 : struct Stack
6229 : {
6230 : size_t nIters = 0;
6231 : double *src_ptr = nullptr;
6232 : GByte *dst_ptr = nullptr;
6233 : GPtrDiff_t src_inc_offset = 0;
6234 : GPtrDiff_t dst_inc_offset = 0;
6235 : };
6236 :
6237 7 : std::vector<Stack> stack(nDims);
6238 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6239 23 : for (size_t i = 0; i < nDims; i++)
6240 : {
6241 32 : stack[i].src_inc_offset =
6242 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6243 16 : stack[i].dst_inc_offset =
6244 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6245 : }
6246 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6247 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6248 :
6249 7 : size_t dimIdx = 0;
6250 7 : const size_t nDimsMinus1 = nDims - 1;
6251 : GByte abyDstNoData[16];
6252 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6253 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6254 : bufferDataType);
6255 :
6256 37 : lbl_next_depth:
6257 37 : if (dimIdx == nDimsMinus1)
6258 : {
6259 25 : auto nIters = count[dimIdx];
6260 25 : double *padfVal = stack[dimIdx].src_ptr;
6261 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6262 : while (true)
6263 : {
6264 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6265 : {
6266 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6267 88 : if (bDTIsComplex)
6268 : {
6269 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6270 : }
6271 88 : if (bTempBufferNeeded)
6272 : {
6273 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6274 : dst_ptr, bufferDataType);
6275 : }
6276 : }
6277 : else
6278 : {
6279 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6280 : }
6281 :
6282 92 : if ((--nIters) == 0)
6283 25 : break;
6284 67 : padfVal += stack[dimIdx].src_inc_offset;
6285 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6286 : }
6287 : }
6288 : else
6289 : {
6290 12 : stack[dimIdx].nIters = count[dimIdx];
6291 : while (true)
6292 : {
6293 30 : dimIdx++;
6294 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6295 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6296 30 : goto lbl_next_depth;
6297 30 : lbl_return_to_caller:
6298 30 : dimIdx--;
6299 30 : if ((--stack[dimIdx].nIters) == 0)
6300 12 : break;
6301 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6302 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6303 : }
6304 : }
6305 37 : if (dimIdx > 0)
6306 30 : goto lbl_return_to_caller;
6307 :
6308 7 : if (bTempBufferNeeded)
6309 2 : VSIFree(pTempBuffer);
6310 7 : return true;
6311 : }
6312 :
6313 : /************************************************************************/
6314 : /* IWrite() */
6315 : /************************************************************************/
6316 :
6317 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6318 : const size_t *count, const GInt64 *arrayStep,
6319 : const GPtrDiff_t *bufferStride,
6320 : const GDALExtendedDataType &bufferDataType,
6321 : const void *pSrcBuffer)
6322 : {
6323 16 : const double dfScale = m_dfScale;
6324 16 : const double dfOffset = m_dfOffset;
6325 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6326 : const auto dtDouble =
6327 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6328 16 : const size_t nDTSize = dtDouble.GetSize();
6329 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6330 : const bool bSelfAndParentHaveNoData =
6331 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6332 16 : double dfNoData = 0;
6333 16 : if (m_bHasNoData)
6334 : {
6335 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6336 : &dfNoData, GDT_Float64, 0, 1);
6337 : }
6338 :
6339 16 : double adfSrcNoData[2] = {0, 0};
6340 16 : if (bSelfAndParentHaveNoData)
6341 : {
6342 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6343 7 : m_poParent->GetDataType(),
6344 : &adfSrcNoData[0], dtDouble);
6345 : }
6346 :
6347 16 : const auto nDims = GetDimensions().size();
6348 16 : if (nDims == 0)
6349 : {
6350 : double adfVal[2];
6351 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6352 : dtDouble);
6353 16 : if (bSelfAndParentHaveNoData &&
6354 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6355 : {
6356 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6357 2 : bufferStride, m_poParent->GetDataType(),
6358 4 : m_poParent->GetRawNoDataValue());
6359 : }
6360 : else
6361 : {
6362 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6363 8 : if (bDTIsComplex)
6364 : {
6365 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6366 : }
6367 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6368 8 : bufferStride, dtDouble, &adfVal[0]);
6369 : }
6370 : }
6371 :
6372 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6373 6 : size_t nElts = 1;
6374 6 : tmpBufferStrideVector.resize(nDims);
6375 20 : for (size_t i = 0; i < nDims; i++)
6376 14 : nElts *= count[i];
6377 6 : tmpBufferStrideVector.back() = 1;
6378 14 : for (size_t i = nDims - 1; i > 0;)
6379 : {
6380 8 : --i;
6381 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6382 : }
6383 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6384 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6385 6 : if (!pTempBuffer)
6386 0 : return false;
6387 :
6388 : struct Stack
6389 : {
6390 : size_t nIters = 0;
6391 : double *dst_ptr = nullptr;
6392 : const GByte *src_ptr = nullptr;
6393 : GPtrDiff_t src_inc_offset = 0;
6394 : GPtrDiff_t dst_inc_offset = 0;
6395 : };
6396 :
6397 6 : std::vector<Stack> stack(nDims);
6398 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6399 20 : for (size_t i = 0; i < nDims; i++)
6400 : {
6401 28 : stack[i].dst_inc_offset =
6402 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6403 14 : stack[i].src_inc_offset =
6404 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6405 : }
6406 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6407 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6408 :
6409 6 : size_t dimIdx = 0;
6410 6 : const size_t nDimsMinus1 = nDims - 1;
6411 :
6412 34 : lbl_next_depth:
6413 34 : if (dimIdx == nDimsMinus1)
6414 : {
6415 23 : auto nIters = count[dimIdx];
6416 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6417 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6418 : while (true)
6419 : {
6420 : double adfVal[2];
6421 : const double *padfSrcVal;
6422 86 : if (bIsBufferDataTypeNativeDataType)
6423 : {
6424 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6425 : }
6426 : else
6427 : {
6428 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6429 : &adfVal[0], dtDouble);
6430 36 : padfSrcVal = adfVal;
6431 : }
6432 :
6433 148 : if (bSelfAndParentHaveNoData &&
6434 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6435 : {
6436 3 : dst_ptr[0] = adfSrcNoData[0];
6437 3 : if (bDTIsComplex)
6438 : {
6439 1 : dst_ptr[1] = adfSrcNoData[1];
6440 : }
6441 : }
6442 : else
6443 : {
6444 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6445 83 : if (bDTIsComplex)
6446 : {
6447 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6448 : }
6449 : }
6450 :
6451 86 : if ((--nIters) == 0)
6452 23 : break;
6453 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6454 63 : src_ptr += stack[dimIdx].src_inc_offset;
6455 63 : }
6456 : }
6457 : else
6458 : {
6459 11 : stack[dimIdx].nIters = count[dimIdx];
6460 : while (true)
6461 : {
6462 28 : dimIdx++;
6463 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6464 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6465 28 : goto lbl_next_depth;
6466 28 : lbl_return_to_caller:
6467 28 : dimIdx--;
6468 28 : if ((--stack[dimIdx].nIters) == 0)
6469 11 : break;
6470 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6471 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6472 : }
6473 : }
6474 34 : if (dimIdx > 0)
6475 28 : goto lbl_return_to_caller;
6476 :
6477 : // If the parent array is not double/complex-double, then convert the
6478 : // values to it, before calling Write(), as some implementations can be
6479 : // very slow when doing the type conversion.
6480 6 : const auto &eParentDT = m_poParent->GetDataType();
6481 6 : const size_t nParentDTSize = eParentDT.GetSize();
6482 6 : if (nParentDTSize <= nDTSize / 2)
6483 : {
6484 : // Copy in-place by making sure that source and target do not overlap
6485 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6486 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6487 :
6488 : // Copy first element
6489 : {
6490 6 : std::vector<GByte> abyTemp(nParentDTSize);
6491 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6492 6 : static_cast<int>(nDTSize), &abyTemp[0],
6493 : eParentNumericDT, static_cast<int>(nParentDTSize),
6494 : 1);
6495 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6496 : }
6497 : // Remaining elements
6498 86 : for (size_t i = 1; i < nElts; ++i)
6499 : {
6500 80 : GDALCopyWords64(
6501 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6502 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6503 : eParentNumericDT, 0, 1);
6504 : }
6505 : }
6506 :
6507 : const bool ret =
6508 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6509 : eParentDT, pTempBuffer);
6510 :
6511 6 : VSIFree(pTempBuffer);
6512 6 : return ret;
6513 : }
6514 :
6515 : /************************************************************************/
6516 : /* GetUnscaled() */
6517 : /************************************************************************/
6518 :
6519 : /** Return an array that is the unscaled version of the current one.
6520 : *
6521 : * That is each value of the unscaled array will be
6522 : * unscaled_value = raw_value * GetScale() + GetOffset()
6523 : *
6524 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6525 : * from unscaled values to raw values.
6526 : *
6527 : * This is the same as the C function GDALMDArrayGetUnscaled().
6528 : *
6529 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6530 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6531 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6532 : * @return a new array, that holds a reference to the original one, and thus is
6533 : * a view of it (not a copy), or nullptr in case of error.
6534 : */
6535 : std::shared_ptr<GDALMDArray>
6536 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6537 : double dfOverriddenDstNodata) const
6538 : {
6539 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6540 17 : if (!self)
6541 : {
6542 0 : CPLError(CE_Failure, CPLE_AppDefined,
6543 : "Driver implementation issue: m_pSelf not set !");
6544 0 : return nullptr;
6545 : }
6546 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6547 : {
6548 0 : CPLError(CE_Failure, CPLE_AppDefined,
6549 : "GetUnscaled() only supports numeric data type");
6550 0 : return nullptr;
6551 : }
6552 : const double dfScale =
6553 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6554 : const double dfOffset =
6555 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6556 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6557 4 : return self;
6558 :
6559 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6560 13 : ? GDT_CFloat64
6561 13 : : GDT_Float64;
6562 14 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
6563 1 : GetDataType().GetNumericDataType() == GDT_Float32)
6564 1 : eDT = GDT_Float32;
6565 :
6566 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6567 13 : dfOverriddenDstNodata, eDT);
6568 : }
6569 :
6570 : /************************************************************************/
6571 : /* GDALMDArrayMask */
6572 : /************************************************************************/
6573 :
6574 : class GDALMDArrayMask final : public GDALPamMDArray
6575 : {
6576 : private:
6577 : std::shared_ptr<GDALMDArray> m_poParent{};
6578 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6579 : double m_dfMissingValue = 0.0;
6580 : bool m_bHasMissingValue = false;
6581 : double m_dfFillValue = 0.0;
6582 : bool m_bHasFillValue = false;
6583 : double m_dfValidMin = 0.0;
6584 : bool m_bHasValidMin = false;
6585 : double m_dfValidMax = 0.0;
6586 : bool m_bHasValidMax = false;
6587 : std::vector<uint32_t> m_anValidFlagMasks{};
6588 : std::vector<uint32_t> m_anValidFlagValues{};
6589 :
6590 : bool Init(CSLConstList papszOptions);
6591 :
6592 : template <typename Type>
6593 : void
6594 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6595 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6596 : const void *pTempBuffer,
6597 : const GDALExtendedDataType &oTmpBufferDT,
6598 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6599 :
6600 : protected:
6601 45 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6602 90 : : GDALAbstractMDArray(std::string(),
6603 90 : "Mask of " + poParent->GetFullName()),
6604 90 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6605 90 : GDALPamMultiDim::GetPAM(poParent),
6606 : poParent->GetContext()),
6607 225 : m_poParent(std::move(poParent))
6608 : {
6609 45 : }
6610 :
6611 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6612 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6613 : const GDALExtendedDataType &bufferDataType,
6614 : void *pDstBuffer) const override;
6615 :
6616 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6617 : CSLConstList papszOptions) const override
6618 : {
6619 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6620 : }
6621 :
6622 : public:
6623 : static std::shared_ptr<GDALMDArrayMask>
6624 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6625 : CSLConstList papszOptions);
6626 :
6627 1 : bool IsWritable() const override
6628 : {
6629 1 : return false;
6630 : }
6631 :
6632 48 : const std::string &GetFilename() const override
6633 : {
6634 48 : return m_poParent->GetFilename();
6635 : }
6636 :
6637 : const std::vector<std::shared_ptr<GDALDimension>> &
6638 373 : GetDimensions() const override
6639 : {
6640 373 : return m_poParent->GetDimensions();
6641 : }
6642 :
6643 132 : const GDALExtendedDataType &GetDataType() const override
6644 : {
6645 132 : return m_dt;
6646 : }
6647 :
6648 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6649 : {
6650 1 : return m_poParent->GetSpatialRef();
6651 : }
6652 :
6653 2 : std::vector<GUInt64> GetBlockSize() const override
6654 : {
6655 2 : return m_poParent->GetBlockSize();
6656 : }
6657 : };
6658 :
6659 : /************************************************************************/
6660 : /* GDALMDArrayMask::Create() */
6661 : /************************************************************************/
6662 :
6663 : /* static */ std::shared_ptr<GDALMDArrayMask>
6664 45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6665 : CSLConstList papszOptions)
6666 : {
6667 90 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6668 45 : newAr->SetSelf(newAr);
6669 45 : if (!newAr->Init(papszOptions))
6670 6 : return nullptr;
6671 39 : return newAr;
6672 : }
6673 :
6674 : /************************************************************************/
6675 : /* GDALMDArrayMask::Init() */
6676 : /************************************************************************/
6677 :
6678 45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6679 : {
6680 : const auto GetSingleValNumericAttr =
6681 180 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6682 : {
6683 540 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6684 180 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6685 : {
6686 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6687 21 : if (anDimSizes.empty() ||
6688 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6689 : {
6690 11 : bHasVal = true;
6691 11 : dfVal = poAttr->ReadAsDouble();
6692 : }
6693 : }
6694 180 : };
6695 :
6696 45 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6697 45 : m_dfMissingValue);
6698 45 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6699 45 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6700 45 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6701 :
6702 : {
6703 135 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6704 50 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6705 55 : poValidRange->GetDimensionsSize()[0] == 2 &&
6706 5 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6707 : {
6708 5 : m_bHasValidMin = true;
6709 5 : m_bHasValidMax = true;
6710 5 : auto vals = poValidRange->ReadAsDoubleArray();
6711 5 : CPLAssert(vals.size() == 2);
6712 5 : m_dfValidMin = vals[0];
6713 5 : m_dfValidMax = vals[1];
6714 : }
6715 : }
6716 :
6717 : // Take into account
6718 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6719 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6720 : const char *pszUnmaskFlags =
6721 45 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6722 45 : if (pszUnmaskFlags)
6723 : {
6724 : const auto IsScalarStringAttr =
6725 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6726 : {
6727 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6728 26 : (poAttr->GetDimensionsSize().empty() ||
6729 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6730 26 : poAttr->GetDimensionsSize()[0] == 1));
6731 : };
6732 :
6733 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6734 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6735 : {
6736 1 : CPLError(CE_Failure, CPLE_AppDefined,
6737 : "UNMASK_FLAGS option specified but array has no "
6738 : "flag_meanings attribute");
6739 1 : return false;
6740 : }
6741 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6742 13 : if (!pszFlagMeanings)
6743 : {
6744 1 : CPLError(CE_Failure, CPLE_AppDefined,
6745 : "Cannot read flag_meanings attribute");
6746 1 : return false;
6747 : }
6748 :
6749 : const auto IsSingleDimNumericAttr =
6750 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6751 : {
6752 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6753 26 : poAttr->GetDimensionsSize().size() == 1;
6754 : };
6755 :
6756 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6757 : const bool bHasFlagValues =
6758 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6759 :
6760 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6761 : const bool bHasFlagMasks =
6762 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6763 :
6764 12 : if (!bHasFlagValues && !bHasFlagMasks)
6765 : {
6766 1 : CPLError(CE_Failure, CPLE_AppDefined,
6767 : "Cannot find flag_values and/or flag_masks attribute");
6768 1 : return false;
6769 : }
6770 :
6771 : const CPLStringList aosUnmaskFlags(
6772 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6773 : const CPLStringList aosFlagMeanings(
6774 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6775 :
6776 11 : if (bHasFlagValues)
6777 : {
6778 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6779 : // We could support Int64 or UInt64, but more work...
6780 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6781 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6782 : {
6783 0 : CPLError(CE_Failure, CPLE_NotSupported,
6784 : "Unsupported data type for flag_values attribute: %s",
6785 : GDALGetDataTypeName(eType));
6786 0 : return false;
6787 : }
6788 : }
6789 :
6790 11 : if (bHasFlagMasks)
6791 : {
6792 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6793 : // We could support Int64 or UInt64, but more work...
6794 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6795 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6796 : {
6797 0 : CPLError(CE_Failure, CPLE_NotSupported,
6798 : "Unsupported data type for flag_masks attribute: %s",
6799 : GDALGetDataTypeName(eType));
6800 0 : return false;
6801 : }
6802 : }
6803 :
6804 : const std::vector<double> adfValues(
6805 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6806 11 : : std::vector<double>());
6807 : const std::vector<double> adfMasks(
6808 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6809 11 : : std::vector<double>());
6810 :
6811 18 : if (bHasFlagValues &&
6812 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6813 : {
6814 1 : CPLError(CE_Failure, CPLE_AppDefined,
6815 : "Number of values in flag_values attribute is different "
6816 : "from the one in flag_meanings");
6817 1 : return false;
6818 : }
6819 :
6820 16 : if (bHasFlagMasks &&
6821 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6822 : {
6823 1 : CPLError(CE_Failure, CPLE_AppDefined,
6824 : "Number of values in flag_masks attribute is different "
6825 : "from the one in flag_meanings");
6826 1 : return false;
6827 : }
6828 :
6829 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6830 : {
6831 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6832 11 : if (nIdxFlag < 0)
6833 : {
6834 1 : CPLError(
6835 : CE_Failure, CPLE_AppDefined,
6836 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6837 : aosUnmaskFlags[i], pszFlagMeanings);
6838 1 : return false;
6839 : }
6840 :
6841 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6842 : {
6843 0 : CPLError(CE_Failure, CPLE_AppDefined,
6844 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6845 0 : adfValues[nIdxFlag]);
6846 0 : return false;
6847 : }
6848 :
6849 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6850 : {
6851 0 : CPLError(CE_Failure, CPLE_AppDefined,
6852 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6853 0 : adfMasks[nIdxFlag]);
6854 0 : return false;
6855 : }
6856 :
6857 10 : if (bHasFlagValues)
6858 : {
6859 12 : m_anValidFlagValues.push_back(
6860 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6861 : }
6862 :
6863 10 : if (bHasFlagMasks)
6864 : {
6865 12 : m_anValidFlagMasks.push_back(
6866 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6867 : }
6868 : }
6869 : }
6870 :
6871 39 : return true;
6872 : }
6873 :
6874 : /************************************************************************/
6875 : /* IRead() */
6876 : /************************************************************************/
6877 :
6878 48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6879 : const GInt64 *arrayStep,
6880 : const GPtrDiff_t *bufferStride,
6881 : const GDALExtendedDataType &bufferDataType,
6882 : void *pDstBuffer) const
6883 : {
6884 48 : size_t nElts = 1;
6885 48 : const size_t nDims = GetDimensionCount();
6886 96 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6887 132 : for (size_t i = 0; i < nDims; i++)
6888 84 : nElts *= count[i];
6889 48 : if (nDims > 0)
6890 : {
6891 43 : tmpBufferStrideVector.back() = 1;
6892 84 : for (size_t i = nDims - 1; i > 0;)
6893 : {
6894 41 : --i;
6895 41 : tmpBufferStrideVector[i] =
6896 41 : tmpBufferStrideVector[i + 1] * count[i + 1];
6897 : }
6898 : }
6899 :
6900 : /* Optimized case: if we are an integer data type and that there is no */
6901 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
6902 : /* directly */
6903 46 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
6904 70 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
6905 32 : m_anValidFlagMasks.empty() &&
6906 103 : m_poParent->GetRawNoDataValue() == nullptr &&
6907 9 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
6908 : {
6909 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
6910 7 : if (bBufferDataTypeIsByte) // Byte case
6911 : {
6912 4 : bool bContiguous = true;
6913 10 : for (size_t i = 0; i < nDims; i++)
6914 : {
6915 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
6916 : {
6917 1 : bContiguous = false;
6918 1 : break;
6919 : }
6920 : }
6921 4 : if (bContiguous)
6922 : {
6923 : // CPLDebug("GDAL", "GetMask(): contiguous case");
6924 3 : memset(pDstBuffer, 1, nElts);
6925 3 : return true;
6926 : }
6927 : }
6928 :
6929 : struct Stack
6930 : {
6931 : size_t nIters = 0;
6932 : GByte *dst_ptr = nullptr;
6933 : GPtrDiff_t dst_inc_offset = 0;
6934 : };
6935 :
6936 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
6937 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
6938 13 : for (size_t i = 0; i < nDims; i++)
6939 : {
6940 9 : stack[i].dst_inc_offset =
6941 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6942 : }
6943 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6944 :
6945 4 : size_t dimIdx = 0;
6946 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
6947 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
6948 4 : CPLAssert(nBufferDTSize <= 16);
6949 4 : const GByte flag = 1;
6950 : // Coverity misses that m_dt is of type Byte
6951 : // coverity[overrun-buffer-val]
6952 4 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
6953 :
6954 28 : lbl_next_depth:
6955 28 : if (dimIdx == nDimsMinus1)
6956 : {
6957 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
6958 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6959 :
6960 : while (true)
6961 : {
6962 : // cppcheck-suppress knownConditionTrueFalse
6963 73 : if (bBufferDataTypeIsByte)
6964 : {
6965 24 : *dst_ptr = flag;
6966 : }
6967 : else
6968 : {
6969 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
6970 : }
6971 :
6972 73 : if ((--nIters) == 0)
6973 19 : break;
6974 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
6975 : }
6976 : }
6977 : else
6978 : {
6979 9 : stack[dimIdx].nIters = count[dimIdx];
6980 : while (true)
6981 : {
6982 24 : dimIdx++;
6983 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6984 24 : goto lbl_next_depth;
6985 24 : lbl_return_to_caller:
6986 24 : dimIdx--;
6987 24 : if ((--stack[dimIdx].nIters) == 0)
6988 9 : break;
6989 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6990 : }
6991 : }
6992 28 : if (dimIdx > 0)
6993 24 : goto lbl_return_to_caller;
6994 :
6995 4 : return true;
6996 : }
6997 :
6998 : const auto oTmpBufferDT =
6999 41 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7000 : ? GDALExtendedDataType::Create(GDT_Float64)
7001 82 : : m_poParent->GetDataType();
7002 41 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7003 41 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7004 41 : if (!pTempBuffer)
7005 0 : return false;
7006 82 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7007 41 : tmpBufferStrideVector.data(), oTmpBufferDT,
7008 : pTempBuffer))
7009 : {
7010 0 : VSIFree(pTempBuffer);
7011 0 : return false;
7012 : }
7013 :
7014 41 : switch (oTmpBufferDT.GetNumericDataType())
7015 : {
7016 6 : case GDT_Byte:
7017 6 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7018 : pTempBuffer, oTmpBufferDT,
7019 : tmpBufferStrideVector);
7020 6 : break;
7021 :
7022 0 : case GDT_Int8:
7023 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7024 : pTempBuffer, oTmpBufferDT,
7025 : tmpBufferStrideVector);
7026 0 : break;
7027 :
7028 1 : case GDT_UInt16:
7029 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7030 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7031 : tmpBufferStrideVector);
7032 1 : break;
7033 :
7034 14 : case GDT_Int16:
7035 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7036 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7037 : tmpBufferStrideVector);
7038 14 : break;
7039 :
7040 1 : case GDT_UInt32:
7041 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7042 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7043 : tmpBufferStrideVector);
7044 1 : break;
7045 :
7046 5 : case GDT_Int32:
7047 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7048 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7049 : tmpBufferStrideVector);
7050 5 : break;
7051 :
7052 0 : case GDT_UInt64:
7053 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7054 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7055 : tmpBufferStrideVector);
7056 0 : break;
7057 :
7058 0 : case GDT_Int64:
7059 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7060 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7061 : tmpBufferStrideVector);
7062 0 : break;
7063 :
7064 7 : case GDT_Float32:
7065 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7066 : pTempBuffer, oTmpBufferDT,
7067 : tmpBufferStrideVector);
7068 7 : break;
7069 :
7070 7 : case GDT_Float64:
7071 7 : ReadInternal<double>(count, bufferStride, bufferDataType,
7072 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7073 : tmpBufferStrideVector);
7074 7 : break;
7075 0 : case GDT_Unknown:
7076 : case GDT_CInt16:
7077 : case GDT_CInt32:
7078 : case GDT_CFloat32:
7079 : case GDT_CFloat64:
7080 : case GDT_TypeCount:
7081 0 : CPLAssert(false);
7082 : break;
7083 : }
7084 :
7085 41 : VSIFree(pTempBuffer);
7086 :
7087 41 : return true;
7088 : }
7089 :
7090 : /************************************************************************/
7091 : /* IsValidForDT() */
7092 : /************************************************************************/
7093 :
7094 38 : template <typename Type> static bool IsValidForDT(double dfVal)
7095 : {
7096 38 : if (std::isnan(dfVal))
7097 0 : return false;
7098 38 : if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
7099 0 : return false;
7100 38 : if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
7101 0 : return false;
7102 38 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7103 : }
7104 :
7105 9 : template <> bool IsValidForDT<double>(double)
7106 : {
7107 9 : return true;
7108 : }
7109 :
7110 : /************************************************************************/
7111 : /* IsNan() */
7112 : /************************************************************************/
7113 :
7114 1038 : template <typename Type> inline bool IsNan(Type)
7115 : {
7116 1038 : return false;
7117 : }
7118 :
7119 25 : template <> bool IsNan<double>(double val)
7120 : {
7121 25 : return std::isnan(val);
7122 : }
7123 :
7124 26 : template <> bool IsNan<float>(float val)
7125 : {
7126 26 : return std::isnan(val);
7127 : }
7128 :
7129 : /************************************************************************/
7130 : /* ReadInternal() */
7131 : /************************************************************************/
7132 :
7133 : template <typename Type>
7134 41 : void GDALMDArrayMask::ReadInternal(
7135 : const size_t *count, const GPtrDiff_t *bufferStride,
7136 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7137 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7138 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7139 : {
7140 41 : const size_t nDims = GetDimensionCount();
7141 :
7142 205 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7143 : {
7144 205 : if (bHasVal)
7145 : {
7146 47 : if (IsValidForDT<Type>(dfVal))
7147 : {
7148 47 : return static_cast<Type>(dfVal);
7149 : }
7150 : else
7151 : {
7152 0 : bHasVal = false;
7153 : }
7154 : }
7155 158 : return 0;
7156 : };
7157 :
7158 41 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7159 41 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7160 : const Type nNoDataValue =
7161 41 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7162 41 : bool bHasMissingValue = m_bHasMissingValue;
7163 41 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7164 41 : bool bHasFillValue = m_bHasFillValue;
7165 41 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7166 41 : bool bHasValidMin = m_bHasValidMin;
7167 41 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7168 41 : bool bHasValidMax = m_bHasValidMax;
7169 41 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7170 41 : const bool bHasValidFlags =
7171 41 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7172 :
7173 348 : const auto IsValidFlag = [this](Type v)
7174 : {
7175 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7176 : {
7177 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7178 : {
7179 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7180 : m_anValidFlagValues[i])
7181 : {
7182 4 : return true;
7183 : }
7184 : }
7185 : }
7186 42 : else if (!m_anValidFlagValues.empty())
7187 : {
7188 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7189 : {
7190 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7191 : {
7192 4 : return true;
7193 : }
7194 : }
7195 : }
7196 : else /* if( !m_anValidFlagMasks.empty() ) */
7197 : {
7198 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7199 : {
7200 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7201 : {
7202 9 : return true;
7203 : }
7204 : }
7205 : }
7206 37 : return false;
7207 : };
7208 :
7209 : #define GET_MASK_FOR_SAMPLE(v) \
7210 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7211 : !(bHasMissingValue && v == nMissingValue) && \
7212 : !(bHasFillValue && v == nFillValue) && \
7213 : !(bHasValidMin && v < nValidMin) && \
7214 : !(bHasValidMax && v > nValidMax) && \
7215 : (!bHasValidFlags || IsValidFlag(v)));
7216 :
7217 41 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7218 : /* Optimized case: Byte output and output buffer is contiguous */
7219 41 : if (bBufferDataTypeIsByte)
7220 : {
7221 37 : bool bContiguous = true;
7222 96 : for (size_t i = 0; i < nDims; i++)
7223 : {
7224 60 : if (bufferStride[i] != tmpBufferStrideVector[i])
7225 : {
7226 1 : bContiguous = false;
7227 1 : break;
7228 : }
7229 : }
7230 37 : if (bContiguous)
7231 : {
7232 36 : size_t nElts = 1;
7233 95 : for (size_t i = 0; i < nDims; i++)
7234 59 : nElts *= count[i];
7235 :
7236 670 : for (size_t i = 0; i < nElts; i++)
7237 : {
7238 634 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7239 634 : static_cast<GByte *>(pDstBuffer)[i] =
7240 634 : GET_MASK_FOR_SAMPLE(*pSrc);
7241 : }
7242 36 : return;
7243 : }
7244 : }
7245 :
7246 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7247 :
7248 : struct Stack
7249 : {
7250 : size_t nIters = 0;
7251 : const GByte *src_ptr = nullptr;
7252 : GByte *dst_ptr = nullptr;
7253 : GPtrDiff_t src_inc_offset = 0;
7254 : GPtrDiff_t dst_inc_offset = 0;
7255 : };
7256 :
7257 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7258 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7259 15 : for (size_t i = 0; i < nDims; i++)
7260 : {
7261 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7262 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7263 10 : stack[i].dst_inc_offset =
7264 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7265 : }
7266 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7267 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7268 :
7269 5 : size_t dimIdx = 0;
7270 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7271 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7272 5 : CPLAssert(nBufferDTSize <= 16);
7273 15 : for (GByte flag = 0; flag <= 1; flag++)
7274 : {
7275 : // Coverity misses that m_dt is of type Byte
7276 : // coverity[overrun-buffer-val]
7277 10 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
7278 : bufferDataType);
7279 : }
7280 :
7281 43 : lbl_next_depth:
7282 43 : if (dimIdx == nDimsMinus1)
7283 : {
7284 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7285 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7286 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7287 :
7288 420 : while (true)
7289 : {
7290 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7291 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7292 :
7293 455 : if (bBufferDataTypeIsByte)
7294 : {
7295 24 : *dst_ptr = flag;
7296 : }
7297 : else
7298 : {
7299 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7300 : }
7301 :
7302 455 : if ((--nIters) == 0)
7303 35 : break;
7304 420 : src_ptr += stack[dimIdx].src_inc_offset;
7305 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7306 : }
7307 : }
7308 : else
7309 : {
7310 8 : stack[dimIdx].nIters = count[dimIdx];
7311 : while (true)
7312 : {
7313 38 : dimIdx++;
7314 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7315 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7316 38 : goto lbl_next_depth;
7317 38 : lbl_return_to_caller:
7318 38 : dimIdx--;
7319 38 : if ((--stack[dimIdx].nIters) == 0)
7320 8 : break;
7321 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7322 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7323 : }
7324 : }
7325 43 : if (dimIdx > 0)
7326 38 : goto lbl_return_to_caller;
7327 : }
7328 :
7329 : /************************************************************************/
7330 : /* GetMask() */
7331 : /************************************************************************/
7332 :
7333 : /** Return an array that is a mask for the current array
7334 :
7335 : This array will be of type Byte, with values set to 0 to indicate invalid
7336 : pixels of the current array, and values set to 1 to indicate valid pixels.
7337 :
7338 : The generic implementation honours the NoDataValue, as well as various
7339 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7340 : and valid_range.
7341 :
7342 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7343 : can be used to specify strings of the "flag_meanings" attribute
7344 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7345 : for which pixels matching any of those flags will be set at 1 in the mask array,
7346 : and pixels matching none of those flags will be set at 0.
7347 : For example, let's consider the following netCDF variable defined with:
7348 : \verbatim
7349 : l2p_flags:valid_min = 0s ;
7350 : l2p_flags:valid_max = 256s ;
7351 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7352 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7353 : \endverbatim
7354 :
7355 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7356 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7357 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7358 : will be 1.
7359 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7360 : will be 0.
7361 :
7362 : This is the same as the C function GDALMDArrayGetMask().
7363 :
7364 : @param papszOptions NULL-terminated list of options, or NULL.
7365 :
7366 : @return a new array, that holds a reference to the original one, and thus is
7367 : a view of it (not a copy), or nullptr in case of error.
7368 : */
7369 : std::shared_ptr<GDALMDArray>
7370 46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7371 : {
7372 92 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7373 46 : if (!self)
7374 : {
7375 0 : CPLError(CE_Failure, CPLE_AppDefined,
7376 : "Driver implementation issue: m_pSelf not set !");
7377 0 : return nullptr;
7378 : }
7379 46 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7380 : {
7381 1 : CPLError(CE_Failure, CPLE_AppDefined,
7382 : "GetMask() only supports numeric data type");
7383 1 : return nullptr;
7384 : }
7385 45 : return GDALMDArrayMask::Create(self, papszOptions);
7386 : }
7387 :
7388 : /************************************************************************/
7389 : /* IsRegularlySpaced() */
7390 : /************************************************************************/
7391 :
7392 : /** Returns whether an array is a 1D regularly spaced array.
7393 : *
7394 : * @param[out] dfStart First value in the array
7395 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7396 : * @return true if the array is regularly spaced.
7397 : */
7398 181 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7399 : {
7400 181 : dfStart = 0;
7401 181 : dfIncrement = 0;
7402 181 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7403 0 : return false;
7404 181 : const auto nSize = GetDimensions()[0]->GetSize();
7405 181 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7406 2 : return false;
7407 :
7408 179 : size_t nCount = static_cast<size_t>(nSize);
7409 358 : std::vector<double> adfTmp;
7410 : try
7411 : {
7412 179 : adfTmp.resize(nCount);
7413 : }
7414 0 : catch (const std::exception &)
7415 : {
7416 0 : return false;
7417 : }
7418 :
7419 179 : GUInt64 anStart[1] = {0};
7420 179 : size_t anCount[1] = {nCount};
7421 :
7422 : const auto IsRegularlySpacedInternal =
7423 82516 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7424 : {
7425 251 : dfStart = adfTmp[0];
7426 251 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7427 251 : if (dfIncrement == 0)
7428 : {
7429 3 : return false;
7430 : }
7431 20564 : for (size_t i = 1; i < anCount[0]; i++)
7432 : {
7433 20316 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7434 20316 : 1e-3 * fabs(dfIncrement))
7435 : {
7436 0 : return false;
7437 : }
7438 : }
7439 248 : return true;
7440 179 : };
7441 :
7442 : // First try with the first block(s). This can avoid excessive processing
7443 : // time, for example with Zarr datasets.
7444 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7445 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7446 179 : const auto nBlockSize = GetBlockSize()[0];
7447 179 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7448 : {
7449 : size_t nReducedCount =
7450 75 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7451 436 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7452 361 : nReducedCount *= 2;
7453 75 : anCount[0] = nReducedCount;
7454 75 : if (!Read(anStart, anCount, nullptr, nullptr,
7455 150 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7456 : {
7457 0 : return false;
7458 : }
7459 75 : if (!IsRegularlySpacedInternal())
7460 : {
7461 3 : return false;
7462 : }
7463 :
7464 : // Get next values
7465 72 : anStart[0] = nReducedCount;
7466 72 : anCount[0] = nCount - nReducedCount;
7467 : }
7468 :
7469 176 : if (!Read(anStart, anCount, nullptr, nullptr,
7470 352 : GDALExtendedDataType::Create(GDT_Float64),
7471 176 : &adfTmp[static_cast<size_t>(anStart[0])]))
7472 : {
7473 0 : return false;
7474 : }
7475 :
7476 176 : return IsRegularlySpacedInternal();
7477 : }
7478 :
7479 : /************************************************************************/
7480 : /* GuessGeoTransform() */
7481 : /************************************************************************/
7482 :
7483 : /** Returns whether 2 specified dimensions form a geotransform
7484 : *
7485 : * @param nDimX Index of the X axis.
7486 : * @param nDimY Index of the Y axis.
7487 : * @param bPixelIsPoint Whether the geotransform should be returned
7488 : * with the pixel-is-point (pixel-center) convention
7489 : * (bPixelIsPoint = true), or with the pixel-is-area
7490 : * (top left corner convention)
7491 : * (bPixelIsPoint = false)
7492 : * @param[out] adfGeoTransform Computed geotransform
7493 : * @return true if a geotransform could be computed.
7494 : */
7495 214 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7496 : bool bPixelIsPoint,
7497 : double adfGeoTransform[6]) const
7498 : {
7499 214 : const auto &dims(GetDimensions());
7500 428 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7501 428 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7502 214 : double dfXStart = 0.0;
7503 214 : double dfXSpacing = 0.0;
7504 214 : double dfYStart = 0.0;
7505 214 : double dfYSpacing = 0.0;
7506 488 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7507 274 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7508 319 : poVarY && poVarY->GetDimensionCount() == 1 &&
7509 91 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7510 437 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7511 86 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7512 : {
7513 86 : adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7514 86 : adfGeoTransform[1] = dfXSpacing;
7515 86 : adfGeoTransform[2] = 0;
7516 86 : adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7517 86 : adfGeoTransform[4] = 0;
7518 86 : adfGeoTransform[5] = dfYSpacing;
7519 86 : return true;
7520 : }
7521 128 : return false;
7522 : }
7523 :
7524 : /************************************************************************/
7525 : /* GDALMDArrayResampled */
7526 : /************************************************************************/
7527 :
7528 : class GDALMDArrayResampledDataset;
7529 :
7530 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7531 : {
7532 : protected:
7533 : CPLErr IReadBlock(int, int, void *) override;
7534 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7535 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7536 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7537 : GSpacing nLineSpaceBuf,
7538 : GDALRasterIOExtraArg *psExtraArg) override;
7539 :
7540 : public:
7541 : explicit GDALMDArrayResampledDatasetRasterBand(
7542 : GDALMDArrayResampledDataset *poDSIn);
7543 :
7544 : double GetNoDataValue(int *pbHasNoData) override;
7545 : };
7546 :
7547 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7548 : {
7549 : friend class GDALMDArrayResampled;
7550 : friend class GDALMDArrayResampledDatasetRasterBand;
7551 :
7552 : std::shared_ptr<GDALMDArray> m_poArray;
7553 : const size_t m_iXDim;
7554 : const size_t m_iYDim;
7555 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7556 : bool m_bHasGT = false;
7557 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7558 :
7559 : std::vector<GUInt64> m_anOffset{};
7560 : std::vector<size_t> m_anCount{};
7561 : std::vector<GPtrDiff_t> m_anStride{};
7562 :
7563 : std::string m_osFilenameLong{};
7564 : std::string m_osFilenameLat{};
7565 :
7566 : public:
7567 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7568 : size_t iXDim, size_t iYDim)
7569 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7570 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7571 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7572 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7573 : {
7574 24 : const auto &dims(m_poArray->GetDimensions());
7575 :
7576 24 : nRasterYSize = static_cast<int>(
7577 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7578 24 : nRasterXSize = static_cast<int>(
7579 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7580 :
7581 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7582 24 : m_adfGeoTransform);
7583 :
7584 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7585 24 : }
7586 :
7587 48 : ~GDALMDArrayResampledDataset()
7588 24 : {
7589 24 : if (!m_osFilenameLong.empty())
7590 5 : VSIUnlink(m_osFilenameLong.c_str());
7591 24 : if (!m_osFilenameLat.empty())
7592 5 : VSIUnlink(m_osFilenameLat.c_str());
7593 48 : }
7594 :
7595 43 : CPLErr GetGeoTransform(double *padfGeoTransform) override
7596 : {
7597 43 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7598 43 : return m_bHasGT ? CE_None : CE_Failure;
7599 : }
7600 :
7601 105 : const OGRSpatialReference *GetSpatialRef() const override
7602 : {
7603 105 : m_poSRS = m_poArray->GetSpatialRef();
7604 105 : if (m_poSRS)
7605 : {
7606 79 : m_poSRS.reset(m_poSRS->Clone());
7607 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7608 237 : for (auto &m : axisMapping)
7609 : {
7610 158 : if (m == static_cast<int>(m_iXDim) + 1)
7611 79 : m = 1;
7612 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7613 79 : m = 2;
7614 : }
7615 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7616 : }
7617 105 : return m_poSRS.get();
7618 : }
7619 :
7620 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7621 : const std::string &osFilenameLat)
7622 : {
7623 5 : m_osFilenameLong = osFilenameLong;
7624 5 : m_osFilenameLat = osFilenameLat;
7625 10 : CPLStringList aosGeoLoc;
7626 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7627 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7628 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7629 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7630 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7631 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7632 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7633 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7634 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7635 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7636 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7637 5 : }
7638 : };
7639 :
7640 : /************************************************************************/
7641 : /* GDALMDArrayResampledDatasetRasterBand() */
7642 : /************************************************************************/
7643 :
7644 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7645 24 : GDALMDArrayResampledDataset *poDSIn)
7646 : {
7647 24 : const auto &poArray(poDSIn->m_poArray);
7648 24 : const auto blockSize(poArray->GetBlockSize());
7649 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7650 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7651 13 : blockSize[poDSIn->m_iYDim]))
7652 24 : : 1;
7653 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7654 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7655 13 : blockSize[poDSIn->m_iXDim]))
7656 24 : : poDSIn->GetRasterXSize();
7657 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7658 24 : eAccess = poDSIn->eAccess;
7659 24 : }
7660 :
7661 : /************************************************************************/
7662 : /* GetNoDataValue() */
7663 : /************************************************************************/
7664 :
7665 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7666 : {
7667 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7668 50 : const auto &poArray(l_poDS->m_poArray);
7669 50 : bool bHasNodata = false;
7670 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7671 50 : if (pbHasNoData)
7672 46 : *pbHasNoData = bHasNodata;
7673 50 : return dfRes;
7674 : }
7675 :
7676 : /************************************************************************/
7677 : /* IReadBlock() */
7678 : /************************************************************************/
7679 :
7680 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7681 : int nBlockYOff,
7682 : void *pImage)
7683 : {
7684 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7685 0 : const int nXOff = nBlockXOff * nBlockXSize;
7686 0 : const int nYOff = nBlockYOff * nBlockYSize;
7687 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7688 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7689 : GDALRasterIOExtraArg sExtraArg;
7690 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7691 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7692 : nReqXSize, nReqYSize, eDataType, nDTSize,
7693 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7694 : }
7695 :
7696 : /************************************************************************/
7697 : /* IRasterIO() */
7698 : /************************************************************************/
7699 :
7700 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7701 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7702 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7703 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7704 : GDALRasterIOExtraArg *psExtraArg)
7705 : {
7706 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7707 32 : const auto &poArray(l_poDS->m_poArray);
7708 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7709 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7710 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7711 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7712 : {
7713 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7714 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7715 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7716 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7717 :
7718 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7719 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7720 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7721 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7722 :
7723 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7724 32 : l_poDS->m_anCount.data(), nullptr,
7725 32 : l_poDS->m_anStride.data(),
7726 64 : GDALExtendedDataType::Create(eBufType), pData)
7727 32 : ? CE_None
7728 32 : : CE_Failure;
7729 : }
7730 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7731 : pData, nBufXSize, nBufYSize, eBufType,
7732 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7733 : }
7734 :
7735 : class GDALMDArrayResampled final : public GDALPamMDArray
7736 : {
7737 : private:
7738 : std::shared_ptr<GDALMDArray> m_poParent{};
7739 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7740 : std::vector<GUInt64> m_anBlockSize;
7741 : GDALExtendedDataType m_dt;
7742 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7743 : std::shared_ptr<GDALMDArray> m_poVarX{};
7744 : std::shared_ptr<GDALMDArray> m_poVarY{};
7745 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7746 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7747 :
7748 : protected:
7749 21 : GDALMDArrayResampled(
7750 : const std::shared_ptr<GDALMDArray> &poParent,
7751 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7752 : const std::vector<GUInt64> &anBlockSize)
7753 42 : : GDALAbstractMDArray(std::string(),
7754 42 : "Resampled view of " + poParent->GetFullName()),
7755 : GDALPamMDArray(
7756 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7757 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7758 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7759 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7760 : {
7761 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7762 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7763 21 : }
7764 :
7765 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7766 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7767 : const GDALExtendedDataType &bufferDataType,
7768 : void *pDstBuffer) const override;
7769 :
7770 : public:
7771 : static std::shared_ptr<GDALMDArray>
7772 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7773 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7774 : GDALRIOResampleAlg resampleAlg,
7775 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7776 :
7777 42 : ~GDALMDArrayResampled()
7778 21 : {
7779 : // First close the warped VRT
7780 21 : m_poReprojectedDS.reset();
7781 21 : m_poParentDS.reset();
7782 42 : }
7783 :
7784 11 : bool IsWritable() const override
7785 : {
7786 11 : return false;
7787 : }
7788 :
7789 74 : const std::string &GetFilename() const override
7790 : {
7791 74 : return m_poParent->GetFilename();
7792 : }
7793 :
7794 : const std::vector<std::shared_ptr<GDALDimension>> &
7795 257 : GetDimensions() const override
7796 : {
7797 257 : return m_apoDims;
7798 : }
7799 :
7800 109 : const GDALExtendedDataType &GetDataType() const override
7801 : {
7802 109 : return m_dt;
7803 : }
7804 :
7805 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7806 : {
7807 21 : return m_poSRS;
7808 : }
7809 :
7810 12 : std::vector<GUInt64> GetBlockSize() const override
7811 : {
7812 12 : return m_anBlockSize;
7813 : }
7814 :
7815 : std::shared_ptr<GDALAttribute>
7816 1 : GetAttribute(const std::string &osName) const override
7817 : {
7818 1 : return m_poParent->GetAttribute(osName);
7819 : }
7820 :
7821 : std::vector<std::shared_ptr<GDALAttribute>>
7822 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7823 : {
7824 12 : return m_poParent->GetAttributes(papszOptions);
7825 : }
7826 :
7827 1 : const std::string &GetUnit() const override
7828 : {
7829 1 : return m_poParent->GetUnit();
7830 : }
7831 :
7832 1 : const void *GetRawNoDataValue() const override
7833 : {
7834 1 : return m_poParent->GetRawNoDataValue();
7835 : }
7836 :
7837 1 : double GetOffset(bool *pbHasOffset,
7838 : GDALDataType *peStorageType) const override
7839 : {
7840 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7841 : }
7842 :
7843 1 : double GetScale(bool *pbHasScale,
7844 : GDALDataType *peStorageType) const override
7845 : {
7846 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7847 : }
7848 : };
7849 :
7850 : /************************************************************************/
7851 : /* GDALMDArrayResampled::Create() */
7852 : /************************************************************************/
7853 :
7854 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7855 : const std::shared_ptr<GDALMDArray> &poParent,
7856 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7857 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7858 : CSLConstList /* papszOptions */)
7859 : {
7860 29 : const char *pszResampleAlg = "nearest";
7861 29 : bool unsupported = false;
7862 29 : switch (resampleAlg)
7863 : {
7864 16 : case GRIORA_NearestNeighbour:
7865 16 : pszResampleAlg = "nearest";
7866 16 : break;
7867 2 : case GRIORA_Bilinear:
7868 2 : pszResampleAlg = "bilinear";
7869 2 : break;
7870 5 : case GRIORA_Cubic:
7871 5 : pszResampleAlg = "cubic";
7872 5 : break;
7873 1 : case GRIORA_CubicSpline:
7874 1 : pszResampleAlg = "cubicspline";
7875 1 : break;
7876 1 : case GRIORA_Lanczos:
7877 1 : pszResampleAlg = "lanczos";
7878 1 : break;
7879 1 : case GRIORA_Average:
7880 1 : pszResampleAlg = "average";
7881 1 : break;
7882 1 : case GRIORA_Mode:
7883 1 : pszResampleAlg = "mode";
7884 1 : break;
7885 1 : case GRIORA_Gauss:
7886 1 : unsupported = true;
7887 1 : break;
7888 0 : case GRIORA_RESERVED_START:
7889 0 : unsupported = true;
7890 0 : break;
7891 0 : case GRIORA_RESERVED_END:
7892 0 : unsupported = true;
7893 0 : break;
7894 1 : case GRIORA_RMS:
7895 1 : pszResampleAlg = "rms";
7896 1 : break;
7897 : }
7898 29 : if (unsupported)
7899 : {
7900 1 : CPLError(CE_Failure, CPLE_NotSupported,
7901 : "Unsupported resample method for GetResampled()");
7902 1 : return nullptr;
7903 : }
7904 :
7905 28 : if (poParent->GetDimensionCount() < 2)
7906 : {
7907 1 : CPLError(CE_Failure, CPLE_NotSupported,
7908 : "GetResampled() only supports 2 dimensions or more");
7909 1 : return nullptr;
7910 : }
7911 :
7912 27 : const auto &aoParentDims = poParent->GetDimensions();
7913 27 : if (apoNewDimsIn.size() != aoParentDims.size())
7914 : {
7915 2 : CPLError(CE_Failure, CPLE_AppDefined,
7916 : "GetResampled(): apoNewDims size should be the same as "
7917 : "GetDimensionCount()");
7918 2 : return nullptr;
7919 : }
7920 :
7921 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
7922 25 : apoNewDims.reserve(apoNewDimsIn.size());
7923 :
7924 50 : std::vector<GUInt64> anBlockSize;
7925 25 : anBlockSize.reserve(apoNewDimsIn.size());
7926 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
7927 :
7928 50 : auto apoParentDims = poParent->GetDimensions();
7929 : // Special case for NASA EMIT datasets
7930 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
7931 7 : apoParentDims[0]->GetName() == "downtrack" &&
7932 32 : apoParentDims[1]->GetName() == "crosstrack" &&
7933 2 : apoParentDims[2]->GetName() == "bands");
7934 :
7935 : const size_t iYDimParent =
7936 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
7937 : const size_t iXDimParent =
7938 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
7939 :
7940 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
7941 : {
7942 53 : if (i == iYDimParent || i == iXDimParent)
7943 48 : continue;
7944 5 : if (apoNewDimsIn[i] == nullptr)
7945 : {
7946 3 : apoNewDims.emplace_back(aoParentDims[i]);
7947 : }
7948 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
7949 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
7950 : {
7951 1 : CPLError(CE_Failure, CPLE_AppDefined,
7952 : "GetResampled(): apoNewDims[%u] should be the same "
7953 : "as its parent",
7954 : i);
7955 1 : return nullptr;
7956 : }
7957 : else
7958 : {
7959 1 : apoNewDims.emplace_back(aoParentDims[i]);
7960 : }
7961 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
7962 : }
7963 :
7964 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
7965 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
7966 :
7967 24 : double dfXStart = 0.0;
7968 24 : double dfXSpacing = 0.0;
7969 24 : bool gotXSpacing = false;
7970 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
7971 24 : if (poNewDimX)
7972 : {
7973 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
7974 : {
7975 0 : CPLError(CE_Failure, CPLE_NotSupported,
7976 : "Too big size for X dimension");
7977 0 : return nullptr;
7978 : }
7979 4 : auto var = poNewDimX->GetIndexingVariable();
7980 4 : if (var)
7981 : {
7982 2 : if (var->GetDimensionCount() != 1 ||
7983 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
7984 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
7985 : {
7986 0 : CPLError(CE_Failure, CPLE_NotSupported,
7987 : "New X dimension should be indexed by a regularly "
7988 : "spaced variable");
7989 0 : return nullptr;
7990 : }
7991 1 : gotXSpacing = true;
7992 : }
7993 : }
7994 :
7995 24 : double dfYStart = 0.0;
7996 24 : double dfYSpacing = 0.0;
7997 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
7998 24 : bool gotYSpacing = false;
7999 24 : if (poNewDimY)
8000 : {
8001 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8002 : {
8003 0 : CPLError(CE_Failure, CPLE_NotSupported,
8004 : "Too big size for Y dimension");
8005 0 : return nullptr;
8006 : }
8007 4 : auto var = poNewDimY->GetIndexingVariable();
8008 4 : if (var)
8009 : {
8010 2 : if (var->GetDimensionCount() != 1 ||
8011 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8012 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8013 : {
8014 0 : CPLError(CE_Failure, CPLE_NotSupported,
8015 : "New Y dimension should be indexed by a regularly "
8016 : "spaced variable");
8017 0 : return nullptr;
8018 : }
8019 1 : gotYSpacing = true;
8020 : }
8021 : }
8022 :
8023 : // This limitation could probably be removed
8024 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8025 : {
8026 0 : CPLError(CE_Failure, CPLE_NotSupported,
8027 : "Either none of new X or Y dimension should have an indexing "
8028 : "variable, or both should both should have one.");
8029 0 : return nullptr;
8030 : }
8031 :
8032 48 : std::string osDstWKT;
8033 24 : if (poTargetSRS)
8034 : {
8035 2 : char *pszDstWKT = nullptr;
8036 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8037 : {
8038 0 : CPLFree(pszDstWKT);
8039 0 : return nullptr;
8040 : }
8041 2 : osDstWKT = pszDstWKT;
8042 2 : CPLFree(pszDstWKT);
8043 : }
8044 :
8045 : // Use coordinate variables for geolocation array
8046 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8047 24 : bool useGeolocationArray = false;
8048 24 : if (apoCoordinateVars.size() >= 2)
8049 : {
8050 0 : std::shared_ptr<GDALMDArray> poLongVar;
8051 0 : std::shared_ptr<GDALMDArray> poLatVar;
8052 15 : for (const auto &poCoordVar : apoCoordinateVars)
8053 : {
8054 10 : const auto &osName = poCoordVar->GetName();
8055 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8056 20 : std::string osStandardName;
8057 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8058 2 : poAttr->GetDimensionCount() == 0)
8059 : {
8060 2 : const char *pszStandardName = poAttr->ReadAsString();
8061 2 : if (pszStandardName)
8062 2 : osStandardName = pszStandardName;
8063 : }
8064 21 : if (osName == "lon" || osName == "longitude" ||
8065 21 : osName == "Longitude" || osStandardName == "longitude")
8066 : {
8067 5 : poLongVar = poCoordVar;
8068 : }
8069 6 : else if (osName == "lat" || osName == "latitude" ||
8070 6 : osName == "Latitude" || osStandardName == "latitude")
8071 : {
8072 5 : poLatVar = poCoordVar;
8073 : }
8074 : }
8075 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8076 : {
8077 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8078 5 : const auto &longDims = poLongVar->GetDimensions();
8079 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8080 5 : const auto &latDims = poLatVar->GetDimensions();
8081 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8082 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8083 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8084 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8085 : {
8086 : // Geolocation arrays are 1D, and of consistent size with
8087 : // the variable
8088 0 : useGeolocationArray = true;
8089 : }
8090 1 : else if ((longDimCount == 2 ||
8091 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8092 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8093 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8094 1 : (latDimCount == 2 ||
8095 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8096 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8097 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8098 :
8099 : {
8100 : // Geolocation arrays are 2D (or 3D with first dimension of
8101 : // size 1, as found in Sentinel 5P products), and of consistent
8102 : // size with the variable
8103 5 : useGeolocationArray = true;
8104 : }
8105 : else
8106 : {
8107 0 : CPLDebug(
8108 : "GDAL",
8109 : "Longitude and latitude coordinate variables found, "
8110 : "but their characteristics are not compatible of using "
8111 : "them as geolocation arrays");
8112 : }
8113 5 : if (useGeolocationArray)
8114 : {
8115 10 : CPLDebug("GDAL",
8116 : "Setting geolocation array from variables %s and %s",
8117 5 : poLongVar->GetName().c_str(),
8118 5 : poLatVar->GetName().c_str());
8119 : const std::string osFilenameLong =
8120 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8121 : const std::string osFilenameLat =
8122 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8123 : std::unique_ptr<GDALDataset> poTmpLongDS(
8124 : longDimCount == 1
8125 0 : ? poLongVar->AsClassicDataset(0, 0)
8126 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8127 15 : longDimCount - 2));
8128 5 : auto hTIFFLongDS = GDALTranslate(
8129 : osFilenameLong.c_str(),
8130 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8131 : std::unique_ptr<GDALDataset> poTmpLatDS(
8132 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8133 20 : : poLatVar->AsClassicDataset(
8134 15 : latDimCount - 1, latDimCount - 2));
8135 5 : auto hTIFFLatDS = GDALTranslate(
8136 : osFilenameLat.c_str(),
8137 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8138 5 : const bool bError =
8139 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8140 5 : GDALClose(hTIFFLongDS);
8141 5 : GDALClose(hTIFFLatDS);
8142 5 : if (bError)
8143 : {
8144 0 : VSIUnlink(osFilenameLong.c_str());
8145 0 : VSIUnlink(osFilenameLat.c_str());
8146 0 : return nullptr;
8147 : }
8148 :
8149 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8150 : }
8151 : }
8152 : else
8153 : {
8154 0 : CPLDebug("GDAL",
8155 : "Coordinate variables available for %s, but "
8156 : "longitude and/or latitude variables were not identified",
8157 0 : poParent->GetName().c_str());
8158 : }
8159 : }
8160 :
8161 : // Build gdalwarp arguments
8162 48 : CPLStringList aosArgv;
8163 :
8164 24 : aosArgv.AddString("-of");
8165 24 : aosArgv.AddString("VRT");
8166 :
8167 24 : aosArgv.AddString("-r");
8168 24 : aosArgv.AddString(pszResampleAlg);
8169 :
8170 24 : if (!osDstWKT.empty())
8171 : {
8172 2 : aosArgv.AddString("-t_srs");
8173 2 : aosArgv.AddString(osDstWKT.c_str());
8174 : }
8175 :
8176 24 : if (useGeolocationArray)
8177 5 : aosArgv.AddString("-geoloc");
8178 :
8179 24 : if (gotXSpacing && gotYSpacing)
8180 : {
8181 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8182 : const double dfXMax =
8183 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8184 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8185 : const double dfYMin =
8186 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8187 1 : aosArgv.AddString("-te");
8188 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8189 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8190 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8191 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8192 : }
8193 :
8194 24 : if (poNewDimX && poNewDimY)
8195 : {
8196 3 : aosArgv.AddString("-ts");
8197 : aosArgv.AddString(
8198 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8199 : aosArgv.AddString(
8200 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8201 : }
8202 21 : else if (poNewDimX)
8203 : {
8204 1 : aosArgv.AddString("-ts");
8205 : aosArgv.AddString(
8206 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8207 1 : aosArgv.AddString("0");
8208 : }
8209 20 : else if (poNewDimY)
8210 : {
8211 1 : aosArgv.AddString("-ts");
8212 1 : aosArgv.AddString("0");
8213 : aosArgv.AddString(
8214 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8215 : }
8216 :
8217 : // Create a warped VRT dataset
8218 : GDALWarpAppOptions *psOptions =
8219 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8220 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8221 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8222 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8223 24 : GDALWarpAppOptionsFree(psOptions);
8224 24 : if (poReprojectedDS == nullptr)
8225 3 : return nullptr;
8226 :
8227 : int nBlockXSize;
8228 : int nBlockYSize;
8229 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8230 21 : anBlockSize.emplace_back(nBlockYSize);
8231 21 : anBlockSize.emplace_back(nBlockXSize);
8232 :
8233 21 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8234 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8235 21 : CPLAssert(eErr == CE_None);
8236 21 : CPL_IGNORE_RET_VAL(eErr);
8237 :
8238 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8239 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8240 42 : poReprojectedDS->GetRasterYSize());
8241 : auto varY = GDALMDArrayRegularlySpaced::Create(
8242 63 : std::string(), poDimY->GetName(), poDimY,
8243 84 : adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8244 21 : poDimY->SetIndexingVariable(varY);
8245 :
8246 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8247 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8248 42 : poReprojectedDS->GetRasterXSize());
8249 : auto varX = GDALMDArrayRegularlySpaced::Create(
8250 63 : std::string(), poDimX->GetName(), poDimX,
8251 84 : adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8252 21 : poDimX->SetIndexingVariable(varX);
8253 :
8254 21 : apoNewDims.emplace_back(poDimY);
8255 21 : apoNewDims.emplace_back(poDimX);
8256 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8257 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8258 21 : newAr->SetSelf(newAr);
8259 21 : if (poTargetSRS)
8260 : {
8261 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8262 : }
8263 : else
8264 : {
8265 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8266 : }
8267 21 : newAr->m_poVarX = varX;
8268 21 : newAr->m_poVarY = varY;
8269 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8270 21 : newAr->m_poParentDS = std::move(poParentDS);
8271 :
8272 : // If the input array is y,x,band ordered, the above newAr is
8273 : // actually band,y,x ordered as it is more convenient for
8274 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8275 : // array to the order of the input array
8276 21 : if (bYXBandOrder)
8277 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8278 :
8279 19 : return newAr;
8280 : }
8281 :
8282 : /************************************************************************/
8283 : /* GDALMDArrayResampled::IRead() */
8284 : /************************************************************************/
8285 :
8286 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8287 : const size_t *count, const GInt64 *arrayStep,
8288 : const GPtrDiff_t *bufferStride,
8289 : const GDALExtendedDataType &bufferDataType,
8290 : void *pDstBuffer) const
8291 : {
8292 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8293 0 : return false;
8294 :
8295 : struct Stack
8296 : {
8297 : size_t nIters = 0;
8298 : GByte *dst_ptr = nullptr;
8299 : GPtrDiff_t dst_inc_offset = 0;
8300 : };
8301 :
8302 29 : const auto nDims = GetDimensionCount();
8303 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8304 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8305 92 : for (size_t i = 0; i < nDims; i++)
8306 : {
8307 63 : stack[i].dst_inc_offset =
8308 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8309 : }
8310 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8311 :
8312 29 : size_t dimIdx = 0;
8313 29 : const size_t iDimY = nDims - 2;
8314 29 : const size_t iDimX = nDims - 1;
8315 : // Use an array to avoid a false positive warning from CLang Static
8316 : // Analyzer about flushCaches being never read
8317 29 : bool flushCaches[] = {false};
8318 : const bool bYXBandOrder =
8319 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8320 :
8321 38 : lbl_next_depth:
8322 38 : if (dimIdx == iDimY)
8323 : {
8324 33 : if (flushCaches[0])
8325 : {
8326 5 : flushCaches[0] = false;
8327 : // When changing of 2D slice, flush GDAL 2D buffers
8328 5 : m_poParentDS->FlushCache(false);
8329 5 : m_poReprojectedDS->FlushCache(false);
8330 : }
8331 :
8332 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8333 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8334 : arrayStep, bufferStride, bufferDataType,
8335 33 : stack[dimIdx].dst_ptr))
8336 : {
8337 0 : return false;
8338 : }
8339 : }
8340 : else
8341 : {
8342 5 : stack[dimIdx].nIters = count[dimIdx];
8343 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8344 5 : arrayStartIdx[dimIdx])
8345 : {
8346 1 : flushCaches[0] = true;
8347 : }
8348 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8349 5 : arrayStartIdx[dimIdx];
8350 : while (true)
8351 : {
8352 9 : dimIdx++;
8353 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8354 9 : goto lbl_next_depth;
8355 9 : lbl_return_to_caller:
8356 9 : dimIdx--;
8357 9 : if ((--stack[dimIdx].nIters) == 0)
8358 5 : break;
8359 4 : flushCaches[0] = true;
8360 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8361 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8362 : }
8363 : }
8364 38 : if (dimIdx > 0)
8365 9 : goto lbl_return_to_caller;
8366 :
8367 29 : return true;
8368 : }
8369 :
8370 : /************************************************************************/
8371 : /* GetResampled() */
8372 : /************************************************************************/
8373 :
8374 : /** Return an array that is a resampled / reprojected view of the current array
8375 : *
8376 : * This is the same as the C function GDALMDArrayGetResampled().
8377 : *
8378 : * Currently this method can only resample along the last 2 dimensions, unless
8379 : * orthorectifying a NASA EMIT dataset.
8380 : *
8381 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8382 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8383 : *
8384 : * Options available are:
8385 : * <ul>
8386 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8387 : * Can be set to NO to use generic reprojection method.
8388 : * </li>
8389 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8390 : * orthorectification to take into account the value of the
8391 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8392 : * current array along the band dimension are valid.</li>
8393 : * </ul>
8394 : *
8395 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8396 : * apoNewDims[i] can be NULL to let the method automatically
8397 : * determine it.
8398 : * @param resampleAlg Resampling algorithm
8399 : * @param poTargetSRS Target SRS, or nullptr
8400 : * @param papszOptions NULL-terminated list of options, or NULL.
8401 : *
8402 : * @return a new array, that holds a reference to the original one, and thus is
8403 : * a view of it (not a copy), or nullptr in case of error.
8404 : *
8405 : * @since 3.4
8406 : */
8407 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8408 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8409 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8410 : CSLConstList papszOptions) const
8411 : {
8412 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8413 38 : if (!self)
8414 : {
8415 0 : CPLError(CE_Failure, CPLE_AppDefined,
8416 : "Driver implementation issue: m_pSelf not set !");
8417 0 : return nullptr;
8418 : }
8419 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8420 : {
8421 0 : CPLError(CE_Failure, CPLE_AppDefined,
8422 : "GetResampled() only supports numeric data type");
8423 0 : return nullptr;
8424 : }
8425 :
8426 : // Special case for NASA EMIT datasets
8427 76 : auto apoDims = GetDimensions();
8428 36 : if (poTargetSRS == nullptr &&
8429 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8430 20 : apoDims[1]->GetName() == "crosstrack" &&
8431 10 : apoDims[2]->GetName() == "bands" &&
8432 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8433 1 : apoNewDims ==
8434 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8435 30 : apoDims[2]})) ||
8436 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8437 3 : apoDims[1]->GetName() == "crosstrack" &&
8438 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8439 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8440 : "EMIT_ORTHORECTIFICATION", "YES")))
8441 : {
8442 9 : auto poRootGroup = GetRootGroup();
8443 9 : if (poRootGroup)
8444 : {
8445 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8446 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8447 9 : if (poAttrGeotransform &&
8448 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8449 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8450 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8451 9 : poLocationGroup)
8452 : {
8453 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8454 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8455 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8456 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8457 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8458 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8459 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8460 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8461 : {
8462 : return CreateGLTOrthorectified(
8463 : self, poRootGroup, poGLT_X, poGLT_Y,
8464 : /* nGLTIndexOffset = */ -1,
8465 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8466 : }
8467 : }
8468 : }
8469 : }
8470 :
8471 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8472 : "EMIT_ORTHORECTIFICATION", "NO")))
8473 : {
8474 0 : CPLError(CE_Failure, CPLE_AppDefined,
8475 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8476 : "parameters are not compatible with it");
8477 0 : return nullptr;
8478 : }
8479 :
8480 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8481 29 : poTargetSRS, papszOptions);
8482 : }
8483 :
8484 : /************************************************************************/
8485 : /* GDALDatasetFromArray() */
8486 : /************************************************************************/
8487 :
8488 : class GDALDatasetFromArray;
8489 :
8490 : namespace
8491 : {
8492 : struct MetadataItem
8493 : {
8494 : std::shared_ptr<GDALMDArray> poArray{};
8495 : std::string osName{};
8496 : std::string osDefinition{};
8497 : bool bDefinitionUsesPctForG = false;
8498 : };
8499 :
8500 : struct BandImageryMetadata
8501 : {
8502 : std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
8503 : double dfCentralWavelengthToMicrometer = 1.0;
8504 : std::shared_ptr<GDALMDArray> poFWHMArray{};
8505 : double dfFWHMToMicrometer = 1.0;
8506 : };
8507 :
8508 : } // namespace
8509 :
8510 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8511 : {
8512 : std::vector<GUInt64> m_anOffset{};
8513 : std::vector<size_t> m_anCount{};
8514 : std::vector<GPtrDiff_t> m_anStride{};
8515 :
8516 : protected:
8517 : CPLErr IReadBlock(int, int, void *) override;
8518 : CPLErr IWriteBlock(int, int, void *) override;
8519 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8520 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8521 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8522 : GSpacing nLineSpaceBuf,
8523 : GDALRasterIOExtraArg *psExtraArg) override;
8524 :
8525 : public:
8526 : explicit GDALRasterBandFromArray(
8527 : GDALDatasetFromArray *poDSIn,
8528 : const std::vector<GUInt64> &anOtherDimCoord,
8529 : const std::vector<std::vector<MetadataItem>>
8530 : &aoBandParameterMetadataItems,
8531 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8532 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8533 :
8534 : double GetNoDataValue(int *pbHasNoData) override;
8535 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8536 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8537 : double GetOffset(int *pbHasOffset) override;
8538 : double GetScale(int *pbHasScale) override;
8539 : const char *GetUnitType() override;
8540 : GDALColorInterp GetColorInterpretation() override;
8541 : };
8542 :
8543 : class GDALDatasetFromArray final : public GDALPamDataset
8544 : {
8545 : friend class GDALRasterBandFromArray;
8546 :
8547 : std::shared_ptr<GDALMDArray> m_poArray;
8548 : size_t m_iXDim;
8549 : size_t m_iYDim;
8550 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8551 : bool m_bHasGT = false;
8552 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8553 : GDALMultiDomainMetadata m_oMDD{};
8554 : std::string m_osOvrFilename{};
8555 :
8556 : public:
8557 190 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8558 : size_t iXDim, size_t iYDim)
8559 190 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8560 : {
8561 : // Initialize an overview filename from the filename of the array
8562 : // and its name.
8563 190 : const std::string &osFilename = m_poArray->GetFilename();
8564 190 : if (!osFilename.empty())
8565 : {
8566 169 : m_osOvrFilename = osFilename;
8567 169 : m_osOvrFilename += '.';
8568 6346 : for (char ch : m_poArray->GetName())
8569 : {
8570 6177 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8571 5478 : (ch >= 'a' && ch <= 'z') || ch == '_')
8572 : {
8573 4940 : m_osOvrFilename += ch;
8574 : }
8575 : else
8576 : {
8577 1237 : m_osOvrFilename += '_';
8578 : }
8579 : }
8580 169 : m_osOvrFilename += ".ovr";
8581 169 : oOvManager.Initialize(this);
8582 : }
8583 190 : }
8584 :
8585 : static GDALDatasetFromArray *
8586 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8587 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8588 : CSLConstList papszOptions);
8589 :
8590 380 : ~GDALDatasetFromArray()
8591 190 : {
8592 190 : GDALDatasetFromArray::Close();
8593 380 : }
8594 :
8595 312 : CPLErr Close() override
8596 : {
8597 312 : CPLErr eErr = CE_None;
8598 312 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8599 : {
8600 312 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8601 : CE_None)
8602 0 : eErr = CE_Failure;
8603 312 : m_poArray.reset();
8604 : }
8605 312 : return eErr;
8606 : }
8607 :
8608 49 : CPLErr GetGeoTransform(double *padfGeoTransform) override
8609 : {
8610 49 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8611 49 : return m_bHasGT ? CE_None : CE_Failure;
8612 : }
8613 :
8614 57 : const OGRSpatialReference *GetSpatialRef() const override
8615 : {
8616 57 : if (m_poArray->GetDimensionCount() < 2)
8617 3 : return nullptr;
8618 54 : m_poSRS = m_poArray->GetSpatialRef();
8619 54 : if (m_poSRS)
8620 : {
8621 20 : m_poSRS.reset(m_poSRS->Clone());
8622 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8623 60 : for (auto &m : axisMapping)
8624 : {
8625 40 : if (m == static_cast<int>(m_iXDim) + 1)
8626 20 : m = 1;
8627 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8628 20 : m = 2;
8629 : }
8630 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8631 : }
8632 54 : return m_poSRS.get();
8633 : }
8634 :
8635 4 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8636 : {
8637 4 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8638 : }
8639 :
8640 144 : char **GetMetadata(const char *pszDomain) override
8641 : {
8642 144 : return m_oMDD.GetMetadata(pszDomain);
8643 : }
8644 :
8645 204 : const char *GetMetadataItem(const char *pszName,
8646 : const char *pszDomain) override
8647 : {
8648 378 : if (!m_osOvrFilename.empty() && pszName &&
8649 390 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8650 12 : EQUAL(pszDomain, "OVERVIEWS"))
8651 : {
8652 12 : return m_osOvrFilename.c_str();
8653 : }
8654 192 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8655 : }
8656 : };
8657 :
8658 : /************************************************************************/
8659 : /* GDALRasterBandFromArray() */
8660 : /************************************************************************/
8661 :
8662 252 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8663 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8664 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8665 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8666 252 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8667 : {
8668 252 : const auto &poArray(poDSIn->m_poArray);
8669 252 : const auto &dims(poArray->GetDimensions());
8670 252 : const auto nDimCount(dims.size());
8671 504 : const auto blockSize(poArray->GetBlockSize());
8672 241 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8673 493 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8674 132 : blockSize[poDSIn->m_iYDim]))
8675 : : 1;
8676 252 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8677 143 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8678 143 : blockSize[poDSIn->m_iXDim]))
8679 252 : : poDSIn->GetRasterXSize();
8680 252 : eDataType = poArray->GetDataType().GetNumericDataType();
8681 252 : eAccess = poDSIn->eAccess;
8682 252 : m_anOffset.resize(nDimCount);
8683 252 : m_anCount.resize(nDimCount, 1);
8684 252 : m_anStride.resize(nDimCount);
8685 853 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8686 : {
8687 601 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8688 : {
8689 216 : std::string dimName(dims[i]->GetName());
8690 108 : GUInt64 nIndex = anOtherDimCoord[j];
8691 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8692 : // subsetted dimensions as generated by GetView()
8693 108 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8694 : {
8695 : CPLStringList aosTokens(
8696 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8697 6 : if (aosTokens.size() == 5)
8698 : {
8699 6 : dimName = aosTokens[1];
8700 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8701 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8702 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8703 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8704 0 : : nStartDim - (nIndex * -nIncrDim);
8705 : }
8706 : }
8707 108 : if (nDimCount != 3 || dimName != "Band")
8708 : {
8709 52 : SetMetadataItem(
8710 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8711 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8712 : }
8713 :
8714 108 : auto indexingVar = dims[i]->GetIndexingVariable();
8715 :
8716 : // If the indexing variable is also listed in band parameter arrays,
8717 : // then don't use our default formatting
8718 108 : if (indexingVar)
8719 : {
8720 38 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8721 : {
8722 12 : if (oItem.poArray->GetFullName() ==
8723 12 : indexingVar->GetFullName())
8724 : {
8725 12 : indexingVar.reset();
8726 12 : break;
8727 : }
8728 : }
8729 : }
8730 :
8731 134 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8732 26 : indexingVar->GetDimensions()[0]->GetSize() ==
8733 26 : dims[i]->GetSize())
8734 : {
8735 26 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8736 : {
8737 0 : if (!bHasWarned)
8738 : {
8739 0 : CPLError(
8740 : CE_Warning, CPLE_AppDefined,
8741 : "Maximum delay to load band metadata from "
8742 : "dimension indexing variables has expired. "
8743 : "Increase the value of the "
8744 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8745 : "option of GDALMDArray::AsClassicDataset() "
8746 : "(also accessible as the "
8747 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8748 : "configuration option), "
8749 : "or set it to 'unlimited' for unlimited delay. ");
8750 0 : bHasWarned = true;
8751 : }
8752 : }
8753 : else
8754 : {
8755 26 : size_t nCount = 1;
8756 26 : const auto &dt(indexingVar->GetDataType());
8757 52 : std::vector<GByte> abyTmp(dt.GetSize());
8758 52 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8759 26 : nullptr, nullptr, dt, &abyTmp[0]))
8760 : {
8761 26 : char *pszTmp = nullptr;
8762 26 : GDALExtendedDataType::CopyValue(
8763 26 : &abyTmp[0], dt, &pszTmp,
8764 52 : GDALExtendedDataType::CreateString());
8765 26 : if (pszTmp)
8766 : {
8767 26 : SetMetadataItem(
8768 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8769 : pszTmp);
8770 26 : CPLFree(pszTmp);
8771 : }
8772 :
8773 26 : const auto &unit(indexingVar->GetUnit());
8774 26 : if (!unit.empty())
8775 : {
8776 12 : SetMetadataItem(
8777 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8778 : unit.c_str());
8779 : }
8780 : }
8781 : }
8782 : }
8783 :
8784 124 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8785 : {
8786 32 : CPLString osVal;
8787 :
8788 16 : size_t nCount = 1;
8789 16 : const auto &dt(oItem.poArray->GetDataType());
8790 16 : if (oItem.bDefinitionUsesPctForG)
8791 : {
8792 : // There is one and only one %[x][.y]f|g in osDefinition
8793 12 : std::vector<GByte> abyTmp(dt.GetSize());
8794 12 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8795 6 : nullptr, nullptr, dt, &abyTmp[0]))
8796 : {
8797 6 : double dfVal = 0;
8798 6 : GDALExtendedDataType::CopyValue(
8799 6 : &abyTmp[0], dt, &dfVal,
8800 12 : GDALExtendedDataType::Create(GDT_Float64));
8801 6 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8802 : }
8803 : }
8804 : else
8805 : {
8806 : // There should be zero or one %s in osDefinition
8807 10 : char *pszValue = nullptr;
8808 10 : if (dt.GetClass() == GEDTC_STRING)
8809 : {
8810 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8811 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8812 : dt, &pszValue));
8813 : }
8814 : else
8815 : {
8816 16 : std::vector<GByte> abyTmp(dt.GetSize());
8817 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8818 : nullptr, nullptr, dt,
8819 8 : &abyTmp[0]))
8820 : {
8821 8 : GDALExtendedDataType::CopyValue(
8822 8 : &abyTmp[0], dt, &pszValue,
8823 16 : GDALExtendedDataType::CreateString());
8824 : }
8825 : }
8826 :
8827 10 : if (pszValue)
8828 : {
8829 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8830 10 : CPLFree(pszValue);
8831 : }
8832 : }
8833 16 : if (!osVal.empty())
8834 16 : SetMetadataItem(oItem.osName.c_str(), osVal);
8835 : }
8836 :
8837 108 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8838 : {
8839 : auto &poCentralWavelengthArray =
8840 2 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8841 2 : size_t nCount = 1;
8842 2 : const auto &dt(poCentralWavelengthArray->GetDataType());
8843 4 : std::vector<GByte> abyTmp(dt.GetSize());
8844 4 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8845 : &nCount, nullptr, nullptr,
8846 2 : dt, &abyTmp[0]))
8847 : {
8848 2 : double dfVal = 0;
8849 2 : GDALExtendedDataType::CopyValue(
8850 2 : &abyTmp[0], dt, &dfVal,
8851 4 : GDALExtendedDataType::Create(GDT_Float64));
8852 2 : SetMetadataItem(
8853 : "CENTRAL_WAVELENGTH_UM",
8854 : CPLSPrintf(
8855 2 : "%g", dfVal * aoBandImageryMetadata[j]
8856 2 : .dfCentralWavelengthToMicrometer),
8857 : "IMAGERY");
8858 : }
8859 : }
8860 :
8861 108 : if (aoBandImageryMetadata[j].poFWHMArray)
8862 : {
8863 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
8864 2 : size_t nCount = 1;
8865 2 : const auto &dt(poFWHMArray->GetDataType());
8866 4 : std::vector<GByte> abyTmp(dt.GetSize());
8867 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
8868 2 : nullptr, dt, &abyTmp[0]))
8869 : {
8870 2 : double dfVal = 0;
8871 2 : GDALExtendedDataType::CopyValue(
8872 2 : &abyTmp[0], dt, &dfVal,
8873 4 : GDALExtendedDataType::Create(GDT_Float64));
8874 2 : SetMetadataItem(
8875 : "FWHM_UM",
8876 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
8877 2 : .dfFWHMToMicrometer),
8878 : "IMAGERY");
8879 : }
8880 : }
8881 :
8882 108 : m_anOffset[i] = anOtherDimCoord[j];
8883 108 : j++;
8884 : }
8885 : }
8886 252 : }
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 320 : 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 320 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9025 320 : const auto &poArray(l_poDS->m_poArray);
9026 320 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9027 320 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9028 320 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9029 320 : (nLineSpaceBuf % nBufferDTSize) == 0)
9030 : {
9031 320 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9032 320 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9033 640 : m_anStride[l_poDS->m_iXDim] =
9034 320 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9035 320 : if (poArray->GetDimensionCount() >= 2)
9036 : {
9037 311 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9038 311 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9039 311 : m_anStride[l_poDS->m_iYDim] =
9040 311 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9041 : }
9042 320 : if (eRWFlag == GF_Read)
9043 : {
9044 632 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9045 316 : m_anStride.data(),
9046 632 : GDALExtendedDataType::Create(eBufType), pData)
9047 316 : ? CE_None
9048 316 : : 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 223 : 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 223 : const auto nDimCount(array->GetDimensionCount());
9129 223 : if (nDimCount == 0)
9130 : {
9131 1 : CPLError(CE_Failure, CPLE_NotSupported,
9132 : "Unsupported number of dimensions");
9133 1 : return nullptr;
9134 : }
9135 443 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9136 221 : 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 221 : if (iXDim >= nDimCount ||
9144 207 : (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 215 : GUInt64 nTotalBands = 1;
9150 215 : const auto &dims(array->GetDimensions());
9151 697 : for (size_t i = 0; i < nDimCount; ++i)
9152 : {
9153 483 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9154 : {
9155 66 : 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 65 : nTotalBands *= dims[i]->GetSize();
9162 : }
9163 : }
9164 :
9165 428 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9166 696 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9167 : {
9168 482 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9169 : {
9170 65 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9171 65 : ++j;
9172 : }
9173 : }
9174 :
9175 214 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9176 :
9177 : const char *pszBandMetadata =
9178 214 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9179 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9180 428 : nNewDimCount);
9181 214 : if (pszBandMetadata)
9182 : {
9183 21 : if (!poRootGroup)
9184 : {
9185 1 : CPLError(CE_Failure, CPLE_AppDefined,
9186 : "Root group should be provided when BAND_METADATA is set");
9187 14 : return nullptr;
9188 : }
9189 20 : CPLJSONDocument oDoc;
9190 20 : if (!oDoc.LoadMemory(pszBandMetadata))
9191 : {
9192 1 : CPLError(CE_Failure, CPLE_AppDefined,
9193 : "Invalid JSON content for BAND_METADATA");
9194 1 : return nullptr;
9195 : }
9196 19 : auto oRoot = oDoc.GetRoot();
9197 19 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9198 : {
9199 1 : CPLError(CE_Failure, CPLE_AppDefined,
9200 : "Value of BAND_METADATA should be an array");
9201 1 : return nullptr;
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 400 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9387 : const char *pszBandImageryMetadata =
9388 200 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9389 200 : if (pszBandImageryMetadata)
9390 : {
9391 12 : if (!poRootGroup)
9392 : {
9393 1 : CPLError(CE_Failure, CPLE_AppDefined,
9394 : "Root group should be provided when BAND_IMAGERY_METADATA "
9395 : "is set");
9396 10 : return nullptr;
9397 : }
9398 11 : CPLJSONDocument oDoc;
9399 11 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9400 : {
9401 1 : CPLError(CE_Failure, CPLE_AppDefined,
9402 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9403 1 : return nullptr;
9404 : }
9405 10 : auto oRoot = oDoc.GetRoot();
9406 10 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9407 : {
9408 1 : CPLError(CE_Failure, CPLE_AppDefined,
9409 : "Value of BAND_IMAGERY_METADATA should be an object");
9410 1 : return nullptr;
9411 : }
9412 12 : for (const auto &oJsonItem : oRoot.GetChildren())
9413 : {
9414 22 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9415 12 : oJsonItem.GetName() == "FWHM_UM")
9416 : {
9417 18 : auto osBandArrayFullname = oJsonItem.GetString("array");
9418 9 : if (osBandArrayFullname.empty())
9419 : {
9420 1 : CPLError(
9421 : CE_Failure, CPLE_AppDefined,
9422 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
9423 2 : oJsonItem.GetName().c_str());
9424 1 : return nullptr;
9425 : }
9426 : auto poArray =
9427 8 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9428 8 : if (!poArray)
9429 : {
9430 1 : CPLError(CE_Failure, CPLE_AppDefined,
9431 : "Array %s cannot be found",
9432 : osBandArrayFullname.c_str());
9433 1 : return nullptr;
9434 : }
9435 7 : if (poArray->GetDimensionCount() != 1)
9436 : {
9437 1 : CPLError(CE_Failure, CPLE_AppDefined,
9438 : "Array %s is not a 1D array",
9439 : osBandArrayFullname.c_str());
9440 1 : return nullptr;
9441 : }
9442 : const auto &osAuxArrayDimName =
9443 6 : poArray->GetDimensions()[0]->GetName();
9444 : auto oIter =
9445 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9446 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9447 : {
9448 1 : CPLError(CE_Failure, CPLE_AppDefined,
9449 : "Dimension \"%s\" of array \"%s\" is not a "
9450 : "non-X/Y dimension of array \"%s\"",
9451 : osAuxArrayDimName.c_str(),
9452 : osBandArrayFullname.c_str(),
9453 1 : array->GetName().c_str());
9454 1 : return nullptr;
9455 : }
9456 5 : const size_t iExtraDimIdx = oIter->second;
9457 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9458 :
9459 10 : std::string osUnit = oJsonItem.GetString("unit", "um");
9460 5 : if (STARTS_WITH(osUnit.c_str(), "${"))
9461 : {
9462 3 : if (osUnit.back() != '}')
9463 : {
9464 2 : CPLError(CE_Failure, CPLE_AppDefined,
9465 : "Value of "
9466 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9467 : "%s is invalid",
9468 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9469 2 : return nullptr;
9470 : }
9471 2 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9472 2 : auto poAttr = poArray->GetAttribute(osAttrName);
9473 2 : if (!poAttr)
9474 : {
9475 2 : CPLError(CE_Failure, CPLE_AppDefined,
9476 : "Value of "
9477 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9478 : "%s is invalid: %s is not an attribute of %s",
9479 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9480 : osAttrName.c_str(),
9481 : osBandArrayFullname.c_str());
9482 1 : return nullptr;
9483 : }
9484 1 : const char *pszValue = poAttr->ReadAsString();
9485 1 : if (!pszValue)
9486 : {
9487 0 : CPLError(CE_Failure, CPLE_AppDefined,
9488 : "Cannot get value of attribute %s of %s as a "
9489 : "string",
9490 : osAttrName.c_str(),
9491 : osBandArrayFullname.c_str());
9492 0 : return nullptr;
9493 : }
9494 1 : osUnit = pszValue;
9495 : }
9496 3 : double dfConvToUM = 1.0;
9497 7 : if (osUnit == "nm" || osUnit == "nanometre" ||
9498 9 : osUnit == "nanometres" || osUnit == "nanometer" ||
9499 2 : osUnit == "nanometers")
9500 : {
9501 1 : dfConvToUM = 1e-3;
9502 : }
9503 3 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9504 1 : osUnit == "micrometres" || osUnit == "micrometer" ||
9505 1 : osUnit == "micrometers"))
9506 : {
9507 2 : CPLError(CE_Failure, CPLE_AppDefined,
9508 : "Unhandled value for "
9509 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9510 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9511 1 : return nullptr;
9512 : }
9513 :
9514 2 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9515 2 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9516 : {
9517 1 : item.poCentralWavelengthArray = std::move(poArray);
9518 1 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9519 : }
9520 : else
9521 : {
9522 1 : item.poFWHMArray = std::move(poArray);
9523 1 : item.dfFWHMToMicrometer = dfConvToUM;
9524 : }
9525 : }
9526 : else
9527 : {
9528 1 : CPLError(CE_Warning, CPLE_AppDefined,
9529 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9530 2 : oJsonItem.GetName().c_str());
9531 : }
9532 : }
9533 : }
9534 :
9535 380 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9536 :
9537 190 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9538 :
9539 190 : poDS->nRasterYSize =
9540 190 : nDimCount < 2 ? 1
9541 179 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9542 179 : dims[iYDim]->GetSize()));
9543 380 : poDS->nRasterXSize = static_cast<int>(
9544 190 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9545 :
9546 380 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9547 380 : std::vector<GUInt64> anStackIters(nDimCount);
9548 380 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9549 600 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9550 : {
9551 410 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9552 : {
9553 41 : anMapNewToOld[j] = i;
9554 41 : j++;
9555 : }
9556 : }
9557 :
9558 380 : poDS->m_bHasGT =
9559 190 : array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9560 :
9561 380 : const auto attrs(array->GetAttributes());
9562 268 : for (const auto &attr : attrs)
9563 : {
9564 78 : if (attr->GetName() != "COLOR_INTERPRETATION")
9565 : {
9566 144 : auto stringArray = attr->ReadAsStringArray();
9567 144 : std::string val;
9568 72 : if (stringArray.size() > 1)
9569 : {
9570 22 : val += '{';
9571 : }
9572 166 : for (int i = 0; i < stringArray.size(); ++i)
9573 : {
9574 94 : if (i > 0)
9575 22 : val += ',';
9576 94 : val += stringArray[i];
9577 : }
9578 72 : if (stringArray.size() > 1)
9579 : {
9580 22 : val += '}';
9581 : }
9582 72 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9583 : }
9584 : }
9585 :
9586 190 : const char *pszDelay = CSLFetchNameValueDef(
9587 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9588 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9589 : const double dfDelay =
9590 190 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9591 190 : const auto nStartTime = time(nullptr);
9592 190 : bool bHasWarned = false;
9593 : // Instantiate bands by iterating over non-XY variables
9594 190 : size_t iDim = 0;
9595 190 : int nCurBand = 1;
9596 295 : lbl_next_depth:
9597 295 : if (iDim < nNewDimCount)
9598 : {
9599 43 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9600 43 : anOtherDimCoord[iDim] = 0;
9601 : while (true)
9602 : {
9603 105 : ++iDim;
9604 105 : goto lbl_next_depth;
9605 105 : lbl_return_to_caller:
9606 105 : --iDim;
9607 105 : --anStackIters[iDim];
9608 105 : if (anStackIters[iDim] == 0)
9609 43 : break;
9610 62 : ++anOtherDimCoord[iDim];
9611 : }
9612 : }
9613 : else
9614 : {
9615 504 : poDS->SetBand(nCurBand,
9616 : new GDALRasterBandFromArray(
9617 252 : poDS.get(), anOtherDimCoord,
9618 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9619 252 : dfDelay, nStartTime, bHasWarned));
9620 252 : ++nCurBand;
9621 : }
9622 295 : if (iDim > 0)
9623 105 : goto lbl_return_to_caller;
9624 :
9625 190 : if (!array->GetFilename().empty())
9626 : {
9627 169 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9628 : std::string osDerivedDatasetName(
9629 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9630 338 : int(iYDim), array->GetFullName().c_str()));
9631 169 : if (!array->GetContext().empty())
9632 : {
9633 2 : osDerivedDatasetName += " with context ";
9634 2 : osDerivedDatasetName += array->GetContext();
9635 : }
9636 169 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9637 169 : poDS->TryLoadXML();
9638 :
9639 2 : for (const auto &[pszKey, pszValue] :
9640 : cpl::IterateNameValue(static_cast<CSLConstList>(
9641 171 : poDS->GDALPamDataset::GetMetadata())))
9642 : {
9643 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9644 : }
9645 : }
9646 :
9647 190 : return poDS.release();
9648 : }
9649 :
9650 : /************************************************************************/
9651 : /* AsClassicDataset() */
9652 : /************************************************************************/
9653 :
9654 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9655 : *
9656 : * In the case of > 2D arrays, additional dimensions will be represented as
9657 : * raster bands.
9658 : *
9659 : * The "reverse" method is GDALRasterBand::AsMDArray().
9660 : *
9661 : * This is the same as the C function GDALMDArrayAsClassicDataset().
9662 : *
9663 : * @param iXDim Index of the dimension that will be used as the X/width axis.
9664 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
9665 : * Ignored if the dimension count is 1.
9666 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9667 : * and BAND_IMAGERY_METADATA option.
9668 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9669 : * nullptr. Current supported options are:
9670 : * <ul>
9671 : * <li>BAND_METADATA: JSON serialized array defining which
9672 : * arrays of the poRootGroup, indexed by non-X and Y
9673 : * dimensions, should be mapped as band metadata items.
9674 : * Each array item should be an object with the
9675 : * following members:
9676 : * - "array": full name of a band parameter array.
9677 : * Such array must be a one
9678 : * dimensional array, and its dimension must be one of
9679 : * the dimensions of the array on which the method is
9680 : * called (excluding the X and Y dimensons).
9681 : * - "item_name": band metadata item name
9682 : * - "item_value": (optional) String, where "%[x][.y]f",
9683 : * "%[x][.y]g" or "%s" printf-like formatting can be
9684 : * used to format the corresponding value of the
9685 : * parameter array. The percentage character should be
9686 : * repeated: "%%"
9687 : * "${attribute_name}" can also be used to include the
9688 : * value of an attribute for the array.
9689 : * If "item_value" is not provided, a default formatting
9690 : * of the value will be applied.
9691 : *
9692 : * Example:
9693 : * [
9694 : * {
9695 : * "array": "/sensor_band_parameters/wavelengths",
9696 : * "item_name": "WAVELENGTH",
9697 : * "item_value": "%.1f ${units}"
9698 : * },
9699 : * {
9700 : * "array": "/sensor_band_parameters/fwhm",
9701 : * "item_name": "FWHM"
9702 : * },
9703 : * {
9704 : * "array": "/sensor_band_parameters/fwhm",
9705 : * "item_name": "FWHM_UNIT",
9706 : * "item_value": "${units}"
9707 : * }
9708 : * ]
9709 : * </li>
9710 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
9711 : * JSON serialized object defining which arrays of the
9712 : * poRootGroup, indexed by non-X and Y dimensions,
9713 : * should be mapped as band metadata items in the
9714 : * band IMAGERY domain.
9715 : * The object currently accepts 2 members:
9716 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
9717 : * micrometers.
9718 : * - "FWHM_UM": Full-width half-maximum
9719 : * in micrometers.
9720 : * The value of each member should be an object with the
9721 : * following members:
9722 : * - "array": (required) full name of a band parameter
9723 : * array.
9724 : * Such array must be a one dimensional array, and its
9725 : * dimension must be one of the dimensions of the
9726 : * array on which the method is called
9727 : * (excluding the X and Y dimensons).
9728 : * - "unit": (optional) unit of the values pointed in
9729 : * the above array.
9730 : * Can be a literal string or a string of the form
9731 : * "${attribute_name}" to point to an attribute for
9732 : * the array.
9733 : * Accepted values are "um", "micrometer"
9734 : * (with UK vs US spelling, singular or plural), "nm",
9735 : * "nanometer" (with UK vs US spelling, singular or
9736 : * plural)
9737 : * If not provided, micrometer is assumed.
9738 : *
9739 : * Example for EMIT datasets:
9740 : * {
9741 : * "CENTRAL_WAVELENGTH_UM": {
9742 : * "array": "/sensor_band_parameters/wavelengths",
9743 : * "unit": "${units}"
9744 : * },
9745 : * "FWHM_UM": {
9746 : * "array": "/sensor_band_parameters/fwhm",
9747 : * "unit": "${units}"
9748 : * }
9749 : * }
9750 : * </li>
9751 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
9752 : * seconds allowed to set the DIM_{dimname}_VALUE band
9753 : * metadata items from the indexing variable of the
9754 : * dimensions.
9755 : * Default value is 5. 'unlimited' can be used to mean
9756 : * unlimited delay. Can also be defined globally with
9757 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
9758 : * option.</li>
9759 : * </ul>
9760 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9761 : */
9762 : GDALDataset *
9763 223 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
9764 : const std::shared_ptr<GDALGroup> &poRootGroup,
9765 : CSLConstList papszOptions) const
9766 : {
9767 446 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
9768 223 : if (!self)
9769 : {
9770 0 : CPLError(CE_Failure, CPLE_AppDefined,
9771 : "Driver implementation issue: m_pSelf not set !");
9772 0 : return nullptr;
9773 : }
9774 223 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
9775 223 : papszOptions);
9776 : }
9777 :
9778 : /************************************************************************/
9779 : /* GetStatistics() */
9780 : /************************************************************************/
9781 :
9782 : /**
9783 : * \brief Fetch statistics.
9784 : *
9785 : * Returns the minimum, maximum, mean and standard deviation of all
9786 : * pixel values in this array.
9787 : *
9788 : * If bForce is FALSE results will only be returned if it can be done
9789 : * quickly (i.e. without scanning the data). If bForce is FALSE and
9790 : * results cannot be returned efficiently, the method will return CE_Warning
9791 : * but no warning will have been issued. This is a non-standard use of
9792 : * the CE_Warning return value to indicate "nothing done".
9793 : *
9794 : * When cached statistics are not available, and bForce is TRUE,
9795 : * ComputeStatistics() is called.
9796 : *
9797 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
9798 : * will generally cache statistics in the .aux.xml file allowing fast fetch
9799 : * after the first request.
9800 : *
9801 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9802 : *
9803 : * This method is the same as the C function GDALMDArrayGetStatistics().
9804 : *
9805 : * @param bApproxOK Currently ignored. In the future, should be set to true
9806 : * if statistics on the whole array are wished, or to false if a subset of it
9807 : * may be used.
9808 : *
9809 : * @param bForce If false statistics will only be returned if it can
9810 : * be done without rescanning the image.
9811 : *
9812 : * @param pdfMin Location into which to load image minimum (may be NULL).
9813 : *
9814 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9815 : *
9816 : * @param pdfMean Location into which to load image mean (may be NULL).
9817 : *
9818 : * @param pdfStdDev Location into which to load image standard deviation
9819 : * (may be NULL).
9820 : *
9821 : * @param pnValidCount Number of samples whose value is different from the
9822 : * nodata value. (may be NULL)
9823 : *
9824 : * @param pfnProgress a function to call to report progress, or NULL.
9825 : *
9826 : * @param pProgressData application data to pass to the progress function.
9827 : *
9828 : * @return CE_None on success, CE_Warning if no values returned,
9829 : * CE_Failure if an error occurs.
9830 : *
9831 : * @since GDAL 3.2
9832 : */
9833 :
9834 7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
9835 : double *pdfMax, double *pdfMean,
9836 : double *pdfStdDev, GUInt64 *pnValidCount,
9837 : GDALProgressFunc pfnProgress,
9838 : void *pProgressData)
9839 : {
9840 7 : if (!bForce)
9841 1 : return CE_Warning;
9842 :
9843 12 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
9844 6 : pnValidCount, pfnProgress, pProgressData, nullptr)
9845 6 : ? CE_None
9846 6 : : CE_Failure;
9847 : }
9848 :
9849 : /************************************************************************/
9850 : /* ComputeStatistics() */
9851 : /************************************************************************/
9852 :
9853 : /**
9854 : * \brief Compute statistics.
9855 : *
9856 : * Returns the minimum, maximum, mean and standard deviation of all
9857 : * pixel values in this array.
9858 : *
9859 : * Pixels taken into account in statistics are those whose mask value
9860 : * (as determined by GetMask()) is non-zero.
9861 : *
9862 : * Once computed, the statistics will generally be "set" back on the
9863 : * owing dataset.
9864 : *
9865 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9866 : *
9867 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
9868 : * and GDALMDArrayComputeStatisticsEx().
9869 : *
9870 : * @param bApproxOK Currently ignored. In the future, should be set to true
9871 : * if statistics on the whole array are wished, or to false if a subset of it
9872 : * may be used.
9873 : *
9874 : * @param pdfMin Location into which to load image minimum (may be NULL).
9875 : *
9876 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9877 : *
9878 : * @param pdfMean Location into which to load image mean (may be NULL).
9879 : *
9880 : * @param pdfStdDev Location into which to load image standard deviation
9881 : * (may be NULL).
9882 : *
9883 : * @param pnValidCount Number of samples whose value is different from the
9884 : * nodata value. (may be NULL)
9885 : *
9886 : * @param pfnProgress a function to call to report progress, or NULL.
9887 : *
9888 : * @param pProgressData application data to pass to the progress function.
9889 : *
9890 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
9891 : * Options are driver specific. For now the netCDF and Zarr
9892 : * drivers recognize UPDATE_METADATA=YES, whose effect is
9893 : * to add or update the actual_range attribute with the
9894 : * computed min/max, only if done on the full array, in non
9895 : * approximate mode, and the dataset is opened in update
9896 : * mode.
9897 : *
9898 : * @return true on success
9899 : *
9900 : * @since GDAL 3.2
9901 : */
9902 :
9903 10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
9904 : double *pdfMax, double *pdfMean,
9905 : double *pdfStdDev, GUInt64 *pnValidCount,
9906 : GDALProgressFunc pfnProgress,
9907 : void *pProgressData,
9908 : CSLConstList papszOptions)
9909 : {
9910 : struct StatsPerChunkType
9911 : {
9912 : const GDALMDArray *array = nullptr;
9913 : std::shared_ptr<GDALMDArray> poMask{};
9914 : double dfMin = std::numeric_limits<double>::max();
9915 : double dfMax = -std::numeric_limits<double>::max();
9916 : double dfMean = 0.0;
9917 : double dfM2 = 0.0;
9918 : GUInt64 nValidCount = 0;
9919 : std::vector<GByte> abyData{};
9920 : std::vector<double> adfData{};
9921 : std::vector<GByte> abyMaskData{};
9922 : GDALProgressFunc pfnProgress = nullptr;
9923 : void *pProgressData = nullptr;
9924 : };
9925 :
9926 10 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
9927 : const GUInt64 *chunkArrayStartIdx,
9928 : const size_t *chunkCount, GUInt64 iCurChunk,
9929 : GUInt64 nChunkCount, void *pUserData)
9930 : {
9931 10 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
9932 10 : const GDALMDArray *array = data->array;
9933 10 : const GDALMDArray *poMask = data->poMask.get();
9934 10 : const size_t nDims = array->GetDimensionCount();
9935 10 : size_t nVals = 1;
9936 27 : for (size_t i = 0; i < nDims; i++)
9937 17 : nVals *= chunkCount[i];
9938 :
9939 : // Get mask
9940 10 : data->abyMaskData.resize(nVals);
9941 10 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9942 10 : poMask->GetDataType(), &data->abyMaskData[0])))
9943 : {
9944 0 : return false;
9945 : }
9946 :
9947 : // Get data
9948 10 : const auto &oType = array->GetDataType();
9949 10 : if (oType.GetNumericDataType() == GDT_Float64)
9950 : {
9951 4 : data->adfData.resize(nVals);
9952 4 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9953 4 : oType, &data->adfData[0]))
9954 : {
9955 0 : return false;
9956 : }
9957 : }
9958 : else
9959 : {
9960 6 : data->abyData.resize(nVals * oType.GetSize());
9961 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9962 6 : oType, &data->abyData[0]))
9963 : {
9964 0 : return false;
9965 : }
9966 6 : data->adfData.resize(nVals);
9967 6 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
9968 6 : static_cast<int>(oType.GetSize()),
9969 6 : &data->adfData[0], GDT_Float64,
9970 : static_cast<int>(sizeof(double)),
9971 : static_cast<GPtrDiff_t>(nVals));
9972 : }
9973 469 : for (size_t i = 0; i < nVals; i++)
9974 : {
9975 459 : if (data->abyMaskData[i])
9976 : {
9977 454 : const double dfValue = data->adfData[i];
9978 454 : data->dfMin = std::min(data->dfMin, dfValue);
9979 454 : data->dfMax = std::max(data->dfMax, dfValue);
9980 454 : data->nValidCount++;
9981 454 : const double dfDelta = dfValue - data->dfMean;
9982 454 : data->dfMean += dfDelta / data->nValidCount;
9983 454 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
9984 : }
9985 : }
9986 10 : if (data->pfnProgress &&
9987 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
9988 : "", data->pProgressData))
9989 : {
9990 0 : return false;
9991 : }
9992 10 : return true;
9993 : };
9994 :
9995 10 : const auto &oType = GetDataType();
9996 20 : if (oType.GetClass() != GEDTC_NUMERIC ||
9997 10 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
9998 : {
9999 0 : CPLError(
10000 : CE_Failure, CPLE_NotSupported,
10001 : "Statistics can only be computed on non-complex numeric data type");
10002 0 : return false;
10003 : }
10004 :
10005 10 : const size_t nDims = GetDimensionCount();
10006 20 : std::vector<GUInt64> arrayStartIdx(nDims);
10007 20 : std::vector<GUInt64> count(nDims);
10008 10 : const auto &poDims = GetDimensions();
10009 27 : for (size_t i = 0; i < nDims; i++)
10010 : {
10011 17 : count[i] = poDims[i]->GetSize();
10012 : }
10013 10 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10014 : const size_t nMaxChunkSize =
10015 : pszSwathSize
10016 10 : ? static_cast<size_t>(
10017 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10018 0 : CPLAtoGIntBig(pszSwathSize)))
10019 : : static_cast<size_t>(
10020 10 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10021 10 : GDALGetCacheMax64() / 4));
10022 20 : StatsPerChunkType sData;
10023 10 : sData.array = this;
10024 10 : sData.poMask = GetMask(nullptr);
10025 10 : if (sData.poMask == nullptr)
10026 : {
10027 0 : return false;
10028 : }
10029 10 : sData.pfnProgress = pfnProgress;
10030 10 : sData.pProgressData = pProgressData;
10031 10 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10032 20 : GetProcessingChunkSize(nMaxChunkSize).data(),
10033 10 : PerChunkFunc, &sData))
10034 : {
10035 0 : return false;
10036 : }
10037 :
10038 10 : if (pdfMin)
10039 10 : *pdfMin = sData.dfMin;
10040 :
10041 10 : if (pdfMax)
10042 10 : *pdfMax = sData.dfMax;
10043 :
10044 10 : if (pdfMean)
10045 8 : *pdfMean = sData.dfMean;
10046 :
10047 : const double dfStdDev =
10048 10 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10049 10 : if (pdfStdDev)
10050 8 : *pdfStdDev = dfStdDev;
10051 :
10052 10 : if (pnValidCount)
10053 8 : *pnValidCount = sData.nValidCount;
10054 :
10055 10 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10056 10 : sData.nValidCount, papszOptions);
10057 :
10058 10 : return true;
10059 : }
10060 :
10061 : /************************************************************************/
10062 : /* SetStatistics() */
10063 : /************************************************************************/
10064 : //! @cond Doxygen_Suppress
10065 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10066 : double /* dfMax */, double /* dfMean */,
10067 : double /* dfStdDev */,
10068 : GUInt64 /* nValidCount */,
10069 : CSLConstList /* papszOptions */)
10070 : {
10071 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10072 5 : return false;
10073 : }
10074 :
10075 : //! @endcond
10076 :
10077 : /************************************************************************/
10078 : /* ClearStatistics() */
10079 : /************************************************************************/
10080 :
10081 : /**
10082 : * \brief Clear statistics.
10083 : *
10084 : * @since GDAL 3.4
10085 : */
10086 0 : void GDALMDArray::ClearStatistics()
10087 : {
10088 0 : }
10089 :
10090 : /************************************************************************/
10091 : /* GetCoordinateVariables() */
10092 : /************************************************************************/
10093 :
10094 : /**
10095 : * \brief Return coordinate variables.
10096 : *
10097 : * Coordinate variables are an alternate way of indexing an array that can
10098 : * be sometimes used. For example, an array collected through remote sensing
10099 : * might be indexed by (scanline, pixel). But there can be
10100 : * a longitude and latitude arrays alongside that are also both indexed by
10101 : * (scanline, pixel), and are referenced from operational arrays for
10102 : * reprojection purposes.
10103 : *
10104 : * For netCDF, this will return the arrays referenced by the "coordinates"
10105 : * attribute.
10106 : *
10107 : * This method is the same as the C function
10108 : * GDALMDArrayGetCoordinateVariables().
10109 : *
10110 : * @return a vector of arrays
10111 : *
10112 : * @since GDAL 3.4
10113 : */
10114 :
10115 : std::vector<std::shared_ptr<GDALMDArray>>
10116 13 : GDALMDArray::GetCoordinateVariables() const
10117 : {
10118 13 : return {};
10119 : }
10120 :
10121 : /************************************************************************/
10122 : /* ~GDALExtendedDataType() */
10123 : /************************************************************************/
10124 :
10125 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10126 :
10127 : /************************************************************************/
10128 : /* GDALExtendedDataType() */
10129 : /************************************************************************/
10130 :
10131 8308 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10132 8308 : GDALExtendedDataTypeSubType eSubType)
10133 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10134 8308 : m_nMaxStringLength(nMaxStringLength)
10135 : {
10136 8308 : }
10137 :
10138 : /************************************************************************/
10139 : /* GDALExtendedDataType() */
10140 : /************************************************************************/
10141 :
10142 35323 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10143 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10144 35323 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10145 : {
10146 35323 : }
10147 :
10148 : /************************************************************************/
10149 : /* GDALExtendedDataType() */
10150 : /************************************************************************/
10151 :
10152 632 : GDALExtendedDataType::GDALExtendedDataType(
10153 : const std::string &osName, size_t nTotalSize,
10154 632 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10155 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10156 632 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10157 : {
10158 632 : }
10159 :
10160 : /************************************************************************/
10161 : /* GDALExtendedDataType() */
10162 : /************************************************************************/
10163 :
10164 : /** Copy constructor. */
10165 15444 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10166 30888 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10167 15444 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10168 15444 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
10169 : {
10170 15444 : if (m_eClass == GEDTC_COMPOUND)
10171 : {
10172 431 : for (const auto &elt : other.m_aoComponents)
10173 : {
10174 281 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10175 : }
10176 : }
10177 15444 : }
10178 :
10179 : /************************************************************************/
10180 : /* operator= () */
10181 : /************************************************************************/
10182 :
10183 : /** Copy assignment. */
10184 : GDALExtendedDataType &
10185 606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10186 : {
10187 606 : if (this != &other)
10188 : {
10189 606 : m_osName = other.m_osName;
10190 606 : m_eClass = other.m_eClass;
10191 606 : m_eSubType = other.m_eSubType;
10192 606 : m_eNumericDT = other.m_eNumericDT;
10193 606 : m_nSize = other.m_nSize;
10194 606 : m_nMaxStringLength = other.m_nMaxStringLength;
10195 606 : m_aoComponents.clear();
10196 606 : if (m_eClass == GEDTC_COMPOUND)
10197 : {
10198 0 : for (const auto &elt : other.m_aoComponents)
10199 : {
10200 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10201 : }
10202 : }
10203 : }
10204 606 : return *this;
10205 : }
10206 :
10207 : /************************************************************************/
10208 : /* operator= () */
10209 : /************************************************************************/
10210 :
10211 : /** Move assignment. */
10212 : GDALExtendedDataType &
10213 14546 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10214 : {
10215 14546 : m_osName = std::move(other.m_osName);
10216 14546 : m_eClass = other.m_eClass;
10217 14546 : m_eSubType = other.m_eSubType;
10218 14546 : m_eNumericDT = other.m_eNumericDT;
10219 14546 : m_nSize = other.m_nSize;
10220 14546 : m_nMaxStringLength = other.m_nMaxStringLength;
10221 14546 : m_aoComponents = std::move(other.m_aoComponents);
10222 14546 : other.m_eClass = GEDTC_NUMERIC;
10223 14546 : other.m_eNumericDT = GDT_Unknown;
10224 14546 : other.m_nSize = 0;
10225 14546 : other.m_nMaxStringLength = 0;
10226 14546 : return *this;
10227 : }
10228 :
10229 : /************************************************************************/
10230 : /* Create() */
10231 : /************************************************************************/
10232 :
10233 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10234 : *
10235 : * This is the same as the C function GDALExtendedDataTypeCreate()
10236 : *
10237 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10238 : * GDT_TypeCount
10239 : */
10240 35316 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10241 : {
10242 35316 : return GDALExtendedDataType(eType);
10243 : }
10244 :
10245 : /************************************************************************/
10246 : /* Create() */
10247 : /************************************************************************/
10248 :
10249 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10250 : *
10251 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10252 : *
10253 : * @param osName Type name.
10254 : * @param nTotalSize Total size of the type in bytes.
10255 : * Should be large enough to store all components.
10256 : * @param components Components of the compound type.
10257 : */
10258 639 : GDALExtendedDataType GDALExtendedDataType::Create(
10259 : const std::string &osName, size_t nTotalSize,
10260 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10261 : {
10262 639 : size_t nLastOffset = 0;
10263 : // Some arbitrary threshold to avoid potential integer overflows
10264 639 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10265 : {
10266 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10267 2 : return GDALExtendedDataType(GDT_Unknown);
10268 : }
10269 3155 : for (const auto &comp : components)
10270 : {
10271 : // Check alignment too ?
10272 2519 : if (comp->GetOffset() < nLastOffset)
10273 : {
10274 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10275 1 : return GDALExtendedDataType(GDT_Unknown);
10276 : }
10277 2518 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10278 : }
10279 636 : if (nTotalSize < nLastOffset)
10280 : {
10281 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10282 1 : return GDALExtendedDataType(GDT_Unknown);
10283 : }
10284 635 : if (nTotalSize == 0 || components.empty())
10285 : {
10286 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10287 3 : return GDALExtendedDataType(GDT_Unknown);
10288 : }
10289 632 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10290 : }
10291 :
10292 : /************************************************************************/
10293 : /* Create() */
10294 : /************************************************************************/
10295 :
10296 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10297 : *
10298 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10299 : *
10300 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10301 : * unknown/unlimited
10302 : * @param eSubType Subtype.
10303 : */
10304 : GDALExtendedDataType
10305 8308 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10306 : GDALExtendedDataTypeSubType eSubType)
10307 : {
10308 8308 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10309 : }
10310 :
10311 : /************************************************************************/
10312 : /* operator==() */
10313 : /************************************************************************/
10314 :
10315 : /** Equality operator.
10316 : *
10317 : * This is the same as the C function GDALExtendedDataTypeEquals().
10318 : */
10319 2240 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10320 : {
10321 2213 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10322 4453 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10323 : {
10324 177 : return false;
10325 : }
10326 2063 : if (m_eClass == GEDTC_NUMERIC)
10327 : {
10328 866 : return m_eNumericDT == other.m_eNumericDT;
10329 : }
10330 1197 : if (m_eClass == GEDTC_STRING)
10331 : {
10332 1016 : return true;
10333 : }
10334 181 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10335 181 : if (m_aoComponents.size() != other.m_aoComponents.size())
10336 : {
10337 2 : return false;
10338 : }
10339 806 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10340 : {
10341 627 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10342 : {
10343 0 : return false;
10344 : }
10345 : }
10346 179 : return true;
10347 : }
10348 :
10349 : /************************************************************************/
10350 : /* CanConvertTo() */
10351 : /************************************************************************/
10352 :
10353 : /** Return whether this data type can be converted to the other one.
10354 : *
10355 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10356 : *
10357 : * @param other Target data type for the conversion being considered.
10358 : */
10359 8133 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10360 : {
10361 8133 : if (m_eClass == GEDTC_NUMERIC)
10362 : {
10363 5781 : if (m_eNumericDT == GDT_Unknown)
10364 0 : return false;
10365 5781 : if (other.m_eClass == GEDTC_NUMERIC &&
10366 5694 : other.m_eNumericDT == GDT_Unknown)
10367 0 : return false;
10368 5868 : return other.m_eClass == GEDTC_NUMERIC ||
10369 5868 : other.m_eClass == GEDTC_STRING;
10370 : }
10371 2352 : if (m_eClass == GEDTC_STRING)
10372 : {
10373 2213 : return other.m_eClass == m_eClass;
10374 : }
10375 139 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10376 139 : if (other.m_eClass != GEDTC_COMPOUND)
10377 0 : return false;
10378 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10379 278 : srcComponents;
10380 568 : for (const auto &srcComp : m_aoComponents)
10381 : {
10382 429 : srcComponents[srcComp->GetName()] = &srcComp;
10383 : }
10384 419 : for (const auto &dstComp : other.m_aoComponents)
10385 : {
10386 281 : auto oIter = srcComponents.find(dstComp->GetName());
10387 281 : if (oIter == srcComponents.end())
10388 1 : return false;
10389 280 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10390 0 : return false;
10391 : }
10392 138 : return true;
10393 : }
10394 :
10395 : /************************************************************************/
10396 : /* NeedsFreeDynamicMemory() */
10397 : /************************************************************************/
10398 :
10399 : /** Return whether the data type holds dynamically allocated memory, that
10400 : * needs to be freed with FreeDynamicMemory().
10401 : *
10402 : */
10403 3528 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10404 : {
10405 3528 : switch (m_eClass)
10406 : {
10407 844 : case GEDTC_STRING:
10408 844 : return true;
10409 :
10410 2611 : case GEDTC_NUMERIC:
10411 2611 : return false;
10412 :
10413 73 : case GEDTC_COMPOUND:
10414 : {
10415 186 : for (const auto &comp : m_aoComponents)
10416 : {
10417 172 : if (comp->GetType().NeedsFreeDynamicMemory())
10418 59 : return true;
10419 : }
10420 : }
10421 : }
10422 14 : return false;
10423 : }
10424 :
10425 : /************************************************************************/
10426 : /* FreeDynamicMemory() */
10427 : /************************************************************************/
10428 :
10429 : /** Release the dynamic memory (strings typically) from a raw value.
10430 : *
10431 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10432 : *
10433 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10434 : */
10435 3260 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10436 : {
10437 3260 : switch (m_eClass)
10438 : {
10439 2286 : case GEDTC_STRING:
10440 : {
10441 : char *pszStr;
10442 2286 : memcpy(&pszStr, pBuffer, sizeof(char *));
10443 2286 : if (pszStr)
10444 : {
10445 1781 : VSIFree(pszStr);
10446 : }
10447 2286 : break;
10448 : }
10449 :
10450 835 : case GEDTC_NUMERIC:
10451 : {
10452 835 : break;
10453 : }
10454 :
10455 139 : case GEDTC_COMPOUND:
10456 : {
10457 139 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10458 605 : for (const auto &comp : m_aoComponents)
10459 : {
10460 932 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10461 466 : comp->GetOffset());
10462 : }
10463 139 : break;
10464 : }
10465 : }
10466 3260 : }
10467 :
10468 : /************************************************************************/
10469 : /* ~GDALEDTComponent() */
10470 : /************************************************************************/
10471 :
10472 : GDALEDTComponent::~GDALEDTComponent() = default;
10473 :
10474 : /************************************************************************/
10475 : /* GDALEDTComponent() */
10476 : /************************************************************************/
10477 :
10478 : /** constructor of a GDALEDTComponent
10479 : *
10480 : * This is the same as the C function GDALEDTComponendCreate()
10481 : *
10482 : * @param name Component name
10483 : * @param offset Offset in byte of the component in the compound data type.
10484 : * In case of nesting of compound data type, this should be
10485 : * the offset to the immediate belonging data type, not to the
10486 : * higher level one.
10487 : * @param type Component data type.
10488 : */
10489 2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10490 2510 : const GDALExtendedDataType &type)
10491 2510 : : m_osName(name), m_nOffset(offset), m_oType(type)
10492 : {
10493 2510 : }
10494 :
10495 : /************************************************************************/
10496 : /* GDALEDTComponent() */
10497 : /************************************************************************/
10498 :
10499 : /** Copy constructor. */
10500 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10501 :
10502 : /************************************************************************/
10503 : /* operator==() */
10504 : /************************************************************************/
10505 :
10506 : /** Equality operator.
10507 : */
10508 627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10509 : {
10510 1254 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10511 1254 : m_oType == other.m_oType;
10512 : }
10513 :
10514 : /************************************************************************/
10515 : /* ~GDALDimension() */
10516 : /************************************************************************/
10517 :
10518 : GDALDimension::~GDALDimension() = default;
10519 :
10520 : /************************************************************************/
10521 : /* GDALDimension() */
10522 : /************************************************************************/
10523 :
10524 : //! @cond Doxygen_Suppress
10525 : /** Constructor.
10526 : *
10527 : * @param osParentName Parent name
10528 : * @param osName name
10529 : * @param osType type. See GetType().
10530 : * @param osDirection direction. See GetDirection().
10531 : * @param nSize size.
10532 : */
10533 8119 : GDALDimension::GDALDimension(const std::string &osParentName,
10534 : const std::string &osName,
10535 : const std::string &osType,
10536 8119 : const std::string &osDirection, GUInt64 nSize)
10537 : : m_osName(osName),
10538 : m_osFullName(
10539 8119 : !osParentName.empty()
10540 12004 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10541 : : osName),
10542 28242 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10543 : {
10544 8119 : }
10545 :
10546 : //! @endcond
10547 :
10548 : /************************************************************************/
10549 : /* GetIndexingVariable() */
10550 : /************************************************************************/
10551 :
10552 : /** Return the variable that is used to index the dimension (if there is one).
10553 : *
10554 : * This is the array, typically one-dimensional, describing the values taken
10555 : * by the dimension.
10556 : */
10557 49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10558 : {
10559 49 : return nullptr;
10560 : }
10561 :
10562 : /************************************************************************/
10563 : /* SetIndexingVariable() */
10564 : /************************************************************************/
10565 :
10566 : /** Set the variable that is used to index the dimension.
10567 : *
10568 : * This is the array, typically one-dimensional, describing the values taken
10569 : * by the dimension.
10570 : *
10571 : * Optionally implemented by drivers.
10572 : *
10573 : * Drivers known to implement it: MEM.
10574 : *
10575 : * @param poArray Variable to use to index the dimension.
10576 : * @return true in case of success.
10577 : */
10578 3 : bool GDALDimension::SetIndexingVariable(
10579 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10580 : {
10581 3 : CPLError(CE_Failure, CPLE_NotSupported,
10582 : "SetIndexingVariable() not implemented");
10583 3 : return false;
10584 : }
10585 :
10586 : /************************************************************************/
10587 : /* Rename() */
10588 : /************************************************************************/
10589 :
10590 : /** Rename the dimension.
10591 : *
10592 : * This is not implemented by all drivers.
10593 : *
10594 : * Drivers known to implement it: MEM, netCDF, ZARR.
10595 : *
10596 : * This is the same as the C function GDALDimensionRename().
10597 : *
10598 : * @param osNewName New name.
10599 : *
10600 : * @return true in case of success
10601 : * @since GDAL 3.8
10602 : */
10603 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10604 : {
10605 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10606 0 : return false;
10607 : }
10608 :
10609 : /************************************************************************/
10610 : /* BaseRename() */
10611 : /************************************************************************/
10612 :
10613 : //! @cond Doxygen_Suppress
10614 8 : void GDALDimension::BaseRename(const std::string &osNewName)
10615 : {
10616 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
10617 8 : m_osFullName += osNewName;
10618 8 : m_osName = osNewName;
10619 8 : }
10620 :
10621 : //! @endcond
10622 :
10623 : //! @cond Doxygen_Suppress
10624 : /************************************************************************/
10625 : /* ParentRenamed() */
10626 : /************************************************************************/
10627 :
10628 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
10629 : {
10630 8 : m_osFullName = osNewParentFullName;
10631 8 : m_osFullName += "/";
10632 8 : m_osFullName += m_osName;
10633 8 : }
10634 :
10635 : //! @endcond
10636 :
10637 : //! @cond Doxygen_Suppress
10638 : /************************************************************************/
10639 : /* ParentDeleted() */
10640 : /************************************************************************/
10641 :
10642 4 : void GDALDimension::ParentDeleted()
10643 : {
10644 4 : }
10645 :
10646 : //! @endcond
10647 :
10648 : /************************************************************************/
10649 : /************************************************************************/
10650 : /************************************************************************/
10651 : /* C API */
10652 : /************************************************************************/
10653 : /************************************************************************/
10654 : /************************************************************************/
10655 :
10656 : /************************************************************************/
10657 : /* GDALExtendedDataTypeCreate() */
10658 : /************************************************************************/
10659 :
10660 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10661 : *
10662 : * This is the same as the C++ method GDALExtendedDataType::Create()
10663 : *
10664 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10665 : *
10666 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10667 : * GDT_TypeCount
10668 : *
10669 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10670 : */
10671 1984 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
10672 : {
10673 1984 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
10674 : {
10675 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10676 : "Illegal GDT_Unknown/GDT_TypeCount argument");
10677 0 : return nullptr;
10678 : }
10679 : return new GDALExtendedDataTypeHS(
10680 1984 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
10681 : }
10682 :
10683 : /************************************************************************/
10684 : /* GDALExtendedDataTypeCreateString() */
10685 : /************************************************************************/
10686 :
10687 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10688 : *
10689 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10690 : *
10691 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10692 : *
10693 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10694 : */
10695 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
10696 : {
10697 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10698 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
10699 : }
10700 :
10701 : /************************************************************************/
10702 : /* GDALExtendedDataTypeCreateStringEx() */
10703 : /************************************************************************/
10704 :
10705 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10706 : *
10707 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10708 : *
10709 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10710 : *
10711 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10712 : * @since GDAL 3.4
10713 : */
10714 : GDALExtendedDataTypeH
10715 188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
10716 : GDALExtendedDataTypeSubType eSubType)
10717 : {
10718 188 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10719 188 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
10720 : }
10721 :
10722 : /************************************************************************/
10723 : /* GDALExtendedDataTypeCreateCompound() */
10724 : /************************************************************************/
10725 :
10726 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10727 : *
10728 : * This is the same as the C++ method GDALExtendedDataType::Create(const
10729 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
10730 : *
10731 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10732 : *
10733 : * @param pszName Type name.
10734 : * @param nTotalSize Total size of the type in bytes.
10735 : * Should be large enough to store all components.
10736 : * @param nComponents Number of components in comps array.
10737 : * @param comps Components.
10738 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10739 : */
10740 : GDALExtendedDataTypeH
10741 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
10742 : size_t nComponents,
10743 : const GDALEDTComponentH *comps)
10744 : {
10745 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
10746 54 : for (size_t i = 0; i < nComponents; i++)
10747 : {
10748 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
10749 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
10750 : }
10751 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
10752 66 : std::move(compsCpp));
10753 22 : if (dt.GetClass() != GEDTC_COMPOUND)
10754 6 : return nullptr;
10755 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
10756 : }
10757 :
10758 : /************************************************************************/
10759 : /* GDALExtendedDataTypeRelease() */
10760 : /************************************************************************/
10761 :
10762 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
10763 : *
10764 : * Note: when applied on a object coming from a driver, this does not
10765 : * destroy the object in the file, database, etc...
10766 : */
10767 6503 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
10768 : {
10769 6503 : delete hEDT;
10770 6503 : }
10771 :
10772 : /************************************************************************/
10773 : /* GDALExtendedDataTypeGetName() */
10774 : /************************************************************************/
10775 :
10776 : /** Return type name.
10777 : *
10778 : * This is the same as the C++ method GDALExtendedDataType::GetName()
10779 : */
10780 7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
10781 : {
10782 7 : VALIDATE_POINTER1(hEDT, __func__, "");
10783 7 : return hEDT->m_poImpl->GetName().c_str();
10784 : }
10785 :
10786 : /************************************************************************/
10787 : /* GDALExtendedDataTypeGetClass() */
10788 : /************************************************************************/
10789 :
10790 : /** Return type class.
10791 : *
10792 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
10793 : */
10794 : GDALExtendedDataTypeClass
10795 7500 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
10796 : {
10797 7500 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
10798 7500 : return hEDT->m_poImpl->GetClass();
10799 : }
10800 :
10801 : /************************************************************************/
10802 : /* GDALExtendedDataTypeGetNumericDataType() */
10803 : /************************************************************************/
10804 :
10805 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
10806 : *
10807 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
10808 : */
10809 584 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
10810 : {
10811 584 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
10812 584 : return hEDT->m_poImpl->GetNumericDataType();
10813 : }
10814 :
10815 : /************************************************************************/
10816 : /* GDALExtendedDataTypeGetSize() */
10817 : /************************************************************************/
10818 :
10819 : /** Return data type size in bytes.
10820 : *
10821 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
10822 : */
10823 2494 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
10824 : {
10825 2494 : VALIDATE_POINTER1(hEDT, __func__, 0);
10826 2494 : return hEDT->m_poImpl->GetSize();
10827 : }
10828 :
10829 : /************************************************************************/
10830 : /* GDALExtendedDataTypeGetMaxStringLength() */
10831 : /************************************************************************/
10832 :
10833 : /** Return the maximum length of a string in bytes.
10834 : *
10835 : * 0 indicates unknown/unlimited string.
10836 : *
10837 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
10838 : */
10839 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
10840 : {
10841 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
10842 3 : return hEDT->m_poImpl->GetMaxStringLength();
10843 : }
10844 :
10845 : /************************************************************************/
10846 : /* GDALExtendedDataTypeCanConvertTo() */
10847 : /************************************************************************/
10848 :
10849 : /** Return whether this data type can be converted to the other one.
10850 : *
10851 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
10852 : *
10853 : * @param hSourceEDT Source data type for the conversion being considered.
10854 : * @param hTargetEDT Target data type for the conversion being considered.
10855 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
10856 : */
10857 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
10858 : GDALExtendedDataTypeH hTargetEDT)
10859 : {
10860 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
10861 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
10862 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
10863 : }
10864 :
10865 : /************************************************************************/
10866 : /* GDALExtendedDataTypeEquals() */
10867 : /************************************************************************/
10868 :
10869 : /** Return whether this data type is equal to another one.
10870 : *
10871 : * This is the same as the C++ method GDALExtendedDataType::operator==()
10872 : *
10873 : * @param hFirstEDT First data type.
10874 : * @param hSecondEDT Second data type.
10875 : * @return TRUE if they are equal. FALSE otherwise.
10876 : */
10877 98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
10878 : GDALExtendedDataTypeH hSecondEDT)
10879 : {
10880 98 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
10881 98 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
10882 98 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
10883 : }
10884 :
10885 : /************************************************************************/
10886 : /* GDALExtendedDataTypeGetSubType() */
10887 : /************************************************************************/
10888 :
10889 : /** Return the subtype of a type.
10890 : *
10891 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
10892 : *
10893 : * @param hEDT Data type.
10894 : * @return subtype.
10895 : * @since 3.4
10896 : */
10897 : GDALExtendedDataTypeSubType
10898 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
10899 : {
10900 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
10901 104 : return hEDT->m_poImpl->GetSubType();
10902 : }
10903 :
10904 : /************************************************************************/
10905 : /* GDALExtendedDataTypeGetComponents() */
10906 : /************************************************************************/
10907 :
10908 : /** Return the components of the data type (only valid when GetClass() ==
10909 : * GEDTC_COMPOUND)
10910 : *
10911 : * The returned array and its content must be freed with
10912 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
10913 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
10914 : * individual array members).
10915 : *
10916 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
10917 : *
10918 : * @param hEDT Data type
10919 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
10920 : * @return an array of *pnCount components.
10921 : */
10922 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
10923 : size_t *pnCount)
10924 : {
10925 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
10926 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
10927 44 : const auto &components = hEDT->m_poImpl->GetComponents();
10928 : auto ret = static_cast<GDALEDTComponentH *>(
10929 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
10930 131 : for (size_t i = 0; i < components.size(); i++)
10931 : {
10932 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
10933 : }
10934 44 : *pnCount = components.size();
10935 44 : return ret;
10936 : }
10937 :
10938 : /************************************************************************/
10939 : /* GDALExtendedDataTypeFreeComponents() */
10940 : /************************************************************************/
10941 :
10942 : /** Free the return of GDALExtendedDataTypeGetComponents().
10943 : *
10944 : * @param components return value of GDALExtendedDataTypeGetComponents()
10945 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
10946 : */
10947 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
10948 : size_t nCount)
10949 : {
10950 131 : for (size_t i = 0; i < nCount; i++)
10951 : {
10952 87 : delete components[i];
10953 : }
10954 44 : CPLFree(components);
10955 44 : }
10956 :
10957 : /************************************************************************/
10958 : /* GDALEDTComponentCreate() */
10959 : /************************************************************************/
10960 :
10961 : /** Create a new GDALEDTComponent.
10962 : *
10963 : * The returned value must be freed with GDALEDTComponentRelease().
10964 : *
10965 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
10966 : */
10967 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
10968 : GDALExtendedDataTypeH hType)
10969 : {
10970 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10971 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
10972 : return new GDALEDTComponentHS(
10973 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
10974 : }
10975 :
10976 : /************************************************************************/
10977 : /* GDALEDTComponentRelease() */
10978 : /************************************************************************/
10979 :
10980 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
10981 : *
10982 : * Note: when applied on a object coming from a driver, this does not
10983 : * destroy the object in the file, database, etc...
10984 : */
10985 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
10986 : {
10987 61 : delete hComp;
10988 61 : }
10989 :
10990 : /************************************************************************/
10991 : /* GDALEDTComponentGetName() */
10992 : /************************************************************************/
10993 :
10994 : /** Return the name.
10995 : *
10996 : * The returned pointer is valid until hComp is released.
10997 : *
10998 : * This is the same as the C++ method GDALEDTComponent::GetName().
10999 : */
11000 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11001 : {
11002 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11003 33 : return hComp->m_poImpl->GetName().c_str();
11004 : }
11005 :
11006 : /************************************************************************/
11007 : /* GDALEDTComponentGetOffset() */
11008 : /************************************************************************/
11009 :
11010 : /** Return the offset (in bytes) of the component in the compound data type.
11011 : *
11012 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11013 : */
11014 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11015 : {
11016 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11017 31 : return hComp->m_poImpl->GetOffset();
11018 : }
11019 :
11020 : /************************************************************************/
11021 : /* GDALEDTComponentGetType() */
11022 : /************************************************************************/
11023 :
11024 : /** Return the data type of the component.
11025 : *
11026 : * This is the same as the C++ method GDALEDTComponent::GetType().
11027 : */
11028 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11029 : {
11030 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11031 : return new GDALExtendedDataTypeHS(
11032 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11033 : }
11034 :
11035 : /************************************************************************/
11036 : /* GDALGroupRelease() */
11037 : /************************************************************************/
11038 :
11039 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11040 : *
11041 : * Note: when applied on a object coming from a driver, this does not
11042 : * destroy the object in the file, database, etc...
11043 : */
11044 1385 : void GDALGroupRelease(GDALGroupH hGroup)
11045 : {
11046 1385 : delete hGroup;
11047 1385 : }
11048 :
11049 : /************************************************************************/
11050 : /* GDALGroupGetName() */
11051 : /************************************************************************/
11052 :
11053 : /** Return the name of the group.
11054 : *
11055 : * The returned pointer is valid until hGroup is released.
11056 : *
11057 : * This is the same as the C++ method GDALGroup::GetName().
11058 : */
11059 87 : const char *GDALGroupGetName(GDALGroupH hGroup)
11060 : {
11061 87 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11062 87 : return hGroup->m_poImpl->GetName().c_str();
11063 : }
11064 :
11065 : /************************************************************************/
11066 : /* GDALGroupGetFullName() */
11067 : /************************************************************************/
11068 :
11069 : /** Return the full name of the group.
11070 : *
11071 : * The returned pointer is valid until hGroup is released.
11072 : *
11073 : * This is the same as the C++ method GDALGroup::GetFullName().
11074 : */
11075 41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11076 : {
11077 41 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11078 41 : return hGroup->m_poImpl->GetFullName().c_str();
11079 : }
11080 :
11081 : /************************************************************************/
11082 : /* GDALGroupGetMDArrayNames() */
11083 : /************************************************************************/
11084 :
11085 : /** Return the list of multidimensional array names contained in this group.
11086 : *
11087 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11088 : *
11089 : * @return the array names, to be freed with CSLDestroy()
11090 : */
11091 317 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11092 : {
11093 317 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11094 634 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11095 634 : CPLStringList res;
11096 805 : for (const auto &name : names)
11097 : {
11098 488 : res.AddString(name.c_str());
11099 : }
11100 317 : return res.StealList();
11101 : }
11102 :
11103 : /************************************************************************/
11104 : /* GDALGroupOpenMDArray() */
11105 : /************************************************************************/
11106 :
11107 : /** Open and return a multidimensional array.
11108 : *
11109 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11110 : *
11111 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11112 : */
11113 761 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11114 : CSLConstList papszOptions)
11115 : {
11116 761 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11117 761 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11118 2283 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11119 2283 : papszOptions);
11120 761 : if (!array)
11121 28 : return nullptr;
11122 733 : return new GDALMDArrayHS(array);
11123 : }
11124 :
11125 : /************************************************************************/
11126 : /* GDALGroupOpenMDArrayFromFullname() */
11127 : /************************************************************************/
11128 :
11129 : /** Open and return a multidimensional array from its fully qualified name.
11130 : *
11131 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11132 : *
11133 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11134 : *
11135 : * @since GDAL 3.2
11136 : */
11137 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11138 : const char *pszFullname,
11139 : CSLConstList papszOptions)
11140 : {
11141 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11142 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11143 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11144 48 : std::string(pszFullname), papszOptions);
11145 16 : if (!array)
11146 2 : return nullptr;
11147 14 : return new GDALMDArrayHS(array);
11148 : }
11149 :
11150 : /************************************************************************/
11151 : /* GDALGroupResolveMDArray() */
11152 : /************************************************************************/
11153 :
11154 : /** Locate an array in a group and its subgroups by name.
11155 : *
11156 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11157 : * @since GDAL 3.2
11158 : */
11159 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11160 : const char *pszStartingPoint,
11161 : CSLConstList papszOptions)
11162 : {
11163 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11164 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11165 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11166 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11167 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11168 19 : if (!array)
11169 2 : return nullptr;
11170 17 : return new GDALMDArrayHS(array);
11171 : }
11172 :
11173 : /************************************************************************/
11174 : /* GDALGroupGetGroupNames() */
11175 : /************************************************************************/
11176 :
11177 : /** Return the list of sub-groups contained in this group.
11178 : *
11179 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11180 : *
11181 : * @return the group names, to be freed with CSLDestroy()
11182 : */
11183 95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11184 : {
11185 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11186 190 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11187 190 : CPLStringList res;
11188 215 : for (const auto &name : names)
11189 : {
11190 120 : res.AddString(name.c_str());
11191 : }
11192 95 : return res.StealList();
11193 : }
11194 :
11195 : /************************************************************************/
11196 : /* GDALGroupOpenGroup() */
11197 : /************************************************************************/
11198 :
11199 : /** Open and return a sub-group.
11200 : *
11201 : * This is the same as the C++ method GDALGroup::OpenGroup().
11202 : *
11203 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11204 : */
11205 157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11206 : CSLConstList papszOptions)
11207 : {
11208 157 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11209 157 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11210 : auto subGroup =
11211 471 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11212 157 : if (!subGroup)
11213 28 : return nullptr;
11214 129 : return new GDALGroupHS(subGroup);
11215 : }
11216 :
11217 : /************************************************************************/
11218 : /* GDALGroupGetVectorLayerNames() */
11219 : /************************************************************************/
11220 :
11221 : /** Return the list of layer names contained in this group.
11222 : *
11223 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11224 : *
11225 : * @return the group names, to be freed with CSLDestroy()
11226 : * @since 3.4
11227 : */
11228 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11229 : CSLConstList papszOptions)
11230 : {
11231 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11232 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11233 16 : CPLStringList res;
11234 18 : for (const auto &name : names)
11235 : {
11236 10 : res.AddString(name.c_str());
11237 : }
11238 8 : return res.StealList();
11239 : }
11240 :
11241 : /************************************************************************/
11242 : /* GDALGroupOpenVectorLayer() */
11243 : /************************************************************************/
11244 :
11245 : /** Open and return a vector layer.
11246 : *
11247 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11248 : *
11249 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11250 : * the returned handled if only valid while the parent GDALDatasetH is kept
11251 : * opened.
11252 : *
11253 : * @return the vector layer, or nullptr.
11254 : * @since 3.4
11255 : */
11256 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11257 : const char *pszVectorLayerName,
11258 : CSLConstList papszOptions)
11259 : {
11260 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11261 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11262 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11263 24 : std::string(pszVectorLayerName), papszOptions));
11264 : }
11265 :
11266 : /************************************************************************/
11267 : /* GDALGroupOpenMDArrayFromFullname() */
11268 : /************************************************************************/
11269 :
11270 : /** Open and return a sub-group from its fully qualified name.
11271 : *
11272 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11273 : *
11274 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11275 : *
11276 : * @since GDAL 3.2
11277 : */
11278 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11279 : const char *pszFullname,
11280 : CSLConstList papszOptions)
11281 : {
11282 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11283 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11284 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11285 9 : std::string(pszFullname), papszOptions);
11286 3 : if (!subGroup)
11287 2 : return nullptr;
11288 1 : return new GDALGroupHS(subGroup);
11289 : }
11290 :
11291 : /************************************************************************/
11292 : /* GDALGroupGetDimensions() */
11293 : /************************************************************************/
11294 :
11295 : /** Return the list of dimensions contained in this group and used by its
11296 : * arrays.
11297 : *
11298 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11299 : * array itself needs to be freed, CPLFree() should be called (and
11300 : * GDALDimensionRelease() on individual array members).
11301 : *
11302 : * This is the same as the C++ method GDALGroup::GetDimensions().
11303 : *
11304 : * @param hGroup Group.
11305 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11306 : * @param papszOptions Driver specific options determining how dimensions
11307 : * should be retrieved. Pass nullptr for default behavior.
11308 : *
11309 : * @return an array of *pnCount dimensions.
11310 : */
11311 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11312 : CSLConstList papszOptions)
11313 : {
11314 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11315 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11316 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11317 : auto ret = static_cast<GDALDimensionH *>(
11318 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11319 230 : for (size_t i = 0; i < dims.size(); i++)
11320 : {
11321 157 : ret[i] = new GDALDimensionHS(dims[i]);
11322 : }
11323 73 : *pnCount = dims.size();
11324 73 : return ret;
11325 : }
11326 :
11327 : /************************************************************************/
11328 : /* GDALGroupGetAttribute() */
11329 : /************************************************************************/
11330 :
11331 : /** Return an attribute by its name.
11332 : *
11333 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11334 : *
11335 : * The returned attribute must be freed with GDALAttributeRelease().
11336 : */
11337 78 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11338 : {
11339 78 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11340 78 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11341 234 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11342 78 : if (attr)
11343 74 : return new GDALAttributeHS(attr);
11344 4 : return nullptr;
11345 : }
11346 :
11347 : /************************************************************************/
11348 : /* GDALGroupGetAttributes() */
11349 : /************************************************************************/
11350 :
11351 : /** Return the list of attributes contained in this group.
11352 : *
11353 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11354 : * array itself needs to be freed, CPLFree() should be called (and
11355 : * GDALAttributeRelease() on individual array members).
11356 : *
11357 : * This is the same as the C++ method GDALGroup::GetAttributes().
11358 : *
11359 : * @param hGroup Group.
11360 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11361 : * @param papszOptions Driver specific options determining how attributes
11362 : * should be retrieved. Pass nullptr for default behavior.
11363 : *
11364 : * @return an array of *pnCount attributes.
11365 : */
11366 67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11367 : CSLConstList papszOptions)
11368 : {
11369 67 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11370 67 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11371 67 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11372 : auto ret = static_cast<GDALAttributeH *>(
11373 67 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11374 221 : for (size_t i = 0; i < attrs.size(); i++)
11375 : {
11376 154 : ret[i] = new GDALAttributeHS(attrs[i]);
11377 : }
11378 67 : *pnCount = attrs.size();
11379 67 : return ret;
11380 : }
11381 :
11382 : /************************************************************************/
11383 : /* GDALGroupGetStructuralInfo() */
11384 : /************************************************************************/
11385 :
11386 : /** Return structural information on the group.
11387 : *
11388 : * This may be the compression, etc..
11389 : *
11390 : * The return value should not be freed and is valid until GDALGroup is
11391 : * released or this function called again.
11392 : *
11393 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11394 : */
11395 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11396 : {
11397 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11398 4 : return hGroup->m_poImpl->GetStructuralInfo();
11399 : }
11400 :
11401 : /************************************************************************/
11402 : /* GDALReleaseAttributes() */
11403 : /************************************************************************/
11404 :
11405 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11406 : *
11407 : * @param attributes return pointer of above methods
11408 : * @param nCount *pnCount value returned by above methods
11409 : */
11410 125 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11411 : {
11412 408 : for (size_t i = 0; i < nCount; i++)
11413 : {
11414 283 : delete attributes[i];
11415 : }
11416 125 : CPLFree(attributes);
11417 125 : }
11418 :
11419 : /************************************************************************/
11420 : /* GDALGroupCreateGroup() */
11421 : /************************************************************************/
11422 :
11423 : /** Create a sub-group within a group.
11424 : *
11425 : * This is the same as the C++ method GDALGroup::CreateGroup().
11426 : *
11427 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11428 : */
11429 173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11430 : CSLConstList papszOptions)
11431 : {
11432 173 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11433 173 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11434 519 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11435 519 : papszOptions);
11436 173 : if (!ret)
11437 49 : return nullptr;
11438 124 : return new GDALGroupHS(ret);
11439 : }
11440 :
11441 : /************************************************************************/
11442 : /* GDALGroupDeleteGroup() */
11443 : /************************************************************************/
11444 :
11445 : /** Delete a sub-group from a group.
11446 : *
11447 : * After this call, if a previously obtained instance of the deleted object
11448 : * is still alive, no method other than for freeing it should be invoked.
11449 : *
11450 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11451 : *
11452 : * @return true in case of success.
11453 : * @since GDAL 3.8
11454 : */
11455 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11456 : CSLConstList papszOptions)
11457 : {
11458 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11459 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11460 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11461 20 : papszOptions);
11462 : }
11463 :
11464 : /************************************************************************/
11465 : /* GDALGroupCreateDimension() */
11466 : /************************************************************************/
11467 :
11468 : /** Create a dimension within a group.
11469 : *
11470 : * This is the same as the C++ method GDALGroup::CreateDimension().
11471 : *
11472 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11473 : */
11474 658 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11475 : const char *pszType,
11476 : const char *pszDirection, GUInt64 nSize,
11477 : CSLConstList papszOptions)
11478 : {
11479 658 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11480 658 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11481 658 : auto ret = hGroup->m_poImpl->CreateDimension(
11482 1316 : std::string(pszName), std::string(pszType ? pszType : ""),
11483 2632 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11484 658 : if (!ret)
11485 9 : return nullptr;
11486 649 : return new GDALDimensionHS(ret);
11487 : }
11488 :
11489 : /************************************************************************/
11490 : /* GDALGroupCreateMDArray() */
11491 : /************************************************************************/
11492 :
11493 : /** Create a multidimensional array within a group.
11494 : *
11495 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11496 : *
11497 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11498 : */
11499 599 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11500 : size_t nDimensions,
11501 : GDALDimensionH *pahDimensions,
11502 : GDALExtendedDataTypeH hEDT,
11503 : CSLConstList papszOptions)
11504 : {
11505 599 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11506 599 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11507 599 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11508 1198 : std::vector<std::shared_ptr<GDALDimension>> dims;
11509 599 : dims.reserve(nDimensions);
11510 1413 : for (size_t i = 0; i < nDimensions; i++)
11511 814 : dims.push_back(pahDimensions[i]->m_poImpl);
11512 1797 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11513 1797 : *(hEDT->m_poImpl), papszOptions);
11514 599 : if (!ret)
11515 65 : return nullptr;
11516 534 : return new GDALMDArrayHS(ret);
11517 : }
11518 :
11519 : /************************************************************************/
11520 : /* GDALGroupDeleteMDArray() */
11521 : /************************************************************************/
11522 :
11523 : /** Delete an array from a group.
11524 : *
11525 : * After this call, if a previously obtained instance of the deleted object
11526 : * is still alive, no method other than for freeing it should be invoked.
11527 : *
11528 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
11529 : *
11530 : * @return true in case of success.
11531 : * @since GDAL 3.8
11532 : */
11533 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
11534 : CSLConstList papszOptions)
11535 : {
11536 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11537 20 : VALIDATE_POINTER1(pszName, __func__, false);
11538 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
11539 : }
11540 :
11541 : /************************************************************************/
11542 : /* GDALGroupCreateAttribute() */
11543 : /************************************************************************/
11544 :
11545 : /** Create a attribute within a group.
11546 : *
11547 : * This is the same as the C++ method GDALGroup::CreateAttribute().
11548 : *
11549 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11550 : */
11551 120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
11552 : size_t nDimensions,
11553 : const GUInt64 *panDimensions,
11554 : GDALExtendedDataTypeH hEDT,
11555 : CSLConstList papszOptions)
11556 : {
11557 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11558 120 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11559 240 : std::vector<GUInt64> dims;
11560 120 : dims.reserve(nDimensions);
11561 166 : for (size_t i = 0; i < nDimensions; i++)
11562 46 : dims.push_back(panDimensions[i]);
11563 120 : auto ret = hGroup->m_poImpl->CreateAttribute(
11564 360 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11565 120 : if (!ret)
11566 14 : return nullptr;
11567 106 : return new GDALAttributeHS(ret);
11568 : }
11569 :
11570 : /************************************************************************/
11571 : /* GDALGroupDeleteAttribute() */
11572 : /************************************************************************/
11573 :
11574 : /** Delete an attribute from a group.
11575 : *
11576 : * After this call, if a previously obtained instance of the deleted object
11577 : * is still alive, no method other than for freeing it should be invoked.
11578 : *
11579 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
11580 : *
11581 : * @return true in case of success.
11582 : * @since GDAL 3.8
11583 : */
11584 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
11585 : CSLConstList papszOptions)
11586 : {
11587 25 : VALIDATE_POINTER1(hGroup, __func__, false);
11588 25 : VALIDATE_POINTER1(pszName, __func__, false);
11589 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
11590 25 : papszOptions);
11591 : }
11592 :
11593 : /************************************************************************/
11594 : /* GDALGroupRename() */
11595 : /************************************************************************/
11596 :
11597 : /** Rename the group.
11598 : *
11599 : * This is not implemented by all drivers.
11600 : *
11601 : * Drivers known to implement it: MEM, netCDF.
11602 : *
11603 : * This is the same as the C++ method GDALGroup::Rename()
11604 : *
11605 : * @return true in case of success
11606 : * @since GDAL 3.8
11607 : */
11608 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
11609 : {
11610 45 : VALIDATE_POINTER1(hGroup, __func__, false);
11611 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
11612 45 : return hGroup->m_poImpl->Rename(pszNewName);
11613 : }
11614 :
11615 : /************************************************************************/
11616 : /* GDALGroupSubsetDimensionFromSelection() */
11617 : /************************************************************************/
11618 :
11619 : /** Return a virtual group whose one dimension has been subset according to a
11620 : * selection.
11621 : *
11622 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
11623 : *
11624 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
11625 : */
11626 : GDALGroupH
11627 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
11628 : const char *pszSelection,
11629 : CPL_UNUSED CSLConstList papszOptions)
11630 : {
11631 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11632 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
11633 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
11634 42 : std::string(pszSelection));
11635 14 : if (!hNewGroup)
11636 8 : return nullptr;
11637 6 : return new GDALGroupHS(hNewGroup);
11638 : }
11639 :
11640 : /************************************************************************/
11641 : /* GDALMDArrayRelease() */
11642 : /************************************************************************/
11643 :
11644 : /** Release the GDAL in-memory object associated with a GDALMDArray.
11645 : *
11646 : * Note: when applied on a object coming from a driver, this does not
11647 : * destroy the object in the file, database, etc...
11648 : */
11649 1964 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
11650 : {
11651 1964 : delete hMDArray;
11652 1964 : }
11653 :
11654 : /************************************************************************/
11655 : /* GDALMDArrayGetName() */
11656 : /************************************************************************/
11657 :
11658 : /** Return array name.
11659 : *
11660 : * This is the same as the C++ method GDALMDArray::GetName()
11661 : */
11662 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
11663 : {
11664 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11665 83 : return hArray->m_poImpl->GetName().c_str();
11666 : }
11667 :
11668 : /************************************************************************/
11669 : /* GDALMDArrayGetFullName() */
11670 : /************************************************************************/
11671 :
11672 : /** Return array full name.
11673 : *
11674 : * This is the same as the C++ method GDALMDArray::GetFullName()
11675 : */
11676 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
11677 : {
11678 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11679 50 : return hArray->m_poImpl->GetFullName().c_str();
11680 : }
11681 :
11682 : /************************************************************************/
11683 : /* GDALMDArrayGetName() */
11684 : /************************************************************************/
11685 :
11686 : /** Return the total number of values in the array.
11687 : *
11688 : * This is the same as the C++ method
11689 : * GDALAbstractMDArray::GetTotalElementsCount()
11690 : */
11691 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
11692 : {
11693 6 : VALIDATE_POINTER1(hArray, __func__, 0);
11694 6 : return hArray->m_poImpl->GetTotalElementsCount();
11695 : }
11696 :
11697 : /************************************************************************/
11698 : /* GDALMDArrayGetDimensionCount() */
11699 : /************************************************************************/
11700 :
11701 : /** Return the number of dimensions.
11702 : *
11703 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
11704 : */
11705 10157 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
11706 : {
11707 10157 : VALIDATE_POINTER1(hArray, __func__, 0);
11708 10157 : return hArray->m_poImpl->GetDimensionCount();
11709 : }
11710 :
11711 : /************************************************************************/
11712 : /* GDALMDArrayGetDimensions() */
11713 : /************************************************************************/
11714 :
11715 : /** Return the dimensions of the array
11716 : *
11717 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11718 : * array itself needs to be freed, CPLFree() should be called (and
11719 : * GDALDimensionRelease() on individual array members).
11720 : *
11721 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
11722 : *
11723 : * @param hArray Array.
11724 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11725 : *
11726 : * @return an array of *pnCount dimensions.
11727 : */
11728 2249 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
11729 : {
11730 2249 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11731 2249 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11732 2249 : const auto &dims(hArray->m_poImpl->GetDimensions());
11733 : auto ret = static_cast<GDALDimensionH *>(
11734 2249 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11735 6355 : for (size_t i = 0; i < dims.size(); i++)
11736 : {
11737 4106 : ret[i] = new GDALDimensionHS(dims[i]);
11738 : }
11739 2249 : *pnCount = dims.size();
11740 2249 : return ret;
11741 : }
11742 :
11743 : /************************************************************************/
11744 : /* GDALReleaseDimensions() */
11745 : /************************************************************************/
11746 :
11747 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
11748 : *
11749 : * @param dims return pointer of above methods
11750 : * @param nCount *pnCount value returned by above methods
11751 : */
11752 2322 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
11753 : {
11754 6585 : for (size_t i = 0; i < nCount; i++)
11755 : {
11756 4263 : delete dims[i];
11757 : }
11758 2322 : CPLFree(dims);
11759 2322 : }
11760 :
11761 : /************************************************************************/
11762 : /* GDALMDArrayGetDataType() */
11763 : /************************************************************************/
11764 :
11765 : /** Return the data type
11766 : *
11767 : * The return must be freed with GDALExtendedDataTypeRelease().
11768 : */
11769 3819 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
11770 : {
11771 3819 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11772 : return new GDALExtendedDataTypeHS(
11773 3819 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
11774 : }
11775 :
11776 : /************************************************************************/
11777 : /* GDALMDArrayRead() */
11778 : /************************************************************************/
11779 :
11780 : /** Read part or totality of a multidimensional array.
11781 : *
11782 : * This is the same as the C++ method GDALAbstractMDArray::Read()
11783 : *
11784 : * @return TRUE in case of success.
11785 : */
11786 1916 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11787 : const size_t *count, const GInt64 *arrayStep,
11788 : const GPtrDiff_t *bufferStride,
11789 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
11790 : const void *pDstBufferAllocStart,
11791 : size_t nDstBufferAllocSize)
11792 : {
11793 1916 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11794 1916 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11795 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11796 : {
11797 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11798 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11799 : }
11800 1916 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11801 1916 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
11802 : // coverity[var_deref_model]
11803 3832 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
11804 1916 : *(bufferDataType->m_poImpl), pDstBuffer,
11805 1916 : pDstBufferAllocStart, nDstBufferAllocSize);
11806 : }
11807 :
11808 : /************************************************************************/
11809 : /* GDALMDArrayWrite() */
11810 : /************************************************************************/
11811 :
11812 : /** Write part or totality of a multidimensional array.
11813 : *
11814 : * This is the same as the C++ method GDALAbstractMDArray::Write()
11815 : *
11816 : * @return TRUE in case of success.
11817 : */
11818 554 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11819 : const size_t *count, const GInt64 *arrayStep,
11820 : const GPtrDiff_t *bufferStride,
11821 : GDALExtendedDataTypeH bufferDataType,
11822 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
11823 : size_t nSrcBufferAllocSize)
11824 : {
11825 554 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11826 554 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11827 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11828 : {
11829 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11830 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11831 : }
11832 554 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11833 554 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
11834 : // coverity[var_deref_model]
11835 1108 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
11836 554 : bufferStride, *(bufferDataType->m_poImpl),
11837 : pSrcBuffer, pSrcBufferAllocStart,
11838 554 : nSrcBufferAllocSize);
11839 : }
11840 :
11841 : /************************************************************************/
11842 : /* GDALMDArrayAdviseRead() */
11843 : /************************************************************************/
11844 :
11845 : /** Advise driver of upcoming read requests.
11846 : *
11847 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11848 : *
11849 : * @return TRUE in case of success.
11850 : *
11851 : * @since GDAL 3.2
11852 : */
11853 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11854 : const size_t *count)
11855 : {
11856 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
11857 : }
11858 :
11859 : /************************************************************************/
11860 : /* GDALMDArrayAdviseReadEx() */
11861 : /************************************************************************/
11862 :
11863 : /** Advise driver of upcoming read requests.
11864 : *
11865 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11866 : *
11867 : * @return TRUE in case of success.
11868 : *
11869 : * @since GDAL 3.4
11870 : */
11871 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11872 : const size_t *count, CSLConstList papszOptions)
11873 : {
11874 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11875 : // coverity[var_deref_model]
11876 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
11877 : }
11878 :
11879 : /************************************************************************/
11880 : /* GDALMDArrayGetAttribute() */
11881 : /************************************************************************/
11882 :
11883 : /** Return an attribute by its name.
11884 : *
11885 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11886 : *
11887 : * The returned attribute must be freed with GDALAttributeRelease().
11888 : */
11889 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
11890 : {
11891 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11892 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11893 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
11894 119 : if (attr)
11895 110 : return new GDALAttributeHS(attr);
11896 9 : return nullptr;
11897 : }
11898 :
11899 : /************************************************************************/
11900 : /* GDALMDArrayGetAttributes() */
11901 : /************************************************************************/
11902 :
11903 : /** Return the list of attributes contained in this array.
11904 : *
11905 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11906 : * array itself needs to be freed, CPLFree() should be called (and
11907 : * GDALAttributeRelease() on individual array members).
11908 : *
11909 : * This is the same as the C++ method GDALMDArray::GetAttributes().
11910 : *
11911 : * @param hArray Array.
11912 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11913 : * @param papszOptions Driver specific options determining how attributes
11914 : * should be retrieved. Pass nullptr for default behavior.
11915 : *
11916 : * @return an array of *pnCount attributes.
11917 : */
11918 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
11919 : CSLConstList papszOptions)
11920 : {
11921 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11922 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11923 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
11924 : auto ret = static_cast<GDALAttributeH *>(
11925 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11926 187 : for (size_t i = 0; i < attrs.size(); i++)
11927 : {
11928 129 : ret[i] = new GDALAttributeHS(attrs[i]);
11929 : }
11930 58 : *pnCount = attrs.size();
11931 58 : return ret;
11932 : }
11933 :
11934 : /************************************************************************/
11935 : /* GDALMDArrayCreateAttribute() */
11936 : /************************************************************************/
11937 :
11938 : /** Create a attribute within an array.
11939 : *
11940 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
11941 : *
11942 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11943 : */
11944 150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
11945 : const char *pszName,
11946 : size_t nDimensions,
11947 : const GUInt64 *panDimensions,
11948 : GDALExtendedDataTypeH hEDT,
11949 : CSLConstList papszOptions)
11950 : {
11951 150 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11952 150 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11953 150 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11954 300 : std::vector<GUInt64> dims;
11955 150 : dims.reserve(nDimensions);
11956 175 : for (size_t i = 0; i < nDimensions; i++)
11957 25 : dims.push_back(panDimensions[i]);
11958 150 : auto ret = hArray->m_poImpl->CreateAttribute(
11959 450 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11960 150 : if (!ret)
11961 9 : return nullptr;
11962 141 : return new GDALAttributeHS(ret);
11963 : }
11964 :
11965 : /************************************************************************/
11966 : /* GDALMDArrayDeleteAttribute() */
11967 : /************************************************************************/
11968 :
11969 : /** Delete an attribute from an array.
11970 : *
11971 : * After this call, if a previously obtained instance of the deleted object
11972 : * is still alive, no method other than for freeing it should be invoked.
11973 : *
11974 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
11975 : *
11976 : * @return true in case of success.
11977 : * @since GDAL 3.8
11978 : */
11979 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
11980 : CSLConstList papszOptions)
11981 : {
11982 24 : VALIDATE_POINTER1(hArray, __func__, false);
11983 24 : VALIDATE_POINTER1(pszName, __func__, false);
11984 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
11985 24 : papszOptions);
11986 : }
11987 :
11988 : /************************************************************************/
11989 : /* GDALMDArrayGetRawNoDataValue() */
11990 : /************************************************************************/
11991 :
11992 : /** Return the nodata value as a "raw" value.
11993 : *
11994 : * The value returned might be nullptr in case of no nodata value. When
11995 : * a nodata value is registered, a non-nullptr will be returned whose size in
11996 : * bytes is GetDataType().GetSize().
11997 : *
11998 : * The returned value should not be modified or freed.
11999 : *
12000 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12001 : *
12002 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12003 : */
12004 74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12005 : {
12006 74 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12007 74 : return hArray->m_poImpl->GetRawNoDataValue();
12008 : }
12009 :
12010 : /************************************************************************/
12011 : /* GDALMDArrayGetNoDataValueAsDouble() */
12012 : /************************************************************************/
12013 :
12014 : /** Return the nodata value as a double.
12015 : *
12016 : * The value returned might be nullptr in case of no nodata value. When
12017 : * a nodata value is registered, a non-nullptr will be returned whose size in
12018 : * bytes is GetDataType().GetSize().
12019 : *
12020 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12021 : *
12022 : * @param hArray Array handle.
12023 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12024 : * if a nodata value exists and can be converted to double. Might be nullptr.
12025 : *
12026 : * @return the nodata value as a double. A 0.0 value might also indicate the
12027 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12028 : * will be set to false then).
12029 : */
12030 118 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12031 : int *pbHasNoDataValue)
12032 : {
12033 118 : VALIDATE_POINTER1(hArray, __func__, 0);
12034 118 : bool bHasNodataValue = false;
12035 118 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12036 118 : if (pbHasNoDataValue)
12037 118 : *pbHasNoDataValue = bHasNodataValue;
12038 118 : return ret;
12039 : }
12040 :
12041 : /************************************************************************/
12042 : /* GDALMDArrayGetNoDataValueAsInt64() */
12043 : /************************************************************************/
12044 :
12045 : /** Return the nodata value as a Int64.
12046 : *
12047 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12048 : *
12049 : * @param hArray Array handle.
12050 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12051 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12052 : *
12053 : * @return the nodata value as a Int64.
12054 : * @since GDAL 3.5
12055 : */
12056 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12057 : int *pbHasNoDataValue)
12058 : {
12059 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12060 11 : bool bHasNodataValue = false;
12061 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12062 11 : if (pbHasNoDataValue)
12063 11 : *pbHasNoDataValue = bHasNodataValue;
12064 11 : return ret;
12065 : }
12066 :
12067 : /************************************************************************/
12068 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12069 : /************************************************************************/
12070 :
12071 : /** Return the nodata value as a UInt64.
12072 : *
12073 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12074 : *
12075 : * @param hArray Array handle.
12076 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12077 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12078 : *
12079 : * @return the nodata value as a UInt64.
12080 : * @since GDAL 3.5
12081 : */
12082 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12083 : int *pbHasNoDataValue)
12084 : {
12085 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12086 7 : bool bHasNodataValue = false;
12087 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12088 7 : if (pbHasNoDataValue)
12089 7 : *pbHasNoDataValue = bHasNodataValue;
12090 7 : return ret;
12091 : }
12092 :
12093 : /************************************************************************/
12094 : /* GDALMDArraySetRawNoDataValue() */
12095 : /************************************************************************/
12096 :
12097 : /** Set the nodata value as a "raw" value.
12098 : *
12099 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12100 : * void*).
12101 : *
12102 : * @return TRUE in case of success.
12103 : */
12104 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12105 : {
12106 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12107 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12108 : }
12109 :
12110 : /************************************************************************/
12111 : /* GDALMDArraySetNoDataValueAsDouble() */
12112 : /************************************************************************/
12113 :
12114 : /** Set the nodata value as a double.
12115 : *
12116 : * If the natural data type of the attribute/array is not double, type
12117 : * conversion will occur to the type returned by GetDataType().
12118 : *
12119 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12120 : *
12121 : * @return TRUE in case of success.
12122 : */
12123 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12124 : {
12125 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12126 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12127 : }
12128 :
12129 : /************************************************************************/
12130 : /* GDALMDArraySetNoDataValueAsInt64() */
12131 : /************************************************************************/
12132 :
12133 : /** Set the nodata value as a Int64.
12134 : *
12135 : * If the natural data type of the attribute/array is not Int64, type conversion
12136 : * will occur to the type returned by GetDataType().
12137 : *
12138 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12139 : *
12140 : * @return TRUE in case of success.
12141 : * @since GDAL 3.5
12142 : */
12143 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12144 : {
12145 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12146 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12147 : }
12148 :
12149 : /************************************************************************/
12150 : /* GDALMDArraySetNoDataValueAsUInt64() */
12151 : /************************************************************************/
12152 :
12153 : /** Set the nodata value as a UInt64.
12154 : *
12155 : * If the natural data type of the attribute/array is not UInt64, type
12156 : * conversion will occur to the type returned by GetDataType().
12157 : *
12158 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12159 : *
12160 : * @return TRUE in case of success.
12161 : * @since GDAL 3.5
12162 : */
12163 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12164 : uint64_t nNoDataValue)
12165 : {
12166 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12167 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12168 : }
12169 :
12170 : /************************************************************************/
12171 : /* GDALMDArrayResize() */
12172 : /************************************************************************/
12173 :
12174 : /** Resize an array to new dimensions.
12175 : *
12176 : * Not all drivers may allow this operation, and with restrictions (e.g.
12177 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12178 : *
12179 : * Resizing a dimension used in other arrays will cause those other arrays
12180 : * to be resized.
12181 : *
12182 : * This is the same as the C++ method GDALMDArray::Resize().
12183 : *
12184 : * @param hArray Array.
12185 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12186 : * new size of each indexing dimension.
12187 : * @param papszOptions Options. (Driver specific)
12188 : * @return true in case of success.
12189 : * @since GDAL 3.7
12190 : */
12191 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12192 : CSLConstList papszOptions)
12193 : {
12194 42 : VALIDATE_POINTER1(hArray, __func__, false);
12195 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12196 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12197 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12198 : {
12199 83 : anNewDimSizes[i] = panNewDimSizes[i];
12200 : }
12201 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12202 : }
12203 :
12204 : /************************************************************************/
12205 : /* GDALMDArraySetScale() */
12206 : /************************************************************************/
12207 :
12208 : /** Set the scale value to apply to raw values.
12209 : *
12210 : * unscaled_value = raw_value * GetScale() + GetOffset()
12211 : *
12212 : * This is the same as the C++ method GDALMDArray::SetScale().
12213 : *
12214 : * @return TRUE in case of success.
12215 : */
12216 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12217 : {
12218 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12219 0 : return hArray->m_poImpl->SetScale(dfScale);
12220 : }
12221 :
12222 : /************************************************************************/
12223 : /* GDALMDArraySetScaleEx() */
12224 : /************************************************************************/
12225 :
12226 : /** Set the scale value to apply to raw values.
12227 : *
12228 : * unscaled_value = raw_value * GetScale() + GetOffset()
12229 : *
12230 : * This is the same as the C++ method GDALMDArray::SetScale().
12231 : *
12232 : * @return TRUE in case of success.
12233 : * @since GDAL 3.3
12234 : */
12235 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12236 : GDALDataType eStorageType)
12237 : {
12238 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12239 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12240 : }
12241 :
12242 : /************************************************************************/
12243 : /* GDALMDArraySetOffset() */
12244 : /************************************************************************/
12245 :
12246 : /** Set the scale value to apply to raw values.
12247 : *
12248 : * unscaled_value = raw_value * GetScale() + GetOffset()
12249 : *
12250 : * This is the same as the C++ method GDALMDArray::SetOffset().
12251 : *
12252 : * @return TRUE in case of success.
12253 : */
12254 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12255 : {
12256 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12257 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12258 : }
12259 :
12260 : /************************************************************************/
12261 : /* GDALMDArraySetOffsetEx() */
12262 : /************************************************************************/
12263 :
12264 : /** Set the scale value to apply to raw values.
12265 : *
12266 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12267 : *
12268 : * This is the same as the C++ method GDALMDArray::SetOffset().
12269 : *
12270 : * @return TRUE in case of success.
12271 : * @since GDAL 3.3
12272 : */
12273 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12274 : GDALDataType eStorageType)
12275 : {
12276 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12277 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12278 : }
12279 :
12280 : /************************************************************************/
12281 : /* GDALMDArrayGetScale() */
12282 : /************************************************************************/
12283 :
12284 : /** Get the scale value to apply to raw values.
12285 : *
12286 : * unscaled_value = raw_value * GetScale() + GetOffset()
12287 : *
12288 : * This is the same as the C++ method GDALMDArray::GetScale().
12289 : *
12290 : * @return the scale value
12291 : */
12292 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12293 : {
12294 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12295 103 : bool bHasValue = false;
12296 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12297 103 : if (pbHasValue)
12298 103 : *pbHasValue = bHasValue;
12299 103 : return dfRet;
12300 : }
12301 :
12302 : /************************************************************************/
12303 : /* GDALMDArrayGetScaleEx() */
12304 : /************************************************************************/
12305 :
12306 : /** Get the scale value to apply to raw values.
12307 : *
12308 : * unscaled_value = raw_value * GetScale() + GetScale()
12309 : *
12310 : * This is the same as the C++ method GDALMDArray::GetScale().
12311 : *
12312 : * @return the scale value
12313 : * @since GDAL 3.3
12314 : */
12315 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12316 : GDALDataType *peStorageType)
12317 : {
12318 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12319 5 : bool bHasValue = false;
12320 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12321 5 : if (pbHasValue)
12322 5 : *pbHasValue = bHasValue;
12323 5 : return dfRet;
12324 : }
12325 :
12326 : /************************************************************************/
12327 : /* GDALMDArrayGetOffset() */
12328 : /************************************************************************/
12329 :
12330 : /** Get the scale value to apply to raw values.
12331 : *
12332 : * unscaled_value = raw_value * GetScale() + GetOffset()
12333 : *
12334 : * This is the same as the C++ method GDALMDArray::GetOffset().
12335 : *
12336 : * @return the scale value
12337 : */
12338 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12339 : {
12340 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12341 100 : bool bHasValue = false;
12342 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12343 100 : if (pbHasValue)
12344 100 : *pbHasValue = bHasValue;
12345 100 : return dfRet;
12346 : }
12347 :
12348 : /************************************************************************/
12349 : /* GDALMDArrayGetOffsetEx() */
12350 : /************************************************************************/
12351 :
12352 : /** Get the scale value to apply to raw values.
12353 : *
12354 : * unscaled_value = raw_value * GetScale() + GetOffset()
12355 : *
12356 : * This is the same as the C++ method GDALMDArray::GetOffset().
12357 : *
12358 : * @return the scale value
12359 : * @since GDAL 3.3
12360 : */
12361 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12362 : GDALDataType *peStorageType)
12363 : {
12364 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12365 5 : bool bHasValue = false;
12366 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12367 5 : if (pbHasValue)
12368 5 : *pbHasValue = bHasValue;
12369 5 : return dfRet;
12370 : }
12371 :
12372 : /************************************************************************/
12373 : /* GDALMDArrayGetBlockSize() */
12374 : /************************************************************************/
12375 :
12376 : /** Return the "natural" block size of the array along all dimensions.
12377 : *
12378 : * Some drivers might organize the array in tiles/blocks and reading/writing
12379 : * aligned on those tile/block boundaries will be more efficient.
12380 : *
12381 : * The returned number of elements in the vector is the same as
12382 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12383 : * the natural block size along the considered dimension.
12384 : * "Flat" arrays will typically return a vector of values set to 0.
12385 : *
12386 : * The default implementation will return a vector of values set to 0.
12387 : *
12388 : * This method is used by GetProcessingChunkSize().
12389 : *
12390 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12391 : * theoretical case of a 32-bit platform, this might exceed its size_t
12392 : * allocation capabilities.
12393 : *
12394 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12395 : *
12396 : * @return the block size, in number of elements along each dimension.
12397 : */
12398 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12399 : {
12400 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12401 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12402 93 : auto res = hArray->m_poImpl->GetBlockSize();
12403 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12404 285 : for (size_t i = 0; i < res.size(); i++)
12405 : {
12406 192 : ret[i] = res[i];
12407 : }
12408 93 : *pnCount = res.size();
12409 93 : return ret;
12410 : }
12411 :
12412 : /***********************************************************************/
12413 : /* GDALMDArrayGetProcessingChunkSize() */
12414 : /************************************************************************/
12415 :
12416 : /** \brief Return an optimal chunk size for read/write operations, given the
12417 : * natural block size and memory constraints specified.
12418 : *
12419 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12420 : * multiple of those returned by GetBlockSize() (unless the block define by
12421 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12422 : * returned by this method).
12423 : *
12424 : * This is the same as the C++ method
12425 : * GDALAbstractMDArray::GetProcessingChunkSize().
12426 : *
12427 : * @param hArray Array.
12428 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12429 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12430 : * chunk.
12431 : *
12432 : * @return the chunk size, in number of elements along each dimension.
12433 : */
12434 :
12435 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12436 : size_t nMaxChunkMemory)
12437 : {
12438 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12439 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12440 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12441 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12442 3 : for (size_t i = 0; i < res.size(); i++)
12443 : {
12444 2 : ret[i] = res[i];
12445 : }
12446 1 : *pnCount = res.size();
12447 1 : return ret;
12448 : }
12449 :
12450 : /************************************************************************/
12451 : /* GDALMDArrayGetStructuralInfo() */
12452 : /************************************************************************/
12453 :
12454 : /** Return structural information on the array.
12455 : *
12456 : * This may be the compression, etc..
12457 : *
12458 : * The return value should not be freed and is valid until GDALMDArray is
12459 : * released or this function called again.
12460 : *
12461 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12462 : */
12463 6 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12464 : {
12465 6 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12466 6 : return hArray->m_poImpl->GetStructuralInfo();
12467 : }
12468 :
12469 : /************************************************************************/
12470 : /* GDALMDArrayGetView() */
12471 : /************************************************************************/
12472 :
12473 : /** Return a view of the array using slicing or field access.
12474 : *
12475 : * The returned object should be released with GDALMDArrayRelease().
12476 : *
12477 : * This is the same as the C++ method GDALMDArray::GetView().
12478 : */
12479 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12480 : {
12481 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12482 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12483 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12484 430 : if (!sliced)
12485 22 : return nullptr;
12486 408 : return new GDALMDArrayHS(sliced);
12487 : }
12488 :
12489 : /************************************************************************/
12490 : /* GDALMDArrayTranspose() */
12491 : /************************************************************************/
12492 :
12493 : /** Return a view of the array whose axis have been reordered.
12494 : *
12495 : * The returned object should be released with GDALMDArrayRelease().
12496 : *
12497 : * This is the same as the C++ method GDALMDArray::Transpose().
12498 : */
12499 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12500 : const int *panMapNewAxisToOldAxis)
12501 : {
12502 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12503 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12504 44 : if (nNewAxisCount)
12505 : {
12506 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12507 : nNewAxisCount * sizeof(int));
12508 : }
12509 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12510 44 : if (!reordered)
12511 7 : return nullptr;
12512 37 : return new GDALMDArrayHS(reordered);
12513 : }
12514 :
12515 : /************************************************************************/
12516 : /* GDALMDArrayGetUnscaled() */
12517 : /************************************************************************/
12518 :
12519 : /** Return an array that is the unscaled version of the current one.
12520 : *
12521 : * That is each value of the unscaled array will be
12522 : * unscaled_value = raw_value * GetScale() + GetOffset()
12523 : *
12524 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
12525 : * from unscaled values to raw values.
12526 : *
12527 : * The returned object should be released with GDALMDArrayRelease().
12528 : *
12529 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
12530 : */
12531 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
12532 : {
12533 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12534 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
12535 13 : if (!unscaled)
12536 0 : return nullptr;
12537 13 : return new GDALMDArrayHS(unscaled);
12538 : }
12539 :
12540 : /************************************************************************/
12541 : /* GDALMDArrayGetMask() */
12542 : /************************************************************************/
12543 :
12544 : /** Return an array that is a mask for the current array
12545 : *
12546 : * This array will be of type Byte, with values set to 0 to indicate invalid
12547 : * pixels of the current array, and values set to 1 to indicate valid pixels.
12548 : *
12549 : * The returned object should be released with GDALMDArrayRelease().
12550 : *
12551 : * This is the same as the C++ method GDALMDArray::GetMask().
12552 : */
12553 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
12554 : {
12555 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12556 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
12557 35 : if (!unscaled)
12558 7 : return nullptr;
12559 28 : return new GDALMDArrayHS(unscaled);
12560 : }
12561 :
12562 : /************************************************************************/
12563 : /* GDALMDArrayGetResampled() */
12564 : /************************************************************************/
12565 :
12566 : /** Return an array that is a resampled / reprojected view of the current array
12567 : *
12568 : * This is the same as the C++ method GDALMDArray::GetResampled().
12569 : *
12570 : * Currently this method can only resample along the last 2 dimensions, unless
12571 : * orthorectifying a NASA EMIT dataset.
12572 : *
12573 : * The returned object should be released with GDALMDArrayRelease().
12574 : *
12575 : * @since 3.4
12576 : */
12577 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
12578 : const GDALDimensionH *pahNewDims,
12579 : GDALRIOResampleAlg resampleAlg,
12580 : OGRSpatialReferenceH hTargetSRS,
12581 : CSLConstList papszOptions)
12582 : {
12583 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12584 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
12585 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
12586 112 : for (size_t i = 0; i < nNewDimCount; ++i)
12587 : {
12588 78 : if (pahNewDims[i])
12589 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
12590 : }
12591 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
12592 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
12593 68 : papszOptions);
12594 34 : if (!poNewArray)
12595 8 : return nullptr;
12596 26 : return new GDALMDArrayHS(poNewArray);
12597 : }
12598 :
12599 : /************************************************************************/
12600 : /* GDALMDArraySetUnit() */
12601 : /************************************************************************/
12602 :
12603 : /** Set the variable unit.
12604 : *
12605 : * Values should conform as much as possible with those allowed by
12606 : * the NetCDF CF conventions:
12607 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12608 : * but others might be returned.
12609 : *
12610 : * Few examples are "meter", "degrees", "second", ...
12611 : * Empty value means unknown.
12612 : *
12613 : * This is the same as the C function GDALMDArraySetUnit()
12614 : *
12615 : * @param hArray array.
12616 : * @param pszUnit unit name.
12617 : * @return TRUE in case of success.
12618 : */
12619 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
12620 : {
12621 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12622 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
12623 : }
12624 :
12625 : /************************************************************************/
12626 : /* GDALMDArrayGetUnit() */
12627 : /************************************************************************/
12628 :
12629 : /** Return the array unit.
12630 : *
12631 : * Values should conform as much as possible with those allowed by
12632 : * the NetCDF CF conventions:
12633 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12634 : * but others might be returned.
12635 : *
12636 : * Few examples are "meter", "degrees", "second", ...
12637 : * Empty value means unknown.
12638 : *
12639 : * The return value should not be freed and is valid until GDALMDArray is
12640 : * released or this function called again.
12641 : *
12642 : * This is the same as the C++ method GDALMDArray::GetUnit().
12643 : */
12644 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
12645 : {
12646 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12647 111 : return hArray->m_poImpl->GetUnit().c_str();
12648 : }
12649 :
12650 : /************************************************************************/
12651 : /* GDALMDArrayGetSpatialRef() */
12652 : /************************************************************************/
12653 :
12654 : /** Assign a spatial reference system object to the array.
12655 : *
12656 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
12657 : * @return TRUE in case of success.
12658 : */
12659 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
12660 : {
12661 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12662 60 : return hArray->m_poImpl->SetSpatialRef(
12663 60 : OGRSpatialReference::FromHandle(hSRS));
12664 : }
12665 :
12666 : /************************************************************************/
12667 : /* GDALMDArrayGetSpatialRef() */
12668 : /************************************************************************/
12669 :
12670 : /** Return the spatial reference system object associated with the array.
12671 : *
12672 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
12673 : *
12674 : * The returned object must be freed with OSRDestroySpatialReference().
12675 : */
12676 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
12677 : {
12678 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12679 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
12680 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
12681 : }
12682 :
12683 : /************************************************************************/
12684 : /* GDALMDArrayGetStatistics() */
12685 : /************************************************************************/
12686 :
12687 : /**
12688 : * \brief Fetch statistics.
12689 : *
12690 : * This is the same as the C++ method GDALMDArray::GetStatistics().
12691 : *
12692 : * @since GDAL 3.2
12693 : */
12694 :
12695 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
12696 : int bApproxOK, int bForce, double *pdfMin,
12697 : double *pdfMax, double *pdfMean,
12698 : double *pdfStdDev, GUInt64 *pnValidCount,
12699 : GDALProgressFunc pfnProgress,
12700 : void *pProgressData)
12701 : {
12702 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
12703 30 : return hArray->m_poImpl->GetStatistics(
12704 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
12705 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
12706 : }
12707 :
12708 : /************************************************************************/
12709 : /* GDALMDArrayComputeStatistics() */
12710 : /************************************************************************/
12711 :
12712 : /**
12713 : * \brief Compute statistics.
12714 : *
12715 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12716 : *
12717 : * @since GDAL 3.2
12718 : * @see GDALMDArrayComputeStatisticsEx()
12719 : */
12720 :
12721 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12722 : int bApproxOK, double *pdfMin, double *pdfMax,
12723 : double *pdfMean, double *pdfStdDev,
12724 : GUInt64 *pnValidCount,
12725 : GDALProgressFunc pfnProgress,
12726 : void *pProgressData)
12727 : {
12728 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12729 0 : return hArray->m_poImpl->ComputeStatistics(
12730 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12731 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
12732 : }
12733 :
12734 : /************************************************************************/
12735 : /* GDALMDArrayComputeStatisticsEx() */
12736 : /************************************************************************/
12737 :
12738 : /**
12739 : * \brief Compute statistics.
12740 : *
12741 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
12742 : *
12743 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12744 : *
12745 : * @since GDAL 3.8
12746 : */
12747 :
12748 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12749 : int bApproxOK, double *pdfMin,
12750 : double *pdfMax, double *pdfMean,
12751 : double *pdfStdDev, GUInt64 *pnValidCount,
12752 : GDALProgressFunc pfnProgress,
12753 : void *pProgressData,
12754 : CSLConstList papszOptions)
12755 : {
12756 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12757 8 : return hArray->m_poImpl->ComputeStatistics(
12758 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12759 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
12760 : }
12761 :
12762 : /************************************************************************/
12763 : /* GDALMDArrayGetCoordinateVariables() */
12764 : /************************************************************************/
12765 :
12766 : /** Return coordinate variables.
12767 : *
12768 : * The returned array must be freed with GDALReleaseArrays(). If only the array
12769 : * itself needs to be freed, CPLFree() should be called (and
12770 : * GDALMDArrayRelease() on individual array members).
12771 : *
12772 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
12773 : *
12774 : * @param hArray Array.
12775 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12776 : *
12777 : * @return an array of *pnCount arrays.
12778 : * @since 3.4
12779 : */
12780 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
12781 : size_t *pnCount)
12782 : {
12783 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12784 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12785 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
12786 : auto ret = static_cast<GDALMDArrayH *>(
12787 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
12788 29 : for (size_t i = 0; i < coordinates.size(); i++)
12789 : {
12790 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
12791 : }
12792 13 : *pnCount = coordinates.size();
12793 13 : return ret;
12794 : }
12795 :
12796 : /************************************************************************/
12797 : /* GDALMDArrayGetGridded() */
12798 : /************************************************************************/
12799 :
12800 : /** Return a gridded array from scattered point data, that is from an array
12801 : * whose last dimension is the indexing variable of X and Y arrays.
12802 : *
12803 : * The returned object should be released with GDALMDArrayRelease().
12804 : *
12805 : * This is the same as the C++ method GDALMDArray::GetGridded().
12806 : *
12807 : * @since GDAL 3.7
12808 : */
12809 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
12810 : const char *pszGridOptions,
12811 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
12812 : CSLConstList papszOptions)
12813 : {
12814 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12815 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
12816 22 : auto gridded = hArray->m_poImpl->GetGridded(
12817 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
12818 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
12819 22 : if (!gridded)
12820 19 : return nullptr;
12821 3 : return new GDALMDArrayHS(gridded);
12822 : }
12823 :
12824 : /************************************************************************/
12825 : /* GDALMDArrayGetMeshGrid() */
12826 : /************************************************************************/
12827 :
12828 : /** Return a list of multidimensional arrays from a list of one-dimensional
12829 : * arrays.
12830 : *
12831 : * This is typically used to transform one-dimensional longitude, latitude
12832 : * arrays into 2D ones.
12833 : *
12834 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
12835 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
12836 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
12837 : * repeated to fill the matrix along the first dimension for x1, the second
12838 : * for x2 and so on.
12839 : *
12840 : * For example, if x = [1, 2], and y = [3, 4, 5],
12841 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
12842 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
12843 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
12844 : *
12845 : * and
12846 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
12847 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
12848 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
12849 : *
12850 : * The currently supported options are:
12851 : * <ul>
12852 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
12853 : * output.
12854 : * </li>
12855 : * </ul>
12856 : *
12857 : * This is the same as
12858 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
12859 : * function.
12860 : *
12861 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
12862 : * If only the array itself needs to be freed, CPLFree() should be called
12863 : * (and GDALMDArrayRelease() on individual array members).
12864 : *
12865 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
12866 : *
12867 : * @param pahInputArrays Input arrays
12868 : * @param nCountInputArrays Number of input arrays
12869 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
12870 : * @param papszOptions NULL, or NULL terminated list of options.
12871 : *
12872 : * @return an array of *pnCountOutputArrays arrays.
12873 : * @since 3.10
12874 : */
12875 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
12876 : size_t nCountInputArrays,
12877 : size_t *pnCountOutputArrays,
12878 : CSLConstList papszOptions)
12879 : {
12880 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
12881 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
12882 :
12883 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
12884 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
12885 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
12886 :
12887 : const auto apoOutputArrays =
12888 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
12889 : auto ret = static_cast<GDALMDArrayH *>(
12890 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
12891 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
12892 : {
12893 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
12894 : }
12895 7 : *pnCountOutputArrays = apoOutputArrays.size();
12896 7 : return ret;
12897 : }
12898 :
12899 : /************************************************************************/
12900 : /* GDALReleaseArrays() */
12901 : /************************************************************************/
12902 :
12903 : /** Free the return of GDALMDArrayGetCoordinateVariables()
12904 : *
12905 : * @param arrays return pointer of above methods
12906 : * @param nCount *pnCount value returned by above methods
12907 : */
12908 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
12909 : {
12910 46 : for (size_t i = 0; i < nCount; i++)
12911 : {
12912 26 : delete arrays[i];
12913 : }
12914 20 : CPLFree(arrays);
12915 20 : }
12916 :
12917 : /************************************************************************/
12918 : /* GDALMDArrayCache() */
12919 : /************************************************************************/
12920 :
12921 : /**
12922 : * \brief Cache the content of the array into an auxiliary filename.
12923 : *
12924 : * This is the same as the C++ method GDALMDArray::Cache().
12925 : *
12926 : * @since GDAL 3.4
12927 : */
12928 :
12929 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
12930 : {
12931 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12932 7 : return hArray->m_poImpl->Cache(papszOptions);
12933 : }
12934 :
12935 : /************************************************************************/
12936 : /* GDALMDArrayRename() */
12937 : /************************************************************************/
12938 :
12939 : /** Rename the array.
12940 : *
12941 : * This is not implemented by all drivers.
12942 : *
12943 : * Drivers known to implement it: MEM, netCDF, Zarr.
12944 : *
12945 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
12946 : *
12947 : * @return true in case of success
12948 : * @since GDAL 3.8
12949 : */
12950 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
12951 : {
12952 28 : VALIDATE_POINTER1(hArray, __func__, false);
12953 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
12954 28 : return hArray->m_poImpl->Rename(pszNewName);
12955 : }
12956 :
12957 : /************************************************************************/
12958 : /* GDALAttributeRelease() */
12959 : /************************************************************************/
12960 :
12961 : /** Release the GDAL in-memory object associated with a GDALAttribute.
12962 : *
12963 : * Note: when applied on a object coming from a driver, this does not
12964 : * destroy the object in the file, database, etc...
12965 : */
12966 714 : void GDALAttributeRelease(GDALAttributeH hAttr)
12967 : {
12968 714 : delete hAttr;
12969 714 : }
12970 :
12971 : /************************************************************************/
12972 : /* GDALAttributeGetName() */
12973 : /************************************************************************/
12974 :
12975 : /** Return the name of the attribute.
12976 : *
12977 : * The returned pointer is valid until hAttr is released.
12978 : *
12979 : * This is the same as the C++ method GDALAttribute::GetName().
12980 : */
12981 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
12982 : {
12983 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12984 361 : return hAttr->m_poImpl->GetName().c_str();
12985 : }
12986 :
12987 : /************************************************************************/
12988 : /* GDALAttributeGetFullName() */
12989 : /************************************************************************/
12990 :
12991 : /** Return the full name of the attribute.
12992 : *
12993 : * The returned pointer is valid until hAttr is released.
12994 : *
12995 : * This is the same as the C++ method GDALAttribute::GetFullName().
12996 : */
12997 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
12998 : {
12999 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13000 49 : return hAttr->m_poImpl->GetFullName().c_str();
13001 : }
13002 :
13003 : /************************************************************************/
13004 : /* GDALAttributeGetTotalElementsCount() */
13005 : /************************************************************************/
13006 :
13007 : /** Return the total number of values in the attribute.
13008 : *
13009 : * This is the same as the C++ method
13010 : * GDALAbstractMDArray::GetTotalElementsCount()
13011 : */
13012 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13013 : {
13014 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13015 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13016 : }
13017 :
13018 : /************************************************************************/
13019 : /* GDALAttributeGetDimensionCount() */
13020 : /************************************************************************/
13021 :
13022 : /** Return the number of dimensions.
13023 : *
13024 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13025 : */
13026 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13027 : {
13028 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13029 12 : return hAttr->m_poImpl->GetDimensionCount();
13030 : }
13031 :
13032 : /************************************************************************/
13033 : /* GDALAttributeGetDimensionsSize() */
13034 : /************************************************************************/
13035 :
13036 : /** Return the dimension sizes of the attribute.
13037 : *
13038 : * The returned array must be freed with CPLFree()
13039 : *
13040 : * @param hAttr Attribute.
13041 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13042 : *
13043 : * @return an array of *pnCount values.
13044 : */
13045 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13046 : {
13047 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13048 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13049 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13050 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13051 22 : for (size_t i = 0; i < dims.size(); i++)
13052 : {
13053 11 : ret[i] = dims[i]->GetSize();
13054 : }
13055 11 : *pnCount = dims.size();
13056 11 : return ret;
13057 : }
13058 :
13059 : /************************************************************************/
13060 : /* GDALAttributeGetDataType() */
13061 : /************************************************************************/
13062 :
13063 : /** Return the data type
13064 : *
13065 : * The return must be freed with GDALExtendedDataTypeRelease().
13066 : */
13067 427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13068 : {
13069 427 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13070 : return new GDALExtendedDataTypeHS(
13071 427 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13072 : }
13073 :
13074 : /************************************************************************/
13075 : /* GDALAttributeReadAsRaw() */
13076 : /************************************************************************/
13077 :
13078 : /** Return the raw value of an attribute.
13079 : *
13080 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13081 : *
13082 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13083 : *
13084 : * @param hAttr Attribute.
13085 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13086 : *
13087 : * @return a buffer of *pnSize bytes.
13088 : */
13089 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13090 : {
13091 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13092 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13093 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13094 6 : *pnSize = res.size();
13095 6 : auto ret = res.StealData();
13096 6 : if (!ret)
13097 : {
13098 0 : *pnSize = 0;
13099 0 : return nullptr;
13100 : }
13101 6 : return ret;
13102 : }
13103 :
13104 : /************************************************************************/
13105 : /* GDALAttributeFreeRawResult() */
13106 : /************************************************************************/
13107 :
13108 : /** Free the return of GDALAttributeAsRaw()
13109 : */
13110 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13111 : CPL_UNUSED size_t nSize)
13112 : {
13113 6 : VALIDATE_POINTER0(hAttr, __func__);
13114 6 : if (raw)
13115 : {
13116 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13117 6 : const auto nDTSize(dt.GetSize());
13118 6 : GByte *pabyPtr = raw;
13119 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13120 6 : CPLAssert(nSize == nDTSize * nEltCount);
13121 12 : for (size_t i = 0; i < nEltCount; ++i)
13122 : {
13123 6 : dt.FreeDynamicMemory(pabyPtr);
13124 6 : pabyPtr += nDTSize;
13125 : }
13126 6 : CPLFree(raw);
13127 : }
13128 : }
13129 :
13130 : /************************************************************************/
13131 : /* GDALAttributeReadAsString() */
13132 : /************************************************************************/
13133 :
13134 : /** Return the value of an attribute as a string.
13135 : *
13136 : * The returned string should not be freed, and its lifetime does not
13137 : * excess a next call to ReadAsString() on the same object, or the deletion
13138 : * of the object itself.
13139 : *
13140 : * This function will only return the first element if there are several.
13141 : *
13142 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13143 : *
13144 : * @return a string, or nullptr.
13145 : */
13146 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13147 : {
13148 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13149 107 : return hAttr->m_poImpl->ReadAsString();
13150 : }
13151 :
13152 : /************************************************************************/
13153 : /* GDALAttributeReadAsInt() */
13154 : /************************************************************************/
13155 :
13156 : /** Return the value of an attribute as a integer.
13157 : *
13158 : * This function will only return the first element if there are several.
13159 : *
13160 : * It can fail if its value can not be converted to integer.
13161 : *
13162 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13163 : *
13164 : * @return a integer, or INT_MIN in case of error.
13165 : */
13166 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13167 : {
13168 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13169 22 : return hAttr->m_poImpl->ReadAsInt();
13170 : }
13171 :
13172 : /************************************************************************/
13173 : /* GDALAttributeReadAsInt64() */
13174 : /************************************************************************/
13175 :
13176 : /** Return the value of an attribute as a int64_t.
13177 : *
13178 : * This function will only return the first element if there are several.
13179 : *
13180 : * It can fail if its value can not be converted to integer.
13181 : *
13182 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13183 : *
13184 : * @return an int64_t, or INT64_MIN in case of error.
13185 : */
13186 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13187 : {
13188 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13189 15 : return hAttr->m_poImpl->ReadAsInt64();
13190 : }
13191 :
13192 : /************************************************************************/
13193 : /* GDALAttributeReadAsDouble() */
13194 : /************************************************************************/
13195 :
13196 : /** Return the value of an attribute as a double.
13197 : *
13198 : * This function will only return the first element if there are several.
13199 : *
13200 : * It can fail if its value can not be converted to double.
13201 : *
13202 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13203 : *
13204 : * @return a double value.
13205 : */
13206 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13207 : {
13208 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13209 40 : return hAttr->m_poImpl->ReadAsDouble();
13210 : }
13211 :
13212 : /************************************************************************/
13213 : /* GDALAttributeReadAsStringArray() */
13214 : /************************************************************************/
13215 :
13216 : /** Return the value of an attribute as an array of strings.
13217 : *
13218 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13219 : *
13220 : * The return value must be freed with CSLDestroy().
13221 : */
13222 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13223 : {
13224 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13225 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13226 : }
13227 :
13228 : /************************************************************************/
13229 : /* GDALAttributeReadAsIntArray() */
13230 : /************************************************************************/
13231 :
13232 : /** Return the value of an attribute as an array of integers.
13233 : *
13234 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13235 : *
13236 : * @param hAttr Attribute
13237 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13238 : * @return array to be freed with CPLFree(), or nullptr.
13239 : */
13240 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13241 : {
13242 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13243 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13244 15 : *pnCount = 0;
13245 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13246 15 : if (tmp.empty())
13247 0 : return nullptr;
13248 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13249 15 : if (!ret)
13250 0 : return nullptr;
13251 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13252 15 : *pnCount = tmp.size();
13253 15 : return ret;
13254 : }
13255 :
13256 : /************************************************************************/
13257 : /* GDALAttributeReadAsInt64Array() */
13258 : /************************************************************************/
13259 :
13260 : /** Return the value of an attribute as an array of int64_t.
13261 : *
13262 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13263 : *
13264 : * @param hAttr Attribute
13265 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13266 : * @return array to be freed with CPLFree(), or nullptr.
13267 : */
13268 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13269 : {
13270 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13271 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13272 14 : *pnCount = 0;
13273 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13274 14 : if (tmp.empty())
13275 0 : return nullptr;
13276 : auto ret = static_cast<int64_t *>(
13277 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13278 14 : if (!ret)
13279 0 : return nullptr;
13280 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13281 14 : *pnCount = tmp.size();
13282 14 : return ret;
13283 : }
13284 :
13285 : /************************************************************************/
13286 : /* GDALAttributeReadAsDoubleArray() */
13287 : /************************************************************************/
13288 :
13289 : /** Return the value of an attribute as an array of doubles.
13290 : *
13291 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13292 : *
13293 : * @param hAttr Attribute
13294 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13295 : * @return array to be freed with CPLFree(), or nullptr.
13296 : */
13297 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13298 : {
13299 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13300 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13301 29 : *pnCount = 0;
13302 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13303 29 : if (tmp.empty())
13304 0 : return nullptr;
13305 : auto ret =
13306 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13307 29 : if (!ret)
13308 0 : return nullptr;
13309 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13310 29 : *pnCount = tmp.size();
13311 29 : return ret;
13312 : }
13313 :
13314 : /************************************************************************/
13315 : /* GDALAttributeWriteRaw() */
13316 : /************************************************************************/
13317 :
13318 : /** Write an attribute from raw values expressed in GetDataType()
13319 : *
13320 : * The values should be provided in the type of GetDataType() and there should
13321 : * be exactly GetTotalElementsCount() of them.
13322 : * If GetDataType() is a string, each value should be a char* pointer.
13323 : *
13324 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13325 : *
13326 : * @param hAttr Attribute
13327 : * @param pabyValue Buffer of nLen bytes.
13328 : * @param nLength Size of pabyValue in bytes. Should be equal to
13329 : * GetTotalElementsCount() * GetDataType().GetSize()
13330 : * @return TRUE in case of success.
13331 : */
13332 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13333 : size_t nLength)
13334 : {
13335 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13336 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13337 : }
13338 :
13339 : /************************************************************************/
13340 : /* GDALAttributeWriteString() */
13341 : /************************************************************************/
13342 :
13343 : /** Write an attribute from a string value.
13344 : *
13345 : * Type conversion will be performed if needed. If the attribute contains
13346 : * multiple values, only the first one will be updated.
13347 : *
13348 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13349 : *
13350 : * @param hAttr Attribute
13351 : * @param pszVal Pointer to a string.
13352 : * @return TRUE in case of success.
13353 : */
13354 175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13355 : {
13356 175 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13357 175 : return hAttr->m_poImpl->Write(pszVal);
13358 : }
13359 :
13360 : /************************************************************************/
13361 : /* GDALAttributeWriteInt() */
13362 : /************************************************************************/
13363 :
13364 : /** Write an attribute from a integer value.
13365 : *
13366 : * Type conversion will be performed if needed. If the attribute contains
13367 : * multiple values, only the first one will be updated.
13368 : *
13369 : * This is the same as the C++ method GDALAttribute::WriteInt()
13370 : *
13371 : * @param hAttr Attribute
13372 : * @param nVal Value.
13373 : * @return TRUE in case of success.
13374 : */
13375 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13376 : {
13377 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13378 22 : return hAttr->m_poImpl->WriteInt(nVal);
13379 : }
13380 :
13381 : /************************************************************************/
13382 : /* GDALAttributeWriteInt64() */
13383 : /************************************************************************/
13384 :
13385 : /** Write an attribute from an int64_t value.
13386 : *
13387 : * Type conversion will be performed if needed. If the attribute contains
13388 : * multiple values, only the first one will be updated.
13389 : *
13390 : * This is the same as the C++ method GDALAttribute::WriteLong()
13391 : *
13392 : * @param hAttr Attribute
13393 : * @param nVal Value.
13394 : * @return TRUE in case of success.
13395 : */
13396 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13397 : {
13398 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13399 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13400 : }
13401 :
13402 : /************************************************************************/
13403 : /* GDALAttributeWriteDouble() */
13404 : /************************************************************************/
13405 :
13406 : /** Write an attribute from a double value.
13407 : *
13408 : * Type conversion will be performed if needed. If the attribute contains
13409 : * multiple values, only the first one will be updated.
13410 : *
13411 : * This is the same as the C++ method GDALAttribute::Write(double);
13412 : *
13413 : * @param hAttr Attribute
13414 : * @param dfVal Value.
13415 : *
13416 : * @return TRUE in case of success.
13417 : */
13418 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13419 : {
13420 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13421 11 : return hAttr->m_poImpl->Write(dfVal);
13422 : }
13423 :
13424 : /************************************************************************/
13425 : /* GDALAttributeWriteStringArray() */
13426 : /************************************************************************/
13427 :
13428 : /** Write an attribute from an array of strings.
13429 : *
13430 : * Type conversion will be performed if needed.
13431 : *
13432 : * Exactly GetTotalElementsCount() strings must be provided
13433 : *
13434 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13435 : *
13436 : * @param hAttr Attribute
13437 : * @param papszValues Array of strings.
13438 : * @return TRUE in case of success.
13439 : */
13440 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13441 : CSLConstList papszValues)
13442 : {
13443 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13444 8 : return hAttr->m_poImpl->Write(papszValues);
13445 : }
13446 :
13447 : /************************************************************************/
13448 : /* GDALAttributeWriteIntArray() */
13449 : /************************************************************************/
13450 :
13451 : /** Write an attribute from an array of int.
13452 : *
13453 : * Type conversion will be performed if needed.
13454 : *
13455 : * Exactly GetTotalElementsCount() strings must be provided
13456 : *
13457 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13458 : * size_t)
13459 : *
13460 : * @param hAttr Attribute
13461 : * @param panValues Array of int.
13462 : * @param nCount Should be equal to GetTotalElementsCount().
13463 : * @return TRUE in case of success.
13464 : */
13465 9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13466 : size_t nCount)
13467 : {
13468 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13469 9 : return hAttr->m_poImpl->Write(panValues, nCount);
13470 : }
13471 :
13472 : /************************************************************************/
13473 : /* GDALAttributeWriteInt64Array() */
13474 : /************************************************************************/
13475 :
13476 : /** Write an attribute from an array of int64_t.
13477 : *
13478 : * Type conversion will be performed if needed.
13479 : *
13480 : * Exactly GetTotalElementsCount() strings must be provided
13481 : *
13482 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13483 : * size_t)
13484 : *
13485 : * @param hAttr Attribute
13486 : * @param panValues Array of int64_t.
13487 : * @param nCount Should be equal to GetTotalElementsCount().
13488 : * @return TRUE in case of success.
13489 : */
13490 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13491 : size_t nCount)
13492 : {
13493 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13494 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13495 : }
13496 :
13497 : /************************************************************************/
13498 : /* GDALAttributeWriteDoubleArray() */
13499 : /************************************************************************/
13500 :
13501 : /** Write an attribute from an array of double.
13502 : *
13503 : * Type conversion will be performed if needed.
13504 : *
13505 : * Exactly GetTotalElementsCount() strings must be provided
13506 : *
13507 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13508 : * size_t)
13509 : *
13510 : * @param hAttr Attribute
13511 : * @param padfValues Array of double.
13512 : * @param nCount Should be equal to GetTotalElementsCount().
13513 : * @return TRUE in case of success.
13514 : */
13515 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
13516 : const double *padfValues, size_t nCount)
13517 : {
13518 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13519 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
13520 : }
13521 :
13522 : /************************************************************************/
13523 : /* GDALAttributeRename() */
13524 : /************************************************************************/
13525 :
13526 : /** Rename the attribute.
13527 : *
13528 : * This is not implemented by all drivers.
13529 : *
13530 : * Drivers known to implement it: MEM, netCDF.
13531 : *
13532 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13533 : *
13534 : * @return true in case of success
13535 : * @since GDAL 3.8
13536 : */
13537 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
13538 : {
13539 27 : VALIDATE_POINTER1(hAttr, __func__, false);
13540 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
13541 27 : return hAttr->m_poImpl->Rename(pszNewName);
13542 : }
13543 :
13544 : /************************************************************************/
13545 : /* GDALDimensionRelease() */
13546 : /************************************************************************/
13547 :
13548 : /** Release the GDAL in-memory object associated with a GDALDimension.
13549 : *
13550 : * Note: when applied on a object coming from a driver, this does not
13551 : * destroy the object in the file, database, etc...
13552 : */
13553 4842 : void GDALDimensionRelease(GDALDimensionH hDim)
13554 : {
13555 4842 : delete hDim;
13556 4842 : }
13557 :
13558 : /************************************************************************/
13559 : /* GDALDimensionGetName() */
13560 : /************************************************************************/
13561 :
13562 : /** Return dimension name.
13563 : *
13564 : * This is the same as the C++ method GDALDimension::GetName()
13565 : */
13566 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
13567 : {
13568 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13569 284 : return hDim->m_poImpl->GetName().c_str();
13570 : }
13571 :
13572 : /************************************************************************/
13573 : /* GDALDimensionGetFullName() */
13574 : /************************************************************************/
13575 :
13576 : /** Return dimension full name.
13577 : *
13578 : * This is the same as the C++ method GDALDimension::GetFullName()
13579 : */
13580 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
13581 : {
13582 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13583 80 : return hDim->m_poImpl->GetFullName().c_str();
13584 : }
13585 :
13586 : /************************************************************************/
13587 : /* GDALDimensionGetType() */
13588 : /************************************************************************/
13589 :
13590 : /** Return dimension type.
13591 : *
13592 : * This is the same as the C++ method GDALDimension::GetType()
13593 : */
13594 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
13595 : {
13596 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13597 62 : return hDim->m_poImpl->GetType().c_str();
13598 : }
13599 :
13600 : /************************************************************************/
13601 : /* GDALDimensionGetDirection() */
13602 : /************************************************************************/
13603 :
13604 : /** Return dimension direction.
13605 : *
13606 : * This is the same as the C++ method GDALDimension::GetDirection()
13607 : */
13608 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
13609 : {
13610 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13611 32 : return hDim->m_poImpl->GetDirection().c_str();
13612 : }
13613 :
13614 : /************************************************************************/
13615 : /* GDALDimensionGetSize() */
13616 : /************************************************************************/
13617 :
13618 : /** Return the size, that is the number of values along the dimension.
13619 : *
13620 : * This is the same as the C++ method GDALDimension::GetSize()
13621 : */
13622 3629 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
13623 : {
13624 3629 : VALIDATE_POINTER1(hDim, __func__, 0);
13625 3629 : return hDim->m_poImpl->GetSize();
13626 : }
13627 :
13628 : /************************************************************************/
13629 : /* GDALDimensionGetIndexingVariable() */
13630 : /************************************************************************/
13631 :
13632 : /** Return the variable that is used to index the dimension (if there is one).
13633 : *
13634 : * This is the array, typically one-dimensional, describing the values taken
13635 : * by the dimension.
13636 : *
13637 : * The returned value should be freed with GDALMDArrayRelease().
13638 : *
13639 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
13640 : */
13641 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
13642 : {
13643 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13644 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
13645 118 : if (!var)
13646 10 : return nullptr;
13647 108 : return new GDALMDArrayHS(var);
13648 : }
13649 :
13650 : /************************************************************************/
13651 : /* GDALDimensionSetIndexingVariable() */
13652 : /************************************************************************/
13653 :
13654 : /** Set the variable that is used to index the dimension.
13655 : *
13656 : * This is the array, typically one-dimensional, describing the values taken
13657 : * by the dimension.
13658 : *
13659 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
13660 : *
13661 : * @return TRUE in case of success.
13662 : */
13663 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
13664 : {
13665 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
13666 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
13667 46 : : nullptr);
13668 : }
13669 :
13670 : /************************************************************************/
13671 : /* GDALDimensionRename() */
13672 : /************************************************************************/
13673 :
13674 : /** Rename the dimension.
13675 : *
13676 : * This is not implemented by all drivers.
13677 : *
13678 : * Drivers known to implement it: MEM, netCDF.
13679 : *
13680 : * This is the same as the C++ method GDALDimension::Rename()
13681 : *
13682 : * @return true in case of success
13683 : * @since GDAL 3.8
13684 : */
13685 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
13686 : {
13687 31 : VALIDATE_POINTER1(hDim, __func__, false);
13688 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
13689 31 : return hDim->m_poImpl->Rename(pszNewName);
13690 : }
13691 :
13692 : /************************************************************************/
13693 : /* GDALDatasetGetRootGroup() */
13694 : /************************************************************************/
13695 :
13696 : /** Return the root GDALGroup of this dataset.
13697 : *
13698 : * Only valid for multidimensional datasets.
13699 : *
13700 : * The returned value must be freed with GDALGroupRelease().
13701 : *
13702 : * This is the same as the C++ method GDALDataset::GetRootGroup().
13703 : *
13704 : * @since GDAL 3.1
13705 : */
13706 1129 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
13707 : {
13708 1129 : VALIDATE_POINTER1(hDS, __func__, nullptr);
13709 1129 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
13710 1129 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
13711 : }
13712 :
13713 : /************************************************************************/
13714 : /* GDALRasterBandAsMDArray() */
13715 : /************************************************************************/
13716 :
13717 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
13718 : *
13719 : * The band must be linked to a GDALDataset. If this dataset is not already
13720 : * marked as shared, it will be, so that the returned array holds a reference
13721 : * to it.
13722 : *
13723 : * If the dataset has a geotransform attached, the X and Y dimensions of the
13724 : * returned array will have an associated indexing variable.
13725 : *
13726 : * The returned pointer must be released with GDALMDArrayRelease().
13727 : *
13728 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
13729 : *
13730 : * @return a new array, or NULL.
13731 : *
13732 : * @since GDAL 3.1
13733 : */
13734 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
13735 : {
13736 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
13737 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
13738 21 : if (!poArray)
13739 0 : return nullptr;
13740 21 : return new GDALMDArrayHS(poArray);
13741 : }
13742 :
13743 : /************************************************************************/
13744 : /* GDALMDArrayAsClassicDataset() */
13745 : /************************************************************************/
13746 :
13747 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13748 : *
13749 : * Only 2D or more arrays are supported.
13750 : *
13751 : * In the case of > 2D arrays, additional dimensions will be represented as
13752 : * raster bands.
13753 : *
13754 : * The "reverse" method is GDALRasterBand::AsMDArray().
13755 : *
13756 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13757 : *
13758 : * @param hArray Array.
13759 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13760 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13761 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13762 : */
13763 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
13764 : size_t iYDim)
13765 : {
13766 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13767 0 : return GDALDataset::ToHandle(
13768 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
13769 : }
13770 :
13771 : /************************************************************************/
13772 : /* GDALMDArrayAsClassicDatasetEx() */
13773 : /************************************************************************/
13774 :
13775 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13776 : *
13777 : * Only 2D or more arrays are supported.
13778 : *
13779 : * In the case of > 2D arrays, additional dimensions will be represented as
13780 : * raster bands.
13781 : *
13782 : * The "reverse" method is GDALRasterBand::AsMDArray().
13783 : *
13784 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13785 : * @param hArray Array.
13786 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13787 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13788 : * Ignored if the dimension count is 1.
13789 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
13790 : * BAND_IMAGERY_METADATA option.
13791 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
13792 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13793 : * @since GDAL 3.8
13794 : */
13795 71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
13796 : size_t iYDim, GDALGroupH hRootGroup,
13797 : CSLConstList papszOptions)
13798 : {
13799 71 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13800 142 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
13801 142 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
13802 142 : papszOptions));
13803 : }
13804 :
13805 : //! @cond Doxygen_Suppress
13806 :
13807 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
13808 : const std::string &osName,
13809 : const std::string &osValue,
13810 180 : GDALExtendedDataTypeSubType eSubType)
13811 : : GDALAbstractMDArray(osParentName, osName),
13812 : GDALAttribute(osParentName, osName),
13813 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
13814 : {
13815 180 : }
13816 :
13817 : const std::vector<std::shared_ptr<GDALDimension>> &
13818 30 : GDALAttributeString::GetDimensions() const
13819 : {
13820 30 : return m_dims;
13821 : }
13822 :
13823 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
13824 : {
13825 21 : return m_dt;
13826 : }
13827 :
13828 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
13829 : const GPtrDiff_t *,
13830 : const GDALExtendedDataType &bufferDataType,
13831 : void *pDstBuffer) const
13832 : {
13833 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
13834 0 : return false;
13835 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
13836 10 : if (!pszStr)
13837 0 : return false;
13838 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
13839 10 : *static_cast<char **>(pDstBuffer) = pszStr;
13840 10 : return true;
13841 : }
13842 :
13843 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13844 : const std::string &osName,
13845 66 : double dfValue)
13846 : : GDALAbstractMDArray(osParentName, osName),
13847 : GDALAttribute(osParentName, osName),
13848 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
13849 : {
13850 66 : }
13851 :
13852 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13853 : const std::string &osName,
13854 27 : int nValue)
13855 : : GDALAbstractMDArray(osParentName, osName),
13856 : GDALAttribute(osParentName, osName),
13857 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
13858 : {
13859 27 : }
13860 :
13861 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13862 : const std::string &osName,
13863 7 : const std::vector<GUInt32> &anValues)
13864 : : GDALAbstractMDArray(osParentName, osName),
13865 : GDALAttribute(osParentName, osName),
13866 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
13867 : {
13868 7 : m_dims.push_back(std::make_shared<GDALDimension>(
13869 14 : std::string(), "dim0", std::string(), std::string(),
13870 7 : m_anValuesUInt32.size()));
13871 7 : }
13872 :
13873 : const std::vector<std::shared_ptr<GDALDimension>> &
13874 14 : GDALAttributeNumeric::GetDimensions() const
13875 : {
13876 14 : return m_dims;
13877 : }
13878 :
13879 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
13880 : {
13881 8 : return m_dt;
13882 : }
13883 :
13884 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
13885 : const size_t *count, const GInt64 *arrayStep,
13886 : const GPtrDiff_t *bufferStride,
13887 : const GDALExtendedDataType &bufferDataType,
13888 : void *pDstBuffer) const
13889 : {
13890 4 : if (m_dims.empty())
13891 : {
13892 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
13893 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
13894 : bufferDataType);
13895 : else
13896 : {
13897 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
13898 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
13899 : bufferDataType);
13900 : }
13901 : }
13902 : else
13903 : {
13904 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
13905 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13906 30 : for (size_t i = 0; i < count[0]; ++i)
13907 : {
13908 29 : GDALExtendedDataType::CopyValue(
13909 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
13910 29 : i * arrayStep[0])],
13911 29 : m_dt, pabyDstBuffer, bufferDataType);
13912 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
13913 : }
13914 : }
13915 4 : return true;
13916 : }
13917 :
13918 192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
13919 : const std::string &osParentName, const std::string &osName,
13920 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13921 192 : double dfIncrement, double dfOffsetInIncrement)
13922 : : GDALAbstractMDArray(osParentName, osName),
13923 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
13924 : m_dfIncrement(dfIncrement),
13925 384 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
13926 : {
13927 192 : }
13928 :
13929 192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
13930 : const std::string &osParentName, const std::string &osName,
13931 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13932 : double dfIncrement, double dfOffsetInIncrement)
13933 : {
13934 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
13935 192 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
13936 192 : poArray->SetSelf(poArray);
13937 192 : return poArray;
13938 : }
13939 :
13940 : const std::vector<std::shared_ptr<GDALDimension>> &
13941 786 : GDALMDArrayRegularlySpaced::GetDimensions() const
13942 : {
13943 786 : return m_dims;
13944 : }
13945 :
13946 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
13947 : {
13948 316 : return m_dt;
13949 : }
13950 :
13951 : std::vector<std::shared_ptr<GDALAttribute>>
13952 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
13953 : {
13954 4 : return m_attributes;
13955 : }
13956 :
13957 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
13958 : const std::shared_ptr<GDALAttribute> &poAttr)
13959 : {
13960 0 : m_attributes.emplace_back(poAttr);
13961 0 : }
13962 :
13963 188 : bool GDALMDArrayRegularlySpaced::IRead(
13964 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
13965 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
13966 : void *pDstBuffer) const
13967 : {
13968 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13969 14719 : for (size_t i = 0; i < count[0]; i++)
13970 : {
13971 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
13972 14531 : m_dfOffsetInIncrement) *
13973 14531 : m_dfIncrement;
13974 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
13975 : bufferDataType);
13976 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
13977 : }
13978 188 : return true;
13979 : }
13980 :
13981 2898 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
13982 : const std::string &osParentName, const std::string &osName,
13983 2898 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
13984 2898 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
13985 : {
13986 2898 : }
13987 :
13988 : std::shared_ptr<GDALMDArray>
13989 702 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
13990 : {
13991 702 : return m_poIndexingVariable.lock();
13992 : }
13993 :
13994 : // cppcheck-suppress passedByValue
13995 484 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
13996 : std::shared_ptr<GDALMDArray> poIndexingVariable)
13997 : {
13998 484 : m_poIndexingVariable = poIndexingVariable;
13999 484 : return true;
14000 : }
14001 :
14002 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14003 : {
14004 33 : m_nSize = nNewSize;
14005 33 : }
14006 :
14007 : /************************************************************************/
14008 : /* GDALPamMultiDim::Private */
14009 : /************************************************************************/
14010 :
14011 : struct GDALPamMultiDim::Private
14012 : {
14013 : std::string m_osFilename{};
14014 : std::string m_osPamFilename{};
14015 :
14016 : struct Statistics
14017 : {
14018 : bool bHasStats = false;
14019 : bool bApproxStats = false;
14020 : double dfMin = 0;
14021 : double dfMax = 0;
14022 : double dfMean = 0;
14023 : double dfStdDev = 0;
14024 : GUInt64 nValidCount = 0;
14025 : };
14026 :
14027 : struct ArrayInfo
14028 : {
14029 : std::shared_ptr<OGRSpatialReference> poSRS{};
14030 : // cppcheck-suppress unusedStructMember
14031 : Statistics stats{};
14032 : };
14033 :
14034 : typedef std::pair<std::string, std::string> NameContext;
14035 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14036 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14037 : bool m_bDirty = false;
14038 : bool m_bLoaded = false;
14039 : };
14040 :
14041 : /************************************************************************/
14042 : /* GDALPamMultiDim */
14043 : /************************************************************************/
14044 :
14045 1368 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14046 1368 : : d(new Private())
14047 : {
14048 1368 : d->m_osFilename = osFilename;
14049 1368 : }
14050 :
14051 : /************************************************************************/
14052 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14053 : /************************************************************************/
14054 :
14055 1368 : GDALPamMultiDim::~GDALPamMultiDim()
14056 : {
14057 1368 : if (d->m_bDirty)
14058 29 : Save();
14059 1368 : }
14060 :
14061 : /************************************************************************/
14062 : /* GDALPamMultiDim::Load() */
14063 : /************************************************************************/
14064 :
14065 101 : void GDALPamMultiDim::Load()
14066 : {
14067 101 : if (d->m_bLoaded)
14068 90 : return;
14069 44 : d->m_bLoaded = true;
14070 :
14071 44 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14072 44 : d->m_osPamFilename =
14073 88 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14074 44 : CPLXMLTreeCloser oTree(nullptr);
14075 : {
14076 88 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14077 44 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14078 : }
14079 44 : if (!oTree)
14080 : {
14081 33 : return;
14082 : }
14083 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14084 11 : if (!poPAMMultiDim)
14085 0 : return;
14086 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14087 24 : psIter = psIter->psNext)
14088 : {
14089 24 : if (psIter->eType == CXT_Element &&
14090 24 : strcmp(psIter->pszValue, "Array") == 0)
14091 : {
14092 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14093 13 : if (!pszName)
14094 0 : continue;
14095 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14096 : const auto oKey =
14097 26 : std::pair<std::string, std::string>(pszName, pszContext);
14098 :
14099 : /* --------------------------------------------------------------------
14100 : */
14101 : /* Check for an SRS node. */
14102 : /* --------------------------------------------------------------------
14103 : */
14104 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14105 13 : if (psSRSNode)
14106 : {
14107 : std::shared_ptr<OGRSpatialReference> poSRS =
14108 6 : std::make_shared<OGRSpatialReference>();
14109 3 : poSRS->SetFromUserInput(
14110 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14111 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14112 3 : const char *pszMapping = CPLGetXMLValue(
14113 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14114 3 : if (pszMapping)
14115 : {
14116 : char **papszTokens =
14117 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14118 6 : std::vector<int> anMapping;
14119 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14120 : {
14121 6 : anMapping.push_back(atoi(papszTokens[i]));
14122 : }
14123 3 : CSLDestroy(papszTokens);
14124 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14125 : }
14126 : else
14127 : {
14128 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14129 : }
14130 :
14131 : const char *pszCoordinateEpoch =
14132 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14133 3 : if (pszCoordinateEpoch)
14134 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14135 :
14136 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14137 : }
14138 :
14139 : const CPLXMLNode *psStatistics =
14140 13 : CPLGetXMLNode(psIter, "Statistics");
14141 13 : if (psStatistics)
14142 : {
14143 7 : Private::Statistics sStats;
14144 7 : sStats.bHasStats = true;
14145 7 : sStats.bApproxStats = CPLTestBool(
14146 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14147 7 : sStats.dfMin =
14148 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14149 7 : sStats.dfMax =
14150 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14151 7 : sStats.dfMean =
14152 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14153 7 : sStats.dfStdDev =
14154 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14155 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14156 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14157 7 : d->m_oMapArray[oKey].stats = sStats;
14158 13 : }
14159 : }
14160 : else
14161 : {
14162 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14163 11 : psIter->psNext = nullptr;
14164 11 : d->m_apoOtherNodes.emplace_back(
14165 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14166 11 : psIter->psNext = psNextBackup;
14167 : }
14168 : }
14169 : }
14170 :
14171 : /************************************************************************/
14172 : /* GDALPamMultiDim::Save() */
14173 : /************************************************************************/
14174 :
14175 29 : void GDALPamMultiDim::Save()
14176 : {
14177 : CPLXMLTreeCloser oTree(
14178 58 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14179 33 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14180 : {
14181 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14182 : }
14183 108 : for (const auto &kv : d->m_oMapArray)
14184 : {
14185 : CPLXMLNode *psArrayNode =
14186 79 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14187 79 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14188 79 : if (!kv.first.second.empty())
14189 : {
14190 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14191 : kv.first.second.c_str());
14192 : }
14193 79 : if (kv.second.poSRS)
14194 : {
14195 71 : char *pszWKT = nullptr;
14196 : {
14197 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14198 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14199 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14200 : }
14201 : CPLXMLNode *psSRSNode =
14202 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14203 71 : CPLFree(pszWKT);
14204 : const auto &mapping =
14205 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14206 142 : CPLString osMapping;
14207 213 : for (size_t i = 0; i < mapping.size(); ++i)
14208 : {
14209 142 : if (!osMapping.empty())
14210 71 : osMapping += ",";
14211 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14212 : }
14213 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14214 : osMapping.c_str());
14215 :
14216 : const double dfCoordinateEpoch =
14217 71 : kv.second.poSRS->GetCoordinateEpoch();
14218 71 : if (dfCoordinateEpoch > 0)
14219 : {
14220 : std::string osCoordinateEpoch =
14221 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14222 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14223 : {
14224 6 : while (osCoordinateEpoch.back() == '0')
14225 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14226 : }
14227 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14228 : osCoordinateEpoch.c_str());
14229 : }
14230 : }
14231 :
14232 79 : if (kv.second.stats.bHasStats)
14233 : {
14234 : CPLXMLNode *psMDArray =
14235 5 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14236 5 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14237 5 : kv.second.stats.bApproxStats ? "1"
14238 : : "0");
14239 5 : CPLCreateXMLElementAndValue(
14240 : psMDArray, "Minimum",
14241 5 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14242 5 : CPLCreateXMLElementAndValue(
14243 : psMDArray, "Maximum",
14244 5 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14245 5 : CPLCreateXMLElementAndValue(
14246 5 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14247 5 : CPLCreateXMLElementAndValue(
14248 : psMDArray, "StdDev",
14249 5 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14250 5 : CPLCreateXMLElementAndValue(
14251 : psMDArray, "ValidSampleCount",
14252 5 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14253 : }
14254 : }
14255 :
14256 58 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
14257 29 : CPLInstallErrorHandlerAccumulator(aoErrors);
14258 : const int bSaved =
14259 29 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14260 29 : CPLUninstallErrorHandlerAccumulator();
14261 :
14262 29 : const char *pszNewPam = nullptr;
14263 29 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14264 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14265 : {
14266 0 : CPLErrorReset();
14267 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14268 : }
14269 : else
14270 : {
14271 29 : for (const auto &oError : aoErrors)
14272 : {
14273 0 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
14274 : }
14275 : }
14276 29 : }
14277 :
14278 : /************************************************************************/
14279 : /* GDALPamMultiDim::GetSpatialRef() */
14280 : /************************************************************************/
14281 :
14282 : std::shared_ptr<OGRSpatialReference>
14283 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14284 : const std::string &osContext)
14285 : {
14286 10 : Load();
14287 : auto oIter =
14288 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14289 10 : if (oIter != d->m_oMapArray.end())
14290 2 : return oIter->second.poSRS;
14291 8 : return nullptr;
14292 : }
14293 :
14294 : /************************************************************************/
14295 : /* GDALPamMultiDim::SetSpatialRef() */
14296 : /************************************************************************/
14297 :
14298 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14299 : const std::string &osContext,
14300 : const OGRSpatialReference *poSRS)
14301 : {
14302 72 : Load();
14303 72 : d->m_bDirty = true;
14304 72 : if (poSRS && !poSRS->IsEmpty())
14305 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14306 : poSRS->Clone());
14307 : else
14308 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14309 1 : .poSRS.reset();
14310 72 : }
14311 :
14312 : /************************************************************************/
14313 : /* GetStatistics() */
14314 : /************************************************************************/
14315 :
14316 13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14317 : const std::string &osContext,
14318 : bool bApproxOK, double *pdfMin,
14319 : double *pdfMax, double *pdfMean,
14320 : double *pdfStdDev, GUInt64 *pnValidCount)
14321 : {
14322 13 : Load();
14323 : auto oIter =
14324 13 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14325 13 : if (oIter == d->m_oMapArray.end())
14326 6 : return CE_Failure;
14327 7 : const auto &stats = oIter->second.stats;
14328 7 : if (!stats.bHasStats)
14329 1 : return CE_Failure;
14330 6 : if (!bApproxOK && stats.bApproxStats)
14331 0 : return CE_Failure;
14332 6 : if (pdfMin)
14333 6 : *pdfMin = stats.dfMin;
14334 6 : if (pdfMax)
14335 6 : *pdfMax = stats.dfMax;
14336 6 : if (pdfMean)
14337 6 : *pdfMean = stats.dfMean;
14338 6 : if (pdfStdDev)
14339 6 : *pdfStdDev = stats.dfStdDev;
14340 6 : if (pnValidCount)
14341 6 : *pnValidCount = stats.nValidCount;
14342 6 : return CE_None;
14343 : }
14344 :
14345 : /************************************************************************/
14346 : /* SetStatistics() */
14347 : /************************************************************************/
14348 :
14349 5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14350 : const std::string &osContext,
14351 : bool bApproxStats, double dfMin,
14352 : double dfMax, double dfMean,
14353 : double dfStdDev, GUInt64 nValidCount)
14354 : {
14355 5 : Load();
14356 5 : d->m_bDirty = true;
14357 : auto &stats =
14358 5 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14359 5 : stats.bHasStats = true;
14360 5 : stats.bApproxStats = bApproxStats;
14361 5 : stats.dfMin = dfMin;
14362 5 : stats.dfMax = dfMax;
14363 5 : stats.dfMean = dfMean;
14364 5 : stats.dfStdDev = dfStdDev;
14365 5 : stats.nValidCount = nValidCount;
14366 5 : }
14367 :
14368 : /************************************************************************/
14369 : /* ClearStatistics() */
14370 : /************************************************************************/
14371 :
14372 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14373 : const std::string &osContext)
14374 : {
14375 0 : Load();
14376 0 : d->m_bDirty = true;
14377 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14378 : false;
14379 0 : }
14380 :
14381 : /************************************************************************/
14382 : /* ClearStatistics() */
14383 : /************************************************************************/
14384 :
14385 1 : void GDALPamMultiDim::ClearStatistics()
14386 : {
14387 1 : Load();
14388 1 : d->m_bDirty = true;
14389 3 : for (auto &kv : d->m_oMapArray)
14390 2 : kv.second.stats.bHasStats = false;
14391 1 : }
14392 :
14393 : /************************************************************************/
14394 : /* GetPAM() */
14395 : /************************************************************************/
14396 :
14397 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14398 784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14399 : {
14400 784 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14401 784 : if (poPamArray)
14402 563 : return poPamArray->GetPAM();
14403 221 : return nullptr;
14404 : }
14405 :
14406 : /************************************************************************/
14407 : /* GDALPamMDArray */
14408 : /************************************************************************/
14409 :
14410 3634 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14411 : const std::string &osName,
14412 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14413 0 : const std::string &osContext)
14414 : :
14415 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14416 : GDALAbstractMDArray(osParentName, osName),
14417 : #endif
14418 3634 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14419 : {
14420 3634 : }
14421 :
14422 : /************************************************************************/
14423 : /* GDALPamMDArray::SetSpatialRef() */
14424 : /************************************************************************/
14425 :
14426 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14427 : {
14428 72 : if (!m_poPam)
14429 0 : return false;
14430 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14431 72 : return true;
14432 : }
14433 :
14434 : /************************************************************************/
14435 : /* GDALPamMDArray::GetSpatialRef() */
14436 : /************************************************************************/
14437 :
14438 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14439 : {
14440 10 : if (!m_poPam)
14441 0 : return nullptr;
14442 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14443 : }
14444 :
14445 : /************************************************************************/
14446 : /* GetStatistics() */
14447 : /************************************************************************/
14448 :
14449 13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14450 : double *pdfMin, double *pdfMax,
14451 : double *pdfMean, double *pdfStdDev,
14452 : GUInt64 *pnValidCount,
14453 : GDALProgressFunc pfnProgress,
14454 : void *pProgressData)
14455 : {
14456 13 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14457 : bApproxOK, pdfMin, pdfMax, pdfMean,
14458 13 : pdfStdDev, pnValidCount) == CE_None)
14459 : {
14460 6 : return CE_None;
14461 : }
14462 7 : if (!bForce)
14463 4 : return CE_Warning;
14464 :
14465 3 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14466 : pdfMean, pdfStdDev, pnValidCount,
14467 3 : pfnProgress, pProgressData);
14468 : }
14469 :
14470 : /************************************************************************/
14471 : /* SetStatistics() */
14472 : /************************************************************************/
14473 :
14474 5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14475 : double dfMax, double dfMean, double dfStdDev,
14476 : GUInt64 nValidCount,
14477 : CSLConstList /* papszOptions */)
14478 : {
14479 5 : if (!m_poPam)
14480 0 : return false;
14481 5 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14482 : dfMax, dfMean, dfStdDev, nValidCount);
14483 5 : return true;
14484 : }
14485 :
14486 : /************************************************************************/
14487 : /* ClearStatistics() */
14488 : /************************************************************************/
14489 :
14490 0 : void GDALPamMDArray::ClearStatistics()
14491 : {
14492 0 : if (!m_poPam)
14493 0 : return;
14494 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14495 : }
14496 :
14497 : //! @endcond
|