Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <assert.h>
15 : #include <algorithm>
16 : #include <limits>
17 : #include <list>
18 : #include <queue>
19 : #include <set>
20 : #include <utility>
21 : #include <time.h>
22 :
23 : #include <cmath>
24 : #include <ctype.h> // isalnum
25 :
26 : #include "cpl_error_internal.h"
27 : #include "cpl_float.h"
28 : #include "gdal_priv.h"
29 : #include "gdal_pam.h"
30 : #include "gdal_pam_multidim.h"
31 : #include "gdal_rat.h"
32 : #include "gdal_utils.h"
33 : #include "cpl_safemaths.hpp"
34 : #include "memmultidim.h"
35 : #include "ogrsf_frmts.h"
36 : #include "gdalmultidim_priv.h"
37 :
38 : #if defined(__clang__) || defined(_MSC_VER)
39 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40 : #endif
41 :
42 : /************************************************************************/
43 : /* GDALMDArrayUnscaled */
44 : /************************************************************************/
45 :
46 : class GDALMDArrayUnscaled final : public GDALPamMDArray
47 : {
48 : private:
49 : std::shared_ptr<GDALMDArray> m_poParent{};
50 : const GDALExtendedDataType m_dt;
51 : bool m_bHasNoData;
52 : const double m_dfScale;
53 : const double m_dfOffset;
54 : std::vector<GByte> m_abyRawNoData{};
55 :
56 : protected:
57 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58 : double dfScale, double dfOffset,
59 : double dfOverriddenDstNodata, GDALDataType eDT)
60 26 : : GDALAbstractMDArray(std::string(),
61 26 : "Unscaled view of " + poParent->GetFullName()),
62 : GDALPamMDArray(
63 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
64 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65 13 : m_poParent(std::move(poParent)),
66 : m_dt(GDALExtendedDataType::Create(eDT)),
67 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
69 : {
70 13 : m_abyRawNoData.resize(m_dt.GetSize());
71 : const auto eNonComplexDT =
72 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73 26 : GDALCopyWords64(
74 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77 13 : }
78 :
79 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81 : const GDALExtendedDataType &bufferDataType,
82 : void *pDstBuffer) const override;
83 :
84 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86 : const GDALExtendedDataType &bufferDataType,
87 : const void *pSrcBuffer) override;
88 :
89 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90 : CSLConstList papszOptions) const override
91 : {
92 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93 : }
94 :
95 : public:
96 : static std::shared_ptr<GDALMDArrayUnscaled>
97 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98 : double dfOffset, double dfDstNodata, GDALDataType eDT)
99 : {
100 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102 13 : newAr->SetSelf(newAr);
103 13 : return newAr;
104 : }
105 :
106 1 : bool IsWritable() const override
107 : {
108 1 : return m_poParent->IsWritable();
109 : }
110 :
111 15 : const std::string &GetFilename() const override
112 : {
113 15 : return m_poParent->GetFilename();
114 : }
115 :
116 : const std::vector<std::shared_ptr<GDALDimension>> &
117 220 : GetDimensions() const override
118 : {
119 220 : return m_poParent->GetDimensions();
120 : }
121 :
122 103 : const GDALExtendedDataType &GetDataType() const override
123 : {
124 103 : return m_dt;
125 : }
126 :
127 1 : const std::string &GetUnit() const override
128 : {
129 1 : return m_poParent->GetUnit();
130 : }
131 :
132 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133 : {
134 1 : return m_poParent->GetSpatialRef();
135 : }
136 :
137 6 : const void *GetRawNoDataValue() const override
138 : {
139 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140 : }
141 :
142 1 : bool SetRawNoDataValue(const void *pRawNoData) override
143 : {
144 1 : m_bHasNoData = true;
145 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146 1 : return true;
147 : }
148 :
149 4 : std::vector<GUInt64> GetBlockSize() const override
150 : {
151 4 : return m_poParent->GetBlockSize();
152 : }
153 :
154 : std::shared_ptr<GDALAttribute>
155 0 : GetAttribute(const std::string &osName) const override
156 : {
157 0 : return m_poParent->GetAttribute(osName);
158 : }
159 :
160 : std::vector<std::shared_ptr<GDALAttribute>>
161 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
162 : {
163 1 : return m_poParent->GetAttributes(papszOptions);
164 : }
165 :
166 0 : bool SetUnit(const std::string &osUnit) override
167 : {
168 0 : return m_poParent->SetUnit(osUnit);
169 : }
170 :
171 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172 : {
173 0 : return m_poParent->SetSpatialRef(poSRS);
174 : }
175 :
176 : std::shared_ptr<GDALAttribute>
177 1 : CreateAttribute(const std::string &osName,
178 : const std::vector<GUInt64> &anDimensions,
179 : const GDALExtendedDataType &oDataType,
180 : CSLConstList papszOptions = nullptr) override
181 : {
182 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183 1 : papszOptions);
184 : }
185 : };
186 :
187 : /************************************************************************/
188 : /* ~GDALIHasAttribute() */
189 : /************************************************************************/
190 :
191 : GDALIHasAttribute::~GDALIHasAttribute() = default;
192 :
193 : /************************************************************************/
194 : /* GetAttribute() */
195 : /************************************************************************/
196 :
197 : /** Return an attribute by its name.
198 : *
199 : * If the attribute does not exist, nullptr should be silently returned.
200 : *
201 : * @note Driver implementation: this method will fallback to
202 : * GetAttributeFromAttributes() is not explicitly implemented
203 : *
204 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
205 : *
206 : * This is the same as the C function GDALGroupGetAttribute() or
207 : * GDALMDArrayGetAttribute().
208 : *
209 : * @param osName Attribute name
210 : * @return the attribute, or nullptr if it does not exist or an error occurred.
211 : */
212 : std::shared_ptr<GDALAttribute>
213 2144 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
214 : {
215 2144 : return GetAttributeFromAttributes(osName);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetAttributeFromAttributes() */
220 : /************************************************************************/
221 :
222 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
223 : */
224 : std::shared_ptr<GDALAttribute>
225 2144 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226 : {
227 4288 : auto attrs(GetAttributes());
228 14813 : for (const auto &attr : attrs)
229 : {
230 14125 : if (attr->GetName() == osName)
231 1456 : return attr;
232 : }
233 688 : return nullptr;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetAttributes() */
238 : /************************************************************************/
239 :
240 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241 : *
242 : * If the attribute does not exist, nullptr should be silently returned.
243 : *
244 : * @note Driver implementation: optionally implemented. If implemented,
245 : * GetAttribute() should also be implemented.
246 : *
247 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
248 : *
249 : * This is the same as the C function GDALGroupGetAttributes() or
250 : * GDALMDArrayGetAttributes().
251 :
252 : * @param papszOptions Driver specific options determining how attributes
253 : * should be retrieved. Pass nullptr for default behavior.
254 : *
255 : * @return the attributes.
256 : */
257 : std::vector<std::shared_ptr<GDALAttribute>>
258 54 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259 : {
260 54 : return {};
261 : }
262 :
263 : /************************************************************************/
264 : /* CreateAttribute() */
265 : /************************************************************************/
266 :
267 : /** Create an attribute within a GDALMDArray or GDALGroup.
268 : *
269 : * The attribute might not be "physically" created until a value is written
270 : * into it.
271 : *
272 : * Optionally implemented.
273 : *
274 : * Drivers known to implement it: MEM, netCDF
275 : *
276 : * This is the same as the C function GDALGroupCreateAttribute() or
277 : * GDALMDArrayCreateAttribute()
278 : *
279 : * @param osName Attribute name.
280 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
281 : * dimension first to the fastest varying dimension last.
282 : * Empty for a scalar attribute (common case)
283 : * @param oDataType Attribute data type.
284 : * @param papszOptions Driver specific options determining how the attribute.
285 : * should be created.
286 : *
287 : * @return the new attribute, or nullptr if case of error
288 : */
289 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290 : CPL_UNUSED const std::string &osName,
291 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292 : CPL_UNUSED const GDALExtendedDataType &oDataType,
293 : CPL_UNUSED CSLConstList papszOptions)
294 : {
295 0 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "CreateAttribute() not implemented");
297 0 : return nullptr;
298 : }
299 :
300 : /************************************************************************/
301 : /* DeleteAttribute() */
302 : /************************************************************************/
303 :
304 : /** Delete an attribute from a GDALMDArray or GDALGroup.
305 : *
306 : * Optionally implemented.
307 : *
308 : * After this call, if a previously obtained instance of the deleted object
309 : * is still alive, no method other than for freeing it should be invoked.
310 : *
311 : * Drivers known to implement it: MEM, netCDF
312 : *
313 : * This is the same as the C function GDALGroupDeleteAttribute() or
314 : * GDALMDArrayDeleteAttribute()
315 : *
316 : * @param osName Attribute name.
317 : * @param papszOptions Driver specific options determining how the attribute.
318 : * should be deleted.
319 : *
320 : * @return true in case of success
321 : * @since GDAL 3.8
322 : */
323 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324 : CPL_UNUSED CSLConstList papszOptions)
325 : {
326 0 : CPLError(CE_Failure, CPLE_NotSupported,
327 : "DeleteAttribute() not implemented");
328 0 : return false;
329 : }
330 :
331 : /************************************************************************/
332 : /* GDALGroup() */
333 : /************************************************************************/
334 :
335 : //! @cond Doxygen_Suppress
336 7299 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337 7299 : const std::string &osContext)
338 7298 : : m_osName(osParentName.empty() ? "/" : osName),
339 : m_osFullName(
340 14597 : !osParentName.empty()
341 11245 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342 : : "/"),
343 18545 : m_osContext(osContext)
344 : {
345 7298 : }
346 :
347 : //! @endcond
348 :
349 : /************************************************************************/
350 : /* ~GDALGroup() */
351 : /************************************************************************/
352 :
353 : GDALGroup::~GDALGroup() = default;
354 :
355 : /************************************************************************/
356 : /* GetMDArrayNames() */
357 : /************************************************************************/
358 :
359 : /** Return the list of multidimensional array names contained in this group.
360 : *
361 : * @note Driver implementation: optionally implemented. If implemented,
362 : * OpenMDArray() should also be implemented.
363 : *
364 : * Drivers known to implement it: MEM, netCDF.
365 : *
366 : * This is the same as the C function GDALGroupGetMDArrayNames().
367 : *
368 : * @param papszOptions Driver specific options determining how arrays
369 : * should be retrieved. Pass nullptr for default behavior.
370 : *
371 : * @return the array names.
372 : */
373 : std::vector<std::string>
374 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375 : {
376 0 : return {};
377 : }
378 :
379 : /************************************************************************/
380 : /* GetMDArrayFullNamesRecursive() */
381 : /************************************************************************/
382 :
383 : /** Return the list of multidimensional array full names contained in this
384 : * group and its subgroups.
385 : *
386 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387 : *
388 : * @param papszGroupOptions Driver specific options determining how groups
389 : * should be retrieved. Pass nullptr for default behavior.
390 : * @param papszArrayOptions Driver specific options determining how arrays
391 : * should be retrieved. Pass nullptr for default behavior.
392 : *
393 : * @return the array full names.
394 : *
395 : * @since 3.11
396 : */
397 : std::vector<std::string>
398 8 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399 : CSLConstList papszArrayOptions) const
400 : {
401 8 : std::vector<std::string> ret;
402 16 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
403 8 : stackGroups.push_back(nullptr); // nullptr means this
404 19 : while (!stackGroups.empty())
405 : {
406 22 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407 11 : stackGroups.erase(stackGroups.begin());
408 11 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409 25 : for (const std::string &arrayName :
410 61 : poCurGroup->GetMDArrayNames(papszArrayOptions))
411 : {
412 50 : std::string osFullName = poCurGroup->GetFullName();
413 25 : if (!osFullName.empty() && osFullName.back() != '/')
414 3 : osFullName += '/';
415 25 : osFullName += arrayName;
416 25 : ret.push_back(std::move(osFullName));
417 : }
418 11 : auto insertionPoint = stackGroups.begin();
419 3 : for (const auto &osSubGroup :
420 17 : poCurGroup->GetGroupNames(papszGroupOptions))
421 : {
422 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423 3 : if (poSubGroup)
424 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
425 : }
426 : }
427 :
428 16 : return ret;
429 : }
430 :
431 : /************************************************************************/
432 : /* OpenMDArray() */
433 : /************************************************************************/
434 :
435 : /** Open and return a multidimensional array.
436 : *
437 : * @note Driver implementation: optionally implemented. If implemented,
438 : * GetMDArrayNames() should also be implemented.
439 : *
440 : * Drivers known to implement it: MEM, netCDF.
441 : *
442 : * This is the same as the C function GDALGroupOpenMDArray().
443 : *
444 : * @param osName Array name.
445 : * @param papszOptions Driver specific options determining how the array should
446 : * be opened. Pass nullptr for default behavior.
447 : *
448 : * @return the array, or nullptr.
449 : */
450 : std::shared_ptr<GDALMDArray>
451 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452 : CPL_UNUSED CSLConstList papszOptions) const
453 : {
454 0 : return nullptr;
455 : }
456 :
457 : /************************************************************************/
458 : /* GetGroupNames() */
459 : /************************************************************************/
460 :
461 : /** Return the list of sub-groups contained in this group.
462 : *
463 : * @note Driver implementation: optionally implemented. If implemented,
464 : * OpenGroup() should also be implemented.
465 : *
466 : * Drivers known to implement it: MEM, netCDF.
467 : *
468 : * This is the same as the C function GDALGroupGetGroupNames().
469 : *
470 : * @param papszOptions Driver specific options determining how groups
471 : * should be retrieved. Pass nullptr for default behavior.
472 : *
473 : * @return the group names.
474 : */
475 : std::vector<std::string>
476 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477 : {
478 4 : return {};
479 : }
480 :
481 : /************************************************************************/
482 : /* OpenGroup() */
483 : /************************************************************************/
484 :
485 : /** Open and return a sub-group.
486 : *
487 : * @note Driver implementation: optionally implemented. If implemented,
488 : * GetGroupNames() should also be implemented.
489 : *
490 : * Drivers known to implement it: MEM, netCDF.
491 : *
492 : * This is the same as the C function GDALGroupOpenGroup().
493 : *
494 : * @param osName Sub-group name.
495 : * @param papszOptions Driver specific options determining how the sub-group
496 : * should be opened. Pass nullptr for default behavior.
497 : *
498 : * @return the group, or nullptr.
499 : */
500 : std::shared_ptr<GDALGroup>
501 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502 : CPL_UNUSED CSLConstList papszOptions) const
503 : {
504 4 : return nullptr;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetVectorLayerNames() */
509 : /************************************************************************/
510 :
511 : /** Return the list of layer names contained in this group.
512 : *
513 : * @note Driver implementation: optionally implemented. If implemented,
514 : * OpenVectorLayer() should also be implemented.
515 : *
516 : * Drivers known to implement it: OpenFileGDB, FileGDB
517 : *
518 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519 : * GDALDataset::GetLayer() should then be used.
520 : *
521 : * This is the same as the C function GDALGroupGetVectorLayerNames().
522 : *
523 : * @param papszOptions Driver specific options determining how layers
524 : * should be retrieved. Pass nullptr for default behavior.
525 : *
526 : * @return the vector layer names.
527 : * @since GDAL 3.4
528 : */
529 : std::vector<std::string>
530 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531 : {
532 1 : return {};
533 : }
534 :
535 : /************************************************************************/
536 : /* OpenVectorLayer() */
537 : /************************************************************************/
538 :
539 : /** Open and return a vector layer.
540 : *
541 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542 : * lifetime of the returned OGRLayer* is linked to the one of the owner
543 : * dataset (contrary to the general design of this class where objects can be
544 : * used independently of the object that returned them)
545 : *
546 : * @note Driver implementation: optionally implemented. If implemented,
547 : * GetVectorLayerNames() should also be implemented.
548 : *
549 : * Drivers known to implement it: MEM, netCDF.
550 : *
551 : * This is the same as the C function GDALGroupOpenVectorLayer().
552 : *
553 : * @param osName Vector layer name.
554 : * @param papszOptions Driver specific options determining how the layer should
555 : * be opened. Pass nullptr for default behavior.
556 : *
557 : * @return the group, or nullptr.
558 : */
559 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560 : CPL_UNUSED CSLConstList papszOptions) const
561 : {
562 2 : return nullptr;
563 : }
564 :
565 : /************************************************************************/
566 : /* GetDimensions() */
567 : /************************************************************************/
568 :
569 : /** Return the list of dimensions contained in this group and used by its
570 : * arrays.
571 : *
572 : * This is for dimensions that can potentially be used by several arrays.
573 : * Not all drivers might implement this. To retrieve the dimensions used by
574 : * a specific array, use GDALMDArray::GetDimensions().
575 : *
576 : * Drivers known to implement it: MEM, netCDF
577 : *
578 : * This is the same as the C function GDALGroupGetDimensions().
579 : *
580 : * @param papszOptions Driver specific options determining how groups
581 : * should be retrieved. Pass nullptr for default behavior.
582 : *
583 : * @return the dimensions.
584 : */
585 : std::vector<std::shared_ptr<GDALDimension>>
586 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587 : {
588 11 : return {};
589 : }
590 :
591 : /************************************************************************/
592 : /* GetStructuralInfo() */
593 : /************************************************************************/
594 :
595 : /** Return structural information on the group.
596 : *
597 : * This may be the compression, etc..
598 : *
599 : * The return value should not be freed and is valid until GDALGroup is
600 : * released or this function called again.
601 : *
602 : * This is the same as the C function GDALGroupGetStructuralInfo().
603 : */
604 42 : CSLConstList GDALGroup::GetStructuralInfo() const
605 : {
606 42 : return nullptr;
607 : }
608 :
609 : /************************************************************************/
610 : /* CreateGroup() */
611 : /************************************************************************/
612 :
613 : /** Create a sub-group within a group.
614 : *
615 : * Optionally implemented by drivers.
616 : *
617 : * Drivers known to implement it: MEM, netCDF
618 : *
619 : * This is the same as the C function GDALGroupCreateGroup().
620 : *
621 : * @param osName Sub-group name.
622 : * @param papszOptions Driver specific options determining how the sub-group
623 : * should be created.
624 : *
625 : * @return the new sub-group, or nullptr in case of error.
626 : */
627 : std::shared_ptr<GDALGroup>
628 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629 : CPL_UNUSED CSLConstList papszOptions)
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632 0 : return nullptr;
633 : }
634 :
635 : /************************************************************************/
636 : /* DeleteGroup() */
637 : /************************************************************************/
638 :
639 : /** Delete a sub-group from a group.
640 : *
641 : * Optionally implemented.
642 : *
643 : * After this call, if a previously obtained instance of the deleted object
644 : * is still alive, no method other than for freeing it should be invoked.
645 : *
646 : * Drivers known to implement it: MEM, Zarr
647 : *
648 : * This is the same as the C function GDALGroupDeleteGroup().
649 : *
650 : * @param osName Sub-group name.
651 : * @param papszOptions Driver specific options determining how the group.
652 : * should be deleted.
653 : *
654 : * @return true in case of success
655 : * @since GDAL 3.8
656 : */
657 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658 : CPL_UNUSED CSLConstList papszOptions)
659 : {
660 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661 0 : return false;
662 : }
663 :
664 : /************************************************************************/
665 : /* CreateDimension() */
666 : /************************************************************************/
667 :
668 : /** Create a dimension within a group.
669 : *
670 : * @note Driver implementation: drivers supporting CreateDimension() should
671 : * implement this method, but do not have necessarily to implement
672 : * GDALGroup::GetDimensions().
673 : *
674 : * Drivers known to implement it: MEM, netCDF
675 : *
676 : * This is the same as the C function GDALGroupCreateDimension().
677 : *
678 : * @param osName Dimension name.
679 : * @param osType Dimension type (might be empty, and ignored by drivers)
680 : * @param osDirection Dimension direction (might be empty, and ignored by
681 : * drivers)
682 : * @param nSize Number of values indexed by this dimension. Should be > 0.
683 : * @param papszOptions Driver specific options determining how the dimension
684 : * should be created.
685 : *
686 : * @return the new dimension, or nullptr if case of error
687 : */
688 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691 : CPL_UNUSED CSLConstList papszOptions)
692 : {
693 0 : CPLError(CE_Failure, CPLE_NotSupported,
694 : "CreateDimension() not implemented");
695 0 : return nullptr;
696 : }
697 :
698 : /************************************************************************/
699 : /* CreateMDArray() */
700 : /************************************************************************/
701 :
702 : /** Create a multidimensional array within a group.
703 : *
704 : * It is recommended that the GDALDimension objects passed in aoDimensions
705 : * belong to this group, either by retrieving them with GetDimensions()
706 : * or creating a new one with CreateDimension().
707 : *
708 : * Optionally implemented.
709 : *
710 : * Drivers known to implement it: MEM, netCDF
711 : *
712 : * This is the same as the C function GDALGroupCreateMDArray().
713 : *
714 : * @note Driver implementation: drivers should take into account the possibility
715 : * that GDALDimension object passed in aoDimensions might belong to a different
716 : * group / dataset / driver and act accordingly.
717 : *
718 : * @param osName Array name.
719 : * @param aoDimensions List of dimensions, ordered from the slowest varying
720 : * dimension first to the fastest varying dimension last.
721 : * Might be empty for a scalar array (if supported by
722 : * driver)
723 : * @param oDataType Array data type.
724 : * @param papszOptions Driver specific options determining how the array
725 : * should be created.
726 : *
727 : * @return the new array, or nullptr in case of error
728 : */
729 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730 : CPL_UNUSED const std::string &osName,
731 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732 : CPL_UNUSED const GDALExtendedDataType &oDataType,
733 : CPL_UNUSED CSLConstList papszOptions)
734 : {
735 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736 0 : return nullptr;
737 : }
738 :
739 : /************************************************************************/
740 : /* DeleteMDArray() */
741 : /************************************************************************/
742 :
743 : /** Delete an array from a group.
744 : *
745 : * Optionally implemented.
746 : *
747 : * After this call, if a previously obtained instance of the deleted object
748 : * is still alive, no method other than for freeing it should be invoked.
749 : *
750 : * Drivers known to implement it: MEM, Zarr
751 : *
752 : * This is the same as the C function GDALGroupDeleteMDArray().
753 : *
754 : * @param osName Arrayname.
755 : * @param papszOptions Driver specific options determining how the array.
756 : * should be deleted.
757 : *
758 : * @return true in case of success
759 : * @since GDAL 3.8
760 : */
761 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762 : CPL_UNUSED CSLConstList papszOptions)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765 0 : return false;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetTotalCopyCost() */
770 : /************************************************************************/
771 :
772 : /** Return a total "cost" to copy the group.
773 : *
774 : * Used as a parameter for CopFrom()
775 : */
776 32 : GUInt64 GDALGroup::GetTotalCopyCost() const
777 : {
778 32 : GUInt64 nCost = COPY_COST;
779 32 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780 :
781 64 : auto groupNames = GetGroupNames();
782 38 : for (const auto &name : groupNames)
783 : {
784 12 : auto subGroup = OpenGroup(name);
785 6 : if (subGroup)
786 : {
787 6 : nCost += subGroup->GetTotalCopyCost();
788 : }
789 : }
790 :
791 32 : auto arrayNames = GetMDArrayNames();
792 98 : for (const auto &name : arrayNames)
793 : {
794 132 : auto array = OpenMDArray(name);
795 66 : if (array)
796 : {
797 66 : nCost += array->GetTotalCopyCost();
798 : }
799 : }
800 64 : return nCost;
801 : }
802 :
803 : /************************************************************************/
804 : /* CopyFrom() */
805 : /************************************************************************/
806 :
807 : /** Copy the content of a group into a new (generally empty) group.
808 : *
809 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
811 : * of some output drivers this is not recommended)
812 : * @param poSrcGroup Source group. Must NOT be nullptr.
813 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
814 : * stop the copy. In relaxed mode, the copy will be attempted to
815 : * be pursued.
816 : * @param nCurCost Should be provided as a variable initially set to 0.
817 : * @param nTotalCost Total cost from GetTotalCopyCost().
818 : * @param pfnProgress Progress callback, or nullptr.
819 : * @param pProgressData Progress user data, or nulptr.
820 : * @param papszOptions Creation options. Currently, only array creation
821 : * options are supported. They must be prefixed with
822 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
823 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
824 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
825 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
826 : * Restriction to arrays of a given name is done with adding
827 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
828 : * a full qualified name.
829 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
830 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
831 : * scaled to UInt16 with scale and offset values being computed from the minimum
832 : * and maximum of the source array. The integer data type used can be set with
833 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
834 : *
835 : * @return true in case of success (or partial success if bStrict == false).
836 : */
837 32 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
838 : GDALDataset *poSrcDS,
839 : const std::shared_ptr<GDALGroup> &poSrcGroup,
840 : bool bStrict, GUInt64 &nCurCost,
841 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
842 : void *pProgressData, CSLConstList papszOptions)
843 : {
844 32 : if (pfnProgress == nullptr)
845 0 : pfnProgress = GDALDummyProgress;
846 :
847 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
848 : if (!(x)) \
849 : { \
850 : if (bStrict) \
851 : return false; \
852 : continue; \
853 : } \
854 : (void)0
855 :
856 : try
857 : {
858 32 : nCurCost += GDALGroup::COPY_COST;
859 :
860 64 : const auto srcDims = poSrcGroup->GetDimensions();
861 : std::map<std::string, std::shared_ptr<GDALDimension>>
862 64 : mapExistingDstDims;
863 64 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
864 84 : for (const auto &dim : srcDims)
865 : {
866 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
867 52 : dim->GetDirection(), dim->GetSize());
868 52 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
869 52 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
870 104 : auto poIndexingVarSrc(dim->GetIndexingVariable());
871 52 : if (poIndexingVarSrc)
872 : {
873 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
874 33 : ->GetName()] =
875 66 : dim->GetName();
876 : }
877 : }
878 :
879 64 : auto attrs = poSrcGroup->GetAttributes();
880 46 : for (const auto &attr : attrs)
881 : {
882 : auto dstAttr =
883 14 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
884 28 : attr->GetDataType());
885 14 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
886 14 : auto raw(attr->ReadAsRaw());
887 14 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
888 0 : return false;
889 : }
890 32 : if (!attrs.empty())
891 : {
892 7 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
893 7 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
894 0 : return false;
895 : }
896 :
897 : const auto CopyArray =
898 66 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
899 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
900 : papszOptions, bStrict, &nCurCost,
901 590 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
902 : {
903 : // Map source dimensions to target dimensions
904 132 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
905 66 : const auto &srcArrayDims(srcArray->GetDimensions());
906 165 : for (const auto &dim : srcArrayDims)
907 : {
908 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
909 99 : dim->GetFullName());
910 99 : if (dstDim && dstDim->GetSize() == dim->GetSize())
911 : {
912 89 : dstArrayDims.emplace_back(dstDim);
913 : }
914 : else
915 : {
916 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
917 19 : if (oIter != mapExistingDstDims.end() &&
918 9 : oIter->second->GetSize() == dim->GetSize())
919 : {
920 8 : dstArrayDims.emplace_back(oIter->second);
921 : }
922 : else
923 : {
924 2 : std::string newDimName;
925 2 : if (oIter == mapExistingDstDims.end())
926 : {
927 1 : newDimName = dim->GetName();
928 : }
929 : else
930 : {
931 1 : std::string newDimNamePrefix(srcArray->GetName() +
932 3 : '_' + dim->GetName());
933 1 : newDimName = newDimNamePrefix;
934 1 : int nIterCount = 2;
935 0 : while (
936 1 : cpl::contains(mapExistingDstDims, newDimName))
937 : {
938 0 : newDimName = newDimNamePrefix +
939 0 : CPLSPrintf("_%d", nIterCount);
940 0 : nIterCount++;
941 : }
942 : }
943 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
944 : dim->GetDirection(),
945 4 : dim->GetSize());
946 2 : if (!dstDim)
947 0 : return false;
948 2 : mapExistingDstDims[newDimName] = dstDim;
949 2 : dstArrayDims.emplace_back(dstDim);
950 : }
951 : }
952 : }
953 :
954 132 : CPLStringList aosArrayCO;
955 66 : bool bAutoScale = false;
956 66 : GDALDataType eAutoScaleType = GDT_UInt16;
957 73 : for (const char *pszItem : cpl::Iterate(papszOptions))
958 : {
959 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
960 : {
961 7 : const char *pszOption = pszItem + strlen("ARRAY:");
962 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
963 : {
964 1 : const char *pszNext = strchr(pszOption, ':');
965 1 : if (pszNext != nullptr)
966 : {
967 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
968 1 : if (static_cast<size_t>(nDim) ==
969 1 : dstArrayDims.size())
970 : {
971 1 : pszOption = pszNext + 1;
972 : }
973 : else
974 : {
975 0 : pszOption = nullptr;
976 : }
977 : }
978 : }
979 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
980 : {
981 2 : const char *pszName = pszOption + strlen("IF(NAME=");
982 2 : const char *pszNext = strchr(pszName, ':');
983 2 : if (pszNext != nullptr && pszNext > pszName &&
984 2 : pszNext[-1] == ')')
985 : {
986 4 : CPLString osName;
987 2 : osName.assign(pszName, pszNext - pszName - 1);
988 3 : if (osName == srcArray->GetName() ||
989 1 : osName == srcArray->GetFullName())
990 : {
991 2 : pszOption = pszNext + 1;
992 : }
993 : else
994 : {
995 0 : pszOption = nullptr;
996 : }
997 : }
998 : }
999 7 : if (pszOption)
1000 : {
1001 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1002 : {
1003 : bAutoScale =
1004 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1005 : }
1006 5 : else if (STARTS_WITH_CI(pszOption,
1007 : "AUTOSCALE_DATA_TYPE="))
1008 : {
1009 1 : const char *pszDataType =
1010 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1011 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1012 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1013 1 : GDALDataTypeIsFloating(eAutoScaleType))
1014 : {
1015 0 : CPLError(CE_Failure, CPLE_NotSupported,
1016 : "Unsupported value for "
1017 : "AUTOSCALE_DATA_TYPE");
1018 0 : return false;
1019 : }
1020 : }
1021 : else
1022 : {
1023 4 : aosArrayCO.AddString(pszOption);
1024 : }
1025 : }
1026 : }
1027 : }
1028 :
1029 66 : if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
1030 : {
1031 130 : const auto anBlockSize = srcArray->GetBlockSize();
1032 130 : std::string osBlockSize;
1033 162 : for (auto v : anBlockSize)
1034 : {
1035 97 : if (!osBlockSize.empty())
1036 34 : osBlockSize += ',';
1037 97 : osBlockSize += std::to_string(v);
1038 : }
1039 65 : if (!osBlockSize.empty())
1040 63 : aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
1041 : }
1042 :
1043 : auto oIterDimName =
1044 66 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1045 66 : const auto &srcArrayType = srcArray->GetDataType();
1046 :
1047 66 : std::shared_ptr<GDALMDArray> dstArray;
1048 :
1049 : // Only autoscale non-indexing variables
1050 66 : bool bHasOffset = false;
1051 66 : bool bHasScale = false;
1052 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1053 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1054 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1055 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1056 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1057 70 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1058 68 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1059 : {
1060 2 : constexpr bool bApproxOK = false;
1061 2 : constexpr bool bForce = true;
1062 2 : double dfMin = 0.0;
1063 2 : double dfMax = 0.0;
1064 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1065 : nullptr, nullptr, nullptr, nullptr,
1066 2 : nullptr) != CE_None)
1067 : {
1068 0 : CPLError(CE_Failure, CPLE_AppDefined,
1069 : "Could not retrieve statistics for array %s",
1070 0 : srcArray->GetName().c_str());
1071 0 : return false;
1072 : }
1073 2 : double dfDTMin = 0;
1074 2 : double dfDTMax = 0;
1075 : #define setDTMinMax(ctype) \
1076 : do \
1077 : { \
1078 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1079 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1080 : } while (0)
1081 :
1082 2 : switch (eAutoScaleType)
1083 : {
1084 0 : case GDT_Byte:
1085 0 : setDTMinMax(GByte);
1086 0 : break;
1087 0 : case GDT_Int8:
1088 0 : setDTMinMax(GInt8);
1089 0 : break;
1090 1 : case GDT_UInt16:
1091 1 : setDTMinMax(GUInt16);
1092 1 : break;
1093 1 : case GDT_Int16:
1094 1 : setDTMinMax(GInt16);
1095 1 : break;
1096 0 : case GDT_UInt32:
1097 0 : setDTMinMax(GUInt32);
1098 0 : break;
1099 0 : case GDT_Int32:
1100 0 : setDTMinMax(GInt32);
1101 0 : break;
1102 0 : case GDT_UInt64:
1103 0 : setDTMinMax(std::uint64_t);
1104 0 : break;
1105 0 : case GDT_Int64:
1106 0 : setDTMinMax(std::int64_t);
1107 0 : break;
1108 0 : case GDT_Float16:
1109 : case GDT_Float32:
1110 : case GDT_Float64:
1111 : case GDT_Unknown:
1112 : case GDT_CInt16:
1113 : case GDT_CInt32:
1114 : case GDT_CFloat16:
1115 : case GDT_CFloat32:
1116 : case GDT_CFloat64:
1117 : case GDT_TypeCount:
1118 0 : CPLAssert(false);
1119 : }
1120 :
1121 : dstArray =
1122 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1123 4 : GDALExtendedDataType::Create(eAutoScaleType),
1124 4 : aosArrayCO.List());
1125 2 : if (!dstArray)
1126 0 : return !bStrict;
1127 :
1128 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1129 : {
1130 : // If there's a nodata value in the source array, reserve
1131 : // DTMax for that purpose in the target scaled array
1132 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1133 : {
1134 0 : CPLError(CE_Failure, CPLE_AppDefined,
1135 : "Cannot set nodata value");
1136 0 : return false;
1137 : }
1138 1 : dfDTMax--;
1139 : }
1140 2 : const double dfScale =
1141 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1142 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1143 :
1144 4 : if (!dstArray->SetOffset(dfOffset) ||
1145 2 : !dstArray->SetScale(dfScale))
1146 : {
1147 0 : CPLError(CE_Failure, CPLE_AppDefined,
1148 : "Cannot set scale/offset");
1149 0 : return false;
1150 : }
1151 :
1152 2 : auto poUnscaled = dstArray->GetUnscaled();
1153 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1154 : {
1155 1 : poUnscaled->SetNoDataValue(
1156 : srcArray->GetNoDataValueAsDouble());
1157 : }
1158 :
1159 : // Copy source array into unscaled array
1160 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1161 : nCurCost, nTotalCost, pfnProgress,
1162 2 : pProgressData))
1163 : {
1164 0 : return false;
1165 : }
1166 : }
1167 : else
1168 : {
1169 128 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1170 128 : srcArrayType, aosArrayCO.List());
1171 64 : if (!dstArray)
1172 0 : return !bStrict;
1173 :
1174 128 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1175 : nCurCost, nTotalCost, pfnProgress,
1176 64 : pProgressData))
1177 : {
1178 0 : return false;
1179 : }
1180 : }
1181 :
1182 : // If this array is the indexing variable of a dimension, link them
1183 : // together.
1184 66 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1185 : {
1186 : auto oCorrespondingDimIter =
1187 33 : mapExistingDstDims.find(oIterDimName->second);
1188 33 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1189 : {
1190 : CPLErrorStateBackuper oErrorStateBackuper(
1191 33 : CPLQuietErrorHandler);
1192 66 : oCorrespondingDimIter->second->SetIndexingVariable(
1193 33 : std::move(dstArray));
1194 : }
1195 : }
1196 :
1197 66 : return true;
1198 32 : };
1199 :
1200 64 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1201 :
1202 : // Start by copying arrays that are indexing variables of dimensions
1203 98 : for (const auto &name : arrayNames)
1204 : {
1205 66 : auto srcArray = poSrcGroup->OpenMDArray(name);
1206 66 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1207 :
1208 66 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1209 66 : srcArray->GetName()))
1210 : {
1211 33 : if (!CopyArray(srcArray))
1212 0 : return false;
1213 : }
1214 : }
1215 :
1216 : // Then copy regular arrays
1217 98 : for (const auto &name : arrayNames)
1218 : {
1219 66 : auto srcArray = poSrcGroup->OpenMDArray(name);
1220 66 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1221 :
1222 66 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1223 66 : srcArray->GetName()))
1224 : {
1225 33 : if (!CopyArray(srcArray))
1226 0 : return false;
1227 : }
1228 : }
1229 :
1230 64 : const auto groupNames = poSrcGroup->GetGroupNames();
1231 38 : for (const auto &name : groupNames)
1232 : {
1233 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1234 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1235 6 : auto dstSubGroup = CreateGroup(name);
1236 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1237 12 : if (!dstSubGroup->CopyFrom(
1238 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1239 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1240 0 : return false;
1241 : }
1242 :
1243 32 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1244 0 : return false;
1245 :
1246 32 : return true;
1247 : }
1248 0 : catch (const std::exception &e)
1249 : {
1250 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1251 0 : return false;
1252 : }
1253 : }
1254 :
1255 : /************************************************************************/
1256 : /* GetInnerMostGroup() */
1257 : /************************************************************************/
1258 :
1259 : //! @cond Doxygen_Suppress
1260 : const GDALGroup *
1261 1452 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1262 : std::shared_ptr<GDALGroup> &curGroupHolder,
1263 : std::string &osLastPart) const
1264 : {
1265 1452 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1266 6 : return nullptr;
1267 1446 : const GDALGroup *poCurGroup = this;
1268 : CPLStringList aosTokens(
1269 2892 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1270 1446 : if (aosTokens.size() == 0)
1271 : {
1272 0 : return nullptr;
1273 : }
1274 :
1275 1784 : for (int i = 0; i < aosTokens.size() - 1; i++)
1276 : {
1277 350 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1278 350 : if (!curGroupHolder)
1279 : {
1280 12 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1281 : aosTokens[i]);
1282 12 : return nullptr;
1283 : }
1284 338 : poCurGroup = curGroupHolder.get();
1285 : }
1286 1434 : osLastPart = aosTokens[aosTokens.size() - 1];
1287 1434 : return poCurGroup;
1288 : }
1289 :
1290 : //! @endcond
1291 :
1292 : /************************************************************************/
1293 : /* OpenMDArrayFromFullname() */
1294 : /************************************************************************/
1295 :
1296 : /** Get an array from its fully qualified name */
1297 : std::shared_ptr<GDALMDArray>
1298 676 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1299 : CSLConstList papszOptions) const
1300 : {
1301 1352 : std::string osName;
1302 676 : std::shared_ptr<GDALGroup> curGroupHolder;
1303 676 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1304 676 : if (poGroup == nullptr)
1305 12 : return nullptr;
1306 664 : return poGroup->OpenMDArray(osName, papszOptions);
1307 : }
1308 :
1309 : /************************************************************************/
1310 : /* OpenAttributeFromFullname() */
1311 : /************************************************************************/
1312 :
1313 : /** Get an attribute from its fully qualified name */
1314 : std::shared_ptr<GDALAttribute>
1315 9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1316 : CSLConstList papszOptions) const
1317 : {
1318 9 : const auto pos = osFullName.rfind('/');
1319 9 : if (pos == std::string::npos)
1320 0 : return nullptr;
1321 18 : const std::string attrName = osFullName.substr(pos + 1);
1322 9 : if (pos == 0)
1323 2 : return GetAttribute(attrName);
1324 14 : const std::string container = osFullName.substr(0, pos);
1325 14 : auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1326 7 : if (poArray)
1327 4 : return poArray->GetAttribute(attrName);
1328 6 : auto poGroup = OpenGroupFromFullname(container, papszOptions);
1329 3 : if (poGroup)
1330 1 : return poGroup->GetAttribute(attrName);
1331 2 : return nullptr;
1332 : }
1333 :
1334 : /************************************************************************/
1335 : /* ResolveMDArray() */
1336 : /************************************************************************/
1337 :
1338 : /** Locate an array in a group and its subgroups by name.
1339 : *
1340 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1341 : * used
1342 : * Otherwise the search will start from the group identified by osStartingPath,
1343 : * and an array whose name is osName will be looked for in this group (if
1344 : * osStartingPath is empty or "/", then the current group is used). If there
1345 : * is no match, then a recursive descendent search will be made in its
1346 : * subgroups. If there is no match in the subgroups, then the parent (if
1347 : * existing) of the group pointed by osStartingPath will be used as the new
1348 : * starting point for the search.
1349 : *
1350 : * @param osName name, qualified or not
1351 : * @param osStartingPath fully qualified name of the (sub-)group from which
1352 : * the search should be started. If this is a non-empty
1353 : * string, the group on which this method is called should
1354 : * nominally be the root group (otherwise the path will
1355 : * be interpreted as from the current group)
1356 : * @param papszOptions options to pass to OpenMDArray()
1357 : * @since GDAL 3.2
1358 : */
1359 : std::shared_ptr<GDALMDArray>
1360 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1361 : const std::string &osStartingPath,
1362 : CSLConstList papszOptions) const
1363 : {
1364 19 : if (!osName.empty() && osName[0] == '/')
1365 : {
1366 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1367 1 : if (poArray)
1368 1 : return poArray;
1369 : }
1370 36 : std::string osPath(osStartingPath);
1371 36 : std::set<std::string> oSetAlreadyVisited;
1372 :
1373 : while (true)
1374 : {
1375 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1376 0 : std::shared_ptr<GDALGroup> poGroup;
1377 :
1378 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1379 22 : bool goOn = false;
1380 22 : if (osPath.empty() || osPath == "/")
1381 : {
1382 11 : goOn = true;
1383 : }
1384 : else
1385 : {
1386 22 : std::string osLastPart;
1387 : const GDALGroup *poGroupPtr =
1388 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1389 11 : if (poGroupPtr)
1390 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1391 22 : if (poGroup &&
1392 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1393 : {
1394 11 : oQueue.push(poGroup);
1395 11 : goOn = true;
1396 : }
1397 : }
1398 :
1399 22 : if (goOn)
1400 : {
1401 17 : do
1402 : {
1403 : const GDALGroup *groupPtr;
1404 39 : if (!oQueue.empty())
1405 : {
1406 28 : poGroup = oQueue.front();
1407 28 : oQueue.pop();
1408 28 : groupPtr = poGroup.get();
1409 : }
1410 : else
1411 : {
1412 11 : groupPtr = this;
1413 : }
1414 :
1415 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1416 39 : if (poArray)
1417 16 : return poArray;
1418 :
1419 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1420 47 : for (const auto &osGroupName : aosGroupNames)
1421 : {
1422 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1423 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1424 48 : poSubGroup->GetFullName()))
1425 : {
1426 24 : oQueue.push(poSubGroup);
1427 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1428 : }
1429 : }
1430 23 : } while (!oQueue.empty());
1431 : }
1432 :
1433 6 : if (osPath.empty() || osPath == "/")
1434 2 : break;
1435 :
1436 4 : const auto nPos = osPath.rfind('/');
1437 4 : if (nPos == 0)
1438 1 : osPath = "/";
1439 : else
1440 : {
1441 3 : if (nPos == std::string::npos)
1442 0 : break;
1443 3 : osPath.resize(nPos);
1444 : }
1445 4 : }
1446 2 : return nullptr;
1447 : }
1448 :
1449 : /************************************************************************/
1450 : /* OpenGroupFromFullname() */
1451 : /************************************************************************/
1452 :
1453 : /** Get a group from its fully qualified name.
1454 : * @since GDAL 3.2
1455 : */
1456 : std::shared_ptr<GDALGroup>
1457 564 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1458 : CSLConstList papszOptions) const
1459 : {
1460 1128 : std::string osName;
1461 564 : std::shared_ptr<GDALGroup> curGroupHolder;
1462 564 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1463 564 : if (poGroup == nullptr)
1464 4 : return nullptr;
1465 560 : return poGroup->OpenGroup(osName, papszOptions);
1466 : }
1467 :
1468 : /************************************************************************/
1469 : /* OpenDimensionFromFullname() */
1470 : /************************************************************************/
1471 :
1472 : /** Get a dimension from its fully qualified name */
1473 : std::shared_ptr<GDALDimension>
1474 201 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1475 : {
1476 402 : std::string osName;
1477 201 : std::shared_ptr<GDALGroup> curGroupHolder;
1478 201 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1479 201 : if (poGroup == nullptr)
1480 2 : return nullptr;
1481 398 : auto dims(poGroup->GetDimensions());
1482 342 : for (auto &dim : dims)
1483 : {
1484 290 : if (dim->GetName() == osName)
1485 147 : return dim;
1486 : }
1487 52 : return nullptr;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* ClearStatistics() */
1492 : /************************************************************************/
1493 :
1494 : /**
1495 : * \brief Clear statistics.
1496 : *
1497 : * @since GDAL 3.4
1498 : */
1499 0 : void GDALGroup::ClearStatistics()
1500 : {
1501 0 : auto groupNames = GetGroupNames();
1502 0 : for (const auto &name : groupNames)
1503 : {
1504 0 : auto subGroup = OpenGroup(name);
1505 0 : if (subGroup)
1506 : {
1507 0 : subGroup->ClearStatistics();
1508 : }
1509 : }
1510 :
1511 0 : auto arrayNames = GetMDArrayNames();
1512 0 : for (const auto &name : arrayNames)
1513 : {
1514 0 : auto array = OpenMDArray(name);
1515 0 : if (array)
1516 : {
1517 0 : array->ClearStatistics();
1518 : }
1519 : }
1520 0 : }
1521 :
1522 : /************************************************************************/
1523 : /* Rename() */
1524 : /************************************************************************/
1525 :
1526 : /** Rename the group.
1527 : *
1528 : * This is not implemented by all drivers.
1529 : *
1530 : * Drivers known to implement it: MEM, netCDF, ZARR.
1531 : *
1532 : * This is the same as the C function GDALGroupRename().
1533 : *
1534 : * @param osNewName New name.
1535 : *
1536 : * @return true in case of success
1537 : * @since GDAL 3.8
1538 : */
1539 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1540 : {
1541 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1542 0 : return false;
1543 : }
1544 :
1545 : /************************************************************************/
1546 : /* BaseRename() */
1547 : /************************************************************************/
1548 :
1549 : //! @cond Doxygen_Suppress
1550 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1551 : {
1552 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1553 8 : m_osFullName += osNewName;
1554 8 : m_osName = osNewName;
1555 :
1556 8 : NotifyChildrenOfRenaming();
1557 8 : }
1558 :
1559 : //! @endcond
1560 :
1561 : /************************************************************************/
1562 : /* ParentRenamed() */
1563 : /************************************************************************/
1564 :
1565 : //! @cond Doxygen_Suppress
1566 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1567 : {
1568 7 : m_osFullName = osNewParentFullName;
1569 7 : m_osFullName += "/";
1570 7 : m_osFullName += m_osName;
1571 :
1572 7 : NotifyChildrenOfRenaming();
1573 7 : }
1574 :
1575 : //! @endcond
1576 :
1577 : /************************************************************************/
1578 : /* Deleted() */
1579 : /************************************************************************/
1580 :
1581 : //! @cond Doxygen_Suppress
1582 22 : void GDALGroup::Deleted()
1583 : {
1584 22 : m_bValid = false;
1585 :
1586 22 : NotifyChildrenOfDeletion();
1587 22 : }
1588 :
1589 : //! @endcond
1590 :
1591 : /************************************************************************/
1592 : /* ParentDeleted() */
1593 : /************************************************************************/
1594 :
1595 : //! @cond Doxygen_Suppress
1596 3 : void GDALGroup::ParentDeleted()
1597 : {
1598 3 : Deleted();
1599 3 : }
1600 :
1601 : //! @endcond
1602 :
1603 : /************************************************************************/
1604 : /* CheckValidAndErrorOutIfNot() */
1605 : /************************************************************************/
1606 :
1607 : //! @cond Doxygen_Suppress
1608 17188 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1609 : {
1610 17188 : if (!m_bValid)
1611 : {
1612 14 : CPLError(CE_Failure, CPLE_AppDefined,
1613 : "This object has been deleted. No action on it is possible");
1614 : }
1615 17188 : return m_bValid;
1616 : }
1617 :
1618 : //! @endcond
1619 :
1620 : /************************************************************************/
1621 : /* ~GDALAbstractMDArray() */
1622 : /************************************************************************/
1623 :
1624 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1625 :
1626 : /************************************************************************/
1627 : /* GDALAbstractMDArray() */
1628 : /************************************************************************/
1629 :
1630 : //! @cond Doxygen_Suppress
1631 35820 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1632 35820 : const std::string &osName)
1633 : : m_osName(osName),
1634 : m_osFullName(
1635 35820 : !osParentName.empty()
1636 69718 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1637 141358 : : osName)
1638 : {
1639 35820 : }
1640 :
1641 : //! @endcond
1642 :
1643 : /************************************************************************/
1644 : /* GetDimensions() */
1645 : /************************************************************************/
1646 :
1647 : /** \fn GDALAbstractMDArray::GetDimensions() const
1648 : * \brief Return the dimensions of an attribute/array.
1649 : *
1650 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1651 : * similar to GDALAttributeGetDimensionsSize().
1652 : */
1653 :
1654 : /************************************************************************/
1655 : /* GetDataType() */
1656 : /************************************************************************/
1657 :
1658 : /** \fn GDALAbstractMDArray::GetDataType() const
1659 : * \brief Return the data type of an attribute/array.
1660 : *
1661 : * This is the same as the C functions GDALMDArrayGetDataType() and
1662 : * GDALAttributeGetDataType()
1663 : */
1664 :
1665 : /************************************************************************/
1666 : /* GetDimensionCount() */
1667 : /************************************************************************/
1668 :
1669 : /** Return the number of dimensions.
1670 : *
1671 : * Default implementation is GetDimensions().size(), and may be overridden by
1672 : * drivers if they have a faster / less expensive implementations.
1673 : *
1674 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1675 : * GDALAttributeGetDimensionCount().
1676 : *
1677 : */
1678 27206 : size_t GDALAbstractMDArray::GetDimensionCount() const
1679 : {
1680 27206 : return GetDimensions().size();
1681 : }
1682 :
1683 : /************************************************************************/
1684 : /* Rename() */
1685 : /************************************************************************/
1686 :
1687 : /** Rename the attribute/array.
1688 : *
1689 : * This is not implemented by all drivers.
1690 : *
1691 : * Drivers known to implement it: MEM, netCDF, Zarr.
1692 : *
1693 : * This is the same as the C functions GDALMDArrayRename() or
1694 : * GDALAttributeRename().
1695 : *
1696 : * @param osNewName New name.
1697 : *
1698 : * @return true in case of success
1699 : * @since GDAL 3.8
1700 : */
1701 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1702 : {
1703 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1704 0 : return false;
1705 : }
1706 :
1707 : /************************************************************************/
1708 : /* CopyValue() */
1709 : /************************************************************************/
1710 :
1711 : /** Convert a value from a source type to a destination type.
1712 : *
1713 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1714 : * that must be freed with CPLFree().
1715 : */
1716 84970 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1717 : const GDALExtendedDataType &srcType,
1718 : void *pDst,
1719 : const GDALExtendedDataType &dstType)
1720 : {
1721 164630 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1722 79660 : dstType.GetClass() == GEDTC_NUMERIC)
1723 : {
1724 78652 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1725 : dstType.GetNumericDataType(), 0, 1);
1726 78652 : return true;
1727 : }
1728 11406 : if (srcType.GetClass() == GEDTC_STRING &&
1729 5088 : dstType.GetClass() == GEDTC_STRING)
1730 : {
1731 : const char *srcStrPtr;
1732 3840 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1733 3840 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1734 3840 : *reinterpret_cast<void **>(pDst) = pszDup;
1735 3840 : return true;
1736 : }
1737 3486 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1738 1008 : dstType.GetClass() == GEDTC_STRING)
1739 : {
1740 1008 : const char *str = nullptr;
1741 1008 : switch (srcType.GetNumericDataType())
1742 : {
1743 0 : case GDT_Unknown:
1744 0 : break;
1745 0 : case GDT_Byte:
1746 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1747 0 : break;
1748 3 : case GDT_Int8:
1749 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1750 3 : break;
1751 66 : case GDT_UInt16:
1752 66 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1753 66 : break;
1754 0 : case GDT_Int16:
1755 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1756 0 : break;
1757 26 : case GDT_UInt32:
1758 26 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1759 26 : break;
1760 69 : case GDT_Int32:
1761 69 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1762 69 : break;
1763 0 : case GDT_UInt64:
1764 : str =
1765 0 : CPLSPrintf(CPL_FRMT_GUIB,
1766 : static_cast<GUIntBig>(
1767 : *static_cast<const std::uint64_t *>(pSrc)));
1768 0 : break;
1769 30 : case GDT_Int64:
1770 30 : str = CPLSPrintf(CPL_FRMT_GIB,
1771 : static_cast<GIntBig>(
1772 : *static_cast<const std::int64_t *>(pSrc)));
1773 30 : break;
1774 0 : case GDT_Float16:
1775 0 : str = CPLSPrintf("%.5g",
1776 : double(*static_cast<const GFloat16 *>(pSrc)));
1777 0 : break;
1778 60 : case GDT_Float32:
1779 120 : str = CPLSPrintf(
1780 : "%.9g",
1781 60 : static_cast<double>(*static_cast<const float *>(pSrc)));
1782 60 : break;
1783 752 : case GDT_Float64:
1784 752 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1785 752 : break;
1786 2 : case GDT_CInt16:
1787 : {
1788 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1789 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1790 2 : break;
1791 : }
1792 0 : case GDT_CInt32:
1793 : {
1794 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1795 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1796 0 : break;
1797 : }
1798 0 : case GDT_CFloat16:
1799 : {
1800 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1801 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1802 0 : break;
1803 : }
1804 0 : case GDT_CFloat32:
1805 : {
1806 0 : const float *src = static_cast<const float *>(pSrc);
1807 0 : str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1808 0 : break;
1809 : }
1810 0 : case GDT_CFloat64:
1811 : {
1812 0 : const double *src = static_cast<const double *>(pSrc);
1813 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1814 0 : break;
1815 : }
1816 0 : case GDT_TypeCount:
1817 0 : CPLAssert(false);
1818 : break;
1819 : }
1820 1008 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1821 1008 : *reinterpret_cast<void **>(pDst) = pszDup;
1822 1008 : return true;
1823 : }
1824 2718 : if (srcType.GetClass() == GEDTC_STRING &&
1825 1248 : dstType.GetClass() == GEDTC_NUMERIC)
1826 : {
1827 : const char *srcStrPtr;
1828 1248 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1829 1248 : if (dstType.GetNumericDataType() == GDT_Int64)
1830 : {
1831 2 : *(static_cast<int64_t *>(pDst)) =
1832 2 : srcStrPtr == nullptr ? 0
1833 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1834 : }
1835 1246 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1836 : {
1837 2 : *(static_cast<uint64_t *>(pDst)) =
1838 2 : srcStrPtr == nullptr
1839 2 : ? 0
1840 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1841 : }
1842 : else
1843 : {
1844 1244 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1845 1244 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1846 : dstType.GetNumericDataType(), 0, 1);
1847 : }
1848 1248 : return true;
1849 : }
1850 444 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1851 222 : dstType.GetClass() == GEDTC_COMPOUND)
1852 : {
1853 222 : const auto &srcComponents = srcType.GetComponents();
1854 222 : const auto &dstComponents = dstType.GetComponents();
1855 222 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1856 222 : GByte *pabyDst = static_cast<GByte *>(pDst);
1857 :
1858 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1859 444 : srcComponentMap;
1860 1078 : for (const auto &srcComp : srcComponents)
1861 : {
1862 856 : srcComponentMap[srcComp->GetName()] = &srcComp;
1863 : }
1864 598 : for (const auto &dstComp : dstComponents)
1865 : {
1866 376 : auto oIter = srcComponentMap.find(dstComp->GetName());
1867 376 : if (oIter == srcComponentMap.end())
1868 0 : return false;
1869 376 : const auto &srcComp = *(oIter->second);
1870 1128 : if (!GDALExtendedDataType::CopyValue(
1871 376 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1872 376 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1873 : {
1874 0 : return false;
1875 : }
1876 : }
1877 222 : return true;
1878 : }
1879 :
1880 0 : return false;
1881 : }
1882 :
1883 : /************************************************************************/
1884 : /* CopyValues() */
1885 : /************************************************************************/
1886 :
1887 : /** Convert severals value from a source type to a destination type.
1888 : *
1889 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1890 : * that must be freed with CPLFree().
1891 : */
1892 370 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1893 : const GDALExtendedDataType &srcType,
1894 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1895 : const GDALExtendedDataType &dstType,
1896 : GPtrDiff_t nDstStrideInElts,
1897 : size_t nValues)
1898 : {
1899 : const auto nSrcStrideInBytes =
1900 370 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1901 : const auto nDstStrideInBytes =
1902 370 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1903 636 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1904 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1905 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1906 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1907 902 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1908 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1909 : {
1910 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1911 : static_cast<int>(nSrcStrideInBytes), pDst,
1912 : dstType.GetNumericDataType(),
1913 : static_cast<int>(nDstStrideInBytes), nValues);
1914 : }
1915 : else
1916 : {
1917 104 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1918 104 : GByte *pabyDst = static_cast<GByte *>(pDst);
1919 208 : for (size_t i = 0; i < nValues; ++i)
1920 : {
1921 104 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1922 0 : return false;
1923 104 : pabySrc += nSrcStrideInBytes;
1924 104 : pabyDst += nDstStrideInBytes;
1925 : }
1926 : }
1927 370 : return true;
1928 : }
1929 :
1930 : /************************************************************************/
1931 : /* CheckReadWriteParams() */
1932 : /************************************************************************/
1933 : //! @cond Doxygen_Suppress
1934 10185 : bool GDALAbstractMDArray::CheckReadWriteParams(
1935 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1936 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1937 : const void *buffer, const void *buffer_alloc_start,
1938 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1939 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1940 : {
1941 0 : const auto lamda_error = []()
1942 : {
1943 0 : CPLError(CE_Failure, CPLE_AppDefined,
1944 : "Not all elements pointed by buffer will fit in "
1945 : "[buffer_alloc_start, "
1946 : "buffer_alloc_start + buffer_alloc_size]");
1947 0 : };
1948 :
1949 10185 : const auto &dims = GetDimensions();
1950 10185 : if (dims.empty())
1951 : {
1952 4430 : if (buffer_alloc_start)
1953 : {
1954 4032 : const size_t elementSize = bufferDataType.GetSize();
1955 4032 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1956 4032 : const GByte *paby_buffer_alloc_start =
1957 : static_cast<const GByte *>(buffer_alloc_start);
1958 4032 : const GByte *paby_buffer_alloc_end =
1959 : paby_buffer_alloc_start + buffer_alloc_size;
1960 :
1961 4032 : if (paby_buffer < paby_buffer_alloc_start ||
1962 4032 : paby_buffer + elementSize > paby_buffer_alloc_end)
1963 : {
1964 0 : lamda_error();
1965 0 : return false;
1966 : }
1967 : }
1968 4430 : return true;
1969 : }
1970 :
1971 5755 : if (arrayStep == nullptr)
1972 : {
1973 1670 : tmp_arrayStep.resize(dims.size(), 1);
1974 1670 : arrayStep = tmp_arrayStep.data();
1975 : }
1976 15806 : for (size_t i = 0; i < dims.size(); i++)
1977 : {
1978 10051 : assert(count);
1979 10051 : if (count[i] == 0)
1980 : {
1981 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1982 : static_cast<unsigned>(i));
1983 0 : return false;
1984 : }
1985 : }
1986 5755 : bool bufferStride_all_positive = true;
1987 5755 : if (bufferStride == nullptr)
1988 : {
1989 1350 : GPtrDiff_t stride = 1;
1990 1350 : assert(dims.empty() || count != nullptr);
1991 : // To compute strides we must proceed from the fastest varying dimension
1992 : // (the last one), and then reverse the result
1993 3008 : for (size_t i = dims.size(); i != 0;)
1994 : {
1995 1658 : --i;
1996 1658 : tmp_bufferStride.push_back(stride);
1997 1658 : GUInt64 newStride = 0;
1998 : bool bOK;
1999 : try
2000 : {
2001 1658 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2002 3316 : CPLSM(static_cast<uint64_t>(count[i])))
2003 1658 : .v();
2004 1658 : bOK = static_cast<size_t>(newStride) == newStride &&
2005 1658 : newStride < std::numeric_limits<size_t>::max() / 2;
2006 : }
2007 0 : catch (...)
2008 : {
2009 0 : bOK = false;
2010 : }
2011 1658 : if (!bOK)
2012 : {
2013 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2014 0 : return false;
2015 : }
2016 1658 : stride = static_cast<GPtrDiff_t>(newStride);
2017 : }
2018 1350 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2019 1350 : bufferStride = tmp_bufferStride.data();
2020 : }
2021 : else
2022 : {
2023 12796 : for (size_t i = 0; i < dims.size(); i++)
2024 : {
2025 8392 : if (bufferStride[i] < 0)
2026 : {
2027 1 : bufferStride_all_positive = false;
2028 1 : break;
2029 : }
2030 : }
2031 : }
2032 15777 : for (size_t i = 0; i < dims.size(); i++)
2033 : {
2034 10032 : assert(arrayStartIdx);
2035 10032 : assert(count);
2036 10032 : if (arrayStartIdx[i] >= dims[i]->GetSize())
2037 : {
2038 2 : CPLError(CE_Failure, CPLE_AppDefined,
2039 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2040 : static_cast<unsigned>(i),
2041 2 : static_cast<GUInt64>(arrayStartIdx[i]),
2042 2 : static_cast<GUInt64>(dims[i]->GetSize()));
2043 2 : return false;
2044 : }
2045 : bool bOverflow;
2046 10030 : if (arrayStep[i] >= 0)
2047 : {
2048 : try
2049 : {
2050 9375 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2051 9377 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2052 37503 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2053 9375 : .v() >= dims[i]->GetSize();
2054 : }
2055 1 : catch (...)
2056 : {
2057 1 : bOverflow = true;
2058 : }
2059 9376 : if (bOverflow)
2060 : {
2061 5 : CPLError(CE_Failure, CPLE_AppDefined,
2062 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2063 : ">= " CPL_FRMT_GUIB,
2064 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2065 : static_cast<unsigned>(i),
2066 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2067 5 : return false;
2068 : }
2069 : }
2070 : else
2071 : {
2072 : try
2073 : {
2074 654 : bOverflow =
2075 654 : arrayStartIdx[i] <
2076 654 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2077 1308 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2078 : ? (static_cast<uint64_t>(1) << 63)
2079 1308 : : static_cast<uint64_t>(-arrayStep[i])))
2080 654 : .v();
2081 : }
2082 0 : catch (...)
2083 : {
2084 0 : bOverflow = true;
2085 : }
2086 654 : if (bOverflow)
2087 : {
2088 3 : CPLError(
2089 : CE_Failure, CPLE_AppDefined,
2090 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2091 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2092 : static_cast<unsigned>(i));
2093 3 : return false;
2094 : }
2095 : }
2096 : }
2097 :
2098 5745 : if (buffer_alloc_start)
2099 : {
2100 2798 : const size_t elementSize = bufferDataType.GetSize();
2101 2798 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2102 2798 : const GByte *paby_buffer_alloc_start =
2103 : static_cast<const GByte *>(buffer_alloc_start);
2104 2798 : const GByte *paby_buffer_alloc_end =
2105 : paby_buffer_alloc_start + buffer_alloc_size;
2106 2798 : if (bufferStride_all_positive)
2107 : {
2108 2798 : if (paby_buffer < paby_buffer_alloc_start)
2109 : {
2110 0 : lamda_error();
2111 0 : return false;
2112 : }
2113 2798 : GUInt64 nOffset = elementSize;
2114 7972 : for (size_t i = 0; i < dims.size(); i++)
2115 : {
2116 : try
2117 : {
2118 5174 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2119 5174 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2120 10348 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2121 20696 : CPLSM(static_cast<uint64_t>(elementSize)))
2122 5174 : .v();
2123 : }
2124 0 : catch (...)
2125 : {
2126 0 : lamda_error();
2127 0 : return false;
2128 : }
2129 : }
2130 : #if SIZEOF_VOIDP == 4
2131 : if (static_cast<size_t>(nOffset) != nOffset)
2132 : {
2133 : lamda_error();
2134 : return false;
2135 : }
2136 : #endif
2137 2798 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2138 : {
2139 0 : lamda_error();
2140 0 : return false;
2141 : }
2142 : }
2143 0 : else if (dims.size() < 31)
2144 : {
2145 : // Check all corners of the hypercube
2146 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2147 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2148 : {
2149 0 : const GByte *paby = paby_buffer;
2150 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2151 : i++)
2152 : {
2153 0 : if (iCornerCode & (1U << i))
2154 : {
2155 : // We should check for integer overflows
2156 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2157 : }
2158 : }
2159 0 : if (paby < paby_buffer_alloc_start ||
2160 0 : paby + elementSize > paby_buffer_alloc_end)
2161 : {
2162 0 : lamda_error();
2163 0 : return false;
2164 : }
2165 : }
2166 : }
2167 : }
2168 :
2169 5745 : return true;
2170 : }
2171 :
2172 : //! @endcond
2173 :
2174 : /************************************************************************/
2175 : /* Read() */
2176 : /************************************************************************/
2177 :
2178 : /** Read part or totality of a multidimensional array or attribute.
2179 : *
2180 : * This will extract the content of a hyper-rectangle from the array into
2181 : * a user supplied buffer.
2182 : *
2183 : * If bufferDataType is of type string, the values written in pDstBuffer
2184 : * will be char* pointers and the strings should be freed with CPLFree().
2185 : *
2186 : * This is the same as the C function GDALMDArrayRead().
2187 : *
2188 : * @param arrayStartIdx Values representing the starting index to read
2189 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2190 : * Array of GetDimensionCount() values. Must not be
2191 : * nullptr, unless for a zero-dimensional array.
2192 : *
2193 : * @param count Values representing the number of values to extract in
2194 : * each dimension.
2195 : * Array of GetDimensionCount() values. Must not be
2196 : * nullptr, unless for a zero-dimensional array.
2197 : *
2198 : * @param arrayStep Spacing between values to extract in each dimension.
2199 : * The spacing is in number of array elements, not bytes.
2200 : * If provided, must contain GetDimensionCount() values.
2201 : * If set to nullptr, [1, 1, ... 1] will be used as a
2202 : * default to indicate consecutive elements.
2203 : *
2204 : * @param bufferStride Spacing between values to store in pDstBuffer.
2205 : * The spacing is in number of array elements, not bytes.
2206 : * If provided, must contain GetDimensionCount() values.
2207 : * Negative values are possible (for example to reorder
2208 : * from bottom-to-top to top-to-bottom).
2209 : * If set to nullptr, will be set so that pDstBuffer is
2210 : * written in a compact way, with elements of the last /
2211 : * fastest varying dimension being consecutive.
2212 : *
2213 : * @param bufferDataType Data type of values in pDstBuffer.
2214 : *
2215 : * @param pDstBuffer User buffer to store the values read. Should be big
2216 : * enough to store the number of values indicated by
2217 : * count[] and with the spacing of bufferStride[].
2218 : *
2219 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2220 : * validity of pDstBuffer. pDstBufferAllocStart
2221 : * should be the pointer returned by the malloc() or equivalent call used to
2222 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2223 : * bufferStride[] values are all positive), but not necessarily. If specified,
2224 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2225 : * validation is needed, nullptr can be passed.
2226 : *
2227 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2228 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2229 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2230 : * set to the appropriate value.
2231 : * If no validation is needed, 0 can be passed.
2232 : *
2233 : * @return true in case of success.
2234 : */
2235 3660 : bool GDALAbstractMDArray::Read(
2236 : const GUInt64 *arrayStartIdx, const size_t *count,
2237 : const GInt64 *arrayStep, // step in elements
2238 : const GPtrDiff_t *bufferStride, // stride in elements
2239 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2240 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2241 : {
2242 3660 : if (!GetDataType().CanConvertTo(bufferDataType))
2243 : {
2244 0 : CPLError(CE_Failure, CPLE_AppDefined,
2245 : "Array data type is not convertible to buffer data type");
2246 0 : return false;
2247 : }
2248 :
2249 7320 : std::vector<GInt64> tmp_arrayStep;
2250 7320 : std::vector<GPtrDiff_t> tmp_bufferStride;
2251 3660 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2252 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2253 : nDstBufferAllocSize, tmp_arrayStep,
2254 : tmp_bufferStride))
2255 : {
2256 0 : return false;
2257 : }
2258 :
2259 3660 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2260 3660 : pDstBuffer);
2261 : }
2262 :
2263 : /************************************************************************/
2264 : /* IWrite() */
2265 : /************************************************************************/
2266 :
2267 : //! @cond Doxygen_Suppress
2268 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2269 : const GInt64 *, const GPtrDiff_t *,
2270 : const GDALExtendedDataType &, const void *)
2271 : {
2272 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2273 1 : return false;
2274 : }
2275 :
2276 : //! @endcond
2277 :
2278 : /************************************************************************/
2279 : /* Write() */
2280 : /************************************************************************/
2281 :
2282 : /** Write part or totality of a multidimensional array or attribute.
2283 : *
2284 : * This will set the content of a hyper-rectangle into the array from
2285 : * a user supplied buffer.
2286 : *
2287 : * If bufferDataType is of type string, the values read from pSrcBuffer
2288 : * will be char* pointers.
2289 : *
2290 : * This is the same as the C function GDALMDArrayWrite().
2291 : *
2292 : * @param arrayStartIdx Values representing the starting index to write
2293 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2294 : * Array of GetDimensionCount() values. Must not be
2295 : * nullptr, unless for a zero-dimensional array.
2296 : *
2297 : * @param count Values representing the number of values to write in
2298 : * each dimension.
2299 : * Array of GetDimensionCount() values. Must not be
2300 : * nullptr, unless for a zero-dimensional array.
2301 : *
2302 : * @param arrayStep Spacing between values to write in each dimension.
2303 : * The spacing is in number of array elements, not bytes.
2304 : * If provided, must contain GetDimensionCount() values.
2305 : * If set to nullptr, [1, 1, ... 1] will be used as a
2306 : * default to indicate consecutive elements.
2307 : *
2308 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2309 : * The spacing is in number of array elements, not bytes.
2310 : * If provided, must contain GetDimensionCount() values.
2311 : * Negative values are possible (for example to reorder
2312 : * from bottom-to-top to top-to-bottom).
2313 : * If set to nullptr, will be set so that pSrcBuffer is
2314 : * written in a compact way, with elements of the last /
2315 : * fastest varying dimension being consecutive.
2316 : *
2317 : * @param bufferDataType Data type of values in pSrcBuffer.
2318 : *
2319 : * @param pSrcBuffer User buffer to read the values from. Should be big
2320 : * enough to store the number of values indicated by
2321 : * count[] and with the spacing of bufferStride[].
2322 : *
2323 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2324 : * validity of pSrcBuffer. pSrcBufferAllocStart
2325 : * should be the pointer returned by the malloc() or equivalent call used to
2326 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2327 : * bufferStride[] values are all positive), but not necessarily. If specified,
2328 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2329 : * validation is needed, nullptr can be passed.
2330 : *
2331 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2332 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2333 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2334 : * set to the appropriate value.
2335 : * If no validation is needed, 0 can be passed.
2336 : *
2337 : * @return true in case of success.
2338 : */
2339 2162 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2340 : const size_t *count, const GInt64 *arrayStep,
2341 : const GPtrDiff_t *bufferStride,
2342 : const GDALExtendedDataType &bufferDataType,
2343 : const void *pSrcBuffer,
2344 : const void *pSrcBufferAllocStart,
2345 : size_t nSrcBufferAllocSize)
2346 : {
2347 2162 : if (!bufferDataType.CanConvertTo(GetDataType()))
2348 : {
2349 0 : CPLError(CE_Failure, CPLE_AppDefined,
2350 : "Buffer data type is not convertible to array data type");
2351 0 : return false;
2352 : }
2353 :
2354 4324 : std::vector<GInt64> tmp_arrayStep;
2355 4324 : std::vector<GPtrDiff_t> tmp_bufferStride;
2356 2162 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2357 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2358 : nSrcBufferAllocSize, tmp_arrayStep,
2359 : tmp_bufferStride))
2360 : {
2361 0 : return false;
2362 : }
2363 :
2364 2162 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2365 2162 : pSrcBuffer);
2366 : }
2367 :
2368 : /************************************************************************/
2369 : /* GetTotalElementsCount() */
2370 : /************************************************************************/
2371 :
2372 : /** Return the total number of values in the array.
2373 : *
2374 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2375 : * and GDALAttributeGetTotalElementsCount().
2376 : *
2377 : */
2378 1408 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2379 : {
2380 1408 : const auto &dims = GetDimensions();
2381 1408 : if (dims.empty())
2382 752 : return 1;
2383 656 : GUInt64 nElts = 1;
2384 1448 : for (const auto &dim : dims)
2385 : {
2386 : try
2387 : {
2388 792 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2389 2376 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2390 792 : .v();
2391 : }
2392 0 : catch (...)
2393 : {
2394 0 : return 0;
2395 : }
2396 : }
2397 656 : return nElts;
2398 : }
2399 :
2400 : /************************************************************************/
2401 : /* GetBlockSize() */
2402 : /************************************************************************/
2403 :
2404 : /** Return the "natural" block size of the array along all dimensions.
2405 : *
2406 : * Some drivers might organize the array in tiles/blocks and reading/writing
2407 : * aligned on those tile/block boundaries will be more efficient.
2408 : *
2409 : * The returned number of elements in the vector is the same as
2410 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2411 : * the natural block size along the considered dimension.
2412 : * "Flat" arrays will typically return a vector of values set to 0.
2413 : *
2414 : * The default implementation will return a vector of values set to 0.
2415 : *
2416 : * This method is used by GetProcessingChunkSize().
2417 : *
2418 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2419 : * theoretical case of a 32-bit platform, this might exceed its size_t
2420 : * allocation capabilities.
2421 : *
2422 : * This is the same as the C function GDALMDArrayGetBlockSize().
2423 : *
2424 : * @return the block size, in number of elements along each dimension.
2425 : */
2426 297 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2427 : {
2428 297 : return std::vector<GUInt64>(GetDimensionCount());
2429 : }
2430 :
2431 : /************************************************************************/
2432 : /* GetProcessingChunkSize() */
2433 : /************************************************************************/
2434 :
2435 : /** \brief Return an optimal chunk size for read/write operations, given the
2436 : * natural block size and memory constraints specified.
2437 : *
2438 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2439 : * multiple of those returned by GetBlockSize() (unless the block define by
2440 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2441 : * returned by this method).
2442 : *
2443 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2444 : *
2445 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2446 : * chunk.
2447 : *
2448 : * @return the chunk size, in number of elements along each dimension.
2449 : */
2450 : std::vector<size_t>
2451 90 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2452 : {
2453 90 : const auto &dims = GetDimensions();
2454 90 : const auto &nDTSize = GetDataType().GetSize();
2455 90 : std::vector<size_t> anChunkSize;
2456 180 : auto blockSize = GetBlockSize();
2457 90 : CPLAssert(blockSize.size() == dims.size());
2458 90 : size_t nChunkSize = nDTSize;
2459 90 : bool bOverflow = false;
2460 90 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2461 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2462 : // [1, min(sizet_max, dim_size[i])]
2463 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2464 246 : for (size_t i = 0; i < dims.size(); i++)
2465 : {
2466 : const auto sizeDimI =
2467 312 : std::max(static_cast<size_t>(1),
2468 312 : static_cast<size_t>(
2469 312 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2470 156 : std::min(blockSize[i], dims[i]->GetSize()))));
2471 156 : anChunkSize.push_back(sizeDimI);
2472 156 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2473 : {
2474 4 : bOverflow = true;
2475 : }
2476 : else
2477 : {
2478 152 : nChunkSize *= sizeDimI;
2479 : }
2480 : }
2481 90 : if (nChunkSize == 0)
2482 0 : return anChunkSize;
2483 :
2484 : // If the product of all anChunkSize[i] does not fit on size_t, then
2485 : // set lowest anChunkSize[i] to 1.
2486 90 : if (bOverflow)
2487 : {
2488 2 : nChunkSize = nDTSize;
2489 2 : bOverflow = false;
2490 8 : for (size_t i = dims.size(); i > 0;)
2491 : {
2492 6 : --i;
2493 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2494 : {
2495 4 : bOverflow = true;
2496 4 : anChunkSize[i] = 1;
2497 : }
2498 : else
2499 : {
2500 2 : nChunkSize *= anChunkSize[i];
2501 : }
2502 : }
2503 : }
2504 :
2505 90 : nChunkSize = nDTSize;
2506 180 : std::vector<size_t> anAccBlockSizeFromStart;
2507 246 : for (size_t i = 0; i < dims.size(); i++)
2508 : {
2509 156 : nChunkSize *= anChunkSize[i];
2510 156 : anAccBlockSizeFromStart.push_back(nChunkSize);
2511 : }
2512 90 : if (nChunkSize <= nMaxChunkMemory / 2)
2513 : {
2514 86 : size_t nVoxelsFromEnd = 1;
2515 234 : for (size_t i = dims.size(); i > 0;)
2516 : {
2517 148 : --i;
2518 : const auto nCurBlockSize =
2519 148 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2520 148 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2521 148 : if (nMul >= 2)
2522 : {
2523 140 : const auto nSizeThisDim(dims[i]->GetSize());
2524 : const auto nBlocksThisDim =
2525 140 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2526 140 : anChunkSize[i] = static_cast<size_t>(std::min(
2527 140 : anChunkSize[i] *
2528 280 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2529 140 : nSizeThisDim));
2530 : }
2531 148 : nVoxelsFromEnd *= anChunkSize[i];
2532 : }
2533 : }
2534 90 : return anChunkSize;
2535 : }
2536 :
2537 : /************************************************************************/
2538 : /* BaseRename() */
2539 : /************************************************************************/
2540 :
2541 : //! @cond Doxygen_Suppress
2542 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2543 : {
2544 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2545 18 : m_osFullName += osNewName;
2546 18 : m_osName = osNewName;
2547 :
2548 18 : NotifyChildrenOfRenaming();
2549 18 : }
2550 :
2551 : //! @endcond
2552 :
2553 : //! @cond Doxygen_Suppress
2554 : /************************************************************************/
2555 : /* ParentRenamed() */
2556 : /************************************************************************/
2557 :
2558 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2559 : {
2560 50 : m_osFullName = osNewParentFullName;
2561 50 : m_osFullName += "/";
2562 50 : m_osFullName += m_osName;
2563 :
2564 50 : NotifyChildrenOfRenaming();
2565 50 : }
2566 :
2567 : //! @endcond
2568 :
2569 : /************************************************************************/
2570 : /* Deleted() */
2571 : /************************************************************************/
2572 :
2573 : //! @cond Doxygen_Suppress
2574 52 : void GDALAbstractMDArray::Deleted()
2575 : {
2576 52 : m_bValid = false;
2577 :
2578 52 : NotifyChildrenOfDeletion();
2579 52 : }
2580 :
2581 : //! @endcond
2582 :
2583 : /************************************************************************/
2584 : /* ParentDeleted() */
2585 : /************************************************************************/
2586 :
2587 : //! @cond Doxygen_Suppress
2588 28 : void GDALAbstractMDArray::ParentDeleted()
2589 : {
2590 28 : Deleted();
2591 28 : }
2592 :
2593 : //! @endcond
2594 :
2595 : /************************************************************************/
2596 : /* CheckValidAndErrorOutIfNot() */
2597 : /************************************************************************/
2598 :
2599 : //! @cond Doxygen_Suppress
2600 6550 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2601 : {
2602 6550 : if (!m_bValid)
2603 : {
2604 26 : CPLError(CE_Failure, CPLE_AppDefined,
2605 : "This object has been deleted. No action on it is possible");
2606 : }
2607 6550 : return m_bValid;
2608 : }
2609 :
2610 : //! @endcond
2611 :
2612 : /************************************************************************/
2613 : /* SetUnit() */
2614 : /************************************************************************/
2615 :
2616 : /** Set the variable unit.
2617 : *
2618 : * Values should conform as much as possible with those allowed by
2619 : * the NetCDF CF conventions:
2620 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2621 : * but others might be returned.
2622 : *
2623 : * Few examples are "meter", "degrees", "second", ...
2624 : * Empty value means unknown.
2625 : *
2626 : * This is the same as the C function GDALMDArraySetUnit()
2627 : *
2628 : * @note Driver implementation: optionally implemented.
2629 : *
2630 : * @param osUnit unit name.
2631 : * @return true in case of success.
2632 : */
2633 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2634 : {
2635 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2636 0 : return false;
2637 : }
2638 :
2639 : /************************************************************************/
2640 : /* GetUnit() */
2641 : /************************************************************************/
2642 :
2643 : /** Return the array unit.
2644 : *
2645 : * Values should conform as much as possible with those allowed by
2646 : * the NetCDF CF conventions:
2647 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2648 : * but others might be returned.
2649 : *
2650 : * Few examples are "meter", "degrees", "second", ...
2651 : * Empty value means unknown.
2652 : *
2653 : * This is the same as the C function GDALMDArrayGetUnit()
2654 : */
2655 5 : const std::string &GDALMDArray::GetUnit() const
2656 : {
2657 5 : static const std::string emptyString;
2658 5 : return emptyString;
2659 : }
2660 :
2661 : /************************************************************************/
2662 : /* SetSpatialRef() */
2663 : /************************************************************************/
2664 :
2665 : /** Assign a spatial reference system object to the array.
2666 : *
2667 : * This is the same as the C function GDALMDArraySetSpatialRef().
2668 : */
2669 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2670 : {
2671 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2672 0 : return false;
2673 : }
2674 :
2675 : /************************************************************************/
2676 : /* GetSpatialRef() */
2677 : /************************************************************************/
2678 :
2679 : /** Return the spatial reference system object associated with the array.
2680 : *
2681 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2682 : */
2683 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2684 : {
2685 4 : return nullptr;
2686 : }
2687 :
2688 : /************************************************************************/
2689 : /* GetRawNoDataValue() */
2690 : /************************************************************************/
2691 :
2692 : /** Return the nodata value as a "raw" value.
2693 : *
2694 : * The value returned might be nullptr in case of no nodata value. When
2695 : * a nodata value is registered, a non-nullptr will be returned whose size in
2696 : * bytes is GetDataType().GetSize().
2697 : *
2698 : * The returned value should not be modified or freed. It is valid until
2699 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2700 : * SetRawNoDataValue(), or any similar methods.
2701 : *
2702 : * @note Driver implementation: this method shall be implemented if nodata
2703 : * is supported.
2704 : *
2705 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2706 : *
2707 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2708 : */
2709 5 : const void *GDALMDArray::GetRawNoDataValue() const
2710 : {
2711 5 : return nullptr;
2712 : }
2713 :
2714 : /************************************************************************/
2715 : /* GetNoDataValueAsDouble() */
2716 : /************************************************************************/
2717 :
2718 : /** Return the nodata value as a double.
2719 : *
2720 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2721 : *
2722 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2723 : * a nodata value exists and can be converted to double. Might be nullptr.
2724 : *
2725 : * @return the nodata value as a double. A 0.0 value might also indicate the
2726 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2727 : * set to false then).
2728 : */
2729 22506 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2730 : {
2731 22506 : const void *pNoData = GetRawNoDataValue();
2732 22506 : double dfNoData = 0.0;
2733 22506 : const auto &eDT = GetDataType();
2734 22506 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2735 22506 : if (ok)
2736 : {
2737 22194 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2738 : GDT_Float64, 0, 1);
2739 : }
2740 22506 : if (pbHasNoData)
2741 465 : *pbHasNoData = ok;
2742 22506 : return dfNoData;
2743 : }
2744 :
2745 : /************************************************************************/
2746 : /* GetNoDataValueAsInt64() */
2747 : /************************************************************************/
2748 :
2749 : /** Return the nodata value as a Int64.
2750 : *
2751 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2752 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2753 : *
2754 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2755 : *
2756 : * @return the nodata value as a Int64
2757 : *
2758 : * @since GDAL 3.5
2759 : */
2760 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2761 : {
2762 12 : const void *pNoData = GetRawNoDataValue();
2763 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2764 12 : const auto &eDT = GetDataType();
2765 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2766 12 : if (ok)
2767 : {
2768 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2769 : GDT_Int64, 0, 1);
2770 : }
2771 12 : if (pbHasNoData)
2772 12 : *pbHasNoData = ok;
2773 12 : return nNoData;
2774 : }
2775 :
2776 : /************************************************************************/
2777 : /* GetNoDataValueAsUInt64() */
2778 : /************************************************************************/
2779 :
2780 : /** Return the nodata value as a UInt64.
2781 : *
2782 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2783 :
2784 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2785 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2786 : *
2787 : * @return the nodata value as a UInt64
2788 : *
2789 : * @since GDAL 3.5
2790 : */
2791 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2792 : {
2793 8 : const void *pNoData = GetRawNoDataValue();
2794 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2795 8 : const auto &eDT = GetDataType();
2796 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2797 8 : if (ok)
2798 : {
2799 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2800 : GDT_UInt64, 0, 1);
2801 : }
2802 8 : if (pbHasNoData)
2803 8 : *pbHasNoData = ok;
2804 8 : return nNoData;
2805 : }
2806 :
2807 : /************************************************************************/
2808 : /* SetRawNoDataValue() */
2809 : /************************************************************************/
2810 :
2811 : /** Set the nodata value as a "raw" value.
2812 : *
2813 : * The value passed might be nullptr in case of no nodata value. When
2814 : * a nodata value is registered, a non-nullptr whose size in
2815 : * bytes is GetDataType().GetSize() must be passed.
2816 : *
2817 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2818 : *
2819 : * @note Driver implementation: this method shall be implemented if setting
2820 : nodata
2821 : * is supported.
2822 :
2823 : * @return true in case of success.
2824 : */
2825 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2826 : {
2827 0 : CPLError(CE_Failure, CPLE_NotSupported,
2828 : "SetRawNoDataValue() not implemented");
2829 0 : return false;
2830 : }
2831 :
2832 : /************************************************************************/
2833 : /* SetNoDataValue() */
2834 : /************************************************************************/
2835 :
2836 : /** Set the nodata value as a double.
2837 : *
2838 : * If the natural data type of the attribute/array is not double, type
2839 : * conversion will occur to the type returned by GetDataType().
2840 : *
2841 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2842 : *
2843 : * @return true in case of success.
2844 : */
2845 61 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2846 : {
2847 61 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2848 61 : bool bRet = false;
2849 61 : if (GDALExtendedDataType::CopyValue(
2850 122 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2851 61 : GetDataType()))
2852 : {
2853 61 : bRet = SetRawNoDataValue(pRawNoData);
2854 : }
2855 61 : CPLFree(pRawNoData);
2856 61 : return bRet;
2857 : }
2858 :
2859 : /************************************************************************/
2860 : /* SetNoDataValue() */
2861 : /************************************************************************/
2862 :
2863 : /** Set the nodata value as a Int64.
2864 : *
2865 : * If the natural data type of the attribute/array is not Int64, type conversion
2866 : * will occur to the type returned by GetDataType().
2867 : *
2868 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2869 : *
2870 : * @return true in case of success.
2871 : *
2872 : * @since GDAL 3.5
2873 : */
2874 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2875 : {
2876 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2877 3 : bool bRet = false;
2878 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2879 6 : GDALExtendedDataType::Create(GDT_Int64),
2880 3 : pRawNoData, GetDataType()))
2881 : {
2882 3 : bRet = SetRawNoDataValue(pRawNoData);
2883 : }
2884 3 : CPLFree(pRawNoData);
2885 3 : return bRet;
2886 : }
2887 :
2888 : /************************************************************************/
2889 : /* SetNoDataValue() */
2890 : /************************************************************************/
2891 :
2892 : /** Set the nodata value as a Int64.
2893 : *
2894 : * If the natural data type of the attribute/array is not Int64, type conversion
2895 : * will occur to the type returned by GetDataType().
2896 : *
2897 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2898 : *
2899 : * @return true in case of success.
2900 : *
2901 : * @since GDAL 3.5
2902 : */
2903 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2904 : {
2905 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2906 1 : bool bRet = false;
2907 1 : if (GDALExtendedDataType::CopyValue(
2908 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2909 1 : GetDataType()))
2910 : {
2911 1 : bRet = SetRawNoDataValue(pRawNoData);
2912 : }
2913 1 : CPLFree(pRawNoData);
2914 1 : return bRet;
2915 : }
2916 :
2917 : /************************************************************************/
2918 : /* Resize() */
2919 : /************************************************************************/
2920 :
2921 : /** Resize an array to new dimensions.
2922 : *
2923 : * Not all drivers may allow this operation, and with restrictions (e.g.
2924 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2925 : *
2926 : * Resizing a dimension used in other arrays will cause those other arrays
2927 : * to be resized.
2928 : *
2929 : * This is the same as the C function GDALMDArrayResize().
2930 : *
2931 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2932 : * new size of each indexing dimension.
2933 : * @param papszOptions Options. (Driver specific)
2934 : * @return true in case of success.
2935 : * @since GDAL 3.7
2936 : */
2937 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2938 : CPL_UNUSED CSLConstList papszOptions)
2939 : {
2940 0 : CPLError(CE_Failure, CPLE_NotSupported,
2941 : "Resize() is not supported for this array");
2942 0 : return false;
2943 : }
2944 :
2945 : /************************************************************************/
2946 : /* SetScale() */
2947 : /************************************************************************/
2948 :
2949 : /** Set the scale value to apply to raw values.
2950 : *
2951 : * unscaled_value = raw_value * GetScale() + GetOffset()
2952 : *
2953 : * This is the same as the C function GDALMDArraySetScale() /
2954 : * GDALMDArraySetScaleEx().
2955 : *
2956 : * @note Driver implementation: this method shall be implemented if setting
2957 : * scale is supported.
2958 : *
2959 : * @param dfScale scale
2960 : * @param eStorageType Data type to which create the potential attribute that
2961 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2962 : * implementation will decide automatically the data type. Note that changing
2963 : * the data type after initial setting might not be supported.
2964 : * @return true in case of success.
2965 : */
2966 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2967 : CPL_UNUSED GDALDataType eStorageType)
2968 : {
2969 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2970 0 : return false;
2971 : }
2972 :
2973 : /************************************************************************/
2974 : /* SetOffset) */
2975 : /************************************************************************/
2976 :
2977 : /** Set the offset value to apply to raw values.
2978 : *
2979 : * unscaled_value = raw_value * GetScale() + GetOffset()
2980 : *
2981 : * This is the same as the C function GDALMDArraySetOffset() /
2982 : * GDALMDArraySetOffsetEx().
2983 : *
2984 : * @note Driver implementation: this method shall be implemented if setting
2985 : * offset is supported.
2986 : *
2987 : * @param dfOffset Offset
2988 : * @param eStorageType Data type to which create the potential attribute that
2989 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2990 : * implementation will decide automatically the data type. Note that changing
2991 : * the data type after initial setting might not be supported.
2992 : * @return true in case of success.
2993 : */
2994 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2995 : CPL_UNUSED GDALDataType eStorageType)
2996 : {
2997 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2998 0 : return false;
2999 : }
3000 :
3001 : /************************************************************************/
3002 : /* GetScale() */
3003 : /************************************************************************/
3004 :
3005 : /** Get the scale value to apply to raw values.
3006 : *
3007 : * unscaled_value = raw_value * GetScale() + GetOffset()
3008 : *
3009 : * This is the same as the C function GDALMDArrayGetScale().
3010 : *
3011 : * @note Driver implementation: this method shall be implemented if getting
3012 : * scale is supported.
3013 : *
3014 : * @param pbHasScale Pointer to a output boolean that will be set to true if
3015 : * a scale value exists. Might be nullptr.
3016 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3017 : * the storage type of the scale value, when known/relevant. Otherwise will be
3018 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3019 : *
3020 : * @return the scale value. A 1.0 value might also indicate the
3021 : * absence of a scale value.
3022 : */
3023 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3024 : CPL_UNUSED GDALDataType *peStorageType) const
3025 : {
3026 20 : if (pbHasScale)
3027 20 : *pbHasScale = false;
3028 20 : return 1.0;
3029 : }
3030 :
3031 : /************************************************************************/
3032 : /* GetOffset() */
3033 : /************************************************************************/
3034 :
3035 : /** Get the offset value to apply to raw values.
3036 : *
3037 : * unscaled_value = raw_value * GetScale() + GetOffset()
3038 : *
3039 : * This is the same as the C function GDALMDArrayGetOffset().
3040 : *
3041 : * @note Driver implementation: this method shall be implemented if getting
3042 : * offset is supported.
3043 : *
3044 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3045 : * a offset value exists. Might be nullptr.
3046 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3047 : * the storage type of the offset value, when known/relevant. Otherwise will be
3048 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3049 : *
3050 : * @return the offset value. A 0.0 value might also indicate the
3051 : * absence of a offset value.
3052 : */
3053 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3054 : CPL_UNUSED GDALDataType *peStorageType) const
3055 : {
3056 20 : if (pbHasOffset)
3057 20 : *pbHasOffset = false;
3058 20 : return 0.0;
3059 : }
3060 :
3061 : /************************************************************************/
3062 : /* ProcessPerChunk() */
3063 : /************************************************************************/
3064 :
3065 : namespace
3066 : {
3067 : enum class Caller
3068 : {
3069 : CALLER_END_OF_LOOP,
3070 : CALLER_IN_LOOP,
3071 : };
3072 : }
3073 :
3074 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3075 : *
3076 : * This method is to be used when doing operations on an array, or a subset of
3077 : * it, in a chunk by chunk way.
3078 : *
3079 : * @param arrayStartIdx Values representing the starting index to use
3080 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3081 : * Array of GetDimensionCount() values. Must not be
3082 : * nullptr, unless for a zero-dimensional array.
3083 : *
3084 : * @param count Values representing the number of values to use in
3085 : * each dimension.
3086 : * Array of GetDimensionCount() values. Must not be
3087 : * nullptr, unless for a zero-dimensional array.
3088 : *
3089 : * @param chunkSize Values representing the chunk size in each dimension.
3090 : * Might typically the output of GetProcessingChunkSize().
3091 : * Array of GetDimensionCount() values. Must not be
3092 : * nullptr, unless for a zero-dimensional array.
3093 : *
3094 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3095 : * Must NOT be nullptr.
3096 : *
3097 : * @param pUserData Pointer to pass as the value of the pUserData argument
3098 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3099 : *
3100 : * @return true in case of success.
3101 : */
3102 88 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3103 : const GUInt64 *count,
3104 : const size_t *chunkSize,
3105 : FuncProcessPerChunkType pfnFunc,
3106 : void *pUserData)
3107 : {
3108 88 : const auto &dims = GetDimensions();
3109 88 : if (dims.empty())
3110 : {
3111 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3112 : }
3113 :
3114 : // Sanity check
3115 86 : size_t nTotalChunkSize = 1;
3116 219 : for (size_t i = 0; i < dims.size(); i++)
3117 : {
3118 140 : const auto nSizeThisDim(dims[i]->GetSize());
3119 140 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3120 138 : arrayStartIdx[i] > nSizeThisDim - count[i])
3121 : {
3122 4 : CPLError(CE_Failure, CPLE_AppDefined,
3123 : "Inconsistent arrayStartIdx[] / count[] values "
3124 : "regarding array size");
3125 4 : return false;
3126 : }
3127 270 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3128 134 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3129 : {
3130 3 : CPLError(CE_Failure, CPLE_AppDefined,
3131 : "Inconsistent chunkSize[] values");
3132 3 : return false;
3133 : }
3134 133 : nTotalChunkSize *= chunkSize[i];
3135 : }
3136 :
3137 79 : size_t dimIdx = 0;
3138 158 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3139 158 : std::vector<size_t> chunkCount(dims.size());
3140 :
3141 : struct Stack
3142 : {
3143 : GUInt64 nBlockCounter = 0;
3144 : GUInt64 nBlocksMinusOne = 0;
3145 : size_t first_count = 0; // only used if nBlocks > 1
3146 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3147 : };
3148 :
3149 158 : std::vector<Stack> stack(dims.size());
3150 79 : GUInt64 iCurChunk = 0;
3151 79 : GUInt64 nChunkCount = 1;
3152 211 : for (size_t i = 0; i < dims.size(); i++)
3153 : {
3154 132 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3155 132 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3156 132 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3157 132 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3158 132 : if (stack[i].nBlocksMinusOne == 0)
3159 : {
3160 127 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3161 127 : chunkCount[i] = static_cast<size_t>(count[i]);
3162 : }
3163 : else
3164 : {
3165 5 : stack[i].first_count = static_cast<size_t>(
3166 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3167 : }
3168 : }
3169 :
3170 79 : lbl_next_depth:
3171 321 : if (dimIdx == dims.size())
3172 : {
3173 112 : ++iCurChunk;
3174 112 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3175 : iCurChunk, nChunkCount, pUserData))
3176 : {
3177 1 : return false;
3178 : }
3179 : }
3180 : else
3181 : {
3182 209 : if (stack[dimIdx].nBlocksMinusOne != 0)
3183 : {
3184 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3185 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3186 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3187 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3188 : while (true)
3189 : {
3190 33 : dimIdx++;
3191 33 : goto lbl_next_depth;
3192 33 : lbl_return_to_caller_in_loop:
3193 33 : --stack[dimIdx].nBlockCounter;
3194 33 : if (stack[dimIdx].nBlockCounter == 0)
3195 11 : break;
3196 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3197 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3198 : }
3199 :
3200 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3201 22 : chunkCount[dimIdx] =
3202 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3203 11 : chunkArrayStartIdx[dimIdx]);
3204 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3205 : }
3206 209 : dimIdx++;
3207 209 : goto lbl_next_depth;
3208 207 : lbl_return_to_caller_end_of_loop:
3209 207 : if (dimIdx == 0)
3210 78 : goto end;
3211 : }
3212 :
3213 240 : assert(dimIdx > 0);
3214 240 : dimIdx--;
3215 : // cppcheck-suppress negativeContainerIndex
3216 240 : switch (stack[dimIdx].return_point)
3217 : {
3218 207 : case Caller::CALLER_END_OF_LOOP:
3219 207 : goto lbl_return_to_caller_end_of_loop;
3220 33 : case Caller::CALLER_IN_LOOP:
3221 33 : goto lbl_return_to_caller_in_loop;
3222 : }
3223 78 : end:
3224 78 : return true;
3225 : }
3226 :
3227 : /************************************************************************/
3228 : /* GDALAttribute() */
3229 : /************************************************************************/
3230 :
3231 : //! @cond Doxygen_Suppress
3232 28441 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3233 0 : CPL_UNUSED const std::string &osName)
3234 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3235 28441 : : GDALAbstractMDArray(osParentName, osName)
3236 : #endif
3237 : {
3238 28441 : }
3239 :
3240 : GDALAttribute::~GDALAttribute() = default;
3241 :
3242 : //! @endcond
3243 :
3244 : /************************************************************************/
3245 : /* GetDimensionSize() */
3246 : /************************************************************************/
3247 :
3248 : /** Return the size of the dimensions of the attribute.
3249 : *
3250 : * This will be an empty array for a scalar (single value) attribute.
3251 : *
3252 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3253 : */
3254 655 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3255 : {
3256 655 : const auto &dims = GetDimensions();
3257 655 : std::vector<GUInt64> ret;
3258 655 : ret.reserve(dims.size());
3259 805 : for (const auto &dim : dims)
3260 150 : ret.push_back(dim->GetSize());
3261 655 : return ret;
3262 : }
3263 :
3264 : /************************************************************************/
3265 : /* GDALRawResult() */
3266 : /************************************************************************/
3267 :
3268 : //! @cond Doxygen_Suppress
3269 214 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3270 214 : size_t nEltCount)
3271 428 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3272 214 : m_raw(raw)
3273 : {
3274 214 : }
3275 :
3276 : //! @endcond
3277 :
3278 : /************************************************************************/
3279 : /* GDALRawResult() */
3280 : /************************************************************************/
3281 :
3282 : /** Move constructor. */
3283 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3284 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3285 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3286 : {
3287 0 : other.m_nEltCount = 0;
3288 0 : other.m_nSize = 0;
3289 0 : other.m_raw = nullptr;
3290 0 : }
3291 :
3292 : /************************************************************************/
3293 : /* FreeMe() */
3294 : /************************************************************************/
3295 :
3296 214 : void GDALRawResult::FreeMe()
3297 : {
3298 214 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3299 : {
3300 103 : GByte *pabyPtr = m_raw;
3301 103 : const auto nDTSize(m_dt.GetSize());
3302 206 : for (size_t i = 0; i < m_nEltCount; ++i)
3303 : {
3304 103 : m_dt.FreeDynamicMemory(pabyPtr);
3305 103 : pabyPtr += nDTSize;
3306 : }
3307 : }
3308 214 : VSIFree(m_raw);
3309 214 : }
3310 :
3311 : /************************************************************************/
3312 : /* operator=() */
3313 : /************************************************************************/
3314 :
3315 : /** Move assignment. */
3316 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3317 : {
3318 0 : FreeMe();
3319 0 : m_dt = std::move(other.m_dt);
3320 0 : m_nEltCount = other.m_nEltCount;
3321 0 : m_nSize = other.m_nSize;
3322 0 : m_raw = other.m_raw;
3323 0 : other.m_nEltCount = 0;
3324 0 : other.m_nSize = 0;
3325 0 : other.m_raw = nullptr;
3326 0 : return *this;
3327 : }
3328 :
3329 : /************************************************************************/
3330 : /* ~GDALRawResult() */
3331 : /************************************************************************/
3332 :
3333 : /** Destructor. */
3334 214 : GDALRawResult::~GDALRawResult()
3335 : {
3336 214 : FreeMe();
3337 214 : }
3338 :
3339 : /************************************************************************/
3340 : /* StealData() */
3341 : /************************************************************************/
3342 :
3343 : //! @cond Doxygen_Suppress
3344 : /** Return buffer to caller which becomes owner of it.
3345 : * Only to be used by GDALAttributeReadAsRaw().
3346 : */
3347 6 : GByte *GDALRawResult::StealData()
3348 : {
3349 6 : GByte *ret = m_raw;
3350 6 : m_raw = nullptr;
3351 6 : m_nEltCount = 0;
3352 6 : m_nSize = 0;
3353 6 : return ret;
3354 : }
3355 :
3356 : //! @endcond
3357 :
3358 : /************************************************************************/
3359 : /* ReadAsRaw() */
3360 : /************************************************************************/
3361 :
3362 : /** Return the raw value of an attribute.
3363 : *
3364 : *
3365 : * This is the same as the C function GDALAttributeReadAsRaw().
3366 : */
3367 214 : GDALRawResult GDALAttribute::ReadAsRaw() const
3368 : {
3369 214 : const auto nEltCount(GetTotalElementsCount());
3370 214 : const auto &dt(GetDataType());
3371 214 : const auto nDTSize(dt.GetSize());
3372 : GByte *res = static_cast<GByte *>(
3373 214 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3374 214 : if (!res)
3375 0 : return GDALRawResult(nullptr, dt, 0);
3376 214 : const auto &dims = GetDimensions();
3377 214 : const auto nDims = GetDimensionCount();
3378 428 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3379 428 : std::vector<size_t> count(1 + nDims);
3380 237 : for (size_t i = 0; i < nDims; i++)
3381 : {
3382 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3383 : }
3384 214 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3385 214 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3386 : {
3387 0 : VSIFree(res);
3388 0 : return GDALRawResult(nullptr, dt, 0);
3389 : }
3390 214 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3391 : }
3392 :
3393 : /************************************************************************/
3394 : /* ReadAsString() */
3395 : /************************************************************************/
3396 :
3397 : /** Return the value of an attribute as a string.
3398 : *
3399 : * The returned string should not be freed, and its lifetime does not
3400 : * excess a next call to ReadAsString() on the same object, or the deletion
3401 : * of the object itself.
3402 : *
3403 : * This function will only return the first element if there are several.
3404 : *
3405 : * This is the same as the C function GDALAttributeReadAsString()
3406 : *
3407 : * @return a string, or nullptr.
3408 : */
3409 1904 : const char *GDALAttribute::ReadAsString() const
3410 : {
3411 1904 : const auto nDims = GetDimensionCount();
3412 3808 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3413 3808 : std::vector<size_t> count(1 + nDims, 1);
3414 1904 : char *szRet = nullptr;
3415 1904 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3416 1904 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3417 5711 : sizeof(szRet)) ||
3418 1903 : szRet == nullptr)
3419 : {
3420 5 : return nullptr;
3421 : }
3422 1899 : m_osCachedVal = szRet;
3423 1899 : CPLFree(szRet);
3424 1899 : return m_osCachedVal.c_str();
3425 : }
3426 :
3427 : /************************************************************************/
3428 : /* ReadAsInt() */
3429 : /************************************************************************/
3430 :
3431 : /** Return the value of an attribute as a integer.
3432 : *
3433 : * This function will only return the first element if there are several.
3434 : *
3435 : * It can fail if its value can not be converted to integer.
3436 : *
3437 : * This is the same as the C function GDALAttributeReadAsInt()
3438 : *
3439 : * @return a integer, or INT_MIN in case of error.
3440 : */
3441 514 : int GDALAttribute::ReadAsInt() const
3442 : {
3443 514 : const auto nDims = GetDimensionCount();
3444 1028 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3445 514 : std::vector<size_t> count(1 + nDims, 1);
3446 514 : int nRet = INT_MIN;
3447 514 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3448 1028 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3449 1028 : return nRet;
3450 : }
3451 :
3452 : /************************************************************************/
3453 : /* ReadAsInt64() */
3454 : /************************************************************************/
3455 :
3456 : /** Return the value of an attribute as an int64_t.
3457 : *
3458 : * This function will only return the first element if there are several.
3459 : *
3460 : * It can fail if its value can not be converted to long.
3461 : *
3462 : * This is the same as the C function GDALAttributeReadAsInt64()
3463 : *
3464 : * @return an int64_t, or INT64_MIN in case of error.
3465 : */
3466 102 : int64_t GDALAttribute::ReadAsInt64() const
3467 : {
3468 102 : const auto nDims = GetDimensionCount();
3469 204 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3470 102 : std::vector<size_t> count(1 + nDims, 1);
3471 102 : int64_t nRet = INT64_MIN;
3472 102 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3473 204 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3474 204 : return nRet;
3475 : }
3476 :
3477 : /************************************************************************/
3478 : /* ReadAsDouble() */
3479 : /************************************************************************/
3480 :
3481 : /** Return the value of an attribute as a double.
3482 : *
3483 : * This function will only return the first element if there are several.
3484 : *
3485 : * It can fail if its value can not be converted to double.
3486 : *
3487 : * This is the same as the C function GDALAttributeReadAsInt()
3488 : *
3489 : * @return a double value.
3490 : */
3491 544 : double GDALAttribute::ReadAsDouble() const
3492 : {
3493 544 : const auto nDims = GetDimensionCount();
3494 1088 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3495 544 : std::vector<size_t> count(1 + nDims, 1);
3496 544 : double dfRet = 0;
3497 544 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3498 544 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3499 544 : sizeof(dfRet));
3500 1088 : return dfRet;
3501 : }
3502 :
3503 : /************************************************************************/
3504 : /* ReadAsStringArray() */
3505 : /************************************************************************/
3506 :
3507 : /** Return the value of an attribute as an array of strings.
3508 : *
3509 : * This is the same as the C function GDALAttributeReadAsStringArray()
3510 : */
3511 169 : CPLStringList GDALAttribute::ReadAsStringArray() const
3512 : {
3513 169 : const auto nElts = GetTotalElementsCount();
3514 169 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3515 0 : return CPLStringList();
3516 : char **papszList = static_cast<char **>(
3517 169 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3518 169 : const auto &dims = GetDimensions();
3519 169 : const auto nDims = GetDimensionCount();
3520 338 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3521 338 : std::vector<size_t> count(1 + nDims);
3522 264 : for (size_t i = 0; i < nDims; i++)
3523 : {
3524 95 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3525 : }
3526 169 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3527 169 : GDALExtendedDataType::CreateString(), papszList, papszList,
3528 169 : sizeof(char *) * static_cast<int>(nElts));
3529 662 : for (int i = 0; i < static_cast<int>(nElts); i++)
3530 : {
3531 493 : if (papszList[i] == nullptr)
3532 13 : papszList[i] = CPLStrdup("");
3533 : }
3534 169 : return CPLStringList(papszList);
3535 : }
3536 :
3537 : /************************************************************************/
3538 : /* ReadAsIntArray() */
3539 : /************************************************************************/
3540 :
3541 : /** Return the value of an attribute as an array of integers.
3542 : *
3543 : * This is the same as the C function GDALAttributeReadAsIntArray().
3544 : */
3545 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3546 : {
3547 15 : const auto nElts = GetTotalElementsCount();
3548 : #if SIZEOF_VOIDP == 4
3549 : if (nElts > static_cast<size_t>(nElts))
3550 : return {};
3551 : #endif
3552 15 : std::vector<int> res(static_cast<size_t>(nElts));
3553 15 : const auto &dims = GetDimensions();
3554 15 : const auto nDims = GetDimensionCount();
3555 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3556 30 : std::vector<size_t> count(1 + nDims);
3557 32 : for (size_t i = 0; i < nDims; i++)
3558 : {
3559 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3560 : }
3561 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3562 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3563 15 : res.size() * sizeof(res[0]));
3564 30 : return res;
3565 : }
3566 :
3567 : /************************************************************************/
3568 : /* ReadAsInt64Array() */
3569 : /************************************************************************/
3570 :
3571 : /** Return the value of an attribute as an array of int64_t.
3572 : *
3573 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3574 : */
3575 62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3576 : {
3577 62 : const auto nElts = GetTotalElementsCount();
3578 : #if SIZEOF_VOIDP == 4
3579 : if (nElts > static_cast<size_t>(nElts))
3580 : return {};
3581 : #endif
3582 62 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3583 62 : const auto &dims = GetDimensions();
3584 62 : const auto nDims = GetDimensionCount();
3585 124 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3586 124 : std::vector<size_t> count(1 + nDims);
3587 124 : for (size_t i = 0; i < nDims; i++)
3588 : {
3589 62 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3590 : }
3591 62 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3592 124 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3593 62 : res.size() * sizeof(res[0]));
3594 124 : return res;
3595 : }
3596 :
3597 : /************************************************************************/
3598 : /* ReadAsDoubleArray() */
3599 : /************************************************************************/
3600 :
3601 : /** Return the value of an attribute as an array of double.
3602 : *
3603 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3604 : */
3605 94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3606 : {
3607 94 : const auto nElts = GetTotalElementsCount();
3608 : #if SIZEOF_VOIDP == 4
3609 : if (nElts > static_cast<size_t>(nElts))
3610 : return {};
3611 : #endif
3612 94 : std::vector<double> res(static_cast<size_t>(nElts));
3613 94 : const auto &dims = GetDimensions();
3614 94 : const auto nDims = GetDimensionCount();
3615 188 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3616 188 : std::vector<size_t> count(1 + nDims);
3617 172 : for (size_t i = 0; i < nDims; i++)
3618 : {
3619 78 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3620 : }
3621 94 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3622 188 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3623 94 : res.size() * sizeof(res[0]));
3624 188 : return res;
3625 : }
3626 :
3627 : /************************************************************************/
3628 : /* Write() */
3629 : /************************************************************************/
3630 :
3631 : /** Write an attribute from raw values expressed in GetDataType()
3632 : *
3633 : * The values should be provided in the type of GetDataType() and there should
3634 : * be exactly GetTotalElementsCount() of them.
3635 : * If GetDataType() is a string, each value should be a char* pointer.
3636 : *
3637 : * This is the same as the C function GDALAttributeWriteRaw().
3638 : *
3639 : * @param pabyValue Buffer of nLen bytes.
3640 : * @param nLen Size of pabyValue in bytes. Should be equal to
3641 : * GetTotalElementsCount() * GetDataType().GetSize()
3642 : * @return true in case of success.
3643 : */
3644 151 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3645 : {
3646 151 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3647 : {
3648 0 : CPLError(CE_Failure, CPLE_AppDefined,
3649 : "Length is not of expected value");
3650 0 : return false;
3651 : }
3652 151 : const auto &dims = GetDimensions();
3653 151 : const auto nDims = GetDimensionCount();
3654 302 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3655 302 : std::vector<size_t> count(1 + nDims);
3656 174 : for (size_t i = 0; i < nDims; i++)
3657 : {
3658 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3659 : }
3660 151 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3661 151 : pabyValue, pabyValue, nLen);
3662 : }
3663 :
3664 : /************************************************************************/
3665 : /* Write() */
3666 : /************************************************************************/
3667 :
3668 : /** Write an attribute from a string value.
3669 : *
3670 : * Type conversion will be performed if needed. If the attribute contains
3671 : * multiple values, only the first one will be updated.
3672 : *
3673 : * This is the same as the C function GDALAttributeWriteString().
3674 : *
3675 : * @param pszValue Pointer to a string.
3676 : * @return true in case of success.
3677 : */
3678 391 : bool GDALAttribute::Write(const char *pszValue)
3679 : {
3680 391 : const auto nDims = GetDimensionCount();
3681 782 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3682 391 : std::vector<size_t> count(1 + nDims, 1);
3683 391 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3684 782 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3685 782 : sizeof(pszValue));
3686 : }
3687 :
3688 : /************************************************************************/
3689 : /* WriteInt() */
3690 : /************************************************************************/
3691 :
3692 : /** Write an attribute from a integer value.
3693 : *
3694 : * Type conversion will be performed if needed. If the attribute contains
3695 : * multiple values, only the first one will be updated.
3696 : *
3697 : * This is the same as the C function GDALAttributeWriteInt().
3698 : *
3699 : * @param nVal Value.
3700 : * @return true in case of success.
3701 : */
3702 22 : bool GDALAttribute::WriteInt(int nVal)
3703 : {
3704 22 : const auto nDims = GetDimensionCount();
3705 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3706 22 : std::vector<size_t> count(1 + nDims, 1);
3707 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3708 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3709 44 : sizeof(nVal));
3710 : }
3711 :
3712 : /************************************************************************/
3713 : /* WriteInt64() */
3714 : /************************************************************************/
3715 :
3716 : /** Write an attribute from an int64_t value.
3717 : *
3718 : * Type conversion will be performed if needed. If the attribute contains
3719 : * multiple values, only the first one will be updated.
3720 : *
3721 : * This is the same as the C function GDALAttributeWriteInt().
3722 : *
3723 : * @param nVal Value.
3724 : * @return true in case of success.
3725 : */
3726 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3727 : {
3728 11 : const auto nDims = GetDimensionCount();
3729 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3730 11 : std::vector<size_t> count(1 + nDims, 1);
3731 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3732 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3733 22 : sizeof(nVal));
3734 : }
3735 :
3736 : /************************************************************************/
3737 : /* Write() */
3738 : /************************************************************************/
3739 :
3740 : /** Write an attribute from a double value.
3741 : *
3742 : * Type conversion will be performed if needed. If the attribute contains
3743 : * multiple values, only the first one will be updated.
3744 : *
3745 : * This is the same as the C function GDALAttributeWriteDouble().
3746 : *
3747 : * @param dfVal Value.
3748 : * @return true in case of success.
3749 : */
3750 39 : bool GDALAttribute::Write(double dfVal)
3751 : {
3752 39 : const auto nDims = GetDimensionCount();
3753 78 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3754 39 : std::vector<size_t> count(1 + nDims, 1);
3755 39 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3756 78 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3757 78 : sizeof(dfVal));
3758 : }
3759 :
3760 : /************************************************************************/
3761 : /* Write() */
3762 : /************************************************************************/
3763 :
3764 : /** Write an attribute from an array of strings.
3765 : *
3766 : * Type conversion will be performed if needed.
3767 : *
3768 : * Exactly GetTotalElementsCount() strings must be provided
3769 : *
3770 : * This is the same as the C function GDALAttributeWriteStringArray().
3771 : *
3772 : * @param vals Array of strings.
3773 : * @return true in case of success.
3774 : */
3775 8 : bool GDALAttribute::Write(CSLConstList vals)
3776 : {
3777 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3778 : {
3779 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3780 1 : return false;
3781 : }
3782 7 : const auto nDims = GetDimensionCount();
3783 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3784 7 : std::vector<size_t> count(1 + nDims);
3785 7 : const auto &dims = GetDimensions();
3786 15 : for (size_t i = 0; i < nDims; i++)
3787 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3788 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3789 7 : GDALExtendedDataType::CreateString(), vals, vals,
3790 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3791 : }
3792 :
3793 : /************************************************************************/
3794 : /* Write() */
3795 : /************************************************************************/
3796 :
3797 : /** Write an attribute from an array of int.
3798 : *
3799 : * Type conversion will be performed if needed.
3800 : *
3801 : * Exactly GetTotalElementsCount() strings must be provided
3802 : *
3803 : * This is the same as the C function GDALAttributeWriteIntArray()
3804 : *
3805 : * @param vals Array of int.
3806 : * @param nVals Should be equal to GetTotalElementsCount().
3807 : * @return true in case of success.
3808 : */
3809 11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3810 : {
3811 11 : if (nVals != GetTotalElementsCount())
3812 : {
3813 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3814 1 : return false;
3815 : }
3816 10 : const auto nDims = GetDimensionCount();
3817 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3818 10 : std::vector<size_t> count(1 + nDims);
3819 10 : const auto &dims = GetDimensions();
3820 20 : for (size_t i = 0; i < nDims; i++)
3821 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3822 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3823 10 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3824 20 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3825 : }
3826 :
3827 : /************************************************************************/
3828 : /* Write() */
3829 : /************************************************************************/
3830 :
3831 : /** Write an attribute from an array of int64_t.
3832 : *
3833 : * Type conversion will be performed if needed.
3834 : *
3835 : * Exactly GetTotalElementsCount() strings must be provided
3836 : *
3837 : * This is the same as the C function GDALAttributeWriteLongArray()
3838 : *
3839 : * @param vals Array of int64_t.
3840 : * @param nVals Should be equal to GetTotalElementsCount().
3841 : * @return true in case of success.
3842 : */
3843 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3844 : {
3845 10 : if (nVals != GetTotalElementsCount())
3846 : {
3847 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3848 0 : return false;
3849 : }
3850 10 : const auto nDims = GetDimensionCount();
3851 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3852 10 : std::vector<size_t> count(1 + nDims);
3853 10 : const auto &dims = GetDimensions();
3854 20 : for (size_t i = 0; i < nDims; i++)
3855 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3856 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3857 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3858 10 : static_cast<size_t>(GetTotalElementsCount()) *
3859 10 : sizeof(int64_t));
3860 : }
3861 :
3862 : /************************************************************************/
3863 : /* Write() */
3864 : /************************************************************************/
3865 :
3866 : /** Write an attribute from an array of double.
3867 : *
3868 : * Type conversion will be performed if needed.
3869 : *
3870 : * Exactly GetTotalElementsCount() strings must be provided
3871 : *
3872 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3873 : *
3874 : * @param vals Array of double.
3875 : * @param nVals Should be equal to GetTotalElementsCount().
3876 : * @return true in case of success.
3877 : */
3878 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3879 : {
3880 7 : if (nVals != GetTotalElementsCount())
3881 : {
3882 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3883 1 : return false;
3884 : }
3885 6 : const auto nDims = GetDimensionCount();
3886 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3887 6 : std::vector<size_t> count(1 + nDims);
3888 6 : const auto &dims = GetDimensions();
3889 13 : for (size_t i = 0; i < nDims; i++)
3890 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3891 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3892 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3893 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3894 : }
3895 :
3896 : /************************************************************************/
3897 : /* GDALMDArray() */
3898 : /************************************************************************/
3899 :
3900 : //! @cond Doxygen_Suppress
3901 7379 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3902 : CPL_UNUSED const std::string &osName,
3903 0 : const std::string &osContext)
3904 : :
3905 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3906 : GDALAbstractMDArray(osParentName, osName),
3907 : #endif
3908 7379 : m_osContext(osContext)
3909 : {
3910 7379 : }
3911 :
3912 : //! @endcond
3913 :
3914 : /************************************************************************/
3915 : /* GetTotalCopyCost() */
3916 : /************************************************************************/
3917 :
3918 : /** Return a total "cost" to copy the array.
3919 : *
3920 : * Used as a parameter for CopyFrom()
3921 : */
3922 70 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3923 : {
3924 140 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3925 140 : GetTotalElementsCount() * GetDataType().GetSize();
3926 : }
3927 :
3928 : /************************************************************************/
3929 : /* CopyFromAllExceptValues() */
3930 : /************************************************************************/
3931 :
3932 : //! @cond Doxygen_Suppress
3933 :
3934 209 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3935 : bool bStrict, GUInt64 &nCurCost,
3936 : const GUInt64 nTotalCost,
3937 : GDALProgressFunc pfnProgress,
3938 : void *pProgressData)
3939 : {
3940 : // Nodata setting must be one of the first things done for TileDB
3941 209 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3942 209 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3943 : {
3944 13 : SetRawNoDataValue(pNoData);
3945 : }
3946 :
3947 209 : const bool bThisIsUnscaledArray =
3948 209 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3949 418 : auto attrs = poSrcArray->GetAttributes();
3950 282 : for (const auto &attr : attrs)
3951 : {
3952 73 : const auto &osAttrName = attr->GetName();
3953 73 : if (bThisIsUnscaledArray)
3954 : {
3955 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3956 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3957 1 : osAttrName == "valid_range")
3958 : {
3959 1 : continue;
3960 : }
3961 : }
3962 :
3963 72 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3964 144 : attr->GetDataType());
3965 72 : if (!dstAttr)
3966 : {
3967 0 : if (bStrict)
3968 0 : return false;
3969 0 : continue;
3970 : }
3971 72 : auto raw = attr->ReadAsRaw();
3972 72 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3973 0 : return false;
3974 : }
3975 209 : if (!attrs.empty())
3976 : {
3977 42 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3978 73 : if (pfnProgress &&
3979 31 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3980 0 : return false;
3981 : }
3982 :
3983 209 : auto srcSRS = poSrcArray->GetSpatialRef();
3984 209 : if (srcSRS)
3985 : {
3986 19 : SetSpatialRef(srcSRS.get());
3987 : }
3988 :
3989 209 : const std::string &osUnit(poSrcArray->GetUnit());
3990 209 : if (!osUnit.empty())
3991 : {
3992 22 : SetUnit(osUnit);
3993 : }
3994 :
3995 209 : bool bGotValue = false;
3996 209 : GDALDataType eOffsetStorageType = GDT_Unknown;
3997 : const double dfOffset =
3998 209 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3999 209 : if (bGotValue)
4000 : {
4001 3 : SetOffset(dfOffset, eOffsetStorageType);
4002 : }
4003 :
4004 209 : bGotValue = false;
4005 209 : GDALDataType eScaleStorageType = GDT_Unknown;
4006 209 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
4007 209 : if (bGotValue)
4008 : {
4009 3 : SetScale(dfScale, eScaleStorageType);
4010 : }
4011 :
4012 209 : return true;
4013 : }
4014 :
4015 : //! @endcond
4016 :
4017 : /************************************************************************/
4018 : /* CopyFrom() */
4019 : /************************************************************************/
4020 :
4021 : /** Copy the content of an array into a new (generally empty) array.
4022 : *
4023 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
4024 : * of some output drivers this is not recommended)
4025 : * @param poSrcArray Source array. Should NOT be nullptr.
4026 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
4027 : * stop the copy. In relaxed mode, the copy will be attempted to
4028 : * be pursued.
4029 : * @param nCurCost Should be provided as a variable initially set to 0.
4030 : * @param nTotalCost Total cost from GetTotalCopyCost().
4031 : * @param pfnProgress Progress callback, or nullptr.
4032 : * @param pProgressData Progress user data, or nulptr.
4033 : *
4034 : * @return true in case of success (or partial success if bStrict == false).
4035 : */
4036 68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4037 : const GDALMDArray *poSrcArray, bool bStrict,
4038 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
4039 : GDALProgressFunc pfnProgress, void *pProgressData)
4040 : {
4041 68 : if (pfnProgress == nullptr)
4042 4 : pfnProgress = GDALDummyProgress;
4043 :
4044 68 : nCurCost += GDALMDArray::COPY_COST;
4045 :
4046 68 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4047 : pfnProgress, pProgressData))
4048 : {
4049 0 : return false;
4050 : }
4051 :
4052 68 : const auto &dims = poSrcArray->GetDimensions();
4053 68 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4054 68 : if (dims.empty())
4055 : {
4056 2 : std::vector<GByte> abyTmp(nDTSize);
4057 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4058 2 : GetDataType(), &abyTmp[0]) &&
4059 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4060 4 : &abyTmp[0])) &&
4061 : bStrict)
4062 : {
4063 0 : return false;
4064 : }
4065 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4066 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4067 0 : return false;
4068 : }
4069 : else
4070 : {
4071 66 : std::vector<GUInt64> arrayStartIdx(dims.size());
4072 66 : std::vector<GUInt64> count(dims.size());
4073 172 : for (size_t i = 0; i < dims.size(); i++)
4074 : {
4075 106 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4076 : }
4077 :
4078 : struct CopyFunc
4079 : {
4080 : GDALMDArray *poDstArray = nullptr;
4081 : std::vector<GByte> abyTmp{};
4082 : GDALProgressFunc pfnProgress = nullptr;
4083 : void *pProgressData = nullptr;
4084 : GUInt64 nCurCost = 0;
4085 : GUInt64 nTotalCost = 0;
4086 : GUInt64 nTotalBytesThisArray = 0;
4087 : bool bStop = false;
4088 :
4089 84 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4090 : const GUInt64 *chunkArrayStartIdx,
4091 : const size_t *chunkCount, GUInt64 iCurChunk,
4092 : GUInt64 nChunkCount, void *pUserData)
4093 : {
4094 84 : const auto &dt(l_poSrcArray->GetDataType());
4095 84 : auto data = static_cast<CopyFunc *>(pUserData);
4096 84 : auto poDstArray = data->poDstArray;
4097 84 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4098 84 : nullptr, dt, &data->abyTmp[0]))
4099 : {
4100 1 : return false;
4101 : }
4102 : bool bRet =
4103 83 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4104 83 : nullptr, dt, &data->abyTmp[0]);
4105 83 : if (dt.NeedsFreeDynamicMemory())
4106 : {
4107 5 : const auto l_nDTSize = dt.GetSize();
4108 5 : GByte *ptr = &data->abyTmp[0];
4109 5 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4110 5 : size_t nEltCount = 1;
4111 10 : for (size_t i = 0; i < l_nDims; ++i)
4112 : {
4113 5 : nEltCount *= chunkCount[i];
4114 : }
4115 22 : for (size_t i = 0; i < nEltCount; i++)
4116 : {
4117 17 : dt.FreeDynamicMemory(ptr);
4118 17 : ptr += l_nDTSize;
4119 : }
4120 : }
4121 83 : if (!bRet)
4122 : {
4123 0 : return false;
4124 : }
4125 :
4126 83 : double dfCurCost =
4127 83 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4128 83 : data->nTotalBytesThisArray;
4129 83 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4130 : data->pProgressData))
4131 : {
4132 0 : data->bStop = true;
4133 0 : return false;
4134 : }
4135 :
4136 83 : return true;
4137 : }
4138 : };
4139 :
4140 66 : CopyFunc copyFunc;
4141 66 : copyFunc.poDstArray = this;
4142 66 : copyFunc.nCurCost = nCurCost;
4143 66 : copyFunc.nTotalCost = nTotalCost;
4144 66 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4145 66 : copyFunc.pfnProgress = pfnProgress;
4146 66 : copyFunc.pProgressData = pProgressData;
4147 : const char *pszSwathSize =
4148 66 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4149 : const size_t nMaxChunkSize =
4150 : pszSwathSize
4151 66 : ? static_cast<size_t>(
4152 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4153 1 : CPLAtoGIntBig(pszSwathSize)))
4154 : : static_cast<size_t>(
4155 65 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4156 65 : GDALGetCacheMax64() / 4));
4157 66 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4158 66 : size_t nRealChunkSize = nDTSize;
4159 172 : for (const auto &nChunkSize : anChunkSizes)
4160 : {
4161 106 : nRealChunkSize *= nChunkSize;
4162 : }
4163 : try
4164 : {
4165 66 : copyFunc.abyTmp.resize(nRealChunkSize);
4166 : }
4167 0 : catch (const std::exception &)
4168 : {
4169 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4170 : "Cannot allocate temporary buffer");
4171 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4172 0 : return false;
4173 : }
4174 197 : if (copyFunc.nTotalBytesThisArray != 0 &&
4175 65 : !const_cast<GDALMDArray *>(poSrcArray)
4176 65 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4177 : anChunkSizes.data(), CopyFunc::f,
4178 132 : ©Func) &&
4179 1 : (bStrict || copyFunc.bStop))
4180 : {
4181 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4182 0 : return false;
4183 : }
4184 66 : nCurCost += copyFunc.nTotalBytesThisArray;
4185 : }
4186 :
4187 68 : return true;
4188 : }
4189 :
4190 : /************************************************************************/
4191 : /* GetStructuralInfo() */
4192 : /************************************************************************/
4193 :
4194 : /** Return structural information on the array.
4195 : *
4196 : * This may be the compression, etc..
4197 : *
4198 : * The return value should not be freed and is valid until GDALMDArray is
4199 : * released or this function called again.
4200 : *
4201 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4202 : */
4203 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4204 : {
4205 95 : return nullptr;
4206 : }
4207 :
4208 : /************************************************************************/
4209 : /* AdviseRead() */
4210 : /************************************************************************/
4211 :
4212 : /** Advise driver of upcoming read requests.
4213 : *
4214 : * Some GDAL drivers operate more efficiently if they know in advance what
4215 : * set of upcoming read requests will be made. The AdviseRead() method allows
4216 : * an application to notify the driver of the region of interest.
4217 : *
4218 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4219 : * accelerate access via some drivers. One such case is when reading through
4220 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4221 : * with the region of interest defined by AdviseRead())
4222 : *
4223 : * This is the same as the C function GDALMDArrayAdviseRead().
4224 : *
4225 : * @param arrayStartIdx Values representing the starting index to read
4226 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4227 : * Array of GetDimensionCount() values.
4228 : * Can be nullptr as a synonymous for [0 for i in
4229 : * range(GetDimensionCount() ]
4230 : *
4231 : * @param count Values representing the number of values to extract in
4232 : * each dimension.
4233 : * Array of GetDimensionCount() values.
4234 : * Can be nullptr as a synonymous for
4235 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4236 : * range(GetDimensionCount() ]
4237 : *
4238 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4239 : * documentation.
4240 : *
4241 : * @return true in case of success (ignoring the advice is a success)
4242 : *
4243 : * @since GDAL 3.2
4244 : */
4245 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4246 : CSLConstList papszOptions) const
4247 : {
4248 25 : const auto nDimCount = GetDimensionCount();
4249 25 : if (nDimCount == 0)
4250 2 : return true;
4251 :
4252 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4253 23 : if (arrayStartIdx == nullptr)
4254 : {
4255 0 : tmp_arrayStartIdx.resize(nDimCount);
4256 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4257 : }
4258 :
4259 46 : std::vector<size_t> tmp_count;
4260 23 : if (count == nullptr)
4261 : {
4262 0 : tmp_count.resize(nDimCount);
4263 0 : const auto &dims = GetDimensions();
4264 0 : for (size_t i = 0; i < nDimCount; i++)
4265 : {
4266 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4267 : #if SIZEOF_VOIDP < 8
4268 : if (nSize != static_cast<size_t>(nSize))
4269 : {
4270 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4271 : return false;
4272 : }
4273 : #endif
4274 0 : tmp_count[i] = static_cast<size_t>(nSize);
4275 : }
4276 0 : count = tmp_count.data();
4277 : }
4278 :
4279 46 : std::vector<GInt64> tmp_arrayStep;
4280 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4281 23 : const GInt64 *arrayStep = nullptr;
4282 23 : const GPtrDiff_t *bufferStride = nullptr;
4283 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4284 46 : GDALExtendedDataType::Create(GDT_Unknown),
4285 : nullptr, nullptr, 0, tmp_arrayStep,
4286 : tmp_bufferStride))
4287 : {
4288 1 : return false;
4289 : }
4290 :
4291 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4292 : }
4293 :
4294 : /************************************************************************/
4295 : /* IAdviseRead() */
4296 : /************************************************************************/
4297 :
4298 : //! @cond Doxygen_Suppress
4299 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4300 : CSLConstList /* papszOptions*/) const
4301 : {
4302 3 : return true;
4303 : }
4304 :
4305 : //! @endcond
4306 :
4307 : /************************************************************************/
4308 : /* MassageName() */
4309 : /************************************************************************/
4310 :
4311 : //! @cond Doxygen_Suppress
4312 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4313 : {
4314 32 : std::string ret;
4315 604 : for (const char ch : inputName)
4316 : {
4317 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4318 138 : ret += '_';
4319 : else
4320 434 : ret += ch;
4321 : }
4322 32 : return ret;
4323 : }
4324 :
4325 : //! @endcond
4326 :
4327 : /************************************************************************/
4328 : /* GetCacheRootGroup() */
4329 : /************************************************************************/
4330 :
4331 : //! @cond Doxygen_Suppress
4332 : std::shared_ptr<GDALGroup>
4333 1600 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4334 : std::string &osCacheFilenameOut) const
4335 : {
4336 1600 : const auto &osFilename = GetFilename();
4337 1600 : if (osFilename.empty())
4338 : {
4339 1 : CPLError(CE_Failure, CPLE_AppDefined,
4340 : "Cannot cache an array with an empty filename");
4341 1 : return nullptr;
4342 : }
4343 :
4344 1599 : osCacheFilenameOut = osFilename + ".gmac";
4345 1599 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4346 : {
4347 0 : const auto nPosQuestionMark = osFilename.find('?');
4348 0 : if (nPosQuestionMark != std::string::npos)
4349 : {
4350 : osCacheFilenameOut =
4351 0 : osFilename.substr(0, nPosQuestionMark)
4352 0 : .append(".gmac")
4353 0 : .append(osFilename.substr(nPosQuestionMark));
4354 : }
4355 : }
4356 1599 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4357 1599 : if (pszProxy != nullptr)
4358 7 : osCacheFilenameOut = pszProxy;
4359 :
4360 1599 : std::unique_ptr<GDALDataset> poDS;
4361 : VSIStatBufL sStat;
4362 1599 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4363 : {
4364 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4365 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4366 : nullptr, nullptr, nullptr));
4367 : }
4368 1599 : if (poDS)
4369 : {
4370 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4371 28 : return poDS->GetRootGroup();
4372 : }
4373 :
4374 1571 : if (bCanCreate)
4375 : {
4376 4 : const char *pszDrvName = "netCDF";
4377 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4378 4 : if (poDrv == nullptr)
4379 : {
4380 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4381 : pszDrvName);
4382 0 : return nullptr;
4383 : }
4384 : {
4385 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4386 8 : CPLErrorStateBackuper oErrorStateBackuper;
4387 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4388 : nullptr, nullptr));
4389 : }
4390 4 : if (!poDS)
4391 : {
4392 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4393 1 : if (pszProxy)
4394 : {
4395 1 : osCacheFilenameOut = pszProxy;
4396 1 : poDS.reset(poDrv->CreateMultiDimensional(
4397 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4398 : }
4399 : }
4400 4 : if (poDS)
4401 : {
4402 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4403 4 : return poDS->GetRootGroup();
4404 : }
4405 : else
4406 : {
4407 0 : CPLError(CE_Failure, CPLE_AppDefined,
4408 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4409 : "configuration option to write the cache in "
4410 : "another directory",
4411 : osCacheFilenameOut.c_str());
4412 : }
4413 : }
4414 :
4415 1567 : return nullptr;
4416 : }
4417 :
4418 : //! @endcond
4419 :
4420 : /************************************************************************/
4421 : /* Cache() */
4422 : /************************************************************************/
4423 :
4424 : /** Cache the content of the array into an auxiliary filename.
4425 : *
4426 : * The main purpose of this method is to be able to cache views that are
4427 : * expensive to compute, such as transposed arrays.
4428 : *
4429 : * The array will be stored in a file whose name is the one of
4430 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4431 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4432 : *
4433 : * If the .gmac file cannot be written next to the dataset, the
4434 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4435 : * directory.
4436 : *
4437 : * The GDALMDArray::Read() method will automatically use the cache when it
4438 : * exists. There is no timestamp checks between the source array and the cached
4439 : * array. If the source arrays changes, the cache must be manually deleted.
4440 : *
4441 : * This is the same as the C function GDALMDArrayCache()
4442 : *
4443 : * @note Driver implementation: optionally implemented.
4444 : *
4445 : * @param papszOptions List of options, null terminated, or NULL. Currently
4446 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4447 : * to specify the block size of the cached array.
4448 : * @return true in case of success.
4449 : */
4450 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4451 : {
4452 14 : std::string osCacheFilename;
4453 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4454 7 : if (!poRG)
4455 1 : return false;
4456 :
4457 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4458 6 : if (poRG->OpenMDArray(osCachedArrayName))
4459 : {
4460 2 : CPLError(CE_Failure, CPLE_NotSupported,
4461 : "An array with same name %s already exists in %s",
4462 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4463 2 : return false;
4464 : }
4465 :
4466 8 : CPLStringList aosOptions;
4467 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4468 4 : const auto &aoDims = GetDimensions();
4469 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4470 4 : if (!aoDims.empty())
4471 : {
4472 : std::string osBlockSize(
4473 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4474 4 : if (osBlockSize.empty())
4475 : {
4476 6 : const auto anBlockSize = GetBlockSize();
4477 3 : int idxDim = 0;
4478 10 : for (auto nBlockSize : anBlockSize)
4479 : {
4480 7 : if (idxDim > 0)
4481 4 : osBlockSize += ',';
4482 7 : if (nBlockSize == 0)
4483 7 : nBlockSize = 256;
4484 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4485 : osBlockSize +=
4486 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4487 7 : idxDim++;
4488 : }
4489 : }
4490 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4491 :
4492 4 : int idxDim = 0;
4493 13 : for (const auto &poDim : aoDims)
4494 : {
4495 9 : auto poNewDim = poRG->CreateDimension(
4496 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4497 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4498 9 : if (!poNewDim)
4499 0 : return false;
4500 9 : aoNewDims.emplace_back(poNewDim);
4501 9 : idxDim++;
4502 : }
4503 : }
4504 :
4505 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4506 8 : GetDataType(), aosOptions.List());
4507 4 : if (!poCachedArray)
4508 : {
4509 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4510 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4511 0 : return false;
4512 : }
4513 :
4514 4 : GUInt64 nCost = 0;
4515 8 : return poCachedArray->CopyFrom(nullptr, this,
4516 : false, // strict
4517 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4518 : }
4519 :
4520 : /************************************************************************/
4521 : /* Read() */
4522 : /************************************************************************/
4523 :
4524 4340 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4525 : const GInt64 *arrayStep, // step in elements
4526 : const GPtrDiff_t *bufferStride, // stride in elements
4527 : const GDALExtendedDataType &bufferDataType,
4528 : void *pDstBuffer, const void *pDstBufferAllocStart,
4529 : size_t nDstBufferAllocSize) const
4530 : {
4531 4340 : if (!m_bHasTriedCachedArray)
4532 : {
4533 2047 : m_bHasTriedCachedArray = true;
4534 2047 : if (IsCacheable())
4535 : {
4536 2047 : const auto &osFilename = GetFilename();
4537 3442 : if (!osFilename.empty() &&
4538 3442 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4539 : {
4540 2770 : std::string osCacheFilename;
4541 2770 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4542 1385 : if (poRG)
4543 : {
4544 : const std::string osCachedArrayName(
4545 32 : MassageName(GetFullName()));
4546 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4547 16 : if (m_poCachedArray)
4548 : {
4549 6 : const auto &dims = GetDimensions();
4550 : const auto &cachedDims =
4551 6 : m_poCachedArray->GetDimensions();
4552 6 : const size_t nDims = dims.size();
4553 : bool ok =
4554 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4555 6 : cachedDims.size() == nDims;
4556 19 : for (size_t i = 0; ok && i < nDims; ++i)
4557 : {
4558 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4559 : }
4560 6 : if (ok)
4561 : {
4562 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4563 : osCachedArrayName.c_str(),
4564 : osCacheFilename.c_str());
4565 : }
4566 : else
4567 : {
4568 0 : CPLError(CE_Warning, CPLE_AppDefined,
4569 : "Cached array %s in %s has incompatible "
4570 : "characteristics with current array.",
4571 : osCachedArrayName.c_str(),
4572 : osCacheFilename.c_str());
4573 0 : m_poCachedArray.reset();
4574 : }
4575 : }
4576 : }
4577 : }
4578 : }
4579 : }
4580 :
4581 4340 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4582 4340 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4583 : {
4584 0 : CPLError(CE_Failure, CPLE_AppDefined,
4585 : "Array data type is not convertible to buffer data type");
4586 0 : return false;
4587 : }
4588 :
4589 8680 : std::vector<GInt64> tmp_arrayStep;
4590 8680 : std::vector<GPtrDiff_t> tmp_bufferStride;
4591 4340 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4592 : bufferStride, bufferDataType, pDstBuffer,
4593 : pDstBufferAllocStart, nDstBufferAllocSize,
4594 : tmp_arrayStep, tmp_bufferStride))
4595 : {
4596 9 : return false;
4597 : }
4598 :
4599 4331 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4600 4331 : bufferDataType, pDstBuffer);
4601 : }
4602 :
4603 : /************************************************************************/
4604 : /* GetRootGroup() */
4605 : /************************************************************************/
4606 :
4607 : /** Return the root group to which this arrays belongs too.
4608 : *
4609 : * Note that arrays may be free standing and some drivers may not implement
4610 : * this method, hence nullptr may be returned.
4611 : *
4612 : * It is used internally by the GetResampled() method to detect if GLT
4613 : * orthorectification is available.
4614 : *
4615 : * @return the root group, or nullptr.
4616 : * @since GDAL 3.8
4617 : */
4618 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4619 : {
4620 0 : return nullptr;
4621 : }
4622 :
4623 : //! @cond Doxygen_Suppress
4624 :
4625 : /************************************************************************/
4626 : /* IsTransposedRequest() */
4627 : /************************************************************************/
4628 :
4629 947 : bool GDALMDArray::IsTransposedRequest(
4630 : const size_t *count,
4631 : const GPtrDiff_t *bufferStride) const // stride in elements
4632 : {
4633 : /*
4634 : For example:
4635 : count = [2,3,4]
4636 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4637 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4638 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4639 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4640 : */
4641 947 : const size_t nDims(GetDimensionCount());
4642 947 : size_t nCurStrideForRowMajorStrides = 1;
4643 947 : bool bRowMajorStrides = true;
4644 947 : size_t nElts = 1;
4645 947 : size_t nLastIdx = 0;
4646 2513 : for (size_t i = nDims; i > 0;)
4647 : {
4648 1566 : --i;
4649 1566 : if (bufferStride[i] < 0)
4650 0 : return false;
4651 1566 : if (static_cast<size_t>(bufferStride[i]) !=
4652 : nCurStrideForRowMajorStrides)
4653 : {
4654 278 : bRowMajorStrides = false;
4655 : }
4656 : // Integer overflows have already been checked in CheckReadWriteParams()
4657 1566 : nCurStrideForRowMajorStrides *= count[i];
4658 1566 : nElts *= count[i];
4659 1566 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4660 : }
4661 947 : if (bRowMajorStrides)
4662 743 : return false;
4663 204 : return nLastIdx == nElts - 1;
4664 : }
4665 :
4666 : /************************************************************************/
4667 : /* CopyToFinalBufferSameDataType() */
4668 : /************************************************************************/
4669 :
4670 : template <size_t N>
4671 72 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4672 : size_t nDims, const size_t *count,
4673 : const GPtrDiff_t *bufferStride)
4674 : {
4675 144 : std::vector<size_t> anStackCount(nDims);
4676 144 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4677 72 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4678 : #if defined(__GNUC__)
4679 : #pragma GCC diagnostic push
4680 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4681 : #endif
4682 72 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4683 : #if defined(__GNUC__)
4684 : #pragma GCC diagnostic pop
4685 : #endif
4686 72 : size_t iDim = 0;
4687 :
4688 761 : lbl_next_depth:
4689 761 : if (iDim == nDims - 1)
4690 : {
4691 673 : size_t n = count[iDim];
4692 673 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4693 673 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4694 29210 : while (n > 0)
4695 : {
4696 28537 : --n;
4697 28537 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4698 28537 : pabyDstBuffer += bufferStrideLastDim;
4699 28537 : pabySrcBuffer += N;
4700 : }
4701 : }
4702 : else
4703 : {
4704 88 : anStackCount[iDim] = count[iDim];
4705 : while (true)
4706 : {
4707 689 : ++iDim;
4708 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4709 689 : goto lbl_next_depth;
4710 689 : lbl_return_to_caller_in_loop:
4711 689 : --iDim;
4712 689 : --anStackCount[iDim];
4713 689 : if (anStackCount[iDim] == 0)
4714 88 : break;
4715 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4716 : }
4717 : }
4718 761 : if (iDim > 0)
4719 689 : goto lbl_return_to_caller_in_loop;
4720 72 : }
4721 :
4722 : /************************************************************************/
4723 : /* CopyToFinalBuffer() */
4724 : /************************************************************************/
4725 :
4726 183 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4727 : const GDALExtendedDataType &eSrcDataType,
4728 : void *pDstBuffer,
4729 : const GDALExtendedDataType &eDstDataType,
4730 : size_t nDims, const size_t *count,
4731 : const GPtrDiff_t *bufferStride)
4732 : {
4733 183 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4734 : // Use specialized implementation for well-known data types when no
4735 : // type conversion is needed
4736 183 : if (eSrcDataType == eDstDataType)
4737 : {
4738 122 : if (nSrcDataTypeSize == 1)
4739 : {
4740 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4741 : count, bufferStride);
4742 72 : return;
4743 : }
4744 81 : else if (nSrcDataTypeSize == 2)
4745 : {
4746 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4747 : count, bufferStride);
4748 1 : return;
4749 : }
4750 80 : else if (nSrcDataTypeSize == 4)
4751 : {
4752 22 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4753 : count, bufferStride);
4754 22 : return;
4755 : }
4756 58 : else if (nSrcDataTypeSize == 8)
4757 : {
4758 8 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4759 : count, bufferStride);
4760 8 : return;
4761 : }
4762 : }
4763 :
4764 111 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4765 222 : std::vector<size_t> anStackCount(nDims);
4766 222 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4767 111 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4768 111 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4769 111 : size_t iDim = 0;
4770 :
4771 380 : lbl_next_depth:
4772 380 : if (iDim == nDims - 1)
4773 : {
4774 370 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4775 370 : pabyDstBufferStack[iDim], eDstDataType,
4776 370 : bufferStride[iDim], count[iDim]);
4777 370 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4778 : }
4779 : else
4780 : {
4781 10 : anStackCount[iDim] = count[iDim];
4782 : while (true)
4783 : {
4784 269 : ++iDim;
4785 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4786 269 : goto lbl_next_depth;
4787 269 : lbl_return_to_caller_in_loop:
4788 269 : --iDim;
4789 269 : --anStackCount[iDim];
4790 269 : if (anStackCount[iDim] == 0)
4791 10 : break;
4792 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4793 : }
4794 : }
4795 380 : if (iDim > 0)
4796 269 : goto lbl_return_to_caller_in_loop;
4797 : }
4798 :
4799 : /************************************************************************/
4800 : /* TransposeLast2Dims() */
4801 : /************************************************************************/
4802 :
4803 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4804 : const GDALExtendedDataType &eDT,
4805 : const size_t nDims, const size_t *count,
4806 : const size_t nEltsNonLast2Dims)
4807 : {
4808 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4809 19 : const auto nDTSize = eDT.GetSize();
4810 : void *pTempBufferForLast2DimsTranspose =
4811 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4812 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4813 0 : return false;
4814 :
4815 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4816 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4817 : {
4818 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4819 : pTempBufferForLast2DimsTranspose,
4820 39 : eDT.GetNumericDataType(), count[nDims - 1],
4821 39 : count[nDims - 2]);
4822 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4823 : nDTSize * nEltsLast2Dims);
4824 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4825 : }
4826 :
4827 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4828 :
4829 19 : return true;
4830 : }
4831 :
4832 : /************************************************************************/
4833 : /* ReadForTransposedRequest() */
4834 : /************************************************************************/
4835 :
4836 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4837 : // transposed view yield to extremely poor/unusable performance. This fixes
4838 : // this by using temporary memory to read in a contiguous buffer in a
4839 : // row-major order, and then do the transposition to the final buffer.
4840 :
4841 202 : bool GDALMDArray::ReadForTransposedRequest(
4842 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4843 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4844 : void *pDstBuffer) const
4845 : {
4846 202 : const size_t nDims(GetDimensionCount());
4847 202 : if (nDims == 0)
4848 : {
4849 0 : CPLAssert(false);
4850 : return false; // shouldn't happen
4851 : }
4852 202 : size_t nElts = 1;
4853 526 : for (size_t i = 0; i < nDims; ++i)
4854 324 : nElts *= count[i];
4855 :
4856 404 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4857 202 : tmpBufferStrides.back() = 1;
4858 324 : for (size_t i = nDims - 1; i > 0;)
4859 : {
4860 122 : --i;
4861 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4862 : }
4863 :
4864 202 : const auto &eDT = GetDataType();
4865 202 : const auto nDTSize = eDT.GetSize();
4866 343 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4867 359 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4868 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4869 : {
4870 : // Optimization of the optimization if only the last 2 dims are
4871 : // transposed that saves on temporary buffer allocation
4872 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4873 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4874 23 : bool bRowMajorStridesForNonLast2Dims = true;
4875 23 : size_t nEltsNonLast2Dims = 1;
4876 40 : for (size_t i = nDims - 2; i > 0;)
4877 : {
4878 17 : --i;
4879 17 : if (static_cast<size_t>(bufferStride[i]) !=
4880 : nCurStrideForRowMajorStrides)
4881 : {
4882 4 : bRowMajorStridesForNonLast2Dims = false;
4883 : }
4884 : // Integer overflows have already been checked in
4885 : // CheckReadWriteParams()
4886 17 : nCurStrideForRowMajorStrides *= count[i];
4887 17 : nEltsNonLast2Dims *= count[i];
4888 : }
4889 23 : if (bRowMajorStridesForNonLast2Dims)
4890 : {
4891 : // We read in the final buffer!
4892 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4893 19 : eDT, pDstBuffer))
4894 : {
4895 0 : return false;
4896 : }
4897 :
4898 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4899 19 : nEltsNonLast2Dims);
4900 : }
4901 : }
4902 :
4903 183 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4904 183 : if (pTempBuffer == nullptr)
4905 0 : return false;
4906 :
4907 183 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4908 183 : pTempBuffer))
4909 : {
4910 0 : VSIFree(pTempBuffer);
4911 0 : return false;
4912 : }
4913 183 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4914 : count, bufferStride);
4915 :
4916 183 : if (eDT.NeedsFreeDynamicMemory())
4917 : {
4918 100 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4919 200 : for (size_t i = 0; i < nElts; ++i)
4920 : {
4921 100 : eDT.FreeDynamicMemory(pabyPtr);
4922 100 : pabyPtr += nDTSize;
4923 : }
4924 : }
4925 :
4926 183 : VSIFree(pTempBuffer);
4927 183 : return true;
4928 : }
4929 :
4930 : /************************************************************************/
4931 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4932 : /************************************************************************/
4933 :
4934 : // Returns true if at all following conditions are met:
4935 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4936 : // defines a row-major ordered contiguous buffer.
4937 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4938 : const size_t *count, const GInt64 *arrayStep,
4939 : const GPtrDiff_t *bufferStride,
4940 : const GDALExtendedDataType &bufferDataType) const
4941 : {
4942 78 : if (bufferDataType != GetDataType())
4943 5 : return false;
4944 73 : size_t nExpectedStride = 1;
4945 166 : for (size_t i = GetDimensionCount(); i > 0;)
4946 : {
4947 96 : --i;
4948 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4949 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4950 : {
4951 3 : return false;
4952 : }
4953 93 : nExpectedStride *= count[i];
4954 : }
4955 70 : return true;
4956 : }
4957 :
4958 : /************************************************************************/
4959 : /* ReadUsingContiguousIRead() */
4960 : /************************************************************************/
4961 :
4962 : // Used for example by the TileDB driver when requesting it with
4963 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4964 : // not defining a row-major ordered contiguous buffer.
4965 : // Should only be called when at least one of the above conditions are true,
4966 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4967 : // returning none.
4968 : // This method will call IRead() again with arrayStep[] == 1,
4969 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4970 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4971 : // content of that temporary buffer onto pDstBuffer.
4972 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4973 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4974 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4975 : void *pDstBuffer) const
4976 : {
4977 7 : const size_t nDims(GetDimensionCount());
4978 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4979 14 : std::vector<size_t> anTmpCount(nDims);
4980 7 : const auto &oType = GetDataType();
4981 7 : size_t nMemArraySize = oType.GetSize();
4982 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4983 7 : GPtrDiff_t nStride = 1;
4984 18 : for (size_t i = nDims; i > 0;)
4985 : {
4986 11 : --i;
4987 11 : if (arrayStep[i] > 0)
4988 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4989 : else
4990 2 : anTmpStartIdx[i] =
4991 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4992 : const uint64_t nCount =
4993 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4994 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4995 : {
4996 0 : CPLError(CE_Failure, CPLE_AppDefined,
4997 : "Read() failed due to too large memory requirement");
4998 0 : return false;
4999 : }
5000 11 : anTmpCount[i] = static_cast<size_t>(nCount);
5001 11 : nMemArraySize *= anTmpCount[i];
5002 11 : anTmpStride[i] = nStride;
5003 11 : nStride *= anTmpCount[i];
5004 : }
5005 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
5006 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
5007 7 : if (!pTmpBuffer)
5008 0 : return false;
5009 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
5010 14 : std::vector<GInt64>(nDims, 1).data(), // steps
5011 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
5012 : {
5013 0 : return false;
5014 : }
5015 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5016 18 : for (size_t i = 0; i < nDims; ++i)
5017 : {
5018 11 : if (arrayStep[i] > 0)
5019 9 : anTmpStartIdx[i] = 0;
5020 : else
5021 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
5022 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
5023 22 : std::string(), std::string(), std::string(), std::string(),
5024 22 : anTmpCount[i]);
5025 : }
5026 : auto poMEMArray =
5027 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5028 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5029 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5030 7 : bufferStride, bufferDataType, pDstBuffer);
5031 : }
5032 :
5033 : //! @endcond
5034 :
5035 : /************************************************************************/
5036 : /* GDALSlicedMDArray */
5037 : /************************************************************************/
5038 :
5039 : class GDALSlicedMDArray final : public GDALPamMDArray
5040 : {
5041 : private:
5042 : std::shared_ptr<GDALMDArray> m_poParent{};
5043 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5044 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
5045 : std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5046 : std::vector<Range>
5047 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5048 :
5049 : mutable std::vector<GUInt64> m_parentStart;
5050 : mutable std::vector<size_t> m_parentCount;
5051 : mutable std::vector<GInt64> m_parentStep;
5052 : mutable std::vector<GPtrDiff_t> m_parentStride;
5053 :
5054 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5055 : const GInt64 *arrayStep,
5056 : const GPtrDiff_t *bufferStride) const;
5057 :
5058 : protected:
5059 686 : explicit GDALSlicedMDArray(
5060 : const std::shared_ptr<GDALMDArray> &poParent,
5061 : const std::string &viewExpr,
5062 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5063 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5064 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5065 : std::vector<Range> &&parentRanges)
5066 2058 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5067 2058 : poParent->GetFullName() +
5068 1372 : " (" + viewExpr + ")"),
5069 1372 : GDALPamMDArray(std::string(),
5070 1372 : "Sliced view of " + poParent->GetFullName() + " (" +
5071 1372 : viewExpr + ")",
5072 1372 : GDALPamMultiDim::GetPAM(poParent),
5073 : poParent->GetContext()),
5074 1372 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5075 686 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5076 686 : m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5077 686 : m_parentRanges(std::move(parentRanges)),
5078 686 : m_parentStart(m_poParent->GetDimensionCount()),
5079 686 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5080 686 : m_parentStep(m_poParent->GetDimensionCount()),
5081 5488 : m_parentStride(m_poParent->GetDimensionCount())
5082 : {
5083 686 : }
5084 :
5085 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5086 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5087 : const GDALExtendedDataType &bufferDataType,
5088 : void *pDstBuffer) const override;
5089 :
5090 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5091 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5092 : const GDALExtendedDataType &bufferDataType,
5093 : const void *pSrcBuffer) override;
5094 :
5095 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5096 : CSLConstList papszOptions) const override;
5097 :
5098 : public:
5099 : static std::shared_ptr<GDALSlicedMDArray>
5100 686 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5101 : const std::string &viewExpr,
5102 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5103 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5104 : std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5105 : std::vector<Range> &&parentRanges)
5106 : {
5107 686 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5108 686 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5109 :
5110 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5111 686 : poParent, viewExpr, std::move(dims),
5112 686 : std::move(mapDimIdxToParentDimIdx),
5113 686 : std::move(apoNewIndexingVariables), std::move(parentRanges))));
5114 686 : newAr->SetSelf(newAr);
5115 686 : return newAr;
5116 : }
5117 :
5118 77 : bool IsWritable() const override
5119 : {
5120 77 : return m_poParent->IsWritable();
5121 : }
5122 :
5123 1119 : const std::string &GetFilename() const override
5124 : {
5125 1119 : return m_poParent->GetFilename();
5126 : }
5127 :
5128 : const std::vector<std::shared_ptr<GDALDimension>> &
5129 4124 : GetDimensions() const override
5130 : {
5131 4124 : return m_dims;
5132 : }
5133 :
5134 1581 : const GDALExtendedDataType &GetDataType() const override
5135 : {
5136 1581 : return m_poParent->GetDataType();
5137 : }
5138 :
5139 2 : const std::string &GetUnit() const override
5140 : {
5141 2 : return m_poParent->GetUnit();
5142 : }
5143 :
5144 : // bool SetUnit(const std::string& osUnit) override { return
5145 : // m_poParent->SetUnit(osUnit); }
5146 :
5147 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5148 : {
5149 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5150 2 : if (!poSrcSRS)
5151 1 : return nullptr;
5152 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5153 2 : std::vector<int> dstMapping;
5154 3 : for (int srcAxis : srcMapping)
5155 : {
5156 2 : bool bFound = false;
5157 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5158 : {
5159 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5160 3 : srcAxis - 1)
5161 : {
5162 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5163 2 : bFound = true;
5164 2 : break;
5165 : }
5166 : }
5167 2 : if (!bFound)
5168 : {
5169 0 : dstMapping.push_back(0);
5170 : }
5171 : }
5172 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5173 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5174 1 : return poClone;
5175 : }
5176 :
5177 61 : const void *GetRawNoDataValue() const override
5178 : {
5179 61 : return m_poParent->GetRawNoDataValue();
5180 : }
5181 :
5182 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5183 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5184 :
5185 2 : double GetOffset(bool *pbHasOffset,
5186 : GDALDataType *peStorageType) const override
5187 : {
5188 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5189 : }
5190 :
5191 2 : double GetScale(bool *pbHasScale,
5192 : GDALDataType *peStorageType) const override
5193 : {
5194 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5195 : }
5196 :
5197 : // bool SetOffset(double dfOffset) override { return
5198 : // m_poParent->SetOffset(dfOffset); }
5199 :
5200 : // bool SetScale(double dfScale) override { return
5201 : // m_poParent->SetScale(dfScale); }
5202 :
5203 263 : std::vector<GUInt64> GetBlockSize() const override
5204 : {
5205 263 : std::vector<GUInt64> ret(GetDimensionCount());
5206 526 : const auto parentBlockSize(m_poParent->GetBlockSize());
5207 749 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5208 : {
5209 486 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5210 486 : if (iOldAxis != static_cast<size_t>(-1))
5211 : {
5212 486 : ret[i] = parentBlockSize[iOldAxis];
5213 : }
5214 : }
5215 526 : return ret;
5216 : }
5217 :
5218 : std::shared_ptr<GDALAttribute>
5219 6 : GetAttribute(const std::string &osName) const override
5220 : {
5221 6 : return m_poParent->GetAttribute(osName);
5222 : }
5223 :
5224 : std::vector<std::shared_ptr<GDALAttribute>>
5225 35 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5226 : {
5227 35 : return m_poParent->GetAttributes(papszOptions);
5228 : }
5229 : };
5230 :
5231 : /************************************************************************/
5232 : /* PrepareParentArrays() */
5233 : /************************************************************************/
5234 :
5235 549 : void GDALSlicedMDArray::PrepareParentArrays(
5236 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5237 : const GPtrDiff_t *bufferStride) const
5238 : {
5239 549 : const size_t nParentDimCount = m_parentRanges.size();
5240 1655 : for (size_t i = 0; i < nParentDimCount; i++)
5241 : {
5242 : // For dimensions in parent that have no existence in sliced array
5243 1106 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5244 : }
5245 :
5246 1424 : for (size_t i = 0; i < m_dims.size(); i++)
5247 : {
5248 875 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5249 875 : if (iParent != static_cast<size_t>(-1))
5250 : {
5251 873 : m_parentStart[iParent] =
5252 873 : m_parentRanges[iParent].m_nIncr >= 0
5253 873 : ? m_parentRanges[iParent].m_nStartIdx +
5254 785 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5255 88 : : m_parentRanges[iParent].m_nStartIdx -
5256 176 : arrayStartIdx[i] *
5257 88 : static_cast<GUInt64>(
5258 88 : -m_parentRanges[iParent].m_nIncr);
5259 873 : m_parentCount[iParent] = count[i];
5260 873 : if (arrayStep)
5261 : {
5262 872 : m_parentStep[iParent] =
5263 872 : count[i] == 1 ? 1 :
5264 : // other checks should have ensured this does
5265 : // not overflow
5266 681 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5267 : }
5268 873 : if (bufferStride)
5269 : {
5270 872 : m_parentStride[iParent] = bufferStride[i];
5271 : }
5272 : }
5273 : }
5274 549 : }
5275 :
5276 : /************************************************************************/
5277 : /* IRead() */
5278 : /************************************************************************/
5279 :
5280 516 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5281 : const GInt64 *arrayStep,
5282 : const GPtrDiff_t *bufferStride,
5283 : const GDALExtendedDataType &bufferDataType,
5284 : void *pDstBuffer) const
5285 : {
5286 516 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5287 1032 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5288 516 : m_parentStep.data(), m_parentStride.data(),
5289 516 : bufferDataType, pDstBuffer);
5290 : }
5291 :
5292 : /************************************************************************/
5293 : /* IWrite() */
5294 : /************************************************************************/
5295 :
5296 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5297 : const size_t *count, const GInt64 *arrayStep,
5298 : const GPtrDiff_t *bufferStride,
5299 : const GDALExtendedDataType &bufferDataType,
5300 : const void *pSrcBuffer)
5301 : {
5302 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5303 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5304 32 : m_parentStep.data(), m_parentStride.data(),
5305 32 : bufferDataType, pSrcBuffer);
5306 : }
5307 :
5308 : /************************************************************************/
5309 : /* IAdviseRead() */
5310 : /************************************************************************/
5311 :
5312 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5313 : const size_t *count,
5314 : CSLConstList papszOptions) const
5315 : {
5316 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5317 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5318 1 : papszOptions);
5319 : }
5320 :
5321 : /************************************************************************/
5322 : /* CreateSlicedArray() */
5323 : /************************************************************************/
5324 :
5325 : static std::shared_ptr<GDALMDArray>
5326 621 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5327 : const std::string &viewExpr, const std::string &activeSlice,
5328 : bool bRenameDimensions,
5329 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5330 : {
5331 621 : const auto &srcDims(self->GetDimensions());
5332 621 : if (srcDims.empty())
5333 : {
5334 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5335 2 : return nullptr;
5336 : }
5337 :
5338 1238 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5339 619 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5340 :
5341 1238 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5342 1238 : std::vector<size_t> mapDimIdxToParentDimIdx;
5343 1238 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5344 619 : newDims.reserve(nTokens);
5345 619 : mapDimIdxToParentDimIdx.reserve(nTokens);
5346 619 : parentRanges.reserve(nTokens);
5347 :
5348 619 : bool bGotEllipsis = false;
5349 619 : size_t nCurSrcDim = 0;
5350 1238 : std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5351 1833 : for (size_t i = 0; i < nTokens; i++)
5352 : {
5353 1230 : const char *pszIdxSpec = aosTokens[i];
5354 1230 : if (EQUAL(pszIdxSpec, "..."))
5355 : {
5356 46 : if (bGotEllipsis)
5357 : {
5358 2 : CPLError(CE_Failure, CPLE_AppDefined,
5359 : "Only one single ellipsis is supported");
5360 2 : return nullptr;
5361 : }
5362 44 : bGotEllipsis = true;
5363 44 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5364 97 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5365 : {
5366 53 : parentRanges.emplace_back(0, 1);
5367 53 : newDims.push_back(srcDims[nCurSrcDim]);
5368 53 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5369 : }
5370 44 : continue;
5371 : }
5372 1184 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5373 1181 : EQUAL(pszIdxSpec, "np.newaxis"))
5374 : {
5375 3 : newDims.push_back(std::make_shared<GDALDimension>(
5376 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5377 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5378 3 : continue;
5379 : }
5380 1181 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5381 : {
5382 326 : if (nCurSrcDim >= srcDims.size())
5383 : {
5384 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5385 : activeSlice.c_str());
5386 7 : return nullptr;
5387 : }
5388 :
5389 324 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5390 324 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5391 324 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5392 320 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5393 : {
5394 5 : CPLError(CE_Failure, CPLE_AppDefined,
5395 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5396 5 : return nullptr;
5397 : }
5398 319 : if (nVal < 0)
5399 0 : nVal += nDimSize;
5400 319 : parentRanges.emplace_back(nVal, 0);
5401 : }
5402 : else
5403 : {
5404 855 : if (nCurSrcDim >= srcDims.size())
5405 : {
5406 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5407 : activeSlice.c_str());
5408 7 : return nullptr;
5409 : }
5410 :
5411 : CPLStringList aosRangeTokens(
5412 854 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5413 854 : int nRangeTokens = aosRangeTokens.size();
5414 854 : if (nRangeTokens > 3)
5415 : {
5416 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5417 : pszIdxSpec);
5418 1 : return nullptr;
5419 : }
5420 853 : if (nRangeTokens <= 1)
5421 : {
5422 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5423 : pszIdxSpec);
5424 1 : return nullptr;
5425 : }
5426 852 : const char *pszStart = aosRangeTokens[0];
5427 852 : const char *pszEnd = aosRangeTokens[1];
5428 852 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5429 852 : GDALSlicedMDArray::Range range;
5430 852 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5431 852 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5432 1703 : if (range.m_nIncr == 0 ||
5433 851 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5434 : {
5435 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5436 1 : return nullptr;
5437 : }
5438 851 : auto startIdx(CPLAtoGIntBig(pszStart));
5439 851 : if (startIdx < 0)
5440 : {
5441 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5442 0 : startIdx = 0;
5443 : else
5444 0 : startIdx = nDimSize + startIdx;
5445 : }
5446 851 : const bool bPosIncr = range.m_nIncr > 0;
5447 851 : range.m_nStartIdx = startIdx;
5448 1702 : range.m_nStartIdx = EQUAL(pszStart, "")
5449 851 : ? (bPosIncr ? 0 : nDimSize - 1)
5450 : : range.m_nStartIdx;
5451 851 : if (range.m_nStartIdx >= nDimSize - 1)
5452 199 : range.m_nStartIdx = nDimSize - 1;
5453 851 : auto endIdx(CPLAtoGIntBig(pszEnd));
5454 851 : if (endIdx < 0)
5455 : {
5456 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5457 1 : if (nDimSize < positiveEndIdx)
5458 0 : endIdx = 0;
5459 : else
5460 1 : endIdx = nDimSize - positiveEndIdx;
5461 : }
5462 851 : GUInt64 nEndIdx = endIdx;
5463 851 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5464 851 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5465 849 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5466 : {
5467 3 : CPLError(CE_Failure, CPLE_AppDefined,
5468 : "Output dimension of size 0 is not allowed");
5469 3 : return nullptr;
5470 : }
5471 848 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5472 848 : const auto nAbsIncr = std::abs(range.m_nIncr);
5473 848 : const GUInt64 newSize =
5474 : bPosIncr
5475 893 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5476 45 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5477 848 : const auto &poSrcDim = srcDims[nCurSrcDim];
5478 1396 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5479 548 : newSize == poSrcDim->GetSize())
5480 : {
5481 179 : newDims.push_back(poSrcDim);
5482 : }
5483 : else
5484 : {
5485 1338 : std::string osNewDimName(poSrcDim->GetName());
5486 669 : if (bRenameDimensions)
5487 : {
5488 : osNewDimName =
5489 1242 : "subset_" + poSrcDim->GetName() +
5490 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5491 : "_" CPL_FRMT_GUIB,
5492 621 : static_cast<GUIntBig>(range.m_nStartIdx),
5493 621 : static_cast<GIntBig>(range.m_nIncr),
5494 621 : static_cast<GUIntBig>(newSize));
5495 : }
5496 : auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5497 1338 : std::string(), osNewDimName, poSrcDim->GetType(),
5498 669 : range.m_nIncr > 0 ? poSrcDim->GetDirection()
5499 : : std::string(),
5500 1338 : newSize);
5501 669 : auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5502 752 : if (poSrcIndexingVar &&
5503 752 : poSrcIndexingVar->GetDimensionCount() == 1 &&
5504 83 : poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5505 : {
5506 : std::vector<std::shared_ptr<GDALDimension>>
5507 332 : indexingVarNewDims{poNewDim};
5508 166 : std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5509 : std::vector<std::shared_ptr<GDALMDArray>>
5510 166 : indexingVarNewIndexingVar;
5511 : std::vector<GDALSlicedMDArray::Range>
5512 166 : indexingVarParentRanges{range};
5513 : auto poNewIndexingVar = GDALSlicedMDArray::Create(
5514 : poSrcIndexingVar, pszIdxSpec,
5515 83 : std::move(indexingVarNewDims),
5516 83 : std::move(indexingVarMapDimIdxToParentDimIdx),
5517 83 : std::move(indexingVarNewIndexingVar),
5518 249 : std::move(indexingVarParentRanges));
5519 83 : poNewDim->SetIndexingVariable(poNewIndexingVar);
5520 83 : apoNewIndexingVariables.push_back(
5521 83 : std::move(poNewIndexingVar));
5522 : }
5523 669 : newDims.push_back(std::move(poNewDim));
5524 : }
5525 848 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5526 848 : parentRanges.emplace_back(range);
5527 : }
5528 :
5529 1167 : nCurSrcDim++;
5530 : }
5531 676 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5532 : {
5533 73 : parentRanges.emplace_back(0, 1);
5534 73 : newDims.push_back(srcDims[nCurSrcDim]);
5535 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5536 : }
5537 :
5538 603 : GDALMDArray::ViewSpec viewSpec;
5539 603 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5540 603 : viewSpec.m_parentRanges = parentRanges;
5541 603 : viewSpecs.emplace_back(std::move(viewSpec));
5542 :
5543 1206 : return GDALSlicedMDArray::Create(
5544 603 : self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5545 1206 : std::move(apoNewIndexingVariables), std::move(parentRanges));
5546 : }
5547 :
5548 : /************************************************************************/
5549 : /* GDALExtractFieldMDArray */
5550 : /************************************************************************/
5551 :
5552 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5553 : {
5554 : private:
5555 : std::shared_ptr<GDALMDArray> m_poParent{};
5556 : GDALExtendedDataType m_dt;
5557 : std::string m_srcCompName;
5558 : mutable std::vector<GByte> m_pabyNoData{};
5559 :
5560 : protected:
5561 98 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5562 : const std::string &fieldName,
5563 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5564 392 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5565 196 : " of " +
5566 98 : poParent->GetFullName()),
5567 : GDALPamMDArray(
5568 196 : std::string(),
5569 196 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5570 196 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5571 : m_poParent(poParent), m_dt(srcComp->GetType()),
5572 490 : m_srcCompName(srcComp->GetName())
5573 : {
5574 98 : m_pabyNoData.resize(m_dt.GetSize());
5575 98 : }
5576 :
5577 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5578 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5579 : const GDALExtendedDataType &bufferDataType,
5580 : void *pDstBuffer) const override;
5581 :
5582 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5583 : CSLConstList papszOptions) const override
5584 : {
5585 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5586 : }
5587 :
5588 : public:
5589 : static std::shared_ptr<GDALExtractFieldMDArray>
5590 98 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5591 : const std::string &fieldName,
5592 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5593 : {
5594 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5595 98 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5596 98 : newAr->SetSelf(newAr);
5597 98 : return newAr;
5598 : }
5599 :
5600 196 : ~GDALExtractFieldMDArray() override
5601 98 : {
5602 98 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5603 196 : }
5604 :
5605 51 : bool IsWritable() const override
5606 : {
5607 51 : return m_poParent->IsWritable();
5608 : }
5609 :
5610 309 : const std::string &GetFilename() const override
5611 : {
5612 309 : return m_poParent->GetFilename();
5613 : }
5614 :
5615 : const std::vector<std::shared_ptr<GDALDimension>> &
5616 418 : GetDimensions() const override
5617 : {
5618 418 : return m_poParent->GetDimensions();
5619 : }
5620 :
5621 344 : const GDALExtendedDataType &GetDataType() const override
5622 : {
5623 344 : return m_dt;
5624 : }
5625 :
5626 2 : const std::string &GetUnit() const override
5627 : {
5628 2 : return m_poParent->GetUnit();
5629 : }
5630 :
5631 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5632 : {
5633 2 : return m_poParent->GetSpatialRef();
5634 : }
5635 :
5636 62 : const void *GetRawNoDataValue() const override
5637 : {
5638 62 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5639 62 : if (parentNoData == nullptr)
5640 2 : return nullptr;
5641 :
5642 60 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5643 60 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5644 :
5645 120 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5646 120 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5647 120 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5648 60 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5649 180 : std::move(comps)));
5650 :
5651 60 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5652 60 : &m_pabyNoData[0], tmpDT);
5653 :
5654 60 : return &m_pabyNoData[0];
5655 : }
5656 :
5657 2 : double GetOffset(bool *pbHasOffset,
5658 : GDALDataType *peStorageType) const override
5659 : {
5660 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5661 : }
5662 :
5663 2 : double GetScale(bool *pbHasScale,
5664 : GDALDataType *peStorageType) const override
5665 : {
5666 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5667 : }
5668 :
5669 52 : std::vector<GUInt64> GetBlockSize() const override
5670 : {
5671 52 : return m_poParent->GetBlockSize();
5672 : }
5673 : };
5674 :
5675 : /************************************************************************/
5676 : /* IRead() */
5677 : /************************************************************************/
5678 :
5679 105 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5680 : const size_t *count,
5681 : const GInt64 *arrayStep,
5682 : const GPtrDiff_t *bufferStride,
5683 : const GDALExtendedDataType &bufferDataType,
5684 : void *pDstBuffer) const
5685 : {
5686 210 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5687 210 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5688 210 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5689 : auto tmpDT(GDALExtendedDataType::Create(
5690 210 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5691 :
5692 105 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5693 210 : tmpDT, pDstBuffer);
5694 : }
5695 :
5696 : /************************************************************************/
5697 : /* CreateFieldNameExtractArray() */
5698 : /************************************************************************/
5699 :
5700 : static std::shared_ptr<GDALMDArray>
5701 99 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5702 : const std::string &fieldName)
5703 : {
5704 99 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5705 99 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5706 235 : for (const auto &comp : self->GetDataType().GetComponents())
5707 : {
5708 234 : if (comp->GetName() == fieldName)
5709 : {
5710 98 : srcComp = ∁
5711 98 : break;
5712 : }
5713 : }
5714 99 : if (srcComp == nullptr)
5715 : {
5716 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5717 : fieldName.c_str());
5718 1 : return nullptr;
5719 : }
5720 98 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5721 : }
5722 :
5723 : /************************************************************************/
5724 : /* GetView() */
5725 : /************************************************************************/
5726 :
5727 : // clang-format off
5728 : /** Return a view of the array using slicing or field access.
5729 : *
5730 : * The slice expression uses the same syntax as NumPy basic slicing and
5731 : * indexing. See
5732 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5733 : * Or it can use field access by name. See
5734 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5735 : *
5736 : * Multiple [] bracket elements can be concatenated, with a slice expression
5737 : * or field name inside each.
5738 : *
5739 : * For basic slicing and indexing, inside each [] bracket element, a list of
5740 : * indexes that apply to successive source dimensions, can be specified, using
5741 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5742 : * or newaxis, using a comma separator.
5743 : *
5744 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5745 : * <ul>
5746 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5747 : * at index 1 in the first dimension, and index 2 in the second dimension
5748 : * from the source array. That is 5</li>
5749 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5750 : * implemented internally doing this intermediate slicing approach.</li>
5751 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5752 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5753 : * first dimension. That is [4,5,6,7].</li>
5754 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5755 : * second dimension. That is [2,6].</li>
5756 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5757 : * the second dimension. That is [[2],[6]].</li>
5758 : * <li>GetView("[::,2]"): Same as
5759 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5760 : * ellipsis only expands to one dimension here.</li>
5761 : * <li>GetView("[:,::2]"):
5762 : * returns a 2-dimensional array, with even-indexed elements of the second
5763 : * dimension. That is [[0,2],[4,6]].</li>
5764 : * <li>GetView("[:,1::2]"): returns a
5765 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5766 : * is [[1,3],[5,7]].</li>
5767 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5768 : * array, with elements of the second dimension with index in the range [1,3[.
5769 : * That is [[1,2],[5,6]].</li>
5770 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5771 : * array, with the values in first dimension reversed. That is
5772 : * [[4,5,6,7],[0,1,2,3]].</li>
5773 : * <li>GetView("[newaxis,...]"): returns a
5774 : * 3-dimensional array, with an additional dimension of size 1 put at the
5775 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5776 : * </ul>
5777 : *
5778 : * One difference with NumPy behavior is that ranges that would result in
5779 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5780 : * GDAL multidimensional model).
5781 : *
5782 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5783 : * Multiple field specification is not supported currently.
5784 : *
5785 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5786 : *
5787 : * \note When using the GDAL Python bindings, natural Python syntax can be
5788 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5789 : * ar.GetView("[0,::,1]['foo']")
5790 : * \note When using the C++ API and integer indexing only, you may use the
5791 : * at(idx0, idx1, ...) method.
5792 : *
5793 : * The returned array holds a reference to the original one, and thus is
5794 : * a view of it (not a copy). If the content of the original array changes,
5795 : * the content of the view array too. When using basic slicing and indexing,
5796 : * the view can be written if the underlying array is writable.
5797 : *
5798 : * This is the same as the C function GDALMDArrayGetView()
5799 : *
5800 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5801 : * access.
5802 : * @return a new array, that holds a reference to the original one, and thus is
5803 : * a view of it (not a copy), or nullptr in case of error.
5804 : */
5805 : // clang-format on
5806 :
5807 : std::shared_ptr<GDALMDArray>
5808 654 : GDALMDArray::GetView(const std::string &viewExpr) const
5809 : {
5810 1308 : std::vector<ViewSpec> viewSpecs;
5811 1308 : return GetView(viewExpr, true, viewSpecs);
5812 : }
5813 :
5814 : //! @cond Doxygen_Suppress
5815 : std::shared_ptr<GDALMDArray>
5816 726 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5817 : std::vector<ViewSpec> &viewSpecs) const
5818 : {
5819 1452 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5820 726 : if (!self)
5821 : {
5822 1 : CPLError(CE_Failure, CPLE_AppDefined,
5823 : "Driver implementation issue: m_pSelf not set !");
5824 1 : return nullptr;
5825 : }
5826 725 : std::string curExpr(viewExpr);
5827 : while (true)
5828 : {
5829 728 : if (curExpr.empty() || curExpr[0] != '[')
5830 : {
5831 2 : CPLError(CE_Failure, CPLE_AppDefined,
5832 : "Slice string should start with ['");
5833 725 : return nullptr;
5834 : }
5835 :
5836 726 : std::string fieldName;
5837 : size_t endExpr;
5838 726 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5839 : {
5840 103 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5841 : {
5842 2 : CPLError(CE_Failure, CPLE_AppDefined,
5843 : "Field access not allowed on non-compound data type");
5844 2 : return nullptr;
5845 : }
5846 101 : size_t idx = 2;
5847 985 : for (; idx < curExpr.size(); idx++)
5848 : {
5849 984 : const char ch = curExpr[idx];
5850 984 : if (ch == curExpr[1])
5851 100 : break;
5852 884 : if (ch == '\\' && idx + 1 < curExpr.size())
5853 : {
5854 1 : fieldName += curExpr[idx + 1];
5855 1 : idx++;
5856 : }
5857 : else
5858 : {
5859 883 : fieldName += ch;
5860 : }
5861 : }
5862 101 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5863 : {
5864 2 : CPLError(CE_Failure, CPLE_AppDefined,
5865 : "Invalid field access specification");
5866 2 : return nullptr;
5867 : }
5868 99 : endExpr = idx + 1;
5869 : }
5870 : else
5871 : {
5872 623 : endExpr = curExpr.find(']');
5873 : }
5874 722 : if (endExpr == std::string::npos)
5875 : {
5876 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5877 1 : return nullptr;
5878 : }
5879 721 : if (endExpr == 1)
5880 : {
5881 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5882 1 : return nullptr;
5883 : }
5884 720 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5885 :
5886 720 : if (!fieldName.empty())
5887 : {
5888 198 : ViewSpec viewSpec;
5889 99 : viewSpec.m_osFieldName = fieldName;
5890 99 : viewSpecs.emplace_back(std::move(viewSpec));
5891 : }
5892 :
5893 720 : auto newArray = !fieldName.empty()
5894 : ? CreateFieldNameExtractArray(self, fieldName)
5895 : : CreateSlicedArray(self, viewExpr, activeSlice,
5896 720 : bRenameDimensions, viewSpecs);
5897 :
5898 720 : if (endExpr == curExpr.size() - 1)
5899 : {
5900 717 : return newArray;
5901 : }
5902 3 : self = std::move(newArray);
5903 3 : curExpr = curExpr.substr(endExpr + 1);
5904 3 : }
5905 : }
5906 :
5907 : //! @endcond
5908 :
5909 : std::shared_ptr<GDALMDArray>
5910 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5911 : {
5912 19 : std::string osExpr("[");
5913 19 : bool bFirst = true;
5914 45 : for (const auto &idx : indices)
5915 : {
5916 26 : if (!bFirst)
5917 7 : osExpr += ',';
5918 26 : bFirst = false;
5919 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5920 : }
5921 57 : return GetView(osExpr + ']');
5922 : }
5923 :
5924 : /************************************************************************/
5925 : /* operator[] */
5926 : /************************************************************************/
5927 :
5928 : /** Return a view of the array using field access
5929 : *
5930 : * Equivalent of GetView("['fieldName']")
5931 : *
5932 : * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5933 : */
5934 : std::shared_ptr<GDALMDArray>
5935 2 : GDALMDArray::operator[](const std::string &fieldName) const
5936 : {
5937 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5938 4 : .replaceAll('\\', "\\\\")
5939 4 : .replaceAll('\'', "\\\'")
5940 6 : .c_str()));
5941 : }
5942 :
5943 : /************************************************************************/
5944 : /* GDALMDArrayTransposed */
5945 : /************************************************************************/
5946 :
5947 : class GDALMDArrayTransposed final : public GDALPamMDArray
5948 : {
5949 : private:
5950 : std::shared_ptr<GDALMDArray> m_poParent{};
5951 : std::vector<int> m_anMapNewAxisToOldAxis{};
5952 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5953 :
5954 : mutable std::vector<GUInt64> m_parentStart;
5955 : mutable std::vector<size_t> m_parentCount;
5956 : mutable std::vector<GInt64> m_parentStep;
5957 : mutable std::vector<GPtrDiff_t> m_parentStride;
5958 :
5959 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5960 : const GInt64 *arrayStep,
5961 : const GPtrDiff_t *bufferStride) const;
5962 :
5963 : static std::string
5964 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5965 : {
5966 84 : std::string ret;
5967 84 : ret += '[';
5968 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5969 : {
5970 228 : if (i > 0)
5971 144 : ret += ',';
5972 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5973 : }
5974 84 : ret += ']';
5975 84 : return ret;
5976 : }
5977 :
5978 : protected:
5979 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5980 : const std::vector<int> &anMapNewAxisToOldAxis,
5981 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5982 84 : : GDALAbstractMDArray(std::string(),
5983 84 : "Transposed view of " + poParent->GetFullName() +
5984 84 : " along " +
5985 42 : MappingToStr(anMapNewAxisToOldAxis)),
5986 84 : GDALPamMDArray(std::string(),
5987 84 : "Transposed view of " + poParent->GetFullName() +
5988 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5989 84 : GDALPamMultiDim::GetPAM(poParent),
5990 : poParent->GetContext()),
5991 42 : m_poParent(std::move(poParent)),
5992 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5993 42 : m_dims(std::move(dims)),
5994 42 : m_parentStart(m_poParent->GetDimensionCount()),
5995 42 : m_parentCount(m_poParent->GetDimensionCount()),
5996 42 : m_parentStep(m_poParent->GetDimensionCount()),
5997 336 : m_parentStride(m_poParent->GetDimensionCount())
5998 : {
5999 42 : }
6000 :
6001 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6002 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6003 : const GDALExtendedDataType &bufferDataType,
6004 : void *pDstBuffer) const override;
6005 :
6006 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
6007 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6008 : const GDALExtendedDataType &bufferDataType,
6009 : const void *pSrcBuffer) override;
6010 :
6011 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6012 : CSLConstList papszOptions) const override;
6013 :
6014 : public:
6015 : static std::shared_ptr<GDALMDArrayTransposed>
6016 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6017 : const std::vector<int> &anMapNewAxisToOldAxis)
6018 : {
6019 42 : const auto &parentDims(poParent->GetDimensions());
6020 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
6021 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6022 : {
6023 114 : if (iOldAxis < 0)
6024 : {
6025 1 : dims.push_back(std::make_shared<GDALDimension>(
6026 2 : std::string(), "newaxis", std::string(), std::string(), 1));
6027 : }
6028 : else
6029 : {
6030 113 : dims.emplace_back(parentDims[iOldAxis]);
6031 : }
6032 : }
6033 :
6034 : auto newAr(
6035 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6036 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
6037 42 : newAr->SetSelf(newAr);
6038 84 : return newAr;
6039 : }
6040 :
6041 1 : bool IsWritable() const override
6042 : {
6043 1 : return m_poParent->IsWritable();
6044 : }
6045 :
6046 84 : const std::string &GetFilename() const override
6047 : {
6048 84 : return m_poParent->GetFilename();
6049 : }
6050 :
6051 : const std::vector<std::shared_ptr<GDALDimension>> &
6052 358 : GetDimensions() const override
6053 : {
6054 358 : return m_dims;
6055 : }
6056 :
6057 141 : const GDALExtendedDataType &GetDataType() const override
6058 : {
6059 141 : return m_poParent->GetDataType();
6060 : }
6061 :
6062 4 : const std::string &GetUnit() const override
6063 : {
6064 4 : return m_poParent->GetUnit();
6065 : }
6066 :
6067 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6068 : {
6069 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
6070 5 : if (!poSrcSRS)
6071 2 : return nullptr;
6072 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6073 6 : std::vector<int> dstMapping;
6074 9 : for (int srcAxis : srcMapping)
6075 : {
6076 6 : bool bFound = false;
6077 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6078 : {
6079 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6080 : {
6081 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6082 6 : bFound = true;
6083 6 : break;
6084 : }
6085 : }
6086 6 : if (!bFound)
6087 : {
6088 0 : dstMapping.push_back(0);
6089 : }
6090 : }
6091 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6092 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6093 3 : return poClone;
6094 : }
6095 :
6096 4 : const void *GetRawNoDataValue() const override
6097 : {
6098 4 : return m_poParent->GetRawNoDataValue();
6099 : }
6100 :
6101 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6102 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6103 :
6104 4 : double GetOffset(bool *pbHasOffset,
6105 : GDALDataType *peStorageType) const override
6106 : {
6107 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6108 : }
6109 :
6110 4 : double GetScale(bool *pbHasScale,
6111 : GDALDataType *peStorageType) const override
6112 : {
6113 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6114 : }
6115 :
6116 : // bool SetOffset(double dfOffset) override { return
6117 : // m_poParent->SetOffset(dfOffset); }
6118 :
6119 : // bool SetScale(double dfScale) override { return
6120 : // m_poParent->SetScale(dfScale); }
6121 :
6122 3 : std::vector<GUInt64> GetBlockSize() const override
6123 : {
6124 3 : std::vector<GUInt64> ret(GetDimensionCount());
6125 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6126 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6127 : {
6128 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6129 8 : if (iOldAxis >= 0)
6130 : {
6131 7 : ret[i] = parentBlockSize[iOldAxis];
6132 : }
6133 : }
6134 6 : return ret;
6135 : }
6136 :
6137 : std::shared_ptr<GDALAttribute>
6138 1 : GetAttribute(const std::string &osName) const override
6139 : {
6140 1 : return m_poParent->GetAttribute(osName);
6141 : }
6142 :
6143 : std::vector<std::shared_ptr<GDALAttribute>>
6144 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6145 : {
6146 6 : return m_poParent->GetAttributes(papszOptions);
6147 : }
6148 : };
6149 :
6150 : /************************************************************************/
6151 : /* PrepareParentArrays() */
6152 : /************************************************************************/
6153 :
6154 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6155 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6156 : const GPtrDiff_t *bufferStride) const
6157 : {
6158 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6159 : {
6160 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6161 129 : if (iOldAxis >= 0)
6162 : {
6163 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6164 128 : m_parentCount[iOldAxis] = count[i];
6165 128 : if (arrayStep) // only null when called from IAdviseRead()
6166 : {
6167 126 : m_parentStep[iOldAxis] = arrayStep[i];
6168 : }
6169 128 : if (bufferStride) // only null when called from IAdviseRead()
6170 : {
6171 126 : m_parentStride[iOldAxis] = bufferStride[i];
6172 : }
6173 : }
6174 : }
6175 47 : }
6176 :
6177 : /************************************************************************/
6178 : /* IRead() */
6179 : /************************************************************************/
6180 :
6181 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6182 : const size_t *count, const GInt64 *arrayStep,
6183 : const GPtrDiff_t *bufferStride,
6184 : const GDALExtendedDataType &bufferDataType,
6185 : void *pDstBuffer) const
6186 : {
6187 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6188 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6189 44 : m_parentStep.data(), m_parentStride.data(),
6190 44 : bufferDataType, pDstBuffer);
6191 : }
6192 :
6193 : /************************************************************************/
6194 : /* IWrite() */
6195 : /************************************************************************/
6196 :
6197 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6198 : const size_t *count, const GInt64 *arrayStep,
6199 : const GPtrDiff_t *bufferStride,
6200 : const GDALExtendedDataType &bufferDataType,
6201 : const void *pSrcBuffer)
6202 : {
6203 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6204 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6205 2 : m_parentStep.data(), m_parentStride.data(),
6206 2 : bufferDataType, pSrcBuffer);
6207 : }
6208 :
6209 : /************************************************************************/
6210 : /* IAdviseRead() */
6211 : /************************************************************************/
6212 :
6213 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6214 : const size_t *count,
6215 : CSLConstList papszOptions) const
6216 : {
6217 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6218 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6219 1 : papszOptions);
6220 : }
6221 :
6222 : /************************************************************************/
6223 : /* Transpose() */
6224 : /************************************************************************/
6225 :
6226 : /** Return a view of the array whose axis have been reordered.
6227 : *
6228 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6229 : * and GetDimensionCount() - 1, and each only once.
6230 : * -1 can be used as a special index value to ask for the insertion of a new
6231 : * axis of size 1.
6232 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6233 : * index of one of its dimension, it corresponds to the axis of index
6234 : * anMapNewAxisToOldAxis[i] from the current array.
6235 : *
6236 : * This is similar to the numpy.transpose() method
6237 : *
6238 : * The returned array holds a reference to the original one, and thus is
6239 : * a view of it (not a copy). If the content of the original array changes,
6240 : * the content of the view array too. The view can be written if the underlying
6241 : * array is writable.
6242 : *
6243 : * Note that I/O performance in such a transposed view might be poor.
6244 : *
6245 : * This is the same as the C function GDALMDArrayTranspose().
6246 : *
6247 : * @return a new array, that holds a reference to the original one, and thus is
6248 : * a view of it (not a copy), or nullptr in case of error.
6249 : */
6250 : std::shared_ptr<GDALMDArray>
6251 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6252 : {
6253 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6254 50 : if (!self)
6255 : {
6256 0 : CPLError(CE_Failure, CPLE_AppDefined,
6257 : "Driver implementation issue: m_pSelf not set !");
6258 0 : return nullptr;
6259 : }
6260 50 : const int nDims = static_cast<int>(GetDimensionCount());
6261 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6262 50 : int nCountOldAxis = 0;
6263 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6264 : {
6265 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6266 : {
6267 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6268 4 : return nullptr;
6269 : }
6270 130 : if (iOldAxis >= 0)
6271 : {
6272 128 : if (alreadyUsedOldAxis[iOldAxis])
6273 : {
6274 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6275 : iOldAxis);
6276 1 : return nullptr;
6277 : }
6278 127 : alreadyUsedOldAxis[iOldAxis] = true;
6279 127 : nCountOldAxis++;
6280 : }
6281 : }
6282 46 : if (nCountOldAxis != nDims)
6283 : {
6284 4 : CPLError(CE_Failure, CPLE_AppDefined,
6285 : "One or several original axis missing");
6286 4 : return nullptr;
6287 : }
6288 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6289 : }
6290 :
6291 : /************************************************************************/
6292 : /* IRead() */
6293 : /************************************************************************/
6294 :
6295 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6296 : const size_t *count, const GInt64 *arrayStep,
6297 : const GPtrDiff_t *bufferStride,
6298 : const GDALExtendedDataType &bufferDataType,
6299 : void *pDstBuffer) const
6300 : {
6301 16 : const double dfScale = m_dfScale;
6302 16 : const double dfOffset = m_dfOffset;
6303 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6304 : const auto dtDouble =
6305 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6306 16 : const size_t nDTSize = dtDouble.GetSize();
6307 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6308 :
6309 16 : double adfSrcNoData[2] = {0, 0};
6310 16 : if (m_bHasNoData)
6311 : {
6312 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6313 9 : m_poParent->GetDataType(),
6314 : &adfSrcNoData[0], dtDouble);
6315 : }
6316 :
6317 16 : const auto nDims = GetDimensions().size();
6318 16 : if (nDims == 0)
6319 : {
6320 : double adfVal[2];
6321 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6322 : dtDouble, &adfVal[0]))
6323 : {
6324 0 : return false;
6325 : }
6326 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6327 : {
6328 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6329 6 : if (bDTIsComplex)
6330 : {
6331 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6332 : }
6333 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6334 : bufferDataType);
6335 : }
6336 : else
6337 : {
6338 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6339 : pDstBuffer, bufferDataType);
6340 : }
6341 9 : return true;
6342 : }
6343 :
6344 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6345 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6346 7 : void *pTempBuffer = pDstBuffer;
6347 7 : if (bTempBufferNeeded)
6348 : {
6349 2 : size_t nElts = 1;
6350 2 : actualBufferStrideVector.resize(nDims);
6351 7 : for (size_t i = 0; i < nDims; i++)
6352 5 : nElts *= count[i];
6353 2 : actualBufferStrideVector.back() = 1;
6354 5 : for (size_t i = nDims - 1; i > 0;)
6355 : {
6356 3 : --i;
6357 3 : actualBufferStrideVector[i] =
6358 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6359 : }
6360 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6361 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6362 2 : if (!pTempBuffer)
6363 0 : return false;
6364 : }
6365 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6366 : actualBufferStridePtr, dtDouble, pTempBuffer))
6367 : {
6368 0 : if (bTempBufferNeeded)
6369 0 : VSIFree(pTempBuffer);
6370 0 : return false;
6371 : }
6372 :
6373 : struct Stack
6374 : {
6375 : size_t nIters = 0;
6376 : double *src_ptr = nullptr;
6377 : GByte *dst_ptr = nullptr;
6378 : GPtrDiff_t src_inc_offset = 0;
6379 : GPtrDiff_t dst_inc_offset = 0;
6380 : };
6381 :
6382 7 : std::vector<Stack> stack(nDims);
6383 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6384 23 : for (size_t i = 0; i < nDims; i++)
6385 : {
6386 32 : stack[i].src_inc_offset =
6387 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6388 16 : stack[i].dst_inc_offset =
6389 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6390 : }
6391 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6392 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6393 :
6394 7 : size_t dimIdx = 0;
6395 7 : const size_t nDimsMinus1 = nDims - 1;
6396 : GByte abyDstNoData[16];
6397 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6398 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6399 : bufferDataType);
6400 :
6401 37 : lbl_next_depth:
6402 37 : if (dimIdx == nDimsMinus1)
6403 : {
6404 25 : auto nIters = count[dimIdx];
6405 25 : double *padfVal = stack[dimIdx].src_ptr;
6406 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6407 : while (true)
6408 : {
6409 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6410 : {
6411 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6412 88 : if (bDTIsComplex)
6413 : {
6414 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6415 : }
6416 88 : if (bTempBufferNeeded)
6417 : {
6418 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6419 : dst_ptr, bufferDataType);
6420 : }
6421 : }
6422 : else
6423 : {
6424 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6425 : }
6426 :
6427 92 : if ((--nIters) == 0)
6428 25 : break;
6429 67 : padfVal += stack[dimIdx].src_inc_offset;
6430 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6431 : }
6432 : }
6433 : else
6434 : {
6435 12 : stack[dimIdx].nIters = count[dimIdx];
6436 : while (true)
6437 : {
6438 30 : dimIdx++;
6439 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6440 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6441 30 : goto lbl_next_depth;
6442 30 : lbl_return_to_caller:
6443 30 : dimIdx--;
6444 30 : if ((--stack[dimIdx].nIters) == 0)
6445 12 : break;
6446 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6447 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6448 : }
6449 : }
6450 37 : if (dimIdx > 0)
6451 30 : goto lbl_return_to_caller;
6452 :
6453 7 : if (bTempBufferNeeded)
6454 2 : VSIFree(pTempBuffer);
6455 7 : return true;
6456 : }
6457 :
6458 : /************************************************************************/
6459 : /* IWrite() */
6460 : /************************************************************************/
6461 :
6462 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6463 : const size_t *count, const GInt64 *arrayStep,
6464 : const GPtrDiff_t *bufferStride,
6465 : const GDALExtendedDataType &bufferDataType,
6466 : const void *pSrcBuffer)
6467 : {
6468 16 : const double dfScale = m_dfScale;
6469 16 : const double dfOffset = m_dfOffset;
6470 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6471 : const auto dtDouble =
6472 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6473 16 : const size_t nDTSize = dtDouble.GetSize();
6474 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6475 : const bool bSelfAndParentHaveNoData =
6476 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6477 16 : double dfNoData = 0;
6478 16 : if (m_bHasNoData)
6479 : {
6480 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6481 : &dfNoData, GDT_Float64, 0, 1);
6482 : }
6483 :
6484 16 : double adfSrcNoData[2] = {0, 0};
6485 16 : if (bSelfAndParentHaveNoData)
6486 : {
6487 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6488 7 : m_poParent->GetDataType(),
6489 : &adfSrcNoData[0], dtDouble);
6490 : }
6491 :
6492 16 : const auto nDims = GetDimensions().size();
6493 16 : if (nDims == 0)
6494 : {
6495 : double adfVal[2];
6496 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6497 : dtDouble);
6498 16 : if (bSelfAndParentHaveNoData &&
6499 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6500 : {
6501 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6502 2 : bufferStride, m_poParent->GetDataType(),
6503 4 : m_poParent->GetRawNoDataValue());
6504 : }
6505 : else
6506 : {
6507 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6508 8 : if (bDTIsComplex)
6509 : {
6510 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6511 : }
6512 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6513 8 : bufferStride, dtDouble, &adfVal[0]);
6514 : }
6515 : }
6516 :
6517 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6518 6 : size_t nElts = 1;
6519 6 : tmpBufferStrideVector.resize(nDims);
6520 20 : for (size_t i = 0; i < nDims; i++)
6521 14 : nElts *= count[i];
6522 6 : tmpBufferStrideVector.back() = 1;
6523 14 : for (size_t i = nDims - 1; i > 0;)
6524 : {
6525 8 : --i;
6526 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6527 : }
6528 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6529 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6530 6 : if (!pTempBuffer)
6531 0 : return false;
6532 :
6533 : struct Stack
6534 : {
6535 : size_t nIters = 0;
6536 : double *dst_ptr = nullptr;
6537 : const GByte *src_ptr = nullptr;
6538 : GPtrDiff_t src_inc_offset = 0;
6539 : GPtrDiff_t dst_inc_offset = 0;
6540 : };
6541 :
6542 6 : std::vector<Stack> stack(nDims);
6543 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6544 20 : for (size_t i = 0; i < nDims; i++)
6545 : {
6546 28 : stack[i].dst_inc_offset =
6547 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6548 14 : stack[i].src_inc_offset =
6549 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6550 : }
6551 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6552 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6553 :
6554 6 : size_t dimIdx = 0;
6555 6 : const size_t nDimsMinus1 = nDims - 1;
6556 :
6557 34 : lbl_next_depth:
6558 34 : if (dimIdx == nDimsMinus1)
6559 : {
6560 23 : auto nIters = count[dimIdx];
6561 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6562 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6563 : while (true)
6564 : {
6565 : double adfVal[2];
6566 : const double *padfSrcVal;
6567 86 : if (bIsBufferDataTypeNativeDataType)
6568 : {
6569 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6570 : }
6571 : else
6572 : {
6573 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6574 : &adfVal[0], dtDouble);
6575 36 : padfSrcVal = adfVal;
6576 : }
6577 :
6578 148 : if (bSelfAndParentHaveNoData &&
6579 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6580 : {
6581 3 : dst_ptr[0] = adfSrcNoData[0];
6582 3 : if (bDTIsComplex)
6583 : {
6584 1 : dst_ptr[1] = adfSrcNoData[1];
6585 : }
6586 : }
6587 : else
6588 : {
6589 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6590 83 : if (bDTIsComplex)
6591 : {
6592 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6593 : }
6594 : }
6595 :
6596 86 : if ((--nIters) == 0)
6597 23 : break;
6598 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6599 63 : src_ptr += stack[dimIdx].src_inc_offset;
6600 63 : }
6601 : }
6602 : else
6603 : {
6604 11 : stack[dimIdx].nIters = count[dimIdx];
6605 : while (true)
6606 : {
6607 28 : dimIdx++;
6608 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6609 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6610 28 : goto lbl_next_depth;
6611 28 : lbl_return_to_caller:
6612 28 : dimIdx--;
6613 28 : if ((--stack[dimIdx].nIters) == 0)
6614 11 : break;
6615 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6616 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6617 : }
6618 : }
6619 34 : if (dimIdx > 0)
6620 28 : goto lbl_return_to_caller;
6621 :
6622 : // If the parent array is not double/complex-double, then convert the
6623 : // values to it, before calling Write(), as some implementations can be
6624 : // very slow when doing the type conversion.
6625 6 : const auto &eParentDT = m_poParent->GetDataType();
6626 6 : const size_t nParentDTSize = eParentDT.GetSize();
6627 6 : if (nParentDTSize <= nDTSize / 2)
6628 : {
6629 : // Copy in-place by making sure that source and target do not overlap
6630 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6631 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6632 :
6633 : // Copy first element
6634 : {
6635 6 : std::vector<GByte> abyTemp(nParentDTSize);
6636 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6637 6 : static_cast<int>(nDTSize), &abyTemp[0],
6638 : eParentNumericDT, static_cast<int>(nParentDTSize),
6639 : 1);
6640 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6641 : }
6642 : // Remaining elements
6643 86 : for (size_t i = 1; i < nElts; ++i)
6644 : {
6645 80 : GDALCopyWords64(
6646 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6647 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6648 : eParentNumericDT, 0, 1);
6649 : }
6650 : }
6651 :
6652 : const bool ret =
6653 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6654 : eParentDT, pTempBuffer);
6655 :
6656 6 : VSIFree(pTempBuffer);
6657 6 : return ret;
6658 : }
6659 :
6660 : /************************************************************************/
6661 : /* GetUnscaled() */
6662 : /************************************************************************/
6663 :
6664 : /** Return an array that is the unscaled version of the current one.
6665 : *
6666 : * That is each value of the unscaled array will be
6667 : * unscaled_value = raw_value * GetScale() + GetOffset()
6668 : *
6669 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6670 : * from unscaled values to raw values.
6671 : *
6672 : * This is the same as the C function GDALMDArrayGetUnscaled().
6673 : *
6674 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6675 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6676 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6677 : * @return a new array, that holds a reference to the original one, and thus is
6678 : * a view of it (not a copy), or nullptr in case of error.
6679 : */
6680 : std::shared_ptr<GDALMDArray>
6681 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6682 : double dfOverriddenDstNodata) const
6683 : {
6684 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6685 17 : if (!self)
6686 : {
6687 0 : CPLError(CE_Failure, CPLE_AppDefined,
6688 : "Driver implementation issue: m_pSelf not set !");
6689 0 : return nullptr;
6690 : }
6691 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6692 : {
6693 0 : CPLError(CE_Failure, CPLE_AppDefined,
6694 : "GetUnscaled() only supports numeric data type");
6695 0 : return nullptr;
6696 : }
6697 : const double dfScale =
6698 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6699 : const double dfOffset =
6700 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6701 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6702 4 : return self;
6703 :
6704 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6705 13 : ? GDT_CFloat64
6706 13 : : GDT_Float64;
6707 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6708 : {
6709 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6710 0 : eDT = GDT_Float16;
6711 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6712 1 : eDT = GDT_Float32;
6713 : }
6714 :
6715 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6716 13 : dfOverriddenDstNodata, eDT);
6717 : }
6718 :
6719 : /************************************************************************/
6720 : /* GDALMDArrayMask */
6721 : /************************************************************************/
6722 :
6723 : class GDALMDArrayMask final : public GDALPamMDArray
6724 : {
6725 : private:
6726 : std::shared_ptr<GDALMDArray> m_poParent{};
6727 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6728 : double m_dfMissingValue = 0.0;
6729 : bool m_bHasMissingValue = false;
6730 : double m_dfFillValue = 0.0;
6731 : bool m_bHasFillValue = false;
6732 : double m_dfValidMin = 0.0;
6733 : bool m_bHasValidMin = false;
6734 : double m_dfValidMax = 0.0;
6735 : bool m_bHasValidMax = false;
6736 : std::vector<uint32_t> m_anValidFlagMasks{};
6737 : std::vector<uint32_t> m_anValidFlagValues{};
6738 :
6739 : bool Init(CSLConstList papszOptions);
6740 :
6741 : template <typename Type>
6742 : void
6743 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6744 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6745 : const void *pTempBuffer,
6746 : const GDALExtendedDataType &oTmpBufferDT,
6747 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6748 :
6749 : protected:
6750 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6751 96 : : GDALAbstractMDArray(std::string(),
6752 96 : "Mask of " + poParent->GetFullName()),
6753 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6754 96 : GDALPamMultiDim::GetPAM(poParent),
6755 : poParent->GetContext()),
6756 240 : m_poParent(std::move(poParent))
6757 : {
6758 48 : }
6759 :
6760 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6761 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6762 : const GDALExtendedDataType &bufferDataType,
6763 : void *pDstBuffer) const override;
6764 :
6765 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6766 : CSLConstList papszOptions) const override
6767 : {
6768 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6769 : }
6770 :
6771 : public:
6772 : static std::shared_ptr<GDALMDArrayMask>
6773 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6774 : CSLConstList papszOptions);
6775 :
6776 1 : bool IsWritable() const override
6777 : {
6778 1 : return false;
6779 : }
6780 :
6781 54 : const std::string &GetFilename() const override
6782 : {
6783 54 : return m_poParent->GetFilename();
6784 : }
6785 :
6786 : const std::vector<std::shared_ptr<GDALDimension>> &
6787 382 : GetDimensions() const override
6788 : {
6789 382 : return m_poParent->GetDimensions();
6790 : }
6791 :
6792 138 : const GDALExtendedDataType &GetDataType() const override
6793 : {
6794 138 : return m_dt;
6795 : }
6796 :
6797 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6798 : {
6799 1 : return m_poParent->GetSpatialRef();
6800 : }
6801 :
6802 2 : std::vector<GUInt64> GetBlockSize() const override
6803 : {
6804 2 : return m_poParent->GetBlockSize();
6805 : }
6806 : };
6807 :
6808 : /************************************************************************/
6809 : /* GDALMDArrayMask::Create() */
6810 : /************************************************************************/
6811 :
6812 : /* static */ std::shared_ptr<GDALMDArrayMask>
6813 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6814 : CSLConstList papszOptions)
6815 : {
6816 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6817 48 : newAr->SetSelf(newAr);
6818 48 : if (!newAr->Init(papszOptions))
6819 6 : return nullptr;
6820 42 : return newAr;
6821 : }
6822 :
6823 : /************************************************************************/
6824 : /* GDALMDArrayMask::Init() */
6825 : /************************************************************************/
6826 :
6827 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6828 : {
6829 : const auto GetSingleValNumericAttr =
6830 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6831 : {
6832 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6833 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6834 : {
6835 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6836 21 : if (anDimSizes.empty() ||
6837 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6838 : {
6839 11 : bHasVal = true;
6840 11 : dfVal = poAttr->ReadAsDouble();
6841 : }
6842 : }
6843 192 : };
6844 :
6845 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6846 48 : m_dfMissingValue);
6847 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6848 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6849 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6850 :
6851 : {
6852 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6853 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6854 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6855 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6856 : {
6857 6 : m_bHasValidMin = true;
6858 6 : m_bHasValidMax = true;
6859 6 : auto vals = poValidRange->ReadAsDoubleArray();
6860 6 : CPLAssert(vals.size() == 2);
6861 6 : m_dfValidMin = vals[0];
6862 6 : m_dfValidMax = vals[1];
6863 : }
6864 : }
6865 :
6866 : // Take into account
6867 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6868 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6869 : const char *pszUnmaskFlags =
6870 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6871 48 : if (pszUnmaskFlags)
6872 : {
6873 : const auto IsScalarStringAttr =
6874 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6875 : {
6876 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6877 26 : (poAttr->GetDimensionsSize().empty() ||
6878 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6879 26 : poAttr->GetDimensionsSize()[0] == 1));
6880 : };
6881 :
6882 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6883 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6884 : {
6885 1 : CPLError(CE_Failure, CPLE_AppDefined,
6886 : "UNMASK_FLAGS option specified but array has no "
6887 : "flag_meanings attribute");
6888 1 : return false;
6889 : }
6890 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6891 13 : if (!pszFlagMeanings)
6892 : {
6893 1 : CPLError(CE_Failure, CPLE_AppDefined,
6894 : "Cannot read flag_meanings attribute");
6895 1 : return false;
6896 : }
6897 :
6898 : const auto IsSingleDimNumericAttr =
6899 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6900 : {
6901 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6902 26 : poAttr->GetDimensionsSize().size() == 1;
6903 : };
6904 :
6905 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6906 : const bool bHasFlagValues =
6907 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6908 :
6909 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6910 : const bool bHasFlagMasks =
6911 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6912 :
6913 12 : if (!bHasFlagValues && !bHasFlagMasks)
6914 : {
6915 1 : CPLError(CE_Failure, CPLE_AppDefined,
6916 : "Cannot find flag_values and/or flag_masks attribute");
6917 1 : return false;
6918 : }
6919 :
6920 : const CPLStringList aosUnmaskFlags(
6921 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6922 : const CPLStringList aosFlagMeanings(
6923 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6924 :
6925 11 : if (bHasFlagValues)
6926 : {
6927 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6928 : // We could support Int64 or UInt64, but more work...
6929 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6930 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6931 : {
6932 0 : CPLError(CE_Failure, CPLE_NotSupported,
6933 : "Unsupported data type for flag_values attribute: %s",
6934 : GDALGetDataTypeName(eType));
6935 0 : return false;
6936 : }
6937 : }
6938 :
6939 11 : if (bHasFlagMasks)
6940 : {
6941 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6942 : // We could support Int64 or UInt64, but more work...
6943 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6944 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6945 : {
6946 0 : CPLError(CE_Failure, CPLE_NotSupported,
6947 : "Unsupported data type for flag_masks attribute: %s",
6948 : GDALGetDataTypeName(eType));
6949 0 : return false;
6950 : }
6951 : }
6952 :
6953 : const std::vector<double> adfValues(
6954 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6955 11 : : std::vector<double>());
6956 : const std::vector<double> adfMasks(
6957 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6958 11 : : std::vector<double>());
6959 :
6960 18 : if (bHasFlagValues &&
6961 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6962 : {
6963 1 : CPLError(CE_Failure, CPLE_AppDefined,
6964 : "Number of values in flag_values attribute is different "
6965 : "from the one in flag_meanings");
6966 1 : return false;
6967 : }
6968 :
6969 16 : if (bHasFlagMasks &&
6970 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6971 : {
6972 1 : CPLError(CE_Failure, CPLE_AppDefined,
6973 : "Number of values in flag_masks attribute is different "
6974 : "from the one in flag_meanings");
6975 1 : return false;
6976 : }
6977 :
6978 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6979 : {
6980 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6981 11 : if (nIdxFlag < 0)
6982 : {
6983 1 : CPLError(
6984 : CE_Failure, CPLE_AppDefined,
6985 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6986 : aosUnmaskFlags[i], pszFlagMeanings);
6987 1 : return false;
6988 : }
6989 :
6990 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6991 : {
6992 0 : CPLError(CE_Failure, CPLE_AppDefined,
6993 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6994 0 : adfValues[nIdxFlag]);
6995 0 : return false;
6996 : }
6997 :
6998 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6999 : {
7000 0 : CPLError(CE_Failure, CPLE_AppDefined,
7001 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
7002 0 : adfMasks[nIdxFlag]);
7003 0 : return false;
7004 : }
7005 :
7006 10 : if (bHasFlagValues)
7007 : {
7008 12 : m_anValidFlagValues.push_back(
7009 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
7010 : }
7011 :
7012 10 : if (bHasFlagMasks)
7013 : {
7014 12 : m_anValidFlagMasks.push_back(
7015 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
7016 : }
7017 : }
7018 : }
7019 :
7020 42 : return true;
7021 : }
7022 :
7023 : /************************************************************************/
7024 : /* IRead() */
7025 : /************************************************************************/
7026 :
7027 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7028 : const GInt64 *arrayStep,
7029 : const GPtrDiff_t *bufferStride,
7030 : const GDALExtendedDataType &bufferDataType,
7031 : void *pDstBuffer) const
7032 : {
7033 51 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7034 : {
7035 0 : CPLError(CE_Failure, CPLE_AppDefined,
7036 : "%s: only reading to a numeric data type is supported",
7037 : __func__);
7038 0 : return false;
7039 : }
7040 51 : size_t nElts = 1;
7041 51 : const size_t nDims = GetDimensionCount();
7042 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7043 139 : for (size_t i = 0; i < nDims; i++)
7044 88 : nElts *= count[i];
7045 51 : if (nDims > 0)
7046 : {
7047 46 : tmpBufferStrideVector.back() = 1;
7048 88 : for (size_t i = nDims - 1; i > 0;)
7049 : {
7050 42 : --i;
7051 42 : tmpBufferStrideVector[i] =
7052 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
7053 : }
7054 : }
7055 :
7056 : /* Optimized case: if we are an integer data type and that there is no */
7057 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
7058 : /* directly */
7059 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7060 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
7061 34 : m_anValidFlagMasks.empty() &&
7062 111 : m_poParent->GetRawNoDataValue() == nullptr &&
7063 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7064 : {
7065 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7066 7 : if (bBufferDataTypeIsByte) // Byte case
7067 : {
7068 4 : bool bContiguous = true;
7069 10 : for (size_t i = 0; i < nDims; i++)
7070 : {
7071 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
7072 : {
7073 1 : bContiguous = false;
7074 1 : break;
7075 : }
7076 : }
7077 4 : if (bContiguous)
7078 : {
7079 : // CPLDebug("GDAL", "GetMask(): contiguous case");
7080 3 : memset(pDstBuffer, 1, nElts);
7081 3 : return true;
7082 : }
7083 : }
7084 :
7085 : struct Stack
7086 : {
7087 : size_t nIters = 0;
7088 : GByte *dst_ptr = nullptr;
7089 : GPtrDiff_t dst_inc_offset = 0;
7090 : };
7091 :
7092 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7093 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7094 13 : for (size_t i = 0; i < nDims; i++)
7095 : {
7096 9 : stack[i].dst_inc_offset =
7097 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7098 : }
7099 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7100 :
7101 4 : size_t dimIdx = 0;
7102 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7103 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7104 4 : CPLAssert(nBufferDTSize <= 16);
7105 4 : const GByte flag = 1;
7106 4 : GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7107 : bufferDataType.GetNumericDataType(), 0, 1);
7108 :
7109 28 : lbl_next_depth:
7110 28 : if (dimIdx == nDimsMinus1)
7111 : {
7112 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7113 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7114 :
7115 : while (true)
7116 : {
7117 : // cppcheck-suppress knownConditionTrueFalse
7118 73 : if (bBufferDataTypeIsByte)
7119 : {
7120 24 : *dst_ptr = flag;
7121 : }
7122 : else
7123 : {
7124 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7125 : }
7126 :
7127 73 : if ((--nIters) == 0)
7128 19 : break;
7129 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7130 : }
7131 : }
7132 : else
7133 : {
7134 9 : stack[dimIdx].nIters = count[dimIdx];
7135 : while (true)
7136 : {
7137 24 : dimIdx++;
7138 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7139 24 : goto lbl_next_depth;
7140 24 : lbl_return_to_caller:
7141 24 : dimIdx--;
7142 24 : if ((--stack[dimIdx].nIters) == 0)
7143 9 : break;
7144 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7145 : }
7146 : }
7147 28 : if (dimIdx > 0)
7148 24 : goto lbl_return_to_caller;
7149 :
7150 4 : return true;
7151 : }
7152 :
7153 : const auto oTmpBufferDT =
7154 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7155 : ? GDALExtendedDataType::Create(GDT_Float64)
7156 88 : : m_poParent->GetDataType();
7157 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7158 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7159 44 : if (!pTempBuffer)
7160 0 : return false;
7161 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7162 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7163 : pTempBuffer))
7164 : {
7165 0 : VSIFree(pTempBuffer);
7166 0 : return false;
7167 : }
7168 :
7169 44 : switch (oTmpBufferDT.GetNumericDataType())
7170 : {
7171 7 : case GDT_Byte:
7172 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7173 : pTempBuffer, oTmpBufferDT,
7174 : tmpBufferStrideVector);
7175 7 : break;
7176 :
7177 0 : case GDT_Int8:
7178 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7179 : pTempBuffer, oTmpBufferDT,
7180 : tmpBufferStrideVector);
7181 0 : break;
7182 :
7183 1 : case GDT_UInt16:
7184 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7185 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7186 : tmpBufferStrideVector);
7187 1 : break;
7188 :
7189 14 : case GDT_Int16:
7190 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7191 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7192 : tmpBufferStrideVector);
7193 14 : break;
7194 :
7195 1 : case GDT_UInt32:
7196 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7197 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7198 : tmpBufferStrideVector);
7199 1 : break;
7200 :
7201 5 : case GDT_Int32:
7202 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7203 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7204 : tmpBufferStrideVector);
7205 5 : break;
7206 :
7207 0 : case GDT_UInt64:
7208 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7209 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7210 : tmpBufferStrideVector);
7211 0 : break;
7212 :
7213 0 : case GDT_Int64:
7214 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7215 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7216 : tmpBufferStrideVector);
7217 0 : break;
7218 :
7219 0 : case GDT_Float16:
7220 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7221 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7222 : tmpBufferStrideVector);
7223 0 : break;
7224 :
7225 7 : case GDT_Float32:
7226 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7227 : pTempBuffer, oTmpBufferDT,
7228 : tmpBufferStrideVector);
7229 7 : break;
7230 :
7231 9 : case GDT_Float64:
7232 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7233 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7234 : tmpBufferStrideVector);
7235 9 : break;
7236 0 : case GDT_Unknown:
7237 : case GDT_CInt16:
7238 : case GDT_CInt32:
7239 : case GDT_CFloat16:
7240 : case GDT_CFloat32:
7241 : case GDT_CFloat64:
7242 : case GDT_TypeCount:
7243 0 : CPLAssert(false);
7244 : break;
7245 : }
7246 :
7247 44 : VSIFree(pTempBuffer);
7248 :
7249 44 : return true;
7250 : }
7251 :
7252 : /************************************************************************/
7253 : /* IsValidForDT() */
7254 : /************************************************************************/
7255 :
7256 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7257 : {
7258 40 : if (std::isnan(dfVal))
7259 0 : return false;
7260 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7261 0 : return false;
7262 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7263 0 : return false;
7264 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7265 : }
7266 :
7267 9 : template <> bool IsValidForDT<double>(double)
7268 : {
7269 9 : return true;
7270 : }
7271 :
7272 : /************************************************************************/
7273 : /* IsNan() */
7274 : /************************************************************************/
7275 :
7276 1438 : template <typename Type> inline bool IsNan(Type)
7277 : {
7278 1438 : return false;
7279 : }
7280 :
7281 65 : template <> bool IsNan<double>(double val)
7282 : {
7283 65 : return std::isnan(val);
7284 : }
7285 :
7286 26 : template <> bool IsNan<float>(float val)
7287 : {
7288 26 : return std::isnan(val);
7289 : }
7290 :
7291 : /************************************************************************/
7292 : /* ReadInternal() */
7293 : /************************************************************************/
7294 :
7295 : template <typename Type>
7296 44 : void GDALMDArrayMask::ReadInternal(
7297 : const size_t *count, const GPtrDiff_t *bufferStride,
7298 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7299 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7300 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7301 : {
7302 44 : const size_t nDims = GetDimensionCount();
7303 :
7304 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7305 : {
7306 220 : if (bHasVal)
7307 : {
7308 49 : if (IsValidForDT<Type>(dfVal))
7309 : {
7310 49 : return static_cast<Type>(dfVal);
7311 : }
7312 : else
7313 : {
7314 0 : bHasVal = false;
7315 : }
7316 : }
7317 171 : return 0;
7318 : };
7319 :
7320 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7321 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7322 : const Type nNoDataValue =
7323 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7324 44 : bool bHasMissingValue = m_bHasMissingValue;
7325 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7326 44 : bool bHasFillValue = m_bHasFillValue;
7327 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7328 44 : bool bHasValidMin = m_bHasValidMin;
7329 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7330 44 : bool bHasValidMax = m_bHasValidMax;
7331 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7332 44 : const bool bHasValidFlags =
7333 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7334 :
7335 351 : const auto IsValidFlag = [this](Type v)
7336 : {
7337 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7338 : {
7339 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7340 : {
7341 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7342 : m_anValidFlagValues[i])
7343 : {
7344 4 : return true;
7345 : }
7346 : }
7347 : }
7348 42 : else if (!m_anValidFlagValues.empty())
7349 : {
7350 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7351 : {
7352 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7353 : {
7354 4 : return true;
7355 : }
7356 : }
7357 : }
7358 : else /* if( !m_anValidFlagMasks.empty() ) */
7359 : {
7360 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7361 : {
7362 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7363 : {
7364 9 : return true;
7365 : }
7366 : }
7367 : }
7368 37 : return false;
7369 : };
7370 :
7371 : #define GET_MASK_FOR_SAMPLE(v) \
7372 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7373 : !(bHasMissingValue && v == nMissingValue) && \
7374 : !(bHasFillValue && v == nFillValue) && \
7375 : !(bHasValidMin && v < nValidMin) && \
7376 : !(bHasValidMax && v > nValidMax) && \
7377 : (!bHasValidFlags || IsValidFlag(v)));
7378 :
7379 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7380 : /* Optimized case: Byte output and output buffer is contiguous */
7381 44 : if (bBufferDataTypeIsByte)
7382 : {
7383 40 : bool bContiguous = true;
7384 103 : for (size_t i = 0; i < nDims; i++)
7385 : {
7386 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7387 : {
7388 1 : bContiguous = false;
7389 1 : break;
7390 : }
7391 : }
7392 40 : if (bContiguous)
7393 : {
7394 39 : size_t nElts = 1;
7395 102 : for (size_t i = 0; i < nDims; i++)
7396 63 : nElts *= count[i];
7397 :
7398 1113 : for (size_t i = 0; i < nElts; i++)
7399 : {
7400 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7401 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7402 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7403 : }
7404 39 : return;
7405 : }
7406 : }
7407 :
7408 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7409 :
7410 : struct Stack
7411 : {
7412 : size_t nIters = 0;
7413 : const GByte *src_ptr = nullptr;
7414 : GByte *dst_ptr = nullptr;
7415 : GPtrDiff_t src_inc_offset = 0;
7416 : GPtrDiff_t dst_inc_offset = 0;
7417 : };
7418 :
7419 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7420 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7421 15 : for (size_t i = 0; i < nDims; i++)
7422 : {
7423 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7424 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7425 10 : stack[i].dst_inc_offset =
7426 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7427 : }
7428 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7429 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7430 :
7431 5 : size_t dimIdx = 0;
7432 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7433 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7434 5 : CPLAssert(nBufferDTSize <= 16);
7435 15 : for (GByte flag = 0; flag <= 1; flag++)
7436 : {
7437 10 : GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7438 : bufferDataType.GetNumericDataType(), 0, 1);
7439 : }
7440 :
7441 43 : lbl_next_depth:
7442 43 : if (dimIdx == nDimsMinus1)
7443 : {
7444 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7445 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7446 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7447 :
7448 420 : while (true)
7449 : {
7450 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7451 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7452 :
7453 455 : if (bBufferDataTypeIsByte)
7454 : {
7455 24 : *dst_ptr = flag;
7456 : }
7457 : else
7458 : {
7459 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7460 : }
7461 :
7462 455 : if ((--nIters) == 0)
7463 35 : break;
7464 420 : src_ptr += stack[dimIdx].src_inc_offset;
7465 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7466 : }
7467 : }
7468 : else
7469 : {
7470 8 : stack[dimIdx].nIters = count[dimIdx];
7471 : while (true)
7472 : {
7473 38 : dimIdx++;
7474 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7475 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7476 38 : goto lbl_next_depth;
7477 38 : lbl_return_to_caller:
7478 38 : dimIdx--;
7479 38 : if ((--stack[dimIdx].nIters) == 0)
7480 8 : break;
7481 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7482 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7483 : }
7484 : }
7485 43 : if (dimIdx > 0)
7486 38 : goto lbl_return_to_caller;
7487 : }
7488 :
7489 : /************************************************************************/
7490 : /* GetMask() */
7491 : /************************************************************************/
7492 :
7493 : /** Return an array that is a mask for the current array
7494 :
7495 : This array will be of type Byte, with values set to 0 to indicate invalid
7496 : pixels of the current array, and values set to 1 to indicate valid pixels.
7497 :
7498 : The generic implementation honours the NoDataValue, as well as various
7499 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7500 : and valid_range.
7501 :
7502 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7503 : can be used to specify strings of the "flag_meanings" attribute
7504 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7505 : for which pixels matching any of those flags will be set at 1 in the mask array,
7506 : and pixels matching none of those flags will be set at 0.
7507 : For example, let's consider the following netCDF variable defined with:
7508 : \verbatim
7509 : l2p_flags:valid_min = 0s ;
7510 : l2p_flags:valid_max = 256s ;
7511 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7512 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7513 : \endverbatim
7514 :
7515 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7516 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7517 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7518 : will be 1.
7519 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7520 : will be 0.
7521 :
7522 : This is the same as the C function GDALMDArrayGetMask().
7523 :
7524 : @param papszOptions NULL-terminated list of options, or NULL.
7525 :
7526 : @return a new array, that holds a reference to the original one, and thus is
7527 : a view of it (not a copy), or nullptr in case of error.
7528 : */
7529 : std::shared_ptr<GDALMDArray>
7530 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7531 : {
7532 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7533 49 : if (!self)
7534 : {
7535 0 : CPLError(CE_Failure, CPLE_AppDefined,
7536 : "Driver implementation issue: m_pSelf not set !");
7537 0 : return nullptr;
7538 : }
7539 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7540 : {
7541 1 : CPLError(CE_Failure, CPLE_AppDefined,
7542 : "GetMask() only supports numeric data type");
7543 1 : return nullptr;
7544 : }
7545 48 : return GDALMDArrayMask::Create(self, papszOptions);
7546 : }
7547 :
7548 : /************************************************************************/
7549 : /* IsRegularlySpaced() */
7550 : /************************************************************************/
7551 :
7552 : /** Returns whether an array is a 1D regularly spaced array.
7553 : *
7554 : * @param[out] dfStart First value in the array
7555 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7556 : * @return true if the array is regularly spaced.
7557 : */
7558 323 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7559 : {
7560 323 : dfStart = 0;
7561 323 : dfIncrement = 0;
7562 323 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7563 0 : return false;
7564 323 : const auto nSize = GetDimensions()[0]->GetSize();
7565 323 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7566 2 : return false;
7567 :
7568 321 : size_t nCount = static_cast<size_t>(nSize);
7569 642 : std::vector<double> adfTmp;
7570 : try
7571 : {
7572 321 : adfTmp.resize(nCount);
7573 : }
7574 0 : catch (const std::exception &)
7575 : {
7576 0 : return false;
7577 : }
7578 :
7579 321 : GUInt64 anStart[1] = {0};
7580 321 : size_t anCount[1] = {nCount};
7581 :
7582 : const auto IsRegularlySpacedInternal =
7583 89644 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7584 : {
7585 419 : dfStart = adfTmp[0];
7586 419 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7587 419 : if (dfIncrement == 0)
7588 : {
7589 3 : return false;
7590 : }
7591 22295 : for (size_t i = 1; i < anCount[0]; i++)
7592 : {
7593 21891 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7594 21891 : 1e-3 * fabs(dfIncrement))
7595 : {
7596 12 : return false;
7597 : }
7598 : }
7599 404 : return true;
7600 321 : };
7601 :
7602 : // First try with the first block(s). This can avoid excessive processing
7603 : // time, for example with Zarr datasets.
7604 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7605 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7606 321 : const auto nBlockSize = GetBlockSize()[0];
7607 321 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7608 : {
7609 : size_t nReducedCount =
7610 101 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7611 514 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7612 413 : nReducedCount *= 2;
7613 101 : anCount[0] = nReducedCount;
7614 101 : if (!Read(anStart, anCount, nullptr, nullptr,
7615 202 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7616 : {
7617 0 : return false;
7618 : }
7619 101 : if (!IsRegularlySpacedInternal())
7620 : {
7621 3 : return false;
7622 : }
7623 :
7624 : // Get next values
7625 98 : anStart[0] = nReducedCount;
7626 98 : anCount[0] = nCount - nReducedCount;
7627 : }
7628 :
7629 318 : if (!Read(anStart, anCount, nullptr, nullptr,
7630 636 : GDALExtendedDataType::Create(GDT_Float64),
7631 318 : &adfTmp[static_cast<size_t>(anStart[0])]))
7632 : {
7633 0 : return false;
7634 : }
7635 :
7636 318 : return IsRegularlySpacedInternal();
7637 : }
7638 :
7639 : /************************************************************************/
7640 : /* GuessGeoTransform() */
7641 : /************************************************************************/
7642 :
7643 : /** Returns whether 2 specified dimensions form a geotransform
7644 : *
7645 : * @param nDimX Index of the X axis.
7646 : * @param nDimY Index of the Y axis.
7647 : * @param bPixelIsPoint Whether the geotransform should be returned
7648 : * with the pixel-is-point (pixel-center) convention
7649 : * (bPixelIsPoint = true), or with the pixel-is-area
7650 : * (top left corner convention)
7651 : * (bPixelIsPoint = false)
7652 : * @param[out] gt Computed geotransform
7653 : * @return true if a geotransform could be computed.
7654 : */
7655 264 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7656 : bool bPixelIsPoint,
7657 : GDALGeoTransform >) const
7658 : {
7659 264 : const auto &dims(GetDimensions());
7660 528 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7661 528 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7662 264 : double dfXStart = 0.0;
7663 264 : double dfXSpacing = 0.0;
7664 264 : double dfYStart = 0.0;
7665 264 : double dfYSpacing = 0.0;
7666 584 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7667 320 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7668 458 : poVarY && poVarY->GetDimensionCount() == 1 &&
7669 149 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7670 568 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7671 144 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7672 : {
7673 144 : gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7674 144 : gt[1] = dfXSpacing;
7675 144 : gt[2] = 0;
7676 144 : gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7677 144 : gt[4] = 0;
7678 144 : gt[5] = dfYSpacing;
7679 144 : return true;
7680 : }
7681 120 : return false;
7682 : }
7683 :
7684 : /** Returns whether 2 specified dimensions form a geotransform
7685 : *
7686 : * @param nDimX Index of the X axis.
7687 : * @param nDimY Index of the Y axis.
7688 : * @param bPixelIsPoint Whether the geotransform should be returned
7689 : * with the pixel-is-point (pixel-center) convention
7690 : * (bPixelIsPoint = true), or with the pixel-is-area
7691 : * (top left corner convention)
7692 : * (bPixelIsPoint = false)
7693 : * @param[out] adfGeoTransform Computed geotransform
7694 : * @return true if a geotransform could be computed.
7695 : */
7696 0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7697 : bool bPixelIsPoint,
7698 : double adfGeoTransform[6]) const
7699 : {
7700 0 : GDALGeoTransform *gt =
7701 : reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7702 0 : return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7703 : }
7704 :
7705 : /************************************************************************/
7706 : /* GDALMDArrayResampled */
7707 : /************************************************************************/
7708 :
7709 : class GDALMDArrayResampledDataset;
7710 :
7711 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7712 : {
7713 : protected:
7714 : CPLErr IReadBlock(int, int, void *) override;
7715 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7716 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7717 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7718 : GSpacing nLineSpaceBuf,
7719 : GDALRasterIOExtraArg *psExtraArg) override;
7720 :
7721 : public:
7722 : explicit GDALMDArrayResampledDatasetRasterBand(
7723 : GDALMDArrayResampledDataset *poDSIn);
7724 :
7725 : double GetNoDataValue(int *pbHasNoData) override;
7726 : };
7727 :
7728 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7729 : {
7730 : friend class GDALMDArrayResampled;
7731 : friend class GDALMDArrayResampledDatasetRasterBand;
7732 :
7733 : std::shared_ptr<GDALMDArray> m_poArray;
7734 : const size_t m_iXDim;
7735 : const size_t m_iYDim;
7736 : GDALGeoTransform m_gt{};
7737 : bool m_bHasGT = false;
7738 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7739 :
7740 : std::vector<GUInt64> m_anOffset{};
7741 : std::vector<size_t> m_anCount{};
7742 : std::vector<GPtrDiff_t> m_anStride{};
7743 :
7744 : std::string m_osFilenameLong{};
7745 : std::string m_osFilenameLat{};
7746 :
7747 : public:
7748 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7749 : size_t iXDim, size_t iYDim)
7750 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7751 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7752 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7753 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7754 : {
7755 24 : const auto &dims(m_poArray->GetDimensions());
7756 :
7757 24 : nRasterYSize = static_cast<int>(
7758 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7759 24 : nRasterXSize = static_cast<int>(
7760 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7761 :
7762 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7763 :
7764 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7765 24 : }
7766 :
7767 : ~GDALMDArrayResampledDataset() override;
7768 :
7769 43 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
7770 : {
7771 43 : gt = m_gt;
7772 43 : return m_bHasGT ? CE_None : CE_Failure;
7773 : }
7774 :
7775 105 : const OGRSpatialReference *GetSpatialRef() const override
7776 : {
7777 105 : m_poSRS = m_poArray->GetSpatialRef();
7778 105 : if (m_poSRS)
7779 : {
7780 79 : m_poSRS.reset(m_poSRS->Clone());
7781 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7782 237 : for (auto &m : axisMapping)
7783 : {
7784 158 : if (m == static_cast<int>(m_iXDim) + 1)
7785 79 : m = 1;
7786 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7787 79 : m = 2;
7788 : }
7789 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7790 : }
7791 105 : return m_poSRS.get();
7792 : }
7793 :
7794 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7795 : const std::string &osFilenameLat)
7796 : {
7797 5 : m_osFilenameLong = osFilenameLong;
7798 5 : m_osFilenameLat = osFilenameLat;
7799 10 : CPLStringList aosGeoLoc;
7800 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7801 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7802 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7803 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7804 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7805 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7806 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7807 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7808 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7809 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7810 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7811 5 : }
7812 : };
7813 :
7814 48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7815 : {
7816 24 : if (!m_osFilenameLong.empty())
7817 5 : VSIUnlink(m_osFilenameLong.c_str());
7818 24 : if (!m_osFilenameLat.empty())
7819 5 : VSIUnlink(m_osFilenameLat.c_str());
7820 48 : }
7821 :
7822 : /************************************************************************/
7823 : /* GDALMDArrayResampledDatasetRasterBand() */
7824 : /************************************************************************/
7825 :
7826 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7827 24 : GDALMDArrayResampledDataset *poDSIn)
7828 : {
7829 24 : const auto &poArray(poDSIn->m_poArray);
7830 24 : const auto blockSize(poArray->GetBlockSize());
7831 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7832 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7833 13 : blockSize[poDSIn->m_iYDim]))
7834 24 : : 1;
7835 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7836 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7837 13 : blockSize[poDSIn->m_iXDim]))
7838 24 : : poDSIn->GetRasterXSize();
7839 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7840 24 : eAccess = poDSIn->eAccess;
7841 24 : }
7842 :
7843 : /************************************************************************/
7844 : /* GetNoDataValue() */
7845 : /************************************************************************/
7846 :
7847 54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7848 : {
7849 54 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7850 54 : const auto &poArray(l_poDS->m_poArray);
7851 54 : bool bHasNodata = false;
7852 54 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7853 54 : if (pbHasNoData)
7854 48 : *pbHasNoData = bHasNodata;
7855 54 : return dfRes;
7856 : }
7857 :
7858 : /************************************************************************/
7859 : /* IReadBlock() */
7860 : /************************************************************************/
7861 :
7862 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7863 : int nBlockYOff,
7864 : void *pImage)
7865 : {
7866 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7867 0 : const int nXOff = nBlockXOff * nBlockXSize;
7868 0 : const int nYOff = nBlockYOff * nBlockYSize;
7869 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7870 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7871 : GDALRasterIOExtraArg sExtraArg;
7872 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7873 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7874 : nReqXSize, nReqYSize, eDataType, nDTSize,
7875 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7876 : }
7877 :
7878 : /************************************************************************/
7879 : /* IRasterIO() */
7880 : /************************************************************************/
7881 :
7882 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7883 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7884 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7885 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7886 : GDALRasterIOExtraArg *psExtraArg)
7887 : {
7888 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7889 32 : const auto &poArray(l_poDS->m_poArray);
7890 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7891 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7892 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7893 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7894 : {
7895 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7896 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7897 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7898 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7899 :
7900 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7901 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7902 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7903 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7904 :
7905 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7906 32 : l_poDS->m_anCount.data(), nullptr,
7907 32 : l_poDS->m_anStride.data(),
7908 64 : GDALExtendedDataType::Create(eBufType), pData)
7909 32 : ? CE_None
7910 32 : : CE_Failure;
7911 : }
7912 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7913 : pData, nBufXSize, nBufYSize, eBufType,
7914 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7915 : }
7916 :
7917 : class GDALMDArrayResampled final : public GDALPamMDArray
7918 : {
7919 : private:
7920 : std::shared_ptr<GDALMDArray> m_poParent{};
7921 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7922 : std::vector<GUInt64> m_anBlockSize;
7923 : GDALExtendedDataType m_dt;
7924 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7925 : std::shared_ptr<GDALMDArray> m_poVarX{};
7926 : std::shared_ptr<GDALMDArray> m_poVarY{};
7927 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7928 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7929 :
7930 : protected:
7931 21 : GDALMDArrayResampled(
7932 : const std::shared_ptr<GDALMDArray> &poParent,
7933 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7934 : const std::vector<GUInt64> &anBlockSize)
7935 42 : : GDALAbstractMDArray(std::string(),
7936 42 : "Resampled view of " + poParent->GetFullName()),
7937 : GDALPamMDArray(
7938 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7939 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7940 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7941 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7942 : {
7943 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7944 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7945 21 : }
7946 :
7947 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7948 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7949 : const GDALExtendedDataType &bufferDataType,
7950 : void *pDstBuffer) const override;
7951 :
7952 : public:
7953 : static std::shared_ptr<GDALMDArray>
7954 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7955 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7956 : GDALRIOResampleAlg resampleAlg,
7957 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7958 :
7959 42 : ~GDALMDArrayResampled() override
7960 21 : {
7961 : // First close the warped VRT
7962 21 : m_poReprojectedDS.reset();
7963 21 : m_poParentDS.reset();
7964 42 : }
7965 :
7966 11 : bool IsWritable() const override
7967 : {
7968 11 : return false;
7969 : }
7970 :
7971 74 : const std::string &GetFilename() const override
7972 : {
7973 74 : return m_poParent->GetFilename();
7974 : }
7975 :
7976 : const std::vector<std::shared_ptr<GDALDimension>> &
7977 257 : GetDimensions() const override
7978 : {
7979 257 : return m_apoDims;
7980 : }
7981 :
7982 109 : const GDALExtendedDataType &GetDataType() const override
7983 : {
7984 109 : return m_dt;
7985 : }
7986 :
7987 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7988 : {
7989 21 : return m_poSRS;
7990 : }
7991 :
7992 12 : std::vector<GUInt64> GetBlockSize() const override
7993 : {
7994 12 : return m_anBlockSize;
7995 : }
7996 :
7997 : std::shared_ptr<GDALAttribute>
7998 1 : GetAttribute(const std::string &osName) const override
7999 : {
8000 1 : return m_poParent->GetAttribute(osName);
8001 : }
8002 :
8003 : std::vector<std::shared_ptr<GDALAttribute>>
8004 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
8005 : {
8006 12 : return m_poParent->GetAttributes(papszOptions);
8007 : }
8008 :
8009 1 : const std::string &GetUnit() const override
8010 : {
8011 1 : return m_poParent->GetUnit();
8012 : }
8013 :
8014 1 : const void *GetRawNoDataValue() const override
8015 : {
8016 1 : return m_poParent->GetRawNoDataValue();
8017 : }
8018 :
8019 1 : double GetOffset(bool *pbHasOffset,
8020 : GDALDataType *peStorageType) const override
8021 : {
8022 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
8023 : }
8024 :
8025 1 : double GetScale(bool *pbHasScale,
8026 : GDALDataType *peStorageType) const override
8027 : {
8028 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
8029 : }
8030 : };
8031 :
8032 : /************************************************************************/
8033 : /* GDALMDArrayResampled::Create() */
8034 : /************************************************************************/
8035 :
8036 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8037 : const std::shared_ptr<GDALMDArray> &poParent,
8038 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8039 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8040 : CSLConstList /* papszOptions */)
8041 : {
8042 29 : const char *pszResampleAlg = "nearest";
8043 29 : bool unsupported = false;
8044 29 : switch (resampleAlg)
8045 : {
8046 16 : case GRIORA_NearestNeighbour:
8047 16 : pszResampleAlg = "nearest";
8048 16 : break;
8049 2 : case GRIORA_Bilinear:
8050 2 : pszResampleAlg = "bilinear";
8051 2 : break;
8052 5 : case GRIORA_Cubic:
8053 5 : pszResampleAlg = "cubic";
8054 5 : break;
8055 1 : case GRIORA_CubicSpline:
8056 1 : pszResampleAlg = "cubicspline";
8057 1 : break;
8058 1 : case GRIORA_Lanczos:
8059 1 : pszResampleAlg = "lanczos";
8060 1 : break;
8061 1 : case GRIORA_Average:
8062 1 : pszResampleAlg = "average";
8063 1 : break;
8064 1 : case GRIORA_Mode:
8065 1 : pszResampleAlg = "mode";
8066 1 : break;
8067 1 : case GRIORA_Gauss:
8068 1 : unsupported = true;
8069 1 : break;
8070 0 : case GRIORA_RESERVED_START:
8071 0 : unsupported = true;
8072 0 : break;
8073 0 : case GRIORA_RESERVED_END:
8074 0 : unsupported = true;
8075 0 : break;
8076 1 : case GRIORA_RMS:
8077 1 : pszResampleAlg = "rms";
8078 1 : break;
8079 : }
8080 29 : if (unsupported)
8081 : {
8082 1 : CPLError(CE_Failure, CPLE_NotSupported,
8083 : "Unsupported resample method for GetResampled()");
8084 1 : return nullptr;
8085 : }
8086 :
8087 28 : if (poParent->GetDimensionCount() < 2)
8088 : {
8089 1 : CPLError(CE_Failure, CPLE_NotSupported,
8090 : "GetResampled() only supports 2 dimensions or more");
8091 1 : return nullptr;
8092 : }
8093 :
8094 27 : const auto &aoParentDims = poParent->GetDimensions();
8095 27 : if (apoNewDimsIn.size() != aoParentDims.size())
8096 : {
8097 2 : CPLError(CE_Failure, CPLE_AppDefined,
8098 : "GetResampled(): apoNewDims size should be the same as "
8099 : "GetDimensionCount()");
8100 2 : return nullptr;
8101 : }
8102 :
8103 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8104 25 : apoNewDims.reserve(apoNewDimsIn.size());
8105 :
8106 50 : std::vector<GUInt64> anBlockSize;
8107 25 : anBlockSize.reserve(apoNewDimsIn.size());
8108 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8109 :
8110 50 : auto apoParentDims = poParent->GetDimensions();
8111 : // Special case for NASA EMIT datasets
8112 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8113 7 : apoParentDims[0]->GetName() == "downtrack" &&
8114 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8115 2 : apoParentDims[2]->GetName() == "bands");
8116 :
8117 : const size_t iYDimParent =
8118 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8119 : const size_t iXDimParent =
8120 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8121 :
8122 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8123 : {
8124 53 : if (i == iYDimParent || i == iXDimParent)
8125 48 : continue;
8126 5 : if (apoNewDimsIn[i] == nullptr)
8127 : {
8128 3 : apoNewDims.emplace_back(aoParentDims[i]);
8129 : }
8130 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8131 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8132 : {
8133 1 : CPLError(CE_Failure, CPLE_AppDefined,
8134 : "GetResampled(): apoNewDims[%u] should be the same "
8135 : "as its parent",
8136 : i);
8137 1 : return nullptr;
8138 : }
8139 : else
8140 : {
8141 1 : apoNewDims.emplace_back(aoParentDims[i]);
8142 : }
8143 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8144 : }
8145 :
8146 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8147 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8148 :
8149 24 : double dfXStart = 0.0;
8150 24 : double dfXSpacing = 0.0;
8151 24 : bool gotXSpacing = false;
8152 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8153 24 : if (poNewDimX)
8154 : {
8155 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8156 : {
8157 0 : CPLError(CE_Failure, CPLE_NotSupported,
8158 : "Too big size for X dimension");
8159 0 : return nullptr;
8160 : }
8161 4 : auto var = poNewDimX->GetIndexingVariable();
8162 4 : if (var)
8163 : {
8164 2 : if (var->GetDimensionCount() != 1 ||
8165 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8166 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8167 : {
8168 0 : CPLError(CE_Failure, CPLE_NotSupported,
8169 : "New X dimension should be indexed by a regularly "
8170 : "spaced variable");
8171 0 : return nullptr;
8172 : }
8173 1 : gotXSpacing = true;
8174 : }
8175 : }
8176 :
8177 24 : double dfYStart = 0.0;
8178 24 : double dfYSpacing = 0.0;
8179 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8180 24 : bool gotYSpacing = false;
8181 24 : if (poNewDimY)
8182 : {
8183 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8184 : {
8185 0 : CPLError(CE_Failure, CPLE_NotSupported,
8186 : "Too big size for Y dimension");
8187 0 : return nullptr;
8188 : }
8189 4 : auto var = poNewDimY->GetIndexingVariable();
8190 4 : if (var)
8191 : {
8192 2 : if (var->GetDimensionCount() != 1 ||
8193 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8194 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8195 : {
8196 0 : CPLError(CE_Failure, CPLE_NotSupported,
8197 : "New Y dimension should be indexed by a regularly "
8198 : "spaced variable");
8199 0 : return nullptr;
8200 : }
8201 1 : gotYSpacing = true;
8202 : }
8203 : }
8204 :
8205 : // This limitation could probably be removed
8206 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8207 : {
8208 0 : CPLError(CE_Failure, CPLE_NotSupported,
8209 : "Either none of new X or Y dimension should have an indexing "
8210 : "variable, or both should both should have one.");
8211 0 : return nullptr;
8212 : }
8213 :
8214 48 : std::string osDstWKT;
8215 24 : if (poTargetSRS)
8216 : {
8217 2 : char *pszDstWKT = nullptr;
8218 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8219 : {
8220 0 : CPLFree(pszDstWKT);
8221 0 : return nullptr;
8222 : }
8223 2 : osDstWKT = pszDstWKT;
8224 2 : CPLFree(pszDstWKT);
8225 : }
8226 :
8227 : // Use coordinate variables for geolocation array
8228 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8229 24 : bool useGeolocationArray = false;
8230 24 : if (apoCoordinateVars.size() >= 2)
8231 : {
8232 0 : std::shared_ptr<GDALMDArray> poLongVar;
8233 0 : std::shared_ptr<GDALMDArray> poLatVar;
8234 15 : for (const auto &poCoordVar : apoCoordinateVars)
8235 : {
8236 10 : const auto &osName = poCoordVar->GetName();
8237 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8238 20 : std::string osStandardName;
8239 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8240 2 : poAttr->GetDimensionCount() == 0)
8241 : {
8242 2 : const char *pszStandardName = poAttr->ReadAsString();
8243 2 : if (pszStandardName)
8244 2 : osStandardName = pszStandardName;
8245 : }
8246 21 : if (osName == "lon" || osName == "longitude" ||
8247 21 : osName == "Longitude" || osStandardName == "longitude")
8248 : {
8249 5 : poLongVar = poCoordVar;
8250 : }
8251 6 : else if (osName == "lat" || osName == "latitude" ||
8252 6 : osName == "Latitude" || osStandardName == "latitude")
8253 : {
8254 5 : poLatVar = poCoordVar;
8255 : }
8256 : }
8257 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8258 : {
8259 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8260 5 : const auto &longDims = poLongVar->GetDimensions();
8261 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8262 5 : const auto &latDims = poLatVar->GetDimensions();
8263 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8264 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8265 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8266 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8267 : {
8268 : // Geolocation arrays are 1D, and of consistent size with
8269 : // the variable
8270 0 : useGeolocationArray = true;
8271 : }
8272 1 : else if ((longDimCount == 2 ||
8273 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8274 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8275 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8276 1 : (latDimCount == 2 ||
8277 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8278 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8279 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8280 :
8281 : {
8282 : // Geolocation arrays are 2D (or 3D with first dimension of
8283 : // size 1, as found in Sentinel 5P products), and of consistent
8284 : // size with the variable
8285 5 : useGeolocationArray = true;
8286 : }
8287 : else
8288 : {
8289 0 : CPLDebug(
8290 : "GDAL",
8291 : "Longitude and latitude coordinate variables found, "
8292 : "but their characteristics are not compatible of using "
8293 : "them as geolocation arrays");
8294 : }
8295 5 : if (useGeolocationArray)
8296 : {
8297 10 : CPLDebug("GDAL",
8298 : "Setting geolocation array from variables %s and %s",
8299 5 : poLongVar->GetName().c_str(),
8300 5 : poLatVar->GetName().c_str());
8301 : const std::string osFilenameLong =
8302 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8303 : const std::string osFilenameLat =
8304 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8305 : std::unique_ptr<GDALDataset> poTmpLongDS(
8306 : longDimCount == 1
8307 0 : ? poLongVar->AsClassicDataset(0, 0)
8308 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8309 15 : longDimCount - 2));
8310 5 : auto hTIFFLongDS = GDALTranslate(
8311 : osFilenameLong.c_str(),
8312 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8313 : std::unique_ptr<GDALDataset> poTmpLatDS(
8314 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8315 20 : : poLatVar->AsClassicDataset(
8316 15 : latDimCount - 1, latDimCount - 2));
8317 5 : auto hTIFFLatDS = GDALTranslate(
8318 : osFilenameLat.c_str(),
8319 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8320 5 : const bool bError =
8321 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8322 5 : GDALClose(hTIFFLongDS);
8323 5 : GDALClose(hTIFFLatDS);
8324 5 : if (bError)
8325 : {
8326 0 : VSIUnlink(osFilenameLong.c_str());
8327 0 : VSIUnlink(osFilenameLat.c_str());
8328 0 : return nullptr;
8329 : }
8330 :
8331 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8332 : }
8333 : }
8334 : else
8335 : {
8336 0 : CPLDebug("GDAL",
8337 : "Coordinate variables available for %s, but "
8338 : "longitude and/or latitude variables were not identified",
8339 0 : poParent->GetName().c_str());
8340 : }
8341 : }
8342 :
8343 : // Build gdalwarp arguments
8344 48 : CPLStringList aosArgv;
8345 :
8346 24 : aosArgv.AddString("-of");
8347 24 : aosArgv.AddString("VRT");
8348 :
8349 24 : aosArgv.AddString("-r");
8350 24 : aosArgv.AddString(pszResampleAlg);
8351 :
8352 24 : if (!osDstWKT.empty())
8353 : {
8354 2 : aosArgv.AddString("-t_srs");
8355 2 : aosArgv.AddString(osDstWKT.c_str());
8356 : }
8357 :
8358 24 : if (useGeolocationArray)
8359 5 : aosArgv.AddString("-geoloc");
8360 :
8361 24 : if (gotXSpacing && gotYSpacing)
8362 : {
8363 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8364 : const double dfXMax =
8365 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8366 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8367 : const double dfYMin =
8368 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8369 1 : aosArgv.AddString("-te");
8370 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8371 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8372 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8373 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8374 : }
8375 :
8376 24 : if (poNewDimX && poNewDimY)
8377 : {
8378 3 : aosArgv.AddString("-ts");
8379 : aosArgv.AddString(
8380 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8381 : aosArgv.AddString(
8382 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8383 : }
8384 21 : else if (poNewDimX)
8385 : {
8386 1 : aosArgv.AddString("-ts");
8387 : aosArgv.AddString(
8388 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8389 1 : aosArgv.AddString("0");
8390 : }
8391 20 : else if (poNewDimY)
8392 : {
8393 1 : aosArgv.AddString("-ts");
8394 1 : aosArgv.AddString("0");
8395 : aosArgv.AddString(
8396 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8397 : }
8398 :
8399 : // Create a warped VRT dataset
8400 : GDALWarpAppOptions *psOptions =
8401 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8402 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8403 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8404 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8405 24 : GDALWarpAppOptionsFree(psOptions);
8406 24 : if (poReprojectedDS == nullptr)
8407 3 : return nullptr;
8408 :
8409 : int nBlockXSize;
8410 : int nBlockYSize;
8411 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8412 21 : anBlockSize.emplace_back(nBlockYSize);
8413 21 : anBlockSize.emplace_back(nBlockXSize);
8414 :
8415 21 : GDALGeoTransform gt;
8416 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8417 21 : CPLAssert(eErr == CE_None);
8418 21 : CPL_IGNORE_RET_VAL(eErr);
8419 :
8420 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8421 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8422 42 : poReprojectedDS->GetRasterYSize());
8423 : auto varY = GDALMDArrayRegularlySpaced::Create(
8424 63 : std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
8425 21 : poDimY->SetIndexingVariable(varY);
8426 :
8427 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8428 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8429 42 : poReprojectedDS->GetRasterXSize());
8430 : auto varX = GDALMDArrayRegularlySpaced::Create(
8431 63 : std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
8432 21 : poDimX->SetIndexingVariable(varX);
8433 :
8434 21 : apoNewDims.emplace_back(poDimY);
8435 21 : apoNewDims.emplace_back(poDimX);
8436 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8437 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8438 21 : newAr->SetSelf(newAr);
8439 21 : if (poTargetSRS)
8440 : {
8441 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8442 : }
8443 : else
8444 : {
8445 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8446 : }
8447 21 : newAr->m_poVarX = varX;
8448 21 : newAr->m_poVarY = varY;
8449 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8450 21 : newAr->m_poParentDS = std::move(poParentDS);
8451 :
8452 : // If the input array is y,x,band ordered, the above newAr is
8453 : // actually band,y,x ordered as it is more convenient for
8454 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8455 : // array to the order of the input array
8456 21 : if (bYXBandOrder)
8457 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8458 :
8459 19 : return newAr;
8460 : }
8461 :
8462 : /************************************************************************/
8463 : /* GDALMDArrayResampled::IRead() */
8464 : /************************************************************************/
8465 :
8466 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8467 : const size_t *count, const GInt64 *arrayStep,
8468 : const GPtrDiff_t *bufferStride,
8469 : const GDALExtendedDataType &bufferDataType,
8470 : void *pDstBuffer) const
8471 : {
8472 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8473 0 : return false;
8474 :
8475 : struct Stack
8476 : {
8477 : size_t nIters = 0;
8478 : GByte *dst_ptr = nullptr;
8479 : GPtrDiff_t dst_inc_offset = 0;
8480 : };
8481 :
8482 29 : const auto nDims = GetDimensionCount();
8483 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8484 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8485 92 : for (size_t i = 0; i < nDims; i++)
8486 : {
8487 63 : stack[i].dst_inc_offset =
8488 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8489 : }
8490 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8491 :
8492 29 : size_t dimIdx = 0;
8493 29 : const size_t iDimY = nDims - 2;
8494 29 : const size_t iDimX = nDims - 1;
8495 : // Use an array to avoid a false positive warning from CLang Static
8496 : // Analyzer about flushCaches being never read
8497 29 : bool flushCaches[] = {false};
8498 : const bool bYXBandOrder =
8499 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8500 :
8501 38 : lbl_next_depth:
8502 38 : if (dimIdx == iDimY)
8503 : {
8504 33 : if (flushCaches[0])
8505 : {
8506 5 : flushCaches[0] = false;
8507 : // When changing of 2D slice, flush GDAL 2D buffers
8508 5 : m_poParentDS->FlushCache(false);
8509 5 : m_poReprojectedDS->FlushCache(false);
8510 : }
8511 :
8512 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8513 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8514 : arrayStep, bufferStride, bufferDataType,
8515 33 : stack[dimIdx].dst_ptr))
8516 : {
8517 0 : return false;
8518 : }
8519 : }
8520 : else
8521 : {
8522 5 : stack[dimIdx].nIters = count[dimIdx];
8523 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8524 5 : arrayStartIdx[dimIdx])
8525 : {
8526 1 : flushCaches[0] = true;
8527 : }
8528 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8529 5 : arrayStartIdx[dimIdx];
8530 : while (true)
8531 : {
8532 9 : dimIdx++;
8533 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8534 9 : goto lbl_next_depth;
8535 9 : lbl_return_to_caller:
8536 9 : dimIdx--;
8537 9 : if ((--stack[dimIdx].nIters) == 0)
8538 5 : break;
8539 4 : flushCaches[0] = true;
8540 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8541 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8542 : }
8543 : }
8544 38 : if (dimIdx > 0)
8545 9 : goto lbl_return_to_caller;
8546 :
8547 29 : return true;
8548 : }
8549 :
8550 : /************************************************************************/
8551 : /* GetResampled() */
8552 : /************************************************************************/
8553 :
8554 : /** Return an array that is a resampled / reprojected view of the current array
8555 : *
8556 : * This is the same as the C function GDALMDArrayGetResampled().
8557 : *
8558 : * Currently this method can only resample along the last 2 dimensions, unless
8559 : * orthorectifying a NASA EMIT dataset.
8560 : *
8561 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8562 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8563 : *
8564 : * Options available are:
8565 : * <ul>
8566 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8567 : * Can be set to NO to use generic reprojection method.
8568 : * </li>
8569 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8570 : * orthorectification to take into account the value of the
8571 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8572 : * current array along the band dimension are valid.</li>
8573 : * </ul>
8574 : *
8575 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8576 : * apoNewDims[i] can be NULL to let the method automatically
8577 : * determine it.
8578 : * @param resampleAlg Resampling algorithm
8579 : * @param poTargetSRS Target SRS, or nullptr
8580 : * @param papszOptions NULL-terminated list of options, or NULL.
8581 : *
8582 : * @return a new array, that holds a reference to the original one, and thus is
8583 : * a view of it (not a copy), or nullptr in case of error.
8584 : *
8585 : * @since 3.4
8586 : */
8587 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8588 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8589 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8590 : CSLConstList papszOptions) const
8591 : {
8592 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8593 38 : if (!self)
8594 : {
8595 0 : CPLError(CE_Failure, CPLE_AppDefined,
8596 : "Driver implementation issue: m_pSelf not set !");
8597 0 : return nullptr;
8598 : }
8599 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8600 : {
8601 0 : CPLError(CE_Failure, CPLE_AppDefined,
8602 : "GetResampled() only supports numeric data type");
8603 0 : return nullptr;
8604 : }
8605 :
8606 : // Special case for NASA EMIT datasets
8607 76 : auto apoDims = GetDimensions();
8608 36 : if (poTargetSRS == nullptr &&
8609 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8610 20 : apoDims[1]->GetName() == "crosstrack" &&
8611 10 : apoDims[2]->GetName() == "bands" &&
8612 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8613 1 : apoNewDims ==
8614 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8615 30 : apoDims[2]})) ||
8616 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8617 3 : apoDims[1]->GetName() == "crosstrack" &&
8618 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8619 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8620 : "EMIT_ORTHORECTIFICATION", "YES")))
8621 : {
8622 9 : auto poRootGroup = GetRootGroup();
8623 9 : if (poRootGroup)
8624 : {
8625 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8626 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8627 9 : if (poAttrGeotransform &&
8628 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8629 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8630 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8631 9 : poLocationGroup)
8632 : {
8633 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8634 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8635 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8636 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8637 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8638 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8639 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8640 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8641 : {
8642 : return CreateGLTOrthorectified(
8643 : self, poRootGroup, poGLT_X, poGLT_Y,
8644 : /* nGLTIndexOffset = */ -1,
8645 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8646 : }
8647 : }
8648 : }
8649 : }
8650 :
8651 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8652 : "EMIT_ORTHORECTIFICATION", "NO")))
8653 : {
8654 0 : CPLError(CE_Failure, CPLE_AppDefined,
8655 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8656 : "parameters are not compatible with it");
8657 0 : return nullptr;
8658 : }
8659 :
8660 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8661 29 : poTargetSRS, papszOptions);
8662 : }
8663 :
8664 : /************************************************************************/
8665 : /* GDALDatasetFromArray() */
8666 : /************************************************************************/
8667 :
8668 : class GDALDatasetFromArray;
8669 :
8670 : namespace
8671 : {
8672 : struct MetadataItem
8673 : {
8674 : std::shared_ptr<GDALAbstractMDArray> poArray{};
8675 : std::string osName{};
8676 : std::string osDefinition{};
8677 : bool bDefinitionUsesPctForG = false;
8678 : };
8679 :
8680 : struct BandImageryMetadata
8681 : {
8682 : std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8683 : double dfCentralWavelengthToMicrometer = 1.0;
8684 : std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8685 : double dfFWHMToMicrometer = 1.0;
8686 : };
8687 :
8688 : } // namespace
8689 :
8690 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8691 : {
8692 : std::vector<GUInt64> m_anOffset{};
8693 : std::vector<size_t> m_anCount{};
8694 : std::vector<GPtrDiff_t> m_anStride{};
8695 :
8696 : protected:
8697 : CPLErr IReadBlock(int, int, void *) override;
8698 : CPLErr IWriteBlock(int, int, void *) override;
8699 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8700 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8701 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8702 : GSpacing nLineSpaceBuf,
8703 : GDALRasterIOExtraArg *psExtraArg) override;
8704 :
8705 : public:
8706 : explicit GDALRasterBandFromArray(
8707 : GDALDatasetFromArray *poDSIn,
8708 : const std::vector<GUInt64> &anOtherDimCoord,
8709 : const std::vector<std::vector<MetadataItem>>
8710 : &aoBandParameterMetadataItems,
8711 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8712 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8713 :
8714 : double GetNoDataValue(int *pbHasNoData) override;
8715 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8716 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8717 : double GetOffset(int *pbHasOffset) override;
8718 : double GetScale(int *pbHasScale) override;
8719 : const char *GetUnitType() override;
8720 : GDALColorInterp GetColorInterpretation() override;
8721 : };
8722 :
8723 : class GDALDatasetFromArray final : public GDALPamDataset
8724 : {
8725 : friend class GDALRasterBandFromArray;
8726 :
8727 : std::shared_ptr<GDALMDArray> m_poArray;
8728 : size_t m_iXDim;
8729 : size_t m_iYDim;
8730 : GDALGeoTransform m_gt{};
8731 : bool m_bHasGT = false;
8732 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8733 : GDALMultiDomainMetadata m_oMDD{};
8734 : std::string m_osOvrFilename{};
8735 :
8736 : public:
8737 240 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8738 : size_t iXDim, size_t iYDim)
8739 240 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8740 : {
8741 : // Initialize an overview filename from the filename of the array
8742 : // and its name.
8743 240 : const std::string &osFilename = m_poArray->GetFilename();
8744 240 : if (!osFilename.empty())
8745 : {
8746 205 : m_osOvrFilename = osFilename;
8747 205 : m_osOvrFilename += '.';
8748 8066 : for (char ch : m_poArray->GetName())
8749 : {
8750 7861 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8751 6956 : (ch >= 'a' && ch <= 'z') || ch == '_')
8752 : {
8753 6290 : m_osOvrFilename += ch;
8754 : }
8755 : else
8756 : {
8757 1571 : m_osOvrFilename += '_';
8758 : }
8759 : }
8760 205 : m_osOvrFilename += ".ovr";
8761 205 : oOvManager.Initialize(this);
8762 : }
8763 240 : }
8764 :
8765 : static GDALDatasetFromArray *
8766 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8767 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8768 : CSLConstList papszOptions);
8769 :
8770 : ~GDALDatasetFromArray() override;
8771 :
8772 387 : CPLErr Close() override
8773 : {
8774 387 : CPLErr eErr = CE_None;
8775 387 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8776 : {
8777 387 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8778 : CE_None)
8779 0 : eErr = CE_Failure;
8780 387 : m_poArray.reset();
8781 : }
8782 387 : return eErr;
8783 : }
8784 :
8785 73 : CPLErr GetGeoTransform(GDALGeoTransform >) const override
8786 : {
8787 73 : gt = m_gt;
8788 73 : return m_bHasGT ? CE_None : CE_Failure;
8789 : }
8790 :
8791 70 : const OGRSpatialReference *GetSpatialRef() const override
8792 : {
8793 70 : if (m_poArray->GetDimensionCount() < 2)
8794 3 : return nullptr;
8795 67 : m_poSRS = m_poArray->GetSpatialRef();
8796 67 : if (m_poSRS)
8797 : {
8798 28 : m_poSRS.reset(m_poSRS->Clone());
8799 56 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8800 84 : for (auto &m : axisMapping)
8801 : {
8802 56 : if (m == static_cast<int>(m_iXDim) + 1)
8803 28 : m = 1;
8804 28 : else if (m == static_cast<int>(m_iYDim) + 1)
8805 28 : m = 2;
8806 : }
8807 28 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8808 : }
8809 67 : return m_poSRS.get();
8810 : }
8811 :
8812 6 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8813 : {
8814 6 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8815 : }
8816 :
8817 179 : char **GetMetadata(const char *pszDomain) override
8818 : {
8819 179 : return m_oMDD.GetMetadata(pszDomain);
8820 : }
8821 :
8822 237 : const char *GetMetadataItem(const char *pszName,
8823 : const char *pszDomain) override
8824 : {
8825 429 : if (!m_osOvrFilename.empty() && pszName &&
8826 444 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8827 15 : EQUAL(pszDomain, "OVERVIEWS"))
8828 : {
8829 15 : return m_osOvrFilename.c_str();
8830 : }
8831 222 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8832 : }
8833 : };
8834 :
8835 480 : GDALDatasetFromArray::~GDALDatasetFromArray()
8836 : {
8837 240 : GDALDatasetFromArray::Close();
8838 480 : }
8839 :
8840 : /************************************************************************/
8841 : /* GDALRasterBandFromArray() */
8842 : /************************************************************************/
8843 :
8844 316 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8845 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8846 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8847 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8848 316 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8849 : {
8850 316 : const auto &poArray(poDSIn->m_poArray);
8851 316 : const auto &dims(poArray->GetDimensions());
8852 316 : const auto nDimCount(dims.size());
8853 632 : const auto blockSize(poArray->GetBlockSize());
8854 300 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8855 616 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8856 150 : blockSize[poDSIn->m_iYDim]))
8857 : : 1;
8858 316 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8859 165 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8860 165 : blockSize[poDSIn->m_iXDim]))
8861 316 : : poDSIn->GetRasterXSize();
8862 316 : eDataType = poArray->GetDataType().GetNumericDataType();
8863 316 : eAccess = poDSIn->eAccess;
8864 316 : m_anOffset.resize(nDimCount);
8865 316 : m_anCount.resize(nDimCount, 1);
8866 316 : m_anStride.resize(nDimCount);
8867 1063 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8868 : {
8869 747 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8870 : {
8871 262 : std::string dimName(dims[i]->GetName());
8872 131 : GUInt64 nIndex = anOtherDimCoord[j];
8873 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8874 : // subsetted dimensions as generated by GetView()
8875 131 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8876 : {
8877 : CPLStringList aosTokens(
8878 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8879 6 : if (aosTokens.size() == 5)
8880 : {
8881 6 : dimName = aosTokens[1];
8882 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8883 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8884 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8885 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8886 0 : : nStartDim - (nIndex * -nIncrDim);
8887 : }
8888 : }
8889 131 : if (nDimCount != 3 || dimName != "Band")
8890 : {
8891 70 : SetMetadataItem(
8892 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8893 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8894 : }
8895 :
8896 131 : auto indexingVar = dims[i]->GetIndexingVariable();
8897 :
8898 : // If the indexing variable is also listed in band parameter arrays,
8899 : // then don't use our default formatting
8900 131 : if (indexingVar)
8901 : {
8902 47 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8903 : {
8904 14 : if (oItem.poArray->GetFullName() ==
8905 14 : indexingVar->GetFullName())
8906 : {
8907 12 : indexingVar.reset();
8908 12 : break;
8909 : }
8910 : }
8911 : }
8912 :
8913 164 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8914 33 : indexingVar->GetDimensions()[0]->GetSize() ==
8915 33 : dims[i]->GetSize())
8916 : {
8917 33 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8918 : {
8919 0 : if (!bHasWarned)
8920 : {
8921 0 : CPLError(
8922 : CE_Warning, CPLE_AppDefined,
8923 : "Maximum delay to load band metadata from "
8924 : "dimension indexing variables has expired. "
8925 : "Increase the value of the "
8926 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8927 : "option of GDALMDArray::AsClassicDataset() "
8928 : "(also accessible as the "
8929 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8930 : "configuration option), "
8931 : "or set it to 'unlimited' for unlimited delay. ");
8932 0 : bHasWarned = true;
8933 : }
8934 : }
8935 : else
8936 : {
8937 33 : size_t nCount = 1;
8938 33 : const auto &dt(indexingVar->GetDataType());
8939 66 : std::vector<GByte> abyTmp(dt.GetSize());
8940 66 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8941 33 : nullptr, nullptr, dt, &abyTmp[0]))
8942 : {
8943 33 : char *pszTmp = nullptr;
8944 33 : GDALExtendedDataType::CopyValue(
8945 33 : &abyTmp[0], dt, &pszTmp,
8946 66 : GDALExtendedDataType::CreateString());
8947 33 : dt.FreeDynamicMemory(abyTmp.data());
8948 33 : if (pszTmp)
8949 : {
8950 33 : SetMetadataItem(
8951 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8952 : pszTmp);
8953 33 : CPLFree(pszTmp);
8954 : }
8955 :
8956 33 : const auto &unit(indexingVar->GetUnit());
8957 33 : if (!unit.empty())
8958 : {
8959 12 : SetMetadataItem(
8960 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8961 : unit.c_str());
8962 : }
8963 : }
8964 : }
8965 : }
8966 :
8967 149 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8968 : {
8969 36 : CPLString osVal;
8970 :
8971 18 : size_t nCount = 1;
8972 18 : const auto &dt(oItem.poArray->GetDataType());
8973 18 : if (oItem.bDefinitionUsesPctForG)
8974 : {
8975 : // There is one and only one %[x][.y]f|g in osDefinition
8976 16 : std::vector<GByte> abyTmp(dt.GetSize());
8977 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8978 8 : nullptr, nullptr, dt, &abyTmp[0]))
8979 : {
8980 8 : double dfVal = 0;
8981 8 : GDALExtendedDataType::CopyValue(
8982 8 : &abyTmp[0], dt, &dfVal,
8983 16 : GDALExtendedDataType::Create(GDT_Float64));
8984 8 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8985 8 : dt.FreeDynamicMemory(abyTmp.data());
8986 : }
8987 : }
8988 : else
8989 : {
8990 : // There should be zero or one %s in osDefinition
8991 10 : char *pszValue = nullptr;
8992 10 : if (dt.GetClass() == GEDTC_STRING)
8993 : {
8994 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8995 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8996 2 : dt, &pszValue));
8997 : }
8998 : else
8999 : {
9000 16 : std::vector<GByte> abyTmp(dt.GetSize());
9001 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9002 : nullptr, nullptr, dt,
9003 8 : &abyTmp[0]))
9004 : {
9005 8 : GDALExtendedDataType::CopyValue(
9006 8 : &abyTmp[0], dt, &pszValue,
9007 16 : GDALExtendedDataType::CreateString());
9008 : }
9009 : }
9010 :
9011 10 : if (pszValue)
9012 : {
9013 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9014 10 : CPLFree(pszValue);
9015 : }
9016 : }
9017 18 : if (!osVal.empty())
9018 18 : SetMetadataItem(oItem.osName.c_str(), osVal);
9019 : }
9020 :
9021 131 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9022 : {
9023 : auto &poCentralWavelengthArray =
9024 4 : aoBandImageryMetadata[j].poCentralWavelengthArray;
9025 4 : size_t nCount = 1;
9026 4 : const auto &dt(poCentralWavelengthArray->GetDataType());
9027 8 : std::vector<GByte> abyTmp(dt.GetSize());
9028 8 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9029 : &nCount, nullptr, nullptr,
9030 4 : dt, &abyTmp[0]))
9031 : {
9032 4 : double dfVal = 0;
9033 4 : GDALExtendedDataType::CopyValue(
9034 4 : &abyTmp[0], dt, &dfVal,
9035 8 : GDALExtendedDataType::Create(GDT_Float64));
9036 4 : dt.FreeDynamicMemory(abyTmp.data());
9037 4 : SetMetadataItem(
9038 : "CENTRAL_WAVELENGTH_UM",
9039 : CPLSPrintf(
9040 4 : "%g", dfVal * aoBandImageryMetadata[j]
9041 4 : .dfCentralWavelengthToMicrometer),
9042 : "IMAGERY");
9043 : }
9044 : }
9045 :
9046 131 : if (aoBandImageryMetadata[j].poFWHMArray)
9047 : {
9048 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9049 2 : size_t nCount = 1;
9050 2 : const auto &dt(poFWHMArray->GetDataType());
9051 4 : std::vector<GByte> abyTmp(dt.GetSize());
9052 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9053 2 : nullptr, dt, &abyTmp[0]))
9054 : {
9055 2 : double dfVal = 0;
9056 2 : GDALExtendedDataType::CopyValue(
9057 2 : &abyTmp[0], dt, &dfVal,
9058 4 : GDALExtendedDataType::Create(GDT_Float64));
9059 2 : dt.FreeDynamicMemory(abyTmp.data());
9060 2 : SetMetadataItem(
9061 : "FWHM_UM",
9062 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9063 2 : .dfFWHMToMicrometer),
9064 : "IMAGERY");
9065 : }
9066 : }
9067 :
9068 131 : m_anOffset[i] = anOtherDimCoord[j];
9069 131 : j++;
9070 : }
9071 : }
9072 316 : }
9073 :
9074 : /************************************************************************/
9075 : /* GetNoDataValue() */
9076 : /************************************************************************/
9077 :
9078 114 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9079 : {
9080 114 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9081 114 : const auto &poArray(l_poDS->m_poArray);
9082 114 : bool bHasNodata = false;
9083 114 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9084 114 : if (pbHasNoData)
9085 102 : *pbHasNoData = bHasNodata;
9086 114 : return res;
9087 : }
9088 :
9089 : /************************************************************************/
9090 : /* GetNoDataValueAsInt64() */
9091 : /************************************************************************/
9092 :
9093 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9094 : {
9095 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9096 1 : const auto &poArray(l_poDS->m_poArray);
9097 1 : bool bHasNodata = false;
9098 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9099 1 : if (pbHasNoData)
9100 1 : *pbHasNoData = bHasNodata;
9101 1 : return nodata;
9102 : }
9103 :
9104 : /************************************************************************/
9105 : /* GetNoDataValueAsUInt64() */
9106 : /************************************************************************/
9107 :
9108 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9109 : {
9110 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9111 1 : const auto &poArray(l_poDS->m_poArray);
9112 1 : bool bHasNodata = false;
9113 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9114 1 : if (pbHasNoData)
9115 1 : *pbHasNoData = bHasNodata;
9116 1 : return nodata;
9117 : }
9118 :
9119 : /************************************************************************/
9120 : /* GetOffset() */
9121 : /************************************************************************/
9122 :
9123 40 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9124 : {
9125 40 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9126 40 : const auto &poArray(l_poDS->m_poArray);
9127 40 : bool bHasValue = false;
9128 40 : double dfRes = poArray->GetOffset(&bHasValue);
9129 40 : if (pbHasOffset)
9130 21 : *pbHasOffset = bHasValue;
9131 40 : return dfRes;
9132 : }
9133 :
9134 : /************************************************************************/
9135 : /* GetUnitType() */
9136 : /************************************************************************/
9137 :
9138 46 : const char *GDALRasterBandFromArray::GetUnitType()
9139 : {
9140 46 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9141 46 : const auto &poArray(l_poDS->m_poArray);
9142 46 : return poArray->GetUnit().c_str();
9143 : }
9144 :
9145 : /************************************************************************/
9146 : /* GetScale() */
9147 : /************************************************************************/
9148 :
9149 38 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9150 : {
9151 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9152 38 : const auto &poArray(l_poDS->m_poArray);
9153 38 : bool bHasValue = false;
9154 38 : double dfRes = poArray->GetScale(&bHasValue);
9155 38 : if (pbHasScale)
9156 19 : *pbHasScale = bHasValue;
9157 38 : return dfRes;
9158 : }
9159 :
9160 : /************************************************************************/
9161 : /* IReadBlock() */
9162 : /************************************************************************/
9163 :
9164 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9165 : void *pImage)
9166 : {
9167 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9168 68 : const int nXOff = nBlockXOff * nBlockXSize;
9169 68 : const int nYOff = nBlockYOff * nBlockYSize;
9170 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9171 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9172 : GDALRasterIOExtraArg sExtraArg;
9173 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9174 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9175 : nReqXSize, nReqYSize, eDataType, nDTSize,
9176 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9177 : }
9178 :
9179 : /************************************************************************/
9180 : /* IWriteBlock() */
9181 : /************************************************************************/
9182 :
9183 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9184 : void *pImage)
9185 : {
9186 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9187 1 : const int nXOff = nBlockXOff * nBlockXSize;
9188 1 : const int nYOff = nBlockYOff * nBlockYSize;
9189 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9190 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9191 : GDALRasterIOExtraArg sExtraArg;
9192 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9193 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9194 : nReqXSize, nReqYSize, eDataType, nDTSize,
9195 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9196 : }
9197 :
9198 : /************************************************************************/
9199 : /* IRasterIO() */
9200 : /************************************************************************/
9201 :
9202 360 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9203 : int nYOff, int nXSize, int nYSize,
9204 : void *pData, int nBufXSize,
9205 : int nBufYSize, GDALDataType eBufType,
9206 : GSpacing nPixelSpaceBuf,
9207 : GSpacing nLineSpaceBuf,
9208 : GDALRasterIOExtraArg *psExtraArg)
9209 : {
9210 360 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9211 360 : const auto &poArray(l_poDS->m_poArray);
9212 360 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9213 360 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9214 360 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9215 360 : (nLineSpaceBuf % nBufferDTSize) == 0)
9216 : {
9217 360 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9218 360 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9219 720 : m_anStride[l_poDS->m_iXDim] =
9220 360 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9221 360 : if (poArray->GetDimensionCount() >= 2)
9222 : {
9223 347 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9224 347 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9225 347 : m_anStride[l_poDS->m_iYDim] =
9226 347 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9227 : }
9228 360 : if (eRWFlag == GF_Read)
9229 : {
9230 708 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9231 354 : m_anStride.data(),
9232 708 : GDALExtendedDataType::Create(eBufType), pData)
9233 354 : ? CE_None
9234 354 : : CE_Failure;
9235 : }
9236 : else
9237 : {
9238 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9239 6 : m_anStride.data(),
9240 12 : GDALExtendedDataType::Create(eBufType), pData)
9241 6 : ? CE_None
9242 6 : : CE_Failure;
9243 : }
9244 : }
9245 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9246 : pData, nBufXSize, nBufYSize, eBufType,
9247 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9248 : }
9249 :
9250 : /************************************************************************/
9251 : /* GetColorInterpretation() */
9252 : /************************************************************************/
9253 :
9254 66 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9255 : {
9256 66 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9257 66 : const auto &poArray(l_poDS->m_poArray);
9258 198 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9259 66 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9260 : {
9261 6 : bool bOK = false;
9262 6 : GUInt64 nStartIndex = 0;
9263 6 : if (poArray->GetDimensionCount() == 2 &&
9264 0 : poAttr->GetDimensionCount() == 0)
9265 : {
9266 0 : bOK = true;
9267 : }
9268 6 : else if (poArray->GetDimensionCount() == 3)
9269 : {
9270 6 : uint64_t nExtraDimSamples = 1;
9271 6 : const auto &apoDims = poArray->GetDimensions();
9272 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9273 : {
9274 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9275 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9276 : }
9277 6 : if (poAttr->GetDimensionsSize() ==
9278 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9279 : {
9280 6 : bOK = true;
9281 : }
9282 6 : nStartIndex = nBand - 1;
9283 : }
9284 6 : if (bOK)
9285 : {
9286 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9287 6 : const size_t nCount = 1;
9288 6 : const GInt64 arrayStep = 1;
9289 6 : const GPtrDiff_t bufferStride = 1;
9290 6 : char *pszValue = nullptr;
9291 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9292 6 : oStringDT, &pszValue);
9293 6 : if (pszValue)
9294 : {
9295 : const auto eColorInterp =
9296 6 : GDALGetColorInterpretationByName(pszValue);
9297 6 : CPLFree(pszValue);
9298 6 : return eColorInterp;
9299 : }
9300 : }
9301 : }
9302 60 : return GCI_Undefined;
9303 : }
9304 :
9305 : /************************************************************************/
9306 : /* GDALDatasetFromArray::Create() */
9307 : /************************************************************************/
9308 :
9309 292 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9310 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9311 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9312 :
9313 : {
9314 292 : const auto nDimCount(array->GetDimensionCount());
9315 292 : if (nDimCount == 0)
9316 : {
9317 1 : CPLError(CE_Failure, CPLE_NotSupported,
9318 : "Unsupported number of dimensions");
9319 1 : return nullptr;
9320 : }
9321 581 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9322 290 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9323 : {
9324 1 : CPLError(CE_Failure, CPLE_NotSupported,
9325 : "Only arrays with numeric data types "
9326 : "can be exposed as classic GDALDataset");
9327 1 : return nullptr;
9328 : }
9329 290 : if (iXDim >= nDimCount || iYDim >= nDimCount ||
9330 267 : (nDimCount >= 2 && iXDim == iYDim))
9331 : {
9332 8 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9333 8 : return nullptr;
9334 : }
9335 282 : GUInt64 nTotalBands = 1;
9336 282 : const auto &dims(array->GetDimensions());
9337 921 : for (size_t i = 0; i < nDimCount; ++i)
9338 : {
9339 640 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9340 : {
9341 94 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9342 : {
9343 1 : CPLError(CE_Failure, CPLE_AppDefined,
9344 : "Too many bands. Operate on a sliced view");
9345 1 : return nullptr;
9346 : }
9347 93 : nTotalBands *= dims[i]->GetSize();
9348 : }
9349 : }
9350 :
9351 562 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9352 562 : std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9353 920 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9354 : {
9355 639 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9356 : {
9357 93 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9358 93 : oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9359 93 : ++j;
9360 : }
9361 : }
9362 :
9363 281 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9364 :
9365 : const char *pszBandMetadata =
9366 281 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9367 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9368 562 : nNewDimCount);
9369 281 : if (pszBandMetadata)
9370 : {
9371 32 : if (!poRootGroup)
9372 : {
9373 1 : CPLError(CE_Failure, CPLE_AppDefined,
9374 : "Root group should be provided when BAND_METADATA is set");
9375 24 : return nullptr;
9376 : }
9377 31 : CPLJSONDocument oDoc;
9378 31 : if (!oDoc.LoadMemory(pszBandMetadata))
9379 : {
9380 1 : CPLError(CE_Failure, CPLE_AppDefined,
9381 : "Invalid JSON content for BAND_METADATA");
9382 1 : return nullptr;
9383 : }
9384 30 : auto oRoot = oDoc.GetRoot();
9385 30 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9386 : {
9387 1 : CPLError(CE_Failure, CPLE_AppDefined,
9388 : "Value of BAND_METADATA should be an array");
9389 1 : return nullptr;
9390 : }
9391 :
9392 29 : auto oArray = oRoot.ToArray();
9393 38 : for (int j = 0; j < oArray.Size(); ++j)
9394 : {
9395 30 : const auto oJsonItem = oArray[j];
9396 30 : MetadataItem oItem;
9397 30 : size_t iExtraDimIdx = 0;
9398 :
9399 60 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9400 60 : const auto osBandAttributeName = oJsonItem.GetString("attribute");
9401 0 : std::shared_ptr<GDALMDArray> poArray;
9402 0 : std::shared_ptr<GDALAttribute> poAttribute;
9403 30 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9404 : {
9405 1 : CPLError(CE_Failure, CPLE_AppDefined,
9406 : "BAND_METADATA[%d][\"array\"] or "
9407 : "BAND_METADATA[%d][\"attribute\"] is missing",
9408 : j, j);
9409 1 : return nullptr;
9410 : }
9411 48 : else if (!osBandArrayFullname.empty() &&
9412 19 : !osBandAttributeName.empty())
9413 : {
9414 1 : CPLError(
9415 : CE_Failure, CPLE_AppDefined,
9416 : "BAND_METADATA[%d][\"array\"] and "
9417 : "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9418 : j, j);
9419 1 : return nullptr;
9420 : }
9421 28 : else if (!osBandArrayFullname.empty())
9422 : {
9423 : poArray =
9424 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9425 18 : if (!poArray)
9426 : {
9427 1 : CPLError(CE_Failure, CPLE_AppDefined,
9428 : "Array %s cannot be found",
9429 : osBandArrayFullname.c_str());
9430 3 : return nullptr;
9431 : }
9432 17 : if (poArray->GetDimensionCount() != 1)
9433 : {
9434 1 : CPLError(CE_Failure, CPLE_AppDefined,
9435 : "Array %s is not a 1D array",
9436 : osBandArrayFullname.c_str());
9437 1 : return nullptr;
9438 : }
9439 : const auto &osAuxArrayDimName =
9440 16 : poArray->GetDimensions()[0]->GetName();
9441 : auto oIter =
9442 16 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9443 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9444 : {
9445 1 : CPLError(
9446 : CE_Failure, CPLE_AppDefined,
9447 : "Dimension %s of array %s is not a non-X/Y dimension "
9448 : "of array %s",
9449 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9450 1 : array->GetName().c_str());
9451 1 : return nullptr;
9452 : }
9453 15 : iExtraDimIdx = oIter->second;
9454 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9455 : }
9456 : else
9457 : {
9458 10 : CPLAssert(!osBandAttributeName.empty());
9459 10 : poAttribute = !osBandAttributeName.empty() &&
9460 10 : osBandAttributeName[0] == '/'
9461 24 : ? poRootGroup->OpenAttributeFromFullname(
9462 : osBandAttributeName)
9463 10 : : array->GetAttribute(osBandAttributeName);
9464 10 : if (!poAttribute)
9465 : {
9466 2 : CPLError(CE_Failure, CPLE_AppDefined,
9467 : "Attribute %s cannot be found",
9468 : osBandAttributeName.c_str());
9469 8 : return nullptr;
9470 : }
9471 8 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9472 8 : if (aoAttrDims.size() != 1)
9473 : {
9474 4 : CPLError(CE_Failure, CPLE_AppDefined,
9475 : "Attribute %s is not a 1D array",
9476 : osBandAttributeName.c_str());
9477 4 : return nullptr;
9478 : }
9479 4 : bool found = false;
9480 8 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9481 : {
9482 5 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9483 5 : ->GetSize() == aoAttrDims[0])
9484 : {
9485 4 : if (found)
9486 : {
9487 2 : CPLError(CE_Failure, CPLE_AppDefined,
9488 : "Several dimensions of %s have the same "
9489 : "size as attribute %s. Cannot infer which "
9490 : "one to bind to!",
9491 1 : array->GetName().c_str(),
9492 : osBandAttributeName.c_str());
9493 1 : return nullptr;
9494 : }
9495 3 : found = true;
9496 3 : iExtraDimIdx = iter.second;
9497 : }
9498 : }
9499 3 : if (!found)
9500 : {
9501 2 : CPLError(
9502 : CE_Failure, CPLE_AppDefined,
9503 : "No dimension of %s has the same size as attribute %s",
9504 1 : array->GetName().c_str(), osBandAttributeName.c_str());
9505 1 : return nullptr;
9506 : }
9507 : }
9508 :
9509 17 : oItem.osName = oJsonItem.GetString("item_name");
9510 17 : if (oItem.osName.empty())
9511 : {
9512 1 : CPLError(CE_Failure, CPLE_AppDefined,
9513 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9514 1 : return nullptr;
9515 : }
9516 :
9517 32 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9518 :
9519 : // Check correctness of definition
9520 16 : bool bFirstNumericFormatter = true;
9521 16 : std::string osModDefinition;
9522 16 : bool bDefinitionUsesPctForG = false;
9523 79 : for (size_t k = 0; k < osDefinition.size(); ++k)
9524 : {
9525 70 : if (osDefinition[k] == '%')
9526 : {
9527 15 : osModDefinition += osDefinition[k];
9528 15 : if (k + 1 == osDefinition.size())
9529 : {
9530 1 : CPLError(CE_Failure, CPLE_AppDefined,
9531 : "Value of "
9532 : "BAND_METADATA[%d][\"item_value\"] = "
9533 : "%s is invalid at offset %d",
9534 : j, osDefinition.c_str(), int(k));
9535 1 : return nullptr;
9536 : }
9537 14 : ++k;
9538 14 : if (osDefinition[k] == '%')
9539 : {
9540 1 : osModDefinition += osDefinition[k];
9541 1 : continue;
9542 : }
9543 13 : if (!bFirstNumericFormatter)
9544 : {
9545 1 : CPLError(CE_Failure, CPLE_AppDefined,
9546 : "Value of "
9547 : "BAND_METADATA[%d][\"item_value\"] = %s is "
9548 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9549 : "formatters should be specified at most once",
9550 : j, osDefinition.c_str(), int(k));
9551 1 : return nullptr;
9552 : }
9553 12 : bFirstNumericFormatter = false;
9554 19 : for (; k < osDefinition.size(); ++k)
9555 : {
9556 19 : osModDefinition += osDefinition[k];
9557 38 : if (!((osDefinition[k] >= '0' &&
9558 16 : osDefinition[k] <= '9') ||
9559 15 : osDefinition[k] == '.'))
9560 12 : break;
9561 : }
9562 24 : if (k == osDefinition.size() ||
9563 12 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9564 5 : osDefinition[k] != 's'))
9565 : {
9566 1 : CPLError(CE_Failure, CPLE_AppDefined,
9567 : "Value of "
9568 : "BAND_METADATA[%d][\"item_value\"] = "
9569 : "%s is invalid at offset %d: only "
9570 : "%%[x][.y]f|g or %%s formatters are accepted",
9571 : j, osDefinition.c_str(), int(k));
9572 1 : return nullptr;
9573 : }
9574 11 : bDefinitionUsesPctForG =
9575 11 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9576 11 : if (bDefinitionUsesPctForG)
9577 : {
9578 12 : if (poArray &&
9579 12 : poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9580 : {
9581 1 : CPLError(CE_Failure, CPLE_AppDefined,
9582 : "Data type of %s array is not numeric",
9583 1 : poArray->GetName().c_str());
9584 1 : return nullptr;
9585 : }
9586 8 : else if (poAttribute &&
9587 2 : poAttribute->GetDataType().GetClass() !=
9588 6 : GEDTC_NUMERIC)
9589 : {
9590 0 : CPLError(CE_Failure, CPLE_AppDefined,
9591 : "Data type of %s attribute is not numeric",
9592 0 : poAttribute->GetFullName().c_str());
9593 0 : return nullptr;
9594 : }
9595 : }
9596 : }
9597 62 : else if (osDefinition[k] == '$' &&
9598 62 : k + 1 < osDefinition.size() &&
9599 7 : osDefinition[k + 1] == '{')
9600 : {
9601 7 : const auto nPos = osDefinition.find('}', k);
9602 7 : if (nPos == std::string::npos)
9603 : {
9604 1 : CPLError(CE_Failure, CPLE_AppDefined,
9605 : "Value of "
9606 : "BAND_METADATA[%d][\"item_value\"] = "
9607 : "%s is invalid at offset %d",
9608 : j, osDefinition.c_str(), int(k));
9609 3 : return nullptr;
9610 : }
9611 : const auto osAttrName =
9612 6 : osDefinition.substr(k + 2, nPos - (k + 2));
9613 0 : std::shared_ptr<GDALAttribute> poAttr;
9614 6 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9615 : {
9616 4 : poAttr = poArray->GetAttribute(osAttrName);
9617 4 : if (!poAttr)
9618 : {
9619 1 : CPLError(
9620 : CE_Failure, CPLE_AppDefined,
9621 : "Value of "
9622 : "BAND_METADATA[%d][\"item_value\"] = "
9623 : "%s is invalid: %s is not an attribute of %s",
9624 : j, osDefinition.c_str(), osAttrName.c_str(),
9625 1 : poArray->GetName().c_str());
9626 1 : return nullptr;
9627 : }
9628 : }
9629 : else
9630 : {
9631 : poAttr =
9632 2 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9633 2 : if (!poAttr)
9634 : {
9635 1 : CPLError(CE_Failure, CPLE_AppDefined,
9636 : "Value of "
9637 : "BAND_METADATA[%d][\"item_value\"] = "
9638 : "%s is invalid: %s is not an attribute",
9639 : j, osDefinition.c_str(),
9640 : osAttrName.c_str());
9641 1 : return nullptr;
9642 : }
9643 : }
9644 4 : k = nPos;
9645 4 : const char *pszValue = poAttr->ReadAsString();
9646 4 : if (!pszValue)
9647 : {
9648 0 : CPLError(CE_Failure, CPLE_AppDefined,
9649 : "Cannot get value of attribute %s as a "
9650 : "string",
9651 : osAttrName.c_str());
9652 0 : return nullptr;
9653 : }
9654 4 : osModDefinition += pszValue;
9655 : }
9656 : else
9657 : {
9658 48 : osModDefinition += osDefinition[k];
9659 : }
9660 : }
9661 :
9662 9 : if (poArray)
9663 8 : oItem.poArray = std::move(poArray);
9664 : else
9665 1 : oItem.poArray = std::move(poAttribute);
9666 9 : oItem.osDefinition = std::move(osModDefinition);
9667 9 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9668 :
9669 9 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9670 9 : std::move(oItem));
9671 : }
9672 : }
9673 :
9674 514 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9675 : const char *pszBandImageryMetadata =
9676 257 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9677 257 : if (pszBandImageryMetadata)
9678 : {
9679 20 : if (!poRootGroup)
9680 : {
9681 1 : CPLError(CE_Failure, CPLE_AppDefined,
9682 : "Root group should be provided when BAND_IMAGERY_METADATA "
9683 : "is set");
9684 17 : return nullptr;
9685 : }
9686 19 : CPLJSONDocument oDoc;
9687 19 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9688 : {
9689 1 : CPLError(CE_Failure, CPLE_AppDefined,
9690 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9691 1 : return nullptr;
9692 : }
9693 18 : auto oRoot = oDoc.GetRoot();
9694 18 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9695 : {
9696 1 : CPLError(CE_Failure, CPLE_AppDefined,
9697 : "Value of BAND_IMAGERY_METADATA should be an object");
9698 1 : return nullptr;
9699 : }
9700 21 : for (const auto &oJsonItem : oRoot.GetChildren())
9701 : {
9702 38 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9703 20 : oJsonItem.GetName() == "FWHM_UM")
9704 : {
9705 34 : const auto osBandArrayFullname = oJsonItem.GetString("array");
9706 : const auto osBandAttributeName =
9707 34 : oJsonItem.GetString("attribute");
9708 0 : std::shared_ptr<GDALMDArray> poArray;
9709 0 : std::shared_ptr<GDALAttribute> poAttribute;
9710 17 : size_t iExtraDimIdx = 0;
9711 17 : if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9712 : {
9713 2 : CPLError(CE_Failure, CPLE_AppDefined,
9714 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9715 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9716 : "missing",
9717 2 : oJsonItem.GetName().c_str(),
9718 2 : oJsonItem.GetName().c_str());
9719 1 : return nullptr;
9720 : }
9721 25 : else if (!osBandArrayFullname.empty() &&
9722 9 : !osBandAttributeName.empty())
9723 : {
9724 2 : CPLError(CE_Failure, CPLE_AppDefined,
9725 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9726 : "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9727 : "mutually exclusive",
9728 2 : oJsonItem.GetName().c_str(),
9729 2 : oJsonItem.GetName().c_str());
9730 1 : return nullptr;
9731 : }
9732 15 : else if (!osBandArrayFullname.empty())
9733 : {
9734 16 : poArray = poRootGroup->OpenMDArrayFromFullname(
9735 8 : osBandArrayFullname);
9736 8 : if (!poArray)
9737 : {
9738 1 : CPLError(CE_Failure, CPLE_AppDefined,
9739 : "Array %s cannot be found",
9740 : osBandArrayFullname.c_str());
9741 3 : return nullptr;
9742 : }
9743 7 : if (poArray->GetDimensionCount() != 1)
9744 : {
9745 1 : CPLError(CE_Failure, CPLE_AppDefined,
9746 : "Array %s is not a 1D array",
9747 : osBandArrayFullname.c_str());
9748 1 : return nullptr;
9749 : }
9750 : const auto &osAuxArrayDimName =
9751 6 : poArray->GetDimensions()[0]->GetName();
9752 : auto oIter =
9753 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9754 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9755 : {
9756 1 : CPLError(CE_Failure, CPLE_AppDefined,
9757 : "Dimension \"%s\" of array \"%s\" is not a "
9758 : "non-X/Y dimension of array \"%s\"",
9759 : osAuxArrayDimName.c_str(),
9760 : osBandArrayFullname.c_str(),
9761 1 : array->GetName().c_str());
9762 1 : return nullptr;
9763 : }
9764 5 : iExtraDimIdx = oIter->second;
9765 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9766 : }
9767 : else
9768 : {
9769 : poAttribute =
9770 7 : !osBandAttributeName.empty() &&
9771 7 : osBandAttributeName[0] == '/'
9772 16 : ? poRootGroup->OpenAttributeFromFullname(
9773 : osBandAttributeName)
9774 7 : : array->GetAttribute(osBandAttributeName);
9775 7 : if (!poAttribute)
9776 : {
9777 2 : CPLError(CE_Failure, CPLE_AppDefined,
9778 : "Attribute %s cannot be found",
9779 : osBandAttributeName.c_str());
9780 6 : return nullptr;
9781 : }
9782 5 : const auto aoAttrDims = poAttribute->GetDimensionsSize();
9783 5 : if (aoAttrDims.size() != 1)
9784 : {
9785 2 : CPLError(CE_Failure, CPLE_AppDefined,
9786 : "Attribute %s is not a 1D array",
9787 : osBandAttributeName.c_str());
9788 2 : return nullptr;
9789 : }
9790 3 : bool found = false;
9791 6 : for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9792 : {
9793 4 : if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9794 4 : ->GetSize() == aoAttrDims[0])
9795 : {
9796 3 : if (found)
9797 : {
9798 2 : CPLError(CE_Failure, CPLE_AppDefined,
9799 : "Several dimensions of %s have the "
9800 : "same size as attribute %s. Cannot "
9801 : "infer which one to bind to!",
9802 1 : array->GetName().c_str(),
9803 : osBandAttributeName.c_str());
9804 1 : return nullptr;
9805 : }
9806 2 : found = true;
9807 2 : iExtraDimIdx = iter.second;
9808 : }
9809 : }
9810 2 : if (!found)
9811 : {
9812 2 : CPLError(CE_Failure, CPLE_AppDefined,
9813 : "No dimension of %s has the same size as "
9814 : "attribute %s",
9815 1 : array->GetName().c_str(),
9816 : osBandAttributeName.c_str());
9817 1 : return nullptr;
9818 : }
9819 : }
9820 :
9821 12 : std::string osUnit = oJsonItem.GetString("unit", "um");
9822 6 : if (STARTS_WITH(osUnit.c_str(), "${"))
9823 : {
9824 4 : if (osUnit.back() != '}')
9825 : {
9826 2 : CPLError(CE_Failure, CPLE_AppDefined,
9827 : "Value of "
9828 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9829 : "%s is invalid",
9830 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9831 2 : return nullptr;
9832 : }
9833 3 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9834 0 : std::shared_ptr<GDALAttribute> poAttr;
9835 3 : if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9836 : {
9837 2 : poAttr = poArray->GetAttribute(osAttrName);
9838 2 : if (!poAttr)
9839 : {
9840 2 : CPLError(
9841 : CE_Failure, CPLE_AppDefined,
9842 : "Value of "
9843 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9844 : "%s is invalid: %s is not an attribute of %s",
9845 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9846 : osAttrName.c_str(),
9847 : osBandArrayFullname.c_str());
9848 1 : return nullptr;
9849 : }
9850 : }
9851 : else
9852 : {
9853 : poAttr =
9854 1 : poRootGroup->OpenAttributeFromFullname(osAttrName);
9855 1 : if (!poAttr)
9856 : {
9857 0 : CPLError(
9858 : CE_Failure, CPLE_AppDefined,
9859 : "Value of "
9860 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9861 : "%s is invalid: %s is not an attribute",
9862 0 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9863 : osAttrName.c_str());
9864 0 : return nullptr;
9865 : }
9866 : }
9867 :
9868 2 : const char *pszValue = poAttr->ReadAsString();
9869 2 : if (!pszValue)
9870 : {
9871 0 : CPLError(CE_Failure, CPLE_AppDefined,
9872 : "Cannot get value of attribute %s of %s as a "
9873 : "string",
9874 : osAttrName.c_str(),
9875 : osBandArrayFullname.c_str());
9876 0 : return nullptr;
9877 : }
9878 2 : osUnit = pszValue;
9879 : }
9880 4 : double dfConvToUM = 1.0;
9881 10 : if (osUnit == "nm" || osUnit == "nanometre" ||
9882 13 : osUnit == "nanometres" || osUnit == "nanometer" ||
9883 3 : osUnit == "nanometers")
9884 : {
9885 1 : dfConvToUM = 1e-3;
9886 : }
9887 5 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9888 2 : osUnit == "micrometres" || osUnit == "micrometer" ||
9889 1 : osUnit == "micrometers"))
9890 : {
9891 2 : CPLError(CE_Failure, CPLE_AppDefined,
9892 : "Unhandled value for "
9893 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9894 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9895 1 : return nullptr;
9896 : }
9897 :
9898 3 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9899 :
9900 3 : std::shared_ptr<GDALAbstractMDArray> abstractArray;
9901 3 : if (poArray)
9902 2 : abstractArray = std::move(poArray);
9903 : else
9904 1 : abstractArray = std::move(poAttribute);
9905 3 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9906 : {
9907 2 : item.poCentralWavelengthArray = std::move(abstractArray);
9908 2 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9909 : }
9910 : else
9911 : {
9912 1 : item.poFWHMArray = std::move(abstractArray);
9913 1 : item.dfFWHMToMicrometer = dfConvToUM;
9914 : }
9915 : }
9916 : else
9917 : {
9918 1 : CPLError(CE_Warning, CPLE_AppDefined,
9919 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9920 2 : oJsonItem.GetName().c_str());
9921 : }
9922 : }
9923 : }
9924 :
9925 480 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9926 :
9927 240 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9928 :
9929 240 : poDS->nRasterYSize =
9930 240 : nDimCount < 2 ? 1
9931 224 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9932 224 : dims[iYDim]->GetSize()));
9933 480 : poDS->nRasterXSize = static_cast<int>(
9934 240 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9935 :
9936 480 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9937 480 : std::vector<GUInt64> anStackIters(nDimCount);
9938 480 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9939 754 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9940 : {
9941 514 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9942 : {
9943 50 : anMapNewToOld[j] = i;
9944 50 : j++;
9945 : }
9946 : }
9947 :
9948 240 : poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
9949 :
9950 480 : const auto attrs(array->GetAttributes());
9951 381 : for (const auto &attr : attrs)
9952 : {
9953 141 : if (attr->GetName() != "COLOR_INTERPRETATION")
9954 : {
9955 260 : auto stringArray = attr->ReadAsStringArray();
9956 260 : std::string val;
9957 130 : if (stringArray.size() > 1)
9958 : {
9959 55 : val += '{';
9960 : }
9961 537 : for (int i = 0; i < stringArray.size(); ++i)
9962 : {
9963 407 : if (i > 0)
9964 277 : val += ',';
9965 407 : val += stringArray[i];
9966 : }
9967 130 : if (stringArray.size() > 1)
9968 : {
9969 55 : val += '}';
9970 : }
9971 130 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9972 : }
9973 : }
9974 :
9975 240 : const char *pszDelay = CSLFetchNameValueDef(
9976 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9977 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9978 : const double dfDelay =
9979 240 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9980 240 : const auto nStartTime = time(nullptr);
9981 240 : bool bHasWarned = false;
9982 : // Instantiate bands by iterating over non-XY variables
9983 240 : size_t iDim = 0;
9984 240 : int nCurBand = 1;
9985 368 : lbl_next_depth:
9986 368 : if (iDim < nNewDimCount)
9987 : {
9988 52 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9989 52 : anOtherDimCoord[iDim] = 0;
9990 : while (true)
9991 : {
9992 128 : ++iDim;
9993 128 : goto lbl_next_depth;
9994 128 : lbl_return_to_caller:
9995 128 : --iDim;
9996 128 : --anStackIters[iDim];
9997 128 : if (anStackIters[iDim] == 0)
9998 52 : break;
9999 76 : ++anOtherDimCoord[iDim];
10000 : }
10001 : }
10002 : else
10003 : {
10004 632 : poDS->SetBand(nCurBand,
10005 : new GDALRasterBandFromArray(
10006 316 : poDS.get(), anOtherDimCoord,
10007 : aoBandParameterMetadataItems, aoBandImageryMetadata,
10008 316 : dfDelay, nStartTime, bHasWarned));
10009 316 : ++nCurBand;
10010 : }
10011 368 : if (iDim > 0)
10012 128 : goto lbl_return_to_caller;
10013 :
10014 240 : if (!array->GetFilename().empty())
10015 : {
10016 205 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
10017 : std::string osDerivedDatasetName(
10018 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10019 410 : int(iYDim), array->GetFullName().c_str()));
10020 205 : if (!array->GetContext().empty())
10021 : {
10022 2 : osDerivedDatasetName += " with context ";
10023 2 : osDerivedDatasetName += array->GetContext();
10024 : }
10025 205 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10026 205 : poDS->TryLoadXML();
10027 :
10028 2 : for (const auto &[pszKey, pszValue] :
10029 : cpl::IterateNameValue(static_cast<CSLConstList>(
10030 207 : poDS->GDALPamDataset::GetMetadata())))
10031 : {
10032 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10033 : }
10034 : }
10035 :
10036 240 : return poDS.release();
10037 : }
10038 :
10039 : /************************************************************************/
10040 : /* AsClassicDataset() */
10041 : /************************************************************************/
10042 :
10043 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
10044 : *
10045 : * In the case of > 2D arrays, additional dimensions will be represented as
10046 : * raster bands.
10047 : *
10048 : * The "reverse" method is GDALRasterBand::AsMDArray().
10049 : *
10050 : * This is the same as the C function GDALMDArrayAsClassicDataset().
10051 : *
10052 : * @param iXDim Index of the dimension that will be used as the X/width axis.
10053 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
10054 : * Ignored if the dimension count is 1.
10055 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10056 : * and BAND_IMAGERY_METADATA option.
10057 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10058 : * nullptr. Current supported options are:
10059 : * <ul>
10060 : * <li>BAND_METADATA: JSON serialized array defining which
10061 : * arrays of the poRootGroup, indexed by non-X and Y
10062 : * dimensions, should be mapped as band metadata items.
10063 : * Each array item should be an object with the
10064 : * following members:
10065 : * - "array": full name of a band parameter array.
10066 : * Such array must be a one
10067 : * dimensional array, and its dimension must be one of
10068 : * the dimensions of the array on which the method is
10069 : * called (excluding the X and Y dimensions).
10070 : * - "attribute": name relative to *this array or full
10071 : * name of a single dimension numeric array whose size
10072 : * must be one of the dimensions of *this array
10073 : * (excluding the X and Y dimensions).
10074 : * "array" and "attribute" are mutually exclusive.
10075 : * - "item_name": band metadata item name
10076 : * - "item_value": (optional) String, where "%[x][.y]f",
10077 : * "%[x][.y]g" or "%s" printf-like formatting can be
10078 : * used to format the corresponding value of the
10079 : * parameter array. The percentage character should be
10080 : * repeated: "%%"
10081 : * "${attribute_name}" can also be used to include the
10082 : * value of an attribute for "array" when set and if
10083 : * not starting with '/'. Otherwise if starting with
10084 : * '/', it is the full path to the attribute.
10085 : *
10086 : * If "item_value" is not provided, a default formatting
10087 : * of the value will be applied.
10088 : *
10089 : * Example:
10090 : * [
10091 : * {
10092 : * "array": "/sensor_band_parameters/wavelengths",
10093 : * "item_name": "WAVELENGTH",
10094 : * "item_value": "%.1f ${units}"
10095 : * },
10096 : * {
10097 : * "array": "/sensor_band_parameters/fwhm",
10098 : * "item_name": "FWHM"
10099 : * },
10100 : * {
10101 : * "array": "/sensor_band_parameters/fwhm",
10102 : * "item_name": "FWHM_UNIT",
10103 : * "item_value": "${units}"
10104 : * }
10105 : * ]
10106 : *
10107 : * Example for Planet Labs Tanager radiance products:
10108 : * [
10109 : * {
10110 : * "attribute": "center_wavelengths",
10111 : * "item_name": "WAVELENGTH",
10112 : * "item_value": "%.1f ${center_wavelengths_units}"
10113 : * }
10114 : * ]
10115 : *
10116 : * </li>
10117 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10118 : * JSON serialized object defining which arrays of the
10119 : * poRootGroup, indexed by non-X and Y dimensions,
10120 : * should be mapped as band metadata items in the
10121 : * band IMAGERY domain.
10122 : * The object currently accepts 2 members:
10123 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10124 : * micrometers.
10125 : * - "FWHM_UM": Full-width half-maximum
10126 : * in micrometers.
10127 : * The value of each member should be an object with the
10128 : * following members:
10129 : * - "array": full name of a band parameter array.
10130 : * Such array must be a one dimensional array, and its
10131 : * dimension must be one of the dimensions of the
10132 : * array on which the method is called
10133 : * (excluding the X and Y dimensions).
10134 : * - "attribute": name relative to *this array or full
10135 : * name of a single dimension numeric array whose size
10136 : * must be one of the dimensions of *this array
10137 : * (excluding the X and Y dimensions).
10138 : * "array" and "attribute" are mutually exclusive,
10139 : * and one of them is required.
10140 : * - "unit": (optional) unit of the values pointed in
10141 : * the above array.
10142 : * Can be a literal string or a string of the form
10143 : * "${attribute_name}" to point to an attribute for
10144 : * "array" when set and if no starting
10145 : * with '/'. Otherwise if starting with '/', it is
10146 : * the full path to the attribute.
10147 : * Accepted values are "um", "micrometer"
10148 : * (with UK vs US spelling, singular or plural), "nm",
10149 : * "nanometer" (with UK vs US spelling, singular or
10150 : * plural)
10151 : * If not provided, micrometer is assumed.
10152 : *
10153 : * Example for EMIT datasets:
10154 : * {
10155 : * "CENTRAL_WAVELENGTH_UM": {
10156 : * "array": "/sensor_band_parameters/wavelengths",
10157 : * "unit": "${units}"
10158 : * },
10159 : * "FWHM_UM": {
10160 : * "array": "/sensor_band_parameters/fwhm",
10161 : * "unit": "${units}"
10162 : * }
10163 : * }
10164 : *
10165 : * Example for Planet Labs Tanager radiance products:
10166 : * {
10167 : * "CENTRAL_WAVELENGTH_UM": {
10168 : * "attribute": "center_wavelengths",
10169 : * "unit": "${center_wavelengths_units}"
10170 : * },
10171 : * "FWHM_UM": {
10172 : * "attribute": "fwhm",
10173 : * "unit": "${fwhm_units}"
10174 : * }
10175 : * }
10176 : *
10177 : * </li>
10178 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10179 : * seconds allowed to set the DIM_{dimname}_VALUE band
10180 : * metadata items from the indexing variable of the
10181 : * dimensions.
10182 : * Default value is 5. 'unlimited' can be used to mean
10183 : * unlimited delay. Can also be defined globally with
10184 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10185 : * option.</li>
10186 : * </ul>
10187 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10188 : */
10189 : GDALDataset *
10190 292 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10191 : const std::shared_ptr<GDALGroup> &poRootGroup,
10192 : CSLConstList papszOptions) const
10193 : {
10194 584 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10195 292 : if (!self)
10196 : {
10197 0 : CPLError(CE_Failure, CPLE_AppDefined,
10198 : "Driver implementation issue: m_pSelf not set !");
10199 0 : return nullptr;
10200 : }
10201 292 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10202 292 : papszOptions);
10203 : }
10204 :
10205 : /************************************************************************/
10206 : /* GetStatistics() */
10207 : /************************************************************************/
10208 :
10209 : /**
10210 : * \brief Fetch statistics.
10211 : *
10212 : * Returns the minimum, maximum, mean and standard deviation of all
10213 : * pixel values in this array.
10214 : *
10215 : * If bForce is FALSE results will only be returned if it can be done
10216 : * quickly (i.e. without scanning the data). If bForce is FALSE and
10217 : * results cannot be returned efficiently, the method will return CE_Warning
10218 : * but no warning will have been issued. This is a non-standard use of
10219 : * the CE_Warning return value to indicate "nothing done".
10220 : *
10221 : * When cached statistics are not available, and bForce is TRUE,
10222 : * ComputeStatistics() is called.
10223 : *
10224 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10225 : * will generally cache statistics in the .aux.xml file allowing fast fetch
10226 : * after the first request.
10227 : *
10228 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10229 : *
10230 : * This method is the same as the C function GDALMDArrayGetStatistics().
10231 : *
10232 : * @param bApproxOK Currently ignored. In the future, should be set to true
10233 : * if statistics on the whole array are wished, or to false if a subset of it
10234 : * may be used.
10235 : *
10236 : * @param bForce If false statistics will only be returned if it can
10237 : * be done without rescanning the image.
10238 : *
10239 : * @param pdfMin Location into which to load image minimum (may be NULL).
10240 : *
10241 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10242 : *
10243 : * @param pdfMean Location into which to load image mean (may be NULL).
10244 : *
10245 : * @param pdfStdDev Location into which to load image standard deviation
10246 : * (may be NULL).
10247 : *
10248 : * @param pnValidCount Number of samples whose value is different from the
10249 : * nodata value. (may be NULL)
10250 : *
10251 : * @param pfnProgress a function to call to report progress, or NULL.
10252 : *
10253 : * @param pProgressData application data to pass to the progress function.
10254 : *
10255 : * @return CE_None on success, CE_Warning if no values returned,
10256 : * CE_Failure if an error occurs.
10257 : *
10258 : * @since GDAL 3.2
10259 : */
10260 :
10261 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10262 : double *pdfMax, double *pdfMean,
10263 : double *pdfStdDev, GUInt64 *pnValidCount,
10264 : GDALProgressFunc pfnProgress,
10265 : void *pProgressData)
10266 : {
10267 10 : if (!bForce)
10268 1 : return CE_Warning;
10269 :
10270 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10271 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
10272 9 : ? CE_None
10273 9 : : CE_Failure;
10274 : }
10275 :
10276 : /************************************************************************/
10277 : /* ComputeStatistics() */
10278 : /************************************************************************/
10279 :
10280 : /**
10281 : * \brief Compute statistics.
10282 : *
10283 : * Returns the minimum, maximum, mean and standard deviation of all
10284 : * pixel values in this array.
10285 : *
10286 : * Pixels taken into account in statistics are those whose mask value
10287 : * (as determined by GetMask()) is non-zero.
10288 : *
10289 : * Once computed, the statistics will generally be "set" back on the
10290 : * owing dataset.
10291 : *
10292 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10293 : *
10294 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
10295 : * and GDALMDArrayComputeStatisticsEx().
10296 : *
10297 : * @param bApproxOK Currently ignored. In the future, should be set to true
10298 : * if statistics on the whole array are wished, or to false if a subset of it
10299 : * may be used.
10300 : *
10301 : * @param pdfMin Location into which to load image minimum (may be NULL).
10302 : *
10303 : * @param pdfMax Location into which to load image maximum (may be NULL).-
10304 : *
10305 : * @param pdfMean Location into which to load image mean (may be NULL).
10306 : *
10307 : * @param pdfStdDev Location into which to load image standard deviation
10308 : * (may be NULL).
10309 : *
10310 : * @param pnValidCount Number of samples whose value is different from the
10311 : * nodata value. (may be NULL)
10312 : *
10313 : * @param pfnProgress a function to call to report progress, or NULL.
10314 : *
10315 : * @param pProgressData application data to pass to the progress function.
10316 : *
10317 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10318 : * Options are driver specific. For now the netCDF and Zarr
10319 : * drivers recognize UPDATE_METADATA=YES, whose effect is
10320 : * to add or update the actual_range attribute with the
10321 : * computed min/max, only if done on the full array, in non
10322 : * approximate mode, and the dataset is opened in update
10323 : * mode.
10324 : *
10325 : * @return true on success
10326 : *
10327 : * @since GDAL 3.2
10328 : */
10329 :
10330 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10331 : double *pdfMax, double *pdfMean,
10332 : double *pdfStdDev, GUInt64 *pnValidCount,
10333 : GDALProgressFunc pfnProgress,
10334 : void *pProgressData,
10335 : CSLConstList papszOptions)
10336 : {
10337 : struct StatsPerChunkType
10338 : {
10339 : const GDALMDArray *array = nullptr;
10340 : std::shared_ptr<GDALMDArray> poMask{};
10341 : double dfMin = cpl::NumericLimits<double>::max();
10342 : double dfMax = -cpl::NumericLimits<double>::max();
10343 : double dfMean = 0.0;
10344 : double dfM2 = 0.0;
10345 : GUInt64 nValidCount = 0;
10346 : std::vector<GByte> abyData{};
10347 : std::vector<double> adfData{};
10348 : std::vector<GByte> abyMaskData{};
10349 : GDALProgressFunc pfnProgress = nullptr;
10350 : void *pProgressData = nullptr;
10351 : };
10352 :
10353 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10354 : const GUInt64 *chunkArrayStartIdx,
10355 : const size_t *chunkCount, GUInt64 iCurChunk,
10356 : GUInt64 nChunkCount, void *pUserData)
10357 : {
10358 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10359 13 : const GDALMDArray *array = data->array;
10360 13 : const GDALMDArray *poMask = data->poMask.get();
10361 13 : const size_t nDims = array->GetDimensionCount();
10362 13 : size_t nVals = 1;
10363 34 : for (size_t i = 0; i < nDims; i++)
10364 21 : nVals *= chunkCount[i];
10365 :
10366 : // Get mask
10367 13 : data->abyMaskData.resize(nVals);
10368 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10369 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10370 : {
10371 0 : return false;
10372 : }
10373 :
10374 : // Get data
10375 13 : const auto &oType = array->GetDataType();
10376 13 : if (oType.GetNumericDataType() == GDT_Float64)
10377 : {
10378 6 : data->adfData.resize(nVals);
10379 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10380 6 : oType, &data->adfData[0]))
10381 : {
10382 0 : return false;
10383 : }
10384 : }
10385 : else
10386 : {
10387 7 : data->abyData.resize(nVals * oType.GetSize());
10388 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10389 7 : oType, &data->abyData[0]))
10390 : {
10391 0 : return false;
10392 : }
10393 7 : data->adfData.resize(nVals);
10394 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10395 7 : static_cast<int>(oType.GetSize()),
10396 7 : &data->adfData[0], GDT_Float64,
10397 : static_cast<int>(sizeof(double)),
10398 : static_cast<GPtrDiff_t>(nVals));
10399 : }
10400 912 : for (size_t i = 0; i < nVals; i++)
10401 : {
10402 899 : if (data->abyMaskData[i])
10403 : {
10404 894 : const double dfValue = data->adfData[i];
10405 894 : data->dfMin = std::min(data->dfMin, dfValue);
10406 894 : data->dfMax = std::max(data->dfMax, dfValue);
10407 894 : data->nValidCount++;
10408 894 : const double dfDelta = dfValue - data->dfMean;
10409 894 : data->dfMean += dfDelta / data->nValidCount;
10410 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10411 : }
10412 : }
10413 13 : if (data->pfnProgress &&
10414 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10415 : "", data->pProgressData))
10416 : {
10417 0 : return false;
10418 : }
10419 13 : return true;
10420 : };
10421 :
10422 13 : const auto &oType = GetDataType();
10423 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10424 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10425 : {
10426 0 : CPLError(
10427 : CE_Failure, CPLE_NotSupported,
10428 : "Statistics can only be computed on non-complex numeric data type");
10429 0 : return false;
10430 : }
10431 :
10432 13 : const size_t nDims = GetDimensionCount();
10433 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10434 26 : std::vector<GUInt64> count(nDims);
10435 13 : const auto &poDims = GetDimensions();
10436 34 : for (size_t i = 0; i < nDims; i++)
10437 : {
10438 21 : count[i] = poDims[i]->GetSize();
10439 : }
10440 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10441 : const size_t nMaxChunkSize =
10442 : pszSwathSize
10443 13 : ? static_cast<size_t>(
10444 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10445 0 : CPLAtoGIntBig(pszSwathSize)))
10446 : : static_cast<size_t>(
10447 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10448 13 : GDALGetCacheMax64() / 4));
10449 26 : StatsPerChunkType sData;
10450 13 : sData.array = this;
10451 13 : sData.poMask = GetMask(nullptr);
10452 13 : if (sData.poMask == nullptr)
10453 : {
10454 0 : return false;
10455 : }
10456 13 : sData.pfnProgress = pfnProgress;
10457 13 : sData.pProgressData = pProgressData;
10458 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10459 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10460 13 : PerChunkFunc, &sData))
10461 : {
10462 0 : return false;
10463 : }
10464 :
10465 13 : if (pdfMin)
10466 13 : *pdfMin = sData.dfMin;
10467 :
10468 13 : if (pdfMax)
10469 13 : *pdfMax = sData.dfMax;
10470 :
10471 13 : if (pdfMean)
10472 11 : *pdfMean = sData.dfMean;
10473 :
10474 : const double dfStdDev =
10475 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10476 13 : if (pdfStdDev)
10477 11 : *pdfStdDev = dfStdDev;
10478 :
10479 13 : if (pnValidCount)
10480 11 : *pnValidCount = sData.nValidCount;
10481 :
10482 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10483 13 : sData.nValidCount, papszOptions);
10484 :
10485 13 : return true;
10486 : }
10487 :
10488 : /************************************************************************/
10489 : /* SetStatistics() */
10490 : /************************************************************************/
10491 : //! @cond Doxygen_Suppress
10492 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10493 : double /* dfMax */, double /* dfMean */,
10494 : double /* dfStdDev */,
10495 : GUInt64 /* nValidCount */,
10496 : CSLConstList /* papszOptions */)
10497 : {
10498 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10499 5 : return false;
10500 : }
10501 :
10502 : //! @endcond
10503 :
10504 : /************************************************************************/
10505 : /* ClearStatistics() */
10506 : /************************************************************************/
10507 :
10508 : /**
10509 : * \brief Clear statistics.
10510 : *
10511 : * @since GDAL 3.4
10512 : */
10513 0 : void GDALMDArray::ClearStatistics()
10514 : {
10515 0 : }
10516 :
10517 : /************************************************************************/
10518 : /* GetCoordinateVariables() */
10519 : /************************************************************************/
10520 :
10521 : /**
10522 : * \brief Return coordinate variables.
10523 : *
10524 : * Coordinate variables are an alternate way of indexing an array that can
10525 : * be sometimes used. For example, an array collected through remote sensing
10526 : * might be indexed by (scanline, pixel). But there can be
10527 : * a longitude and latitude arrays alongside that are also both indexed by
10528 : * (scanline, pixel), and are referenced from operational arrays for
10529 : * reprojection purposes.
10530 : *
10531 : * For netCDF, this will return the arrays referenced by the "coordinates"
10532 : * attribute.
10533 : *
10534 : * This method is the same as the C function
10535 : * GDALMDArrayGetCoordinateVariables().
10536 : *
10537 : * @return a vector of arrays
10538 : *
10539 : * @since GDAL 3.4
10540 : */
10541 :
10542 : std::vector<std::shared_ptr<GDALMDArray>>
10543 13 : GDALMDArray::GetCoordinateVariables() const
10544 : {
10545 13 : return {};
10546 : }
10547 :
10548 : /************************************************************************/
10549 : /* ~GDALExtendedDataType() */
10550 : /************************************************************************/
10551 :
10552 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10553 :
10554 : /************************************************************************/
10555 : /* GDALExtendedDataType() */
10556 : /************************************************************************/
10557 :
10558 14193 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10559 14193 : GDALExtendedDataTypeSubType eSubType)
10560 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10561 14193 : m_nMaxStringLength(nMaxStringLength)
10562 : {
10563 14193 : }
10564 :
10565 : /************************************************************************/
10566 : /* GDALExtendedDataType() */
10567 : /************************************************************************/
10568 :
10569 63887 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10570 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10571 63887 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10572 : {
10573 63887 : }
10574 :
10575 : /************************************************************************/
10576 : /* GDALExtendedDataType() */
10577 : /************************************************************************/
10578 :
10579 63 : GDALExtendedDataType::GDALExtendedDataType(
10580 : const std::string &osName, GDALDataType eBaseType,
10581 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10582 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10583 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10584 : {
10585 63 : }
10586 :
10587 : /************************************************************************/
10588 : /* GDALExtendedDataType() */
10589 : /************************************************************************/
10590 :
10591 915 : GDALExtendedDataType::GDALExtendedDataType(
10592 : const std::string &osName, size_t nTotalSize,
10593 915 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10594 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10595 915 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10596 : {
10597 915 : }
10598 :
10599 : /************************************************************************/
10600 : /* GDALExtendedDataType() */
10601 : /************************************************************************/
10602 :
10603 : /** Move constructor. */
10604 : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10605 :
10606 : /************************************************************************/
10607 : /* GDALExtendedDataType() */
10608 : /************************************************************************/
10609 :
10610 : /** Copy constructor. */
10611 16948 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10612 33896 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10613 16948 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10614 16948 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10615 16948 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10616 : {
10617 16948 : if (m_eClass == GEDTC_COMPOUND)
10618 : {
10619 481 : for (const auto &elt : other.m_aoComponents)
10620 : {
10621 318 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10622 : }
10623 : }
10624 16948 : }
10625 :
10626 : /************************************************************************/
10627 : /* operator= () */
10628 : /************************************************************************/
10629 :
10630 : /** Copy assignment. */
10631 : GDALExtendedDataType &
10632 1072 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10633 : {
10634 1072 : if (this != &other)
10635 : {
10636 1072 : m_osName = other.m_osName;
10637 1072 : m_eClass = other.m_eClass;
10638 1072 : m_eSubType = other.m_eSubType;
10639 1072 : m_eNumericDT = other.m_eNumericDT;
10640 1072 : m_nSize = other.m_nSize;
10641 1072 : m_nMaxStringLength = other.m_nMaxStringLength;
10642 1072 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10643 1072 : m_aoComponents.clear();
10644 1072 : if (m_eClass == GEDTC_COMPOUND)
10645 : {
10646 0 : for (const auto &elt : other.m_aoComponents)
10647 : {
10648 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10649 : }
10650 : }
10651 : }
10652 1072 : return *this;
10653 : }
10654 :
10655 : /************************************************************************/
10656 : /* operator= () */
10657 : /************************************************************************/
10658 :
10659 : /** Move assignment. */
10660 : GDALExtendedDataType &
10661 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10662 :
10663 : /************************************************************************/
10664 : /* Create() */
10665 : /************************************************************************/
10666 :
10667 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10668 : *
10669 : * This is the same as the C function GDALExtendedDataTypeCreate()
10670 : *
10671 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10672 : * GDT_TypeCount
10673 : */
10674 63880 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10675 : {
10676 63880 : return GDALExtendedDataType(eType);
10677 : }
10678 :
10679 : /************************************************************************/
10680 : /* Create() */
10681 : /************************************************************************/
10682 :
10683 : /** Return a new GDALExtendedDataType from a raster attribute table.
10684 : *
10685 : * @param osName Type name
10686 : * @param eBaseType Base integer data type.
10687 : * @param poRAT Raster attribute table. Must not be NULL.
10688 : * @since 3.12
10689 : */
10690 : GDALExtendedDataType
10691 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10692 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10693 : {
10694 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10695 : }
10696 :
10697 : /************************************************************************/
10698 : /* Create() */
10699 : /************************************************************************/
10700 :
10701 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10702 : *
10703 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10704 : *
10705 : * @param osName Type name.
10706 : * @param nTotalSize Total size of the type in bytes.
10707 : * Should be large enough to store all components.
10708 : * @param components Components of the compound type.
10709 : */
10710 922 : GDALExtendedDataType GDALExtendedDataType::Create(
10711 : const std::string &osName, size_t nTotalSize,
10712 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10713 : {
10714 922 : size_t nLastOffset = 0;
10715 : // Some arbitrary threshold to avoid potential integer overflows
10716 922 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10717 : {
10718 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10719 2 : return GDALExtendedDataType(GDT_Unknown);
10720 : }
10721 4306 : for (const auto &comp : components)
10722 : {
10723 : // Check alignment too ?
10724 3387 : if (comp->GetOffset() < nLastOffset)
10725 : {
10726 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10727 1 : return GDALExtendedDataType(GDT_Unknown);
10728 : }
10729 3386 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10730 : }
10731 919 : if (nTotalSize < nLastOffset)
10732 : {
10733 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10734 1 : return GDALExtendedDataType(GDT_Unknown);
10735 : }
10736 918 : if (nTotalSize == 0 || components.empty())
10737 : {
10738 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10739 3 : return GDALExtendedDataType(GDT_Unknown);
10740 : }
10741 915 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10742 : }
10743 :
10744 : /************************************************************************/
10745 : /* Create() */
10746 : /************************************************************************/
10747 :
10748 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10749 : *
10750 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10751 : *
10752 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10753 : * unknown/unlimited
10754 : * @param eSubType Subtype.
10755 : */
10756 : GDALExtendedDataType
10757 14193 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10758 : GDALExtendedDataTypeSubType eSubType)
10759 : {
10760 14193 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10761 : }
10762 :
10763 : /************************************************************************/
10764 : /* operator==() */
10765 : /************************************************************************/
10766 :
10767 : /** Equality operator.
10768 : *
10769 : * This is the same as the C function GDALExtendedDataTypeEquals().
10770 : */
10771 3138 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10772 : {
10773 3111 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10774 6249 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10775 : {
10776 324 : return false;
10777 : }
10778 2814 : if (m_eClass == GEDTC_NUMERIC)
10779 : {
10780 935 : return m_eNumericDT == other.m_eNumericDT;
10781 : }
10782 1879 : if (m_eClass == GEDTC_STRING)
10783 : {
10784 1632 : return true;
10785 : }
10786 247 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10787 247 : if (m_aoComponents.size() != other.m_aoComponents.size())
10788 : {
10789 2 : return false;
10790 : }
10791 1232 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10792 : {
10793 987 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10794 : {
10795 0 : return false;
10796 : }
10797 : }
10798 245 : return true;
10799 : }
10800 :
10801 : /************************************************************************/
10802 : /* CanConvertTo() */
10803 : /************************************************************************/
10804 :
10805 : /** Return whether this data type can be converted to the other one.
10806 : *
10807 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10808 : *
10809 : * @param other Target data type for the conversion being considered.
10810 : */
10811 10531 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10812 : {
10813 10531 : if (m_eClass == GEDTC_NUMERIC)
10814 : {
10815 7189 : if (m_eNumericDT == GDT_Unknown)
10816 0 : return false;
10817 7189 : if (other.m_eClass == GEDTC_NUMERIC &&
10818 6980 : other.m_eNumericDT == GDT_Unknown)
10819 0 : return false;
10820 7398 : return other.m_eClass == GEDTC_NUMERIC ||
10821 7398 : other.m_eClass == GEDTC_STRING;
10822 : }
10823 3342 : if (m_eClass == GEDTC_STRING)
10824 : {
10825 3121 : return other.m_eClass == m_eClass;
10826 : }
10827 221 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10828 221 : if (other.m_eClass != GEDTC_COMPOUND)
10829 0 : return false;
10830 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10831 442 : srcComponents;
10832 1052 : for (const auto &srcComp : m_aoComponents)
10833 : {
10834 831 : srcComponents[srcComp->GetName()] = &srcComp;
10835 : }
10836 583 : for (const auto &dstComp : other.m_aoComponents)
10837 : {
10838 363 : auto oIter = srcComponents.find(dstComp->GetName());
10839 363 : if (oIter == srcComponents.end())
10840 1 : return false;
10841 362 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10842 0 : return false;
10843 : }
10844 220 : return true;
10845 : }
10846 :
10847 : /************************************************************************/
10848 : /* NeedsFreeDynamicMemory() */
10849 : /************************************************************************/
10850 :
10851 : /** Return whether the data type holds dynamically allocated memory, that
10852 : * needs to be freed with FreeDynamicMemory().
10853 : *
10854 : */
10855 4011 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10856 : {
10857 4011 : switch (m_eClass)
10858 : {
10859 1060 : case GEDTC_STRING:
10860 1060 : return true;
10861 :
10862 2828 : case GEDTC_NUMERIC:
10863 2828 : return false;
10864 :
10865 123 : case GEDTC_COMPOUND:
10866 : {
10867 244 : for (const auto &comp : m_aoComponents)
10868 : {
10869 222 : if (comp->GetType().NeedsFreeDynamicMemory())
10870 101 : return true;
10871 : }
10872 : }
10873 : }
10874 22 : return false;
10875 : }
10876 :
10877 : /************************************************************************/
10878 : /* FreeDynamicMemory() */
10879 : /************************************************************************/
10880 :
10881 : /** Release the dynamic memory (strings typically) from a raw value.
10882 : *
10883 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10884 : *
10885 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10886 : */
10887 4163 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10888 : {
10889 4163 : switch (m_eClass)
10890 : {
10891 2998 : case GEDTC_STRING:
10892 : {
10893 : char *pszStr;
10894 2998 : memcpy(&pszStr, pBuffer, sizeof(char *));
10895 2998 : if (pszStr)
10896 : {
10897 2400 : VSIFree(pszStr);
10898 : }
10899 2998 : break;
10900 : }
10901 :
10902 976 : case GEDTC_NUMERIC:
10903 : {
10904 976 : break;
10905 : }
10906 :
10907 189 : case GEDTC_COMPOUND:
10908 : {
10909 189 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10910 999 : for (const auto &comp : m_aoComponents)
10911 : {
10912 1620 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10913 810 : comp->GetOffset());
10914 : }
10915 189 : break;
10916 : }
10917 : }
10918 4163 : }
10919 :
10920 : /************************************************************************/
10921 : /* ~GDALEDTComponent() */
10922 : /************************************************************************/
10923 :
10924 : GDALEDTComponent::~GDALEDTComponent() = default;
10925 :
10926 : /************************************************************************/
10927 : /* GDALEDTComponent() */
10928 : /************************************************************************/
10929 :
10930 : /** constructor of a GDALEDTComponent
10931 : *
10932 : * This is the same as the C function GDALEDTComponendCreate()
10933 : *
10934 : * @param name Component name
10935 : * @param offset Offset in byte of the component in the compound data type.
10936 : * In case of nesting of compound data type, this should be
10937 : * the offset to the immediate belonging data type, not to the
10938 : * higher level one.
10939 : * @param type Component data type.
10940 : */
10941 3378 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10942 3378 : const GDALExtendedDataType &type)
10943 3378 : : m_osName(name), m_nOffset(offset), m_oType(type)
10944 : {
10945 3378 : }
10946 :
10947 : /************************************************************************/
10948 : /* GDALEDTComponent() */
10949 : /************************************************************************/
10950 :
10951 : /** Copy constructor. */
10952 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10953 :
10954 : /************************************************************************/
10955 : /* operator==() */
10956 : /************************************************************************/
10957 :
10958 : /** Equality operator.
10959 : */
10960 987 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10961 : {
10962 1974 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10963 1974 : m_oType == other.m_oType;
10964 : }
10965 :
10966 : /************************************************************************/
10967 : /* ~GDALDimension() */
10968 : /************************************************************************/
10969 :
10970 : GDALDimension::~GDALDimension() = default;
10971 :
10972 : /************************************************************************/
10973 : /* GDALDimension() */
10974 : /************************************************************************/
10975 :
10976 : //! @cond Doxygen_Suppress
10977 : /** Constructor.
10978 : *
10979 : * @param osParentName Parent name
10980 : * @param osName name
10981 : * @param osType type. See GetType().
10982 : * @param osDirection direction. See GetDirection().
10983 : * @param nSize size.
10984 : */
10985 9315 : GDALDimension::GDALDimension(const std::string &osParentName,
10986 : const std::string &osName,
10987 : const std::string &osType,
10988 9315 : const std::string &osDirection, GUInt64 nSize)
10989 : : m_osName(osName),
10990 : m_osFullName(
10991 9315 : !osParentName.empty()
10992 13770 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10993 : : osName),
10994 32400 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10995 : {
10996 9315 : }
10997 :
10998 : //! @endcond
10999 :
11000 : /************************************************************************/
11001 : /* GetIndexingVariable() */
11002 : /************************************************************************/
11003 :
11004 : /** Return the variable that is used to index the dimension (if there is one).
11005 : *
11006 : * This is the array, typically one-dimensional, describing the values taken
11007 : * by the dimension.
11008 : */
11009 43 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11010 : {
11011 43 : return nullptr;
11012 : }
11013 :
11014 : /************************************************************************/
11015 : /* SetIndexingVariable() */
11016 : /************************************************************************/
11017 :
11018 : /** Set the variable that is used to index the dimension.
11019 : *
11020 : * This is the array, typically one-dimensional, describing the values taken
11021 : * by the dimension.
11022 : *
11023 : * Optionally implemented by drivers.
11024 : *
11025 : * Drivers known to implement it: MEM.
11026 : *
11027 : * @param poArray Variable to use to index the dimension.
11028 : * @return true in case of success.
11029 : */
11030 11 : bool GDALDimension::SetIndexingVariable(
11031 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11032 : {
11033 11 : CPLError(CE_Failure, CPLE_NotSupported,
11034 : "SetIndexingVariable() not implemented");
11035 11 : return false;
11036 : }
11037 :
11038 : /************************************************************************/
11039 : /* Rename() */
11040 : /************************************************************************/
11041 :
11042 : /** Rename the dimension.
11043 : *
11044 : * This is not implemented by all drivers.
11045 : *
11046 : * Drivers known to implement it: MEM, netCDF, ZARR.
11047 : *
11048 : * This is the same as the C function GDALDimensionRename().
11049 : *
11050 : * @param osNewName New name.
11051 : *
11052 : * @return true in case of success
11053 : * @since GDAL 3.8
11054 : */
11055 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11056 : {
11057 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11058 0 : return false;
11059 : }
11060 :
11061 : /************************************************************************/
11062 : /* BaseRename() */
11063 : /************************************************************************/
11064 :
11065 : //! @cond Doxygen_Suppress
11066 8 : void GDALDimension::BaseRename(const std::string &osNewName)
11067 : {
11068 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
11069 8 : m_osFullName += osNewName;
11070 8 : m_osName = osNewName;
11071 8 : }
11072 :
11073 : //! @endcond
11074 :
11075 : //! @cond Doxygen_Suppress
11076 : /************************************************************************/
11077 : /* ParentRenamed() */
11078 : /************************************************************************/
11079 :
11080 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11081 : {
11082 8 : m_osFullName = osNewParentFullName;
11083 8 : m_osFullName += "/";
11084 8 : m_osFullName += m_osName;
11085 8 : }
11086 :
11087 : //! @endcond
11088 :
11089 : //! @cond Doxygen_Suppress
11090 : /************************************************************************/
11091 : /* ParentDeleted() */
11092 : /************************************************************************/
11093 :
11094 4 : void GDALDimension::ParentDeleted()
11095 : {
11096 4 : }
11097 :
11098 : //! @endcond
11099 :
11100 : /************************************************************************/
11101 : /************************************************************************/
11102 : /************************************************************************/
11103 : /* C API */
11104 : /************************************************************************/
11105 : /************************************************************************/
11106 : /************************************************************************/
11107 :
11108 : /************************************************************************/
11109 : /* GDALExtendedDataTypeCreate() */
11110 : /************************************************************************/
11111 :
11112 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11113 : *
11114 : * This is the same as the C++ method GDALExtendedDataType::Create()
11115 : *
11116 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11117 : *
11118 : * @param eType Numeric data type. Must be different from GDT_Unknown and
11119 : * GDT_TypeCount
11120 : *
11121 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11122 : */
11123 2146 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11124 : {
11125 2146 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11126 : {
11127 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11128 : "Illegal GDT_Unknown/GDT_TypeCount argument");
11129 0 : return nullptr;
11130 : }
11131 : return new GDALExtendedDataTypeHS(
11132 2146 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11133 : }
11134 :
11135 : /************************************************************************/
11136 : /* GDALExtendedDataTypeCreateString() */
11137 : /************************************************************************/
11138 :
11139 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11140 : *
11141 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11142 : *
11143 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11144 : *
11145 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11146 : */
11147 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11148 : {
11149 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11150 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
11151 : }
11152 :
11153 : /************************************************************************/
11154 : /* GDALExtendedDataTypeCreateStringEx() */
11155 : /************************************************************************/
11156 :
11157 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
11158 : *
11159 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
11160 : *
11161 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11162 : *
11163 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11164 : * @since GDAL 3.4
11165 : */
11166 : GDALExtendedDataTypeH
11167 222 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11168 : GDALExtendedDataTypeSubType eSubType)
11169 : {
11170 222 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11171 222 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11172 : }
11173 :
11174 : /************************************************************************/
11175 : /* GDALExtendedDataTypeCreateCompound() */
11176 : /************************************************************************/
11177 :
11178 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11179 : *
11180 : * This is the same as the C++ method GDALExtendedDataType::Create(const
11181 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11182 : *
11183 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
11184 : *
11185 : * @param pszName Type name.
11186 : * @param nTotalSize Total size of the type in bytes.
11187 : * Should be large enough to store all components.
11188 : * @param nComponents Number of components in comps array.
11189 : * @param comps Components.
11190 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
11191 : */
11192 : GDALExtendedDataTypeH
11193 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11194 : size_t nComponents,
11195 : const GDALEDTComponentH *comps)
11196 : {
11197 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11198 54 : for (size_t i = 0; i < nComponents; i++)
11199 : {
11200 : compsCpp.emplace_back(
11201 32 : std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11202 : }
11203 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11204 66 : std::move(compsCpp));
11205 22 : if (dt.GetClass() != GEDTC_COMPOUND)
11206 6 : return nullptr;
11207 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11208 : }
11209 :
11210 : /************************************************************************/
11211 : /* GDALExtendedDataTypeRelease() */
11212 : /************************************************************************/
11213 :
11214 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11215 : *
11216 : * Note: when applied on a object coming from a driver, this does not
11217 : * destroy the object in the file, database, etc...
11218 : */
11219 7069 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11220 : {
11221 7069 : delete hEDT;
11222 7069 : }
11223 :
11224 : /************************************************************************/
11225 : /* GDALExtendedDataTypeGetName() */
11226 : /************************************************************************/
11227 :
11228 : /** Return type name.
11229 : *
11230 : * This is the same as the C++ method GDALExtendedDataType::GetName()
11231 : */
11232 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11233 : {
11234 8 : VALIDATE_POINTER1(hEDT, __func__, "");
11235 8 : return hEDT->m_poImpl->GetName().c_str();
11236 : }
11237 :
11238 : /************************************************************************/
11239 : /* GDALExtendedDataTypeGetClass() */
11240 : /************************************************************************/
11241 :
11242 : /** Return type class.
11243 : *
11244 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
11245 : */
11246 : GDALExtendedDataTypeClass
11247 9591 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11248 : {
11249 9591 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11250 9591 : return hEDT->m_poImpl->GetClass();
11251 : }
11252 :
11253 : /************************************************************************/
11254 : /* GDALExtendedDataTypeGetNumericDataType() */
11255 : /************************************************************************/
11256 :
11257 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11258 : *
11259 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11260 : */
11261 2134 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11262 : {
11263 2134 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11264 2134 : return hEDT->m_poImpl->GetNumericDataType();
11265 : }
11266 :
11267 : /************************************************************************/
11268 : /* GDALExtendedDataTypeGetSize() */
11269 : /************************************************************************/
11270 :
11271 : /** Return data type size in bytes.
11272 : *
11273 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
11274 : */
11275 2663 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11276 : {
11277 2663 : VALIDATE_POINTER1(hEDT, __func__, 0);
11278 2663 : return hEDT->m_poImpl->GetSize();
11279 : }
11280 :
11281 : /************************************************************************/
11282 : /* GDALExtendedDataTypeGetMaxStringLength() */
11283 : /************************************************************************/
11284 :
11285 : /** Return the maximum length of a string in bytes.
11286 : *
11287 : * 0 indicates unknown/unlimited string.
11288 : *
11289 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11290 : */
11291 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11292 : {
11293 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
11294 3 : return hEDT->m_poImpl->GetMaxStringLength();
11295 : }
11296 :
11297 : /************************************************************************/
11298 : /* GDALExtendedDataTypeCanConvertTo() */
11299 : /************************************************************************/
11300 :
11301 : /** Return whether this data type can be converted to the other one.
11302 : *
11303 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11304 : *
11305 : * @param hSourceEDT Source data type for the conversion being considered.
11306 : * @param hTargetEDT Target data type for the conversion being considered.
11307 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11308 : */
11309 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11310 : GDALExtendedDataTypeH hTargetEDT)
11311 : {
11312 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11313 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11314 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11315 : }
11316 :
11317 : /************************************************************************/
11318 : /* GDALExtendedDataTypeEquals() */
11319 : /************************************************************************/
11320 :
11321 : /** Return whether this data type is equal to another one.
11322 : *
11323 : * This is the same as the C++ method GDALExtendedDataType::operator==()
11324 : *
11325 : * @param hFirstEDT First data type.
11326 : * @param hSecondEDT Second data type.
11327 : * @return TRUE if they are equal. FALSE otherwise.
11328 : */
11329 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11330 : GDALExtendedDataTypeH hSecondEDT)
11331 : {
11332 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11333 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11334 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11335 : }
11336 :
11337 : /************************************************************************/
11338 : /* GDALExtendedDataTypeGetSubType() */
11339 : /************************************************************************/
11340 :
11341 : /** Return the subtype of a type.
11342 : *
11343 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11344 : *
11345 : * @param hEDT Data type.
11346 : * @return subtype.
11347 : * @since 3.4
11348 : */
11349 : GDALExtendedDataTypeSubType
11350 105 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11351 : {
11352 105 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11353 105 : return hEDT->m_poImpl->GetSubType();
11354 : }
11355 :
11356 : /************************************************************************/
11357 : /* GDALExtendedDataTypeGetRAT() */
11358 : /************************************************************************/
11359 :
11360 : /** Return associated raster attribute table, when there is one.
11361 : *
11362 : * * For the netCDF driver, the RAT will capture enumerated types, with
11363 : * a "value" column with an integer value and a "name" column with the
11364 : * associated name.
11365 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11366 : *
11367 : * @param hEDT Data type.
11368 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11369 : * @since 3.12
11370 : */
11371 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11372 : {
11373 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11374 1 : return GDALRasterAttributeTable::ToHandle(
11375 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11376 : }
11377 :
11378 : /************************************************************************/
11379 : /* GDALExtendedDataTypeGetComponents() */
11380 : /************************************************************************/
11381 :
11382 : /** Return the components of the data type (only valid when GetClass() ==
11383 : * GEDTC_COMPOUND)
11384 : *
11385 : * The returned array and its content must be freed with
11386 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11387 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11388 : * individual array members).
11389 : *
11390 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11391 : *
11392 : * @param hEDT Data type
11393 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11394 : * @return an array of *pnCount components.
11395 : */
11396 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11397 : size_t *pnCount)
11398 : {
11399 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11400 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11401 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11402 : auto ret = static_cast<GDALEDTComponentH *>(
11403 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11404 131 : for (size_t i = 0; i < components.size(); i++)
11405 : {
11406 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11407 : }
11408 44 : *pnCount = components.size();
11409 44 : return ret;
11410 : }
11411 :
11412 : /************************************************************************/
11413 : /* GDALExtendedDataTypeFreeComponents() */
11414 : /************************************************************************/
11415 :
11416 : /** Free the return of GDALExtendedDataTypeGetComponents().
11417 : *
11418 : * @param components return value of GDALExtendedDataTypeGetComponents()
11419 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11420 : */
11421 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11422 : size_t nCount)
11423 : {
11424 131 : for (size_t i = 0; i < nCount; i++)
11425 : {
11426 87 : delete components[i];
11427 : }
11428 44 : CPLFree(components);
11429 44 : }
11430 :
11431 : /************************************************************************/
11432 : /* GDALEDTComponentCreate() */
11433 : /************************************************************************/
11434 :
11435 : /** Create a new GDALEDTComponent.
11436 : *
11437 : * The returned value must be freed with GDALEDTComponentRelease().
11438 : *
11439 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11440 : */
11441 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11442 : GDALExtendedDataTypeH hType)
11443 : {
11444 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11445 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11446 : return new GDALEDTComponentHS(
11447 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11448 : }
11449 :
11450 : /************************************************************************/
11451 : /* GDALEDTComponentRelease() */
11452 : /************************************************************************/
11453 :
11454 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11455 : *
11456 : * Note: when applied on a object coming from a driver, this does not
11457 : * destroy the object in the file, database, etc...
11458 : */
11459 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11460 : {
11461 61 : delete hComp;
11462 61 : }
11463 :
11464 : /************************************************************************/
11465 : /* GDALEDTComponentGetName() */
11466 : /************************************************************************/
11467 :
11468 : /** Return the name.
11469 : *
11470 : * The returned pointer is valid until hComp is released.
11471 : *
11472 : * This is the same as the C++ method GDALEDTComponent::GetName().
11473 : */
11474 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11475 : {
11476 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11477 33 : return hComp->m_poImpl->GetName().c_str();
11478 : }
11479 :
11480 : /************************************************************************/
11481 : /* GDALEDTComponentGetOffset() */
11482 : /************************************************************************/
11483 :
11484 : /** Return the offset (in bytes) of the component in the compound data type.
11485 : *
11486 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11487 : */
11488 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11489 : {
11490 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11491 31 : return hComp->m_poImpl->GetOffset();
11492 : }
11493 :
11494 : /************************************************************************/
11495 : /* GDALEDTComponentGetType() */
11496 : /************************************************************************/
11497 :
11498 : /** Return the data type of the component.
11499 : *
11500 : * This is the same as the C++ method GDALEDTComponent::GetType().
11501 : */
11502 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11503 : {
11504 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11505 : return new GDALExtendedDataTypeHS(
11506 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11507 : }
11508 :
11509 : /************************************************************************/
11510 : /* GDALGroupRelease() */
11511 : /************************************************************************/
11512 :
11513 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11514 : *
11515 : * Note: when applied on a object coming from a driver, this does not
11516 : * destroy the object in the file, database, etc...
11517 : */
11518 1511 : void GDALGroupRelease(GDALGroupH hGroup)
11519 : {
11520 1511 : delete hGroup;
11521 1511 : }
11522 :
11523 : /************************************************************************/
11524 : /* GDALGroupGetName() */
11525 : /************************************************************************/
11526 :
11527 : /** Return the name of the group.
11528 : *
11529 : * The returned pointer is valid until hGroup is released.
11530 : *
11531 : * This is the same as the C++ method GDALGroup::GetName().
11532 : */
11533 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11534 : {
11535 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11536 95 : return hGroup->m_poImpl->GetName().c_str();
11537 : }
11538 :
11539 : /************************************************************************/
11540 : /* GDALGroupGetFullName() */
11541 : /************************************************************************/
11542 :
11543 : /** Return the full name of the group.
11544 : *
11545 : * The returned pointer is valid until hGroup is released.
11546 : *
11547 : * This is the same as the C++ method GDALGroup::GetFullName().
11548 : */
11549 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11550 : {
11551 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11552 47 : return hGroup->m_poImpl->GetFullName().c_str();
11553 : }
11554 :
11555 : /************************************************************************/
11556 : /* GDALGroupGetMDArrayNames() */
11557 : /************************************************************************/
11558 :
11559 : /** Return the list of multidimensional array names contained in this group.
11560 : *
11561 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11562 : *
11563 : * @return the array names, to be freed with CSLDestroy()
11564 : */
11565 331 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11566 : {
11567 331 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11568 662 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11569 662 : CPLStringList res;
11570 839 : for (const auto &name : names)
11571 : {
11572 508 : res.AddString(name.c_str());
11573 : }
11574 331 : return res.StealList();
11575 : }
11576 :
11577 : /************************************************************************/
11578 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11579 : /************************************************************************/
11580 :
11581 : /** Return the list of multidimensional array full names contained in this
11582 : * group and its subgroups.
11583 : *
11584 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11585 : *
11586 : * @return the array names, to be freed with CSLDestroy()
11587 : *
11588 : * @since 3.11
11589 : */
11590 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11591 : CSLConstList papszGroupOptions,
11592 : CSLConstList papszArrayOptions)
11593 : {
11594 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11595 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11596 2 : papszGroupOptions, papszArrayOptions);
11597 2 : CPLStringList res;
11598 5 : for (const auto &name : names)
11599 : {
11600 4 : res.AddString(name.c_str());
11601 : }
11602 1 : return res.StealList();
11603 : }
11604 :
11605 : /************************************************************************/
11606 : /* GDALGroupOpenMDArray() */
11607 : /************************************************************************/
11608 :
11609 : /** Open and return a multidimensional array.
11610 : *
11611 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11612 : *
11613 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11614 : */
11615 820 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11616 : CSLConstList papszOptions)
11617 : {
11618 820 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11619 820 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11620 2460 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11621 2460 : papszOptions);
11622 820 : if (!array)
11623 30 : return nullptr;
11624 790 : return new GDALMDArrayHS(array);
11625 : }
11626 :
11627 : /************************************************************************/
11628 : /* GDALGroupOpenMDArrayFromFullname() */
11629 : /************************************************************************/
11630 :
11631 : /** Open and return a multidimensional array from its fully qualified name.
11632 : *
11633 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11634 : *
11635 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11636 : *
11637 : * @since GDAL 3.2
11638 : */
11639 17 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11640 : const char *pszFullname,
11641 : CSLConstList papszOptions)
11642 : {
11643 17 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11644 17 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11645 17 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11646 51 : std::string(pszFullname), papszOptions);
11647 17 : if (!array)
11648 2 : return nullptr;
11649 15 : return new GDALMDArrayHS(array);
11650 : }
11651 :
11652 : /************************************************************************/
11653 : /* GDALGroupResolveMDArray() */
11654 : /************************************************************************/
11655 :
11656 : /** Locate an array in a group and its subgroups by name.
11657 : *
11658 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11659 : * @since GDAL 3.2
11660 : */
11661 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11662 : const char *pszStartingPoint,
11663 : CSLConstList papszOptions)
11664 : {
11665 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11666 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11667 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11668 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11669 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11670 19 : if (!array)
11671 2 : return nullptr;
11672 17 : return new GDALMDArrayHS(array);
11673 : }
11674 :
11675 : /************************************************************************/
11676 : /* GDALGroupGetGroupNames() */
11677 : /************************************************************************/
11678 :
11679 : /** Return the list of sub-groups contained in this group.
11680 : *
11681 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11682 : *
11683 : * @return the group names, to be freed with CSLDestroy()
11684 : */
11685 98 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11686 : {
11687 98 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11688 196 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11689 196 : CPLStringList res;
11690 221 : for (const auto &name : names)
11691 : {
11692 123 : res.AddString(name.c_str());
11693 : }
11694 98 : return res.StealList();
11695 : }
11696 :
11697 : /************************************************************************/
11698 : /* GDALGroupOpenGroup() */
11699 : /************************************************************************/
11700 :
11701 : /** Open and return a sub-group.
11702 : *
11703 : * This is the same as the C++ method GDALGroup::OpenGroup().
11704 : *
11705 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11706 : */
11707 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11708 : CSLConstList papszOptions)
11709 : {
11710 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11711 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11712 : auto subGroup =
11713 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11714 163 : if (!subGroup)
11715 30 : return nullptr;
11716 133 : return new GDALGroupHS(subGroup);
11717 : }
11718 :
11719 : /************************************************************************/
11720 : /* GDALGroupGetVectorLayerNames() */
11721 : /************************************************************************/
11722 :
11723 : /** Return the list of layer names contained in this group.
11724 : *
11725 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11726 : *
11727 : * @return the group names, to be freed with CSLDestroy()
11728 : * @since 3.4
11729 : */
11730 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11731 : CSLConstList papszOptions)
11732 : {
11733 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11734 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11735 16 : CPLStringList res;
11736 18 : for (const auto &name : names)
11737 : {
11738 10 : res.AddString(name.c_str());
11739 : }
11740 8 : return res.StealList();
11741 : }
11742 :
11743 : /************************************************************************/
11744 : /* GDALGroupOpenVectorLayer() */
11745 : /************************************************************************/
11746 :
11747 : /** Open and return a vector layer.
11748 : *
11749 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11750 : *
11751 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11752 : * the returned handled if only valid while the parent GDALDatasetH is kept
11753 : * opened.
11754 : *
11755 : * @return the vector layer, or nullptr.
11756 : * @since 3.4
11757 : */
11758 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11759 : const char *pszVectorLayerName,
11760 : CSLConstList papszOptions)
11761 : {
11762 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11763 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11764 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11765 24 : std::string(pszVectorLayerName), papszOptions));
11766 : }
11767 :
11768 : /************************************************************************/
11769 : /* GDALGroupOpenMDArrayFromFullname() */
11770 : /************************************************************************/
11771 :
11772 : /** Open and return a sub-group from its fully qualified name.
11773 : *
11774 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11775 : *
11776 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11777 : *
11778 : * @since GDAL 3.2
11779 : */
11780 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11781 : const char *pszFullname,
11782 : CSLConstList papszOptions)
11783 : {
11784 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11785 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11786 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11787 9 : std::string(pszFullname), papszOptions);
11788 3 : if (!subGroup)
11789 2 : return nullptr;
11790 1 : return new GDALGroupHS(subGroup);
11791 : }
11792 :
11793 : /************************************************************************/
11794 : /* GDALGroupGetDimensions() */
11795 : /************************************************************************/
11796 :
11797 : /** Return the list of dimensions contained in this group and used by its
11798 : * arrays.
11799 : *
11800 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11801 : * array itself needs to be freed, CPLFree() should be called (and
11802 : * GDALDimensionRelease() on individual array members).
11803 : *
11804 : * This is the same as the C++ method GDALGroup::GetDimensions().
11805 : *
11806 : * @param hGroup Group.
11807 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11808 : * @param papszOptions Driver specific options determining how dimensions
11809 : * should be retrieved. Pass nullptr for default behavior.
11810 : *
11811 : * @return an array of *pnCount dimensions.
11812 : */
11813 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11814 : CSLConstList papszOptions)
11815 : {
11816 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11817 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11818 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11819 : auto ret = static_cast<GDALDimensionH *>(
11820 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11821 230 : for (size_t i = 0; i < dims.size(); i++)
11822 : {
11823 157 : ret[i] = new GDALDimensionHS(dims[i]);
11824 : }
11825 73 : *pnCount = dims.size();
11826 73 : return ret;
11827 : }
11828 :
11829 : /************************************************************************/
11830 : /* GDALGroupGetAttribute() */
11831 : /************************************************************************/
11832 :
11833 : /** Return an attribute by its name.
11834 : *
11835 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11836 : *
11837 : * The returned attribute must be freed with GDALAttributeRelease().
11838 : */
11839 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11840 : {
11841 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11842 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11843 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11844 80 : if (attr)
11845 76 : return new GDALAttributeHS(attr);
11846 4 : return nullptr;
11847 : }
11848 :
11849 : /************************************************************************/
11850 : /* GDALGroupGetAttributes() */
11851 : /************************************************************************/
11852 :
11853 : /** Return the list of attributes contained in this group.
11854 : *
11855 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11856 : * array itself needs to be freed, CPLFree() should be called (and
11857 : * GDALAttributeRelease() on individual array members).
11858 : *
11859 : * This is the same as the C++ method GDALGroup::GetAttributes().
11860 : *
11861 : * @param hGroup Group.
11862 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11863 : * @param papszOptions Driver specific options determining how attributes
11864 : * should be retrieved. Pass nullptr for default behavior.
11865 : *
11866 : * @return an array of *pnCount attributes.
11867 : */
11868 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11869 : CSLConstList papszOptions)
11870 : {
11871 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11872 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11873 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11874 : auto ret = static_cast<GDALAttributeH *>(
11875 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11876 229 : for (size_t i = 0; i < attrs.size(); i++)
11877 : {
11878 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11879 : }
11880 71 : *pnCount = attrs.size();
11881 71 : return ret;
11882 : }
11883 :
11884 : /************************************************************************/
11885 : /* GDALGroupGetStructuralInfo() */
11886 : /************************************************************************/
11887 :
11888 : /** Return structural information on the group.
11889 : *
11890 : * This may be the compression, etc..
11891 : *
11892 : * The return value should not be freed and is valid until GDALGroup is
11893 : * released or this function called again.
11894 : *
11895 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11896 : */
11897 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11898 : {
11899 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11900 4 : return hGroup->m_poImpl->GetStructuralInfo();
11901 : }
11902 :
11903 : /************************************************************************/
11904 : /* GDALGroupGetDataTypeCount() */
11905 : /************************************************************************/
11906 :
11907 : /** Return the number of data types associated with the group
11908 : * (typically enumerations).
11909 : *
11910 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11911 : *
11912 : * @since 3.12
11913 : */
11914 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11915 : {
11916 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11917 4 : return hGroup->m_poImpl->GetDataTypes().size();
11918 : }
11919 :
11920 : /************************************************************************/
11921 : /* GDALGroupGetDataType() */
11922 : /************************************************************************/
11923 :
11924 : /** Return one of the data types associated with the group.
11925 : *
11926 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11927 : *
11928 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11929 : * or nullptr in case of error.
11930 : * @since 3.12
11931 : */
11932 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11933 : {
11934 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11935 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11936 0 : return nullptr;
11937 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11938 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11939 : }
11940 :
11941 : /************************************************************************/
11942 : /* GDALReleaseAttributes() */
11943 : /************************************************************************/
11944 :
11945 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11946 : *
11947 : * @param attributes return pointer of above methods
11948 : * @param nCount *pnCount value returned by above methods
11949 : */
11950 130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11951 : {
11952 418 : for (size_t i = 0; i < nCount; i++)
11953 : {
11954 288 : delete attributes[i];
11955 : }
11956 130 : CPLFree(attributes);
11957 130 : }
11958 :
11959 : /************************************************************************/
11960 : /* GDALGroupCreateGroup() */
11961 : /************************************************************************/
11962 :
11963 : /** Create a sub-group within a group.
11964 : *
11965 : * This is the same as the C++ method GDALGroup::CreateGroup().
11966 : *
11967 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11968 : */
11969 179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11970 : CSLConstList papszOptions)
11971 : {
11972 179 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11973 179 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11974 537 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11975 537 : papszOptions);
11976 179 : if (!ret)
11977 49 : return nullptr;
11978 130 : return new GDALGroupHS(ret);
11979 : }
11980 :
11981 : /************************************************************************/
11982 : /* GDALGroupDeleteGroup() */
11983 : /************************************************************************/
11984 :
11985 : /** Delete a sub-group from a group.
11986 : *
11987 : * After this call, if a previously obtained instance of the deleted object
11988 : * is still alive, no method other than for freeing it should be invoked.
11989 : *
11990 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11991 : *
11992 : * @return true in case of success.
11993 : * @since GDAL 3.8
11994 : */
11995 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11996 : CSLConstList papszOptions)
11997 : {
11998 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11999 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12000 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12001 20 : papszOptions);
12002 : }
12003 :
12004 : /************************************************************************/
12005 : /* GDALGroupCreateDimension() */
12006 : /************************************************************************/
12007 :
12008 : /** Create a dimension within a group.
12009 : *
12010 : * This is the same as the C++ method GDALGroup::CreateDimension().
12011 : *
12012 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12013 : */
12014 767 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12015 : const char *pszType,
12016 : const char *pszDirection, GUInt64 nSize,
12017 : CSLConstList papszOptions)
12018 : {
12019 767 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12020 767 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12021 767 : auto ret = hGroup->m_poImpl->CreateDimension(
12022 1534 : std::string(pszName), std::string(pszType ? pszType : ""),
12023 3068 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12024 767 : if (!ret)
12025 9 : return nullptr;
12026 758 : return new GDALDimensionHS(ret);
12027 : }
12028 :
12029 : /************************************************************************/
12030 : /* GDALGroupCreateMDArray() */
12031 : /************************************************************************/
12032 :
12033 : /** Create a multidimensional array within a group.
12034 : *
12035 : * This is the same as the C++ method GDALGroup::CreateMDArray().
12036 : *
12037 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12038 : */
12039 709 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12040 : size_t nDimensions,
12041 : GDALDimensionH *pahDimensions,
12042 : GDALExtendedDataTypeH hEDT,
12043 : CSLConstList papszOptions)
12044 : {
12045 709 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12046 709 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12047 709 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12048 1418 : std::vector<std::shared_ptr<GDALDimension>> dims;
12049 709 : dims.reserve(nDimensions);
12050 1692 : for (size_t i = 0; i < nDimensions; i++)
12051 983 : dims.push_back(pahDimensions[i]->m_poImpl);
12052 2127 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12053 2127 : *(hEDT->m_poImpl), papszOptions);
12054 709 : if (!ret)
12055 64 : return nullptr;
12056 645 : return new GDALMDArrayHS(ret);
12057 : }
12058 :
12059 : /************************************************************************/
12060 : /* GDALGroupDeleteMDArray() */
12061 : /************************************************************************/
12062 :
12063 : /** Delete an array from a group.
12064 : *
12065 : * After this call, if a previously obtained instance of the deleted object
12066 : * is still alive, no method other than for freeing it should be invoked.
12067 : *
12068 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
12069 : *
12070 : * @return true in case of success.
12071 : * @since GDAL 3.8
12072 : */
12073 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12074 : CSLConstList papszOptions)
12075 : {
12076 20 : VALIDATE_POINTER1(hGroup, __func__, false);
12077 20 : VALIDATE_POINTER1(pszName, __func__, false);
12078 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12079 : }
12080 :
12081 : /************************************************************************/
12082 : /* GDALGroupCreateAttribute() */
12083 : /************************************************************************/
12084 :
12085 : /** Create a attribute within a group.
12086 : *
12087 : * This is the same as the C++ method GDALGroup::CreateAttribute().
12088 : *
12089 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12090 : */
12091 125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12092 : size_t nDimensions,
12093 : const GUInt64 *panDimensions,
12094 : GDALExtendedDataTypeH hEDT,
12095 : CSLConstList papszOptions)
12096 : {
12097 125 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12098 125 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12099 250 : std::vector<GUInt64> dims;
12100 125 : dims.reserve(nDimensions);
12101 175 : for (size_t i = 0; i < nDimensions; i++)
12102 50 : dims.push_back(panDimensions[i]);
12103 125 : auto ret = hGroup->m_poImpl->CreateAttribute(
12104 375 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12105 125 : if (!ret)
12106 14 : return nullptr;
12107 111 : return new GDALAttributeHS(ret);
12108 : }
12109 :
12110 : /************************************************************************/
12111 : /* GDALGroupDeleteAttribute() */
12112 : /************************************************************************/
12113 :
12114 : /** Delete an attribute from a group.
12115 : *
12116 : * After this call, if a previously obtained instance of the deleted object
12117 : * is still alive, no method other than for freeing it should be invoked.
12118 : *
12119 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
12120 : *
12121 : * @return true in case of success.
12122 : * @since GDAL 3.8
12123 : */
12124 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12125 : CSLConstList papszOptions)
12126 : {
12127 25 : VALIDATE_POINTER1(hGroup, __func__, false);
12128 25 : VALIDATE_POINTER1(pszName, __func__, false);
12129 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12130 25 : papszOptions);
12131 : }
12132 :
12133 : /************************************************************************/
12134 : /* GDALGroupRename() */
12135 : /************************************************************************/
12136 :
12137 : /** Rename the group.
12138 : *
12139 : * This is not implemented by all drivers.
12140 : *
12141 : * Drivers known to implement it: MEM, netCDF.
12142 : *
12143 : * This is the same as the C++ method GDALGroup::Rename()
12144 : *
12145 : * @return true in case of success
12146 : * @since GDAL 3.8
12147 : */
12148 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12149 : {
12150 45 : VALIDATE_POINTER1(hGroup, __func__, false);
12151 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
12152 45 : return hGroup->m_poImpl->Rename(pszNewName);
12153 : }
12154 :
12155 : /************************************************************************/
12156 : /* GDALGroupSubsetDimensionFromSelection() */
12157 : /************************************************************************/
12158 :
12159 : /** Return a virtual group whose one dimension has been subset according to a
12160 : * selection.
12161 : *
12162 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12163 : *
12164 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12165 : */
12166 : GDALGroupH
12167 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12168 : const char *pszSelection,
12169 : CPL_UNUSED CSLConstList papszOptions)
12170 : {
12171 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
12172 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12173 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12174 42 : std::string(pszSelection));
12175 14 : if (!hNewGroup)
12176 8 : return nullptr;
12177 6 : return new GDALGroupHS(hNewGroup);
12178 : }
12179 :
12180 : /************************************************************************/
12181 : /* GDALMDArrayRelease() */
12182 : /************************************************************************/
12183 :
12184 : /** Release the GDAL in-memory object associated with a GDALMDArray.
12185 : *
12186 : * Note: when applied on a object coming from a driver, this does not
12187 : * destroy the object in the file, database, etc...
12188 : */
12189 2171 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12190 : {
12191 2171 : delete hMDArray;
12192 2171 : }
12193 :
12194 : /************************************************************************/
12195 : /* GDALMDArrayGetName() */
12196 : /************************************************************************/
12197 :
12198 : /** Return array name.
12199 : *
12200 : * This is the same as the C++ method GDALMDArray::GetName()
12201 : */
12202 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12203 : {
12204 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12205 83 : return hArray->m_poImpl->GetName().c_str();
12206 : }
12207 :
12208 : /************************************************************************/
12209 : /* GDALMDArrayGetFullName() */
12210 : /************************************************************************/
12211 :
12212 : /** Return array full name.
12213 : *
12214 : * This is the same as the C++ method GDALMDArray::GetFullName()
12215 : */
12216 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12217 : {
12218 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12219 50 : return hArray->m_poImpl->GetFullName().c_str();
12220 : }
12221 :
12222 : /************************************************************************/
12223 : /* GDALMDArrayGetName() */
12224 : /************************************************************************/
12225 :
12226 : /** Return the total number of values in the array.
12227 : *
12228 : * This is the same as the C++ method
12229 : * GDALAbstractMDArray::GetTotalElementsCount()
12230 : */
12231 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12232 : {
12233 6 : VALIDATE_POINTER1(hArray, __func__, 0);
12234 6 : return hArray->m_poImpl->GetTotalElementsCount();
12235 : }
12236 :
12237 : /************************************************************************/
12238 : /* GDALMDArrayGetDimensionCount() */
12239 : /************************************************************************/
12240 :
12241 : /** Return the number of dimensions.
12242 : *
12243 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12244 : */
12245 10821 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12246 : {
12247 10821 : VALIDATE_POINTER1(hArray, __func__, 0);
12248 10821 : return hArray->m_poImpl->GetDimensionCount();
12249 : }
12250 :
12251 : /************************************************************************/
12252 : /* GDALMDArrayGetDimensions() */
12253 : /************************************************************************/
12254 :
12255 : /** Return the dimensions of the array
12256 : *
12257 : * The returned array must be freed with GDALReleaseDimensions(). If only the
12258 : * array itself needs to be freed, CPLFree() should be called (and
12259 : * GDALDimensionRelease() on individual array members).
12260 : *
12261 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12262 : *
12263 : * @param hArray Array.
12264 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12265 : *
12266 : * @return an array of *pnCount dimensions.
12267 : */
12268 2453 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12269 : {
12270 2453 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12271 2453 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12272 2453 : const auto &dims(hArray->m_poImpl->GetDimensions());
12273 : auto ret = static_cast<GDALDimensionH *>(
12274 2453 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12275 6926 : for (size_t i = 0; i < dims.size(); i++)
12276 : {
12277 4473 : ret[i] = new GDALDimensionHS(dims[i]);
12278 : }
12279 2453 : *pnCount = dims.size();
12280 2453 : return ret;
12281 : }
12282 :
12283 : /************************************************************************/
12284 : /* GDALReleaseDimensions() */
12285 : /************************************************************************/
12286 :
12287 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12288 : *
12289 : * @param dims return pointer of above methods
12290 : * @param nCount *pnCount value returned by above methods
12291 : */
12292 2526 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12293 : {
12294 7156 : for (size_t i = 0; i < nCount; i++)
12295 : {
12296 4630 : delete dims[i];
12297 : }
12298 2526 : CPLFree(dims);
12299 2526 : }
12300 :
12301 : /************************************************************************/
12302 : /* GDALMDArrayGetDataType() */
12303 : /************************************************************************/
12304 :
12305 : /** Return the data type
12306 : *
12307 : * The return must be freed with GDALExtendedDataTypeRelease().
12308 : */
12309 4182 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12310 : {
12311 4182 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12312 : return new GDALExtendedDataTypeHS(
12313 4182 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12314 : }
12315 :
12316 : /************************************************************************/
12317 : /* GDALMDArrayRead() */
12318 : /************************************************************************/
12319 :
12320 : /** Read part or totality of a multidimensional array.
12321 : *
12322 : * This is the same as the C++ method GDALAbstractMDArray::Read()
12323 : *
12324 : * @return TRUE in case of success.
12325 : */
12326 1989 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12327 : const size_t *count, const GInt64 *arrayStep,
12328 : const GPtrDiff_t *bufferStride,
12329 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12330 : const void *pDstBufferAllocStart,
12331 : size_t nDstBufferAllocSize)
12332 : {
12333 1989 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12334 1989 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12335 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12336 : {
12337 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12338 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12339 : }
12340 1989 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12341 1989 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12342 3978 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12343 1989 : *(bufferDataType->m_poImpl), pDstBuffer,
12344 1989 : pDstBufferAllocStart, nDstBufferAllocSize);
12345 : }
12346 :
12347 : /************************************************************************/
12348 : /* GDALMDArrayWrite() */
12349 : /************************************************************************/
12350 :
12351 : /** Write part or totality of a multidimensional array.
12352 : *
12353 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12354 : *
12355 : * @return TRUE in case of success.
12356 : */
12357 656 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12358 : const size_t *count, const GInt64 *arrayStep,
12359 : const GPtrDiff_t *bufferStride,
12360 : GDALExtendedDataTypeH bufferDataType,
12361 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12362 : size_t nSrcBufferAllocSize)
12363 : {
12364 656 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12365 656 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12366 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12367 : {
12368 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12369 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12370 : }
12371 656 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12372 656 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12373 1312 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12374 656 : bufferStride, *(bufferDataType->m_poImpl),
12375 : pSrcBuffer, pSrcBufferAllocStart,
12376 656 : nSrcBufferAllocSize);
12377 : }
12378 :
12379 : /************************************************************************/
12380 : /* GDALMDArrayAdviseRead() */
12381 : /************************************************************************/
12382 :
12383 : /** Advise driver of upcoming read requests.
12384 : *
12385 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12386 : *
12387 : * @return TRUE in case of success.
12388 : *
12389 : * @since GDAL 3.2
12390 : */
12391 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12392 : const size_t *count)
12393 : {
12394 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12395 : }
12396 :
12397 : /************************************************************************/
12398 : /* GDALMDArrayAdviseReadEx() */
12399 : /************************************************************************/
12400 :
12401 : /** Advise driver of upcoming read requests.
12402 : *
12403 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12404 : *
12405 : * @return TRUE in case of success.
12406 : *
12407 : * @since GDAL 3.4
12408 : */
12409 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12410 : const size_t *count, CSLConstList papszOptions)
12411 : {
12412 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12413 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12414 : }
12415 :
12416 : /************************************************************************/
12417 : /* GDALMDArrayGetAttribute() */
12418 : /************************************************************************/
12419 :
12420 : /** Return an attribute by its name.
12421 : *
12422 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12423 : *
12424 : * The returned attribute must be freed with GDALAttributeRelease().
12425 : */
12426 120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12427 : {
12428 120 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12429 120 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12430 360 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12431 120 : if (attr)
12432 111 : return new GDALAttributeHS(attr);
12433 9 : return nullptr;
12434 : }
12435 :
12436 : /************************************************************************/
12437 : /* GDALMDArrayGetAttributes() */
12438 : /************************************************************************/
12439 :
12440 : /** Return the list of attributes contained in this array.
12441 : *
12442 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12443 : * array itself needs to be freed, CPLFree() should be called (and
12444 : * GDALAttributeRelease() on individual array members).
12445 : *
12446 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12447 : *
12448 : * @param hArray Array.
12449 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12450 : * @param papszOptions Driver specific options determining how attributes
12451 : * should be retrieved. Pass nullptr for default behavior.
12452 : *
12453 : * @return an array of *pnCount attributes.
12454 : */
12455 59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12456 : CSLConstList papszOptions)
12457 : {
12458 59 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12459 59 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12460 59 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12461 : auto ret = static_cast<GDALAttributeH *>(
12462 59 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12463 189 : for (size_t i = 0; i < attrs.size(); i++)
12464 : {
12465 130 : ret[i] = new GDALAttributeHS(attrs[i]);
12466 : }
12467 59 : *pnCount = attrs.size();
12468 59 : return ret;
12469 : }
12470 :
12471 : /************************************************************************/
12472 : /* GDALMDArrayCreateAttribute() */
12473 : /************************************************************************/
12474 :
12475 : /** Create a attribute within an array.
12476 : *
12477 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12478 : *
12479 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12480 : */
12481 188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12482 : const char *pszName,
12483 : size_t nDimensions,
12484 : const GUInt64 *panDimensions,
12485 : GDALExtendedDataTypeH hEDT,
12486 : CSLConstList papszOptions)
12487 : {
12488 188 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12489 188 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12490 188 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12491 376 : std::vector<GUInt64> dims;
12492 188 : dims.reserve(nDimensions);
12493 249 : for (size_t i = 0; i < nDimensions; i++)
12494 61 : dims.push_back(panDimensions[i]);
12495 188 : auto ret = hArray->m_poImpl->CreateAttribute(
12496 564 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12497 188 : if (!ret)
12498 9 : return nullptr;
12499 179 : return new GDALAttributeHS(ret);
12500 : }
12501 :
12502 : /************************************************************************/
12503 : /* GDALMDArrayDeleteAttribute() */
12504 : /************************************************************************/
12505 :
12506 : /** Delete an attribute from an array.
12507 : *
12508 : * After this call, if a previously obtained instance of the deleted object
12509 : * is still alive, no method other than for freeing it should be invoked.
12510 : *
12511 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12512 : *
12513 : * @return true in case of success.
12514 : * @since GDAL 3.8
12515 : */
12516 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12517 : CSLConstList papszOptions)
12518 : {
12519 24 : VALIDATE_POINTER1(hArray, __func__, false);
12520 24 : VALIDATE_POINTER1(pszName, __func__, false);
12521 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12522 24 : papszOptions);
12523 : }
12524 :
12525 : /************************************************************************/
12526 : /* GDALMDArrayGetRawNoDataValue() */
12527 : /************************************************************************/
12528 :
12529 : /** Return the nodata value as a "raw" value.
12530 : *
12531 : * The value returned might be nullptr in case of no nodata value. When
12532 : * a nodata value is registered, a non-nullptr will be returned whose size in
12533 : * bytes is GetDataType().GetSize().
12534 : *
12535 : * The returned value should not be modified or freed.
12536 : *
12537 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12538 : *
12539 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12540 : */
12541 77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12542 : {
12543 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12544 77 : return hArray->m_poImpl->GetRawNoDataValue();
12545 : }
12546 :
12547 : /************************************************************************/
12548 : /* GDALMDArrayGetNoDataValueAsDouble() */
12549 : /************************************************************************/
12550 :
12551 : /** Return the nodata value as a double.
12552 : *
12553 : * The value returned might be nullptr in case of no nodata value. When
12554 : * a nodata value is registered, a non-nullptr will be returned whose size in
12555 : * bytes is GetDataType().GetSize().
12556 : *
12557 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12558 : *
12559 : * @param hArray Array handle.
12560 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12561 : * if a nodata value exists and can be converted to double. Might be nullptr.
12562 : *
12563 : * @return the nodata value as a double. A 0.0 value might also indicate the
12564 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12565 : * will be set to false then).
12566 : */
12567 121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12568 : int *pbHasNoDataValue)
12569 : {
12570 121 : VALIDATE_POINTER1(hArray, __func__, 0);
12571 121 : bool bHasNodataValue = false;
12572 121 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12573 121 : if (pbHasNoDataValue)
12574 121 : *pbHasNoDataValue = bHasNodataValue;
12575 121 : return ret;
12576 : }
12577 :
12578 : /************************************************************************/
12579 : /* GDALMDArrayGetNoDataValueAsInt64() */
12580 : /************************************************************************/
12581 :
12582 : /** Return the nodata value as a Int64.
12583 : *
12584 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12585 : *
12586 : * @param hArray Array handle.
12587 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12588 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12589 : *
12590 : * @return the nodata value as a Int64.
12591 : * @since GDAL 3.5
12592 : */
12593 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12594 : int *pbHasNoDataValue)
12595 : {
12596 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12597 11 : bool bHasNodataValue = false;
12598 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12599 11 : if (pbHasNoDataValue)
12600 11 : *pbHasNoDataValue = bHasNodataValue;
12601 11 : return ret;
12602 : }
12603 :
12604 : /************************************************************************/
12605 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12606 : /************************************************************************/
12607 :
12608 : /** Return the nodata value as a UInt64.
12609 : *
12610 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12611 : *
12612 : * @param hArray Array handle.
12613 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12614 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12615 : *
12616 : * @return the nodata value as a UInt64.
12617 : * @since GDAL 3.5
12618 : */
12619 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12620 : int *pbHasNoDataValue)
12621 : {
12622 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12623 7 : bool bHasNodataValue = false;
12624 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12625 7 : if (pbHasNoDataValue)
12626 7 : *pbHasNoDataValue = bHasNodataValue;
12627 7 : return ret;
12628 : }
12629 :
12630 : /************************************************************************/
12631 : /* GDALMDArraySetRawNoDataValue() */
12632 : /************************************************************************/
12633 :
12634 : /** Set the nodata value as a "raw" value.
12635 : *
12636 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12637 : * void*).
12638 : *
12639 : * @return TRUE in case of success.
12640 : */
12641 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12642 : {
12643 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12644 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12645 : }
12646 :
12647 : /************************************************************************/
12648 : /* GDALMDArraySetNoDataValueAsDouble() */
12649 : /************************************************************************/
12650 :
12651 : /** Set the nodata value as a double.
12652 : *
12653 : * If the natural data type of the attribute/array is not double, type
12654 : * conversion will occur to the type returned by GetDataType().
12655 : *
12656 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12657 : *
12658 : * @return TRUE in case of success.
12659 : */
12660 55 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12661 : {
12662 55 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12663 55 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12664 : }
12665 :
12666 : /************************************************************************/
12667 : /* GDALMDArraySetNoDataValueAsInt64() */
12668 : /************************************************************************/
12669 :
12670 : /** Set the nodata value as a Int64.
12671 : *
12672 : * If the natural data type of the attribute/array is not Int64, type conversion
12673 : * will occur to the type returned by GetDataType().
12674 : *
12675 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12676 : *
12677 : * @return TRUE in case of success.
12678 : * @since GDAL 3.5
12679 : */
12680 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12681 : {
12682 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12683 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12684 : }
12685 :
12686 : /************************************************************************/
12687 : /* GDALMDArraySetNoDataValueAsUInt64() */
12688 : /************************************************************************/
12689 :
12690 : /** Set the nodata value as a UInt64.
12691 : *
12692 : * If the natural data type of the attribute/array is not UInt64, type
12693 : * conversion will occur to the type returned by GetDataType().
12694 : *
12695 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12696 : *
12697 : * @return TRUE in case of success.
12698 : * @since GDAL 3.5
12699 : */
12700 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12701 : uint64_t nNoDataValue)
12702 : {
12703 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12704 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12705 : }
12706 :
12707 : /************************************************************************/
12708 : /* GDALMDArrayResize() */
12709 : /************************************************************************/
12710 :
12711 : /** Resize an array to new dimensions.
12712 : *
12713 : * Not all drivers may allow this operation, and with restrictions (e.g.
12714 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12715 : *
12716 : * Resizing a dimension used in other arrays will cause those other arrays
12717 : * to be resized.
12718 : *
12719 : * This is the same as the C++ method GDALMDArray::Resize().
12720 : *
12721 : * @param hArray Array.
12722 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12723 : * new size of each indexing dimension.
12724 : * @param papszOptions Options. (Driver specific)
12725 : * @return true in case of success.
12726 : * @since GDAL 3.7
12727 : */
12728 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12729 : CSLConstList papszOptions)
12730 : {
12731 42 : VALIDATE_POINTER1(hArray, __func__, false);
12732 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12733 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12734 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12735 : {
12736 83 : anNewDimSizes[i] = panNewDimSizes[i];
12737 : }
12738 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12739 : }
12740 :
12741 : /************************************************************************/
12742 : /* GDALMDArraySetScale() */
12743 : /************************************************************************/
12744 :
12745 : /** Set the scale value to apply to raw values.
12746 : *
12747 : * unscaled_value = raw_value * GetScale() + GetOffset()
12748 : *
12749 : * This is the same as the C++ method GDALMDArray::SetScale().
12750 : *
12751 : * @return TRUE in case of success.
12752 : */
12753 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12754 : {
12755 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12756 0 : return hArray->m_poImpl->SetScale(dfScale);
12757 : }
12758 :
12759 : /************************************************************************/
12760 : /* GDALMDArraySetScaleEx() */
12761 : /************************************************************************/
12762 :
12763 : /** Set the scale value to apply to raw values.
12764 : *
12765 : * unscaled_value = raw_value * GetScale() + GetOffset()
12766 : *
12767 : * This is the same as the C++ method GDALMDArray::SetScale().
12768 : *
12769 : * @return TRUE in case of success.
12770 : * @since GDAL 3.3
12771 : */
12772 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12773 : GDALDataType eStorageType)
12774 : {
12775 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12776 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12777 : }
12778 :
12779 : /************************************************************************/
12780 : /* GDALMDArraySetOffset() */
12781 : /************************************************************************/
12782 :
12783 : /** Set the scale value to apply to raw values.
12784 : *
12785 : * unscaled_value = raw_value * GetScale() + GetOffset()
12786 : *
12787 : * This is the same as the C++ method GDALMDArray::SetOffset().
12788 : *
12789 : * @return TRUE in case of success.
12790 : */
12791 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12792 : {
12793 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12794 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12795 : }
12796 :
12797 : /************************************************************************/
12798 : /* GDALMDArraySetOffsetEx() */
12799 : /************************************************************************/
12800 :
12801 : /** Set the scale value to apply to raw values.
12802 : *
12803 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12804 : *
12805 : * This is the same as the C++ method GDALMDArray::SetOffset().
12806 : *
12807 : * @return TRUE in case of success.
12808 : * @since GDAL 3.3
12809 : */
12810 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12811 : GDALDataType eStorageType)
12812 : {
12813 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12814 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12815 : }
12816 :
12817 : /************************************************************************/
12818 : /* GDALMDArrayGetScale() */
12819 : /************************************************************************/
12820 :
12821 : /** Get the scale value to apply to raw values.
12822 : *
12823 : * unscaled_value = raw_value * GetScale() + GetOffset()
12824 : *
12825 : * This is the same as the C++ method GDALMDArray::GetScale().
12826 : *
12827 : * @return the scale value
12828 : */
12829 105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12830 : {
12831 105 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12832 105 : bool bHasValue = false;
12833 105 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12834 105 : if (pbHasValue)
12835 105 : *pbHasValue = bHasValue;
12836 105 : return dfRet;
12837 : }
12838 :
12839 : /************************************************************************/
12840 : /* GDALMDArrayGetScaleEx() */
12841 : /************************************************************************/
12842 :
12843 : /** Get the scale value to apply to raw values.
12844 : *
12845 : * unscaled_value = raw_value * GetScale() + GetScale()
12846 : *
12847 : * This is the same as the C++ method GDALMDArray::GetScale().
12848 : *
12849 : * @return the scale value
12850 : * @since GDAL 3.3
12851 : */
12852 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12853 : GDALDataType *peStorageType)
12854 : {
12855 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12856 5 : bool bHasValue = false;
12857 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12858 5 : if (pbHasValue)
12859 5 : *pbHasValue = bHasValue;
12860 5 : return dfRet;
12861 : }
12862 :
12863 : /************************************************************************/
12864 : /* GDALMDArrayGetOffset() */
12865 : /************************************************************************/
12866 :
12867 : /** Get the scale value to apply to raw values.
12868 : *
12869 : * unscaled_value = raw_value * GetScale() + GetOffset()
12870 : *
12871 : * This is the same as the C++ method GDALMDArray::GetOffset().
12872 : *
12873 : * @return the scale value
12874 : */
12875 102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12876 : {
12877 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12878 102 : bool bHasValue = false;
12879 102 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12880 102 : if (pbHasValue)
12881 102 : *pbHasValue = bHasValue;
12882 102 : return dfRet;
12883 : }
12884 :
12885 : /************************************************************************/
12886 : /* GDALMDArrayGetOffsetEx() */
12887 : /************************************************************************/
12888 :
12889 : /** Get the scale value to apply to raw values.
12890 : *
12891 : * unscaled_value = raw_value * GetScale() + GetOffset()
12892 : *
12893 : * This is the same as the C++ method GDALMDArray::GetOffset().
12894 : *
12895 : * @return the scale value
12896 : * @since GDAL 3.3
12897 : */
12898 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12899 : GDALDataType *peStorageType)
12900 : {
12901 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12902 5 : bool bHasValue = false;
12903 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12904 5 : if (pbHasValue)
12905 5 : *pbHasValue = bHasValue;
12906 5 : return dfRet;
12907 : }
12908 :
12909 : /************************************************************************/
12910 : /* GDALMDArrayGetBlockSize() */
12911 : /************************************************************************/
12912 :
12913 : /** Return the "natural" block size of the array along all dimensions.
12914 : *
12915 : * Some drivers might organize the array in tiles/blocks and reading/writing
12916 : * aligned on those tile/block boundaries will be more efficient.
12917 : *
12918 : * The returned number of elements in the vector is the same as
12919 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12920 : * the natural block size along the considered dimension.
12921 : * "Flat" arrays will typically return a vector of values set to 0.
12922 : *
12923 : * The default implementation will return a vector of values set to 0.
12924 : *
12925 : * This method is used by GetProcessingChunkSize().
12926 : *
12927 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12928 : * theoretical case of a 32-bit platform, this might exceed its size_t
12929 : * allocation capabilities.
12930 : *
12931 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12932 : *
12933 : * @return the block size, in number of elements along each dimension.
12934 : */
12935 98 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12936 : {
12937 98 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12938 98 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12939 98 : auto res = hArray->m_poImpl->GetBlockSize();
12940 98 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12941 301 : for (size_t i = 0; i < res.size(); i++)
12942 : {
12943 203 : ret[i] = res[i];
12944 : }
12945 98 : *pnCount = res.size();
12946 98 : return ret;
12947 : }
12948 :
12949 : /***********************************************************************/
12950 : /* GDALMDArrayGetProcessingChunkSize() */
12951 : /************************************************************************/
12952 :
12953 : /** \brief Return an optimal chunk size for read/write operations, given the
12954 : * natural block size and memory constraints specified.
12955 : *
12956 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12957 : * multiple of those returned by GetBlockSize() (unless the block define by
12958 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12959 : * returned by this method).
12960 : *
12961 : * This is the same as the C++ method
12962 : * GDALAbstractMDArray::GetProcessingChunkSize().
12963 : *
12964 : * @param hArray Array.
12965 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12966 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12967 : * chunk.
12968 : *
12969 : * @return the chunk size, in number of elements along each dimension.
12970 : */
12971 :
12972 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12973 : size_t nMaxChunkMemory)
12974 : {
12975 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12976 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12977 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12978 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12979 3 : for (size_t i = 0; i < res.size(); i++)
12980 : {
12981 2 : ret[i] = res[i];
12982 : }
12983 1 : *pnCount = res.size();
12984 1 : return ret;
12985 : }
12986 :
12987 : /************************************************************************/
12988 : /* GDALMDArrayGetStructuralInfo() */
12989 : /************************************************************************/
12990 :
12991 : /** Return structural information on the array.
12992 : *
12993 : * This may be the compression, etc..
12994 : *
12995 : * The return value should not be freed and is valid until GDALMDArray is
12996 : * released or this function called again.
12997 : *
12998 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12999 : */
13000 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13001 : {
13002 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13003 15 : return hArray->m_poImpl->GetStructuralInfo();
13004 : }
13005 :
13006 : /************************************************************************/
13007 : /* GDALMDArrayGetView() */
13008 : /************************************************************************/
13009 :
13010 : /** Return a view of the array using slicing or field access.
13011 : *
13012 : * The returned object should be released with GDALMDArrayRelease().
13013 : *
13014 : * This is the same as the C++ method GDALMDArray::GetView().
13015 : */
13016 433 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13017 : {
13018 433 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13019 433 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13020 1299 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13021 433 : if (!sliced)
13022 22 : return nullptr;
13023 411 : return new GDALMDArrayHS(sliced);
13024 : }
13025 :
13026 : /************************************************************************/
13027 : /* GDALMDArrayTranspose() */
13028 : /************************************************************************/
13029 :
13030 : /** Return a view of the array whose axis have been reordered.
13031 : *
13032 : * The returned object should be released with GDALMDArrayRelease().
13033 : *
13034 : * This is the same as the C++ method GDALMDArray::Transpose().
13035 : */
13036 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13037 : const int *panMapNewAxisToOldAxis)
13038 : {
13039 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13040 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13041 44 : if (nNewAxisCount)
13042 : {
13043 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13044 : nNewAxisCount * sizeof(int));
13045 : }
13046 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13047 44 : if (!reordered)
13048 7 : return nullptr;
13049 37 : return new GDALMDArrayHS(reordered);
13050 : }
13051 :
13052 : /************************************************************************/
13053 : /* GDALMDArrayGetUnscaled() */
13054 : /************************************************************************/
13055 :
13056 : /** Return an array that is the unscaled version of the current one.
13057 : *
13058 : * That is each value of the unscaled array will be
13059 : * unscaled_value = raw_value * GetScale() + GetOffset()
13060 : *
13061 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
13062 : * from unscaled values to raw values.
13063 : *
13064 : * The returned object should be released with GDALMDArrayRelease().
13065 : *
13066 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
13067 : */
13068 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13069 : {
13070 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13071 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
13072 13 : if (!unscaled)
13073 0 : return nullptr;
13074 13 : return new GDALMDArrayHS(unscaled);
13075 : }
13076 :
13077 : /************************************************************************/
13078 : /* GDALMDArrayGetMask() */
13079 : /************************************************************************/
13080 :
13081 : /** Return an array that is a mask for the current array
13082 : *
13083 : * This array will be of type Byte, with values set to 0 to indicate invalid
13084 : * pixels of the current array, and values set to 1 to indicate valid pixels.
13085 : *
13086 : * The returned object should be released with GDALMDArrayRelease().
13087 : *
13088 : * This is the same as the C++ method GDALMDArray::GetMask().
13089 : */
13090 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13091 : {
13092 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13093 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13094 35 : if (!unscaled)
13095 7 : return nullptr;
13096 28 : return new GDALMDArrayHS(unscaled);
13097 : }
13098 :
13099 : /************************************************************************/
13100 : /* GDALMDArrayGetResampled() */
13101 : /************************************************************************/
13102 :
13103 : /** Return an array that is a resampled / reprojected view of the current array
13104 : *
13105 : * This is the same as the C++ method GDALMDArray::GetResampled().
13106 : *
13107 : * Currently this method can only resample along the last 2 dimensions, unless
13108 : * orthorectifying a NASA EMIT dataset.
13109 : *
13110 : * The returned object should be released with GDALMDArrayRelease().
13111 : *
13112 : * @since 3.4
13113 : */
13114 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13115 : const GDALDimensionH *pahNewDims,
13116 : GDALRIOResampleAlg resampleAlg,
13117 : OGRSpatialReferenceH hTargetSRS,
13118 : CSLConstList papszOptions)
13119 : {
13120 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13121 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13122 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13123 112 : for (size_t i = 0; i < nNewDimCount; ++i)
13124 : {
13125 78 : if (pahNewDims[i])
13126 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
13127 : }
13128 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
13129 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13130 68 : papszOptions);
13131 34 : if (!poNewArray)
13132 8 : return nullptr;
13133 26 : return new GDALMDArrayHS(poNewArray);
13134 : }
13135 :
13136 : /************************************************************************/
13137 : /* GDALMDArraySetUnit() */
13138 : /************************************************************************/
13139 :
13140 : /** Set the variable unit.
13141 : *
13142 : * Values should conform as much as possible with those allowed by
13143 : * the NetCDF CF conventions:
13144 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13145 : * but others might be returned.
13146 : *
13147 : * Few examples are "meter", "degrees", "second", ...
13148 : * Empty value means unknown.
13149 : *
13150 : * This is the same as the C function GDALMDArraySetUnit()
13151 : *
13152 : * @param hArray array.
13153 : * @param pszUnit unit name.
13154 : * @return TRUE in case of success.
13155 : */
13156 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13157 : {
13158 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13159 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13160 : }
13161 :
13162 : /************************************************************************/
13163 : /* GDALMDArrayGetUnit() */
13164 : /************************************************************************/
13165 :
13166 : /** Return the array unit.
13167 : *
13168 : * Values should conform as much as possible with those allowed by
13169 : * the NetCDF CF conventions:
13170 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13171 : * but others might be returned.
13172 : *
13173 : * Few examples are "meter", "degrees", "second", ...
13174 : * Empty value means unknown.
13175 : *
13176 : * The return value should not be freed and is valid until GDALMDArray is
13177 : * released or this function called again.
13178 : *
13179 : * This is the same as the C++ method GDALMDArray::GetUnit().
13180 : */
13181 113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13182 : {
13183 113 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13184 113 : return hArray->m_poImpl->GetUnit().c_str();
13185 : }
13186 :
13187 : /************************************************************************/
13188 : /* GDALMDArrayGetSpatialRef() */
13189 : /************************************************************************/
13190 :
13191 : /** Assign a spatial reference system object to the array.
13192 : *
13193 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13194 : * @return TRUE in case of success.
13195 : */
13196 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13197 : {
13198 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13199 60 : return hArray->m_poImpl->SetSpatialRef(
13200 60 : OGRSpatialReference::FromHandle(hSRS));
13201 : }
13202 :
13203 : /************************************************************************/
13204 : /* GDALMDArrayGetSpatialRef() */
13205 : /************************************************************************/
13206 :
13207 : /** Return the spatial reference system object associated with the array.
13208 : *
13209 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13210 : *
13211 : * The returned object must be freed with OSRDestroySpatialReference().
13212 : */
13213 81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13214 : {
13215 81 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13216 81 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
13217 81 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13218 : }
13219 :
13220 : /************************************************************************/
13221 : /* GDALMDArrayGetStatistics() */
13222 : /************************************************************************/
13223 :
13224 : /**
13225 : * \brief Fetch statistics.
13226 : *
13227 : * This is the same as the C++ method GDALMDArray::GetStatistics().
13228 : *
13229 : * @since GDAL 3.2
13230 : */
13231 :
13232 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13233 : int bApproxOK, int bForce, double *pdfMin,
13234 : double *pdfMax, double *pdfMean,
13235 : double *pdfStdDev, GUInt64 *pnValidCount,
13236 : GDALProgressFunc pfnProgress,
13237 : void *pProgressData)
13238 : {
13239 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13240 30 : return hArray->m_poImpl->GetStatistics(
13241 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13242 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13243 : }
13244 :
13245 : /************************************************************************/
13246 : /* GDALMDArrayComputeStatistics() */
13247 : /************************************************************************/
13248 :
13249 : /**
13250 : * \brief Compute statistics.
13251 : *
13252 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13253 : *
13254 : * @since GDAL 3.2
13255 : * @see GDALMDArrayComputeStatisticsEx()
13256 : */
13257 :
13258 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13259 : int bApproxOK, double *pdfMin, double *pdfMax,
13260 : double *pdfMean, double *pdfStdDev,
13261 : GUInt64 *pnValidCount,
13262 : GDALProgressFunc pfnProgress,
13263 : void *pProgressData)
13264 : {
13265 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13266 0 : return hArray->m_poImpl->ComputeStatistics(
13267 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13268 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
13269 : }
13270 :
13271 : /************************************************************************/
13272 : /* GDALMDArrayComputeStatisticsEx() */
13273 : /************************************************************************/
13274 :
13275 : /**
13276 : * \brief Compute statistics.
13277 : *
13278 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13279 : *
13280 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13281 : *
13282 : * @since GDAL 3.8
13283 : */
13284 :
13285 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13286 : int bApproxOK, double *pdfMin,
13287 : double *pdfMax, double *pdfMean,
13288 : double *pdfStdDev, GUInt64 *pnValidCount,
13289 : GDALProgressFunc pfnProgress,
13290 : void *pProgressData,
13291 : CSLConstList papszOptions)
13292 : {
13293 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13294 8 : return hArray->m_poImpl->ComputeStatistics(
13295 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13296 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
13297 : }
13298 :
13299 : /************************************************************************/
13300 : /* GDALMDArrayGetCoordinateVariables() */
13301 : /************************************************************************/
13302 :
13303 : /** Return coordinate variables.
13304 : *
13305 : * The returned array must be freed with GDALReleaseArrays(). If only the array
13306 : * itself needs to be freed, CPLFree() should be called (and
13307 : * GDALMDArrayRelease() on individual array members).
13308 : *
13309 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13310 : *
13311 : * @param hArray Array.
13312 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13313 : *
13314 : * @return an array of *pnCount arrays.
13315 : * @since 3.4
13316 : */
13317 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13318 : size_t *pnCount)
13319 : {
13320 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13321 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13322 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13323 : auto ret = static_cast<GDALMDArrayH *>(
13324 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13325 29 : for (size_t i = 0; i < coordinates.size(); i++)
13326 : {
13327 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
13328 : }
13329 13 : *pnCount = coordinates.size();
13330 13 : return ret;
13331 : }
13332 :
13333 : /************************************************************************/
13334 : /* GDALMDArrayGetGridded() */
13335 : /************************************************************************/
13336 :
13337 : /** Return a gridded array from scattered point data, that is from an array
13338 : * whose last dimension is the indexing variable of X and Y arrays.
13339 : *
13340 : * The returned object should be released with GDALMDArrayRelease().
13341 : *
13342 : * This is the same as the C++ method GDALMDArray::GetGridded().
13343 : *
13344 : * @since GDAL 3.7
13345 : */
13346 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13347 : const char *pszGridOptions,
13348 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13349 : CSLConstList papszOptions)
13350 : {
13351 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13352 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13353 22 : auto gridded = hArray->m_poImpl->GetGridded(
13354 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13355 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13356 22 : if (!gridded)
13357 19 : return nullptr;
13358 3 : return new GDALMDArrayHS(gridded);
13359 : }
13360 :
13361 : /************************************************************************/
13362 : /* GDALMDArrayGetMeshGrid() */
13363 : /************************************************************************/
13364 :
13365 : /** Return a list of multidimensional arrays from a list of one-dimensional
13366 : * arrays.
13367 : *
13368 : * This is typically used to transform one-dimensional longitude, latitude
13369 : * arrays into 2D ones.
13370 : *
13371 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13372 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13373 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13374 : * repeated to fill the matrix along the first dimension for x1, the second
13375 : * for x2 and so on.
13376 : *
13377 : * For example, if x = [1, 2], and y = [3, 4, 5],
13378 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13379 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13380 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13381 : *
13382 : * and
13383 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13384 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13385 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13386 : *
13387 : * The currently supported options are:
13388 : * <ul>
13389 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13390 : * output.
13391 : * </li>
13392 : * </ul>
13393 : *
13394 : * This is the same as
13395 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13396 : * function.
13397 : *
13398 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13399 : * If only the array itself needs to be freed, CPLFree() should be called
13400 : * (and GDALMDArrayRelease() on individual array members).
13401 : *
13402 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13403 : *
13404 : * @param pahInputArrays Input arrays
13405 : * @param nCountInputArrays Number of input arrays
13406 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13407 : * @param papszOptions NULL, or NULL terminated list of options.
13408 : *
13409 : * @return an array of *pnCountOutputArrays arrays.
13410 : * @since 3.10
13411 : */
13412 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13413 : size_t nCountInputArrays,
13414 : size_t *pnCountOutputArrays,
13415 : CSLConstList papszOptions)
13416 : {
13417 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13418 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13419 :
13420 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13421 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13422 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13423 :
13424 : const auto apoOutputArrays =
13425 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13426 : auto ret = static_cast<GDALMDArrayH *>(
13427 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13428 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13429 : {
13430 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13431 : }
13432 7 : *pnCountOutputArrays = apoOutputArrays.size();
13433 7 : return ret;
13434 : }
13435 :
13436 : /************************************************************************/
13437 : /* GDALReleaseArrays() */
13438 : /************************************************************************/
13439 :
13440 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13441 : *
13442 : * @param arrays return pointer of above methods
13443 : * @param nCount *pnCount value returned by above methods
13444 : */
13445 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13446 : {
13447 46 : for (size_t i = 0; i < nCount; i++)
13448 : {
13449 26 : delete arrays[i];
13450 : }
13451 20 : CPLFree(arrays);
13452 20 : }
13453 :
13454 : /************************************************************************/
13455 : /* GDALMDArrayCache() */
13456 : /************************************************************************/
13457 :
13458 : /**
13459 : * \brief Cache the content of the array into an auxiliary filename.
13460 : *
13461 : * This is the same as the C++ method GDALMDArray::Cache().
13462 : *
13463 : * @since GDAL 3.4
13464 : */
13465 :
13466 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13467 : {
13468 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13469 7 : return hArray->m_poImpl->Cache(papszOptions);
13470 : }
13471 :
13472 : /************************************************************************/
13473 : /* GDALMDArrayRename() */
13474 : /************************************************************************/
13475 :
13476 : /** Rename the array.
13477 : *
13478 : * This is not implemented by all drivers.
13479 : *
13480 : * Drivers known to implement it: MEM, netCDF, Zarr.
13481 : *
13482 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13483 : *
13484 : * @return true in case of success
13485 : * @since GDAL 3.8
13486 : */
13487 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13488 : {
13489 28 : VALIDATE_POINTER1(hArray, __func__, false);
13490 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13491 28 : return hArray->m_poImpl->Rename(pszNewName);
13492 : }
13493 :
13494 : /************************************************************************/
13495 : /* GDALAttributeRelease() */
13496 : /************************************************************************/
13497 :
13498 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13499 : *
13500 : * Note: when applied on a object coming from a driver, this does not
13501 : * destroy the object in the file, database, etc...
13502 : */
13503 765 : void GDALAttributeRelease(GDALAttributeH hAttr)
13504 : {
13505 765 : delete hAttr;
13506 765 : }
13507 :
13508 : /************************************************************************/
13509 : /* GDALAttributeGetName() */
13510 : /************************************************************************/
13511 :
13512 : /** Return the name of the attribute.
13513 : *
13514 : * The returned pointer is valid until hAttr is released.
13515 : *
13516 : * This is the same as the C++ method GDALAttribute::GetName().
13517 : */
13518 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13519 : {
13520 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13521 361 : return hAttr->m_poImpl->GetName().c_str();
13522 : }
13523 :
13524 : /************************************************************************/
13525 : /* GDALAttributeGetFullName() */
13526 : /************************************************************************/
13527 :
13528 : /** Return the full name of the attribute.
13529 : *
13530 : * The returned pointer is valid until hAttr is released.
13531 : *
13532 : * This is the same as the C++ method GDALAttribute::GetFullName().
13533 : */
13534 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13535 : {
13536 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13537 49 : return hAttr->m_poImpl->GetFullName().c_str();
13538 : }
13539 :
13540 : /************************************************************************/
13541 : /* GDALAttributeGetTotalElementsCount() */
13542 : /************************************************************************/
13543 :
13544 : /** Return the total number of values in the attribute.
13545 : *
13546 : * This is the same as the C++ method
13547 : * GDALAbstractMDArray::GetTotalElementsCount()
13548 : */
13549 177 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13550 : {
13551 177 : VALIDATE_POINTER1(hAttr, __func__, 0);
13552 177 : return hAttr->m_poImpl->GetTotalElementsCount();
13553 : }
13554 :
13555 : /************************************************************************/
13556 : /* GDALAttributeGetDimensionCount() */
13557 : /************************************************************************/
13558 :
13559 : /** Return the number of dimensions.
13560 : *
13561 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13562 : */
13563 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13564 : {
13565 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13566 12 : return hAttr->m_poImpl->GetDimensionCount();
13567 : }
13568 :
13569 : /************************************************************************/
13570 : /* GDALAttributeGetDimensionsSize() */
13571 : /************************************************************************/
13572 :
13573 : /** Return the dimension sizes of the attribute.
13574 : *
13575 : * The returned array must be freed with CPLFree()
13576 : *
13577 : * @param hAttr Attribute.
13578 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13579 : *
13580 : * @return an array of *pnCount values.
13581 : */
13582 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13583 : {
13584 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13585 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13586 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13587 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13588 22 : for (size_t i = 0; i < dims.size(); i++)
13589 : {
13590 11 : ret[i] = dims[i]->GetSize();
13591 : }
13592 11 : *pnCount = dims.size();
13593 11 : return ret;
13594 : }
13595 :
13596 : /************************************************************************/
13597 : /* GDALAttributeGetDataType() */
13598 : /************************************************************************/
13599 :
13600 : /** Return the data type
13601 : *
13602 : * The return must be freed with GDALExtendedDataTypeRelease().
13603 : */
13604 434 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13605 : {
13606 434 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13607 : return new GDALExtendedDataTypeHS(
13608 434 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13609 : }
13610 :
13611 : /************************************************************************/
13612 : /* GDALAttributeReadAsRaw() */
13613 : /************************************************************************/
13614 :
13615 : /** Return the raw value of an attribute.
13616 : *
13617 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13618 : *
13619 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13620 : *
13621 : * @param hAttr Attribute.
13622 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13623 : *
13624 : * @return a buffer of *pnSize bytes.
13625 : */
13626 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13627 : {
13628 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13629 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13630 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13631 6 : *pnSize = res.size();
13632 6 : auto ret = res.StealData();
13633 6 : if (!ret)
13634 : {
13635 0 : *pnSize = 0;
13636 0 : return nullptr;
13637 : }
13638 6 : return ret;
13639 : }
13640 :
13641 : /************************************************************************/
13642 : /* GDALAttributeFreeRawResult() */
13643 : /************************************************************************/
13644 :
13645 : /** Free the return of GDALAttributeAsRaw()
13646 : */
13647 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13648 : CPL_UNUSED size_t nSize)
13649 : {
13650 6 : VALIDATE_POINTER0(hAttr, __func__);
13651 6 : if (raw)
13652 : {
13653 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13654 6 : const auto nDTSize(dt.GetSize());
13655 6 : GByte *pabyPtr = raw;
13656 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13657 6 : CPLAssert(nSize == nDTSize * nEltCount);
13658 12 : for (size_t i = 0; i < nEltCount; ++i)
13659 : {
13660 6 : dt.FreeDynamicMemory(pabyPtr);
13661 6 : pabyPtr += nDTSize;
13662 : }
13663 6 : CPLFree(raw);
13664 : }
13665 : }
13666 :
13667 : /************************************************************************/
13668 : /* GDALAttributeReadAsString() */
13669 : /************************************************************************/
13670 :
13671 : /** Return the value of an attribute as a string.
13672 : *
13673 : * The returned string should not be freed, and its lifetime does not
13674 : * excess a next call to ReadAsString() on the same object, or the deletion
13675 : * of the object itself.
13676 : *
13677 : * This function will only return the first element if there are several.
13678 : *
13679 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13680 : *
13681 : * @return a string, or nullptr.
13682 : */
13683 108 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13684 : {
13685 108 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13686 108 : return hAttr->m_poImpl->ReadAsString();
13687 : }
13688 :
13689 : /************************************************************************/
13690 : /* GDALAttributeReadAsInt() */
13691 : /************************************************************************/
13692 :
13693 : /** Return the value of an attribute as a integer.
13694 : *
13695 : * This function will only return the first element if there are several.
13696 : *
13697 : * It can fail if its value can not be converted to integer.
13698 : *
13699 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13700 : *
13701 : * @return a integer, or INT_MIN in case of error.
13702 : */
13703 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13704 : {
13705 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13706 22 : return hAttr->m_poImpl->ReadAsInt();
13707 : }
13708 :
13709 : /************************************************************************/
13710 : /* GDALAttributeReadAsInt64() */
13711 : /************************************************************************/
13712 :
13713 : /** Return the value of an attribute as a int64_t.
13714 : *
13715 : * This function will only return the first element if there are several.
13716 : *
13717 : * It can fail if its value can not be converted to integer.
13718 : *
13719 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13720 : *
13721 : * @return an int64_t, or INT64_MIN in case of error.
13722 : */
13723 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13724 : {
13725 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13726 15 : return hAttr->m_poImpl->ReadAsInt64();
13727 : }
13728 :
13729 : /************************************************************************/
13730 : /* GDALAttributeReadAsDouble() */
13731 : /************************************************************************/
13732 :
13733 : /** Return the value of an attribute as a double.
13734 : *
13735 : * This function will only return the first element if there are several.
13736 : *
13737 : * It can fail if its value can not be converted to double.
13738 : *
13739 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13740 : *
13741 : * @return a double value.
13742 : */
13743 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13744 : {
13745 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13746 40 : return hAttr->m_poImpl->ReadAsDouble();
13747 : }
13748 :
13749 : /************************************************************************/
13750 : /* GDALAttributeReadAsStringArray() */
13751 : /************************************************************************/
13752 :
13753 : /** Return the value of an attribute as an array of strings.
13754 : *
13755 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13756 : *
13757 : * The return value must be freed with CSLDestroy().
13758 : */
13759 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13760 : {
13761 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13762 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13763 : }
13764 :
13765 : /************************************************************************/
13766 : /* GDALAttributeReadAsIntArray() */
13767 : /************************************************************************/
13768 :
13769 : /** Return the value of an attribute as an array of integers.
13770 : *
13771 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13772 : *
13773 : * @param hAttr Attribute
13774 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13775 : * @return array to be freed with CPLFree(), or nullptr.
13776 : */
13777 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13778 : {
13779 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13780 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13781 15 : *pnCount = 0;
13782 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13783 15 : if (tmp.empty())
13784 0 : return nullptr;
13785 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13786 15 : if (!ret)
13787 0 : return nullptr;
13788 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13789 15 : *pnCount = tmp.size();
13790 15 : return ret;
13791 : }
13792 :
13793 : /************************************************************************/
13794 : /* GDALAttributeReadAsInt64Array() */
13795 : /************************************************************************/
13796 :
13797 : /** Return the value of an attribute as an array of int64_t.
13798 : *
13799 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13800 : *
13801 : * @param hAttr Attribute
13802 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13803 : * @return array to be freed with CPLFree(), or nullptr.
13804 : */
13805 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13806 : {
13807 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13808 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13809 14 : *pnCount = 0;
13810 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13811 14 : if (tmp.empty())
13812 0 : return nullptr;
13813 : auto ret = static_cast<int64_t *>(
13814 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13815 14 : if (!ret)
13816 0 : return nullptr;
13817 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13818 14 : *pnCount = tmp.size();
13819 14 : return ret;
13820 : }
13821 :
13822 : /************************************************************************/
13823 : /* GDALAttributeReadAsDoubleArray() */
13824 : /************************************************************************/
13825 :
13826 : /** Return the value of an attribute as an array of doubles.
13827 : *
13828 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13829 : *
13830 : * @param hAttr Attribute
13831 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13832 : * @return array to be freed with CPLFree(), or nullptr.
13833 : */
13834 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13835 : {
13836 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13837 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13838 29 : *pnCount = 0;
13839 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13840 29 : if (tmp.empty())
13841 0 : return nullptr;
13842 : auto ret =
13843 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13844 29 : if (!ret)
13845 0 : return nullptr;
13846 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13847 29 : *pnCount = tmp.size();
13848 29 : return ret;
13849 : }
13850 :
13851 : /************************************************************************/
13852 : /* GDALAttributeWriteRaw() */
13853 : /************************************************************************/
13854 :
13855 : /** Write an attribute from raw values expressed in GetDataType()
13856 : *
13857 : * The values should be provided in the type of GetDataType() and there should
13858 : * be exactly GetTotalElementsCount() of them.
13859 : * If GetDataType() is a string, each value should be a char* pointer.
13860 : *
13861 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13862 : *
13863 : * @param hAttr Attribute
13864 : * @param pabyValue Buffer of nLen bytes.
13865 : * @param nLength Size of pabyValue in bytes. Should be equal to
13866 : * GetTotalElementsCount() * GetDataType().GetSize()
13867 : * @return TRUE in case of success.
13868 : */
13869 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13870 : size_t nLength)
13871 : {
13872 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13873 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13874 : }
13875 :
13876 : /************************************************************************/
13877 : /* GDALAttributeWriteString() */
13878 : /************************************************************************/
13879 :
13880 : /** Write an attribute from a string value.
13881 : *
13882 : * Type conversion will be performed if needed. If the attribute contains
13883 : * multiple values, only the first one will be updated.
13884 : *
13885 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13886 : *
13887 : * @param hAttr Attribute
13888 : * @param pszVal Pointer to a string.
13889 : * @return TRUE in case of success.
13890 : */
13891 207 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13892 : {
13893 207 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13894 207 : return hAttr->m_poImpl->Write(pszVal);
13895 : }
13896 :
13897 : /************************************************************************/
13898 : /* GDALAttributeWriteInt() */
13899 : /************************************************************************/
13900 :
13901 : /** Write an attribute from a integer value.
13902 : *
13903 : * Type conversion will be performed if needed. If the attribute contains
13904 : * multiple values, only the first one will be updated.
13905 : *
13906 : * This is the same as the C++ method GDALAttribute::WriteInt()
13907 : *
13908 : * @param hAttr Attribute
13909 : * @param nVal Value.
13910 : * @return TRUE in case of success.
13911 : */
13912 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13913 : {
13914 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13915 22 : return hAttr->m_poImpl->WriteInt(nVal);
13916 : }
13917 :
13918 : /************************************************************************/
13919 : /* GDALAttributeWriteInt64() */
13920 : /************************************************************************/
13921 :
13922 : /** Write an attribute from an int64_t value.
13923 : *
13924 : * Type conversion will be performed if needed. If the attribute contains
13925 : * multiple values, only the first one will be updated.
13926 : *
13927 : * This is the same as the C++ method GDALAttribute::WriteLong()
13928 : *
13929 : * @param hAttr Attribute
13930 : * @param nVal Value.
13931 : * @return TRUE in case of success.
13932 : */
13933 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13934 : {
13935 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13936 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13937 : }
13938 :
13939 : /************************************************************************/
13940 : /* GDALAttributeWriteDouble() */
13941 : /************************************************************************/
13942 :
13943 : /** Write an attribute from a double value.
13944 : *
13945 : * Type conversion will be performed if needed. If the attribute contains
13946 : * multiple values, only the first one will be updated.
13947 : *
13948 : * This is the same as the C++ method GDALAttribute::Write(double);
13949 : *
13950 : * @param hAttr Attribute
13951 : * @param dfVal Value.
13952 : *
13953 : * @return TRUE in case of success.
13954 : */
13955 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13956 : {
13957 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13958 11 : return hAttr->m_poImpl->Write(dfVal);
13959 : }
13960 :
13961 : /************************************************************************/
13962 : /* GDALAttributeWriteStringArray() */
13963 : /************************************************************************/
13964 :
13965 : /** Write an attribute from an array of strings.
13966 : *
13967 : * Type conversion will be performed if needed.
13968 : *
13969 : * Exactly GetTotalElementsCount() strings must be provided
13970 : *
13971 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13972 : *
13973 : * @param hAttr Attribute
13974 : * @param papszValues Array of strings.
13975 : * @return TRUE in case of success.
13976 : */
13977 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13978 : CSLConstList papszValues)
13979 : {
13980 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13981 8 : return hAttr->m_poImpl->Write(papszValues);
13982 : }
13983 :
13984 : /************************************************************************/
13985 : /* GDALAttributeWriteIntArray() */
13986 : /************************************************************************/
13987 :
13988 : /** Write an attribute from an array of int.
13989 : *
13990 : * Type conversion will be performed if needed.
13991 : *
13992 : * Exactly GetTotalElementsCount() strings must be provided
13993 : *
13994 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13995 : * size_t)
13996 : *
13997 : * @param hAttr Attribute
13998 : * @param panValues Array of int.
13999 : * @param nCount Should be equal to GetTotalElementsCount().
14000 : * @return TRUE in case of success.
14001 : */
14002 11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14003 : size_t nCount)
14004 : {
14005 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14006 11 : return hAttr->m_poImpl->Write(panValues, nCount);
14007 : }
14008 :
14009 : /************************************************************************/
14010 : /* GDALAttributeWriteInt64Array() */
14011 : /************************************************************************/
14012 :
14013 : /** Write an attribute from an array of int64_t.
14014 : *
14015 : * Type conversion will be performed if needed.
14016 : *
14017 : * Exactly GetTotalElementsCount() strings must be provided
14018 : *
14019 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14020 : * size_t)
14021 : *
14022 : * @param hAttr Attribute
14023 : * @param panValues Array of int64_t.
14024 : * @param nCount Should be equal to GetTotalElementsCount().
14025 : * @return TRUE in case of success.
14026 : */
14027 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14028 : size_t nCount)
14029 : {
14030 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14031 10 : return hAttr->m_poImpl->Write(panValues, nCount);
14032 : }
14033 :
14034 : /************************************************************************/
14035 : /* GDALAttributeWriteDoubleArray() */
14036 : /************************************************************************/
14037 :
14038 : /** Write an attribute from an array of double.
14039 : *
14040 : * Type conversion will be performed if needed.
14041 : *
14042 : * Exactly GetTotalElementsCount() strings must be provided
14043 : *
14044 : * This is the same as the C++ method GDALAttribute::Write(const double *,
14045 : * size_t)
14046 : *
14047 : * @param hAttr Attribute
14048 : * @param padfValues Array of double.
14049 : * @param nCount Should be equal to GetTotalElementsCount().
14050 : * @return TRUE in case of success.
14051 : */
14052 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14053 : const double *padfValues, size_t nCount)
14054 : {
14055 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
14056 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
14057 : }
14058 :
14059 : /************************************************************************/
14060 : /* GDALAttributeRename() */
14061 : /************************************************************************/
14062 :
14063 : /** Rename the attribute.
14064 : *
14065 : * This is not implemented by all drivers.
14066 : *
14067 : * Drivers known to implement it: MEM, netCDF.
14068 : *
14069 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
14070 : *
14071 : * @return true in case of success
14072 : * @since GDAL 3.8
14073 : */
14074 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14075 : {
14076 27 : VALIDATE_POINTER1(hAttr, __func__, false);
14077 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
14078 27 : return hAttr->m_poImpl->Rename(pszNewName);
14079 : }
14080 :
14081 : /************************************************************************/
14082 : /* GDALDimensionRelease() */
14083 : /************************************************************************/
14084 :
14085 : /** Release the GDAL in-memory object associated with a GDALDimension.
14086 : *
14087 : * Note: when applied on a object coming from a driver, this does not
14088 : * destroy the object in the file, database, etc...
14089 : */
14090 5318 : void GDALDimensionRelease(GDALDimensionH hDim)
14091 : {
14092 5318 : delete hDim;
14093 5318 : }
14094 :
14095 : /************************************************************************/
14096 : /* GDALDimensionGetName() */
14097 : /************************************************************************/
14098 :
14099 : /** Return dimension name.
14100 : *
14101 : * This is the same as the C++ method GDALDimension::GetName()
14102 : */
14103 296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
14104 : {
14105 296 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14106 296 : return hDim->m_poImpl->GetName().c_str();
14107 : }
14108 :
14109 : /************************************************************************/
14110 : /* GDALDimensionGetFullName() */
14111 : /************************************************************************/
14112 :
14113 : /** Return dimension full name.
14114 : *
14115 : * This is the same as the C++ method GDALDimension::GetFullName()
14116 : */
14117 82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14118 : {
14119 82 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14120 82 : return hDim->m_poImpl->GetFullName().c_str();
14121 : }
14122 :
14123 : /************************************************************************/
14124 : /* GDALDimensionGetType() */
14125 : /************************************************************************/
14126 :
14127 : /** Return dimension type.
14128 : *
14129 : * This is the same as the C++ method GDALDimension::GetType()
14130 : */
14131 70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
14132 : {
14133 70 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14134 70 : return hDim->m_poImpl->GetType().c_str();
14135 : }
14136 :
14137 : /************************************************************************/
14138 : /* GDALDimensionGetDirection() */
14139 : /************************************************************************/
14140 :
14141 : /** Return dimension direction.
14142 : *
14143 : * This is the same as the C++ method GDALDimension::GetDirection()
14144 : */
14145 38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14146 : {
14147 38 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14148 38 : return hDim->m_poImpl->GetDirection().c_str();
14149 : }
14150 :
14151 : /************************************************************************/
14152 : /* GDALDimensionGetSize() */
14153 : /************************************************************************/
14154 :
14155 : /** Return the size, that is the number of values along the dimension.
14156 : *
14157 : * This is the same as the C++ method GDALDimension::GetSize()
14158 : */
14159 3958 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14160 : {
14161 3958 : VALIDATE_POINTER1(hDim, __func__, 0);
14162 3958 : return hDim->m_poImpl->GetSize();
14163 : }
14164 :
14165 : /************************************************************************/
14166 : /* GDALDimensionGetIndexingVariable() */
14167 : /************************************************************************/
14168 :
14169 : /** Return the variable that is used to index the dimension (if there is one).
14170 : *
14171 : * This is the array, typically one-dimensional, describing the values taken
14172 : * by the dimension.
14173 : *
14174 : * The returned value should be freed with GDALMDArrayRelease().
14175 : *
14176 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14177 : */
14178 140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14179 : {
14180 140 : VALIDATE_POINTER1(hDim, __func__, nullptr);
14181 280 : auto var(hDim->m_poImpl->GetIndexingVariable());
14182 140 : if (!var)
14183 11 : return nullptr;
14184 129 : return new GDALMDArrayHS(var);
14185 : }
14186 :
14187 : /************************************************************************/
14188 : /* GDALDimensionSetIndexingVariable() */
14189 : /************************************************************************/
14190 :
14191 : /** Set the variable that is used to index the dimension.
14192 : *
14193 : * This is the array, typically one-dimensional, describing the values taken
14194 : * by the dimension.
14195 : *
14196 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14197 : *
14198 : * @return TRUE in case of success.
14199 : */
14200 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14201 : {
14202 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
14203 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14204 46 : : nullptr);
14205 : }
14206 :
14207 : /************************************************************************/
14208 : /* GDALDimensionRename() */
14209 : /************************************************************************/
14210 :
14211 : /** Rename the dimension.
14212 : *
14213 : * This is not implemented by all drivers.
14214 : *
14215 : * Drivers known to implement it: MEM, netCDF.
14216 : *
14217 : * This is the same as the C++ method GDALDimension::Rename()
14218 : *
14219 : * @return true in case of success
14220 : * @since GDAL 3.8
14221 : */
14222 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14223 : {
14224 31 : VALIDATE_POINTER1(hDim, __func__, false);
14225 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
14226 31 : return hDim->m_poImpl->Rename(pszNewName);
14227 : }
14228 :
14229 : /************************************************************************/
14230 : /* GDALDatasetGetRootGroup() */
14231 : /************************************************************************/
14232 :
14233 : /** Return the root GDALGroup of this dataset.
14234 : *
14235 : * Only valid for multidimensional datasets.
14236 : *
14237 : * The returned value must be freed with GDALGroupRelease().
14238 : *
14239 : * This is the same as the C++ method GDALDataset::GetRootGroup().
14240 : *
14241 : * @since GDAL 3.1
14242 : */
14243 1245 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14244 : {
14245 1245 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14246 1245 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14247 1245 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14248 : }
14249 :
14250 : /************************************************************************/
14251 : /* GDALRasterBandAsMDArray() */
14252 : /************************************************************************/
14253 :
14254 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14255 : *
14256 : * The band must be linked to a GDALDataset. If this dataset is not already
14257 : * marked as shared, it will be, so that the returned array holds a reference
14258 : * to it.
14259 : *
14260 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14261 : * returned array will have an associated indexing variable.
14262 : *
14263 : * The returned pointer must be released with GDALMDArrayRelease().
14264 : *
14265 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
14266 : *
14267 : * @return a new array, or NULL.
14268 : *
14269 : * @since GDAL 3.1
14270 : */
14271 24 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14272 : {
14273 24 : VALIDATE_POINTER1(hBand, __func__, nullptr);
14274 48 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14275 24 : if (!poArray)
14276 0 : return nullptr;
14277 24 : return new GDALMDArrayHS(poArray);
14278 : }
14279 :
14280 : /************************************************************************/
14281 : /* GDALDatasetAsMDArray() */
14282 : /************************************************************************/
14283 :
14284 : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14285 : *
14286 : * If this dataset is not already marked as shared, it will be, so that the
14287 : * returned array holds a reference to it.
14288 : *
14289 : * If the dataset has a geotransform attached, the X and Y dimensions of the
14290 : * returned array will have an associated indexing variable.
14291 : *
14292 : * The currently supported list of options is:
14293 : * <ul>
14294 : * <li>DIM_ORDER=<order> where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14295 : * "Band,Y,X" means that the first (slowest changing) dimension is Band
14296 : * and the last (fastest changing direction) is X
14297 : * "Y,X,Band" means that the first (slowest changing) dimension is Y
14298 : * and the last (fastest changing direction) is Band.
14299 : * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14300 : * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14301 : * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14302 : * "Y,X,Band" is use.
14303 : * </li>
14304 : * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|<BandMetadataItem>:
14305 : * item from which to build the band indexing variable.
14306 : * <ul>
14307 : * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14308 : * <li>"{None}" means that no band indexing variable must be created.</li>
14309 : * <li>"{Index}" means that the band index (starting at one) is used.</li>
14310 : * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14311 : * <li><BandMetadataItem> is the name of a band metadata item to use.</li>
14312 : * </ul>
14313 : * </li>
14314 : * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14315 : * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14316 : * Defaults to String.
14317 : * </li>
14318 : * <li>BAND_DIM_NAME=<string>: Name of the band dimension.
14319 : * Defaults to "Band".
14320 : * </li>
14321 : * <li>X_DIM_NAME=<string>: Name of the X dimension. Defaults to "X".
14322 : * </li>
14323 : * <li>Y_DIM_NAME=<string>: Name of the Y dimension. Defaults to "Y".
14324 : * </li>
14325 : * </ul>
14326 : *
14327 : * The returned pointer must be released with GDALMDArrayRelease().
14328 : *
14329 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14330 : * GDALDataset::AsMDArray()
14331 : *
14332 : * This is the same as the C++ method GDALDataset::AsMDArray().
14333 : *
14334 : * @param hDS Dataset handle.
14335 : * @param papszOptions Null-terminated list of strings, or nullptr.
14336 : * @return a new array, or NULL.
14337 : *
14338 : * @since GDAL 3.12
14339 : */
14340 14 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14341 : {
14342 14 : VALIDATE_POINTER1(hDS, __func__, nullptr);
14343 28 : auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14344 14 : if (!poArray)
14345 3 : return nullptr;
14346 11 : return new GDALMDArrayHS(poArray);
14347 : }
14348 :
14349 : /************************************************************************/
14350 : /* GDALMDArrayAsClassicDataset() */
14351 : /************************************************************************/
14352 :
14353 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14354 : *
14355 : * Only 2D or more arrays are supported.
14356 : *
14357 : * In the case of > 2D arrays, additional dimensions will be represented as
14358 : * raster bands.
14359 : *
14360 : * The "reverse" methods are GDALRasterBand::AsMDArray() and
14361 : * GDALDataset::AsMDArray()
14362 : *
14363 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14364 : *
14365 : * @param hArray Array.
14366 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14367 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14368 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14369 : */
14370 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14371 : size_t iYDim)
14372 : {
14373 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14374 0 : return GDALDataset::ToHandle(
14375 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14376 : }
14377 :
14378 : /************************************************************************/
14379 : /* GDALMDArrayAsClassicDatasetEx() */
14380 : /************************************************************************/
14381 :
14382 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
14383 : *
14384 : * Only 2D or more arrays are supported.
14385 : *
14386 : * In the case of > 2D arrays, additional dimensions will be represented as
14387 : * raster bands.
14388 : *
14389 : * The "reverse" method is GDALRasterBand::AsMDArray().
14390 : *
14391 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14392 : * @param hArray Array.
14393 : * @param iXDim Index of the dimension that will be used as the X/width axis.
14394 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
14395 : * Ignored if the dimension count is 1.
14396 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14397 : * BAND_IMAGERY_METADATA option.
14398 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14399 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14400 : * @since GDAL 3.8
14401 : */
14402 100 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14403 : size_t iYDim, GDALGroupH hRootGroup,
14404 : CSLConstList papszOptions)
14405 : {
14406 100 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14407 200 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14408 200 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14409 200 : papszOptions));
14410 : }
14411 :
14412 : //! @cond Doxygen_Suppress
14413 :
14414 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14415 : const std::string &osName,
14416 : const std::string &osValue,
14417 180 : GDALExtendedDataTypeSubType eSubType)
14418 : : GDALAbstractMDArray(osParentName, osName),
14419 : GDALAttribute(osParentName, osName),
14420 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14421 : {
14422 180 : }
14423 :
14424 : const std::vector<std::shared_ptr<GDALDimension>> &
14425 30 : GDALAttributeString::GetDimensions() const
14426 : {
14427 30 : return m_dims;
14428 : }
14429 :
14430 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14431 : {
14432 21 : return m_dt;
14433 : }
14434 :
14435 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14436 : const GPtrDiff_t *,
14437 : const GDALExtendedDataType &bufferDataType,
14438 : void *pDstBuffer) const
14439 : {
14440 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14441 0 : return false;
14442 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14443 10 : if (!pszStr)
14444 0 : return false;
14445 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14446 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14447 10 : return true;
14448 : }
14449 :
14450 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14451 : const std::string &osName,
14452 66 : double dfValue)
14453 : : GDALAbstractMDArray(osParentName, osName),
14454 : GDALAttribute(osParentName, osName),
14455 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14456 : {
14457 66 : }
14458 :
14459 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14460 : const std::string &osName,
14461 27 : int nValue)
14462 : : GDALAbstractMDArray(osParentName, osName),
14463 : GDALAttribute(osParentName, osName),
14464 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14465 : {
14466 27 : }
14467 :
14468 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14469 : const std::string &osName,
14470 7 : const std::vector<GUInt32> &anValues)
14471 : : GDALAbstractMDArray(osParentName, osName),
14472 : GDALAttribute(osParentName, osName),
14473 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14474 : {
14475 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14476 14 : std::string(), "dim0", std::string(), std::string(),
14477 7 : m_anValuesUInt32.size()));
14478 7 : }
14479 :
14480 : const std::vector<std::shared_ptr<GDALDimension>> &
14481 14 : GDALAttributeNumeric::GetDimensions() const
14482 : {
14483 14 : return m_dims;
14484 : }
14485 :
14486 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14487 : {
14488 8 : return m_dt;
14489 : }
14490 :
14491 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14492 : const size_t *count, const GInt64 *arrayStep,
14493 : const GPtrDiff_t *bufferStride,
14494 : const GDALExtendedDataType &bufferDataType,
14495 : void *pDstBuffer) const
14496 : {
14497 4 : if (m_dims.empty())
14498 : {
14499 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14500 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14501 : bufferDataType);
14502 : else
14503 : {
14504 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14505 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14506 : bufferDataType);
14507 : }
14508 : }
14509 : else
14510 : {
14511 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14512 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14513 30 : for (size_t i = 0; i < count[0]; ++i)
14514 : {
14515 29 : GDALExtendedDataType::CopyValue(
14516 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14517 29 : i * arrayStep[0])],
14518 29 : m_dt, pabyDstBuffer, bufferDataType);
14519 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14520 : }
14521 : }
14522 4 : return true;
14523 : }
14524 :
14525 224 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14526 : const std::string &osParentName, const std::string &osName,
14527 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14528 224 : double dfIncrement, double dfOffsetInIncrement)
14529 : : GDALAbstractMDArray(osParentName, osName),
14530 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14531 : m_dfIncrement(dfIncrement),
14532 448 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14533 : {
14534 224 : }
14535 :
14536 224 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14537 : const std::string &osParentName, const std::string &osName,
14538 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14539 : double dfIncrement, double dfOffsetInIncrement)
14540 : {
14541 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14542 224 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14543 224 : poArray->SetSelf(poArray);
14544 224 : return poArray;
14545 : }
14546 :
14547 : const std::vector<std::shared_ptr<GDALDimension>> &
14548 1351 : GDALMDArrayRegularlySpaced::GetDimensions() const
14549 : {
14550 1351 : return m_dims;
14551 : }
14552 :
14553 570 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14554 : {
14555 570 : return m_dt;
14556 : }
14557 :
14558 : std::vector<std::shared_ptr<GDALAttribute>>
14559 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14560 : {
14561 4 : return m_attributes;
14562 : }
14563 :
14564 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14565 : const std::shared_ptr<GDALAttribute> &poAttr)
14566 : {
14567 0 : m_attributes.emplace_back(poAttr);
14568 0 : }
14569 :
14570 293 : bool GDALMDArrayRegularlySpaced::IRead(
14571 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14572 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14573 : void *pDstBuffer) const
14574 : {
14575 293 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14576 16237 : for (size_t i = 0; i < count[0]; i++)
14577 : {
14578 15944 : const double dfVal =
14579 15944 : m_dfStart +
14580 15944 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14581 15944 : m_dfOffsetInIncrement) *
14582 15944 : m_dfIncrement;
14583 15944 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14584 : bufferDataType);
14585 15944 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14586 : }
14587 293 : return true;
14588 : }
14589 :
14590 3978 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14591 : const std::string &osParentName, const std::string &osName,
14592 3978 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14593 3978 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14594 : {
14595 3978 : }
14596 :
14597 : std::shared_ptr<GDALMDArray>
14598 1458 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14599 : {
14600 1458 : return m_poIndexingVariable.lock();
14601 : }
14602 :
14603 : // cppcheck-suppress passedByValue
14604 644 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14605 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14606 : {
14607 644 : m_poIndexingVariable = poIndexingVariable;
14608 644 : return true;
14609 : }
14610 :
14611 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14612 : {
14613 33 : m_nSize = nNewSize;
14614 33 : }
14615 :
14616 : /************************************************************************/
14617 : /* GDALPamMultiDim::Private */
14618 : /************************************************************************/
14619 :
14620 : struct GDALPamMultiDim::Private
14621 : {
14622 : std::string m_osFilename{};
14623 : std::string m_osPamFilename{};
14624 :
14625 : struct Statistics
14626 : {
14627 : bool bHasStats = false;
14628 : bool bApproxStats = false;
14629 : double dfMin = 0;
14630 : double dfMax = 0;
14631 : double dfMean = 0;
14632 : double dfStdDev = 0;
14633 : GUInt64 nValidCount = 0;
14634 : };
14635 :
14636 : struct ArrayInfo
14637 : {
14638 : std::shared_ptr<OGRSpatialReference> poSRS{};
14639 : // cppcheck-suppress unusedStructMember
14640 : Statistics stats{};
14641 : };
14642 :
14643 : typedef std::pair<std::string, std::string> NameContext;
14644 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14645 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14646 : bool m_bDirty = false;
14647 : bool m_bLoaded = false;
14648 : };
14649 :
14650 : /************************************************************************/
14651 : /* GDALPamMultiDim */
14652 : /************************************************************************/
14653 :
14654 1613 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14655 1613 : : d(new Private())
14656 : {
14657 1613 : d->m_osFilename = osFilename;
14658 1612 : }
14659 :
14660 : /************************************************************************/
14661 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14662 : /************************************************************************/
14663 :
14664 1613 : GDALPamMultiDim::~GDALPamMultiDim()
14665 : {
14666 1613 : if (d->m_bDirty)
14667 30 : Save();
14668 1613 : }
14669 :
14670 : /************************************************************************/
14671 : /* GDALPamMultiDim::Load() */
14672 : /************************************************************************/
14673 :
14674 107 : void GDALPamMultiDim::Load()
14675 : {
14676 107 : if (d->m_bLoaded)
14677 96 : return;
14678 45 : d->m_bLoaded = true;
14679 :
14680 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14681 45 : d->m_osPamFilename =
14682 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14683 45 : CPLXMLTreeCloser oTree(nullptr);
14684 : {
14685 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14686 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14687 : }
14688 45 : if (!oTree)
14689 : {
14690 34 : return;
14691 : }
14692 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14693 11 : if (!poPAMMultiDim)
14694 0 : return;
14695 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14696 24 : psIter = psIter->psNext)
14697 : {
14698 24 : if (psIter->eType == CXT_Element &&
14699 24 : strcmp(psIter->pszValue, "Array") == 0)
14700 : {
14701 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14702 13 : if (!pszName)
14703 0 : continue;
14704 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14705 : const auto oKey =
14706 26 : std::pair<std::string, std::string>(pszName, pszContext);
14707 :
14708 : /* --------------------------------------------------------------------
14709 : */
14710 : /* Check for an SRS node. */
14711 : /* --------------------------------------------------------------------
14712 : */
14713 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14714 13 : if (psSRSNode)
14715 : {
14716 : std::shared_ptr<OGRSpatialReference> poSRS =
14717 6 : std::make_shared<OGRSpatialReference>();
14718 3 : poSRS->SetFromUserInput(
14719 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14720 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14721 3 : const char *pszMapping = CPLGetXMLValue(
14722 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14723 3 : if (pszMapping)
14724 : {
14725 : char **papszTokens =
14726 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14727 6 : std::vector<int> anMapping;
14728 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14729 : {
14730 6 : anMapping.push_back(atoi(papszTokens[i]));
14731 : }
14732 3 : CSLDestroy(papszTokens);
14733 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14734 : }
14735 : else
14736 : {
14737 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14738 : }
14739 :
14740 : const char *pszCoordinateEpoch =
14741 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14742 3 : if (pszCoordinateEpoch)
14743 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14744 :
14745 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14746 : }
14747 :
14748 : const CPLXMLNode *psStatistics =
14749 13 : CPLGetXMLNode(psIter, "Statistics");
14750 13 : if (psStatistics)
14751 : {
14752 7 : Private::Statistics sStats;
14753 7 : sStats.bHasStats = true;
14754 7 : sStats.bApproxStats = CPLTestBool(
14755 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14756 7 : sStats.dfMin =
14757 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14758 7 : sStats.dfMax =
14759 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14760 7 : sStats.dfMean =
14761 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14762 7 : sStats.dfStdDev =
14763 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14764 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14765 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14766 7 : d->m_oMapArray[oKey].stats = sStats;
14767 13 : }
14768 : }
14769 : else
14770 : {
14771 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14772 11 : psIter->psNext = nullptr;
14773 11 : d->m_apoOtherNodes.emplace_back(
14774 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14775 11 : psIter->psNext = psNextBackup;
14776 : }
14777 : }
14778 : }
14779 :
14780 : /************************************************************************/
14781 : /* GDALPamMultiDim::Save() */
14782 : /************************************************************************/
14783 :
14784 30 : void GDALPamMultiDim::Save()
14785 : {
14786 : CPLXMLTreeCloser oTree(
14787 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14788 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14789 : {
14790 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14791 : }
14792 112 : for (const auto &kv : d->m_oMapArray)
14793 : {
14794 : CPLXMLNode *psArrayNode =
14795 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14796 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14797 82 : if (!kv.first.second.empty())
14798 : {
14799 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14800 : kv.first.second.c_str());
14801 : }
14802 82 : if (kv.second.poSRS)
14803 : {
14804 71 : char *pszWKT = nullptr;
14805 : {
14806 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14807 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14808 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14809 : }
14810 : CPLXMLNode *psSRSNode =
14811 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14812 71 : CPLFree(pszWKT);
14813 : const auto &mapping =
14814 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14815 142 : CPLString osMapping;
14816 213 : for (size_t i = 0; i < mapping.size(); ++i)
14817 : {
14818 142 : if (!osMapping.empty())
14819 71 : osMapping += ",";
14820 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14821 : }
14822 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14823 : osMapping.c_str());
14824 :
14825 : const double dfCoordinateEpoch =
14826 71 : kv.second.poSRS->GetCoordinateEpoch();
14827 71 : if (dfCoordinateEpoch > 0)
14828 : {
14829 : std::string osCoordinateEpoch =
14830 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14831 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14832 : {
14833 6 : while (osCoordinateEpoch.back() == '0')
14834 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14835 : }
14836 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14837 : osCoordinateEpoch.c_str());
14838 : }
14839 : }
14840 :
14841 82 : if (kv.second.stats.bHasStats)
14842 : {
14843 : CPLXMLNode *psMDArray =
14844 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14845 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14846 8 : kv.second.stats.bApproxStats ? "1"
14847 : : "0");
14848 8 : CPLCreateXMLElementAndValue(
14849 : psMDArray, "Minimum",
14850 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14851 8 : CPLCreateXMLElementAndValue(
14852 : psMDArray, "Maximum",
14853 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14854 8 : CPLCreateXMLElementAndValue(
14855 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14856 8 : CPLCreateXMLElementAndValue(
14857 : psMDArray, "StdDev",
14858 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14859 8 : CPLCreateXMLElementAndValue(
14860 : psMDArray, "ValidSampleCount",
14861 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14862 : }
14863 : }
14864 :
14865 : int bSaved;
14866 60 : CPLErrorAccumulator oErrorAccumulator;
14867 : {
14868 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14869 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14870 : bSaved =
14871 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14872 : }
14873 :
14874 30 : const char *pszNewPam = nullptr;
14875 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14876 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14877 : {
14878 0 : CPLErrorReset();
14879 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14880 : }
14881 : else
14882 : {
14883 30 : oErrorAccumulator.ReplayErrors();
14884 : }
14885 30 : }
14886 :
14887 : /************************************************************************/
14888 : /* GDALPamMultiDim::GetSpatialRef() */
14889 : /************************************************************************/
14890 :
14891 : std::shared_ptr<OGRSpatialReference>
14892 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14893 : const std::string &osContext)
14894 : {
14895 10 : Load();
14896 : auto oIter =
14897 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14898 10 : if (oIter != d->m_oMapArray.end())
14899 2 : return oIter->second.poSRS;
14900 8 : return nullptr;
14901 : }
14902 :
14903 : /************************************************************************/
14904 : /* GDALPamMultiDim::SetSpatialRef() */
14905 : /************************************************************************/
14906 :
14907 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14908 : const std::string &osContext,
14909 : const OGRSpatialReference *poSRS)
14910 : {
14911 72 : Load();
14912 72 : d->m_bDirty = true;
14913 72 : if (poSRS && !poSRS->IsEmpty())
14914 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14915 : poSRS->Clone());
14916 : else
14917 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14918 1 : .poSRS.reset();
14919 72 : }
14920 :
14921 : /************************************************************************/
14922 : /* GetStatistics() */
14923 : /************************************************************************/
14924 :
14925 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14926 : const std::string &osContext,
14927 : bool bApproxOK, double *pdfMin,
14928 : double *pdfMax, double *pdfMean,
14929 : double *pdfStdDev, GUInt64 *pnValidCount)
14930 : {
14931 16 : Load();
14932 : auto oIter =
14933 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14934 16 : if (oIter == d->m_oMapArray.end())
14935 9 : return CE_Failure;
14936 7 : const auto &stats = oIter->second.stats;
14937 7 : if (!stats.bHasStats)
14938 1 : return CE_Failure;
14939 6 : if (!bApproxOK && stats.bApproxStats)
14940 0 : return CE_Failure;
14941 6 : if (pdfMin)
14942 6 : *pdfMin = stats.dfMin;
14943 6 : if (pdfMax)
14944 6 : *pdfMax = stats.dfMax;
14945 6 : if (pdfMean)
14946 6 : *pdfMean = stats.dfMean;
14947 6 : if (pdfStdDev)
14948 6 : *pdfStdDev = stats.dfStdDev;
14949 6 : if (pnValidCount)
14950 6 : *pnValidCount = stats.nValidCount;
14951 6 : return CE_None;
14952 : }
14953 :
14954 : /************************************************************************/
14955 : /* SetStatistics() */
14956 : /************************************************************************/
14957 :
14958 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14959 : const std::string &osContext,
14960 : bool bApproxStats, double dfMin,
14961 : double dfMax, double dfMean,
14962 : double dfStdDev, GUInt64 nValidCount)
14963 : {
14964 8 : Load();
14965 8 : d->m_bDirty = true;
14966 : auto &stats =
14967 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14968 8 : stats.bHasStats = true;
14969 8 : stats.bApproxStats = bApproxStats;
14970 8 : stats.dfMin = dfMin;
14971 8 : stats.dfMax = dfMax;
14972 8 : stats.dfMean = dfMean;
14973 8 : stats.dfStdDev = dfStdDev;
14974 8 : stats.nValidCount = nValidCount;
14975 8 : }
14976 :
14977 : /************************************************************************/
14978 : /* ClearStatistics() */
14979 : /************************************************************************/
14980 :
14981 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14982 : const std::string &osContext)
14983 : {
14984 0 : Load();
14985 0 : d->m_bDirty = true;
14986 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14987 : false;
14988 0 : }
14989 :
14990 : /************************************************************************/
14991 : /* ClearStatistics() */
14992 : /************************************************************************/
14993 :
14994 1 : void GDALPamMultiDim::ClearStatistics()
14995 : {
14996 1 : Load();
14997 1 : d->m_bDirty = true;
14998 3 : for (auto &kv : d->m_oMapArray)
14999 2 : kv.second.stats.bHasStats = false;
15000 1 : }
15001 :
15002 : /************************************************************************/
15003 : /* GetPAM() */
15004 : /************************************************************************/
15005 :
15006 : /*static*/ std::shared_ptr<GDALPamMultiDim>
15007 935 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15008 : {
15009 935 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15010 935 : if (poPamArray)
15011 581 : return poPamArray->GetPAM();
15012 354 : return nullptr;
15013 : }
15014 :
15015 : /************************************************************************/
15016 : /* GDALPamMDArray */
15017 : /************************************************************************/
15018 :
15019 4318 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15020 : const std::string &osName,
15021 : const std::shared_ptr<GDALPamMultiDim> &poPam,
15022 0 : const std::string &osContext)
15023 : :
15024 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15025 : GDALAbstractMDArray(osParentName, osName),
15026 : #endif
15027 4318 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15028 : {
15029 4318 : }
15030 :
15031 : /************************************************************************/
15032 : /* GDALPamMDArray::SetSpatialRef() */
15033 : /************************************************************************/
15034 :
15035 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15036 : {
15037 72 : if (!m_poPam)
15038 0 : return false;
15039 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15040 72 : return true;
15041 : }
15042 :
15043 : /************************************************************************/
15044 : /* GDALPamMDArray::GetSpatialRef() */
15045 : /************************************************************************/
15046 :
15047 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15048 : {
15049 10 : if (!m_poPam)
15050 0 : return nullptr;
15051 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15052 : }
15053 :
15054 : /************************************************************************/
15055 : /* GetStatistics() */
15056 : /************************************************************************/
15057 :
15058 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15059 : double *pdfMin, double *pdfMax,
15060 : double *pdfMean, double *pdfStdDev,
15061 : GUInt64 *pnValidCount,
15062 : GDALProgressFunc pfnProgress,
15063 : void *pProgressData)
15064 : {
15065 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15066 : bApproxOK, pdfMin, pdfMax, pdfMean,
15067 16 : pdfStdDev, pnValidCount) == CE_None)
15068 : {
15069 6 : return CE_None;
15070 : }
15071 10 : if (!bForce)
15072 4 : return CE_Warning;
15073 :
15074 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15075 : pdfMean, pdfStdDev, pnValidCount,
15076 6 : pfnProgress, pProgressData);
15077 : }
15078 :
15079 : /************************************************************************/
15080 : /* SetStatistics() */
15081 : /************************************************************************/
15082 :
15083 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15084 : double dfMax, double dfMean, double dfStdDev,
15085 : GUInt64 nValidCount,
15086 : CSLConstList /* papszOptions */)
15087 : {
15088 8 : if (!m_poPam)
15089 0 : return false;
15090 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15091 : dfMax, dfMean, dfStdDev, nValidCount);
15092 8 : return true;
15093 : }
15094 :
15095 : /************************************************************************/
15096 : /* ClearStatistics() */
15097 : /************************************************************************/
15098 :
15099 0 : void GDALPamMDArray::ClearStatistics()
15100 : {
15101 0 : if (!m_poPam)
15102 0 : return;
15103 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
15104 : }
15105 :
15106 : /************************************************************************/
15107 : /* GDALMDIAsAttribute::GetDimensions() */
15108 : /************************************************************************/
15109 :
15110 : const std::vector<std::shared_ptr<GDALDimension>> &
15111 29 : GDALMDIAsAttribute::GetDimensions() const
15112 : {
15113 29 : return m_dims;
15114 : }
15115 :
15116 : //! @endcond
|