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_rat.h"
31 : #include "gdal_utils.h"
32 : #include "cpl_safemaths.hpp"
33 : #include "memmultidim.h"
34 : #include "ogrsf_frmts.h"
35 : #include "gdalmultidim_priv.h"
36 :
37 : #if defined(__clang__) || defined(_MSC_VER)
38 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
39 : #endif
40 :
41 : /************************************************************************/
42 : /* GDALMDArrayUnscaled */
43 : /************************************************************************/
44 :
45 : class GDALMDArrayUnscaled final : public GDALPamMDArray
46 : {
47 : private:
48 : std::shared_ptr<GDALMDArray> m_poParent{};
49 : const GDALExtendedDataType m_dt;
50 : bool m_bHasNoData;
51 : const double m_dfScale;
52 : const double m_dfOffset;
53 : std::vector<GByte> m_abyRawNoData{};
54 :
55 : protected:
56 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
57 : double dfScale, double dfOffset,
58 : double dfOverriddenDstNodata, GDALDataType eDT)
59 26 : : GDALAbstractMDArray(std::string(),
60 26 : "Unscaled view of " + poParent->GetFullName()),
61 : GDALPamMDArray(
62 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
63 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
64 13 : m_poParent(std::move(poParent)),
65 : m_dt(GDALExtendedDataType::Create(eDT)),
66 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
67 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
68 : {
69 13 : m_abyRawNoData.resize(m_dt.GetSize());
70 : const auto eNonComplexDT =
71 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
72 26 : GDALCopyWords64(
73 13 : &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
74 : eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
75 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
76 13 : }
77 :
78 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
79 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
80 : const GDALExtendedDataType &bufferDataType,
81 : void *pDstBuffer) const override;
82 :
83 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
84 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
85 : const GDALExtendedDataType &bufferDataType,
86 : const void *pSrcBuffer) override;
87 :
88 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
89 : CSLConstList papszOptions) const override
90 : {
91 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
92 : }
93 :
94 : public:
95 : static std::shared_ptr<GDALMDArrayUnscaled>
96 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
97 : double dfOffset, double dfDstNodata, GDALDataType eDT)
98 : {
99 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
100 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
101 13 : newAr->SetSelf(newAr);
102 13 : return newAr;
103 : }
104 :
105 1 : bool IsWritable() const override
106 : {
107 1 : return m_poParent->IsWritable();
108 : }
109 :
110 15 : const std::string &GetFilename() const override
111 : {
112 15 : return m_poParent->GetFilename();
113 : }
114 :
115 : const std::vector<std::shared_ptr<GDALDimension>> &
116 220 : GetDimensions() const override
117 : {
118 220 : return m_poParent->GetDimensions();
119 : }
120 :
121 103 : const GDALExtendedDataType &GetDataType() const override
122 : {
123 103 : return m_dt;
124 : }
125 :
126 1 : const std::string &GetUnit() const override
127 : {
128 1 : return m_poParent->GetUnit();
129 : }
130 :
131 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
132 : {
133 1 : return m_poParent->GetSpatialRef();
134 : }
135 :
136 6 : const void *GetRawNoDataValue() const override
137 : {
138 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
139 : }
140 :
141 1 : bool SetRawNoDataValue(const void *pRawNoData) override
142 : {
143 1 : m_bHasNoData = true;
144 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
145 1 : return true;
146 : }
147 :
148 4 : std::vector<GUInt64> GetBlockSize() const override
149 : {
150 4 : return m_poParent->GetBlockSize();
151 : }
152 :
153 : std::shared_ptr<GDALAttribute>
154 0 : GetAttribute(const std::string &osName) const override
155 : {
156 0 : return m_poParent->GetAttribute(osName);
157 : }
158 :
159 : std::vector<std::shared_ptr<GDALAttribute>>
160 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
161 : {
162 1 : return m_poParent->GetAttributes(papszOptions);
163 : }
164 :
165 0 : bool SetUnit(const std::string &osUnit) override
166 : {
167 0 : return m_poParent->SetUnit(osUnit);
168 : }
169 :
170 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
171 : {
172 0 : return m_poParent->SetSpatialRef(poSRS);
173 : }
174 :
175 : std::shared_ptr<GDALAttribute>
176 1 : CreateAttribute(const std::string &osName,
177 : const std::vector<GUInt64> &anDimensions,
178 : const GDALExtendedDataType &oDataType,
179 : CSLConstList papszOptions = nullptr) override
180 : {
181 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
182 1 : papszOptions);
183 : }
184 : };
185 :
186 : /************************************************************************/
187 : /* ~GDALIHasAttribute() */
188 : /************************************************************************/
189 :
190 : GDALIHasAttribute::~GDALIHasAttribute() = default;
191 :
192 : /************************************************************************/
193 : /* GetAttribute() */
194 : /************************************************************************/
195 :
196 : /** Return an attribute by its name.
197 : *
198 : * If the attribute does not exist, nullptr should be silently returned.
199 : *
200 : * @note Driver implementation: this method will fallback to
201 : * GetAttributeFromAttributes() is not explicitly implemented
202 : *
203 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
204 : *
205 : * This is the same as the C function GDALGroupGetAttribute() or
206 : * GDALMDArrayGetAttribute().
207 : *
208 : * @param osName Attribute name
209 : * @return the attribute, or nullptr if it does not exist or an error occurred.
210 : */
211 : std::shared_ptr<GDALAttribute>
212 1055 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
213 : {
214 1055 : return GetAttributeFromAttributes(osName);
215 : }
216 :
217 : /************************************************************************/
218 : /* GetAttributeFromAttributes() */
219 : /************************************************************************/
220 :
221 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
222 : */
223 : std::shared_ptr<GDALAttribute>
224 1055 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
225 : {
226 2110 : auto attrs(GetAttributes());
227 5491 : for (const auto &attr : attrs)
228 : {
229 5174 : if (attr->GetName() == osName)
230 738 : return attr;
231 : }
232 317 : return nullptr;
233 : }
234 :
235 : /************************************************************************/
236 : /* GetAttributes() */
237 : /************************************************************************/
238 :
239 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
240 : *
241 : * If the attribute does not exist, nullptr should be silently returned.
242 : *
243 : * @note Driver implementation: optionally implemented. If implemented,
244 : * GetAttribute() should also be implemented.
245 : *
246 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
247 : *
248 : * This is the same as the C function GDALGroupGetAttributes() or
249 : * GDALMDArrayGetAttributes().
250 :
251 : * @param papszOptions Driver specific options determining how attributes
252 : * should be retrieved. Pass nullptr for default behavior.
253 : *
254 : * @return the attributes.
255 : */
256 : std::vector<std::shared_ptr<GDALAttribute>>
257 42 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
258 : {
259 42 : return {};
260 : }
261 :
262 : /************************************************************************/
263 : /* CreateAttribute() */
264 : /************************************************************************/
265 :
266 : /** Create an attribute within a GDALMDArray or GDALGroup.
267 : *
268 : * The attribute might not be "physically" created until a value is written
269 : * into it.
270 : *
271 : * Optionally implemented.
272 : *
273 : * Drivers known to implement it: MEM, netCDF
274 : *
275 : * This is the same as the C function GDALGroupCreateAttribute() or
276 : * GDALMDArrayCreateAttribute()
277 : *
278 : * @param osName Attribute name.
279 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
280 : * dimension first to the fastest varying dimension last.
281 : * Empty for a scalar attribute (common case)
282 : * @param oDataType Attribute data type.
283 : * @param papszOptions Driver specific options determining how the attribute.
284 : * should be created.
285 : *
286 : * @return the new attribute, or nullptr if case of error
287 : */
288 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
289 : CPL_UNUSED const std::string &osName,
290 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
291 : CPL_UNUSED const GDALExtendedDataType &oDataType,
292 : CPL_UNUSED CSLConstList papszOptions)
293 : {
294 0 : CPLError(CE_Failure, CPLE_NotSupported,
295 : "CreateAttribute() not implemented");
296 0 : return nullptr;
297 : }
298 :
299 : /************************************************************************/
300 : /* DeleteAttribute() */
301 : /************************************************************************/
302 :
303 : /** Delete an attribute from a GDALMDArray or GDALGroup.
304 : *
305 : * Optionally implemented.
306 : *
307 : * After this call, if a previously obtained instance of the deleted object
308 : * is still alive, no method other than for freeing it should be invoked.
309 : *
310 : * Drivers known to implement it: MEM, netCDF
311 : *
312 : * This is the same as the C function GDALGroupDeleteAttribute() or
313 : * GDALMDArrayDeleteAttribute()
314 : *
315 : * @param osName Attribute name.
316 : * @param papszOptions Driver specific options determining how the attribute.
317 : * should be deleted.
318 : *
319 : * @return true in case of success
320 : * @since GDAL 3.8
321 : */
322 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
323 : CPL_UNUSED CSLConstList papszOptions)
324 : {
325 0 : CPLError(CE_Failure, CPLE_NotSupported,
326 : "DeleteAttribute() not implemented");
327 0 : return false;
328 : }
329 :
330 : /************************************************************************/
331 : /* GDALGroup() */
332 : /************************************************************************/
333 :
334 : //! @cond Doxygen_Suppress
335 6916 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
336 6916 : const std::string &osContext)
337 6916 : : m_osName(osParentName.empty() ? "/" : osName),
338 : m_osFullName(
339 13832 : !osParentName.empty()
340 10709 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
341 : : "/"),
342 17625 : m_osContext(osContext)
343 : {
344 6916 : }
345 :
346 : //! @endcond
347 :
348 : /************************************************************************/
349 : /* ~GDALGroup() */
350 : /************************************************************************/
351 :
352 : GDALGroup::~GDALGroup() = default;
353 :
354 : /************************************************************************/
355 : /* GetMDArrayNames() */
356 : /************************************************************************/
357 :
358 : /** Return the list of multidimensional array names contained in this group.
359 : *
360 : * @note Driver implementation: optionally implemented. If implemented,
361 : * OpenMDArray() should also be implemented.
362 : *
363 : * Drivers known to implement it: MEM, netCDF.
364 : *
365 : * This is the same as the C function GDALGroupGetMDArrayNames().
366 : *
367 : * @param papszOptions Driver specific options determining how arrays
368 : * should be retrieved. Pass nullptr for default behavior.
369 : *
370 : * @return the array names.
371 : */
372 : std::vector<std::string>
373 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
374 : {
375 0 : return {};
376 : }
377 :
378 : /************************************************************************/
379 : /* GetMDArrayFullNamesRecursive() */
380 : /************************************************************************/
381 :
382 : /** Return the list of multidimensional array full names contained in this
383 : * group and its subgroups.
384 : *
385 : * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
386 : *
387 : * @param papszGroupOptions Driver specific options determining how groups
388 : * should be retrieved. Pass nullptr for default behavior.
389 : * @param papszArrayOptions Driver specific options determining how arrays
390 : * should be retrieved. Pass nullptr for default behavior.
391 : *
392 : * @return the array full names.
393 : *
394 : * @since 3.11
395 : */
396 : std::vector<std::string>
397 3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
398 : CSLConstList papszArrayOptions) const
399 : {
400 3 : std::vector<std::string> ret;
401 6 : std::list<std::shared_ptr<GDALGroup>> stackGroups;
402 3 : stackGroups.push_back(nullptr); // nullptr means this
403 9 : while (!stackGroups.empty())
404 : {
405 12 : std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
406 6 : stackGroups.erase(stackGroups.begin());
407 6 : const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
408 10 : for (const std::string &arrayName :
409 26 : poCurGroup->GetMDArrayNames(papszArrayOptions))
410 : {
411 20 : std::string osFullName = poCurGroup->GetFullName();
412 10 : if (!osFullName.empty() && osFullName.back() != '/')
413 3 : osFullName += '/';
414 10 : osFullName += arrayName;
415 10 : ret.push_back(std::move(osFullName));
416 : }
417 6 : auto insertionPoint = stackGroups.begin();
418 3 : for (const auto &osSubGroup :
419 12 : poCurGroup->GetGroupNames(papszGroupOptions))
420 : {
421 6 : auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
422 3 : if (poSubGroup)
423 3 : stackGroups.insert(insertionPoint, std::move(poSubGroup));
424 : }
425 : }
426 :
427 6 : return ret;
428 : }
429 :
430 : /************************************************************************/
431 : /* OpenMDArray() */
432 : /************************************************************************/
433 :
434 : /** Open and return a multidimensional array.
435 : *
436 : * @note Driver implementation: optionally implemented. If implemented,
437 : * GetMDArrayNames() should also be implemented.
438 : *
439 : * Drivers known to implement it: MEM, netCDF.
440 : *
441 : * This is the same as the C function GDALGroupOpenMDArray().
442 : *
443 : * @param osName Array name.
444 : * @param papszOptions Driver specific options determining how the array should
445 : * be opened. Pass nullptr for default behavior.
446 : *
447 : * @return the array, or nullptr.
448 : */
449 : std::shared_ptr<GDALMDArray>
450 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
451 : CPL_UNUSED CSLConstList papszOptions) const
452 : {
453 0 : return nullptr;
454 : }
455 :
456 : /************************************************************************/
457 : /* GetGroupNames() */
458 : /************************************************************************/
459 :
460 : /** Return the list of sub-groups contained in this group.
461 : *
462 : * @note Driver implementation: optionally implemented. If implemented,
463 : * OpenGroup() should also be implemented.
464 : *
465 : * Drivers known to implement it: MEM, netCDF.
466 : *
467 : * This is the same as the C function GDALGroupGetGroupNames().
468 : *
469 : * @param papszOptions Driver specific options determining how groups
470 : * should be retrieved. Pass nullptr for default behavior.
471 : *
472 : * @return the group names.
473 : */
474 : std::vector<std::string>
475 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
476 : {
477 4 : return {};
478 : }
479 :
480 : /************************************************************************/
481 : /* OpenGroup() */
482 : /************************************************************************/
483 :
484 : /** Open and return a sub-group.
485 : *
486 : * @note Driver implementation: optionally implemented. If implemented,
487 : * GetGroupNames() should also be implemented.
488 : *
489 : * Drivers known to implement it: MEM, netCDF.
490 : *
491 : * This is the same as the C function GDALGroupOpenGroup().
492 : *
493 : * @param osName Sub-group name.
494 : * @param papszOptions Driver specific options determining how the sub-group
495 : * should be opened. Pass nullptr for default behavior.
496 : *
497 : * @return the group, or nullptr.
498 : */
499 : std::shared_ptr<GDALGroup>
500 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
501 : CPL_UNUSED CSLConstList papszOptions) const
502 : {
503 4 : return nullptr;
504 : }
505 :
506 : /************************************************************************/
507 : /* GetVectorLayerNames() */
508 : /************************************************************************/
509 :
510 : /** Return the list of layer names contained in this group.
511 : *
512 : * @note Driver implementation: optionally implemented. If implemented,
513 : * OpenVectorLayer() should also be implemented.
514 : *
515 : * Drivers known to implement it: OpenFileGDB, FileGDB
516 : *
517 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
518 : * GDALDataset::GetLayer() should then be used.
519 : *
520 : * This is the same as the C function GDALGroupGetVectorLayerNames().
521 : *
522 : * @param papszOptions Driver specific options determining how layers
523 : * should be retrieved. Pass nullptr for default behavior.
524 : *
525 : * @return the vector layer names.
526 : * @since GDAL 3.4
527 : */
528 : std::vector<std::string>
529 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
530 : {
531 1 : return {};
532 : }
533 :
534 : /************************************************************************/
535 : /* OpenVectorLayer() */
536 : /************************************************************************/
537 :
538 : /** Open and return a vector layer.
539 : *
540 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
541 : * lifetime of the returned OGRLayer* is linked to the one of the owner
542 : * dataset (contrary to the general design of this class where objects can be
543 : * used independently of the object that returned them)
544 : *
545 : * @note Driver implementation: optionally implemented. If implemented,
546 : * GetVectorLayerNames() should also be implemented.
547 : *
548 : * Drivers known to implement it: MEM, netCDF.
549 : *
550 : * This is the same as the C function GDALGroupOpenVectorLayer().
551 : *
552 : * @param osName Vector layer name.
553 : * @param papszOptions Driver specific options determining how the layer should
554 : * be opened. Pass nullptr for default behavior.
555 : *
556 : * @return the group, or nullptr.
557 : */
558 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
559 : CPL_UNUSED CSLConstList papszOptions) const
560 : {
561 2 : return nullptr;
562 : }
563 :
564 : /************************************************************************/
565 : /* GetDimensions() */
566 : /************************************************************************/
567 :
568 : /** Return the list of dimensions contained in this group and used by its
569 : * arrays.
570 : *
571 : * This is for dimensions that can potentially be used by several arrays.
572 : * Not all drivers might implement this. To retrieve the dimensions used by
573 : * a specific array, use GDALMDArray::GetDimensions().
574 : *
575 : * Drivers known to implement it: MEM, netCDF
576 : *
577 : * This is the same as the C function GDALGroupGetDimensions().
578 : *
579 : * @param papszOptions Driver specific options determining how groups
580 : * should be retrieved. Pass nullptr for default behavior.
581 : *
582 : * @return the dimensions.
583 : */
584 : std::vector<std::shared_ptr<GDALDimension>>
585 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
586 : {
587 11 : return {};
588 : }
589 :
590 : /************************************************************************/
591 : /* GetStructuralInfo() */
592 : /************************************************************************/
593 :
594 : /** Return structural information on the group.
595 : *
596 : * This may be the compression, etc..
597 : *
598 : * The return value should not be freed and is valid until GDALGroup is
599 : * released or this function called again.
600 : *
601 : * This is the same as the C function GDALGroupGetStructuralInfo().
602 : */
603 41 : CSLConstList GDALGroup::GetStructuralInfo() const
604 : {
605 41 : return nullptr;
606 : }
607 :
608 : /************************************************************************/
609 : /* CreateGroup() */
610 : /************************************************************************/
611 :
612 : /** Create a sub-group within a group.
613 : *
614 : * Optionally implemented by drivers.
615 : *
616 : * Drivers known to implement it: MEM, netCDF
617 : *
618 : * This is the same as the C function GDALGroupCreateGroup().
619 : *
620 : * @param osName Sub-group name.
621 : * @param papszOptions Driver specific options determining how the sub-group
622 : * should be created.
623 : *
624 : * @return the new sub-group, or nullptr in case of error.
625 : */
626 : std::shared_ptr<GDALGroup>
627 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
628 : CPL_UNUSED CSLConstList papszOptions)
629 : {
630 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
631 0 : return nullptr;
632 : }
633 :
634 : /************************************************************************/
635 : /* DeleteGroup() */
636 : /************************************************************************/
637 :
638 : /** Delete a sub-group from a group.
639 : *
640 : * Optionally implemented.
641 : *
642 : * After this call, if a previously obtained instance of the deleted object
643 : * is still alive, no method other than for freeing it should be invoked.
644 : *
645 : * Drivers known to implement it: MEM, Zarr
646 : *
647 : * This is the same as the C function GDALGroupDeleteGroup().
648 : *
649 : * @param osName Sub-group name.
650 : * @param papszOptions Driver specific options determining how the group.
651 : * should be deleted.
652 : *
653 : * @return true in case of success
654 : * @since GDAL 3.8
655 : */
656 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
657 : CPL_UNUSED CSLConstList papszOptions)
658 : {
659 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
660 0 : return false;
661 : }
662 :
663 : /************************************************************************/
664 : /* CreateDimension() */
665 : /************************************************************************/
666 :
667 : /** Create a dimension within a group.
668 : *
669 : * @note Driver implementation: drivers supporting CreateDimension() should
670 : * implement this method, but do not have necessarily to implement
671 : * GDALGroup::GetDimensions().
672 : *
673 : * Drivers known to implement it: MEM, netCDF
674 : *
675 : * This is the same as the C function GDALGroupCreateDimension().
676 : *
677 : * @param osName Dimension name.
678 : * @param osType Dimension type (might be empty, and ignored by drivers)
679 : * @param osDirection Dimension direction (might be empty, and ignored by
680 : * drivers)
681 : * @param nSize Number of values indexed by this dimension. Should be > 0.
682 : * @param papszOptions Driver specific options determining how the dimension
683 : * should be created.
684 : *
685 : * @return the new dimension, or nullptr if case of error
686 : */
687 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
688 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
689 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
690 : CPL_UNUSED CSLConstList papszOptions)
691 : {
692 0 : CPLError(CE_Failure, CPLE_NotSupported,
693 : "CreateDimension() not implemented");
694 0 : return nullptr;
695 : }
696 :
697 : /************************************************************************/
698 : /* CreateMDArray() */
699 : /************************************************************************/
700 :
701 : /** Create a multidimensional array within a group.
702 : *
703 : * It is recommended that the GDALDimension objects passed in aoDimensions
704 : * belong to this group, either by retrieving them with GetDimensions()
705 : * or creating a new one with CreateDimension().
706 : *
707 : * Optionally implemented.
708 : *
709 : * Drivers known to implement it: MEM, netCDF
710 : *
711 : * This is the same as the C function GDALGroupCreateMDArray().
712 : *
713 : * @note Driver implementation: drivers should take into account the possibility
714 : * that GDALDimension object passed in aoDimensions might belong to a different
715 : * group / dataset / driver and act accordingly.
716 : *
717 : * @param osName Array name.
718 : * @param aoDimensions List of dimensions, ordered from the slowest varying
719 : * dimension first to the fastest varying dimension last.
720 : * Might be empty for a scalar array (if supported by
721 : * driver)
722 : * @param oDataType Array data type.
723 : * @param papszOptions Driver specific options determining how the array
724 : * should be created.
725 : *
726 : * @return the new array, or nullptr in case of error
727 : */
728 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
729 : CPL_UNUSED const std::string &osName,
730 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
731 : CPL_UNUSED const GDALExtendedDataType &oDataType,
732 : CPL_UNUSED CSLConstList papszOptions)
733 : {
734 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
735 0 : return nullptr;
736 : }
737 :
738 : /************************************************************************/
739 : /* DeleteMDArray() */
740 : /************************************************************************/
741 :
742 : /** Delete an array from a group.
743 : *
744 : * Optionally implemented.
745 : *
746 : * After this call, if a previously obtained instance of the deleted object
747 : * is still alive, no method other than for freeing it should be invoked.
748 : *
749 : * Drivers known to implement it: MEM, Zarr
750 : *
751 : * This is the same as the C function GDALGroupDeleteMDArray().
752 : *
753 : * @param osName Arrayname.
754 : * @param papszOptions Driver specific options determining how the array.
755 : * should be deleted.
756 : *
757 : * @return true in case of success
758 : * @since GDAL 3.8
759 : */
760 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
761 : CPL_UNUSED CSLConstList papszOptions)
762 : {
763 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
764 0 : return false;
765 : }
766 :
767 : /************************************************************************/
768 : /* GetTotalCopyCost() */
769 : /************************************************************************/
770 :
771 : /** Return a total "cost" to copy the group.
772 : *
773 : * Used as a parameter for CopFrom()
774 : */
775 25 : GUInt64 GDALGroup::GetTotalCopyCost() const
776 : {
777 25 : GUInt64 nCost = COPY_COST;
778 25 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
779 :
780 50 : auto groupNames = GetGroupNames();
781 31 : for (const auto &name : groupNames)
782 : {
783 12 : auto subGroup = OpenGroup(name);
784 6 : if (subGroup)
785 : {
786 6 : nCost += subGroup->GetTotalCopyCost();
787 : }
788 : }
789 :
790 25 : auto arrayNames = GetMDArrayNames();
791 71 : for (const auto &name : arrayNames)
792 : {
793 92 : auto array = OpenMDArray(name);
794 46 : if (array)
795 : {
796 46 : nCost += array->GetTotalCopyCost();
797 : }
798 : }
799 50 : return nCost;
800 : }
801 :
802 : /************************************************************************/
803 : /* CopyFrom() */
804 : /************************************************************************/
805 :
806 : /** Copy the content of a group into a new (generally empty) group.
807 : *
808 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
809 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
810 : * of some output drivers this is not recommended)
811 : * @param poSrcGroup Source group. Must NOT be nullptr.
812 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
813 : * stop the copy. In relaxed mode, the copy will be attempted to
814 : * be pursued.
815 : * @param nCurCost Should be provided as a variable initially set to 0.
816 : * @param nTotalCost Total cost from GetTotalCopyCost().
817 : * @param pfnProgress Progress callback, or nullptr.
818 : * @param pProgressData Progress user data, or nulptr.
819 : * @param papszOptions Creation options. Currently, only array creation
820 : * options are supported. They must be prefixed with
821 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
822 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
823 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
824 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
825 : * Restriction to arrays of a given name is done with adding
826 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
827 : * a full qualified name.
828 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
829 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
830 : * scaled to UInt16 with scale and offset values being computed from the minimum
831 : * and maximum of the source array. The integer data type used can be set with
832 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
833 : *
834 : * @return true in case of success (or partial success if bStrict == false).
835 : */
836 25 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
837 : GDALDataset *poSrcDS,
838 : const std::shared_ptr<GDALGroup> &poSrcGroup,
839 : bool bStrict, GUInt64 &nCurCost,
840 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
841 : void *pProgressData, CSLConstList papszOptions)
842 : {
843 25 : if (pfnProgress == nullptr)
844 0 : pfnProgress = GDALDummyProgress;
845 :
846 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
847 : if (!(x)) \
848 : { \
849 : if (bStrict) \
850 : return false; \
851 : continue; \
852 : } \
853 : (void)0
854 :
855 : try
856 : {
857 25 : nCurCost += GDALGroup::COPY_COST;
858 :
859 50 : const auto srcDims = poSrcGroup->GetDimensions();
860 : std::map<std::string, std::shared_ptr<GDALDimension>>
861 50 : mapExistingDstDims;
862 50 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
863 63 : for (const auto &dim : srcDims)
864 : {
865 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
866 38 : dim->GetDirection(), dim->GetSize());
867 38 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
868 38 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
869 76 : auto poIndexingVarSrc(dim->GetIndexingVariable());
870 38 : if (poIndexingVarSrc)
871 : {
872 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
873 20 : ->GetName()] =
874 40 : dim->GetName();
875 : }
876 : }
877 :
878 50 : auto attrs = poSrcGroup->GetAttributes();
879 32 : for (const auto &attr : attrs)
880 : {
881 : auto dstAttr =
882 7 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
883 14 : attr->GetDataType());
884 7 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
885 7 : auto raw(attr->ReadAsRaw());
886 7 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
887 0 : return false;
888 : }
889 25 : if (!attrs.empty())
890 : {
891 5 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
892 5 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
893 0 : return false;
894 : }
895 :
896 : const auto CopyArray =
897 46 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
898 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
899 : papszOptions, bStrict, &nCurCost,
900 417 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
901 : {
902 : // Map source dimensions to target dimensions
903 92 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
904 46 : const auto &srcArrayDims(srcArray->GetDimensions());
905 118 : for (const auto &dim : srcArrayDims)
906 : {
907 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
908 72 : dim->GetFullName());
909 72 : if (dstDim && dstDim->GetSize() == dim->GetSize())
910 : {
911 62 : dstArrayDims.emplace_back(dstDim);
912 : }
913 : else
914 : {
915 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
916 19 : if (oIter != mapExistingDstDims.end() &&
917 9 : oIter->second->GetSize() == dim->GetSize())
918 : {
919 8 : dstArrayDims.emplace_back(oIter->second);
920 : }
921 : else
922 : {
923 2 : std::string newDimName;
924 2 : if (oIter == mapExistingDstDims.end())
925 : {
926 1 : newDimName = dim->GetName();
927 : }
928 : else
929 : {
930 1 : std::string newDimNamePrefix(srcArray->GetName() +
931 3 : '_' + dim->GetName());
932 1 : newDimName = newDimNamePrefix;
933 1 : int nIterCount = 2;
934 0 : while (
935 1 : cpl::contains(mapExistingDstDims, newDimName))
936 : {
937 0 : newDimName = newDimNamePrefix +
938 0 : CPLSPrintf("_%d", nIterCount);
939 0 : nIterCount++;
940 : }
941 : }
942 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
943 : dim->GetDirection(),
944 4 : dim->GetSize());
945 2 : if (!dstDim)
946 0 : return false;
947 2 : mapExistingDstDims[newDimName] = dstDim;
948 2 : dstArrayDims.emplace_back(dstDim);
949 : }
950 : }
951 : }
952 :
953 92 : CPLStringList aosArrayCO;
954 46 : bool bAutoScale = false;
955 46 : GDALDataType eAutoScaleType = GDT_UInt16;
956 53 : for (const char *pszItem : cpl::Iterate(papszOptions))
957 : {
958 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
959 : {
960 7 : const char *pszOption = pszItem + strlen("ARRAY:");
961 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
962 : {
963 1 : const char *pszNext = strchr(pszOption, ':');
964 1 : if (pszNext != nullptr)
965 : {
966 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
967 1 : if (static_cast<size_t>(nDim) ==
968 1 : dstArrayDims.size())
969 : {
970 1 : pszOption = pszNext + 1;
971 : }
972 : else
973 : {
974 0 : pszOption = nullptr;
975 : }
976 : }
977 : }
978 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
979 : {
980 2 : const char *pszName = pszOption + strlen("IF(NAME=");
981 2 : const char *pszNext = strchr(pszName, ':');
982 2 : if (pszNext != nullptr && pszNext > pszName &&
983 2 : pszNext[-1] == ')')
984 : {
985 4 : CPLString osName;
986 2 : osName.assign(pszName, pszNext - pszName - 1);
987 3 : if (osName == srcArray->GetName() ||
988 1 : osName == srcArray->GetFullName())
989 : {
990 2 : pszOption = pszNext + 1;
991 : }
992 : else
993 : {
994 0 : pszOption = nullptr;
995 : }
996 : }
997 : }
998 7 : if (pszOption)
999 : {
1000 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1001 : {
1002 : bAutoScale =
1003 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
1004 : }
1005 5 : else if (STARTS_WITH_CI(pszOption,
1006 : "AUTOSCALE_DATA_TYPE="))
1007 : {
1008 1 : const char *pszDataType =
1009 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1010 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1011 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
1012 1 : GDALDataTypeIsFloating(eAutoScaleType))
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_NotSupported,
1015 : "Unsupported value for "
1016 : "AUTOSCALE_DATA_TYPE");
1017 0 : return false;
1018 : }
1019 : }
1020 : else
1021 : {
1022 4 : aosArrayCO.AddString(pszOption);
1023 : }
1024 : }
1025 : }
1026 : }
1027 :
1028 : auto oIterDimName =
1029 46 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1030 46 : const auto &srcArrayType = srcArray->GetDataType();
1031 :
1032 46 : std::shared_ptr<GDALMDArray> dstArray;
1033 :
1034 : // Only autoscale non-indexing variables
1035 46 : bool bHasOffset = false;
1036 46 : bool bHasScale = false;
1037 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1038 4 : (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1039 2 : srcArrayType.GetNumericDataType() == GDT_Float32 ||
1040 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1041 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1042 50 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1043 48 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1044 : {
1045 2 : constexpr bool bApproxOK = false;
1046 2 : constexpr bool bForce = true;
1047 2 : double dfMin = 0.0;
1048 2 : double dfMax = 0.0;
1049 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1050 : nullptr, nullptr, nullptr, nullptr,
1051 2 : nullptr) != CE_None)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "Could not retrieve statistics for array %s",
1055 0 : srcArray->GetName().c_str());
1056 0 : return false;
1057 : }
1058 2 : double dfDTMin = 0;
1059 2 : double dfDTMax = 0;
1060 : #define setDTMinMax(ctype) \
1061 : do \
1062 : { \
1063 : dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest()); \
1064 : dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max()); \
1065 : } while (0)
1066 :
1067 2 : switch (eAutoScaleType)
1068 : {
1069 0 : case GDT_Byte:
1070 0 : setDTMinMax(GByte);
1071 0 : break;
1072 0 : case GDT_Int8:
1073 0 : setDTMinMax(GInt8);
1074 0 : break;
1075 1 : case GDT_UInt16:
1076 1 : setDTMinMax(GUInt16);
1077 1 : break;
1078 1 : case GDT_Int16:
1079 1 : setDTMinMax(GInt16);
1080 1 : break;
1081 0 : case GDT_UInt32:
1082 0 : setDTMinMax(GUInt32);
1083 0 : break;
1084 0 : case GDT_Int32:
1085 0 : setDTMinMax(GInt32);
1086 0 : break;
1087 0 : case GDT_UInt64:
1088 0 : setDTMinMax(std::uint64_t);
1089 0 : break;
1090 0 : case GDT_Int64:
1091 0 : setDTMinMax(std::int64_t);
1092 0 : break;
1093 0 : case GDT_Float16:
1094 : case GDT_Float32:
1095 : case GDT_Float64:
1096 : case GDT_Unknown:
1097 : case GDT_CInt16:
1098 : case GDT_CInt32:
1099 : case GDT_CFloat16:
1100 : case GDT_CFloat32:
1101 : case GDT_CFloat64:
1102 : case GDT_TypeCount:
1103 0 : CPLAssert(false);
1104 : }
1105 :
1106 : dstArray =
1107 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1108 4 : GDALExtendedDataType::Create(eAutoScaleType),
1109 4 : aosArrayCO.List());
1110 2 : if (!dstArray)
1111 0 : return !bStrict;
1112 :
1113 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1114 : {
1115 : // If there's a nodata value in the source array, reserve
1116 : // DTMax for that purpose in the target scaled array
1117 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AppDefined,
1120 : "Cannot set nodata value");
1121 0 : return false;
1122 : }
1123 1 : dfDTMax--;
1124 : }
1125 2 : const double dfScale =
1126 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1127 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1128 :
1129 4 : if (!dstArray->SetOffset(dfOffset) ||
1130 2 : !dstArray->SetScale(dfScale))
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Cannot set scale/offset");
1134 0 : return false;
1135 : }
1136 :
1137 2 : auto poUnscaled = dstArray->GetUnscaled();
1138 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1139 : {
1140 1 : poUnscaled->SetNoDataValue(
1141 : srcArray->GetNoDataValueAsDouble());
1142 : }
1143 :
1144 : // Copy source array into unscaled array
1145 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1146 : nCurCost, nTotalCost, pfnProgress,
1147 2 : pProgressData))
1148 : {
1149 0 : return false;
1150 : }
1151 : }
1152 : else
1153 : {
1154 88 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1155 88 : srcArrayType, aosArrayCO.List());
1156 44 : if (!dstArray)
1157 0 : return !bStrict;
1158 :
1159 88 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1160 : nCurCost, nTotalCost, pfnProgress,
1161 44 : pProgressData))
1162 : {
1163 0 : return false;
1164 : }
1165 : }
1166 :
1167 : // If this array is the indexing variable of a dimension, link them
1168 : // together.
1169 46 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1170 : {
1171 : auto oCorrespondingDimIter =
1172 20 : mapExistingDstDims.find(oIterDimName->second);
1173 20 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1174 : {
1175 : CPLErrorStateBackuper oErrorStateBackuper(
1176 20 : CPLQuietErrorHandler);
1177 40 : oCorrespondingDimIter->second->SetIndexingVariable(
1178 20 : std::move(dstArray));
1179 : }
1180 : }
1181 :
1182 46 : return true;
1183 25 : };
1184 :
1185 50 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1186 :
1187 : // Start by copying arrays that are indexing variables of dimensions
1188 71 : for (const auto &name : arrayNames)
1189 : {
1190 46 : auto srcArray = poSrcGroup->OpenMDArray(name);
1191 46 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1192 :
1193 46 : if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1194 46 : srcArray->GetName()))
1195 : {
1196 20 : if (!CopyArray(srcArray))
1197 0 : return false;
1198 : }
1199 : }
1200 :
1201 : // Then copy regular arrays
1202 71 : for (const auto &name : arrayNames)
1203 : {
1204 46 : auto srcArray = poSrcGroup->OpenMDArray(name);
1205 46 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1206 :
1207 46 : if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1208 46 : srcArray->GetName()))
1209 : {
1210 26 : if (!CopyArray(srcArray))
1211 0 : return false;
1212 : }
1213 : }
1214 :
1215 50 : const auto groupNames = poSrcGroup->GetGroupNames();
1216 31 : for (const auto &name : groupNames)
1217 : {
1218 6 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1219 6 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1220 6 : auto dstSubGroup = CreateGroup(name);
1221 6 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1222 12 : if (!dstSubGroup->CopyFrom(
1223 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1224 6 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1225 0 : return false;
1226 : }
1227 :
1228 25 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1229 0 : return false;
1230 :
1231 25 : return true;
1232 : }
1233 0 : catch (const std::exception &e)
1234 : {
1235 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1236 0 : return false;
1237 : }
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* GetInnerMostGroup() */
1242 : /************************************************************************/
1243 :
1244 : //! @cond Doxygen_Suppress
1245 : const GDALGroup *
1246 1275 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1247 : std::shared_ptr<GDALGroup> &curGroupHolder,
1248 : std::string &osLastPart) const
1249 : {
1250 1275 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1251 6 : return nullptr;
1252 1269 : const GDALGroup *poCurGroup = this;
1253 : CPLStringList aosTokens(
1254 2538 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1255 1269 : if (aosTokens.size() == 0)
1256 : {
1257 0 : return nullptr;
1258 : }
1259 :
1260 1610 : for (int i = 0; i < aosTokens.size() - 1; i++)
1261 : {
1262 349 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1263 349 : if (!curGroupHolder)
1264 : {
1265 8 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1266 : aosTokens[i]);
1267 8 : return nullptr;
1268 : }
1269 341 : poCurGroup = curGroupHolder.get();
1270 : }
1271 1261 : osLastPart = aosTokens[aosTokens.size() - 1];
1272 1261 : return poCurGroup;
1273 : }
1274 :
1275 : //! @endcond
1276 :
1277 : /************************************************************************/
1278 : /* OpenMDArrayFromFullname() */
1279 : /************************************************************************/
1280 :
1281 : /** Get an array from its fully qualified name */
1282 : std::shared_ptr<GDALMDArray>
1283 534 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1284 : CSLConstList papszOptions) const
1285 : {
1286 1068 : std::string osName;
1287 534 : std::shared_ptr<GDALGroup> curGroupHolder;
1288 534 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1289 534 : if (poGroup == nullptr)
1290 10 : return nullptr;
1291 524 : return poGroup->OpenMDArray(osName, papszOptions);
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* ResolveMDArray() */
1296 : /************************************************************************/
1297 :
1298 : /** Locate an array in a group and its subgroups by name.
1299 : *
1300 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1301 : * used
1302 : * Otherwise the search will start from the group identified by osStartingPath,
1303 : * and an array whose name is osName will be looked for in this group (if
1304 : * osStartingPath is empty or "/", then the current group is used). If there
1305 : * is no match, then a recursive descendent search will be made in its
1306 : * subgroups. If there is no match in the subgroups, then the parent (if
1307 : * existing) of the group pointed by osStartingPath will be used as the new
1308 : * starting point for the search.
1309 : *
1310 : * @param osName name, qualified or not
1311 : * @param osStartingPath fully qualified name of the (sub-)group from which
1312 : * the search should be started. If this is a non-empty
1313 : * string, the group on which this method is called should
1314 : * nominally be the root group (otherwise the path will
1315 : * be interpreted as from the current group)
1316 : * @param papszOptions options to pass to OpenMDArray()
1317 : * @since GDAL 3.2
1318 : */
1319 : std::shared_ptr<GDALMDArray>
1320 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1321 : const std::string &osStartingPath,
1322 : CSLConstList papszOptions) const
1323 : {
1324 19 : if (!osName.empty() && osName[0] == '/')
1325 : {
1326 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1327 1 : if (poArray)
1328 1 : return poArray;
1329 : }
1330 36 : std::string osPath(osStartingPath);
1331 36 : std::set<std::string> oSetAlreadyVisited;
1332 :
1333 : while (true)
1334 : {
1335 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1336 0 : std::shared_ptr<GDALGroup> poGroup;
1337 :
1338 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1339 22 : bool goOn = false;
1340 22 : if (osPath.empty() || osPath == "/")
1341 : {
1342 11 : goOn = true;
1343 : }
1344 : else
1345 : {
1346 22 : std::string osLastPart;
1347 : const GDALGroup *poGroupPtr =
1348 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1349 11 : if (poGroupPtr)
1350 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1351 22 : if (poGroup &&
1352 22 : !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1353 : {
1354 11 : oQueue.push(poGroup);
1355 11 : goOn = true;
1356 : }
1357 : }
1358 :
1359 22 : if (goOn)
1360 : {
1361 17 : do
1362 : {
1363 : const GDALGroup *groupPtr;
1364 39 : if (!oQueue.empty())
1365 : {
1366 28 : poGroup = oQueue.front();
1367 28 : oQueue.pop();
1368 28 : groupPtr = poGroup.get();
1369 : }
1370 : else
1371 : {
1372 11 : groupPtr = this;
1373 : }
1374 :
1375 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1376 39 : if (poArray)
1377 16 : return poArray;
1378 :
1379 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1380 47 : for (const auto &osGroupName : aosGroupNames)
1381 : {
1382 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1383 48 : if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1384 48 : poSubGroup->GetFullName()))
1385 : {
1386 24 : oQueue.push(poSubGroup);
1387 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1388 : }
1389 : }
1390 23 : } while (!oQueue.empty());
1391 : }
1392 :
1393 6 : if (osPath.empty() || osPath == "/")
1394 2 : break;
1395 :
1396 4 : const auto nPos = osPath.rfind('/');
1397 4 : if (nPos == 0)
1398 1 : osPath = "/";
1399 : else
1400 : {
1401 3 : if (nPos == std::string::npos)
1402 0 : break;
1403 3 : osPath.resize(nPos);
1404 : }
1405 4 : }
1406 2 : return nullptr;
1407 : }
1408 :
1409 : /************************************************************************/
1410 : /* OpenGroupFromFullname() */
1411 : /************************************************************************/
1412 :
1413 : /** Get a group from its fully qualified name.
1414 : * @since GDAL 3.2
1415 : */
1416 : std::shared_ptr<GDALGroup>
1417 563 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1418 : CSLConstList papszOptions) const
1419 : {
1420 1126 : std::string osName;
1421 563 : std::shared_ptr<GDALGroup> curGroupHolder;
1422 563 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1423 563 : if (poGroup == nullptr)
1424 2 : return nullptr;
1425 561 : return poGroup->OpenGroup(osName, papszOptions);
1426 : }
1427 :
1428 : /************************************************************************/
1429 : /* OpenDimensionFromFullname() */
1430 : /************************************************************************/
1431 :
1432 : /** Get a dimension from its fully qualified name */
1433 : std::shared_ptr<GDALDimension>
1434 167 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1435 : {
1436 334 : std::string osName;
1437 167 : std::shared_ptr<GDALGroup> curGroupHolder;
1438 167 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1439 167 : if (poGroup == nullptr)
1440 2 : return nullptr;
1441 330 : auto dims(poGroup->GetDimensions());
1442 286 : for (auto &dim : dims)
1443 : {
1444 241 : if (dim->GetName() == osName)
1445 120 : return dim;
1446 : }
1447 45 : return nullptr;
1448 : }
1449 :
1450 : /************************************************************************/
1451 : /* ClearStatistics() */
1452 : /************************************************************************/
1453 :
1454 : /**
1455 : * \brief Clear statistics.
1456 : *
1457 : * @since GDAL 3.4
1458 : */
1459 0 : void GDALGroup::ClearStatistics()
1460 : {
1461 0 : auto groupNames = GetGroupNames();
1462 0 : for (const auto &name : groupNames)
1463 : {
1464 0 : auto subGroup = OpenGroup(name);
1465 0 : if (subGroup)
1466 : {
1467 0 : subGroup->ClearStatistics();
1468 : }
1469 : }
1470 :
1471 0 : auto arrayNames = GetMDArrayNames();
1472 0 : for (const auto &name : arrayNames)
1473 : {
1474 0 : auto array = OpenMDArray(name);
1475 0 : if (array)
1476 : {
1477 0 : array->ClearStatistics();
1478 : }
1479 : }
1480 0 : }
1481 :
1482 : /************************************************************************/
1483 : /* Rename() */
1484 : /************************************************************************/
1485 :
1486 : /** Rename the group.
1487 : *
1488 : * This is not implemented by all drivers.
1489 : *
1490 : * Drivers known to implement it: MEM, netCDF, ZARR.
1491 : *
1492 : * This is the same as the C function GDALGroupRename().
1493 : *
1494 : * @param osNewName New name.
1495 : *
1496 : * @return true in case of success
1497 : * @since GDAL 3.8
1498 : */
1499 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1500 : {
1501 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1502 0 : return false;
1503 : }
1504 :
1505 : /************************************************************************/
1506 : /* BaseRename() */
1507 : /************************************************************************/
1508 :
1509 : //! @cond Doxygen_Suppress
1510 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1511 : {
1512 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1513 8 : m_osFullName += osNewName;
1514 8 : m_osName = osNewName;
1515 :
1516 8 : NotifyChildrenOfRenaming();
1517 8 : }
1518 :
1519 : //! @endcond
1520 :
1521 : /************************************************************************/
1522 : /* ParentRenamed() */
1523 : /************************************************************************/
1524 :
1525 : //! @cond Doxygen_Suppress
1526 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1527 : {
1528 7 : m_osFullName = osNewParentFullName;
1529 7 : m_osFullName += "/";
1530 7 : m_osFullName += m_osName;
1531 :
1532 7 : NotifyChildrenOfRenaming();
1533 7 : }
1534 :
1535 : //! @endcond
1536 :
1537 : /************************************************************************/
1538 : /* Deleted() */
1539 : /************************************************************************/
1540 :
1541 : //! @cond Doxygen_Suppress
1542 22 : void GDALGroup::Deleted()
1543 : {
1544 22 : m_bValid = false;
1545 :
1546 22 : NotifyChildrenOfDeletion();
1547 22 : }
1548 :
1549 : //! @endcond
1550 :
1551 : /************************************************************************/
1552 : /* ParentDeleted() */
1553 : /************************************************************************/
1554 :
1555 : //! @cond Doxygen_Suppress
1556 3 : void GDALGroup::ParentDeleted()
1557 : {
1558 3 : Deleted();
1559 3 : }
1560 :
1561 : //! @endcond
1562 :
1563 : /************************************************************************/
1564 : /* CheckValidAndErrorOutIfNot() */
1565 : /************************************************************************/
1566 :
1567 : //! @cond Doxygen_Suppress
1568 12469 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1569 : {
1570 12469 : if (!m_bValid)
1571 : {
1572 14 : CPLError(CE_Failure, CPLE_AppDefined,
1573 : "This object has been deleted. No action on it is possible");
1574 : }
1575 12469 : return m_bValid;
1576 : }
1577 :
1578 : //! @endcond
1579 :
1580 : /************************************************************************/
1581 : /* ~GDALAbstractMDArray() */
1582 : /************************************************************************/
1583 :
1584 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1585 :
1586 : /************************************************************************/
1587 : /* GDALAbstractMDArray() */
1588 : /************************************************************************/
1589 :
1590 : //! @cond Doxygen_Suppress
1591 20955 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1592 20955 : const std::string &osName)
1593 : : m_osName(osName),
1594 : m_osFullName(
1595 20955 : !osParentName.empty()
1596 40207 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1597 82117 : : osName)
1598 : {
1599 20955 : }
1600 :
1601 : //! @endcond
1602 :
1603 : /************************************************************************/
1604 : /* GetDimensions() */
1605 : /************************************************************************/
1606 :
1607 : /** \fn GDALAbstractMDArray::GetDimensions() const
1608 : * \brief Return the dimensions of an attribute/array.
1609 : *
1610 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1611 : * similar to GDALAttributeGetDimensionsSize().
1612 : */
1613 :
1614 : /************************************************************************/
1615 : /* GetDataType() */
1616 : /************************************************************************/
1617 :
1618 : /** \fn GDALAbstractMDArray::GetDataType() const
1619 : * \brief Return the data type of an attribute/array.
1620 : *
1621 : * This is the same as the C functions GDALMDArrayGetDataType() and
1622 : * GDALAttributeGetDataType()
1623 : */
1624 :
1625 : /************************************************************************/
1626 : /* GetDimensionCount() */
1627 : /************************************************************************/
1628 :
1629 : /** Return the number of dimensions.
1630 : *
1631 : * Default implementation is GetDimensions().size(), and may be overridden by
1632 : * drivers if they have a faster / less expensive implementations.
1633 : *
1634 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1635 : * GDALAttributeGetDimensionCount().
1636 : *
1637 : */
1638 23516 : size_t GDALAbstractMDArray::GetDimensionCount() const
1639 : {
1640 23516 : return GetDimensions().size();
1641 : }
1642 :
1643 : /************************************************************************/
1644 : /* Rename() */
1645 : /************************************************************************/
1646 :
1647 : /** Rename the attribute/array.
1648 : *
1649 : * This is not implemented by all drivers.
1650 : *
1651 : * Drivers known to implement it: MEM, netCDF, Zarr.
1652 : *
1653 : * This is the same as the C functions GDALMDArrayRename() or
1654 : * GDALAttributeRename().
1655 : *
1656 : * @param osNewName New name.
1657 : *
1658 : * @return true in case of success
1659 : * @since GDAL 3.8
1660 : */
1661 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1662 : {
1663 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1664 0 : return false;
1665 : }
1666 :
1667 : /************************************************************************/
1668 : /* CopyValue() */
1669 : /************************************************************************/
1670 :
1671 : /** Convert a value from a source type to a destination type.
1672 : *
1673 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1674 : * that must be freed with CPLFree().
1675 : */
1676 80582 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1677 : const GDALExtendedDataType &srcType,
1678 : void *pDst,
1679 : const GDALExtendedDataType &dstType)
1680 : {
1681 157248 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1682 76666 : dstType.GetClass() == GEDTC_NUMERIC)
1683 : {
1684 76419 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1685 : dstType.GetNumericDataType(), 0, 1);
1686 76419 : return true;
1687 : }
1688 7865 : if (srcType.GetClass() == GEDTC_STRING &&
1689 3702 : dstType.GetClass() == GEDTC_STRING)
1690 : {
1691 : const char *srcStrPtr;
1692 3269 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1693 3269 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1694 3269 : *reinterpret_cast<void **>(pDst) = pszDup;
1695 3269 : return true;
1696 : }
1697 1141 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1698 247 : dstType.GetClass() == GEDTC_STRING)
1699 : {
1700 247 : const char *str = nullptr;
1701 247 : switch (srcType.GetNumericDataType())
1702 : {
1703 0 : case GDT_Unknown:
1704 0 : break;
1705 0 : case GDT_Byte:
1706 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1707 0 : break;
1708 3 : case GDT_Int8:
1709 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1710 3 : break;
1711 48 : case GDT_UInt16:
1712 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1713 48 : break;
1714 0 : case GDT_Int16:
1715 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1716 0 : break;
1717 8 : case GDT_UInt32:
1718 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1719 8 : break;
1720 60 : case GDT_Int32:
1721 60 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1722 60 : break;
1723 0 : case GDT_UInt64:
1724 : str =
1725 0 : CPLSPrintf(CPL_FRMT_GUIB,
1726 : static_cast<GUIntBig>(
1727 : *static_cast<const std::uint64_t *>(pSrc)));
1728 0 : break;
1729 24 : case GDT_Int64:
1730 24 : str = CPLSPrintf(CPL_FRMT_GIB,
1731 : static_cast<GIntBig>(
1732 : *static_cast<const std::int64_t *>(pSrc)));
1733 24 : break;
1734 0 : case GDT_Float16:
1735 0 : str = CPLSPrintf("%.5g",
1736 : double(*static_cast<const GFloat16 *>(pSrc)));
1737 0 : break;
1738 17 : case GDT_Float32:
1739 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1740 17 : break;
1741 85 : case GDT_Float64:
1742 85 : str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1743 85 : break;
1744 2 : case GDT_CInt16:
1745 : {
1746 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1747 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1748 2 : break;
1749 : }
1750 0 : case GDT_CInt32:
1751 : {
1752 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1753 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1754 0 : break;
1755 : }
1756 0 : case GDT_CFloat16:
1757 : {
1758 0 : const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1759 0 : str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1760 0 : break;
1761 : }
1762 0 : case GDT_CFloat32:
1763 : {
1764 0 : const float *src = static_cast<const float *>(pSrc);
1765 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1766 0 : break;
1767 : }
1768 0 : case GDT_CFloat64:
1769 : {
1770 0 : const double *src = static_cast<const double *>(pSrc);
1771 0 : str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1772 0 : break;
1773 : }
1774 0 : case GDT_TypeCount:
1775 0 : CPLAssert(false);
1776 : break;
1777 : }
1778 247 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1779 247 : *reinterpret_cast<void **>(pDst) = pszDup;
1780 247 : return true;
1781 : }
1782 1080 : if (srcType.GetClass() == GEDTC_STRING &&
1783 433 : dstType.GetClass() == GEDTC_NUMERIC)
1784 : {
1785 : const char *srcStrPtr;
1786 433 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1787 433 : if (dstType.GetNumericDataType() == GDT_Int64)
1788 : {
1789 2 : *(static_cast<int64_t *>(pDst)) =
1790 2 : srcStrPtr == nullptr ? 0
1791 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1792 : }
1793 431 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1794 : {
1795 2 : *(static_cast<uint64_t *>(pDst)) =
1796 2 : srcStrPtr == nullptr
1797 2 : ? 0
1798 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1799 : }
1800 : else
1801 : {
1802 : // FIXME GDT_UInt64
1803 429 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1804 429 : GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1805 : dstType.GetNumericDataType(), 0, 1);
1806 : }
1807 433 : return true;
1808 : }
1809 428 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1810 214 : dstType.GetClass() == GEDTC_COMPOUND)
1811 : {
1812 214 : const auto &srcComponents = srcType.GetComponents();
1813 214 : const auto &dstComponents = dstType.GetComponents();
1814 214 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1815 214 : GByte *pabyDst = static_cast<GByte *>(pDst);
1816 :
1817 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1818 428 : srcComponentMap;
1819 1025 : for (const auto &srcComp : srcComponents)
1820 : {
1821 811 : srcComponentMap[srcComp->GetName()] = &srcComp;
1822 : }
1823 582 : for (const auto &dstComp : dstComponents)
1824 : {
1825 368 : auto oIter = srcComponentMap.find(dstComp->GetName());
1826 368 : if (oIter == srcComponentMap.end())
1827 0 : return false;
1828 368 : const auto &srcComp = *(oIter->second);
1829 1104 : if (!GDALExtendedDataType::CopyValue(
1830 368 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1831 368 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1832 : {
1833 0 : return false;
1834 : }
1835 : }
1836 214 : return true;
1837 : }
1838 :
1839 0 : return false;
1840 : }
1841 :
1842 : /************************************************************************/
1843 : /* CopyValues() */
1844 : /************************************************************************/
1845 :
1846 : /** Convert severals value from a source type to a destination type.
1847 : *
1848 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1849 : * that must be freed with CPLFree().
1850 : */
1851 365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1852 : const GDALExtendedDataType &srcType,
1853 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1854 : const GDALExtendedDataType &dstType,
1855 : GPtrDiff_t nDstStrideInElts,
1856 : size_t nValues)
1857 : {
1858 : const auto nSrcStrideInBytes =
1859 365 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1860 : const auto nDstStrideInBytes =
1861 365 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1862 631 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1863 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1864 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1865 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1866 897 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1867 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1868 : {
1869 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1870 : static_cast<int>(nSrcStrideInBytes), pDst,
1871 : dstType.GetNumericDataType(),
1872 : static_cast<int>(nDstStrideInBytes), nValues);
1873 : }
1874 : else
1875 : {
1876 99 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1877 99 : GByte *pabyDst = static_cast<GByte *>(pDst);
1878 198 : for (size_t i = 0; i < nValues; ++i)
1879 : {
1880 99 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1881 0 : return false;
1882 99 : pabySrc += nSrcStrideInBytes;
1883 99 : pabyDst += nDstStrideInBytes;
1884 : }
1885 : }
1886 365 : return true;
1887 : }
1888 :
1889 : /************************************************************************/
1890 : /* CheckReadWriteParams() */
1891 : /************************************************************************/
1892 : //! @cond Doxygen_Suppress
1893 8186 : bool GDALAbstractMDArray::CheckReadWriteParams(
1894 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1895 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1896 : const void *buffer, const void *buffer_alloc_start,
1897 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1898 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1899 : {
1900 0 : const auto lamda_error = []()
1901 : {
1902 0 : CPLError(CE_Failure, CPLE_AppDefined,
1903 : "Not all elements pointed by buffer will fit in "
1904 : "[buffer_alloc_start, "
1905 : "buffer_alloc_start + buffer_alloc_size]");
1906 0 : };
1907 :
1908 8186 : const auto &dims = GetDimensions();
1909 8186 : if (dims.empty())
1910 : {
1911 3147 : if (buffer_alloc_start)
1912 : {
1913 2770 : const size_t elementSize = bufferDataType.GetSize();
1914 2770 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1915 2770 : const GByte *paby_buffer_alloc_start =
1916 : static_cast<const GByte *>(buffer_alloc_start);
1917 2770 : const GByte *paby_buffer_alloc_end =
1918 : paby_buffer_alloc_start + buffer_alloc_size;
1919 :
1920 2770 : if (paby_buffer < paby_buffer_alloc_start ||
1921 2770 : paby_buffer + elementSize > paby_buffer_alloc_end)
1922 : {
1923 0 : lamda_error();
1924 0 : return false;
1925 : }
1926 : }
1927 3147 : return true;
1928 : }
1929 :
1930 5039 : if (arrayStep == nullptr)
1931 : {
1932 1346 : tmp_arrayStep.resize(dims.size(), 1);
1933 1346 : arrayStep = tmp_arrayStep.data();
1934 : }
1935 14141 : for (size_t i = 0; i < dims.size(); i++)
1936 : {
1937 9102 : if (count[i] == 0)
1938 : {
1939 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1940 : static_cast<unsigned>(i));
1941 0 : return false;
1942 : }
1943 : }
1944 5039 : bool bufferStride_all_positive = true;
1945 5039 : if (bufferStride == nullptr)
1946 : {
1947 1052 : GPtrDiff_t stride = 1;
1948 : // To compute strides we must proceed from the fastest varying dimension
1949 : // (the last one), and then reverse the result
1950 2397 : for (size_t i = dims.size(); i != 0;)
1951 : {
1952 1345 : --i;
1953 1345 : tmp_bufferStride.push_back(stride);
1954 1345 : GUInt64 newStride = 0;
1955 : bool bOK;
1956 : try
1957 : {
1958 1345 : newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1959 2690 : CPLSM(static_cast<uint64_t>(count[i])))
1960 1345 : .v();
1961 1345 : bOK = static_cast<size_t>(newStride) == newStride &&
1962 1345 : newStride < std::numeric_limits<size_t>::max() / 2;
1963 : }
1964 0 : catch (...)
1965 : {
1966 0 : bOK = false;
1967 : }
1968 1345 : if (!bOK)
1969 : {
1970 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1971 0 : return false;
1972 : }
1973 1345 : stride = static_cast<GPtrDiff_t>(newStride);
1974 : }
1975 1052 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1976 1052 : bufferStride = tmp_bufferStride.data();
1977 : }
1978 : else
1979 : {
1980 11742 : for (size_t i = 0; i < dims.size(); i++)
1981 : {
1982 7756 : if (bufferStride[i] < 0)
1983 : {
1984 1 : bufferStride_all_positive = false;
1985 1 : break;
1986 : }
1987 : }
1988 : }
1989 14112 : for (size_t i = 0; i < dims.size(); i++)
1990 : {
1991 9083 : if (arrayStartIdx[i] >= dims[i]->GetSize())
1992 : {
1993 2 : CPLError(CE_Failure, CPLE_AppDefined,
1994 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1995 : static_cast<unsigned>(i),
1996 2 : static_cast<GUInt64>(arrayStartIdx[i]),
1997 2 : static_cast<GUInt64>(dims[i]->GetSize()));
1998 2 : return false;
1999 : }
2000 : bool bOverflow;
2001 9081 : if (arrayStep[i] >= 0)
2002 : {
2003 : try
2004 : {
2005 8486 : bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2006 8488 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2007 33947 : CPLSM(static_cast<uint64_t>(arrayStep[i])))
2008 8486 : .v() >= dims[i]->GetSize();
2009 : }
2010 1 : catch (...)
2011 : {
2012 1 : bOverflow = true;
2013 : }
2014 8487 : if (bOverflow)
2015 : {
2016 5 : CPLError(CE_Failure, CPLE_AppDefined,
2017 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2018 : ">= " CPL_FRMT_GUIB,
2019 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2020 : static_cast<unsigned>(i),
2021 5 : static_cast<GUInt64>(dims[i]->GetSize()));
2022 5 : return false;
2023 : }
2024 : }
2025 : else
2026 : {
2027 : try
2028 : {
2029 594 : bOverflow =
2030 594 : arrayStartIdx[i] <
2031 594 : (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2032 1188 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2033 : ? (static_cast<uint64_t>(1) << 63)
2034 1188 : : static_cast<uint64_t>(-arrayStep[i])))
2035 594 : .v();
2036 : }
2037 0 : catch (...)
2038 : {
2039 0 : bOverflow = true;
2040 : }
2041 594 : if (bOverflow)
2042 : {
2043 3 : CPLError(
2044 : CE_Failure, CPLE_AppDefined,
2045 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2046 : static_cast<unsigned>(i), static_cast<unsigned>(i),
2047 : static_cast<unsigned>(i));
2048 3 : return false;
2049 : }
2050 : }
2051 : }
2052 :
2053 5029 : if (buffer_alloc_start)
2054 : {
2055 2585 : const size_t elementSize = bufferDataType.GetSize();
2056 2585 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2057 2585 : const GByte *paby_buffer_alloc_start =
2058 : static_cast<const GByte *>(buffer_alloc_start);
2059 2585 : const GByte *paby_buffer_alloc_end =
2060 : paby_buffer_alloc_start + buffer_alloc_size;
2061 2585 : if (bufferStride_all_positive)
2062 : {
2063 2585 : if (paby_buffer < paby_buffer_alloc_start)
2064 : {
2065 0 : lamda_error();
2066 0 : return false;
2067 : }
2068 2585 : GUInt64 nOffset = elementSize;
2069 7418 : for (size_t i = 0; i < dims.size(); i++)
2070 : {
2071 : try
2072 : {
2073 4833 : nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2074 4833 : CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2075 9666 : CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2076 19332 : CPLSM(static_cast<uint64_t>(elementSize)))
2077 4833 : .v();
2078 : }
2079 0 : catch (...)
2080 : {
2081 0 : lamda_error();
2082 0 : return false;
2083 : }
2084 : }
2085 : #if SIZEOF_VOIDP == 4
2086 : if (static_cast<size_t>(nOffset) != nOffset)
2087 : {
2088 : lamda_error();
2089 : return false;
2090 : }
2091 : #endif
2092 2585 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2093 : {
2094 0 : lamda_error();
2095 0 : return false;
2096 : }
2097 : }
2098 0 : else if (dims.size() < 31)
2099 : {
2100 : // Check all corners of the hypercube
2101 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2102 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2103 : {
2104 0 : const GByte *paby = paby_buffer;
2105 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2106 : i++)
2107 : {
2108 0 : if (iCornerCode & (1U << i))
2109 : {
2110 : // We should check for integer overflows
2111 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2112 : }
2113 : }
2114 0 : if (paby < paby_buffer_alloc_start ||
2115 0 : paby + elementSize > paby_buffer_alloc_end)
2116 : {
2117 0 : lamda_error();
2118 0 : return false;
2119 : }
2120 : }
2121 : }
2122 : }
2123 :
2124 5029 : return true;
2125 : }
2126 :
2127 : //! @endcond
2128 :
2129 : /************************************************************************/
2130 : /* Read() */
2131 : /************************************************************************/
2132 :
2133 : /** Read part or totality of a multidimensional array or attribute.
2134 : *
2135 : * This will extract the content of a hyper-rectangle from the array into
2136 : * a user supplied buffer.
2137 : *
2138 : * If bufferDataType is of type string, the values written in pDstBuffer
2139 : * will be char* pointers and the strings should be freed with CPLFree().
2140 : *
2141 : * This is the same as the C function GDALMDArrayRead().
2142 : *
2143 : * @param arrayStartIdx Values representing the starting index to read
2144 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2145 : * Array of GetDimensionCount() values. Must not be
2146 : * nullptr, unless for a zero-dimensional array.
2147 : *
2148 : * @param count Values representing the number of values to extract in
2149 : * each dimension.
2150 : * Array of GetDimensionCount() values. Must not be
2151 : * nullptr, unless for a zero-dimensional array.
2152 : *
2153 : * @param arrayStep Spacing between values to extract in each dimension.
2154 : * The spacing is in number of array elements, not bytes.
2155 : * If provided, must contain GetDimensionCount() values.
2156 : * If set to nullptr, [1, 1, ... 1] will be used as a
2157 : * default to indicate consecutive elements.
2158 : *
2159 : * @param bufferStride Spacing between values to store in pDstBuffer.
2160 : * The spacing is in number of array elements, not bytes.
2161 : * If provided, must contain GetDimensionCount() values.
2162 : * Negative values are possible (for example to reorder
2163 : * from bottom-to-top to top-to-bottom).
2164 : * If set to nullptr, will be set so that pDstBuffer is
2165 : * written in a compact way, with elements of the last /
2166 : * fastest varying dimension being consecutive.
2167 : *
2168 : * @param bufferDataType Data type of values in pDstBuffer.
2169 : *
2170 : * @param pDstBuffer User buffer to store the values read. Should be big
2171 : * enough to store the number of values indicated by
2172 : * count[] and with the spacing of bufferStride[].
2173 : *
2174 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2175 : * validity of pDstBuffer. pDstBufferAllocStart
2176 : * should be the pointer returned by the malloc() or equivalent call used to
2177 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2178 : * bufferStride[] values are all positive), but not necessarily. If specified,
2179 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2180 : * validation is needed, nullptr can be passed.
2181 : *
2182 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2183 : * validate the validity of pDstBuffer. This is the size of the buffer starting
2184 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2185 : * set to the appropriate value.
2186 : * If no validation is needed, 0 can be passed.
2187 : *
2188 : * @return true in case of success.
2189 : */
2190 2457 : bool GDALAbstractMDArray::Read(
2191 : const GUInt64 *arrayStartIdx, const size_t *count,
2192 : const GInt64 *arrayStep, // step in elements
2193 : const GPtrDiff_t *bufferStride, // stride in elements
2194 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2195 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2196 : {
2197 2457 : if (!GetDataType().CanConvertTo(bufferDataType))
2198 : {
2199 0 : CPLError(CE_Failure, CPLE_AppDefined,
2200 : "Array data type is not convertible to buffer data type");
2201 0 : return false;
2202 : }
2203 :
2204 4914 : std::vector<GInt64> tmp_arrayStep;
2205 4914 : std::vector<GPtrDiff_t> tmp_bufferStride;
2206 2457 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2207 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2208 : nDstBufferAllocSize, tmp_arrayStep,
2209 : tmp_bufferStride))
2210 : {
2211 0 : return false;
2212 : }
2213 :
2214 2457 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2215 2457 : pDstBuffer);
2216 : }
2217 :
2218 : /************************************************************************/
2219 : /* IWrite() */
2220 : /************************************************************************/
2221 :
2222 : //! @cond Doxygen_Suppress
2223 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2224 : const GInt64 *, const GPtrDiff_t *,
2225 : const GDALExtendedDataType &, const void *)
2226 : {
2227 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2228 1 : return false;
2229 : }
2230 :
2231 : //! @endcond
2232 :
2233 : /************************************************************************/
2234 : /* Write() */
2235 : /************************************************************************/
2236 :
2237 : /** Write part or totality of a multidimensional array or attribute.
2238 : *
2239 : * This will set the content of a hyper-rectangle into the array from
2240 : * a user supplied buffer.
2241 : *
2242 : * If bufferDataType is of type string, the values read from pSrcBuffer
2243 : * will be char* pointers.
2244 : *
2245 : * This is the same as the C function GDALMDArrayWrite().
2246 : *
2247 : * @param arrayStartIdx Values representing the starting index to write
2248 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2249 : * Array of GetDimensionCount() values. Must not be
2250 : * nullptr, unless for a zero-dimensional array.
2251 : *
2252 : * @param count Values representing the number of values to write in
2253 : * each dimension.
2254 : * Array of GetDimensionCount() values. Must not be
2255 : * nullptr, unless for a zero-dimensional array.
2256 : *
2257 : * @param arrayStep Spacing between values to write in each dimension.
2258 : * The spacing is in number of array elements, not bytes.
2259 : * If provided, must contain GetDimensionCount() values.
2260 : * If set to nullptr, [1, 1, ... 1] will be used as a
2261 : * default to indicate consecutive elements.
2262 : *
2263 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2264 : * The spacing is in number of array elements, not bytes.
2265 : * If provided, must contain GetDimensionCount() values.
2266 : * Negative values are possible (for example to reorder
2267 : * from bottom-to-top to top-to-bottom).
2268 : * If set to nullptr, will be set so that pSrcBuffer is
2269 : * written in a compact way, with elements of the last /
2270 : * fastest varying dimension being consecutive.
2271 : *
2272 : * @param bufferDataType Data type of values in pSrcBuffer.
2273 : *
2274 : * @param pSrcBuffer User buffer to read the values from. Should be big
2275 : * enough to store the number of values indicated by
2276 : * count[] and with the spacing of bufferStride[].
2277 : *
2278 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2279 : * validity of pSrcBuffer. pSrcBufferAllocStart
2280 : * should be the pointer returned by the malloc() or equivalent call used to
2281 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2282 : * bufferStride[] values are all positive), but not necessarily. If specified,
2283 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2284 : * validation is needed, nullptr can be passed.
2285 : *
2286 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2287 : * validate the validity of pSrcBuffer. This is the size of the buffer starting
2288 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2289 : * set to the appropriate value.
2290 : * If no validation is needed, 0 can be passed.
2291 : *
2292 : * @return true in case of success.
2293 : */
2294 1807 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2295 : const size_t *count, const GInt64 *arrayStep,
2296 : const GPtrDiff_t *bufferStride,
2297 : const GDALExtendedDataType &bufferDataType,
2298 : const void *pSrcBuffer,
2299 : const void *pSrcBufferAllocStart,
2300 : size_t nSrcBufferAllocSize)
2301 : {
2302 1807 : if (!bufferDataType.CanConvertTo(GetDataType()))
2303 : {
2304 0 : CPLError(CE_Failure, CPLE_AppDefined,
2305 : "Buffer data type is not convertible to array data type");
2306 0 : return false;
2307 : }
2308 :
2309 3614 : std::vector<GInt64> tmp_arrayStep;
2310 3614 : std::vector<GPtrDiff_t> tmp_bufferStride;
2311 1807 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2312 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2313 : nSrcBufferAllocSize, tmp_arrayStep,
2314 : tmp_bufferStride))
2315 : {
2316 0 : return false;
2317 : }
2318 :
2319 1807 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2320 1807 : pSrcBuffer);
2321 : }
2322 :
2323 : /************************************************************************/
2324 : /* GetTotalElementsCount() */
2325 : /************************************************************************/
2326 :
2327 : /** Return the total number of values in the array.
2328 : *
2329 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2330 : * and GDALAttributeGetTotalElementsCount().
2331 : *
2332 : */
2333 1114 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2334 : {
2335 1114 : const auto &dims = GetDimensions();
2336 1114 : if (dims.empty())
2337 558 : return 1;
2338 556 : GUInt64 nElts = 1;
2339 1232 : for (const auto &dim : dims)
2340 : {
2341 : try
2342 : {
2343 676 : nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2344 2028 : CPLSM(static_cast<uint64_t>(dim->GetSize())))
2345 676 : .v();
2346 : }
2347 0 : catch (...)
2348 : {
2349 0 : return 0;
2350 : }
2351 : }
2352 556 : return nElts;
2353 : }
2354 :
2355 : /************************************************************************/
2356 : /* GetBlockSize() */
2357 : /************************************************************************/
2358 :
2359 : /** Return the "natural" block size of the array along all dimensions.
2360 : *
2361 : * Some drivers might organize the array in tiles/blocks and reading/writing
2362 : * aligned on those tile/block boundaries will be more efficient.
2363 : *
2364 : * The returned number of elements in the vector is the same as
2365 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2366 : * the natural block size along the considered dimension.
2367 : * "Flat" arrays will typically return a vector of values set to 0.
2368 : *
2369 : * The default implementation will return a vector of values set to 0.
2370 : *
2371 : * This method is used by GetProcessingChunkSize().
2372 : *
2373 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2374 : * theoretical case of a 32-bit platform, this might exceed its size_t
2375 : * allocation capabilities.
2376 : *
2377 : * This is the same as the C function GDALMDArrayGetBlockSize().
2378 : *
2379 : * @return the block size, in number of elements along each dimension.
2380 : */
2381 275 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2382 : {
2383 275 : return std::vector<GUInt64>(GetDimensionCount());
2384 : }
2385 :
2386 : /************************************************************************/
2387 : /* GetProcessingChunkSize() */
2388 : /************************************************************************/
2389 :
2390 : /** \brief Return an optimal chunk size for read/write operations, given the
2391 : * natural block size and memory constraints specified.
2392 : *
2393 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2394 : * multiple of those returned by GetBlockSize() (unless the block define by
2395 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2396 : * returned by this method).
2397 : *
2398 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2399 : *
2400 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2401 : * chunk.
2402 : *
2403 : * @return the chunk size, in number of elements along each dimension.
2404 : */
2405 : std::vector<size_t>
2406 70 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2407 : {
2408 70 : const auto &dims = GetDimensions();
2409 70 : const auto &nDTSize = GetDataType().GetSize();
2410 70 : std::vector<size_t> anChunkSize;
2411 140 : auto blockSize = GetBlockSize();
2412 70 : CPLAssert(blockSize.size() == dims.size());
2413 70 : size_t nChunkSize = nDTSize;
2414 70 : bool bOverflow = false;
2415 70 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2416 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2417 : // [1, min(sizet_max, dim_size[i])]
2418 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2419 199 : for (size_t i = 0; i < dims.size(); i++)
2420 : {
2421 : const auto sizeDimI =
2422 258 : std::max(static_cast<size_t>(1),
2423 258 : static_cast<size_t>(
2424 258 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2425 129 : std::min(blockSize[i], dims[i]->GetSize()))));
2426 129 : anChunkSize.push_back(sizeDimI);
2427 129 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2428 : {
2429 4 : bOverflow = true;
2430 : }
2431 : else
2432 : {
2433 125 : nChunkSize *= sizeDimI;
2434 : }
2435 : }
2436 70 : if (nChunkSize == 0)
2437 0 : return anChunkSize;
2438 :
2439 : // If the product of all anChunkSize[i] does not fit on size_t, then
2440 : // set lowest anChunkSize[i] to 1.
2441 70 : if (bOverflow)
2442 : {
2443 2 : nChunkSize = nDTSize;
2444 2 : bOverflow = false;
2445 8 : for (size_t i = dims.size(); i > 0;)
2446 : {
2447 6 : --i;
2448 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2449 : {
2450 4 : bOverflow = true;
2451 4 : anChunkSize[i] = 1;
2452 : }
2453 : else
2454 : {
2455 2 : nChunkSize *= anChunkSize[i];
2456 : }
2457 : }
2458 : }
2459 :
2460 70 : nChunkSize = nDTSize;
2461 140 : std::vector<size_t> anAccBlockSizeFromStart;
2462 199 : for (size_t i = 0; i < dims.size(); i++)
2463 : {
2464 129 : nChunkSize *= anChunkSize[i];
2465 129 : anAccBlockSizeFromStart.push_back(nChunkSize);
2466 : }
2467 70 : if (nChunkSize <= nMaxChunkMemory / 2)
2468 : {
2469 66 : size_t nVoxelsFromEnd = 1;
2470 187 : for (size_t i = dims.size(); i > 0;)
2471 : {
2472 121 : --i;
2473 : const auto nCurBlockSize =
2474 121 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2475 121 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2476 121 : if (nMul >= 2)
2477 : {
2478 113 : const auto nSizeThisDim(dims[i]->GetSize());
2479 : const auto nBlocksThisDim =
2480 113 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2481 113 : anChunkSize[i] = static_cast<size_t>(std::min(
2482 113 : anChunkSize[i] *
2483 226 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2484 113 : nSizeThisDim));
2485 : }
2486 121 : nVoxelsFromEnd *= anChunkSize[i];
2487 : }
2488 : }
2489 70 : return anChunkSize;
2490 : }
2491 :
2492 : /************************************************************************/
2493 : /* BaseRename() */
2494 : /************************************************************************/
2495 :
2496 : //! @cond Doxygen_Suppress
2497 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2498 : {
2499 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2500 18 : m_osFullName += osNewName;
2501 18 : m_osName = osNewName;
2502 :
2503 18 : NotifyChildrenOfRenaming();
2504 18 : }
2505 :
2506 : //! @endcond
2507 :
2508 : //! @cond Doxygen_Suppress
2509 : /************************************************************************/
2510 : /* ParentRenamed() */
2511 : /************************************************************************/
2512 :
2513 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2514 : {
2515 50 : m_osFullName = osNewParentFullName;
2516 50 : m_osFullName += "/";
2517 50 : m_osFullName += m_osName;
2518 :
2519 50 : NotifyChildrenOfRenaming();
2520 50 : }
2521 :
2522 : //! @endcond
2523 :
2524 : /************************************************************************/
2525 : /* Deleted() */
2526 : /************************************************************************/
2527 :
2528 : //! @cond Doxygen_Suppress
2529 52 : void GDALAbstractMDArray::Deleted()
2530 : {
2531 52 : m_bValid = false;
2532 :
2533 52 : NotifyChildrenOfDeletion();
2534 52 : }
2535 :
2536 : //! @endcond
2537 :
2538 : /************************************************************************/
2539 : /* ParentDeleted() */
2540 : /************************************************************************/
2541 :
2542 : //! @cond Doxygen_Suppress
2543 28 : void GDALAbstractMDArray::ParentDeleted()
2544 : {
2545 28 : Deleted();
2546 28 : }
2547 :
2548 : //! @endcond
2549 :
2550 : /************************************************************************/
2551 : /* CheckValidAndErrorOutIfNot() */
2552 : /************************************************************************/
2553 :
2554 : //! @cond Doxygen_Suppress
2555 5815 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2556 : {
2557 5815 : if (!m_bValid)
2558 : {
2559 26 : CPLError(CE_Failure, CPLE_AppDefined,
2560 : "This object has been deleted. No action on it is possible");
2561 : }
2562 5815 : return m_bValid;
2563 : }
2564 :
2565 : //! @endcond
2566 :
2567 : /************************************************************************/
2568 : /* SetUnit() */
2569 : /************************************************************************/
2570 :
2571 : /** Set the variable unit.
2572 : *
2573 : * Values should conform as much as possible with those allowed by
2574 : * the NetCDF CF conventions:
2575 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2576 : * but others might be returned.
2577 : *
2578 : * Few examples are "meter", "degrees", "second", ...
2579 : * Empty value means unknown.
2580 : *
2581 : * This is the same as the C function GDALMDArraySetUnit()
2582 : *
2583 : * @note Driver implementation: optionally implemented.
2584 : *
2585 : * @param osUnit unit name.
2586 : * @return true in case of success.
2587 : */
2588 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2589 : {
2590 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2591 0 : return false;
2592 : }
2593 :
2594 : /************************************************************************/
2595 : /* GetUnit() */
2596 : /************************************************************************/
2597 :
2598 : /** Return the array unit.
2599 : *
2600 : * Values should conform as much as possible with those allowed by
2601 : * the NetCDF CF conventions:
2602 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2603 : * but others might be returned.
2604 : *
2605 : * Few examples are "meter", "degrees", "second", ...
2606 : * Empty value means unknown.
2607 : *
2608 : * This is the same as the C function GDALMDArrayGetUnit()
2609 : */
2610 5 : const std::string &GDALMDArray::GetUnit() const
2611 : {
2612 5 : static const std::string emptyString;
2613 5 : return emptyString;
2614 : }
2615 :
2616 : /************************************************************************/
2617 : /* SetSpatialRef() */
2618 : /************************************************************************/
2619 :
2620 : /** Assign a spatial reference system object to the array.
2621 : *
2622 : * This is the same as the C function GDALMDArraySetSpatialRef().
2623 : */
2624 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2625 : {
2626 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2627 0 : return false;
2628 : }
2629 :
2630 : /************************************************************************/
2631 : /* GetSpatialRef() */
2632 : /************************************************************************/
2633 :
2634 : /** Return the spatial reference system object associated with the array.
2635 : *
2636 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2637 : */
2638 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2639 : {
2640 4 : return nullptr;
2641 : }
2642 :
2643 : /************************************************************************/
2644 : /* GetRawNoDataValue() */
2645 : /************************************************************************/
2646 :
2647 : /** Return the nodata value as a "raw" value.
2648 : *
2649 : * The value returned might be nullptr in case of no nodata value. When
2650 : * a nodata value is registered, a non-nullptr will be returned whose size in
2651 : * bytes is GetDataType().GetSize().
2652 : *
2653 : * The returned value should not be modified or freed. It is valid until
2654 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2655 : * SetRawNoDataValue(), or any similar methods.
2656 : *
2657 : * @note Driver implementation: this method shall be implemented if nodata
2658 : * is supported.
2659 : *
2660 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2661 : *
2662 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2663 : */
2664 5 : const void *GDALMDArray::GetRawNoDataValue() const
2665 : {
2666 5 : return nullptr;
2667 : }
2668 :
2669 : /************************************************************************/
2670 : /* GetNoDataValueAsDouble() */
2671 : /************************************************************************/
2672 :
2673 : /** Return the nodata value as a double.
2674 : *
2675 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2676 : *
2677 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2678 : * a nodata value exists and can be converted to double. Might be nullptr.
2679 : *
2680 : * @return the nodata value as a double. A 0.0 value might also indicate the
2681 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2682 : * set to false then).
2683 : */
2684 22476 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2685 : {
2686 22476 : const void *pNoData = GetRawNoDataValue();
2687 22476 : double dfNoData = 0.0;
2688 22476 : const auto &eDT = GetDataType();
2689 22476 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2690 22476 : if (ok)
2691 : {
2692 22186 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2693 : GDT_Float64, 0, 1);
2694 : }
2695 22476 : if (pbHasNoData)
2696 437 : *pbHasNoData = ok;
2697 22476 : return dfNoData;
2698 : }
2699 :
2700 : /************************************************************************/
2701 : /* GetNoDataValueAsInt64() */
2702 : /************************************************************************/
2703 :
2704 : /** Return the nodata value as a Int64.
2705 : *
2706 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2707 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2708 : *
2709 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2710 : *
2711 : * @return the nodata value as a Int64
2712 : *
2713 : * @since GDAL 3.5
2714 : */
2715 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2716 : {
2717 12 : const void *pNoData = GetRawNoDataValue();
2718 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2719 12 : const auto &eDT = GetDataType();
2720 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2721 12 : if (ok)
2722 : {
2723 8 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2724 : GDT_Int64, 0, 1);
2725 : }
2726 12 : if (pbHasNoData)
2727 12 : *pbHasNoData = ok;
2728 12 : return nNoData;
2729 : }
2730 :
2731 : /************************************************************************/
2732 : /* GetNoDataValueAsUInt64() */
2733 : /************************************************************************/
2734 :
2735 : /** Return the nodata value as a UInt64.
2736 : *
2737 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2738 :
2739 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2740 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2741 : *
2742 : * @return the nodata value as a UInt64
2743 : *
2744 : * @since GDAL 3.5
2745 : */
2746 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2747 : {
2748 8 : const void *pNoData = GetRawNoDataValue();
2749 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2750 8 : const auto &eDT = GetDataType();
2751 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2752 8 : if (ok)
2753 : {
2754 6 : GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2755 : GDT_UInt64, 0, 1);
2756 : }
2757 8 : if (pbHasNoData)
2758 8 : *pbHasNoData = ok;
2759 8 : return nNoData;
2760 : }
2761 :
2762 : /************************************************************************/
2763 : /* SetRawNoDataValue() */
2764 : /************************************************************************/
2765 :
2766 : /** Set the nodata value as a "raw" value.
2767 : *
2768 : * The value passed might be nullptr in case of no nodata value. When
2769 : * a nodata value is registered, a non-nullptr whose size in
2770 : * bytes is GetDataType().GetSize() must be passed.
2771 : *
2772 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2773 : *
2774 : * @note Driver implementation: this method shall be implemented if setting
2775 : nodata
2776 : * is supported.
2777 :
2778 : * @return true in case of success.
2779 : */
2780 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2781 : {
2782 0 : CPLError(CE_Failure, CPLE_NotSupported,
2783 : "SetRawNoDataValue() not implemented");
2784 0 : return false;
2785 : }
2786 :
2787 : /************************************************************************/
2788 : /* SetNoDataValue() */
2789 : /************************************************************************/
2790 :
2791 : /** Set the nodata value as a double.
2792 : *
2793 : * If the natural data type of the attribute/array is not double, type
2794 : * conversion will occur to the type returned by GetDataType().
2795 : *
2796 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2797 : *
2798 : * @return true in case of success.
2799 : */
2800 57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2801 : {
2802 57 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2803 57 : bool bRet = false;
2804 57 : if (GDALExtendedDataType::CopyValue(
2805 114 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2806 57 : GetDataType()))
2807 : {
2808 57 : bRet = SetRawNoDataValue(pRawNoData);
2809 : }
2810 57 : CPLFree(pRawNoData);
2811 57 : return bRet;
2812 : }
2813 :
2814 : /************************************************************************/
2815 : /* SetNoDataValue() */
2816 : /************************************************************************/
2817 :
2818 : /** Set the nodata value as a Int64.
2819 : *
2820 : * If the natural data type of the attribute/array is not Int64, type conversion
2821 : * will occur to the type returned by GetDataType().
2822 : *
2823 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2824 : *
2825 : * @return true in case of success.
2826 : *
2827 : * @since GDAL 3.5
2828 : */
2829 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2830 : {
2831 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2832 3 : bool bRet = false;
2833 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2834 6 : GDALExtendedDataType::Create(GDT_Int64),
2835 3 : pRawNoData, GetDataType()))
2836 : {
2837 3 : bRet = SetRawNoDataValue(pRawNoData);
2838 : }
2839 3 : CPLFree(pRawNoData);
2840 3 : return bRet;
2841 : }
2842 :
2843 : /************************************************************************/
2844 : /* SetNoDataValue() */
2845 : /************************************************************************/
2846 :
2847 : /** Set the nodata value as a Int64.
2848 : *
2849 : * If the natural data type of the attribute/array is not Int64, type conversion
2850 : * will occur to the type returned by GetDataType().
2851 : *
2852 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2853 : *
2854 : * @return true in case of success.
2855 : *
2856 : * @since GDAL 3.5
2857 : */
2858 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2859 : {
2860 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2861 1 : bool bRet = false;
2862 1 : if (GDALExtendedDataType::CopyValue(
2863 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2864 1 : GetDataType()))
2865 : {
2866 1 : bRet = SetRawNoDataValue(pRawNoData);
2867 : }
2868 1 : CPLFree(pRawNoData);
2869 1 : return bRet;
2870 : }
2871 :
2872 : /************************************************************************/
2873 : /* Resize() */
2874 : /************************************************************************/
2875 :
2876 : /** Resize an array to new dimensions.
2877 : *
2878 : * Not all drivers may allow this operation, and with restrictions (e.g.
2879 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2880 : *
2881 : * Resizing a dimension used in other arrays will cause those other arrays
2882 : * to be resized.
2883 : *
2884 : * This is the same as the C function GDALMDArrayResize().
2885 : *
2886 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2887 : * new size of each indexing dimension.
2888 : * @param papszOptions Options. (Driver specific)
2889 : * @return true in case of success.
2890 : * @since GDAL 3.7
2891 : */
2892 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2893 : CPL_UNUSED CSLConstList papszOptions)
2894 : {
2895 0 : CPLError(CE_Failure, CPLE_NotSupported,
2896 : "Resize() is not supported for this array");
2897 0 : return false;
2898 : }
2899 :
2900 : /************************************************************************/
2901 : /* SetScale() */
2902 : /************************************************************************/
2903 :
2904 : /** Set the scale value to apply to raw values.
2905 : *
2906 : * unscaled_value = raw_value * GetScale() + GetOffset()
2907 : *
2908 : * This is the same as the C function GDALMDArraySetScale() /
2909 : * GDALMDArraySetScaleEx().
2910 : *
2911 : * @note Driver implementation: this method shall be implemented if setting
2912 : * scale is supported.
2913 : *
2914 : * @param dfScale scale
2915 : * @param eStorageType Data type to which create the potential attribute that
2916 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2917 : * implementation will decide automatically the data type. Note that changing
2918 : * the data type after initial setting might not be supported.
2919 : * @return true in case of success.
2920 : */
2921 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2922 : CPL_UNUSED GDALDataType eStorageType)
2923 : {
2924 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2925 0 : return false;
2926 : }
2927 :
2928 : /************************************************************************/
2929 : /* SetOffset) */
2930 : /************************************************************************/
2931 :
2932 : /** Set the offset value to apply to raw values.
2933 : *
2934 : * unscaled_value = raw_value * GetScale() + GetOffset()
2935 : *
2936 : * This is the same as the C function GDALMDArraySetOffset() /
2937 : * GDALMDArraySetOffsetEx().
2938 : *
2939 : * @note Driver implementation: this method shall be implemented if setting
2940 : * offset is supported.
2941 : *
2942 : * @param dfOffset Offset
2943 : * @param eStorageType Data type to which create the potential attribute that
2944 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2945 : * implementation will decide automatically the data type. Note that changing
2946 : * the data type after initial setting might not be supported.
2947 : * @return true in case of success.
2948 : */
2949 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2950 : CPL_UNUSED GDALDataType eStorageType)
2951 : {
2952 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2953 0 : return false;
2954 : }
2955 :
2956 : /************************************************************************/
2957 : /* GetScale() */
2958 : /************************************************************************/
2959 :
2960 : /** Get the scale value to apply to raw values.
2961 : *
2962 : * unscaled_value = raw_value * GetScale() + GetOffset()
2963 : *
2964 : * This is the same as the C function GDALMDArrayGetScale().
2965 : *
2966 : * @note Driver implementation: this method shall be implemented if gettings
2967 : * scale is supported.
2968 : *
2969 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2970 : * a scale value exists. Might be nullptr.
2971 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2972 : * the storage type of the scale value, when known/relevant. Otherwise will be
2973 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2974 : *
2975 : * @return the scale value. A 1.0 value might also indicate the
2976 : * absence of a scale value.
2977 : */
2978 20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
2979 : CPL_UNUSED GDALDataType *peStorageType) const
2980 : {
2981 20 : if (pbHasScale)
2982 20 : *pbHasScale = false;
2983 20 : return 1.0;
2984 : }
2985 :
2986 : /************************************************************************/
2987 : /* GetOffset() */
2988 : /************************************************************************/
2989 :
2990 : /** Get the offset value to apply to raw values.
2991 : *
2992 : * unscaled_value = raw_value * GetScale() + GetOffset()
2993 : *
2994 : * This is the same as the C function GDALMDArrayGetOffset().
2995 : *
2996 : * @note Driver implementation: this method shall be implemented if gettings
2997 : * offset is supported.
2998 : *
2999 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
3000 : * a offset value exists. Might be nullptr.
3001 : * @param peStorageType Pointer to a output GDALDataType that will be set to
3002 : * the storage type of the offset value, when known/relevant. Otherwise will be
3003 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3004 : *
3005 : * @return the offset value. A 0.0 value might also indicate the
3006 : * absence of a offset value.
3007 : */
3008 20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3009 : CPL_UNUSED GDALDataType *peStorageType) const
3010 : {
3011 20 : if (pbHasOffset)
3012 20 : *pbHasOffset = false;
3013 20 : return 0.0;
3014 : }
3015 :
3016 : /************************************************************************/
3017 : /* ProcessPerChunk() */
3018 : /************************************************************************/
3019 :
3020 : namespace
3021 : {
3022 : enum class Caller
3023 : {
3024 : CALLER_END_OF_LOOP,
3025 : CALLER_IN_LOOP,
3026 : };
3027 : }
3028 :
3029 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
3030 : *
3031 : * This method is to be used when doing operations on an array, or a subset of
3032 : * it, in a chunk by chunk way.
3033 : *
3034 : * @param arrayStartIdx Values representing the starting index to use
3035 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3036 : * Array of GetDimensionCount() values. Must not be
3037 : * nullptr, unless for a zero-dimensional array.
3038 : *
3039 : * @param count Values representing the number of values to use in
3040 : * each dimension.
3041 : * Array of GetDimensionCount() values. Must not be
3042 : * nullptr, unless for a zero-dimensional array.
3043 : *
3044 : * @param chunkSize Values representing the chunk size in each dimension.
3045 : * Might typically the output of GetProcessingChunkSize().
3046 : * Array of GetDimensionCount() values. Must not be
3047 : * nullptr, unless for a zero-dimensional array.
3048 : *
3049 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3050 : * Must NOT be nullptr.
3051 : *
3052 : * @param pUserData Pointer to pass as the value of the pUserData argument
3053 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3054 : *
3055 : * @return true in case of success.
3056 : */
3057 68 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3058 : const GUInt64 *count,
3059 : const size_t *chunkSize,
3060 : FuncProcessPerChunkType pfnFunc,
3061 : void *pUserData)
3062 : {
3063 68 : const auto &dims = GetDimensions();
3064 68 : if (dims.empty())
3065 : {
3066 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3067 : }
3068 :
3069 : // Sanity check
3070 66 : size_t nTotalChunkSize = 1;
3071 172 : for (size_t i = 0; i < dims.size(); i++)
3072 : {
3073 113 : const auto nSizeThisDim(dims[i]->GetSize());
3074 113 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3075 111 : arrayStartIdx[i] > nSizeThisDim - count[i])
3076 : {
3077 4 : CPLError(CE_Failure, CPLE_AppDefined,
3078 : "Inconsistent arrayStartIdx[] / count[] values "
3079 : "regarding array size");
3080 4 : return false;
3081 : }
3082 216 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3083 107 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3084 : {
3085 3 : CPLError(CE_Failure, CPLE_AppDefined,
3086 : "Inconsistent chunkSize[] values");
3087 3 : return false;
3088 : }
3089 106 : nTotalChunkSize *= chunkSize[i];
3090 : }
3091 :
3092 59 : size_t dimIdx = 0;
3093 118 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3094 118 : std::vector<size_t> chunkCount(dims.size());
3095 :
3096 : struct Stack
3097 : {
3098 : GUInt64 nBlockCounter = 0;
3099 : GUInt64 nBlocksMinusOne = 0;
3100 : size_t first_count = 0; // only used if nBlocks > 1
3101 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3102 : };
3103 :
3104 118 : std::vector<Stack> stack(dims.size());
3105 59 : GUInt64 iCurChunk = 0;
3106 59 : GUInt64 nChunkCount = 1;
3107 164 : for (size_t i = 0; i < dims.size(); i++)
3108 : {
3109 105 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3110 105 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3111 105 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3112 105 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3113 105 : if (stack[i].nBlocksMinusOne == 0)
3114 : {
3115 100 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3116 100 : chunkCount[i] = static_cast<size_t>(count[i]);
3117 : }
3118 : else
3119 : {
3120 5 : stack[i].first_count = static_cast<size_t>(
3121 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3122 : }
3123 : }
3124 :
3125 59 : lbl_next_depth:
3126 274 : if (dimIdx == dims.size())
3127 : {
3128 92 : ++iCurChunk;
3129 92 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3130 : iCurChunk, nChunkCount, pUserData))
3131 : {
3132 0 : return false;
3133 : }
3134 : }
3135 : else
3136 : {
3137 182 : if (stack[dimIdx].nBlocksMinusOne != 0)
3138 : {
3139 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3140 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3141 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3142 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3143 : while (true)
3144 : {
3145 33 : dimIdx++;
3146 33 : goto lbl_next_depth;
3147 33 : lbl_return_to_caller_in_loop:
3148 33 : --stack[dimIdx].nBlockCounter;
3149 33 : if (stack[dimIdx].nBlockCounter == 0)
3150 11 : break;
3151 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3152 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3153 : }
3154 :
3155 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3156 22 : chunkCount[dimIdx] =
3157 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3158 11 : chunkArrayStartIdx[dimIdx]);
3159 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3160 : }
3161 182 : dimIdx++;
3162 182 : goto lbl_next_depth;
3163 182 : lbl_return_to_caller_end_of_loop:
3164 182 : if (dimIdx == 0)
3165 59 : goto end;
3166 : }
3167 :
3168 215 : assert(dimIdx > 0);
3169 215 : dimIdx--;
3170 : // cppcheck-suppress negativeContainerIndex
3171 215 : switch (stack[dimIdx].return_point)
3172 : {
3173 182 : case Caller::CALLER_END_OF_LOOP:
3174 182 : goto lbl_return_to_caller_end_of_loop;
3175 33 : case Caller::CALLER_IN_LOOP:
3176 33 : goto lbl_return_to_caller_in_loop;
3177 : }
3178 59 : end:
3179 59 : return true;
3180 : }
3181 :
3182 : /************************************************************************/
3183 : /* GDALAttribute() */
3184 : /************************************************************************/
3185 :
3186 : //! @cond Doxygen_Suppress
3187 14387 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3188 0 : CPL_UNUSED const std::string &osName)
3189 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3190 14387 : : GDALAbstractMDArray(osParentName, osName)
3191 : #endif
3192 : {
3193 14387 : }
3194 :
3195 : //! @endcond
3196 :
3197 : /************************************************************************/
3198 : /* GetDimensionSize() */
3199 : /************************************************************************/
3200 :
3201 : /** Return the size of the dimensions of the attribute.
3202 : *
3203 : * This will be an empty array for a scalar (single value) attribute.
3204 : *
3205 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3206 : */
3207 367 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3208 : {
3209 367 : const auto &dims = GetDimensions();
3210 367 : std::vector<GUInt64> ret;
3211 367 : ret.reserve(dims.size());
3212 461 : for (const auto &dim : dims)
3213 94 : ret.push_back(dim->GetSize());
3214 367 : return ret;
3215 : }
3216 :
3217 : /************************************************************************/
3218 : /* GDALRawResult() */
3219 : /************************************************************************/
3220 :
3221 : //! @cond Doxygen_Suppress
3222 155 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3223 155 : size_t nEltCount)
3224 310 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3225 155 : m_raw(raw)
3226 : {
3227 155 : }
3228 :
3229 : //! @endcond
3230 :
3231 : /************************************************************************/
3232 : /* GDALRawResult() */
3233 : /************************************************************************/
3234 :
3235 : /** Move constructor. */
3236 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3237 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3238 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3239 : {
3240 0 : other.m_nEltCount = 0;
3241 0 : other.m_nSize = 0;
3242 0 : other.m_raw = nullptr;
3243 0 : }
3244 :
3245 : /************************************************************************/
3246 : /* FreeMe() */
3247 : /************************************************************************/
3248 :
3249 155 : void GDALRawResult::FreeMe()
3250 : {
3251 155 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3252 : {
3253 50 : GByte *pabyPtr = m_raw;
3254 50 : const auto nDTSize(m_dt.GetSize());
3255 100 : for (size_t i = 0; i < m_nEltCount; ++i)
3256 : {
3257 50 : m_dt.FreeDynamicMemory(pabyPtr);
3258 50 : pabyPtr += nDTSize;
3259 : }
3260 : }
3261 155 : VSIFree(m_raw);
3262 155 : }
3263 :
3264 : /************************************************************************/
3265 : /* operator=() */
3266 : /************************************************************************/
3267 :
3268 : /** Move assignment. */
3269 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3270 : {
3271 0 : FreeMe();
3272 0 : m_dt = std::move(other.m_dt);
3273 0 : m_nEltCount = other.m_nEltCount;
3274 0 : m_nSize = other.m_nSize;
3275 0 : m_raw = other.m_raw;
3276 0 : other.m_nEltCount = 0;
3277 0 : other.m_nSize = 0;
3278 0 : other.m_raw = nullptr;
3279 0 : return *this;
3280 : }
3281 :
3282 : /************************************************************************/
3283 : /* ~GDALRawResult() */
3284 : /************************************************************************/
3285 :
3286 : /** Destructor. */
3287 155 : GDALRawResult::~GDALRawResult()
3288 : {
3289 155 : FreeMe();
3290 155 : }
3291 :
3292 : /************************************************************************/
3293 : /* StealData() */
3294 : /************************************************************************/
3295 :
3296 : //! @cond Doxygen_Suppress
3297 : /** Return buffer to caller which becomes owner of it.
3298 : * Only to be used by GDALAttributeReadAsRaw().
3299 : */
3300 6 : GByte *GDALRawResult::StealData()
3301 : {
3302 6 : GByte *ret = m_raw;
3303 6 : m_raw = nullptr;
3304 6 : m_nEltCount = 0;
3305 6 : m_nSize = 0;
3306 6 : return ret;
3307 : }
3308 :
3309 : //! @endcond
3310 :
3311 : /************************************************************************/
3312 : /* ReadAsRaw() */
3313 : /************************************************************************/
3314 :
3315 : /** Return the raw value of an attribute.
3316 : *
3317 : *
3318 : * This is the same as the C function GDALAttributeReadAsRaw().
3319 : */
3320 155 : GDALRawResult GDALAttribute::ReadAsRaw() const
3321 : {
3322 155 : const auto nEltCount(GetTotalElementsCount());
3323 155 : const auto &dt(GetDataType());
3324 155 : const auto nDTSize(dt.GetSize());
3325 : GByte *res = static_cast<GByte *>(
3326 155 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3327 155 : if (!res)
3328 0 : return GDALRawResult(nullptr, dt, 0);
3329 155 : const auto &dims = GetDimensions();
3330 155 : const auto nDims = GetDimensionCount();
3331 310 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3332 310 : std::vector<size_t> count(1 + nDims);
3333 177 : for (size_t i = 0; i < nDims; i++)
3334 : {
3335 22 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3336 : }
3337 155 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3338 155 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3339 : {
3340 0 : VSIFree(res);
3341 0 : return GDALRawResult(nullptr, dt, 0);
3342 : }
3343 155 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3344 : }
3345 :
3346 : /************************************************************************/
3347 : /* ReadAsString() */
3348 : /************************************************************************/
3349 :
3350 : /** Return the value of an attribute as a string.
3351 : *
3352 : * The returned string should not be freed, and its lifetime does not
3353 : * excess a next call to ReadAsString() on the same object, or the deletion
3354 : * of the object itself.
3355 : *
3356 : * This function will only return the first element if there are several.
3357 : *
3358 : * This is the same as the C function GDALAttributeReadAsString()
3359 : *
3360 : * @return a string, or nullptr.
3361 : */
3362 1368 : const char *GDALAttribute::ReadAsString() const
3363 : {
3364 1368 : const auto nDims = GetDimensionCount();
3365 2736 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3366 2736 : std::vector<size_t> count(1 + nDims, 1);
3367 1368 : char *szRet = nullptr;
3368 1368 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3369 1368 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3370 4103 : sizeof(szRet)) ||
3371 1367 : szRet == nullptr)
3372 : {
3373 4 : return nullptr;
3374 : }
3375 1364 : m_osCachedVal = szRet;
3376 1364 : CPLFree(szRet);
3377 1364 : return m_osCachedVal.c_str();
3378 : }
3379 :
3380 : /************************************************************************/
3381 : /* ReadAsInt() */
3382 : /************************************************************************/
3383 :
3384 : /** Return the value of an attribute as a integer.
3385 : *
3386 : * This function will only return the first element if there are several.
3387 : *
3388 : * It can fail if its value can not be converted to integer.
3389 : *
3390 : * This is the same as the C function GDALAttributeReadAsInt()
3391 : *
3392 : * @return a integer, or INT_MIN in case of error.
3393 : */
3394 226 : int GDALAttribute::ReadAsInt() const
3395 : {
3396 226 : const auto nDims = GetDimensionCount();
3397 452 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3398 226 : std::vector<size_t> count(1 + nDims, 1);
3399 226 : int nRet = INT_MIN;
3400 226 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3401 452 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3402 452 : return nRet;
3403 : }
3404 :
3405 : /************************************************************************/
3406 : /* ReadAsInt64() */
3407 : /************************************************************************/
3408 :
3409 : /** Return the value of an attribute as an int64_t.
3410 : *
3411 : * This function will only return the first element if there are several.
3412 : *
3413 : * It can fail if its value can not be converted to long.
3414 : *
3415 : * This is the same as the C function GDALAttributeReadAsInt64()
3416 : *
3417 : * @return an int64_t, or INT64_MIN in case of error.
3418 : */
3419 54 : int64_t GDALAttribute::ReadAsInt64() const
3420 : {
3421 54 : const auto nDims = GetDimensionCount();
3422 108 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3423 54 : std::vector<size_t> count(1 + nDims, 1);
3424 54 : int64_t nRet = INT64_MIN;
3425 54 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3426 108 : GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3427 108 : return nRet;
3428 : }
3429 :
3430 : /************************************************************************/
3431 : /* ReadAsDouble() */
3432 : /************************************************************************/
3433 :
3434 : /** Return the value of an attribute as a double.
3435 : *
3436 : * This function will only return the first element if there are several.
3437 : *
3438 : * It can fail if its value can not be converted to double.
3439 : *
3440 : * This is the same as the C function GDALAttributeReadAsInt()
3441 : *
3442 : * @return a double value.
3443 : */
3444 349 : double GDALAttribute::ReadAsDouble() const
3445 : {
3446 349 : const auto nDims = GetDimensionCount();
3447 698 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3448 349 : std::vector<size_t> count(1 + nDims, 1);
3449 349 : double dfRet = 0;
3450 349 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3451 349 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3452 349 : sizeof(dfRet));
3453 698 : return dfRet;
3454 : }
3455 :
3456 : /************************************************************************/
3457 : /* ReadAsStringArray() */
3458 : /************************************************************************/
3459 :
3460 : /** Return the value of an attribute as an array of strings.
3461 : *
3462 : * This is the same as the C function GDALAttributeReadAsStringArray()
3463 : */
3464 126 : CPLStringList GDALAttribute::ReadAsStringArray() const
3465 : {
3466 126 : const auto nElts = GetTotalElementsCount();
3467 126 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3468 0 : return CPLStringList();
3469 : char **papszList = static_cast<char **>(
3470 126 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3471 126 : const auto &dims = GetDimensions();
3472 126 : const auto nDims = GetDimensionCount();
3473 252 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3474 252 : std::vector<size_t> count(1 + nDims);
3475 195 : for (size_t i = 0; i < nDims; i++)
3476 : {
3477 69 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3478 : }
3479 126 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3480 126 : GDALExtendedDataType::CreateString(), papszList, papszList,
3481 126 : sizeof(char *) * static_cast<int>(nElts));
3482 329 : for (int i = 0; i < static_cast<int>(nElts); i++)
3483 : {
3484 203 : if (papszList[i] == nullptr)
3485 13 : papszList[i] = CPLStrdup("");
3486 : }
3487 126 : return CPLStringList(papszList);
3488 : }
3489 :
3490 : /************************************************************************/
3491 : /* ReadAsIntArray() */
3492 : /************************************************************************/
3493 :
3494 : /** Return the value of an attribute as an array of integers.
3495 : *
3496 : * This is the same as the C function GDALAttributeReadAsIntArray().
3497 : */
3498 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3499 : {
3500 15 : const auto nElts = GetTotalElementsCount();
3501 : #if SIZEOF_VOIDP == 4
3502 : if (nElts > static_cast<size_t>(nElts))
3503 : return {};
3504 : #endif
3505 15 : std::vector<int> res(static_cast<size_t>(nElts));
3506 15 : const auto &dims = GetDimensions();
3507 15 : const auto nDims = GetDimensionCount();
3508 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3509 30 : std::vector<size_t> count(1 + nDims);
3510 32 : for (size_t i = 0; i < nDims; i++)
3511 : {
3512 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3513 : }
3514 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3515 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3516 15 : res.size() * sizeof(res[0]));
3517 30 : return res;
3518 : }
3519 :
3520 : /************************************************************************/
3521 : /* ReadAsInt64Array() */
3522 : /************************************************************************/
3523 :
3524 : /** Return the value of an attribute as an array of int64_t.
3525 : *
3526 : * This is the same as the C function GDALAttributeReadAsInt64Array().
3527 : */
3528 38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3529 : {
3530 38 : const auto nElts = GetTotalElementsCount();
3531 : #if SIZEOF_VOIDP == 4
3532 : if (nElts > static_cast<size_t>(nElts))
3533 : return {};
3534 : #endif
3535 38 : std::vector<int64_t> res(static_cast<size_t>(nElts));
3536 38 : const auto &dims = GetDimensions();
3537 38 : const auto nDims = GetDimensionCount();
3538 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3539 76 : std::vector<size_t> count(1 + nDims);
3540 76 : for (size_t i = 0; i < nDims; i++)
3541 : {
3542 38 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3543 : }
3544 38 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3545 76 : GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3546 38 : res.size() * sizeof(res[0]));
3547 76 : return res;
3548 : }
3549 :
3550 : /************************************************************************/
3551 : /* ReadAsDoubleArray() */
3552 : /************************************************************************/
3553 :
3554 : /** Return the value of an attribute as an array of double.
3555 : *
3556 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3557 : */
3558 88 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3559 : {
3560 88 : const auto nElts = GetTotalElementsCount();
3561 : #if SIZEOF_VOIDP == 4
3562 : if (nElts > static_cast<size_t>(nElts))
3563 : return {};
3564 : #endif
3565 88 : std::vector<double> res(static_cast<size_t>(nElts));
3566 88 : const auto &dims = GetDimensions();
3567 88 : const auto nDims = GetDimensionCount();
3568 176 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3569 176 : std::vector<size_t> count(1 + nDims);
3570 160 : for (size_t i = 0; i < nDims; i++)
3571 : {
3572 72 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3573 : }
3574 88 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3575 176 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3576 88 : res.size() * sizeof(res[0]));
3577 176 : return res;
3578 : }
3579 :
3580 : /************************************************************************/
3581 : /* Write() */
3582 : /************************************************************************/
3583 :
3584 : /** Write an attribute from raw values expressed in GetDataType()
3585 : *
3586 : * The values should be provided in the type of GetDataType() and there should
3587 : * be exactly GetTotalElementsCount() of them.
3588 : * If GetDataType() is a string, each value should be a char* pointer.
3589 : *
3590 : * This is the same as the C function GDALAttributeWriteRaw().
3591 : *
3592 : * @param pabyValue Buffer of nLen bytes.
3593 : * @param nLen Size of pabyValue in bytes. Should be equal to
3594 : * GetTotalElementsCount() * GetDataType().GetSize()
3595 : * @return true in case of success.
3596 : */
3597 96 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3598 : {
3599 96 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3600 : {
3601 0 : CPLError(CE_Failure, CPLE_AppDefined,
3602 : "Length is not of expected value");
3603 0 : return false;
3604 : }
3605 96 : const auto &dims = GetDimensions();
3606 96 : const auto nDims = GetDimensionCount();
3607 192 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3608 192 : std::vector<size_t> count(1 + nDims);
3609 119 : for (size_t i = 0; i < nDims; i++)
3610 : {
3611 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3612 : }
3613 96 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3614 96 : pabyValue, pabyValue, nLen);
3615 : }
3616 :
3617 : /************************************************************************/
3618 : /* Write() */
3619 : /************************************************************************/
3620 :
3621 : /** Write an attribute from a string value.
3622 : *
3623 : * Type conversion will be performed if needed. If the attribute contains
3624 : * multiple values, only the first one will be updated.
3625 : *
3626 : * This is the same as the C function GDALAttributeWriteString().
3627 : *
3628 : * @param pszValue Pointer to a string.
3629 : * @return true in case of success.
3630 : */
3631 304 : bool GDALAttribute::Write(const char *pszValue)
3632 : {
3633 304 : const auto nDims = GetDimensionCount();
3634 608 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3635 304 : std::vector<size_t> count(1 + nDims, 1);
3636 304 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3637 608 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3638 608 : sizeof(pszValue));
3639 : }
3640 :
3641 : /************************************************************************/
3642 : /* WriteInt() */
3643 : /************************************************************************/
3644 :
3645 : /** Write an attribute from a integer value.
3646 : *
3647 : * Type conversion will be performed if needed. If the attribute contains
3648 : * multiple values, only the first one will be updated.
3649 : *
3650 : * This is the same as the C function GDALAttributeWriteInt().
3651 : *
3652 : * @param nVal Value.
3653 : * @return true in case of success.
3654 : */
3655 22 : bool GDALAttribute::WriteInt(int nVal)
3656 : {
3657 22 : const auto nDims = GetDimensionCount();
3658 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3659 22 : std::vector<size_t> count(1 + nDims, 1);
3660 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3661 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3662 44 : sizeof(nVal));
3663 : }
3664 :
3665 : /************************************************************************/
3666 : /* WriteInt64() */
3667 : /************************************************************************/
3668 :
3669 : /** Write an attribute from an int64_t value.
3670 : *
3671 : * Type conversion will be performed if needed. If the attribute contains
3672 : * multiple values, only the first one will be updated.
3673 : *
3674 : * This is the same as the C function GDALAttributeWriteInt().
3675 : *
3676 : * @param nVal Value.
3677 : * @return true in case of success.
3678 : */
3679 11 : bool GDALAttribute::WriteInt64(int64_t nVal)
3680 : {
3681 11 : const auto nDims = GetDimensionCount();
3682 22 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3683 11 : std::vector<size_t> count(1 + nDims, 1);
3684 11 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3685 22 : GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3686 22 : sizeof(nVal));
3687 : }
3688 :
3689 : /************************************************************************/
3690 : /* Write() */
3691 : /************************************************************************/
3692 :
3693 : /** Write an attribute from a double value.
3694 : *
3695 : * Type conversion will be performed if needed. If the attribute contains
3696 : * multiple values, only the first one will be updated.
3697 : *
3698 : * This is the same as the C function GDALAttributeWriteDouble().
3699 : *
3700 : * @param dfVal Value.
3701 : * @return true in case of success.
3702 : */
3703 38 : bool GDALAttribute::Write(double dfVal)
3704 : {
3705 38 : const auto nDims = GetDimensionCount();
3706 76 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3707 38 : std::vector<size_t> count(1 + nDims, 1);
3708 38 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3709 76 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3710 76 : sizeof(dfVal));
3711 : }
3712 :
3713 : /************************************************************************/
3714 : /* Write() */
3715 : /************************************************************************/
3716 :
3717 : /** Write an attribute from an array of strings.
3718 : *
3719 : * Type conversion will be performed if needed.
3720 : *
3721 : * Exactly GetTotalElementsCount() strings must be provided
3722 : *
3723 : * This is the same as the C function GDALAttributeWriteStringArray().
3724 : *
3725 : * @param vals Array of strings.
3726 : * @return true in case of success.
3727 : */
3728 8 : bool GDALAttribute::Write(CSLConstList vals)
3729 : {
3730 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3731 : {
3732 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3733 1 : return false;
3734 : }
3735 7 : const auto nDims = GetDimensionCount();
3736 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3737 7 : std::vector<size_t> count(1 + nDims);
3738 7 : const auto &dims = GetDimensions();
3739 15 : for (size_t i = 0; i < nDims; i++)
3740 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3741 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3742 7 : GDALExtendedDataType::CreateString(), vals, vals,
3743 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3744 : }
3745 :
3746 : /************************************************************************/
3747 : /* Write() */
3748 : /************************************************************************/
3749 :
3750 : /** Write an attribute from an array of int.
3751 : *
3752 : * Type conversion will be performed if needed.
3753 : *
3754 : * Exactly GetTotalElementsCount() strings must be provided
3755 : *
3756 : * This is the same as the C function GDALAttributeWriteIntArray()
3757 : *
3758 : * @param vals Array of int.
3759 : * @param nVals Should be equal to GetTotalElementsCount().
3760 : * @return true in case of success.
3761 : */
3762 9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
3763 : {
3764 9 : if (nVals != GetTotalElementsCount())
3765 : {
3766 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3767 1 : return false;
3768 : }
3769 8 : const auto nDims = GetDimensionCount();
3770 16 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3771 8 : std::vector<size_t> count(1 + nDims);
3772 8 : const auto &dims = GetDimensions();
3773 16 : for (size_t i = 0; i < nDims; i++)
3774 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3775 8 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3776 8 : GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3777 16 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3778 : }
3779 :
3780 : /************************************************************************/
3781 : /* Write() */
3782 : /************************************************************************/
3783 :
3784 : /** Write an attribute from an array of int64_t.
3785 : *
3786 : * Type conversion will be performed if needed.
3787 : *
3788 : * Exactly GetTotalElementsCount() strings must be provided
3789 : *
3790 : * This is the same as the C function GDALAttributeWriteLongArray()
3791 : *
3792 : * @param vals Array of int64_t.
3793 : * @param nVals Should be equal to GetTotalElementsCount().
3794 : * @return true in case of success.
3795 : */
3796 10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3797 : {
3798 10 : if (nVals != GetTotalElementsCount())
3799 : {
3800 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3801 0 : return false;
3802 : }
3803 10 : const auto nDims = GetDimensionCount();
3804 20 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3805 10 : std::vector<size_t> count(1 + nDims);
3806 10 : const auto &dims = GetDimensions();
3807 20 : for (size_t i = 0; i < nDims; i++)
3808 10 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3809 10 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3810 10 : GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3811 10 : static_cast<size_t>(GetTotalElementsCount()) *
3812 10 : sizeof(int64_t));
3813 : }
3814 :
3815 : /************************************************************************/
3816 : /* Write() */
3817 : /************************************************************************/
3818 :
3819 : /** Write an attribute from an array of double.
3820 : *
3821 : * Type conversion will be performed if needed.
3822 : *
3823 : * Exactly GetTotalElementsCount() strings must be provided
3824 : *
3825 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3826 : *
3827 : * @param vals Array of double.
3828 : * @param nVals Should be equal to GetTotalElementsCount().
3829 : * @return true in case of success.
3830 : */
3831 7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3832 : {
3833 7 : if (nVals != GetTotalElementsCount())
3834 : {
3835 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3836 1 : return false;
3837 : }
3838 6 : const auto nDims = GetDimensionCount();
3839 12 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3840 6 : std::vector<size_t> count(1 + nDims);
3841 6 : const auto &dims = GetDimensions();
3842 13 : for (size_t i = 0; i < nDims; i++)
3843 7 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3844 6 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3845 6 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3846 12 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3847 : }
3848 :
3849 : /************************************************************************/
3850 : /* GDALMDArray() */
3851 : /************************************************************************/
3852 :
3853 : //! @cond Doxygen_Suppress
3854 6568 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3855 : CPL_UNUSED const std::string &osName,
3856 0 : const std::string &osContext)
3857 : :
3858 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3859 : GDALAbstractMDArray(osParentName, osName),
3860 : #endif
3861 6568 : m_osContext(osContext)
3862 : {
3863 6568 : }
3864 :
3865 : //! @endcond
3866 :
3867 : /************************************************************************/
3868 : /* GetTotalCopyCost() */
3869 : /************************************************************************/
3870 :
3871 : /** Return a total "cost" to copy the array.
3872 : *
3873 : * Used as a parameter for CopyFrom()
3874 : */
3875 50 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3876 : {
3877 100 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3878 100 : GetTotalElementsCount() * GetDataType().GetSize();
3879 : }
3880 :
3881 : /************************************************************************/
3882 : /* CopyFromAllExceptValues() */
3883 : /************************************************************************/
3884 :
3885 : //! @cond Doxygen_Suppress
3886 :
3887 175 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3888 : bool bStrict, GUInt64 &nCurCost,
3889 : const GUInt64 nTotalCost,
3890 : GDALProgressFunc pfnProgress,
3891 : void *pProgressData)
3892 : {
3893 : // Nodata setting must be one of the first things done for TileDB
3894 175 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3895 175 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3896 : {
3897 13 : SetRawNoDataValue(pNoData);
3898 : }
3899 :
3900 175 : const bool bThisIsUnscaledArray =
3901 175 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3902 350 : auto attrs = poSrcArray->GetAttributes();
3903 222 : for (const auto &attr : attrs)
3904 : {
3905 47 : const auto &osAttrName = attr->GetName();
3906 47 : if (bThisIsUnscaledArray)
3907 : {
3908 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3909 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3910 1 : osAttrName == "valid_range")
3911 : {
3912 1 : continue;
3913 : }
3914 : }
3915 :
3916 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3917 92 : attr->GetDataType());
3918 46 : if (!dstAttr)
3919 : {
3920 0 : if (bStrict)
3921 0 : return false;
3922 0 : continue;
3923 : }
3924 46 : auto raw = attr->ReadAsRaw();
3925 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3926 0 : return false;
3927 : }
3928 175 : if (!attrs.empty())
3929 : {
3930 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3931 46 : if (pfnProgress &&
3932 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3933 0 : return false;
3934 : }
3935 :
3936 175 : auto srcSRS = poSrcArray->GetSpatialRef();
3937 175 : if (srcSRS)
3938 : {
3939 11 : SetSpatialRef(srcSRS.get());
3940 : }
3941 :
3942 175 : const std::string &osUnit(poSrcArray->GetUnit());
3943 175 : if (!osUnit.empty())
3944 : {
3945 18 : SetUnit(osUnit);
3946 : }
3947 :
3948 175 : bool bGotValue = false;
3949 175 : GDALDataType eOffsetStorageType = GDT_Unknown;
3950 : const double dfOffset =
3951 175 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3952 175 : if (bGotValue)
3953 : {
3954 3 : SetOffset(dfOffset, eOffsetStorageType);
3955 : }
3956 :
3957 175 : bGotValue = false;
3958 175 : GDALDataType eScaleStorageType = GDT_Unknown;
3959 175 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3960 175 : if (bGotValue)
3961 : {
3962 3 : SetScale(dfScale, eScaleStorageType);
3963 : }
3964 :
3965 175 : return true;
3966 : }
3967 :
3968 : //! @endcond
3969 :
3970 : /************************************************************************/
3971 : /* CopyFrom() */
3972 : /************************************************************************/
3973 :
3974 : /** Copy the content of an array into a new (generally empty) array.
3975 : *
3976 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
3977 : * of some output drivers this is not recommended)
3978 : * @param poSrcArray Source array. Should NOT be nullptr.
3979 : * @param bStrict Whether to enable strict mode. In strict mode, any error will
3980 : * stop the copy. In relaxed mode, the copy will be attempted to
3981 : * be pursued.
3982 : * @param nCurCost Should be provided as a variable initially set to 0.
3983 : * @param nTotalCost Total cost from GetTotalCopyCost().
3984 : * @param pfnProgress Progress callback, or nullptr.
3985 : * @param pProgressData Progress user data, or nulptr.
3986 : *
3987 : * @return true in case of success (or partial success if bStrict == false).
3988 : */
3989 48 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
3990 : const GDALMDArray *poSrcArray, bool bStrict,
3991 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
3992 : GDALProgressFunc pfnProgress, void *pProgressData)
3993 : {
3994 48 : if (pfnProgress == nullptr)
3995 4 : pfnProgress = GDALDummyProgress;
3996 :
3997 48 : nCurCost += GDALMDArray::COPY_COST;
3998 :
3999 48 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4000 : pfnProgress, pProgressData))
4001 : {
4002 0 : return false;
4003 : }
4004 :
4005 48 : const auto &dims = poSrcArray->GetDimensions();
4006 48 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
4007 48 : if (dims.empty())
4008 : {
4009 2 : std::vector<GByte> abyTmp(nDTSize);
4010 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4011 2 : GetDataType(), &abyTmp[0]) &&
4012 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4013 4 : &abyTmp[0])) &&
4014 : bStrict)
4015 : {
4016 0 : return false;
4017 : }
4018 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4019 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4020 0 : return false;
4021 : }
4022 : else
4023 : {
4024 46 : std::vector<GUInt64> arrayStartIdx(dims.size());
4025 46 : std::vector<GUInt64> count(dims.size());
4026 125 : for (size_t i = 0; i < dims.size(); i++)
4027 : {
4028 79 : count[i] = static_cast<size_t>(dims[i]->GetSize());
4029 : }
4030 :
4031 : struct CopyFunc
4032 : {
4033 : GDALMDArray *poDstArray = nullptr;
4034 : std::vector<GByte> abyTmp{};
4035 : GDALProgressFunc pfnProgress = nullptr;
4036 : void *pProgressData = nullptr;
4037 : GUInt64 nCurCost = 0;
4038 : GUInt64 nTotalCost = 0;
4039 : GUInt64 nTotalBytesThisArray = 0;
4040 : bool bStop = false;
4041 :
4042 64 : static bool f(GDALAbstractMDArray *l_poSrcArray,
4043 : const GUInt64 *chunkArrayStartIdx,
4044 : const size_t *chunkCount, GUInt64 iCurChunk,
4045 : GUInt64 nChunkCount, void *pUserData)
4046 : {
4047 64 : const auto &dt(l_poSrcArray->GetDataType());
4048 64 : auto data = static_cast<CopyFunc *>(pUserData);
4049 64 : auto poDstArray = data->poDstArray;
4050 64 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4051 64 : nullptr, dt, &data->abyTmp[0]))
4052 : {
4053 0 : return false;
4054 : }
4055 : bool bRet =
4056 64 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4057 64 : nullptr, dt, &data->abyTmp[0]);
4058 64 : if (dt.NeedsFreeDynamicMemory())
4059 : {
4060 4 : const auto l_nDTSize = dt.GetSize();
4061 4 : GByte *ptr = &data->abyTmp[0];
4062 4 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4063 4 : size_t nEltCount = 1;
4064 8 : for (size_t i = 0; i < l_nDims; ++i)
4065 : {
4066 4 : nEltCount *= chunkCount[i];
4067 : }
4068 20 : for (size_t i = 0; i < nEltCount; i++)
4069 : {
4070 16 : dt.FreeDynamicMemory(ptr);
4071 16 : ptr += l_nDTSize;
4072 : }
4073 : }
4074 64 : if (!bRet)
4075 : {
4076 0 : return false;
4077 : }
4078 :
4079 64 : double dfCurCost =
4080 64 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4081 64 : data->nTotalBytesThisArray;
4082 64 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4083 : data->pProgressData))
4084 : {
4085 0 : data->bStop = true;
4086 0 : return false;
4087 : }
4088 :
4089 64 : return true;
4090 : }
4091 : };
4092 :
4093 46 : CopyFunc copyFunc;
4094 46 : copyFunc.poDstArray = this;
4095 46 : copyFunc.nCurCost = nCurCost;
4096 46 : copyFunc.nTotalCost = nTotalCost;
4097 46 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4098 46 : copyFunc.pfnProgress = pfnProgress;
4099 46 : copyFunc.pProgressData = pProgressData;
4100 : const char *pszSwathSize =
4101 46 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4102 : const size_t nMaxChunkSize =
4103 : pszSwathSize
4104 46 : ? static_cast<size_t>(
4105 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4106 1 : CPLAtoGIntBig(pszSwathSize)))
4107 : : static_cast<size_t>(
4108 45 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4109 45 : GDALGetCacheMax64() / 4));
4110 46 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4111 46 : size_t nRealChunkSize = nDTSize;
4112 125 : for (const auto &nChunkSize : anChunkSizes)
4113 : {
4114 79 : nRealChunkSize *= nChunkSize;
4115 : }
4116 : try
4117 : {
4118 46 : copyFunc.abyTmp.resize(nRealChunkSize);
4119 : }
4120 0 : catch (const std::exception &)
4121 : {
4122 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4123 : "Cannot allocate temporary buffer");
4124 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4125 0 : return false;
4126 : }
4127 137 : if (copyFunc.nTotalBytesThisArray != 0 &&
4128 45 : !const_cast<GDALMDArray *>(poSrcArray)
4129 45 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4130 : anChunkSizes.data(), CopyFunc::f,
4131 91 : ©Func) &&
4132 0 : (bStrict || copyFunc.bStop))
4133 : {
4134 0 : nCurCost += copyFunc.nTotalBytesThisArray;
4135 0 : return false;
4136 : }
4137 46 : nCurCost += copyFunc.nTotalBytesThisArray;
4138 : }
4139 :
4140 48 : return true;
4141 : }
4142 :
4143 : /************************************************************************/
4144 : /* GetStructuralInfo() */
4145 : /************************************************************************/
4146 :
4147 : /** Return structural information on the array.
4148 : *
4149 : * This may be the compression, etc..
4150 : *
4151 : * The return value should not be freed and is valid until GDALMDArray is
4152 : * released or this function called again.
4153 : *
4154 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
4155 : */
4156 95 : CSLConstList GDALMDArray::GetStructuralInfo() const
4157 : {
4158 95 : return nullptr;
4159 : }
4160 :
4161 : /************************************************************************/
4162 : /* AdviseRead() */
4163 : /************************************************************************/
4164 :
4165 : /** Advise driver of upcoming read requests.
4166 : *
4167 : * Some GDAL drivers operate more efficiently if they know in advance what
4168 : * set of upcoming read requests will be made. The AdviseRead() method allows
4169 : * an application to notify the driver of the region of interest.
4170 : *
4171 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
4172 : * accelerate access via some drivers. One such case is when reading through
4173 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4174 : * with the region of interest defined by AdviseRead())
4175 : *
4176 : * This is the same as the C function GDALMDArrayAdviseRead().
4177 : *
4178 : * @param arrayStartIdx Values representing the starting index to read
4179 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
4180 : * Array of GetDimensionCount() values.
4181 : * Can be nullptr as a synonymous for [0 for i in
4182 : * range(GetDimensionCount() ]
4183 : *
4184 : * @param count Values representing the number of values to extract in
4185 : * each dimension.
4186 : * Array of GetDimensionCount() values.
4187 : * Can be nullptr as a synonymous for
4188 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4189 : * range(GetDimensionCount() ]
4190 : *
4191 : * @param papszOptions Driver specific options, or nullptr. Consult driver
4192 : * documentation.
4193 : *
4194 : * @return true in case of success (ignoring the advice is a success)
4195 : *
4196 : * @since GDAL 3.2
4197 : */
4198 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4199 : CSLConstList papszOptions) const
4200 : {
4201 25 : const auto nDimCount = GetDimensionCount();
4202 25 : if (nDimCount == 0)
4203 2 : return true;
4204 :
4205 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4206 23 : if (arrayStartIdx == nullptr)
4207 : {
4208 0 : tmp_arrayStartIdx.resize(nDimCount);
4209 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4210 : }
4211 :
4212 46 : std::vector<size_t> tmp_count;
4213 23 : if (count == nullptr)
4214 : {
4215 0 : tmp_count.resize(nDimCount);
4216 0 : const auto &dims = GetDimensions();
4217 0 : for (size_t i = 0; i < nDimCount; i++)
4218 : {
4219 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4220 : #if SIZEOF_VOIDP < 8
4221 : if (nSize != static_cast<size_t>(nSize))
4222 : {
4223 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4224 : return false;
4225 : }
4226 : #endif
4227 0 : tmp_count[i] = static_cast<size_t>(nSize);
4228 : }
4229 0 : count = tmp_count.data();
4230 : }
4231 :
4232 46 : std::vector<GInt64> tmp_arrayStep;
4233 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4234 23 : const GInt64 *arrayStep = nullptr;
4235 23 : const GPtrDiff_t *bufferStride = nullptr;
4236 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4237 46 : GDALExtendedDataType::Create(GDT_Unknown),
4238 : nullptr, nullptr, 0, tmp_arrayStep,
4239 : tmp_bufferStride))
4240 : {
4241 1 : return false;
4242 : }
4243 :
4244 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4245 : }
4246 :
4247 : /************************************************************************/
4248 : /* IAdviseRead() */
4249 : /************************************************************************/
4250 :
4251 : //! @cond Doxygen_Suppress
4252 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4253 : CSLConstList /* papszOptions*/) const
4254 : {
4255 3 : return true;
4256 : }
4257 :
4258 : //! @endcond
4259 :
4260 : /************************************************************************/
4261 : /* MassageName() */
4262 : /************************************************************************/
4263 :
4264 : //! @cond Doxygen_Suppress
4265 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4266 : {
4267 32 : std::string ret;
4268 604 : for (const char ch : inputName)
4269 : {
4270 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4271 138 : ret += '_';
4272 : else
4273 434 : ret += ch;
4274 : }
4275 32 : return ret;
4276 : }
4277 :
4278 : //! @endcond
4279 :
4280 : /************************************************************************/
4281 : /* GetCacheRootGroup() */
4282 : /************************************************************************/
4283 :
4284 : //! @cond Doxygen_Suppress
4285 : std::shared_ptr<GDALGroup>
4286 1442 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4287 : std::string &osCacheFilenameOut) const
4288 : {
4289 1442 : const auto &osFilename = GetFilename();
4290 1442 : if (osFilename.empty())
4291 : {
4292 1 : CPLError(CE_Failure, CPLE_AppDefined,
4293 : "Cannot cache an array with an empty filename");
4294 1 : return nullptr;
4295 : }
4296 :
4297 1441 : osCacheFilenameOut = osFilename + ".gmac";
4298 1441 : if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4299 : {
4300 0 : const auto nPosQuestionMark = osFilename.find('?');
4301 0 : if (nPosQuestionMark != std::string::npos)
4302 : {
4303 : osCacheFilenameOut =
4304 0 : osFilename.substr(0, nPosQuestionMark)
4305 0 : .append(".gmac")
4306 0 : .append(osFilename.substr(nPosQuestionMark));
4307 : }
4308 : }
4309 1441 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4310 1441 : if (pszProxy != nullptr)
4311 7 : osCacheFilenameOut = pszProxy;
4312 :
4313 1441 : std::unique_ptr<GDALDataset> poDS;
4314 : VSIStatBufL sStat;
4315 1441 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4316 : {
4317 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4318 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4319 : nullptr, nullptr, nullptr));
4320 : }
4321 1441 : if (poDS)
4322 : {
4323 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4324 28 : return poDS->GetRootGroup();
4325 : }
4326 :
4327 1413 : if (bCanCreate)
4328 : {
4329 4 : const char *pszDrvName = "netCDF";
4330 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4331 4 : if (poDrv == nullptr)
4332 : {
4333 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4334 : pszDrvName);
4335 0 : return nullptr;
4336 : }
4337 : {
4338 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4339 8 : CPLErrorStateBackuper oErrorStateBackuper;
4340 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4341 : nullptr, nullptr));
4342 : }
4343 4 : if (!poDS)
4344 : {
4345 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4346 1 : if (pszProxy)
4347 : {
4348 1 : osCacheFilenameOut = pszProxy;
4349 1 : poDS.reset(poDrv->CreateMultiDimensional(
4350 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4351 : }
4352 : }
4353 4 : if (poDS)
4354 : {
4355 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4356 4 : return poDS->GetRootGroup();
4357 : }
4358 : else
4359 : {
4360 0 : CPLError(CE_Failure, CPLE_AppDefined,
4361 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4362 : "configuration option to write the cache in "
4363 : "another directory",
4364 : osCacheFilenameOut.c_str());
4365 : }
4366 : }
4367 :
4368 1409 : return nullptr;
4369 : }
4370 :
4371 : //! @endcond
4372 :
4373 : /************************************************************************/
4374 : /* Cache() */
4375 : /************************************************************************/
4376 :
4377 : /** Cache the content of the array into an auxiliary filename.
4378 : *
4379 : * The main purpose of this method is to be able to cache views that are
4380 : * expensive to compute, such as transposed arrays.
4381 : *
4382 : * The array will be stored in a file whose name is the one of
4383 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4384 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4385 : *
4386 : * If the .gmac file cannot be written next to the dataset, the
4387 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4388 : * directory.
4389 : *
4390 : * The GDALMDArray::Read() method will automatically use the cache when it
4391 : * exists. There is no timestamp checks between the source array and the cached
4392 : * array. If the source arrays changes, the cache must be manually deleted.
4393 : *
4394 : * This is the same as the C function GDALMDArrayCache()
4395 : *
4396 : * @note Driver implementation: optionally implemented.
4397 : *
4398 : * @param papszOptions List of options, null terminated, or NULL. Currently
4399 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4400 : * to specify the block size of the cached array.
4401 : * @return true in case of success.
4402 : */
4403 7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4404 : {
4405 14 : std::string osCacheFilename;
4406 14 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4407 7 : if (!poRG)
4408 1 : return false;
4409 :
4410 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4411 6 : if (poRG->OpenMDArray(osCachedArrayName))
4412 : {
4413 2 : CPLError(CE_Failure, CPLE_NotSupported,
4414 : "An array with same name %s already exists in %s",
4415 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4416 2 : return false;
4417 : }
4418 :
4419 8 : CPLStringList aosOptions;
4420 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4421 4 : const auto &aoDims = GetDimensions();
4422 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4423 4 : if (!aoDims.empty())
4424 : {
4425 : std::string osBlockSize(
4426 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4427 4 : if (osBlockSize.empty())
4428 : {
4429 6 : const auto anBlockSize = GetBlockSize();
4430 3 : int idxDim = 0;
4431 10 : for (auto nBlockSize : anBlockSize)
4432 : {
4433 7 : if (idxDim > 0)
4434 4 : osBlockSize += ',';
4435 7 : if (nBlockSize == 0)
4436 7 : nBlockSize = 256;
4437 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4438 : osBlockSize +=
4439 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4440 7 : idxDim++;
4441 : }
4442 : }
4443 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4444 :
4445 4 : int idxDim = 0;
4446 13 : for (const auto &poDim : aoDims)
4447 : {
4448 9 : auto poNewDim = poRG->CreateDimension(
4449 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4450 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4451 9 : if (!poNewDim)
4452 0 : return false;
4453 9 : aoNewDims.emplace_back(poNewDim);
4454 9 : idxDim++;
4455 : }
4456 : }
4457 :
4458 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4459 8 : GetDataType(), aosOptions.List());
4460 4 : if (!poCachedArray)
4461 : {
4462 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4463 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4464 0 : return false;
4465 : }
4466 :
4467 4 : GUInt64 nCost = 0;
4468 8 : return poCachedArray->CopyFrom(nullptr, this,
4469 : false, // strict
4470 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4471 : }
4472 :
4473 : /************************************************************************/
4474 : /* Read() */
4475 : /************************************************************************/
4476 :
4477 3899 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4478 : const GInt64 *arrayStep, // step in elements
4479 : const GPtrDiff_t *bufferStride, // stride in elements
4480 : const GDALExtendedDataType &bufferDataType,
4481 : void *pDstBuffer, const void *pDstBufferAllocStart,
4482 : size_t nDstBufferAllocSize) const
4483 : {
4484 3899 : if (!m_bHasTriedCachedArray)
4485 : {
4486 1748 : m_bHasTriedCachedArray = true;
4487 1748 : if (IsCacheable())
4488 : {
4489 1748 : const auto &osFilename = GetFilename();
4490 2986 : if (!osFilename.empty() &&
4491 2986 : !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4492 : {
4493 2456 : std::string osCacheFilename;
4494 2456 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4495 1228 : if (poRG)
4496 : {
4497 : const std::string osCachedArrayName(
4498 32 : MassageName(GetFullName()));
4499 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4500 16 : if (m_poCachedArray)
4501 : {
4502 6 : const auto &dims = GetDimensions();
4503 : const auto &cachedDims =
4504 6 : m_poCachedArray->GetDimensions();
4505 6 : const size_t nDims = dims.size();
4506 : bool ok =
4507 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4508 6 : cachedDims.size() == nDims;
4509 19 : for (size_t i = 0; ok && i < nDims; ++i)
4510 : {
4511 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4512 : }
4513 6 : if (ok)
4514 : {
4515 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4516 : osCachedArrayName.c_str(),
4517 : osCacheFilename.c_str());
4518 : }
4519 : else
4520 : {
4521 0 : CPLError(CE_Warning, CPLE_AppDefined,
4522 : "Cached array %s in %s has incompatible "
4523 : "characteristics with current array.",
4524 : osCachedArrayName.c_str(),
4525 : osCacheFilename.c_str());
4526 0 : m_poCachedArray.reset();
4527 : }
4528 : }
4529 : }
4530 : }
4531 : }
4532 : }
4533 :
4534 3899 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4535 3899 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4536 : {
4537 0 : CPLError(CE_Failure, CPLE_AppDefined,
4538 : "Array data type is not convertible to buffer data type");
4539 0 : return false;
4540 : }
4541 :
4542 7798 : std::vector<GInt64> tmp_arrayStep;
4543 7798 : std::vector<GPtrDiff_t> tmp_bufferStride;
4544 3899 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4545 : bufferStride, bufferDataType, pDstBuffer,
4546 : pDstBufferAllocStart, nDstBufferAllocSize,
4547 : tmp_arrayStep, tmp_bufferStride))
4548 : {
4549 9 : return false;
4550 : }
4551 :
4552 3890 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4553 3890 : bufferDataType, pDstBuffer);
4554 : }
4555 :
4556 : /************************************************************************/
4557 : /* GetRootGroup() */
4558 : /************************************************************************/
4559 :
4560 : /** Return the root group to which this arrays belongs too.
4561 : *
4562 : * Note that arrays may be free standing and some drivers may not implement
4563 : * this method, hence nullptr may be returned.
4564 : *
4565 : * It is used internally by the GetResampled() method to detect if GLT
4566 : * orthorectification is available.
4567 : *
4568 : * @return the root group, or nullptr.
4569 : * @since GDAL 3.8
4570 : */
4571 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4572 : {
4573 0 : return nullptr;
4574 : }
4575 :
4576 : //! @cond Doxygen_Suppress
4577 :
4578 : /************************************************************************/
4579 : /* IsTransposedRequest() */
4580 : /************************************************************************/
4581 :
4582 784 : bool GDALMDArray::IsTransposedRequest(
4583 : const size_t *count,
4584 : const GPtrDiff_t *bufferStride) const // stride in elements
4585 : {
4586 : /*
4587 : For example:
4588 : count = [2,3,4]
4589 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4590 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4591 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4592 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4593 : */
4594 784 : const size_t nDims(GetDimensionCount());
4595 784 : size_t nCurStrideForRowMajorStrides = 1;
4596 784 : bool bRowMajorStrides = true;
4597 784 : size_t nElts = 1;
4598 784 : size_t nLastIdx = 0;
4599 2157 : for (size_t i = nDims; i > 0;)
4600 : {
4601 1373 : --i;
4602 1373 : if (bufferStride[i] < 0)
4603 0 : return false;
4604 1373 : if (static_cast<size_t>(bufferStride[i]) !=
4605 : nCurStrideForRowMajorStrides)
4606 : {
4607 258 : bRowMajorStrides = false;
4608 : }
4609 : // Integer overflows have already been checked in CheckReadWriteParams()
4610 1373 : nCurStrideForRowMajorStrides *= count[i];
4611 1373 : nElts *= count[i];
4612 1373 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4613 : }
4614 784 : if (bRowMajorStrides)
4615 598 : return false;
4616 186 : return nLastIdx == nElts - 1;
4617 : }
4618 :
4619 : /************************************************************************/
4620 : /* CopyToFinalBufferSameDataType() */
4621 : /************************************************************************/
4622 :
4623 : template <size_t N>
4624 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4625 : size_t nDims, const size_t *count,
4626 : const GPtrDiff_t *bufferStride)
4627 : {
4628 120 : std::vector<size_t> anStackCount(nDims);
4629 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4630 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4631 : #if defined(__GNUC__)
4632 : #pragma GCC diagnostic push
4633 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4634 : #endif
4635 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4636 : #if defined(__GNUC__)
4637 : #pragma GCC diagnostic pop
4638 : #endif
4639 60 : size_t iDim = 0;
4640 :
4641 749 : lbl_next_depth:
4642 749 : if (iDim == nDims - 1)
4643 : {
4644 661 : size_t n = count[iDim];
4645 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4646 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4647 29186 : while (n > 0)
4648 : {
4649 28525 : --n;
4650 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4651 28525 : pabyDstBuffer += bufferStrideLastDim;
4652 28525 : pabySrcBuffer += N;
4653 : }
4654 : }
4655 : else
4656 : {
4657 88 : anStackCount[iDim] = count[iDim];
4658 : while (true)
4659 : {
4660 689 : ++iDim;
4661 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4662 689 : goto lbl_next_depth;
4663 689 : lbl_return_to_caller_in_loop:
4664 689 : --iDim;
4665 689 : --anStackCount[iDim];
4666 689 : if (anStackCount[iDim] == 0)
4667 88 : break;
4668 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4669 : }
4670 : }
4671 749 : if (iDim > 0)
4672 689 : goto lbl_return_to_caller_in_loop;
4673 60 : }
4674 :
4675 : /************************************************************************/
4676 : /* CopyToFinalBuffer() */
4677 : /************************************************************************/
4678 :
4679 166 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4680 : const GDALExtendedDataType &eSrcDataType,
4681 : void *pDstBuffer,
4682 : const GDALExtendedDataType &eDstDataType,
4683 : size_t nDims, const size_t *count,
4684 : const GPtrDiff_t *bufferStride)
4685 : {
4686 166 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4687 : // Use specialized implementation for well-known data types when no
4688 : // type conversion is needed
4689 166 : if (eSrcDataType == eDstDataType)
4690 : {
4691 110 : if (nSrcDataTypeSize == 1)
4692 : {
4693 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4694 : count, bufferStride);
4695 60 : return;
4696 : }
4697 69 : else if (nSrcDataTypeSize == 2)
4698 : {
4699 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4700 : count, bufferStride);
4701 1 : return;
4702 : }
4703 68 : else if (nSrcDataTypeSize == 4)
4704 : {
4705 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4706 : count, bufferStride);
4707 14 : return;
4708 : }
4709 54 : else if (nSrcDataTypeSize == 8)
4710 : {
4711 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4712 : count, bufferStride);
4713 4 : return;
4714 : }
4715 : }
4716 :
4717 106 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4718 212 : std::vector<size_t> anStackCount(nDims);
4719 212 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4720 106 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4721 106 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4722 106 : size_t iDim = 0;
4723 :
4724 375 : lbl_next_depth:
4725 375 : if (iDim == nDims - 1)
4726 : {
4727 365 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4728 365 : pabyDstBufferStack[iDim], eDstDataType,
4729 365 : bufferStride[iDim], count[iDim]);
4730 365 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4731 : }
4732 : else
4733 : {
4734 10 : anStackCount[iDim] = count[iDim];
4735 : while (true)
4736 : {
4737 269 : ++iDim;
4738 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4739 269 : goto lbl_next_depth;
4740 269 : lbl_return_to_caller_in_loop:
4741 269 : --iDim;
4742 269 : --anStackCount[iDim];
4743 269 : if (anStackCount[iDim] == 0)
4744 10 : break;
4745 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4746 : }
4747 : }
4748 375 : if (iDim > 0)
4749 269 : goto lbl_return_to_caller_in_loop;
4750 : }
4751 :
4752 : /************************************************************************/
4753 : /* TransposeLast2Dims() */
4754 : /************************************************************************/
4755 :
4756 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4757 : const GDALExtendedDataType &eDT,
4758 : const size_t nDims, const size_t *count,
4759 : const size_t nEltsNonLast2Dims)
4760 : {
4761 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4762 19 : const auto nDTSize = eDT.GetSize();
4763 : void *pTempBufferForLast2DimsTranspose =
4764 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4765 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4766 0 : return false;
4767 :
4768 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4769 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4770 : {
4771 39 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4772 : pTempBufferForLast2DimsTranspose,
4773 39 : eDT.GetNumericDataType(), count[nDims - 1],
4774 39 : count[nDims - 2]);
4775 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4776 : nDTSize * nEltsLast2Dims);
4777 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4778 : }
4779 :
4780 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4781 :
4782 19 : return true;
4783 : }
4784 :
4785 : /************************************************************************/
4786 : /* ReadForTransposedRequest() */
4787 : /************************************************************************/
4788 :
4789 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4790 : // transposed view yield to extremely poor/unusable performance. This fixes
4791 : // this by using temporary memory to read in a contiguous buffer in a
4792 : // row-major order, and then do the transposition to the final buffer.
4793 :
4794 185 : bool GDALMDArray::ReadForTransposedRequest(
4795 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4796 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4797 : void *pDstBuffer) const
4798 : {
4799 185 : const size_t nDims(GetDimensionCount());
4800 185 : if (nDims == 0)
4801 : {
4802 0 : CPLAssert(false);
4803 : return false; // shouldn't happen
4804 : }
4805 185 : size_t nElts = 1;
4806 492 : for (size_t i = 0; i < nDims; ++i)
4807 307 : nElts *= count[i];
4808 :
4809 370 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4810 185 : tmpBufferStrides.back() = 1;
4811 307 : for (size_t i = nDims - 1; i > 0;)
4812 : {
4813 122 : --i;
4814 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4815 : }
4816 :
4817 185 : const auto &eDT = GetDataType();
4818 185 : const auto nDTSize = eDT.GetSize();
4819 314 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4820 330 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4821 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4822 : {
4823 : // Optimization of the optimization if only the last 2 dims are
4824 : // transposed that saves on temporary buffer allocation
4825 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4826 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4827 23 : bool bRowMajorStridesForNonLast2Dims = true;
4828 23 : size_t nEltsNonLast2Dims = 1;
4829 40 : for (size_t i = nDims - 2; i > 0;)
4830 : {
4831 17 : --i;
4832 17 : if (static_cast<size_t>(bufferStride[i]) !=
4833 : nCurStrideForRowMajorStrides)
4834 : {
4835 4 : bRowMajorStridesForNonLast2Dims = false;
4836 : }
4837 : // Integer overflows have already been checked in
4838 : // CheckReadWriteParams()
4839 17 : nCurStrideForRowMajorStrides *= count[i];
4840 17 : nEltsNonLast2Dims *= count[i];
4841 : }
4842 23 : if (bRowMajorStridesForNonLast2Dims)
4843 : {
4844 : // We read in the final buffer!
4845 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4846 19 : eDT, pDstBuffer))
4847 : {
4848 0 : return false;
4849 : }
4850 :
4851 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4852 19 : nEltsNonLast2Dims);
4853 : }
4854 : }
4855 :
4856 166 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4857 166 : if (pTempBuffer == nullptr)
4858 0 : return false;
4859 :
4860 166 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4861 166 : pTempBuffer))
4862 : {
4863 0 : VSIFree(pTempBuffer);
4864 0 : return false;
4865 : }
4866 166 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4867 : count, bufferStride);
4868 :
4869 166 : if (eDT.NeedsFreeDynamicMemory())
4870 : {
4871 95 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4872 190 : for (size_t i = 0; i < nElts; ++i)
4873 : {
4874 95 : eDT.FreeDynamicMemory(pabyPtr);
4875 95 : pabyPtr += nDTSize;
4876 : }
4877 : }
4878 :
4879 166 : VSIFree(pTempBuffer);
4880 166 : return true;
4881 : }
4882 :
4883 : /************************************************************************/
4884 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4885 : /************************************************************************/
4886 :
4887 : // Returns true if at all following conditions are met:
4888 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4889 : // defines a row-major ordered contiguous buffer.
4890 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4891 : const size_t *count, const GInt64 *arrayStep,
4892 : const GPtrDiff_t *bufferStride,
4893 : const GDALExtendedDataType &bufferDataType) const
4894 : {
4895 78 : if (bufferDataType != GetDataType())
4896 5 : return false;
4897 73 : size_t nExpectedStride = 1;
4898 166 : for (size_t i = GetDimensionCount(); i > 0;)
4899 : {
4900 96 : --i;
4901 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4902 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4903 : {
4904 3 : return false;
4905 : }
4906 93 : nExpectedStride *= count[i];
4907 : }
4908 70 : return true;
4909 : }
4910 :
4911 : /************************************************************************/
4912 : /* ReadUsingContiguousIRead() */
4913 : /************************************************************************/
4914 :
4915 : // Used for example by the TileDB driver when requesting it with
4916 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4917 : // not defining a row-major ordered contiguous buffer.
4918 : // Should only be called when at least one of the above conditions are true,
4919 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4920 : // returning none.
4921 : // This method will call IRead() again with arrayStep[] == 1,
4922 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4923 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4924 : // content of that temporary buffer onto pDstBuffer.
4925 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4926 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4927 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4928 : void *pDstBuffer) const
4929 : {
4930 7 : const size_t nDims(GetDimensionCount());
4931 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4932 14 : std::vector<size_t> anTmpCount(nDims);
4933 7 : const auto &oType = GetDataType();
4934 7 : size_t nMemArraySize = oType.GetSize();
4935 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4936 7 : GPtrDiff_t nStride = 1;
4937 18 : for (size_t i = nDims; i > 0;)
4938 : {
4939 11 : --i;
4940 11 : if (arrayStep[i] > 0)
4941 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4942 : else
4943 2 : anTmpStartIdx[i] =
4944 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4945 : const uint64_t nCount =
4946 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4947 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4948 : {
4949 0 : CPLError(CE_Failure, CPLE_AppDefined,
4950 : "Read() failed due to too large memory requirement");
4951 0 : return false;
4952 : }
4953 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4954 11 : nMemArraySize *= anTmpCount[i];
4955 11 : anTmpStride[i] = nStride;
4956 11 : nStride *= anTmpCount[i];
4957 : }
4958 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4959 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4960 7 : if (!pTmpBuffer)
4961 0 : return false;
4962 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4963 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4964 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4965 : {
4966 0 : return false;
4967 : }
4968 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
4969 18 : for (size_t i = 0; i < nDims; ++i)
4970 : {
4971 11 : if (arrayStep[i] > 0)
4972 9 : anTmpStartIdx[i] = 0;
4973 : else
4974 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
4975 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
4976 22 : std::string(), std::string(), std::string(), std::string(),
4977 22 : anTmpCount[i]);
4978 : }
4979 : auto poMEMArray =
4980 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
4981 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
4982 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
4983 7 : bufferStride, bufferDataType, pDstBuffer);
4984 : }
4985 :
4986 : //! @endcond
4987 :
4988 : /************************************************************************/
4989 : /* GDALSlicedMDArray */
4990 : /************************************************************************/
4991 :
4992 : class GDALSlicedMDArray final : public GDALPamMDArray
4993 : {
4994 : private:
4995 : std::shared_ptr<GDALMDArray> m_poParent{};
4996 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4997 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
4998 : std::vector<Range>
4999 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
5000 :
5001 : mutable std::vector<GUInt64> m_parentStart;
5002 : mutable std::vector<size_t> m_parentCount;
5003 : mutable std::vector<GInt64> m_parentStep;
5004 : mutable std::vector<GPtrDiff_t> m_parentStride;
5005 :
5006 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5007 : const GInt64 *arrayStep,
5008 : const GPtrDiff_t *bufferStride) const;
5009 :
5010 : protected:
5011 582 : explicit GDALSlicedMDArray(
5012 : const std::shared_ptr<GDALMDArray> &poParent,
5013 : const std::string &viewExpr,
5014 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5015 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5016 : std::vector<Range> &&parentRanges)
5017 1746 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
5018 1746 : poParent->GetFullName() +
5019 1164 : " (" + viewExpr + ")"),
5020 1164 : GDALPamMDArray(std::string(),
5021 1164 : "Sliced view of " + poParent->GetFullName() + " (" +
5022 1164 : viewExpr + ")",
5023 1164 : GDALPamMultiDim::GetPAM(poParent),
5024 : poParent->GetContext()),
5025 1164 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5026 582 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5027 582 : m_parentRanges(std::move(parentRanges)),
5028 582 : m_parentStart(m_poParent->GetDimensionCount()),
5029 582 : m_parentCount(m_poParent->GetDimensionCount(), 1),
5030 582 : m_parentStep(m_poParent->GetDimensionCount()),
5031 4656 : m_parentStride(m_poParent->GetDimensionCount())
5032 : {
5033 582 : }
5034 :
5035 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5036 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5037 : const GDALExtendedDataType &bufferDataType,
5038 : void *pDstBuffer) const override;
5039 :
5040 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5041 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5042 : const GDALExtendedDataType &bufferDataType,
5043 : const void *pSrcBuffer) override;
5044 :
5045 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5046 : CSLConstList papszOptions) const override;
5047 :
5048 : public:
5049 : static std::shared_ptr<GDALSlicedMDArray>
5050 582 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5051 : const std::string &viewExpr,
5052 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
5053 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
5054 : std::vector<Range> &&parentRanges)
5055 : {
5056 582 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5057 582 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5058 :
5059 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5060 582 : poParent, viewExpr, std::move(dims),
5061 582 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5062 582 : newAr->SetSelf(newAr);
5063 582 : return newAr;
5064 : }
5065 :
5066 56 : bool IsWritable() const override
5067 : {
5068 56 : return m_poParent->IsWritable();
5069 : }
5070 :
5071 990 : const std::string &GetFilename() const override
5072 : {
5073 990 : return m_poParent->GetFilename();
5074 : }
5075 :
5076 : const std::vector<std::shared_ptr<GDALDimension>> &
5077 3675 : GetDimensions() const override
5078 : {
5079 3675 : return m_dims;
5080 : }
5081 :
5082 1396 : const GDALExtendedDataType &GetDataType() const override
5083 : {
5084 1396 : return m_poParent->GetDataType();
5085 : }
5086 :
5087 2 : const std::string &GetUnit() const override
5088 : {
5089 2 : return m_poParent->GetUnit();
5090 : }
5091 :
5092 : // bool SetUnit(const std::string& osUnit) override { return
5093 : // m_poParent->SetUnit(osUnit); }
5094 :
5095 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5096 : {
5097 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
5098 2 : if (!poSrcSRS)
5099 1 : return nullptr;
5100 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5101 2 : std::vector<int> dstMapping;
5102 3 : for (int srcAxis : srcMapping)
5103 : {
5104 2 : bool bFound = false;
5105 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5106 : {
5107 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5108 3 : srcAxis - 1)
5109 : {
5110 2 : dstMapping.push_back(static_cast<int>(i) + 1);
5111 2 : bFound = true;
5112 2 : break;
5113 : }
5114 : }
5115 2 : if (!bFound)
5116 : {
5117 0 : dstMapping.push_back(0);
5118 : }
5119 : }
5120 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5121 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5122 1 : return poClone;
5123 : }
5124 :
5125 57 : const void *GetRawNoDataValue() const override
5126 : {
5127 57 : return m_poParent->GetRawNoDataValue();
5128 : }
5129 :
5130 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5131 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5132 :
5133 2 : double GetOffset(bool *pbHasOffset,
5134 : GDALDataType *peStorageType) const override
5135 : {
5136 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5137 : }
5138 :
5139 2 : double GetScale(bool *pbHasScale,
5140 : GDALDataType *peStorageType) const override
5141 : {
5142 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5143 : }
5144 :
5145 : // bool SetOffset(double dfOffset) override { return
5146 : // m_poParent->SetOffset(dfOffset); }
5147 :
5148 : // bool SetScale(double dfScale) override { return
5149 : // m_poParent->SetScale(dfScale); }
5150 :
5151 198 : std::vector<GUInt64> GetBlockSize() const override
5152 : {
5153 198 : std::vector<GUInt64> ret(GetDimensionCount());
5154 396 : const auto parentBlockSize(m_poParent->GetBlockSize());
5155 598 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5156 : {
5157 400 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5158 400 : if (iOldAxis != static_cast<size_t>(-1))
5159 : {
5160 400 : ret[i] = parentBlockSize[iOldAxis];
5161 : }
5162 : }
5163 396 : return ret;
5164 : }
5165 :
5166 : std::shared_ptr<GDALAttribute>
5167 6 : GetAttribute(const std::string &osName) const override
5168 : {
5169 6 : return m_poParent->GetAttribute(osName);
5170 : }
5171 :
5172 : std::vector<std::shared_ptr<GDALAttribute>>
5173 24 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5174 : {
5175 24 : return m_poParent->GetAttributes(papszOptions);
5176 : }
5177 : };
5178 :
5179 : /************************************************************************/
5180 : /* PrepareParentArrays() */
5181 : /************************************************************************/
5182 :
5183 476 : void GDALSlicedMDArray::PrepareParentArrays(
5184 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5185 : const GPtrDiff_t *bufferStride) const
5186 : {
5187 476 : const size_t nParentDimCount = m_parentRanges.size();
5188 1484 : for (size_t i = 0; i < nParentDimCount; i++)
5189 : {
5190 : // For dimensions in parent that have no existence in sliced array
5191 1008 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5192 : }
5193 :
5194 1253 : for (size_t i = 0; i < m_dims.size(); i++)
5195 : {
5196 777 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5197 777 : if (iParent != static_cast<size_t>(-1))
5198 : {
5199 775 : m_parentStart[iParent] =
5200 775 : m_parentRanges[iParent].m_nIncr >= 0
5201 775 : ? m_parentRanges[iParent].m_nStartIdx +
5202 745 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5203 30 : : m_parentRanges[iParent].m_nStartIdx -
5204 60 : arrayStartIdx[i] *
5205 30 : static_cast<GUInt64>(
5206 30 : -m_parentRanges[iParent].m_nIncr);
5207 775 : m_parentCount[iParent] = count[i];
5208 775 : if (arrayStep)
5209 : {
5210 774 : m_parentStep[iParent] =
5211 774 : count[i] == 1 ? 1 :
5212 : // other checks should have ensured this does
5213 : // not overflow
5214 588 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5215 : }
5216 775 : if (bufferStride)
5217 : {
5218 774 : m_parentStride[iParent] = bufferStride[i];
5219 : }
5220 : }
5221 : }
5222 476 : }
5223 :
5224 : /************************************************************************/
5225 : /* IRead() */
5226 : /************************************************************************/
5227 :
5228 443 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5229 : const GInt64 *arrayStep,
5230 : const GPtrDiff_t *bufferStride,
5231 : const GDALExtendedDataType &bufferDataType,
5232 : void *pDstBuffer) const
5233 : {
5234 443 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5235 886 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5236 443 : m_parentStep.data(), m_parentStride.data(),
5237 443 : bufferDataType, pDstBuffer);
5238 : }
5239 :
5240 : /************************************************************************/
5241 : /* IWrite() */
5242 : /************************************************************************/
5243 :
5244 32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5245 : const size_t *count, const GInt64 *arrayStep,
5246 : const GPtrDiff_t *bufferStride,
5247 : const GDALExtendedDataType &bufferDataType,
5248 : const void *pSrcBuffer)
5249 : {
5250 32 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5251 64 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5252 32 : m_parentStep.data(), m_parentStride.data(),
5253 32 : bufferDataType, pSrcBuffer);
5254 : }
5255 :
5256 : /************************************************************************/
5257 : /* IAdviseRead() */
5258 : /************************************************************************/
5259 :
5260 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5261 : const size_t *count,
5262 : CSLConstList papszOptions) const
5263 : {
5264 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5265 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5266 1 : papszOptions);
5267 : }
5268 :
5269 : /************************************************************************/
5270 : /* CreateSlicedArray() */
5271 : /************************************************************************/
5272 :
5273 : static std::shared_ptr<GDALMDArray>
5274 600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5275 : const std::string &viewExpr, const std::string &activeSlice,
5276 : bool bRenameDimensions,
5277 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5278 : {
5279 600 : const auto &srcDims(self->GetDimensions());
5280 600 : if (srcDims.empty())
5281 : {
5282 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5283 2 : return nullptr;
5284 : }
5285 :
5286 1196 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5287 598 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5288 :
5289 1196 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5290 1196 : std::vector<size_t> mapDimIdxToParentDimIdx;
5291 1196 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5292 598 : newDims.reserve(nTokens);
5293 598 : mapDimIdxToParentDimIdx.reserve(nTokens);
5294 598 : parentRanges.reserve(nTokens);
5295 :
5296 598 : bool bGotEllipsis = false;
5297 598 : size_t nCurSrcDim = 0;
5298 1768 : for (size_t i = 0; i < nTokens; i++)
5299 : {
5300 1186 : const char *pszIdxSpec = aosTokens[i];
5301 1186 : if (EQUAL(pszIdxSpec, "..."))
5302 : {
5303 38 : if (bGotEllipsis)
5304 : {
5305 2 : CPLError(CE_Failure, CPLE_AppDefined,
5306 : "Only one single ellipsis is supported");
5307 2 : return nullptr;
5308 : }
5309 36 : bGotEllipsis = true;
5310 36 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5311 79 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5312 : {
5313 43 : parentRanges.emplace_back(0, 1);
5314 43 : newDims.push_back(srcDims[nCurSrcDim]);
5315 43 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5316 : }
5317 36 : continue;
5318 : }
5319 1148 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5320 1145 : EQUAL(pszIdxSpec, "np.newaxis"))
5321 : {
5322 3 : newDims.push_back(std::make_shared<GDALDimension>(
5323 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5324 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5325 3 : continue;
5326 : }
5327 1145 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5328 : {
5329 325 : if (nCurSrcDim >= srcDims.size())
5330 : {
5331 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5332 : activeSlice.c_str());
5333 7 : return nullptr;
5334 : }
5335 :
5336 323 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5337 323 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5338 323 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5339 319 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5340 : {
5341 5 : CPLError(CE_Failure, CPLE_AppDefined,
5342 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5343 5 : return nullptr;
5344 : }
5345 318 : if (nVal < 0)
5346 0 : nVal += nDimSize;
5347 318 : parentRanges.emplace_back(nVal, 0);
5348 : }
5349 : else
5350 : {
5351 820 : if (nCurSrcDim >= srcDims.size())
5352 : {
5353 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5354 : activeSlice.c_str());
5355 7 : return nullptr;
5356 : }
5357 :
5358 : CPLStringList aosRangeTokens(
5359 819 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5360 819 : int nRangeTokens = aosRangeTokens.size();
5361 819 : if (nRangeTokens > 3)
5362 : {
5363 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5364 : pszIdxSpec);
5365 1 : return nullptr;
5366 : }
5367 818 : if (nRangeTokens <= 1)
5368 : {
5369 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5370 : pszIdxSpec);
5371 1 : return nullptr;
5372 : }
5373 817 : const char *pszStart = aosRangeTokens[0];
5374 817 : const char *pszEnd = aosRangeTokens[1];
5375 817 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5376 817 : GDALSlicedMDArray::Range range;
5377 817 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5378 817 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5379 1633 : if (range.m_nIncr == 0 ||
5380 816 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5381 : {
5382 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5383 1 : return nullptr;
5384 : }
5385 816 : auto startIdx(CPLAtoGIntBig(pszStart));
5386 816 : if (startIdx < 0)
5387 : {
5388 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5389 0 : startIdx = 0;
5390 : else
5391 0 : startIdx = nDimSize + startIdx;
5392 : }
5393 816 : const bool bPosIncr = range.m_nIncr > 0;
5394 816 : range.m_nStartIdx = startIdx;
5395 1632 : range.m_nStartIdx = EQUAL(pszStart, "")
5396 816 : ? (bPosIncr ? 0 : nDimSize - 1)
5397 : : range.m_nStartIdx;
5398 816 : if (range.m_nStartIdx >= nDimSize - 1)
5399 186 : range.m_nStartIdx = nDimSize - 1;
5400 816 : auto endIdx(CPLAtoGIntBig(pszEnd));
5401 816 : if (endIdx < 0)
5402 : {
5403 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5404 1 : if (nDimSize < positiveEndIdx)
5405 0 : endIdx = 0;
5406 : else
5407 1 : endIdx = nDimSize - positiveEndIdx;
5408 : }
5409 816 : GUInt64 nEndIdx = endIdx;
5410 816 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5411 816 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5412 814 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5413 : {
5414 3 : CPLError(CE_Failure, CPLE_AppDefined,
5415 : "Output dimension of size 0 is not allowed");
5416 3 : return nullptr;
5417 : }
5418 813 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5419 813 : const auto nAbsIncr = std::abs(range.m_nIncr);
5420 813 : const GUInt64 newSize =
5421 : bPosIncr
5422 847 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5423 34 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5424 1341 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5425 528 : newSize == srcDims[nCurSrcDim]->GetSize())
5426 : {
5427 159 : newDims.push_back(srcDims[nCurSrcDim]);
5428 : }
5429 : else
5430 : {
5431 654 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5432 654 : if (bRenameDimensions)
5433 : {
5434 : osNewDimName =
5435 1212 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5436 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5437 : "_" CPL_FRMT_GUIB,
5438 606 : static_cast<GUIntBig>(range.m_nStartIdx),
5439 606 : static_cast<GIntBig>(range.m_nIncr),
5440 606 : static_cast<GUIntBig>(newSize));
5441 : }
5442 654 : newDims.push_back(std::make_shared<GDALDimension>(
5443 1308 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5444 1308 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5445 : : std::string(),
5446 : newSize));
5447 : }
5448 813 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5449 813 : parentRanges.emplace_back(range);
5450 : }
5451 :
5452 1131 : nCurSrcDim++;
5453 : }
5454 655 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5455 : {
5456 73 : parentRanges.emplace_back(0, 1);
5457 73 : newDims.push_back(srcDims[nCurSrcDim]);
5458 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5459 : }
5460 :
5461 582 : GDALMDArray::ViewSpec viewSpec;
5462 582 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5463 582 : viewSpec.m_parentRanges = parentRanges;
5464 582 : viewSpecs.emplace_back(std::move(viewSpec));
5465 :
5466 1164 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5467 582 : std::move(mapDimIdxToParentDimIdx),
5468 1164 : std::move(parentRanges));
5469 : }
5470 :
5471 : /************************************************************************/
5472 : /* GDALExtractFieldMDArray */
5473 : /************************************************************************/
5474 :
5475 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5476 : {
5477 : private:
5478 : std::shared_ptr<GDALMDArray> m_poParent{};
5479 : GDALExtendedDataType m_dt;
5480 : std::string m_srcCompName;
5481 : mutable std::vector<GByte> m_pabyNoData{};
5482 :
5483 : protected:
5484 82 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5485 : const std::string &fieldName,
5486 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5487 328 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5488 164 : " of " +
5489 82 : poParent->GetFullName()),
5490 : GDALPamMDArray(
5491 164 : std::string(),
5492 164 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5493 164 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5494 : m_poParent(poParent), m_dt(srcComp->GetType()),
5495 410 : m_srcCompName(srcComp->GetName())
5496 : {
5497 82 : m_pabyNoData.resize(m_dt.GetSize());
5498 82 : }
5499 :
5500 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5501 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5502 : const GDALExtendedDataType &bufferDataType,
5503 : void *pDstBuffer) const override;
5504 :
5505 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5506 : CSLConstList papszOptions) const override
5507 : {
5508 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5509 : }
5510 :
5511 : public:
5512 : static std::shared_ptr<GDALExtractFieldMDArray>
5513 82 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5514 : const std::string &fieldName,
5515 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5516 : {
5517 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5518 82 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5519 82 : newAr->SetSelf(newAr);
5520 82 : return newAr;
5521 : }
5522 :
5523 164 : ~GDALExtractFieldMDArray()
5524 82 : {
5525 82 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5526 164 : }
5527 :
5528 41 : bool IsWritable() const override
5529 : {
5530 41 : return m_poParent->IsWritable();
5531 : }
5532 :
5533 247 : const std::string &GetFilename() const override
5534 : {
5535 247 : return m_poParent->GetFilename();
5536 : }
5537 :
5538 : const std::vector<std::shared_ptr<GDALDimension>> &
5539 343 : GetDimensions() const override
5540 : {
5541 343 : return m_poParent->GetDimensions();
5542 : }
5543 :
5544 289 : const GDALExtendedDataType &GetDataType() const override
5545 : {
5546 289 : return m_dt;
5547 : }
5548 :
5549 2 : const std::string &GetUnit() const override
5550 : {
5551 2 : return m_poParent->GetUnit();
5552 : }
5553 :
5554 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5555 : {
5556 2 : return m_poParent->GetSpatialRef();
5557 : }
5558 :
5559 58 : const void *GetRawNoDataValue() const override
5560 : {
5561 58 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5562 58 : if (parentNoData == nullptr)
5563 1 : return nullptr;
5564 :
5565 57 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5566 57 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5567 :
5568 114 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5569 114 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5570 114 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5571 57 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5572 171 : std::move(comps)));
5573 :
5574 57 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5575 57 : &m_pabyNoData[0], tmpDT);
5576 :
5577 57 : return &m_pabyNoData[0];
5578 : }
5579 :
5580 2 : double GetOffset(bool *pbHasOffset,
5581 : GDALDataType *peStorageType) const override
5582 : {
5583 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5584 : }
5585 :
5586 2 : double GetScale(bool *pbHasScale,
5587 : GDALDataType *peStorageType) const override
5588 : {
5589 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5590 : }
5591 :
5592 42 : std::vector<GUInt64> GetBlockSize() const override
5593 : {
5594 42 : return m_poParent->GetBlockSize();
5595 : }
5596 : };
5597 :
5598 : /************************************************************************/
5599 : /* IRead() */
5600 : /************************************************************************/
5601 :
5602 84 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5603 : const size_t *count,
5604 : const GInt64 *arrayStep,
5605 : const GPtrDiff_t *bufferStride,
5606 : const GDALExtendedDataType &bufferDataType,
5607 : void *pDstBuffer) const
5608 : {
5609 168 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5610 168 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5611 168 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5612 : auto tmpDT(GDALExtendedDataType::Create(
5613 168 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5614 :
5615 84 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5616 168 : tmpDT, pDstBuffer);
5617 : }
5618 :
5619 : /************************************************************************/
5620 : /* CreateFieldNameExtractArray() */
5621 : /************************************************************************/
5622 :
5623 : static std::shared_ptr<GDALMDArray>
5624 83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5625 : const std::string &fieldName)
5626 : {
5627 83 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5628 83 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5629 202 : for (const auto &comp : self->GetDataType().GetComponents())
5630 : {
5631 201 : if (comp->GetName() == fieldName)
5632 : {
5633 82 : srcComp = ∁
5634 82 : break;
5635 : }
5636 : }
5637 83 : if (srcComp == nullptr)
5638 : {
5639 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5640 : fieldName.c_str());
5641 1 : return nullptr;
5642 : }
5643 82 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5644 : }
5645 :
5646 : /************************************************************************/
5647 : /* GetView() */
5648 : /************************************************************************/
5649 :
5650 : // clang-format off
5651 : /** Return a view of the array using slicing or field access.
5652 : *
5653 : * The slice expression uses the same syntax as NumPy basic slicing and
5654 : * indexing. See
5655 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5656 : * Or it can use field access by name. See
5657 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5658 : *
5659 : * Multiple [] bracket elements can be concatenated, with a slice expression
5660 : * or field name inside each.
5661 : *
5662 : * For basic slicing and indexing, inside each [] bracket element, a list of
5663 : * indexes that apply to successive source dimensions, can be specified, using
5664 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5665 : * or newaxis, using a comma separator.
5666 : *
5667 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5668 : * <ul>
5669 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5670 : * at index 1 in the first dimension, and index 2 in the second dimension
5671 : * from the source array. That is 5</li>
5672 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5673 : * implemented internally doing this intermediate slicing approach.</li>
5674 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5675 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5676 : * first dimension. That is [4,5,6,7].</li>
5677 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5678 : * second dimension. That is [2,6].</li>
5679 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5680 : * the second dimension. That is [[2],[6]].</li>
5681 : * <li>GetView("[::,2]"): Same as
5682 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5683 : * ellipsis only expands to one dimension here.</li>
5684 : * <li>GetView("[:,::2]"):
5685 : * returns a 2-dimensional array, with even-indexed elements of the second
5686 : * dimension. That is [[0,2],[4,6]].</li>
5687 : * <li>GetView("[:,1::2]"): returns a
5688 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5689 : * is [[1,3],[5,7]].</li>
5690 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5691 : * array, with elements of the second dimension with index in the range [1,3[.
5692 : * That is [[1,2],[5,6]].</li>
5693 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5694 : * array, with the values in first dimension reversed. That is
5695 : * [[4,5,6,7],[0,1,2,3]].</li>
5696 : * <li>GetView("[newaxis,...]"): returns a
5697 : * 3-dimensional array, with an additional dimension of size 1 put at the
5698 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5699 : * </ul>
5700 : *
5701 : * One difference with NumPy behavior is that ranges that would result in
5702 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5703 : * GDAL multidimensional model).
5704 : *
5705 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5706 : * Multiple field specification is not supported currently.
5707 : *
5708 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5709 : *
5710 : * \note When using the GDAL Python bindings, natural Python syntax can be
5711 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5712 : * ar.GetView("[0,::,1]['foo']")
5713 : * \note When using the C++ API and integer indexing only, you may use the
5714 : * at(idx0, idx1, ...) method.
5715 : *
5716 : * The returned array holds a reference to the original one, and thus is
5717 : * a view of it (not a copy). If the content of the original array changes,
5718 : * the content of the view array too. When using basic slicing and indexing,
5719 : * the view can be written if the underlying array is writable.
5720 : *
5721 : * This is the same as the C function GDALMDArrayGetView()
5722 : *
5723 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5724 : * access.
5725 : * @return a new array, that holds a reference to the original one, and thus is
5726 : * a view of it (not a copy), or nullptr in case of error.
5727 : */
5728 : // clang-format on
5729 :
5730 : std::shared_ptr<GDALMDArray>
5731 619 : GDALMDArray::GetView(const std::string &viewExpr) const
5732 : {
5733 1238 : std::vector<ViewSpec> viewSpecs;
5734 1238 : return GetView(viewExpr, true, viewSpecs);
5735 : }
5736 :
5737 : //! @cond Doxygen_Suppress
5738 : std::shared_ptr<GDALMDArray>
5739 689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5740 : std::vector<ViewSpec> &viewSpecs) const
5741 : {
5742 1378 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5743 689 : if (!self)
5744 : {
5745 1 : CPLError(CE_Failure, CPLE_AppDefined,
5746 : "Driver implementation issue: m_pSelf not set !");
5747 1 : return nullptr;
5748 : }
5749 688 : std::string curExpr(viewExpr);
5750 : while (true)
5751 : {
5752 691 : if (curExpr.empty() || curExpr[0] != '[')
5753 : {
5754 2 : CPLError(CE_Failure, CPLE_AppDefined,
5755 : "Slice string should start with ['");
5756 688 : return nullptr;
5757 : }
5758 :
5759 689 : std::string fieldName;
5760 : size_t endExpr;
5761 689 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5762 : {
5763 87 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5764 : {
5765 2 : CPLError(CE_Failure, CPLE_AppDefined,
5766 : "Field access not allowed on non-compound data type");
5767 2 : return nullptr;
5768 : }
5769 85 : size_t idx = 2;
5770 768 : for (; idx < curExpr.size(); idx++)
5771 : {
5772 767 : const char ch = curExpr[idx];
5773 767 : if (ch == curExpr[1])
5774 84 : break;
5775 683 : if (ch == '\\' && idx + 1 < curExpr.size())
5776 : {
5777 1 : fieldName += curExpr[idx + 1];
5778 1 : idx++;
5779 : }
5780 : else
5781 : {
5782 682 : fieldName += ch;
5783 : }
5784 : }
5785 85 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5786 : {
5787 2 : CPLError(CE_Failure, CPLE_AppDefined,
5788 : "Invalid field access specification");
5789 2 : return nullptr;
5790 : }
5791 83 : endExpr = idx + 1;
5792 : }
5793 : else
5794 : {
5795 602 : endExpr = curExpr.find(']');
5796 : }
5797 685 : if (endExpr == std::string::npos)
5798 : {
5799 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5800 1 : return nullptr;
5801 : }
5802 684 : if (endExpr == 1)
5803 : {
5804 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5805 1 : return nullptr;
5806 : }
5807 683 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5808 :
5809 683 : if (!fieldName.empty())
5810 : {
5811 166 : ViewSpec viewSpec;
5812 83 : viewSpec.m_osFieldName = fieldName;
5813 83 : viewSpecs.emplace_back(std::move(viewSpec));
5814 : }
5815 :
5816 683 : auto newArray = !fieldName.empty()
5817 : ? CreateFieldNameExtractArray(self, fieldName)
5818 : : CreateSlicedArray(self, viewExpr, activeSlice,
5819 683 : bRenameDimensions, viewSpecs);
5820 :
5821 683 : if (endExpr == curExpr.size() - 1)
5822 : {
5823 680 : return newArray;
5824 : }
5825 3 : self = std::move(newArray);
5826 3 : curExpr = curExpr.substr(endExpr + 1);
5827 3 : }
5828 : }
5829 :
5830 : //! @endcond
5831 :
5832 : std::shared_ptr<GDALMDArray>
5833 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5834 : {
5835 19 : std::string osExpr("[");
5836 19 : bool bFirst = true;
5837 45 : for (const auto &idx : indices)
5838 : {
5839 26 : if (!bFirst)
5840 7 : osExpr += ',';
5841 26 : bFirst = false;
5842 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5843 : }
5844 57 : return GetView(osExpr + ']');
5845 : }
5846 :
5847 : /************************************************************************/
5848 : /* operator[] */
5849 : /************************************************************************/
5850 :
5851 : /** Return a view of the array using field access
5852 : *
5853 : * Equivalent of GetView("['fieldName']")
5854 : *
5855 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5856 : */
5857 : std::shared_ptr<GDALMDArray>
5858 2 : GDALMDArray::operator[](const std::string &fieldName) const
5859 : {
5860 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5861 4 : .replaceAll('\\', "\\\\")
5862 4 : .replaceAll('\'', "\\\'")
5863 6 : .c_str()));
5864 : }
5865 :
5866 : /************************************************************************/
5867 : /* GDALMDArrayTransposed */
5868 : /************************************************************************/
5869 :
5870 : class GDALMDArrayTransposed final : public GDALPamMDArray
5871 : {
5872 : private:
5873 : std::shared_ptr<GDALMDArray> m_poParent{};
5874 : std::vector<int> m_anMapNewAxisToOldAxis{};
5875 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5876 :
5877 : mutable std::vector<GUInt64> m_parentStart;
5878 : mutable std::vector<size_t> m_parentCount;
5879 : mutable std::vector<GInt64> m_parentStep;
5880 : mutable std::vector<GPtrDiff_t> m_parentStride;
5881 :
5882 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5883 : const GInt64 *arrayStep,
5884 : const GPtrDiff_t *bufferStride) const;
5885 :
5886 : static std::string
5887 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5888 : {
5889 84 : std::string ret;
5890 84 : ret += '[';
5891 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5892 : {
5893 228 : if (i > 0)
5894 144 : ret += ',';
5895 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5896 : }
5897 84 : ret += ']';
5898 84 : return ret;
5899 : }
5900 :
5901 : protected:
5902 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5903 : const std::vector<int> &anMapNewAxisToOldAxis,
5904 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5905 84 : : GDALAbstractMDArray(std::string(),
5906 84 : "Transposed view of " + poParent->GetFullName() +
5907 84 : " along " +
5908 42 : MappingToStr(anMapNewAxisToOldAxis)),
5909 84 : GDALPamMDArray(std::string(),
5910 84 : "Transposed view of " + poParent->GetFullName() +
5911 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5912 84 : GDALPamMultiDim::GetPAM(poParent),
5913 : poParent->GetContext()),
5914 42 : m_poParent(std::move(poParent)),
5915 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5916 42 : m_dims(std::move(dims)),
5917 42 : m_parentStart(m_poParent->GetDimensionCount()),
5918 42 : m_parentCount(m_poParent->GetDimensionCount()),
5919 42 : m_parentStep(m_poParent->GetDimensionCount()),
5920 336 : m_parentStride(m_poParent->GetDimensionCount())
5921 : {
5922 42 : }
5923 :
5924 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5925 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5926 : const GDALExtendedDataType &bufferDataType,
5927 : void *pDstBuffer) const override;
5928 :
5929 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5930 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5931 : const GDALExtendedDataType &bufferDataType,
5932 : const void *pSrcBuffer) override;
5933 :
5934 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5935 : CSLConstList papszOptions) const override;
5936 :
5937 : public:
5938 : static std::shared_ptr<GDALMDArrayTransposed>
5939 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5940 : const std::vector<int> &anMapNewAxisToOldAxis)
5941 : {
5942 42 : const auto &parentDims(poParent->GetDimensions());
5943 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5944 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5945 : {
5946 114 : if (iOldAxis < 0)
5947 : {
5948 1 : dims.push_back(std::make_shared<GDALDimension>(
5949 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5950 : }
5951 : else
5952 : {
5953 113 : dims.emplace_back(parentDims[iOldAxis]);
5954 : }
5955 : }
5956 :
5957 : auto newAr(
5958 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5959 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5960 42 : newAr->SetSelf(newAr);
5961 84 : return newAr;
5962 : }
5963 :
5964 1 : bool IsWritable() const override
5965 : {
5966 1 : return m_poParent->IsWritable();
5967 : }
5968 :
5969 84 : const std::string &GetFilename() const override
5970 : {
5971 84 : return m_poParent->GetFilename();
5972 : }
5973 :
5974 : const std::vector<std::shared_ptr<GDALDimension>> &
5975 358 : GetDimensions() const override
5976 : {
5977 358 : return m_dims;
5978 : }
5979 :
5980 141 : const GDALExtendedDataType &GetDataType() const override
5981 : {
5982 141 : return m_poParent->GetDataType();
5983 : }
5984 :
5985 4 : const std::string &GetUnit() const override
5986 : {
5987 4 : return m_poParent->GetUnit();
5988 : }
5989 :
5990 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5991 : {
5992 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
5993 5 : if (!poSrcSRS)
5994 2 : return nullptr;
5995 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5996 6 : std::vector<int> dstMapping;
5997 9 : for (int srcAxis : srcMapping)
5998 : {
5999 6 : bool bFound = false;
6000 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6001 : {
6002 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6003 : {
6004 6 : dstMapping.push_back(static_cast<int>(i) + 1);
6005 6 : bFound = true;
6006 6 : break;
6007 : }
6008 : }
6009 6 : if (!bFound)
6010 : {
6011 0 : dstMapping.push_back(0);
6012 : }
6013 : }
6014 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6015 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6016 3 : return poClone;
6017 : }
6018 :
6019 4 : const void *GetRawNoDataValue() const override
6020 : {
6021 4 : return m_poParent->GetRawNoDataValue();
6022 : }
6023 :
6024 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
6025 : // m_poParent->SetRawNoDataValue(pRawNoData); }
6026 :
6027 4 : double GetOffset(bool *pbHasOffset,
6028 : GDALDataType *peStorageType) const override
6029 : {
6030 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
6031 : }
6032 :
6033 4 : double GetScale(bool *pbHasScale,
6034 : GDALDataType *peStorageType) const override
6035 : {
6036 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
6037 : }
6038 :
6039 : // bool SetOffset(double dfOffset) override { return
6040 : // m_poParent->SetOffset(dfOffset); }
6041 :
6042 : // bool SetScale(double dfScale) override { return
6043 : // m_poParent->SetScale(dfScale); }
6044 :
6045 3 : std::vector<GUInt64> GetBlockSize() const override
6046 : {
6047 3 : std::vector<GUInt64> ret(GetDimensionCount());
6048 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
6049 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6050 : {
6051 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6052 8 : if (iOldAxis >= 0)
6053 : {
6054 7 : ret[i] = parentBlockSize[iOldAxis];
6055 : }
6056 : }
6057 6 : return ret;
6058 : }
6059 :
6060 : std::shared_ptr<GDALAttribute>
6061 1 : GetAttribute(const std::string &osName) const override
6062 : {
6063 1 : return m_poParent->GetAttribute(osName);
6064 : }
6065 :
6066 : std::vector<std::shared_ptr<GDALAttribute>>
6067 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
6068 : {
6069 6 : return m_poParent->GetAttributes(papszOptions);
6070 : }
6071 : };
6072 :
6073 : /************************************************************************/
6074 : /* PrepareParentArrays() */
6075 : /************************************************************************/
6076 :
6077 47 : void GDALMDArrayTransposed::PrepareParentArrays(
6078 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6079 : const GPtrDiff_t *bufferStride) const
6080 : {
6081 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6082 : {
6083 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6084 129 : if (iOldAxis >= 0)
6085 : {
6086 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
6087 128 : m_parentCount[iOldAxis] = count[i];
6088 128 : if (arrayStep) // only null when called from IAdviseRead()
6089 : {
6090 126 : m_parentStep[iOldAxis] = arrayStep[i];
6091 : }
6092 128 : if (bufferStride) // only null when called from IAdviseRead()
6093 : {
6094 126 : m_parentStride[iOldAxis] = bufferStride[i];
6095 : }
6096 : }
6097 : }
6098 47 : }
6099 :
6100 : /************************************************************************/
6101 : /* IRead() */
6102 : /************************************************************************/
6103 :
6104 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6105 : const size_t *count, const GInt64 *arrayStep,
6106 : const GPtrDiff_t *bufferStride,
6107 : const GDALExtendedDataType &bufferDataType,
6108 : void *pDstBuffer) const
6109 : {
6110 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6111 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6112 44 : m_parentStep.data(), m_parentStride.data(),
6113 44 : bufferDataType, pDstBuffer);
6114 : }
6115 :
6116 : /************************************************************************/
6117 : /* IWrite() */
6118 : /************************************************************************/
6119 :
6120 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6121 : const size_t *count, const GInt64 *arrayStep,
6122 : const GPtrDiff_t *bufferStride,
6123 : const GDALExtendedDataType &bufferDataType,
6124 : const void *pSrcBuffer)
6125 : {
6126 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6127 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6128 2 : m_parentStep.data(), m_parentStride.data(),
6129 2 : bufferDataType, pSrcBuffer);
6130 : }
6131 :
6132 : /************************************************************************/
6133 : /* IAdviseRead() */
6134 : /************************************************************************/
6135 :
6136 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6137 : const size_t *count,
6138 : CSLConstList papszOptions) const
6139 : {
6140 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6141 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6142 1 : papszOptions);
6143 : }
6144 :
6145 : /************************************************************************/
6146 : /* Transpose() */
6147 : /************************************************************************/
6148 :
6149 : /** Return a view of the array whose axis have been reordered.
6150 : *
6151 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6152 : * and GetDimensionCount() - 1, and each only once.
6153 : * -1 can be used as a special index value to ask for the insertion of a new
6154 : * axis of size 1.
6155 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6156 : * index of one of its dimension, it corresponds to the axis of index
6157 : * anMapNewAxisToOldAxis[i] from the current array.
6158 : *
6159 : * This is similar to the numpy.transpose() method
6160 : *
6161 : * The returned array holds a reference to the original one, and thus is
6162 : * a view of it (not a copy). If the content of the original array changes,
6163 : * the content of the view array too. The view can be written if the underlying
6164 : * array is writable.
6165 : *
6166 : * Note that I/O performance in such a transposed view might be poor.
6167 : *
6168 : * This is the same as the C function GDALMDArrayTranspose().
6169 : *
6170 : * @return a new array, that holds a reference to the original one, and thus is
6171 : * a view of it (not a copy), or nullptr in case of error.
6172 : */
6173 : std::shared_ptr<GDALMDArray>
6174 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6175 : {
6176 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6177 50 : if (!self)
6178 : {
6179 0 : CPLError(CE_Failure, CPLE_AppDefined,
6180 : "Driver implementation issue: m_pSelf not set !");
6181 0 : return nullptr;
6182 : }
6183 50 : const int nDims = static_cast<int>(GetDimensionCount());
6184 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6185 50 : int nCountOldAxis = 0;
6186 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6187 : {
6188 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6189 : {
6190 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6191 4 : return nullptr;
6192 : }
6193 130 : if (iOldAxis >= 0)
6194 : {
6195 128 : if (alreadyUsedOldAxis[iOldAxis])
6196 : {
6197 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6198 : iOldAxis);
6199 1 : return nullptr;
6200 : }
6201 127 : alreadyUsedOldAxis[iOldAxis] = true;
6202 127 : nCountOldAxis++;
6203 : }
6204 : }
6205 46 : if (nCountOldAxis != nDims)
6206 : {
6207 4 : CPLError(CE_Failure, CPLE_AppDefined,
6208 : "One or several original axis missing");
6209 4 : return nullptr;
6210 : }
6211 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6212 : }
6213 :
6214 : /************************************************************************/
6215 : /* IRead() */
6216 : /************************************************************************/
6217 :
6218 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6219 : const size_t *count, const GInt64 *arrayStep,
6220 : const GPtrDiff_t *bufferStride,
6221 : const GDALExtendedDataType &bufferDataType,
6222 : void *pDstBuffer) const
6223 : {
6224 16 : const double dfScale = m_dfScale;
6225 16 : const double dfOffset = m_dfOffset;
6226 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6227 : const auto dtDouble =
6228 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6229 16 : const size_t nDTSize = dtDouble.GetSize();
6230 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6231 :
6232 16 : double adfSrcNoData[2] = {0, 0};
6233 16 : if (m_bHasNoData)
6234 : {
6235 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6236 9 : m_poParent->GetDataType(),
6237 : &adfSrcNoData[0], dtDouble);
6238 : }
6239 :
6240 16 : const auto nDims = GetDimensions().size();
6241 16 : if (nDims == 0)
6242 : {
6243 : double adfVal[2];
6244 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6245 : dtDouble, &adfVal[0]))
6246 : {
6247 0 : return false;
6248 : }
6249 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6250 : {
6251 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6252 6 : if (bDTIsComplex)
6253 : {
6254 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6255 : }
6256 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6257 : bufferDataType);
6258 : }
6259 : else
6260 : {
6261 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6262 : pDstBuffer, bufferDataType);
6263 : }
6264 9 : return true;
6265 : }
6266 :
6267 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6268 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6269 7 : void *pTempBuffer = pDstBuffer;
6270 7 : if (bTempBufferNeeded)
6271 : {
6272 2 : size_t nElts = 1;
6273 2 : actualBufferStrideVector.resize(nDims);
6274 7 : for (size_t i = 0; i < nDims; i++)
6275 5 : nElts *= count[i];
6276 2 : actualBufferStrideVector.back() = 1;
6277 5 : for (size_t i = nDims - 1; i > 0;)
6278 : {
6279 3 : --i;
6280 3 : actualBufferStrideVector[i] =
6281 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6282 : }
6283 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6284 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6285 2 : if (!pTempBuffer)
6286 0 : return false;
6287 : }
6288 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6289 : actualBufferStridePtr, dtDouble, pTempBuffer))
6290 : {
6291 0 : if (bTempBufferNeeded)
6292 0 : VSIFree(pTempBuffer);
6293 0 : return false;
6294 : }
6295 :
6296 : struct Stack
6297 : {
6298 : size_t nIters = 0;
6299 : double *src_ptr = nullptr;
6300 : GByte *dst_ptr = nullptr;
6301 : GPtrDiff_t src_inc_offset = 0;
6302 : GPtrDiff_t dst_inc_offset = 0;
6303 : };
6304 :
6305 7 : std::vector<Stack> stack(nDims);
6306 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6307 23 : for (size_t i = 0; i < nDims; i++)
6308 : {
6309 32 : stack[i].src_inc_offset =
6310 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6311 16 : stack[i].dst_inc_offset =
6312 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6313 : }
6314 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6315 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6316 :
6317 7 : size_t dimIdx = 0;
6318 7 : const size_t nDimsMinus1 = nDims - 1;
6319 : GByte abyDstNoData[16];
6320 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6321 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6322 : bufferDataType);
6323 :
6324 37 : lbl_next_depth:
6325 37 : if (dimIdx == nDimsMinus1)
6326 : {
6327 25 : auto nIters = count[dimIdx];
6328 25 : double *padfVal = stack[dimIdx].src_ptr;
6329 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6330 : while (true)
6331 : {
6332 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6333 : {
6334 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6335 88 : if (bDTIsComplex)
6336 : {
6337 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6338 : }
6339 88 : if (bTempBufferNeeded)
6340 : {
6341 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6342 : dst_ptr, bufferDataType);
6343 : }
6344 : }
6345 : else
6346 : {
6347 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6348 : }
6349 :
6350 92 : if ((--nIters) == 0)
6351 25 : break;
6352 67 : padfVal += stack[dimIdx].src_inc_offset;
6353 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6354 : }
6355 : }
6356 : else
6357 : {
6358 12 : stack[dimIdx].nIters = count[dimIdx];
6359 : while (true)
6360 : {
6361 30 : dimIdx++;
6362 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6363 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6364 30 : goto lbl_next_depth;
6365 30 : lbl_return_to_caller:
6366 30 : dimIdx--;
6367 30 : if ((--stack[dimIdx].nIters) == 0)
6368 12 : break;
6369 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6370 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6371 : }
6372 : }
6373 37 : if (dimIdx > 0)
6374 30 : goto lbl_return_to_caller;
6375 :
6376 7 : if (bTempBufferNeeded)
6377 2 : VSIFree(pTempBuffer);
6378 7 : return true;
6379 : }
6380 :
6381 : /************************************************************************/
6382 : /* IWrite() */
6383 : /************************************************************************/
6384 :
6385 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6386 : const size_t *count, const GInt64 *arrayStep,
6387 : const GPtrDiff_t *bufferStride,
6388 : const GDALExtendedDataType &bufferDataType,
6389 : const void *pSrcBuffer)
6390 : {
6391 16 : const double dfScale = m_dfScale;
6392 16 : const double dfOffset = m_dfOffset;
6393 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6394 : const auto dtDouble =
6395 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6396 16 : const size_t nDTSize = dtDouble.GetSize();
6397 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6398 : const bool bSelfAndParentHaveNoData =
6399 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6400 16 : double dfNoData = 0;
6401 16 : if (m_bHasNoData)
6402 : {
6403 7 : GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6404 : &dfNoData, GDT_Float64, 0, 1);
6405 : }
6406 :
6407 16 : double adfSrcNoData[2] = {0, 0};
6408 16 : if (bSelfAndParentHaveNoData)
6409 : {
6410 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6411 7 : m_poParent->GetDataType(),
6412 : &adfSrcNoData[0], dtDouble);
6413 : }
6414 :
6415 16 : const auto nDims = GetDimensions().size();
6416 16 : if (nDims == 0)
6417 : {
6418 : double adfVal[2];
6419 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6420 : dtDouble);
6421 16 : if (bSelfAndParentHaveNoData &&
6422 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6423 : {
6424 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6425 2 : bufferStride, m_poParent->GetDataType(),
6426 4 : m_poParent->GetRawNoDataValue());
6427 : }
6428 : else
6429 : {
6430 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6431 8 : if (bDTIsComplex)
6432 : {
6433 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6434 : }
6435 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6436 8 : bufferStride, dtDouble, &adfVal[0]);
6437 : }
6438 : }
6439 :
6440 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6441 6 : size_t nElts = 1;
6442 6 : tmpBufferStrideVector.resize(nDims);
6443 20 : for (size_t i = 0; i < nDims; i++)
6444 14 : nElts *= count[i];
6445 6 : tmpBufferStrideVector.back() = 1;
6446 14 : for (size_t i = nDims - 1; i > 0;)
6447 : {
6448 8 : --i;
6449 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6450 : }
6451 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6452 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6453 6 : if (!pTempBuffer)
6454 0 : return false;
6455 :
6456 : struct Stack
6457 : {
6458 : size_t nIters = 0;
6459 : double *dst_ptr = nullptr;
6460 : const GByte *src_ptr = nullptr;
6461 : GPtrDiff_t src_inc_offset = 0;
6462 : GPtrDiff_t dst_inc_offset = 0;
6463 : };
6464 :
6465 6 : std::vector<Stack> stack(nDims);
6466 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6467 20 : for (size_t i = 0; i < nDims; i++)
6468 : {
6469 28 : stack[i].dst_inc_offset =
6470 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6471 14 : stack[i].src_inc_offset =
6472 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6473 : }
6474 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6475 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6476 :
6477 6 : size_t dimIdx = 0;
6478 6 : const size_t nDimsMinus1 = nDims - 1;
6479 :
6480 34 : lbl_next_depth:
6481 34 : if (dimIdx == nDimsMinus1)
6482 : {
6483 23 : auto nIters = count[dimIdx];
6484 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6485 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6486 : while (true)
6487 : {
6488 : double adfVal[2];
6489 : const double *padfSrcVal;
6490 86 : if (bIsBufferDataTypeNativeDataType)
6491 : {
6492 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6493 : }
6494 : else
6495 : {
6496 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6497 : &adfVal[0], dtDouble);
6498 36 : padfSrcVal = adfVal;
6499 : }
6500 :
6501 148 : if (bSelfAndParentHaveNoData &&
6502 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6503 : {
6504 3 : dst_ptr[0] = adfSrcNoData[0];
6505 3 : if (bDTIsComplex)
6506 : {
6507 1 : dst_ptr[1] = adfSrcNoData[1];
6508 : }
6509 : }
6510 : else
6511 : {
6512 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6513 83 : if (bDTIsComplex)
6514 : {
6515 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6516 : }
6517 : }
6518 :
6519 86 : if ((--nIters) == 0)
6520 23 : break;
6521 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6522 63 : src_ptr += stack[dimIdx].src_inc_offset;
6523 63 : }
6524 : }
6525 : else
6526 : {
6527 11 : stack[dimIdx].nIters = count[dimIdx];
6528 : while (true)
6529 : {
6530 28 : dimIdx++;
6531 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6532 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6533 28 : goto lbl_next_depth;
6534 28 : lbl_return_to_caller:
6535 28 : dimIdx--;
6536 28 : if ((--stack[dimIdx].nIters) == 0)
6537 11 : break;
6538 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6539 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6540 : }
6541 : }
6542 34 : if (dimIdx > 0)
6543 28 : goto lbl_return_to_caller;
6544 :
6545 : // If the parent array is not double/complex-double, then convert the
6546 : // values to it, before calling Write(), as some implementations can be
6547 : // very slow when doing the type conversion.
6548 6 : const auto &eParentDT = m_poParent->GetDataType();
6549 6 : const size_t nParentDTSize = eParentDT.GetSize();
6550 6 : if (nParentDTSize <= nDTSize / 2)
6551 : {
6552 : // Copy in-place by making sure that source and target do not overlap
6553 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6554 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6555 :
6556 : // Copy first element
6557 : {
6558 6 : std::vector<GByte> abyTemp(nParentDTSize);
6559 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6560 6 : static_cast<int>(nDTSize), &abyTemp[0],
6561 : eParentNumericDT, static_cast<int>(nParentDTSize),
6562 : 1);
6563 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6564 : }
6565 : // Remaining elements
6566 86 : for (size_t i = 1; i < nElts; ++i)
6567 : {
6568 80 : GDALCopyWords64(
6569 80 : static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6570 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6571 : eParentNumericDT, 0, 1);
6572 : }
6573 : }
6574 :
6575 : const bool ret =
6576 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6577 : eParentDT, pTempBuffer);
6578 :
6579 6 : VSIFree(pTempBuffer);
6580 6 : return ret;
6581 : }
6582 :
6583 : /************************************************************************/
6584 : /* GetUnscaled() */
6585 : /************************************************************************/
6586 :
6587 : /** Return an array that is the unscaled version of the current one.
6588 : *
6589 : * That is each value of the unscaled array will be
6590 : * unscaled_value = raw_value * GetScale() + GetOffset()
6591 : *
6592 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6593 : * from unscaled values to raw values.
6594 : *
6595 : * This is the same as the C function GDALMDArrayGetUnscaled().
6596 : *
6597 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6598 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6599 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6600 : * @return a new array, that holds a reference to the original one, and thus is
6601 : * a view of it (not a copy), or nullptr in case of error.
6602 : */
6603 : std::shared_ptr<GDALMDArray>
6604 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6605 : double dfOverriddenDstNodata) const
6606 : {
6607 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6608 17 : if (!self)
6609 : {
6610 0 : CPLError(CE_Failure, CPLE_AppDefined,
6611 : "Driver implementation issue: m_pSelf not set !");
6612 0 : return nullptr;
6613 : }
6614 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6615 : {
6616 0 : CPLError(CE_Failure, CPLE_AppDefined,
6617 : "GetUnscaled() only supports numeric data type");
6618 0 : return nullptr;
6619 : }
6620 : const double dfScale =
6621 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6622 : const double dfOffset =
6623 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6624 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6625 4 : return self;
6626 :
6627 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6628 13 : ? GDT_CFloat64
6629 13 : : GDT_Float64;
6630 13 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6631 : {
6632 1 : if (GetDataType().GetNumericDataType() == GDT_Float16)
6633 0 : eDT = GDT_Float16;
6634 1 : if (GetDataType().GetNumericDataType() == GDT_Float32)
6635 1 : eDT = GDT_Float32;
6636 : }
6637 :
6638 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6639 13 : dfOverriddenDstNodata, eDT);
6640 : }
6641 :
6642 : /************************************************************************/
6643 : /* GDALMDArrayMask */
6644 : /************************************************************************/
6645 :
6646 : class GDALMDArrayMask final : public GDALPamMDArray
6647 : {
6648 : private:
6649 : std::shared_ptr<GDALMDArray> m_poParent{};
6650 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6651 : double m_dfMissingValue = 0.0;
6652 : bool m_bHasMissingValue = false;
6653 : double m_dfFillValue = 0.0;
6654 : bool m_bHasFillValue = false;
6655 : double m_dfValidMin = 0.0;
6656 : bool m_bHasValidMin = false;
6657 : double m_dfValidMax = 0.0;
6658 : bool m_bHasValidMax = false;
6659 : std::vector<uint32_t> m_anValidFlagMasks{};
6660 : std::vector<uint32_t> m_anValidFlagValues{};
6661 :
6662 : bool Init(CSLConstList papszOptions);
6663 :
6664 : template <typename Type>
6665 : void
6666 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6667 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6668 : const void *pTempBuffer,
6669 : const GDALExtendedDataType &oTmpBufferDT,
6670 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6671 :
6672 : protected:
6673 48 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6674 96 : : GDALAbstractMDArray(std::string(),
6675 96 : "Mask of " + poParent->GetFullName()),
6676 96 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6677 96 : GDALPamMultiDim::GetPAM(poParent),
6678 : poParent->GetContext()),
6679 240 : m_poParent(std::move(poParent))
6680 : {
6681 48 : }
6682 :
6683 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6684 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6685 : const GDALExtendedDataType &bufferDataType,
6686 : void *pDstBuffer) const override;
6687 :
6688 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6689 : CSLConstList papszOptions) const override
6690 : {
6691 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6692 : }
6693 :
6694 : public:
6695 : static std::shared_ptr<GDALMDArrayMask>
6696 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6697 : CSLConstList papszOptions);
6698 :
6699 1 : bool IsWritable() const override
6700 : {
6701 1 : return false;
6702 : }
6703 :
6704 54 : const std::string &GetFilename() const override
6705 : {
6706 54 : return m_poParent->GetFilename();
6707 : }
6708 :
6709 : const std::vector<std::shared_ptr<GDALDimension>> &
6710 382 : GetDimensions() const override
6711 : {
6712 382 : return m_poParent->GetDimensions();
6713 : }
6714 :
6715 138 : const GDALExtendedDataType &GetDataType() const override
6716 : {
6717 138 : return m_dt;
6718 : }
6719 :
6720 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6721 : {
6722 1 : return m_poParent->GetSpatialRef();
6723 : }
6724 :
6725 2 : std::vector<GUInt64> GetBlockSize() const override
6726 : {
6727 2 : return m_poParent->GetBlockSize();
6728 : }
6729 : };
6730 :
6731 : /************************************************************************/
6732 : /* GDALMDArrayMask::Create() */
6733 : /************************************************************************/
6734 :
6735 : /* static */ std::shared_ptr<GDALMDArrayMask>
6736 48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6737 : CSLConstList papszOptions)
6738 : {
6739 96 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6740 48 : newAr->SetSelf(newAr);
6741 48 : if (!newAr->Init(papszOptions))
6742 6 : return nullptr;
6743 42 : return newAr;
6744 : }
6745 :
6746 : /************************************************************************/
6747 : /* GDALMDArrayMask::Init() */
6748 : /************************************************************************/
6749 :
6750 48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6751 : {
6752 : const auto GetSingleValNumericAttr =
6753 192 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6754 : {
6755 576 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6756 192 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6757 : {
6758 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6759 21 : if (anDimSizes.empty() ||
6760 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6761 : {
6762 11 : bHasVal = true;
6763 11 : dfVal = poAttr->ReadAsDouble();
6764 : }
6765 : }
6766 192 : };
6767 :
6768 48 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6769 48 : m_dfMissingValue);
6770 48 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6771 48 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6772 48 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6773 :
6774 : {
6775 144 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6776 54 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6777 60 : poValidRange->GetDimensionsSize()[0] == 2 &&
6778 6 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6779 : {
6780 6 : m_bHasValidMin = true;
6781 6 : m_bHasValidMax = true;
6782 6 : auto vals = poValidRange->ReadAsDoubleArray();
6783 6 : CPLAssert(vals.size() == 2);
6784 6 : m_dfValidMin = vals[0];
6785 6 : m_dfValidMax = vals[1];
6786 : }
6787 : }
6788 :
6789 : // Take into account
6790 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6791 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6792 : const char *pszUnmaskFlags =
6793 48 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6794 48 : if (pszUnmaskFlags)
6795 : {
6796 : const auto IsScalarStringAttr =
6797 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6798 : {
6799 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6800 26 : (poAttr->GetDimensionsSize().empty() ||
6801 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6802 26 : poAttr->GetDimensionsSize()[0] == 1));
6803 : };
6804 :
6805 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6806 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6807 : {
6808 1 : CPLError(CE_Failure, CPLE_AppDefined,
6809 : "UNMASK_FLAGS option specified but array has no "
6810 : "flag_meanings attribute");
6811 1 : return false;
6812 : }
6813 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6814 13 : if (!pszFlagMeanings)
6815 : {
6816 1 : CPLError(CE_Failure, CPLE_AppDefined,
6817 : "Cannot read flag_meanings attribute");
6818 1 : return false;
6819 : }
6820 :
6821 : const auto IsSingleDimNumericAttr =
6822 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6823 : {
6824 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6825 26 : poAttr->GetDimensionsSize().size() == 1;
6826 : };
6827 :
6828 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6829 : const bool bHasFlagValues =
6830 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6831 :
6832 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6833 : const bool bHasFlagMasks =
6834 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6835 :
6836 12 : if (!bHasFlagValues && !bHasFlagMasks)
6837 : {
6838 1 : CPLError(CE_Failure, CPLE_AppDefined,
6839 : "Cannot find flag_values and/or flag_masks attribute");
6840 1 : return false;
6841 : }
6842 :
6843 : const CPLStringList aosUnmaskFlags(
6844 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6845 : const CPLStringList aosFlagMeanings(
6846 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6847 :
6848 11 : if (bHasFlagValues)
6849 : {
6850 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6851 : // We could support Int64 or UInt64, but more work...
6852 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6853 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6854 : {
6855 0 : CPLError(CE_Failure, CPLE_NotSupported,
6856 : "Unsupported data type for flag_values attribute: %s",
6857 : GDALGetDataTypeName(eType));
6858 0 : return false;
6859 : }
6860 : }
6861 :
6862 11 : if (bHasFlagMasks)
6863 : {
6864 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6865 : // We could support Int64 or UInt64, but more work...
6866 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6867 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6868 : {
6869 0 : CPLError(CE_Failure, CPLE_NotSupported,
6870 : "Unsupported data type for flag_masks attribute: %s",
6871 : GDALGetDataTypeName(eType));
6872 0 : return false;
6873 : }
6874 : }
6875 :
6876 : const std::vector<double> adfValues(
6877 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6878 11 : : std::vector<double>());
6879 : const std::vector<double> adfMasks(
6880 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6881 11 : : std::vector<double>());
6882 :
6883 18 : if (bHasFlagValues &&
6884 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6885 : {
6886 1 : CPLError(CE_Failure, CPLE_AppDefined,
6887 : "Number of values in flag_values attribute is different "
6888 : "from the one in flag_meanings");
6889 1 : return false;
6890 : }
6891 :
6892 16 : if (bHasFlagMasks &&
6893 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6894 : {
6895 1 : CPLError(CE_Failure, CPLE_AppDefined,
6896 : "Number of values in flag_masks attribute is different "
6897 : "from the one in flag_meanings");
6898 1 : return false;
6899 : }
6900 :
6901 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6902 : {
6903 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6904 11 : if (nIdxFlag < 0)
6905 : {
6906 1 : CPLError(
6907 : CE_Failure, CPLE_AppDefined,
6908 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6909 : aosUnmaskFlags[i], pszFlagMeanings);
6910 1 : return false;
6911 : }
6912 :
6913 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6914 : {
6915 0 : CPLError(CE_Failure, CPLE_AppDefined,
6916 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6917 0 : adfValues[nIdxFlag]);
6918 0 : return false;
6919 : }
6920 :
6921 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6922 : {
6923 0 : CPLError(CE_Failure, CPLE_AppDefined,
6924 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6925 0 : adfMasks[nIdxFlag]);
6926 0 : return false;
6927 : }
6928 :
6929 10 : if (bHasFlagValues)
6930 : {
6931 12 : m_anValidFlagValues.push_back(
6932 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6933 : }
6934 :
6935 10 : if (bHasFlagMasks)
6936 : {
6937 12 : m_anValidFlagMasks.push_back(
6938 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6939 : }
6940 : }
6941 : }
6942 :
6943 42 : return true;
6944 : }
6945 :
6946 : /************************************************************************/
6947 : /* IRead() */
6948 : /************************************************************************/
6949 :
6950 51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6951 : const GInt64 *arrayStep,
6952 : const GPtrDiff_t *bufferStride,
6953 : const GDALExtendedDataType &bufferDataType,
6954 : void *pDstBuffer) const
6955 : {
6956 51 : size_t nElts = 1;
6957 51 : const size_t nDims = GetDimensionCount();
6958 102 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6959 139 : for (size_t i = 0; i < nDims; i++)
6960 88 : nElts *= count[i];
6961 51 : if (nDims > 0)
6962 : {
6963 46 : tmpBufferStrideVector.back() = 1;
6964 88 : for (size_t i = nDims - 1; i > 0;)
6965 : {
6966 42 : --i;
6967 42 : tmpBufferStrideVector[i] =
6968 42 : tmpBufferStrideVector[i + 1] * count[i + 1];
6969 : }
6970 : }
6971 :
6972 : /* Optimized case: if we are an integer data type and that there is no */
6973 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
6974 : /* directly */
6975 49 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
6976 74 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
6977 34 : m_anValidFlagMasks.empty() &&
6978 111 : m_poParent->GetRawNoDataValue() == nullptr &&
6979 11 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
6980 : {
6981 7 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
6982 7 : if (bBufferDataTypeIsByte) // Byte case
6983 : {
6984 4 : bool bContiguous = true;
6985 10 : for (size_t i = 0; i < nDims; i++)
6986 : {
6987 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
6988 : {
6989 1 : bContiguous = false;
6990 1 : break;
6991 : }
6992 : }
6993 4 : if (bContiguous)
6994 : {
6995 : // CPLDebug("GDAL", "GetMask(): contiguous case");
6996 3 : memset(pDstBuffer, 1, nElts);
6997 3 : return true;
6998 : }
6999 : }
7000 :
7001 : struct Stack
7002 : {
7003 : size_t nIters = 0;
7004 : GByte *dst_ptr = nullptr;
7005 : GPtrDiff_t dst_inc_offset = 0;
7006 : };
7007 :
7008 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7009 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
7010 13 : for (size_t i = 0; i < nDims; i++)
7011 : {
7012 9 : stack[i].dst_inc_offset =
7013 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7014 : }
7015 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7016 :
7017 4 : size_t dimIdx = 0;
7018 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7019 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
7020 4 : CPLAssert(nBufferDTSize <= 16);
7021 4 : const GByte flag = 1;
7022 : // Coverity misses that m_dt is of type Byte
7023 : // coverity[overrun-buffer-val]
7024 4 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
7025 :
7026 28 : lbl_next_depth:
7027 28 : if (dimIdx == nDimsMinus1)
7028 : {
7029 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7030 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7031 :
7032 : while (true)
7033 : {
7034 : // cppcheck-suppress knownConditionTrueFalse
7035 73 : if (bBufferDataTypeIsByte)
7036 : {
7037 24 : *dst_ptr = flag;
7038 : }
7039 : else
7040 : {
7041 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
7042 : }
7043 :
7044 73 : if ((--nIters) == 0)
7045 19 : break;
7046 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
7047 : }
7048 : }
7049 : else
7050 : {
7051 9 : stack[dimIdx].nIters = count[dimIdx];
7052 : while (true)
7053 : {
7054 24 : dimIdx++;
7055 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7056 24 : goto lbl_next_depth;
7057 24 : lbl_return_to_caller:
7058 24 : dimIdx--;
7059 24 : if ((--stack[dimIdx].nIters) == 0)
7060 9 : break;
7061 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7062 : }
7063 : }
7064 28 : if (dimIdx > 0)
7065 24 : goto lbl_return_to_caller;
7066 :
7067 4 : return true;
7068 : }
7069 :
7070 : const auto oTmpBufferDT =
7071 44 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7072 : ? GDALExtendedDataType::Create(GDT_Float64)
7073 88 : : m_poParent->GetDataType();
7074 44 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7075 44 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7076 44 : if (!pTempBuffer)
7077 0 : return false;
7078 88 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7079 44 : tmpBufferStrideVector.data(), oTmpBufferDT,
7080 : pTempBuffer))
7081 : {
7082 0 : VSIFree(pTempBuffer);
7083 0 : return false;
7084 : }
7085 :
7086 44 : switch (oTmpBufferDT.GetNumericDataType())
7087 : {
7088 7 : case GDT_Byte:
7089 7 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7090 : pTempBuffer, oTmpBufferDT,
7091 : tmpBufferStrideVector);
7092 7 : break;
7093 :
7094 0 : case GDT_Int8:
7095 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7096 : pTempBuffer, oTmpBufferDT,
7097 : tmpBufferStrideVector);
7098 0 : break;
7099 :
7100 1 : case GDT_UInt16:
7101 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7102 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7103 : tmpBufferStrideVector);
7104 1 : break;
7105 :
7106 14 : case GDT_Int16:
7107 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7108 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7109 : tmpBufferStrideVector);
7110 14 : break;
7111 :
7112 1 : case GDT_UInt32:
7113 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7114 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7115 : tmpBufferStrideVector);
7116 1 : break;
7117 :
7118 5 : case GDT_Int32:
7119 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7120 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7121 : tmpBufferStrideVector);
7122 5 : break;
7123 :
7124 0 : case GDT_UInt64:
7125 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7126 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7127 : tmpBufferStrideVector);
7128 0 : break;
7129 :
7130 0 : case GDT_Int64:
7131 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7132 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7133 : tmpBufferStrideVector);
7134 0 : break;
7135 :
7136 0 : case GDT_Float16:
7137 0 : ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7138 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7139 : tmpBufferStrideVector);
7140 0 : break;
7141 :
7142 7 : case GDT_Float32:
7143 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7144 : pTempBuffer, oTmpBufferDT,
7145 : tmpBufferStrideVector);
7146 7 : break;
7147 :
7148 9 : case GDT_Float64:
7149 9 : ReadInternal<double>(count, bufferStride, bufferDataType,
7150 : pDstBuffer, pTempBuffer, oTmpBufferDT,
7151 : tmpBufferStrideVector);
7152 9 : break;
7153 0 : case GDT_Unknown:
7154 : case GDT_CInt16:
7155 : case GDT_CInt32:
7156 : case GDT_CFloat16:
7157 : case GDT_CFloat32:
7158 : case GDT_CFloat64:
7159 : case GDT_TypeCount:
7160 0 : CPLAssert(false);
7161 : break;
7162 : }
7163 :
7164 44 : VSIFree(pTempBuffer);
7165 :
7166 44 : return true;
7167 : }
7168 :
7169 : /************************************************************************/
7170 : /* IsValidForDT() */
7171 : /************************************************************************/
7172 :
7173 40 : template <typename Type> static bool IsValidForDT(double dfVal)
7174 : {
7175 40 : if (std::isnan(dfVal))
7176 0 : return false;
7177 40 : if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7178 0 : return false;
7179 40 : if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7180 0 : return false;
7181 40 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7182 : }
7183 :
7184 9 : template <> bool IsValidForDT<double>(double)
7185 : {
7186 9 : return true;
7187 : }
7188 :
7189 : /************************************************************************/
7190 : /* IsNan() */
7191 : /************************************************************************/
7192 :
7193 1438 : template <typename Type> inline bool IsNan(Type)
7194 : {
7195 1438 : return false;
7196 : }
7197 :
7198 65 : template <> bool IsNan<double>(double val)
7199 : {
7200 65 : return std::isnan(val);
7201 : }
7202 :
7203 26 : template <> bool IsNan<float>(float val)
7204 : {
7205 26 : return std::isnan(val);
7206 : }
7207 :
7208 : /************************************************************************/
7209 : /* ReadInternal() */
7210 : /************************************************************************/
7211 :
7212 : template <typename Type>
7213 44 : void GDALMDArrayMask::ReadInternal(
7214 : const size_t *count, const GPtrDiff_t *bufferStride,
7215 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7216 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7217 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7218 : {
7219 44 : const size_t nDims = GetDimensionCount();
7220 :
7221 220 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7222 : {
7223 220 : if (bHasVal)
7224 : {
7225 49 : if (IsValidForDT<Type>(dfVal))
7226 : {
7227 49 : return static_cast<Type>(dfVal);
7228 : }
7229 : else
7230 : {
7231 0 : bHasVal = false;
7232 : }
7233 : }
7234 171 : return 0;
7235 : };
7236 :
7237 44 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7238 44 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7239 : const Type nNoDataValue =
7240 44 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7241 44 : bool bHasMissingValue = m_bHasMissingValue;
7242 44 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7243 44 : bool bHasFillValue = m_bHasFillValue;
7244 44 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7245 44 : bool bHasValidMin = m_bHasValidMin;
7246 44 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7247 44 : bool bHasValidMax = m_bHasValidMax;
7248 44 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7249 44 : const bool bHasValidFlags =
7250 44 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7251 :
7252 351 : const auto IsValidFlag = [this](Type v)
7253 : {
7254 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7255 : {
7256 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7257 : {
7258 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7259 : m_anValidFlagValues[i])
7260 : {
7261 4 : return true;
7262 : }
7263 : }
7264 : }
7265 42 : else if (!m_anValidFlagValues.empty())
7266 : {
7267 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7268 : {
7269 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7270 : {
7271 4 : return true;
7272 : }
7273 : }
7274 : }
7275 : else /* if( !m_anValidFlagMasks.empty() ) */
7276 : {
7277 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7278 : {
7279 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7280 : {
7281 9 : return true;
7282 : }
7283 : }
7284 : }
7285 37 : return false;
7286 : };
7287 :
7288 : #define GET_MASK_FOR_SAMPLE(v) \
7289 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7290 : !(bHasMissingValue && v == nMissingValue) && \
7291 : !(bHasFillValue && v == nFillValue) && \
7292 : !(bHasValidMin && v < nValidMin) && \
7293 : !(bHasValidMax && v > nValidMax) && \
7294 : (!bHasValidFlags || IsValidFlag(v)));
7295 :
7296 44 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7297 : /* Optimized case: Byte output and output buffer is contiguous */
7298 44 : if (bBufferDataTypeIsByte)
7299 : {
7300 40 : bool bContiguous = true;
7301 103 : for (size_t i = 0; i < nDims; i++)
7302 : {
7303 64 : if (bufferStride[i] != tmpBufferStrideVector[i])
7304 : {
7305 1 : bContiguous = false;
7306 1 : break;
7307 : }
7308 : }
7309 40 : if (bContiguous)
7310 : {
7311 39 : size_t nElts = 1;
7312 102 : for (size_t i = 0; i < nDims; i++)
7313 63 : nElts *= count[i];
7314 :
7315 1113 : for (size_t i = 0; i < nElts; i++)
7316 : {
7317 1074 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7318 1074 : static_cast<GByte *>(pDstBuffer)[i] =
7319 1074 : GET_MASK_FOR_SAMPLE(*pSrc);
7320 : }
7321 39 : return;
7322 : }
7323 : }
7324 :
7325 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7326 :
7327 : struct Stack
7328 : {
7329 : size_t nIters = 0;
7330 : const GByte *src_ptr = nullptr;
7331 : GByte *dst_ptr = nullptr;
7332 : GPtrDiff_t src_inc_offset = 0;
7333 : GPtrDiff_t dst_inc_offset = 0;
7334 : };
7335 :
7336 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7337 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7338 15 : for (size_t i = 0; i < nDims; i++)
7339 : {
7340 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7341 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7342 10 : stack[i].dst_inc_offset =
7343 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7344 : }
7345 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7346 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7347 :
7348 5 : size_t dimIdx = 0;
7349 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7350 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7351 5 : CPLAssert(nBufferDTSize <= 16);
7352 15 : for (GByte flag = 0; flag <= 1; flag++)
7353 : {
7354 : // Coverity misses that m_dt is of type Byte
7355 : // coverity[overrun-buffer-val]
7356 10 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
7357 : bufferDataType);
7358 : }
7359 :
7360 43 : lbl_next_depth:
7361 43 : if (dimIdx == nDimsMinus1)
7362 : {
7363 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7364 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7365 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7366 :
7367 420 : while (true)
7368 : {
7369 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7370 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7371 :
7372 455 : if (bBufferDataTypeIsByte)
7373 : {
7374 24 : *dst_ptr = flag;
7375 : }
7376 : else
7377 : {
7378 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7379 : }
7380 :
7381 455 : if ((--nIters) == 0)
7382 35 : break;
7383 420 : src_ptr += stack[dimIdx].src_inc_offset;
7384 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7385 : }
7386 : }
7387 : else
7388 : {
7389 8 : stack[dimIdx].nIters = count[dimIdx];
7390 : while (true)
7391 : {
7392 38 : dimIdx++;
7393 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7394 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7395 38 : goto lbl_next_depth;
7396 38 : lbl_return_to_caller:
7397 38 : dimIdx--;
7398 38 : if ((--stack[dimIdx].nIters) == 0)
7399 8 : break;
7400 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7401 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7402 : }
7403 : }
7404 43 : if (dimIdx > 0)
7405 38 : goto lbl_return_to_caller;
7406 : }
7407 :
7408 : /************************************************************************/
7409 : /* GetMask() */
7410 : /************************************************************************/
7411 :
7412 : /** Return an array that is a mask for the current array
7413 :
7414 : This array will be of type Byte, with values set to 0 to indicate invalid
7415 : pixels of the current array, and values set to 1 to indicate valid pixels.
7416 :
7417 : The generic implementation honours the NoDataValue, as well as various
7418 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7419 : and valid_range.
7420 :
7421 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7422 : can be used to specify strings of the "flag_meanings" attribute
7423 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7424 : for which pixels matching any of those flags will be set at 1 in the mask array,
7425 : and pixels matching none of those flags will be set at 0.
7426 : For example, let's consider the following netCDF variable defined with:
7427 : \verbatim
7428 : l2p_flags:valid_min = 0s ;
7429 : l2p_flags:valid_max = 256s ;
7430 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7431 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7432 : \endverbatim
7433 :
7434 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7435 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7436 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7437 : will be 1.
7438 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7439 : will be 0.
7440 :
7441 : This is the same as the C function GDALMDArrayGetMask().
7442 :
7443 : @param papszOptions NULL-terminated list of options, or NULL.
7444 :
7445 : @return a new array, that holds a reference to the original one, and thus is
7446 : a view of it (not a copy), or nullptr in case of error.
7447 : */
7448 : std::shared_ptr<GDALMDArray>
7449 49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7450 : {
7451 98 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7452 49 : if (!self)
7453 : {
7454 0 : CPLError(CE_Failure, CPLE_AppDefined,
7455 : "Driver implementation issue: m_pSelf not set !");
7456 0 : return nullptr;
7457 : }
7458 49 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7459 : {
7460 1 : CPLError(CE_Failure, CPLE_AppDefined,
7461 : "GetMask() only supports numeric data type");
7462 1 : return nullptr;
7463 : }
7464 48 : return GDALMDArrayMask::Create(self, papszOptions);
7465 : }
7466 :
7467 : /************************************************************************/
7468 : /* IsRegularlySpaced() */
7469 : /************************************************************************/
7470 :
7471 : /** Returns whether an array is a 1D regularly spaced array.
7472 : *
7473 : * @param[out] dfStart First value in the array
7474 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7475 : * @return true if the array is regularly spaced.
7476 : */
7477 189 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7478 : {
7479 189 : dfStart = 0;
7480 189 : dfIncrement = 0;
7481 189 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7482 0 : return false;
7483 189 : const auto nSize = GetDimensions()[0]->GetSize();
7484 189 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7485 2 : return false;
7486 :
7487 187 : size_t nCount = static_cast<size_t>(nSize);
7488 374 : std::vector<double> adfTmp;
7489 : try
7490 : {
7491 187 : adfTmp.resize(nCount);
7492 : }
7493 0 : catch (const std::exception &)
7494 : {
7495 0 : return false;
7496 : }
7497 :
7498 187 : GUInt64 anStart[1] = {0};
7499 187 : size_t anCount[1] = {nCount};
7500 :
7501 : const auto IsRegularlySpacedInternal =
7502 83950 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7503 : {
7504 261 : dfStart = adfTmp[0];
7505 261 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7506 261 : if (dfIncrement == 0)
7507 : {
7508 3 : return false;
7509 : }
7510 20920 : for (size_t i = 1; i < anCount[0]; i++)
7511 : {
7512 20662 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7513 20662 : 1e-3 * fabs(dfIncrement))
7514 : {
7515 0 : return false;
7516 : }
7517 : }
7518 258 : return true;
7519 187 : };
7520 :
7521 : // First try with the first block(s). This can avoid excessive processing
7522 : // time, for example with Zarr datasets.
7523 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7524 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7525 187 : const auto nBlockSize = GetBlockSize()[0];
7526 187 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7527 : {
7528 : size_t nReducedCount =
7529 77 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7530 440 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7531 363 : nReducedCount *= 2;
7532 77 : anCount[0] = nReducedCount;
7533 77 : if (!Read(anStart, anCount, nullptr, nullptr,
7534 154 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7535 : {
7536 0 : return false;
7537 : }
7538 77 : if (!IsRegularlySpacedInternal())
7539 : {
7540 3 : return false;
7541 : }
7542 :
7543 : // Get next values
7544 74 : anStart[0] = nReducedCount;
7545 74 : anCount[0] = nCount - nReducedCount;
7546 : }
7547 :
7548 184 : if (!Read(anStart, anCount, nullptr, nullptr,
7549 368 : GDALExtendedDataType::Create(GDT_Float64),
7550 184 : &adfTmp[static_cast<size_t>(anStart[0])]))
7551 : {
7552 0 : return false;
7553 : }
7554 :
7555 184 : return IsRegularlySpacedInternal();
7556 : }
7557 :
7558 : /************************************************************************/
7559 : /* GuessGeoTransform() */
7560 : /************************************************************************/
7561 :
7562 : /** Returns whether 2 specified dimensions form a geotransform
7563 : *
7564 : * @param nDimX Index of the X axis.
7565 : * @param nDimY Index of the Y axis.
7566 : * @param bPixelIsPoint Whether the geotransform should be returned
7567 : * with the pixel-is-point (pixel-center) convention
7568 : * (bPixelIsPoint = true), or with the pixel-is-area
7569 : * (top left corner convention)
7570 : * (bPixelIsPoint = false)
7571 : * @param[out] adfGeoTransform Computed geotransform
7572 : * @return true if a geotransform could be computed.
7573 : */
7574 227 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7575 : bool bPixelIsPoint,
7576 : double adfGeoTransform[6]) const
7577 : {
7578 227 : const auto &dims(GetDimensions());
7579 454 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7580 454 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7581 227 : double dfXStart = 0.0;
7582 227 : double dfXSpacing = 0.0;
7583 227 : double dfYStart = 0.0;
7584 227 : double dfYSpacing = 0.0;
7585 511 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7586 284 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7587 332 : poVarY && poVarY->GetDimensionCount() == 1 &&
7588 95 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7589 459 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7590 90 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7591 : {
7592 90 : adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7593 90 : adfGeoTransform[1] = dfXSpacing;
7594 90 : adfGeoTransform[2] = 0;
7595 90 : adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7596 90 : adfGeoTransform[4] = 0;
7597 90 : adfGeoTransform[5] = dfYSpacing;
7598 90 : return true;
7599 : }
7600 137 : return false;
7601 : }
7602 :
7603 : /************************************************************************/
7604 : /* GDALMDArrayResampled */
7605 : /************************************************************************/
7606 :
7607 : class GDALMDArrayResampledDataset;
7608 :
7609 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7610 : {
7611 : protected:
7612 : CPLErr IReadBlock(int, int, void *) override;
7613 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7614 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7615 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7616 : GSpacing nLineSpaceBuf,
7617 : GDALRasterIOExtraArg *psExtraArg) override;
7618 :
7619 : public:
7620 : explicit GDALMDArrayResampledDatasetRasterBand(
7621 : GDALMDArrayResampledDataset *poDSIn);
7622 :
7623 : double GetNoDataValue(int *pbHasNoData) override;
7624 : };
7625 :
7626 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7627 : {
7628 : friend class GDALMDArrayResampled;
7629 : friend class GDALMDArrayResampledDatasetRasterBand;
7630 :
7631 : std::shared_ptr<GDALMDArray> m_poArray;
7632 : const size_t m_iXDim;
7633 : const size_t m_iYDim;
7634 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7635 : bool m_bHasGT = false;
7636 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7637 :
7638 : std::vector<GUInt64> m_anOffset{};
7639 : std::vector<size_t> m_anCount{};
7640 : std::vector<GPtrDiff_t> m_anStride{};
7641 :
7642 : std::string m_osFilenameLong{};
7643 : std::string m_osFilenameLat{};
7644 :
7645 : public:
7646 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7647 : size_t iXDim, size_t iYDim)
7648 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7649 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7650 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7651 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7652 : {
7653 24 : const auto &dims(m_poArray->GetDimensions());
7654 :
7655 24 : nRasterYSize = static_cast<int>(
7656 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7657 24 : nRasterXSize = static_cast<int>(
7658 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7659 :
7660 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7661 24 : m_adfGeoTransform);
7662 :
7663 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7664 24 : }
7665 :
7666 48 : ~GDALMDArrayResampledDataset()
7667 24 : {
7668 24 : if (!m_osFilenameLong.empty())
7669 5 : VSIUnlink(m_osFilenameLong.c_str());
7670 24 : if (!m_osFilenameLat.empty())
7671 5 : VSIUnlink(m_osFilenameLat.c_str());
7672 48 : }
7673 :
7674 43 : CPLErr GetGeoTransform(double *padfGeoTransform) override
7675 : {
7676 43 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7677 43 : return m_bHasGT ? CE_None : CE_Failure;
7678 : }
7679 :
7680 105 : const OGRSpatialReference *GetSpatialRef() const override
7681 : {
7682 105 : m_poSRS = m_poArray->GetSpatialRef();
7683 105 : if (m_poSRS)
7684 : {
7685 79 : m_poSRS.reset(m_poSRS->Clone());
7686 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7687 237 : for (auto &m : axisMapping)
7688 : {
7689 158 : if (m == static_cast<int>(m_iXDim) + 1)
7690 79 : m = 1;
7691 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7692 79 : m = 2;
7693 : }
7694 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7695 : }
7696 105 : return m_poSRS.get();
7697 : }
7698 :
7699 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7700 : const std::string &osFilenameLat)
7701 : {
7702 5 : m_osFilenameLong = osFilenameLong;
7703 5 : m_osFilenameLat = osFilenameLat;
7704 10 : CPLStringList aosGeoLoc;
7705 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7706 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7707 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7708 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7709 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7710 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7711 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7712 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7713 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7714 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7715 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7716 5 : }
7717 : };
7718 :
7719 : /************************************************************************/
7720 : /* GDALMDArrayResampledDatasetRasterBand() */
7721 : /************************************************************************/
7722 :
7723 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7724 24 : GDALMDArrayResampledDataset *poDSIn)
7725 : {
7726 24 : const auto &poArray(poDSIn->m_poArray);
7727 24 : const auto blockSize(poArray->GetBlockSize());
7728 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7729 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7730 13 : blockSize[poDSIn->m_iYDim]))
7731 24 : : 1;
7732 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7733 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7734 13 : blockSize[poDSIn->m_iXDim]))
7735 24 : : poDSIn->GetRasterXSize();
7736 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7737 24 : eAccess = poDSIn->eAccess;
7738 24 : }
7739 :
7740 : /************************************************************************/
7741 : /* GetNoDataValue() */
7742 : /************************************************************************/
7743 :
7744 50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7745 : {
7746 50 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7747 50 : const auto &poArray(l_poDS->m_poArray);
7748 50 : bool bHasNodata = false;
7749 50 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7750 50 : if (pbHasNoData)
7751 46 : *pbHasNoData = bHasNodata;
7752 50 : return dfRes;
7753 : }
7754 :
7755 : /************************************************************************/
7756 : /* IReadBlock() */
7757 : /************************************************************************/
7758 :
7759 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7760 : int nBlockYOff,
7761 : void *pImage)
7762 : {
7763 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7764 0 : const int nXOff = nBlockXOff * nBlockXSize;
7765 0 : const int nYOff = nBlockYOff * nBlockYSize;
7766 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7767 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7768 : GDALRasterIOExtraArg sExtraArg;
7769 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7770 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7771 : nReqXSize, nReqYSize, eDataType, nDTSize,
7772 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7773 : }
7774 :
7775 : /************************************************************************/
7776 : /* IRasterIO() */
7777 : /************************************************************************/
7778 :
7779 32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7780 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7781 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7782 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7783 : GDALRasterIOExtraArg *psExtraArg)
7784 : {
7785 32 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7786 32 : const auto &poArray(l_poDS->m_poArray);
7787 32 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7788 32 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7789 32 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7790 32 : (nLineSpaceBuf % nBufferDTSize) == 0)
7791 : {
7792 32 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7793 32 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7794 64 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7795 32 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7796 :
7797 32 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7798 32 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7799 64 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7800 32 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7801 :
7802 64 : return poArray->Read(l_poDS->m_anOffset.data(),
7803 32 : l_poDS->m_anCount.data(), nullptr,
7804 32 : l_poDS->m_anStride.data(),
7805 64 : GDALExtendedDataType::Create(eBufType), pData)
7806 32 : ? CE_None
7807 32 : : CE_Failure;
7808 : }
7809 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7810 : pData, nBufXSize, nBufYSize, eBufType,
7811 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7812 : }
7813 :
7814 : class GDALMDArrayResampled final : public GDALPamMDArray
7815 : {
7816 : private:
7817 : std::shared_ptr<GDALMDArray> m_poParent{};
7818 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7819 : std::vector<GUInt64> m_anBlockSize;
7820 : GDALExtendedDataType m_dt;
7821 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7822 : std::shared_ptr<GDALMDArray> m_poVarX{};
7823 : std::shared_ptr<GDALMDArray> m_poVarY{};
7824 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7825 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7826 :
7827 : protected:
7828 21 : GDALMDArrayResampled(
7829 : const std::shared_ptr<GDALMDArray> &poParent,
7830 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7831 : const std::vector<GUInt64> &anBlockSize)
7832 42 : : GDALAbstractMDArray(std::string(),
7833 42 : "Resampled view of " + poParent->GetFullName()),
7834 : GDALPamMDArray(
7835 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7836 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7837 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7838 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7839 : {
7840 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7841 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7842 21 : }
7843 :
7844 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7845 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7846 : const GDALExtendedDataType &bufferDataType,
7847 : void *pDstBuffer) const override;
7848 :
7849 : public:
7850 : static std::shared_ptr<GDALMDArray>
7851 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7852 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7853 : GDALRIOResampleAlg resampleAlg,
7854 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7855 :
7856 42 : ~GDALMDArrayResampled()
7857 21 : {
7858 : // First close the warped VRT
7859 21 : m_poReprojectedDS.reset();
7860 21 : m_poParentDS.reset();
7861 42 : }
7862 :
7863 11 : bool IsWritable() const override
7864 : {
7865 11 : return false;
7866 : }
7867 :
7868 74 : const std::string &GetFilename() const override
7869 : {
7870 74 : return m_poParent->GetFilename();
7871 : }
7872 :
7873 : const std::vector<std::shared_ptr<GDALDimension>> &
7874 257 : GetDimensions() const override
7875 : {
7876 257 : return m_apoDims;
7877 : }
7878 :
7879 109 : const GDALExtendedDataType &GetDataType() const override
7880 : {
7881 109 : return m_dt;
7882 : }
7883 :
7884 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7885 : {
7886 21 : return m_poSRS;
7887 : }
7888 :
7889 12 : std::vector<GUInt64> GetBlockSize() const override
7890 : {
7891 12 : return m_anBlockSize;
7892 : }
7893 :
7894 : std::shared_ptr<GDALAttribute>
7895 1 : GetAttribute(const std::string &osName) const override
7896 : {
7897 1 : return m_poParent->GetAttribute(osName);
7898 : }
7899 :
7900 : std::vector<std::shared_ptr<GDALAttribute>>
7901 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7902 : {
7903 12 : return m_poParent->GetAttributes(papszOptions);
7904 : }
7905 :
7906 1 : const std::string &GetUnit() const override
7907 : {
7908 1 : return m_poParent->GetUnit();
7909 : }
7910 :
7911 1 : const void *GetRawNoDataValue() const override
7912 : {
7913 1 : return m_poParent->GetRawNoDataValue();
7914 : }
7915 :
7916 1 : double GetOffset(bool *pbHasOffset,
7917 : GDALDataType *peStorageType) const override
7918 : {
7919 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7920 : }
7921 :
7922 1 : double GetScale(bool *pbHasScale,
7923 : GDALDataType *peStorageType) const override
7924 : {
7925 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7926 : }
7927 : };
7928 :
7929 : /************************************************************************/
7930 : /* GDALMDArrayResampled::Create() */
7931 : /************************************************************************/
7932 :
7933 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7934 : const std::shared_ptr<GDALMDArray> &poParent,
7935 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7936 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7937 : CSLConstList /* papszOptions */)
7938 : {
7939 29 : const char *pszResampleAlg = "nearest";
7940 29 : bool unsupported = false;
7941 29 : switch (resampleAlg)
7942 : {
7943 16 : case GRIORA_NearestNeighbour:
7944 16 : pszResampleAlg = "nearest";
7945 16 : break;
7946 2 : case GRIORA_Bilinear:
7947 2 : pszResampleAlg = "bilinear";
7948 2 : break;
7949 5 : case GRIORA_Cubic:
7950 5 : pszResampleAlg = "cubic";
7951 5 : break;
7952 1 : case GRIORA_CubicSpline:
7953 1 : pszResampleAlg = "cubicspline";
7954 1 : break;
7955 1 : case GRIORA_Lanczos:
7956 1 : pszResampleAlg = "lanczos";
7957 1 : break;
7958 1 : case GRIORA_Average:
7959 1 : pszResampleAlg = "average";
7960 1 : break;
7961 1 : case GRIORA_Mode:
7962 1 : pszResampleAlg = "mode";
7963 1 : break;
7964 1 : case GRIORA_Gauss:
7965 1 : unsupported = true;
7966 1 : break;
7967 0 : case GRIORA_RESERVED_START:
7968 0 : unsupported = true;
7969 0 : break;
7970 0 : case GRIORA_RESERVED_END:
7971 0 : unsupported = true;
7972 0 : break;
7973 1 : case GRIORA_RMS:
7974 1 : pszResampleAlg = "rms";
7975 1 : break;
7976 : }
7977 29 : if (unsupported)
7978 : {
7979 1 : CPLError(CE_Failure, CPLE_NotSupported,
7980 : "Unsupported resample method for GetResampled()");
7981 1 : return nullptr;
7982 : }
7983 :
7984 28 : if (poParent->GetDimensionCount() < 2)
7985 : {
7986 1 : CPLError(CE_Failure, CPLE_NotSupported,
7987 : "GetResampled() only supports 2 dimensions or more");
7988 1 : return nullptr;
7989 : }
7990 :
7991 27 : const auto &aoParentDims = poParent->GetDimensions();
7992 27 : if (apoNewDimsIn.size() != aoParentDims.size())
7993 : {
7994 2 : CPLError(CE_Failure, CPLE_AppDefined,
7995 : "GetResampled(): apoNewDims size should be the same as "
7996 : "GetDimensionCount()");
7997 2 : return nullptr;
7998 : }
7999 :
8000 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8001 25 : apoNewDims.reserve(apoNewDimsIn.size());
8002 :
8003 50 : std::vector<GUInt64> anBlockSize;
8004 25 : anBlockSize.reserve(apoNewDimsIn.size());
8005 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
8006 :
8007 50 : auto apoParentDims = poParent->GetDimensions();
8008 : // Special case for NASA EMIT datasets
8009 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8010 7 : apoParentDims[0]->GetName() == "downtrack" &&
8011 32 : apoParentDims[1]->GetName() == "crosstrack" &&
8012 2 : apoParentDims[2]->GetName() == "bands");
8013 :
8014 : const size_t iYDimParent =
8015 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8016 : const size_t iXDimParent =
8017 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8018 :
8019 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8020 : {
8021 53 : if (i == iYDimParent || i == iXDimParent)
8022 48 : continue;
8023 5 : if (apoNewDimsIn[i] == nullptr)
8024 : {
8025 3 : apoNewDims.emplace_back(aoParentDims[i]);
8026 : }
8027 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8028 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8029 : {
8030 1 : CPLError(CE_Failure, CPLE_AppDefined,
8031 : "GetResampled(): apoNewDims[%u] should be the same "
8032 : "as its parent",
8033 : i);
8034 1 : return nullptr;
8035 : }
8036 : else
8037 : {
8038 1 : apoNewDims.emplace_back(aoParentDims[i]);
8039 : }
8040 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
8041 : }
8042 :
8043 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8044 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8045 :
8046 24 : double dfXStart = 0.0;
8047 24 : double dfXSpacing = 0.0;
8048 24 : bool gotXSpacing = false;
8049 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
8050 24 : if (poNewDimX)
8051 : {
8052 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8053 : {
8054 0 : CPLError(CE_Failure, CPLE_NotSupported,
8055 : "Too big size for X dimension");
8056 0 : return nullptr;
8057 : }
8058 4 : auto var = poNewDimX->GetIndexingVariable();
8059 4 : if (var)
8060 : {
8061 2 : if (var->GetDimensionCount() != 1 ||
8062 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8063 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8064 : {
8065 0 : CPLError(CE_Failure, CPLE_NotSupported,
8066 : "New X dimension should be indexed by a regularly "
8067 : "spaced variable");
8068 0 : return nullptr;
8069 : }
8070 1 : gotXSpacing = true;
8071 : }
8072 : }
8073 :
8074 24 : double dfYStart = 0.0;
8075 24 : double dfYSpacing = 0.0;
8076 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
8077 24 : bool gotYSpacing = false;
8078 24 : if (poNewDimY)
8079 : {
8080 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8081 : {
8082 0 : CPLError(CE_Failure, CPLE_NotSupported,
8083 : "Too big size for Y dimension");
8084 0 : return nullptr;
8085 : }
8086 4 : auto var = poNewDimY->GetIndexingVariable();
8087 4 : if (var)
8088 : {
8089 2 : if (var->GetDimensionCount() != 1 ||
8090 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8091 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8092 : {
8093 0 : CPLError(CE_Failure, CPLE_NotSupported,
8094 : "New Y dimension should be indexed by a regularly "
8095 : "spaced variable");
8096 0 : return nullptr;
8097 : }
8098 1 : gotYSpacing = true;
8099 : }
8100 : }
8101 :
8102 : // This limitation could probably be removed
8103 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8104 : {
8105 0 : CPLError(CE_Failure, CPLE_NotSupported,
8106 : "Either none of new X or Y dimension should have an indexing "
8107 : "variable, or both should both should have one.");
8108 0 : return nullptr;
8109 : }
8110 :
8111 48 : std::string osDstWKT;
8112 24 : if (poTargetSRS)
8113 : {
8114 2 : char *pszDstWKT = nullptr;
8115 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8116 : {
8117 0 : CPLFree(pszDstWKT);
8118 0 : return nullptr;
8119 : }
8120 2 : osDstWKT = pszDstWKT;
8121 2 : CPLFree(pszDstWKT);
8122 : }
8123 :
8124 : // Use coordinate variables for geolocation array
8125 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8126 24 : bool useGeolocationArray = false;
8127 24 : if (apoCoordinateVars.size() >= 2)
8128 : {
8129 0 : std::shared_ptr<GDALMDArray> poLongVar;
8130 0 : std::shared_ptr<GDALMDArray> poLatVar;
8131 15 : for (const auto &poCoordVar : apoCoordinateVars)
8132 : {
8133 10 : const auto &osName = poCoordVar->GetName();
8134 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
8135 20 : std::string osStandardName;
8136 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8137 2 : poAttr->GetDimensionCount() == 0)
8138 : {
8139 2 : const char *pszStandardName = poAttr->ReadAsString();
8140 2 : if (pszStandardName)
8141 2 : osStandardName = pszStandardName;
8142 : }
8143 21 : if (osName == "lon" || osName == "longitude" ||
8144 21 : osName == "Longitude" || osStandardName == "longitude")
8145 : {
8146 5 : poLongVar = poCoordVar;
8147 : }
8148 6 : else if (osName == "lat" || osName == "latitude" ||
8149 6 : osName == "Latitude" || osStandardName == "latitude")
8150 : {
8151 5 : poLatVar = poCoordVar;
8152 : }
8153 : }
8154 5 : if (poLatVar != nullptr && poLongVar != nullptr)
8155 : {
8156 5 : const auto longDimCount = poLongVar->GetDimensionCount();
8157 5 : const auto &longDims = poLongVar->GetDimensions();
8158 5 : const auto latDimCount = poLatVar->GetDimensionCount();
8159 5 : const auto &latDims = poLatVar->GetDimensions();
8160 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8161 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8162 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8163 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8164 : {
8165 : // Geolocation arrays are 1D, and of consistent size with
8166 : // the variable
8167 0 : useGeolocationArray = true;
8168 : }
8169 1 : else if ((longDimCount == 2 ||
8170 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8171 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8172 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8173 1 : (latDimCount == 2 ||
8174 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8175 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8176 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8177 :
8178 : {
8179 : // Geolocation arrays are 2D (or 3D with first dimension of
8180 : // size 1, as found in Sentinel 5P products), and of consistent
8181 : // size with the variable
8182 5 : useGeolocationArray = true;
8183 : }
8184 : else
8185 : {
8186 0 : CPLDebug(
8187 : "GDAL",
8188 : "Longitude and latitude coordinate variables found, "
8189 : "but their characteristics are not compatible of using "
8190 : "them as geolocation arrays");
8191 : }
8192 5 : if (useGeolocationArray)
8193 : {
8194 10 : CPLDebug("GDAL",
8195 : "Setting geolocation array from variables %s and %s",
8196 5 : poLongVar->GetName().c_str(),
8197 5 : poLatVar->GetName().c_str());
8198 : const std::string osFilenameLong =
8199 5 : VSIMemGenerateHiddenFilename("longitude.tif");
8200 : const std::string osFilenameLat =
8201 5 : VSIMemGenerateHiddenFilename("latitude.tif");
8202 : std::unique_ptr<GDALDataset> poTmpLongDS(
8203 : longDimCount == 1
8204 0 : ? poLongVar->AsClassicDataset(0, 0)
8205 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8206 15 : longDimCount - 2));
8207 5 : auto hTIFFLongDS = GDALTranslate(
8208 : osFilenameLong.c_str(),
8209 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8210 : std::unique_ptr<GDALDataset> poTmpLatDS(
8211 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8212 20 : : poLatVar->AsClassicDataset(
8213 15 : latDimCount - 1, latDimCount - 2));
8214 5 : auto hTIFFLatDS = GDALTranslate(
8215 : osFilenameLat.c_str(),
8216 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8217 5 : const bool bError =
8218 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8219 5 : GDALClose(hTIFFLongDS);
8220 5 : GDALClose(hTIFFLatDS);
8221 5 : if (bError)
8222 : {
8223 0 : VSIUnlink(osFilenameLong.c_str());
8224 0 : VSIUnlink(osFilenameLat.c_str());
8225 0 : return nullptr;
8226 : }
8227 :
8228 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8229 : }
8230 : }
8231 : else
8232 : {
8233 0 : CPLDebug("GDAL",
8234 : "Coordinate variables available for %s, but "
8235 : "longitude and/or latitude variables were not identified",
8236 0 : poParent->GetName().c_str());
8237 : }
8238 : }
8239 :
8240 : // Build gdalwarp arguments
8241 48 : CPLStringList aosArgv;
8242 :
8243 24 : aosArgv.AddString("-of");
8244 24 : aosArgv.AddString("VRT");
8245 :
8246 24 : aosArgv.AddString("-r");
8247 24 : aosArgv.AddString(pszResampleAlg);
8248 :
8249 24 : if (!osDstWKT.empty())
8250 : {
8251 2 : aosArgv.AddString("-t_srs");
8252 2 : aosArgv.AddString(osDstWKT.c_str());
8253 : }
8254 :
8255 24 : if (useGeolocationArray)
8256 5 : aosArgv.AddString("-geoloc");
8257 :
8258 24 : if (gotXSpacing && gotYSpacing)
8259 : {
8260 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8261 : const double dfXMax =
8262 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8263 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8264 : const double dfYMin =
8265 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8266 1 : aosArgv.AddString("-te");
8267 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8268 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8269 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8270 1 : aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8271 : }
8272 :
8273 24 : if (poNewDimX && poNewDimY)
8274 : {
8275 3 : aosArgv.AddString("-ts");
8276 : aosArgv.AddString(
8277 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8278 : aosArgv.AddString(
8279 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8280 : }
8281 21 : else if (poNewDimX)
8282 : {
8283 1 : aosArgv.AddString("-ts");
8284 : aosArgv.AddString(
8285 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8286 1 : aosArgv.AddString("0");
8287 : }
8288 20 : else if (poNewDimY)
8289 : {
8290 1 : aosArgv.AddString("-ts");
8291 1 : aosArgv.AddString("0");
8292 : aosArgv.AddString(
8293 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8294 : }
8295 :
8296 : // Create a warped VRT dataset
8297 : GDALWarpAppOptions *psOptions =
8298 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8299 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8300 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8301 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8302 24 : GDALWarpAppOptionsFree(psOptions);
8303 24 : if (poReprojectedDS == nullptr)
8304 3 : return nullptr;
8305 :
8306 : int nBlockXSize;
8307 : int nBlockYSize;
8308 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8309 21 : anBlockSize.emplace_back(nBlockYSize);
8310 21 : anBlockSize.emplace_back(nBlockXSize);
8311 :
8312 21 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8313 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8314 21 : CPLAssert(eErr == CE_None);
8315 21 : CPL_IGNORE_RET_VAL(eErr);
8316 :
8317 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8318 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8319 42 : poReprojectedDS->GetRasterYSize());
8320 : auto varY = GDALMDArrayRegularlySpaced::Create(
8321 63 : std::string(), poDimY->GetName(), poDimY,
8322 84 : adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8323 21 : poDimY->SetIndexingVariable(varY);
8324 :
8325 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8326 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8327 42 : poReprojectedDS->GetRasterXSize());
8328 : auto varX = GDALMDArrayRegularlySpaced::Create(
8329 63 : std::string(), poDimX->GetName(), poDimX,
8330 84 : adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8331 21 : poDimX->SetIndexingVariable(varX);
8332 :
8333 21 : apoNewDims.emplace_back(poDimY);
8334 21 : apoNewDims.emplace_back(poDimX);
8335 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8336 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8337 21 : newAr->SetSelf(newAr);
8338 21 : if (poTargetSRS)
8339 : {
8340 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8341 : }
8342 : else
8343 : {
8344 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8345 : }
8346 21 : newAr->m_poVarX = varX;
8347 21 : newAr->m_poVarY = varY;
8348 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8349 21 : newAr->m_poParentDS = std::move(poParentDS);
8350 :
8351 : // If the input array is y,x,band ordered, the above newAr is
8352 : // actually band,y,x ordered as it is more convenient for
8353 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8354 : // array to the order of the input array
8355 21 : if (bYXBandOrder)
8356 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8357 :
8358 19 : return newAr;
8359 : }
8360 :
8361 : /************************************************************************/
8362 : /* GDALMDArrayResampled::IRead() */
8363 : /************************************************************************/
8364 :
8365 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8366 : const size_t *count, const GInt64 *arrayStep,
8367 : const GPtrDiff_t *bufferStride,
8368 : const GDALExtendedDataType &bufferDataType,
8369 : void *pDstBuffer) const
8370 : {
8371 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8372 0 : return false;
8373 :
8374 : struct Stack
8375 : {
8376 : size_t nIters = 0;
8377 : GByte *dst_ptr = nullptr;
8378 : GPtrDiff_t dst_inc_offset = 0;
8379 : };
8380 :
8381 29 : const auto nDims = GetDimensionCount();
8382 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8383 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8384 92 : for (size_t i = 0; i < nDims; i++)
8385 : {
8386 63 : stack[i].dst_inc_offset =
8387 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8388 : }
8389 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8390 :
8391 29 : size_t dimIdx = 0;
8392 29 : const size_t iDimY = nDims - 2;
8393 29 : const size_t iDimX = nDims - 1;
8394 : // Use an array to avoid a false positive warning from CLang Static
8395 : // Analyzer about flushCaches being never read
8396 29 : bool flushCaches[] = {false};
8397 : const bool bYXBandOrder =
8398 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8399 :
8400 38 : lbl_next_depth:
8401 38 : if (dimIdx == iDimY)
8402 : {
8403 33 : if (flushCaches[0])
8404 : {
8405 5 : flushCaches[0] = false;
8406 : // When changing of 2D slice, flush GDAL 2D buffers
8407 5 : m_poParentDS->FlushCache(false);
8408 5 : m_poReprojectedDS->FlushCache(false);
8409 : }
8410 :
8411 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8412 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8413 : arrayStep, bufferStride, bufferDataType,
8414 33 : stack[dimIdx].dst_ptr))
8415 : {
8416 0 : return false;
8417 : }
8418 : }
8419 : else
8420 : {
8421 5 : stack[dimIdx].nIters = count[dimIdx];
8422 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8423 5 : arrayStartIdx[dimIdx])
8424 : {
8425 1 : flushCaches[0] = true;
8426 : }
8427 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8428 5 : arrayStartIdx[dimIdx];
8429 : while (true)
8430 : {
8431 9 : dimIdx++;
8432 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8433 9 : goto lbl_next_depth;
8434 9 : lbl_return_to_caller:
8435 9 : dimIdx--;
8436 9 : if ((--stack[dimIdx].nIters) == 0)
8437 5 : break;
8438 4 : flushCaches[0] = true;
8439 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8440 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8441 : }
8442 : }
8443 38 : if (dimIdx > 0)
8444 9 : goto lbl_return_to_caller;
8445 :
8446 29 : return true;
8447 : }
8448 :
8449 : /************************************************************************/
8450 : /* GetResampled() */
8451 : /************************************************************************/
8452 :
8453 : /** Return an array that is a resampled / reprojected view of the current array
8454 : *
8455 : * This is the same as the C function GDALMDArrayGetResampled().
8456 : *
8457 : * Currently this method can only resample along the last 2 dimensions, unless
8458 : * orthorectifying a NASA EMIT dataset.
8459 : *
8460 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8461 : * geometry lookup table (GLT) is used by default for fast orthorectification.
8462 : *
8463 : * Options available are:
8464 : * <ul>
8465 : * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8466 : * Can be set to NO to use generic reprojection method.
8467 : * </li>
8468 : * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8469 : * orthorectification to take into account the value of the
8470 : * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8471 : * current array along the band dimension are valid.</li>
8472 : * </ul>
8473 : *
8474 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8475 : * apoNewDims[i] can be NULL to let the method automatically
8476 : * determine it.
8477 : * @param resampleAlg Resampling algorithm
8478 : * @param poTargetSRS Target SRS, or nullptr
8479 : * @param papszOptions NULL-terminated list of options, or NULL.
8480 : *
8481 : * @return a new array, that holds a reference to the original one, and thus is
8482 : * a view of it (not a copy), or nullptr in case of error.
8483 : *
8484 : * @since 3.4
8485 : */
8486 38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8487 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8488 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8489 : CSLConstList papszOptions) const
8490 : {
8491 76 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8492 38 : if (!self)
8493 : {
8494 0 : CPLError(CE_Failure, CPLE_AppDefined,
8495 : "Driver implementation issue: m_pSelf not set !");
8496 0 : return nullptr;
8497 : }
8498 38 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8499 : {
8500 0 : CPLError(CE_Failure, CPLE_AppDefined,
8501 : "GetResampled() only supports numeric data type");
8502 0 : return nullptr;
8503 : }
8504 :
8505 : // Special case for NASA EMIT datasets
8506 76 : auto apoDims = GetDimensions();
8507 36 : if (poTargetSRS == nullptr &&
8508 59 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8509 20 : apoDims[1]->GetName() == "crosstrack" &&
8510 10 : apoDims[2]->GetName() == "bands" &&
8511 48 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8512 1 : apoNewDims ==
8513 42 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8514 30 : apoDims[2]})) ||
8515 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8516 3 : apoDims[1]->GetName() == "crosstrack" &&
8517 77 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8518 13 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8519 : "EMIT_ORTHORECTIFICATION", "YES")))
8520 : {
8521 9 : auto poRootGroup = GetRootGroup();
8522 9 : if (poRootGroup)
8523 : {
8524 18 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8525 18 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8526 9 : if (poAttrGeotransform &&
8527 9 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8528 9 : poAttrGeotransform->GetDimensionCount() == 1 &&
8529 27 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8530 9 : poLocationGroup)
8531 : {
8532 18 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8533 18 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8534 27 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8535 18 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8536 18 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8537 27 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8538 27 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8539 9 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8540 : {
8541 : return CreateGLTOrthorectified(
8542 : self, poRootGroup, poGLT_X, poGLT_Y,
8543 : /* nGLTIndexOffset = */ -1,
8544 18 : poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8545 : }
8546 : }
8547 : }
8548 : }
8549 :
8550 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8551 : "EMIT_ORTHORECTIFICATION", "NO")))
8552 : {
8553 0 : CPLError(CE_Failure, CPLE_AppDefined,
8554 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8555 : "parameters are not compatible with it");
8556 0 : return nullptr;
8557 : }
8558 :
8559 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8560 29 : poTargetSRS, papszOptions);
8561 : }
8562 :
8563 : /************************************************************************/
8564 : /* GDALDatasetFromArray() */
8565 : /************************************************************************/
8566 :
8567 : class GDALDatasetFromArray;
8568 :
8569 : namespace
8570 : {
8571 : struct MetadataItem
8572 : {
8573 : std::shared_ptr<GDALMDArray> poArray{};
8574 : std::string osName{};
8575 : std::string osDefinition{};
8576 : bool bDefinitionUsesPctForG = false;
8577 : };
8578 :
8579 : struct BandImageryMetadata
8580 : {
8581 : std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
8582 : double dfCentralWavelengthToMicrometer = 1.0;
8583 : std::shared_ptr<GDALMDArray> poFWHMArray{};
8584 : double dfFWHMToMicrometer = 1.0;
8585 : };
8586 :
8587 : } // namespace
8588 :
8589 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8590 : {
8591 : std::vector<GUInt64> m_anOffset{};
8592 : std::vector<size_t> m_anCount{};
8593 : std::vector<GPtrDiff_t> m_anStride{};
8594 :
8595 : protected:
8596 : CPLErr IReadBlock(int, int, void *) override;
8597 : CPLErr IWriteBlock(int, int, void *) override;
8598 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8599 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8600 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8601 : GSpacing nLineSpaceBuf,
8602 : GDALRasterIOExtraArg *psExtraArg) override;
8603 :
8604 : public:
8605 : explicit GDALRasterBandFromArray(
8606 : GDALDatasetFromArray *poDSIn,
8607 : const std::vector<GUInt64> &anOtherDimCoord,
8608 : const std::vector<std::vector<MetadataItem>>
8609 : &aoBandParameterMetadataItems,
8610 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8611 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8612 :
8613 : double GetNoDataValue(int *pbHasNoData) override;
8614 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8615 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8616 : double GetOffset(int *pbHasOffset) override;
8617 : double GetScale(int *pbHasScale) override;
8618 : const char *GetUnitType() override;
8619 : GDALColorInterp GetColorInterpretation() override;
8620 : };
8621 :
8622 : class GDALDatasetFromArray final : public GDALPamDataset
8623 : {
8624 : friend class GDALRasterBandFromArray;
8625 :
8626 : std::shared_ptr<GDALMDArray> m_poArray;
8627 : size_t m_iXDim;
8628 : size_t m_iYDim;
8629 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8630 : bool m_bHasGT = false;
8631 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8632 : GDALMultiDomainMetadata m_oMDD{};
8633 : std::string m_osOvrFilename{};
8634 :
8635 : public:
8636 203 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8637 : size_t iXDim, size_t iYDim)
8638 203 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8639 : {
8640 : // Initialize an overview filename from the filename of the array
8641 : // and its name.
8642 203 : const std::string &osFilename = m_poArray->GetFilename();
8643 203 : if (!osFilename.empty())
8644 : {
8645 181 : m_osOvrFilename = osFilename;
8646 181 : m_osOvrFilename += '.';
8647 6630 : for (char ch : m_poArray->GetName())
8648 : {
8649 6449 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8650 5715 : (ch >= 'a' && ch <= 'z') || ch == '_')
8651 : {
8652 5188 : m_osOvrFilename += ch;
8653 : }
8654 : else
8655 : {
8656 1261 : m_osOvrFilename += '_';
8657 : }
8658 : }
8659 181 : m_osOvrFilename += ".ovr";
8660 181 : oOvManager.Initialize(this);
8661 : }
8662 203 : }
8663 :
8664 : static GDALDatasetFromArray *
8665 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8666 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8667 : CSLConstList papszOptions);
8668 :
8669 406 : ~GDALDatasetFromArray()
8670 203 : {
8671 203 : GDALDatasetFromArray::Close();
8672 406 : }
8673 :
8674 332 : CPLErr Close() override
8675 : {
8676 332 : CPLErr eErr = CE_None;
8677 332 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8678 : {
8679 332 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8680 : CE_None)
8681 0 : eErr = CE_Failure;
8682 332 : m_poArray.reset();
8683 : }
8684 332 : return eErr;
8685 : }
8686 :
8687 54 : CPLErr GetGeoTransform(double *padfGeoTransform) override
8688 : {
8689 54 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8690 54 : return m_bHasGT ? CE_None : CE_Failure;
8691 : }
8692 :
8693 62 : const OGRSpatialReference *GetSpatialRef() const override
8694 : {
8695 62 : if (m_poArray->GetDimensionCount() < 2)
8696 3 : return nullptr;
8697 59 : m_poSRS = m_poArray->GetSpatialRef();
8698 59 : if (m_poSRS)
8699 : {
8700 20 : m_poSRS.reset(m_poSRS->Clone());
8701 40 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8702 60 : for (auto &m : axisMapping)
8703 : {
8704 40 : if (m == static_cast<int>(m_iXDim) + 1)
8705 20 : m = 1;
8706 20 : else if (m == static_cast<int>(m_iYDim) + 1)
8707 20 : m = 2;
8708 : }
8709 20 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8710 : }
8711 59 : return m_poSRS.get();
8712 : }
8713 :
8714 5 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8715 : {
8716 5 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8717 : }
8718 :
8719 163 : char **GetMetadata(const char *pszDomain) override
8720 : {
8721 163 : return m_oMDD.GetMetadata(pszDomain);
8722 : }
8723 :
8724 233 : const char *GetMetadataItem(const char *pszName,
8725 : const char *pszDomain) override
8726 : {
8727 421 : if (!m_osOvrFilename.empty() && pszName &&
8728 435 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8729 14 : EQUAL(pszDomain, "OVERVIEWS"))
8730 : {
8731 14 : return m_osOvrFilename.c_str();
8732 : }
8733 219 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8734 : }
8735 : };
8736 :
8737 : /************************************************************************/
8738 : /* GDALRasterBandFromArray() */
8739 : /************************************************************************/
8740 :
8741 275 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8742 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8743 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8744 : const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8745 275 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8746 : {
8747 275 : const auto &poArray(poDSIn->m_poArray);
8748 275 : const auto &dims(poArray->GetDimensions());
8749 275 : const auto nDimCount(dims.size());
8750 550 : const auto blockSize(poArray->GetBlockSize());
8751 263 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8752 538 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8753 138 : blockSize[poDSIn->m_iYDim]))
8754 : : 1;
8755 275 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8756 150 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8757 150 : blockSize[poDSIn->m_iXDim]))
8758 275 : : poDSIn->GetRasterXSize();
8759 275 : eDataType = poArray->GetDataType().GetNumericDataType();
8760 275 : eAccess = poDSIn->eAccess;
8761 275 : m_anOffset.resize(nDimCount);
8762 275 : m_anCount.resize(nDimCount, 1);
8763 275 : m_anStride.resize(nDimCount);
8764 935 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8765 : {
8766 660 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8767 : {
8768 244 : std::string dimName(dims[i]->GetName());
8769 122 : GUInt64 nIndex = anOtherDimCoord[j];
8770 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8771 : // subsetted dimensions as generated by GetView()
8772 122 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8773 : {
8774 : CPLStringList aosTokens(
8775 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8776 6 : if (aosTokens.size() == 5)
8777 : {
8778 6 : dimName = aosTokens[1];
8779 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8780 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8781 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8782 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8783 0 : : nStartDim - (nIndex * -nIncrDim);
8784 : }
8785 : }
8786 122 : if (nDimCount != 3 || dimName != "Band")
8787 : {
8788 66 : SetMetadataItem(
8789 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8790 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8791 : }
8792 :
8793 122 : auto indexingVar = dims[i]->GetIndexingVariable();
8794 :
8795 : // If the indexing variable is also listed in band parameter arrays,
8796 : // then don't use our default formatting
8797 122 : if (indexingVar)
8798 : {
8799 38 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8800 : {
8801 12 : if (oItem.poArray->GetFullName() ==
8802 12 : indexingVar->GetFullName())
8803 : {
8804 12 : indexingVar.reset();
8805 12 : break;
8806 : }
8807 : }
8808 : }
8809 :
8810 148 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8811 26 : indexingVar->GetDimensions()[0]->GetSize() ==
8812 26 : dims[i]->GetSize())
8813 : {
8814 26 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8815 : {
8816 0 : if (!bHasWarned)
8817 : {
8818 0 : CPLError(
8819 : CE_Warning, CPLE_AppDefined,
8820 : "Maximum delay to load band metadata from "
8821 : "dimension indexing variables has expired. "
8822 : "Increase the value of the "
8823 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8824 : "option of GDALMDArray::AsClassicDataset() "
8825 : "(also accessible as the "
8826 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8827 : "configuration option), "
8828 : "or set it to 'unlimited' for unlimited delay. ");
8829 0 : bHasWarned = true;
8830 : }
8831 : }
8832 : else
8833 : {
8834 26 : size_t nCount = 1;
8835 26 : const auto &dt(indexingVar->GetDataType());
8836 52 : std::vector<GByte> abyTmp(dt.GetSize());
8837 52 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8838 26 : nullptr, nullptr, dt, &abyTmp[0]))
8839 : {
8840 26 : char *pszTmp = nullptr;
8841 26 : GDALExtendedDataType::CopyValue(
8842 26 : &abyTmp[0], dt, &pszTmp,
8843 52 : GDALExtendedDataType::CreateString());
8844 26 : if (pszTmp)
8845 : {
8846 26 : SetMetadataItem(
8847 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8848 : pszTmp);
8849 26 : CPLFree(pszTmp);
8850 : }
8851 :
8852 26 : const auto &unit(indexingVar->GetUnit());
8853 26 : if (!unit.empty())
8854 : {
8855 12 : SetMetadataItem(
8856 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8857 : unit.c_str());
8858 : }
8859 : }
8860 : }
8861 : }
8862 :
8863 138 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8864 : {
8865 32 : CPLString osVal;
8866 :
8867 16 : size_t nCount = 1;
8868 16 : const auto &dt(oItem.poArray->GetDataType());
8869 16 : if (oItem.bDefinitionUsesPctForG)
8870 : {
8871 : // There is one and only one %[x][.y]f|g in osDefinition
8872 12 : std::vector<GByte> abyTmp(dt.GetSize());
8873 12 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8874 6 : nullptr, nullptr, dt, &abyTmp[0]))
8875 : {
8876 6 : double dfVal = 0;
8877 6 : GDALExtendedDataType::CopyValue(
8878 6 : &abyTmp[0], dt, &dfVal,
8879 12 : GDALExtendedDataType::Create(GDT_Float64));
8880 6 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8881 : }
8882 : }
8883 : else
8884 : {
8885 : // There should be zero or one %s in osDefinition
8886 10 : char *pszValue = nullptr;
8887 10 : if (dt.GetClass() == GEDTC_STRING)
8888 : {
8889 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8890 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8891 : dt, &pszValue));
8892 : }
8893 : else
8894 : {
8895 16 : std::vector<GByte> abyTmp(dt.GetSize());
8896 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8897 : nullptr, nullptr, dt,
8898 8 : &abyTmp[0]))
8899 : {
8900 8 : GDALExtendedDataType::CopyValue(
8901 8 : &abyTmp[0], dt, &pszValue,
8902 16 : GDALExtendedDataType::CreateString());
8903 : }
8904 : }
8905 :
8906 10 : if (pszValue)
8907 : {
8908 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8909 10 : CPLFree(pszValue);
8910 : }
8911 : }
8912 16 : if (!osVal.empty())
8913 16 : SetMetadataItem(oItem.osName.c_str(), osVal);
8914 : }
8915 :
8916 122 : if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8917 : {
8918 : auto &poCentralWavelengthArray =
8919 2 : aoBandImageryMetadata[j].poCentralWavelengthArray;
8920 2 : size_t nCount = 1;
8921 2 : const auto &dt(poCentralWavelengthArray->GetDataType());
8922 4 : std::vector<GByte> abyTmp(dt.GetSize());
8923 4 : if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8924 : &nCount, nullptr, nullptr,
8925 2 : dt, &abyTmp[0]))
8926 : {
8927 2 : double dfVal = 0;
8928 2 : GDALExtendedDataType::CopyValue(
8929 2 : &abyTmp[0], dt, &dfVal,
8930 4 : GDALExtendedDataType::Create(GDT_Float64));
8931 2 : SetMetadataItem(
8932 : "CENTRAL_WAVELENGTH_UM",
8933 : CPLSPrintf(
8934 2 : "%g", dfVal * aoBandImageryMetadata[j]
8935 2 : .dfCentralWavelengthToMicrometer),
8936 : "IMAGERY");
8937 : }
8938 : }
8939 :
8940 122 : if (aoBandImageryMetadata[j].poFWHMArray)
8941 : {
8942 2 : auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
8943 2 : size_t nCount = 1;
8944 2 : const auto &dt(poFWHMArray->GetDataType());
8945 4 : std::vector<GByte> abyTmp(dt.GetSize());
8946 4 : if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
8947 2 : nullptr, dt, &abyTmp[0]))
8948 : {
8949 2 : double dfVal = 0;
8950 2 : GDALExtendedDataType::CopyValue(
8951 2 : &abyTmp[0], dt, &dfVal,
8952 4 : GDALExtendedDataType::Create(GDT_Float64));
8953 2 : SetMetadataItem(
8954 : "FWHM_UM",
8955 2 : CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
8956 2 : .dfFWHMToMicrometer),
8957 : "IMAGERY");
8958 : }
8959 : }
8960 :
8961 122 : m_anOffset[i] = anOtherDimCoord[j];
8962 122 : j++;
8963 : }
8964 : }
8965 275 : }
8966 :
8967 : /************************************************************************/
8968 : /* GetNoDataValue() */
8969 : /************************************************************************/
8970 :
8971 109 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
8972 : {
8973 109 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8974 109 : const auto &poArray(l_poDS->m_poArray);
8975 109 : bool bHasNodata = false;
8976 109 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
8977 109 : if (pbHasNoData)
8978 97 : *pbHasNoData = bHasNodata;
8979 109 : return res;
8980 : }
8981 :
8982 : /************************************************************************/
8983 : /* GetNoDataValueAsInt64() */
8984 : /************************************************************************/
8985 :
8986 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
8987 : {
8988 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8989 1 : const auto &poArray(l_poDS->m_poArray);
8990 1 : bool bHasNodata = false;
8991 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
8992 1 : if (pbHasNoData)
8993 1 : *pbHasNoData = bHasNodata;
8994 1 : return nodata;
8995 : }
8996 :
8997 : /************************************************************************/
8998 : /* GetNoDataValueAsUInt64() */
8999 : /************************************************************************/
9000 :
9001 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9002 : {
9003 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9004 1 : const auto &poArray(l_poDS->m_poArray);
9005 1 : bool bHasNodata = false;
9006 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9007 1 : if (pbHasNoData)
9008 1 : *pbHasNoData = bHasNodata;
9009 1 : return nodata;
9010 : }
9011 :
9012 : /************************************************************************/
9013 : /* GetOffset() */
9014 : /************************************************************************/
9015 :
9016 38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9017 : {
9018 38 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9019 38 : const auto &poArray(l_poDS->m_poArray);
9020 38 : bool bHasValue = false;
9021 38 : double dfRes = poArray->GetOffset(&bHasValue);
9022 38 : if (pbHasOffset)
9023 19 : *pbHasOffset = bHasValue;
9024 38 : return dfRes;
9025 : }
9026 :
9027 : /************************************************************************/
9028 : /* GetUnitType() */
9029 : /************************************************************************/
9030 :
9031 44 : const char *GDALRasterBandFromArray::GetUnitType()
9032 : {
9033 44 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9034 44 : const auto &poArray(l_poDS->m_poArray);
9035 44 : return poArray->GetUnit().c_str();
9036 : }
9037 :
9038 : /************************************************************************/
9039 : /* GetScale() */
9040 : /************************************************************************/
9041 :
9042 36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9043 : {
9044 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9045 36 : const auto &poArray(l_poDS->m_poArray);
9046 36 : bool bHasValue = false;
9047 36 : double dfRes = poArray->GetScale(&bHasValue);
9048 36 : if (pbHasScale)
9049 17 : *pbHasScale = bHasValue;
9050 36 : return dfRes;
9051 : }
9052 :
9053 : /************************************************************************/
9054 : /* IReadBlock() */
9055 : /************************************************************************/
9056 :
9057 68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9058 : void *pImage)
9059 : {
9060 68 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9061 68 : const int nXOff = nBlockXOff * nBlockXSize;
9062 68 : const int nYOff = nBlockYOff * nBlockYSize;
9063 68 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9064 68 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9065 : GDALRasterIOExtraArg sExtraArg;
9066 68 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9067 136 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9068 : nReqXSize, nReqYSize, eDataType, nDTSize,
9069 136 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9070 : }
9071 :
9072 : /************************************************************************/
9073 : /* IWriteBlock() */
9074 : /************************************************************************/
9075 :
9076 1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9077 : void *pImage)
9078 : {
9079 1 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9080 1 : const int nXOff = nBlockXOff * nBlockXSize;
9081 1 : const int nYOff = nBlockYOff * nBlockYSize;
9082 1 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9083 1 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9084 : GDALRasterIOExtraArg sExtraArg;
9085 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9086 2 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9087 : nReqXSize, nReqYSize, eDataType, nDTSize,
9088 2 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9089 : }
9090 :
9091 : /************************************************************************/
9092 : /* IRasterIO() */
9093 : /************************************************************************/
9094 :
9095 328 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9096 : int nYOff, int nXSize, int nYSize,
9097 : void *pData, int nBufXSize,
9098 : int nBufYSize, GDALDataType eBufType,
9099 : GSpacing nPixelSpaceBuf,
9100 : GSpacing nLineSpaceBuf,
9101 : GDALRasterIOExtraArg *psExtraArg)
9102 : {
9103 328 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9104 328 : const auto &poArray(l_poDS->m_poArray);
9105 328 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9106 328 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9107 328 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9108 328 : (nLineSpaceBuf % nBufferDTSize) == 0)
9109 : {
9110 328 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9111 328 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9112 656 : m_anStride[l_poDS->m_iXDim] =
9113 328 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9114 328 : if (poArray->GetDimensionCount() >= 2)
9115 : {
9116 319 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9117 319 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9118 319 : m_anStride[l_poDS->m_iYDim] =
9119 319 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9120 : }
9121 328 : if (eRWFlag == GF_Read)
9122 : {
9123 644 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9124 322 : m_anStride.data(),
9125 644 : GDALExtendedDataType::Create(eBufType), pData)
9126 322 : ? CE_None
9127 322 : : CE_Failure;
9128 : }
9129 : else
9130 : {
9131 12 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9132 6 : m_anStride.data(),
9133 12 : GDALExtendedDataType::Create(eBufType), pData)
9134 6 : ? CE_None
9135 6 : : CE_Failure;
9136 : }
9137 : }
9138 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9139 : pData, nBufXSize, nBufYSize, eBufType,
9140 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9141 : }
9142 :
9143 : /************************************************************************/
9144 : /* GetColorInterpretation() */
9145 : /************************************************************************/
9146 :
9147 58 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9148 : {
9149 58 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9150 58 : const auto &poArray(l_poDS->m_poArray);
9151 174 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9152 58 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9153 : {
9154 6 : bool bOK = false;
9155 6 : GUInt64 nStartIndex = 0;
9156 6 : if (poArray->GetDimensionCount() == 2 &&
9157 0 : poAttr->GetDimensionCount() == 0)
9158 : {
9159 0 : bOK = true;
9160 : }
9161 6 : else if (poArray->GetDimensionCount() == 3)
9162 : {
9163 6 : uint64_t nExtraDimSamples = 1;
9164 6 : const auto &apoDims = poArray->GetDimensions();
9165 24 : for (size_t i = 0; i < apoDims.size(); ++i)
9166 : {
9167 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9168 6 : nExtraDimSamples *= apoDims[i]->GetSize();
9169 : }
9170 6 : if (poAttr->GetDimensionsSize() ==
9171 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9172 : {
9173 6 : bOK = true;
9174 : }
9175 6 : nStartIndex = nBand - 1;
9176 : }
9177 6 : if (bOK)
9178 : {
9179 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
9180 6 : const size_t nCount = 1;
9181 6 : const GInt64 arrayStep = 1;
9182 6 : const GPtrDiff_t bufferStride = 1;
9183 6 : char *pszValue = nullptr;
9184 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9185 6 : oStringDT, &pszValue);
9186 6 : if (pszValue)
9187 : {
9188 : const auto eColorInterp =
9189 6 : GDALGetColorInterpretationByName(pszValue);
9190 6 : CPLFree(pszValue);
9191 6 : return eColorInterp;
9192 : }
9193 : }
9194 : }
9195 52 : return GCI_Undefined;
9196 : }
9197 :
9198 : /************************************************************************/
9199 : /* GDALDatasetFromArray::Create() */
9200 : /************************************************************************/
9201 :
9202 236 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
9203 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9204 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9205 :
9206 : {
9207 236 : const auto nDimCount(array->GetDimensionCount());
9208 236 : if (nDimCount == 0)
9209 : {
9210 1 : CPLError(CE_Failure, CPLE_NotSupported,
9211 : "Unsupported number of dimensions");
9212 1 : return nullptr;
9213 : }
9214 469 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9215 234 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
9216 : {
9217 1 : CPLError(CE_Failure, CPLE_NotSupported,
9218 : "Only arrays with numeric data types "
9219 : "can be exposed as classic GDALDataset");
9220 1 : return nullptr;
9221 : }
9222 234 : if (iXDim >= nDimCount ||
9223 219 : (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
9224 : {
9225 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9226 6 : return nullptr;
9227 : }
9228 228 : GUInt64 nTotalBands = 1;
9229 228 : const auto &dims(array->GetDimensions());
9230 739 : for (size_t i = 0; i < nDimCount; ++i)
9231 : {
9232 512 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9233 : {
9234 70 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9235 : {
9236 1 : CPLError(CE_Failure, CPLE_AppDefined,
9237 : "Too many bands. Operate on a sliced view");
9238 1 : return nullptr;
9239 : }
9240 69 : nTotalBands *= dims[i]->GetSize();
9241 : }
9242 : }
9243 :
9244 454 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9245 738 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9246 : {
9247 511 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9248 : {
9249 69 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9250 69 : ++j;
9251 : }
9252 : }
9253 :
9254 227 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9255 :
9256 : const char *pszBandMetadata =
9257 227 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9258 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9259 454 : nNewDimCount);
9260 227 : if (pszBandMetadata)
9261 : {
9262 21 : if (!poRootGroup)
9263 : {
9264 1 : CPLError(CE_Failure, CPLE_AppDefined,
9265 : "Root group should be provided when BAND_METADATA is set");
9266 14 : return nullptr;
9267 : }
9268 20 : CPLJSONDocument oDoc;
9269 20 : if (!oDoc.LoadMemory(pszBandMetadata))
9270 : {
9271 1 : CPLError(CE_Failure, CPLE_AppDefined,
9272 : "Invalid JSON content for BAND_METADATA");
9273 1 : return nullptr;
9274 : }
9275 19 : auto oRoot = oDoc.GetRoot();
9276 19 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9277 : {
9278 1 : CPLError(CE_Failure, CPLE_AppDefined,
9279 : "Value of BAND_METADATA should be an array");
9280 1 : return nullptr;
9281 : }
9282 :
9283 18 : auto oArray = oRoot.ToArray();
9284 26 : for (int j = 0; j < oArray.Size(); ++j)
9285 : {
9286 19 : const auto oJsonItem = oArray[j];
9287 19 : MetadataItem oItem;
9288 :
9289 38 : auto osBandArrayFullname = oJsonItem.GetString("array");
9290 19 : if (osBandArrayFullname.empty())
9291 : {
9292 1 : CPLError(CE_Failure, CPLE_AppDefined,
9293 : "BAND_METADATA[%d][\"array\"] is missing", j);
9294 1 : return nullptr;
9295 : }
9296 : oItem.poArray =
9297 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9298 18 : if (!oItem.poArray)
9299 : {
9300 1 : CPLError(CE_Failure, CPLE_AppDefined,
9301 : "Array %s cannot be found",
9302 : osBandArrayFullname.c_str());
9303 1 : return nullptr;
9304 : }
9305 17 : if (oItem.poArray->GetDimensionCount() != 1)
9306 : {
9307 1 : CPLError(CE_Failure, CPLE_AppDefined,
9308 : "Array %s is not a 1D array",
9309 : osBandArrayFullname.c_str());
9310 1 : return nullptr;
9311 : }
9312 : const auto &osAuxArrayDimName =
9313 16 : oItem.poArray->GetDimensions()[0]->GetName();
9314 16 : auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9315 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9316 : {
9317 1 : CPLError(CE_Failure, CPLE_AppDefined,
9318 : "Dimension %s of array %s is not a non-X/Y dimension "
9319 : "of array %s",
9320 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9321 1 : array->GetName().c_str());
9322 1 : return nullptr;
9323 : }
9324 15 : const size_t iExtraDimIdx = oIter->second;
9325 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9326 :
9327 15 : oItem.osName = oJsonItem.GetString("item_name");
9328 15 : if (oItem.osName.empty())
9329 : {
9330 1 : CPLError(CE_Failure, CPLE_AppDefined,
9331 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9332 1 : return nullptr;
9333 : }
9334 :
9335 28 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9336 :
9337 : // Check correctness of definition
9338 14 : bool bFirstNumericFormatter = true;
9339 14 : std::string osModDefinition;
9340 14 : bool bDefinitionUsesPctForG = false;
9341 72 : for (size_t k = 0; k < osDefinition.size(); ++k)
9342 : {
9343 64 : if (osDefinition[k] == '%')
9344 : {
9345 13 : osModDefinition += osDefinition[k];
9346 13 : if (k + 1 == osDefinition.size())
9347 : {
9348 1 : CPLError(CE_Failure, CPLE_AppDefined,
9349 : "Value of "
9350 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9351 : "%s is invalid at offset %d",
9352 : osAuxArrayDimName.c_str(), j,
9353 : osDefinition.c_str(), int(k));
9354 1 : return nullptr;
9355 : }
9356 12 : ++k;
9357 12 : if (osDefinition[k] == '%')
9358 : {
9359 1 : osModDefinition += osDefinition[k];
9360 1 : continue;
9361 : }
9362 11 : if (!bFirstNumericFormatter)
9363 : {
9364 1 : CPLError(
9365 : CE_Failure, CPLE_AppDefined,
9366 : "Value of "
9367 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
9368 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9369 : "formatters should be specified at most once",
9370 : osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
9371 : int(k));
9372 1 : return nullptr;
9373 : }
9374 10 : bFirstNumericFormatter = false;
9375 13 : for (; k < osDefinition.size(); ++k)
9376 : {
9377 13 : osModDefinition += osDefinition[k];
9378 26 : if (!((osDefinition[k] >= '0' &&
9379 12 : osDefinition[k] <= '9') ||
9380 11 : osDefinition[k] == '.'))
9381 10 : break;
9382 : }
9383 20 : if (k == osDefinition.size() ||
9384 10 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9385 5 : osDefinition[k] != 's'))
9386 : {
9387 1 : CPLError(CE_Failure, CPLE_AppDefined,
9388 : "Value of "
9389 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9390 : "%s is invalid at offset %d: only "
9391 : "%%[x][.y]f|g or %%s formatters are accepted",
9392 : osAuxArrayDimName.c_str(), j,
9393 : osDefinition.c_str(), int(k));
9394 1 : return nullptr;
9395 : }
9396 9 : bDefinitionUsesPctForG =
9397 9 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9398 9 : if (bDefinitionUsesPctForG)
9399 : {
9400 5 : if (oItem.poArray->GetDataType().GetClass() !=
9401 : GEDTC_NUMERIC)
9402 : {
9403 1 : CPLError(CE_Failure, CPLE_AppDefined,
9404 : "Data type of %s array is not numeric",
9405 : osAuxArrayDimName.c_str());
9406 1 : return nullptr;
9407 : }
9408 : }
9409 : }
9410 56 : else if (osDefinition[k] == '$' &&
9411 56 : k + 1 < osDefinition.size() &&
9412 5 : osDefinition[k + 1] == '{')
9413 : {
9414 5 : const auto nPos = osDefinition.find('}', k);
9415 5 : if (nPos == std::string::npos)
9416 : {
9417 1 : CPLError(CE_Failure, CPLE_AppDefined,
9418 : "Value of "
9419 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9420 : "%s is invalid at offset %d",
9421 : osAuxArrayDimName.c_str(), j,
9422 : osDefinition.c_str(), int(k));
9423 2 : return nullptr;
9424 : }
9425 : const auto osAttrName =
9426 4 : osDefinition.substr(k + 2, nPos - (k + 2));
9427 4 : auto poAttr = oItem.poArray->GetAttribute(osAttrName);
9428 4 : if (!poAttr)
9429 : {
9430 1 : CPLError(CE_Failure, CPLE_AppDefined,
9431 : "Value of "
9432 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9433 : "%s is invalid: %s is not an attribute of %s",
9434 : osAuxArrayDimName.c_str(), j,
9435 : osDefinition.c_str(), osAttrName.c_str(),
9436 : osAuxArrayDimName.c_str());
9437 1 : return nullptr;
9438 : }
9439 3 : k = nPos;
9440 3 : const char *pszValue = poAttr->ReadAsString();
9441 3 : if (!pszValue)
9442 : {
9443 0 : CPLError(CE_Failure, CPLE_AppDefined,
9444 : "Cannot get value of attribute %s of %s as a "
9445 : "string",
9446 : osAttrName.c_str(), osAuxArrayDimName.c_str());
9447 0 : return nullptr;
9448 : }
9449 3 : osModDefinition += pszValue;
9450 : }
9451 : else
9452 : {
9453 46 : osModDefinition += osDefinition[k];
9454 : }
9455 : }
9456 :
9457 8 : oItem.osDefinition = std::move(osModDefinition);
9458 8 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9459 :
9460 8 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9461 8 : std::move(oItem));
9462 : }
9463 : }
9464 :
9465 426 : std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9466 : const char *pszBandImageryMetadata =
9467 213 : CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9468 213 : if (pszBandImageryMetadata)
9469 : {
9470 12 : if (!poRootGroup)
9471 : {
9472 1 : CPLError(CE_Failure, CPLE_AppDefined,
9473 : "Root group should be provided when BAND_IMAGERY_METADATA "
9474 : "is set");
9475 10 : return nullptr;
9476 : }
9477 11 : CPLJSONDocument oDoc;
9478 11 : if (!oDoc.LoadMemory(pszBandImageryMetadata))
9479 : {
9480 1 : CPLError(CE_Failure, CPLE_AppDefined,
9481 : "Invalid JSON content for BAND_IMAGERY_METADATA");
9482 1 : return nullptr;
9483 : }
9484 10 : auto oRoot = oDoc.GetRoot();
9485 10 : if (oRoot.GetType() != CPLJSONObject::Type::Object)
9486 : {
9487 1 : CPLError(CE_Failure, CPLE_AppDefined,
9488 : "Value of BAND_IMAGERY_METADATA should be an object");
9489 1 : return nullptr;
9490 : }
9491 12 : for (const auto &oJsonItem : oRoot.GetChildren())
9492 : {
9493 22 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9494 12 : oJsonItem.GetName() == "FWHM_UM")
9495 : {
9496 18 : auto osBandArrayFullname = oJsonItem.GetString("array");
9497 9 : if (osBandArrayFullname.empty())
9498 : {
9499 1 : CPLError(
9500 : CE_Failure, CPLE_AppDefined,
9501 : "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
9502 2 : oJsonItem.GetName().c_str());
9503 1 : return nullptr;
9504 : }
9505 : auto poArray =
9506 8 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9507 8 : if (!poArray)
9508 : {
9509 1 : CPLError(CE_Failure, CPLE_AppDefined,
9510 : "Array %s cannot be found",
9511 : osBandArrayFullname.c_str());
9512 1 : return nullptr;
9513 : }
9514 7 : if (poArray->GetDimensionCount() != 1)
9515 : {
9516 1 : CPLError(CE_Failure, CPLE_AppDefined,
9517 : "Array %s is not a 1D array",
9518 : osBandArrayFullname.c_str());
9519 1 : return nullptr;
9520 : }
9521 : const auto &osAuxArrayDimName =
9522 6 : poArray->GetDimensions()[0]->GetName();
9523 : auto oIter =
9524 6 : oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9525 6 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9526 : {
9527 1 : CPLError(CE_Failure, CPLE_AppDefined,
9528 : "Dimension \"%s\" of array \"%s\" is not a "
9529 : "non-X/Y dimension of array \"%s\"",
9530 : osAuxArrayDimName.c_str(),
9531 : osBandArrayFullname.c_str(),
9532 1 : array->GetName().c_str());
9533 1 : return nullptr;
9534 : }
9535 5 : const size_t iExtraDimIdx = oIter->second;
9536 5 : CPLAssert(iExtraDimIdx < nNewDimCount);
9537 :
9538 10 : std::string osUnit = oJsonItem.GetString("unit", "um");
9539 5 : if (STARTS_WITH(osUnit.c_str(), "${"))
9540 : {
9541 3 : if (osUnit.back() != '}')
9542 : {
9543 2 : CPLError(CE_Failure, CPLE_AppDefined,
9544 : "Value of "
9545 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9546 : "%s is invalid",
9547 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9548 2 : return nullptr;
9549 : }
9550 2 : const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9551 2 : auto poAttr = poArray->GetAttribute(osAttrName);
9552 2 : if (!poAttr)
9553 : {
9554 2 : CPLError(CE_Failure, CPLE_AppDefined,
9555 : "Value of "
9556 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9557 : "%s is invalid: %s is not an attribute of %s",
9558 2 : oJsonItem.GetName().c_str(), osUnit.c_str(),
9559 : osAttrName.c_str(),
9560 : osBandArrayFullname.c_str());
9561 1 : return nullptr;
9562 : }
9563 1 : const char *pszValue = poAttr->ReadAsString();
9564 1 : if (!pszValue)
9565 : {
9566 0 : CPLError(CE_Failure, CPLE_AppDefined,
9567 : "Cannot get value of attribute %s of %s as a "
9568 : "string",
9569 : osAttrName.c_str(),
9570 : osBandArrayFullname.c_str());
9571 0 : return nullptr;
9572 : }
9573 1 : osUnit = pszValue;
9574 : }
9575 3 : double dfConvToUM = 1.0;
9576 7 : if (osUnit == "nm" || osUnit == "nanometre" ||
9577 9 : osUnit == "nanometres" || osUnit == "nanometer" ||
9578 2 : osUnit == "nanometers")
9579 : {
9580 1 : dfConvToUM = 1e-3;
9581 : }
9582 3 : else if (!(osUnit == "um" || osUnit == "micrometre" ||
9583 1 : osUnit == "micrometres" || osUnit == "micrometer" ||
9584 1 : osUnit == "micrometers"))
9585 : {
9586 2 : CPLError(CE_Failure, CPLE_AppDefined,
9587 : "Unhandled value for "
9588 : "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9589 2 : oJsonItem.GetName().c_str(), osUnit.c_str());
9590 1 : return nullptr;
9591 : }
9592 :
9593 2 : BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9594 2 : if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9595 : {
9596 1 : item.poCentralWavelengthArray = std::move(poArray);
9597 1 : item.dfCentralWavelengthToMicrometer = dfConvToUM;
9598 : }
9599 : else
9600 : {
9601 1 : item.poFWHMArray = std::move(poArray);
9602 1 : item.dfFWHMToMicrometer = dfConvToUM;
9603 : }
9604 : }
9605 : else
9606 : {
9607 1 : CPLError(CE_Warning, CPLE_AppDefined,
9608 : "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9609 2 : oJsonItem.GetName().c_str());
9610 : }
9611 : }
9612 : }
9613 :
9614 406 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9615 :
9616 203 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9617 :
9618 203 : poDS->nRasterYSize =
9619 203 : nDimCount < 2 ? 1
9620 191 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9621 191 : dims[iYDim]->GetSize()));
9622 406 : poDS->nRasterXSize = static_cast<int>(
9623 203 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9624 :
9625 406 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9626 406 : std::vector<GUInt64> anStackIters(nDimCount);
9627 406 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9628 642 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9629 : {
9630 439 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9631 : {
9632 45 : anMapNewToOld[j] = i;
9633 45 : j++;
9634 : }
9635 : }
9636 :
9637 406 : poDS->m_bHasGT =
9638 203 : array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9639 :
9640 406 : const auto attrs(array->GetAttributes());
9641 306 : for (const auto &attr : attrs)
9642 : {
9643 103 : if (attr->GetName() != "COLOR_INTERPRETATION")
9644 : {
9645 188 : auto stringArray = attr->ReadAsStringArray();
9646 188 : std::string val;
9647 94 : if (stringArray.size() > 1)
9648 : {
9649 38 : val += '{';
9650 : }
9651 226 : for (int i = 0; i < stringArray.size(); ++i)
9652 : {
9653 132 : if (i > 0)
9654 38 : val += ',';
9655 132 : val += stringArray[i];
9656 : }
9657 94 : if (stringArray.size() > 1)
9658 : {
9659 38 : val += '}';
9660 : }
9661 94 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9662 : }
9663 : }
9664 :
9665 203 : const char *pszDelay = CSLFetchNameValueDef(
9666 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9667 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9668 : const double dfDelay =
9669 203 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9670 203 : const auto nStartTime = time(nullptr);
9671 203 : bool bHasWarned = false;
9672 : // Instantiate bands by iterating over non-XY variables
9673 203 : size_t iDim = 0;
9674 203 : int nCurBand = 1;
9675 322 : lbl_next_depth:
9676 322 : if (iDim < nNewDimCount)
9677 : {
9678 47 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9679 47 : anOtherDimCoord[iDim] = 0;
9680 : while (true)
9681 : {
9682 119 : ++iDim;
9683 119 : goto lbl_next_depth;
9684 119 : lbl_return_to_caller:
9685 119 : --iDim;
9686 119 : --anStackIters[iDim];
9687 119 : if (anStackIters[iDim] == 0)
9688 47 : break;
9689 72 : ++anOtherDimCoord[iDim];
9690 : }
9691 : }
9692 : else
9693 : {
9694 550 : poDS->SetBand(nCurBand,
9695 : new GDALRasterBandFromArray(
9696 275 : poDS.get(), anOtherDimCoord,
9697 : aoBandParameterMetadataItems, aoBandImageryMetadata,
9698 275 : dfDelay, nStartTime, bHasWarned));
9699 275 : ++nCurBand;
9700 : }
9701 322 : if (iDim > 0)
9702 119 : goto lbl_return_to_caller;
9703 :
9704 203 : if (!array->GetFilename().empty())
9705 : {
9706 181 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9707 : std::string osDerivedDatasetName(
9708 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9709 362 : int(iYDim), array->GetFullName().c_str()));
9710 181 : if (!array->GetContext().empty())
9711 : {
9712 2 : osDerivedDatasetName += " with context ";
9713 2 : osDerivedDatasetName += array->GetContext();
9714 : }
9715 181 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9716 181 : poDS->TryLoadXML();
9717 :
9718 2 : for (const auto &[pszKey, pszValue] :
9719 : cpl::IterateNameValue(static_cast<CSLConstList>(
9720 183 : poDS->GDALPamDataset::GetMetadata())))
9721 : {
9722 1 : poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9723 : }
9724 : }
9725 :
9726 203 : return poDS.release();
9727 : }
9728 :
9729 : /************************************************************************/
9730 : /* AsClassicDataset() */
9731 : /************************************************************************/
9732 :
9733 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9734 : *
9735 : * In the case of > 2D arrays, additional dimensions will be represented as
9736 : * raster bands.
9737 : *
9738 : * The "reverse" method is GDALRasterBand::AsMDArray().
9739 : *
9740 : * This is the same as the C function GDALMDArrayAsClassicDataset().
9741 : *
9742 : * @param iXDim Index of the dimension that will be used as the X/width axis.
9743 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
9744 : * Ignored if the dimension count is 1.
9745 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9746 : * and BAND_IMAGERY_METADATA option.
9747 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9748 : * nullptr. Current supported options are:
9749 : * <ul>
9750 : * <li>BAND_METADATA: JSON serialized array defining which
9751 : * arrays of the poRootGroup, indexed by non-X and Y
9752 : * dimensions, should be mapped as band metadata items.
9753 : * Each array item should be an object with the
9754 : * following members:
9755 : * - "array": full name of a band parameter array.
9756 : * Such array must be a one
9757 : * dimensional array, and its dimension must be one of
9758 : * the dimensions of the array on which the method is
9759 : * called (excluding the X and Y dimensons).
9760 : * - "item_name": band metadata item name
9761 : * - "item_value": (optional) String, where "%[x][.y]f",
9762 : * "%[x][.y]g" or "%s" printf-like formatting can be
9763 : * used to format the corresponding value of the
9764 : * parameter array. The percentage character should be
9765 : * repeated: "%%"
9766 : * "${attribute_name}" can also be used to include the
9767 : * value of an attribute for the array.
9768 : * If "item_value" is not provided, a default formatting
9769 : * of the value will be applied.
9770 : *
9771 : * Example:
9772 : * [
9773 : * {
9774 : * "array": "/sensor_band_parameters/wavelengths",
9775 : * "item_name": "WAVELENGTH",
9776 : * "item_value": "%.1f ${units}"
9777 : * },
9778 : * {
9779 : * "array": "/sensor_band_parameters/fwhm",
9780 : * "item_name": "FWHM"
9781 : * },
9782 : * {
9783 : * "array": "/sensor_band_parameters/fwhm",
9784 : * "item_name": "FWHM_UNIT",
9785 : * "item_value": "${units}"
9786 : * }
9787 : * ]
9788 : * </li>
9789 : * <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
9790 : * JSON serialized object defining which arrays of the
9791 : * poRootGroup, indexed by non-X and Y dimensions,
9792 : * should be mapped as band metadata items in the
9793 : * band IMAGERY domain.
9794 : * The object currently accepts 2 members:
9795 : * - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
9796 : * micrometers.
9797 : * - "FWHM_UM": Full-width half-maximum
9798 : * in micrometers.
9799 : * The value of each member should be an object with the
9800 : * following members:
9801 : * - "array": (required) full name of a band parameter
9802 : * array.
9803 : * Such array must be a one dimensional array, and its
9804 : * dimension must be one of the dimensions of the
9805 : * array on which the method is called
9806 : * (excluding the X and Y dimensons).
9807 : * - "unit": (optional) unit of the values pointed in
9808 : * the above array.
9809 : * Can be a literal string or a string of the form
9810 : * "${attribute_name}" to point to an attribute for
9811 : * the array.
9812 : * Accepted values are "um", "micrometer"
9813 : * (with UK vs US spelling, singular or plural), "nm",
9814 : * "nanometer" (with UK vs US spelling, singular or
9815 : * plural)
9816 : * If not provided, micrometer is assumed.
9817 : *
9818 : * Example for EMIT datasets:
9819 : * {
9820 : * "CENTRAL_WAVELENGTH_UM": {
9821 : * "array": "/sensor_band_parameters/wavelengths",
9822 : * "unit": "${units}"
9823 : * },
9824 : * "FWHM_UM": {
9825 : * "array": "/sensor_band_parameters/fwhm",
9826 : * "unit": "${units}"
9827 : * }
9828 : * }
9829 : * </li>
9830 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
9831 : * seconds allowed to set the DIM_{dimname}_VALUE band
9832 : * metadata items from the indexing variable of the
9833 : * dimensions.
9834 : * Default value is 5. 'unlimited' can be used to mean
9835 : * unlimited delay. Can also be defined globally with
9836 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
9837 : * option.</li>
9838 : * </ul>
9839 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9840 : */
9841 : GDALDataset *
9842 236 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
9843 : const std::shared_ptr<GDALGroup> &poRootGroup,
9844 : CSLConstList papszOptions) const
9845 : {
9846 472 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
9847 236 : if (!self)
9848 : {
9849 0 : CPLError(CE_Failure, CPLE_AppDefined,
9850 : "Driver implementation issue: m_pSelf not set !");
9851 0 : return nullptr;
9852 : }
9853 236 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
9854 236 : papszOptions);
9855 : }
9856 :
9857 : /************************************************************************/
9858 : /* GetStatistics() */
9859 : /************************************************************************/
9860 :
9861 : /**
9862 : * \brief Fetch statistics.
9863 : *
9864 : * Returns the minimum, maximum, mean and standard deviation of all
9865 : * pixel values in this array.
9866 : *
9867 : * If bForce is FALSE results will only be returned if it can be done
9868 : * quickly (i.e. without scanning the data). If bForce is FALSE and
9869 : * results cannot be returned efficiently, the method will return CE_Warning
9870 : * but no warning will have been issued. This is a non-standard use of
9871 : * the CE_Warning return value to indicate "nothing done".
9872 : *
9873 : * When cached statistics are not available, and bForce is TRUE,
9874 : * ComputeStatistics() is called.
9875 : *
9876 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
9877 : * will generally cache statistics in the .aux.xml file allowing fast fetch
9878 : * after the first request.
9879 : *
9880 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9881 : *
9882 : * This method is the same as the C function GDALMDArrayGetStatistics().
9883 : *
9884 : * @param bApproxOK Currently ignored. In the future, should be set to true
9885 : * if statistics on the whole array are wished, or to false if a subset of it
9886 : * may be used.
9887 : *
9888 : * @param bForce If false statistics will only be returned if it can
9889 : * be done without rescanning the image.
9890 : *
9891 : * @param pdfMin Location into which to load image minimum (may be NULL).
9892 : *
9893 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9894 : *
9895 : * @param pdfMean Location into which to load image mean (may be NULL).
9896 : *
9897 : * @param pdfStdDev Location into which to load image standard deviation
9898 : * (may be NULL).
9899 : *
9900 : * @param pnValidCount Number of samples whose value is different from the
9901 : * nodata value. (may be NULL)
9902 : *
9903 : * @param pfnProgress a function to call to report progress, or NULL.
9904 : *
9905 : * @param pProgressData application data to pass to the progress function.
9906 : *
9907 : * @return CE_None on success, CE_Warning if no values returned,
9908 : * CE_Failure if an error occurs.
9909 : *
9910 : * @since GDAL 3.2
9911 : */
9912 :
9913 10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
9914 : double *pdfMax, double *pdfMean,
9915 : double *pdfStdDev, GUInt64 *pnValidCount,
9916 : GDALProgressFunc pfnProgress,
9917 : void *pProgressData)
9918 : {
9919 10 : if (!bForce)
9920 1 : return CE_Warning;
9921 :
9922 18 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
9923 9 : pnValidCount, pfnProgress, pProgressData, nullptr)
9924 9 : ? CE_None
9925 9 : : CE_Failure;
9926 : }
9927 :
9928 : /************************************************************************/
9929 : /* ComputeStatistics() */
9930 : /************************************************************************/
9931 :
9932 : /**
9933 : * \brief Compute statistics.
9934 : *
9935 : * Returns the minimum, maximum, mean and standard deviation of all
9936 : * pixel values in this array.
9937 : *
9938 : * Pixels taken into account in statistics are those whose mask value
9939 : * (as determined by GetMask()) is non-zero.
9940 : *
9941 : * Once computed, the statistics will generally be "set" back on the
9942 : * owing dataset.
9943 : *
9944 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9945 : *
9946 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
9947 : * and GDALMDArrayComputeStatisticsEx().
9948 : *
9949 : * @param bApproxOK Currently ignored. In the future, should be set to true
9950 : * if statistics on the whole array are wished, or to false if a subset of it
9951 : * may be used.
9952 : *
9953 : * @param pdfMin Location into which to load image minimum (may be NULL).
9954 : *
9955 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9956 : *
9957 : * @param pdfMean Location into which to load image mean (may be NULL).
9958 : *
9959 : * @param pdfStdDev Location into which to load image standard deviation
9960 : * (may be NULL).
9961 : *
9962 : * @param pnValidCount Number of samples whose value is different from the
9963 : * nodata value. (may be NULL)
9964 : *
9965 : * @param pfnProgress a function to call to report progress, or NULL.
9966 : *
9967 : * @param pProgressData application data to pass to the progress function.
9968 : *
9969 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
9970 : * Options are driver specific. For now the netCDF and Zarr
9971 : * drivers recognize UPDATE_METADATA=YES, whose effect is
9972 : * to add or update the actual_range attribute with the
9973 : * computed min/max, only if done on the full array, in non
9974 : * approximate mode, and the dataset is opened in update
9975 : * mode.
9976 : *
9977 : * @return true on success
9978 : *
9979 : * @since GDAL 3.2
9980 : */
9981 :
9982 13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
9983 : double *pdfMax, double *pdfMean,
9984 : double *pdfStdDev, GUInt64 *pnValidCount,
9985 : GDALProgressFunc pfnProgress,
9986 : void *pProgressData,
9987 : CSLConstList papszOptions)
9988 : {
9989 : struct StatsPerChunkType
9990 : {
9991 : const GDALMDArray *array = nullptr;
9992 : std::shared_ptr<GDALMDArray> poMask{};
9993 : double dfMin = cpl::NumericLimits<double>::max();
9994 : double dfMax = -cpl::NumericLimits<double>::max();
9995 : double dfMean = 0.0;
9996 : double dfM2 = 0.0;
9997 : GUInt64 nValidCount = 0;
9998 : std::vector<GByte> abyData{};
9999 : std::vector<double> adfData{};
10000 : std::vector<GByte> abyMaskData{};
10001 : GDALProgressFunc pfnProgress = nullptr;
10002 : void *pProgressData = nullptr;
10003 : };
10004 :
10005 13 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
10006 : const GUInt64 *chunkArrayStartIdx,
10007 : const size_t *chunkCount, GUInt64 iCurChunk,
10008 : GUInt64 nChunkCount, void *pUserData)
10009 : {
10010 13 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10011 13 : const GDALMDArray *array = data->array;
10012 13 : const GDALMDArray *poMask = data->poMask.get();
10013 13 : const size_t nDims = array->GetDimensionCount();
10014 13 : size_t nVals = 1;
10015 34 : for (size_t i = 0; i < nDims; i++)
10016 21 : nVals *= chunkCount[i];
10017 :
10018 : // Get mask
10019 13 : data->abyMaskData.resize(nVals);
10020 13 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10021 13 : poMask->GetDataType(), &data->abyMaskData[0])))
10022 : {
10023 0 : return false;
10024 : }
10025 :
10026 : // Get data
10027 13 : const auto &oType = array->GetDataType();
10028 13 : if (oType.GetNumericDataType() == GDT_Float64)
10029 : {
10030 6 : data->adfData.resize(nVals);
10031 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10032 6 : oType, &data->adfData[0]))
10033 : {
10034 0 : return false;
10035 : }
10036 : }
10037 : else
10038 : {
10039 7 : data->abyData.resize(nVals * oType.GetSize());
10040 7 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10041 7 : oType, &data->abyData[0]))
10042 : {
10043 0 : return false;
10044 : }
10045 7 : data->adfData.resize(nVals);
10046 7 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10047 7 : static_cast<int>(oType.GetSize()),
10048 7 : &data->adfData[0], GDT_Float64,
10049 : static_cast<int>(sizeof(double)),
10050 : static_cast<GPtrDiff_t>(nVals));
10051 : }
10052 912 : for (size_t i = 0; i < nVals; i++)
10053 : {
10054 899 : if (data->abyMaskData[i])
10055 : {
10056 894 : const double dfValue = data->adfData[i];
10057 894 : data->dfMin = std::min(data->dfMin, dfValue);
10058 894 : data->dfMax = std::max(data->dfMax, dfValue);
10059 894 : data->nValidCount++;
10060 894 : const double dfDelta = dfValue - data->dfMean;
10061 894 : data->dfMean += dfDelta / data->nValidCount;
10062 894 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
10063 : }
10064 : }
10065 13 : if (data->pfnProgress &&
10066 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10067 : "", data->pProgressData))
10068 : {
10069 0 : return false;
10070 : }
10071 13 : return true;
10072 : };
10073 :
10074 13 : const auto &oType = GetDataType();
10075 26 : if (oType.GetClass() != GEDTC_NUMERIC ||
10076 13 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
10077 : {
10078 0 : CPLError(
10079 : CE_Failure, CPLE_NotSupported,
10080 : "Statistics can only be computed on non-complex numeric data type");
10081 0 : return false;
10082 : }
10083 :
10084 13 : const size_t nDims = GetDimensionCount();
10085 26 : std::vector<GUInt64> arrayStartIdx(nDims);
10086 26 : std::vector<GUInt64> count(nDims);
10087 13 : const auto &poDims = GetDimensions();
10088 34 : for (size_t i = 0; i < nDims; i++)
10089 : {
10090 21 : count[i] = poDims[i]->GetSize();
10091 : }
10092 13 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10093 : const size_t nMaxChunkSize =
10094 : pszSwathSize
10095 13 : ? static_cast<size_t>(
10096 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10097 0 : CPLAtoGIntBig(pszSwathSize)))
10098 : : static_cast<size_t>(
10099 13 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10100 13 : GDALGetCacheMax64() / 4));
10101 26 : StatsPerChunkType sData;
10102 13 : sData.array = this;
10103 13 : sData.poMask = GetMask(nullptr);
10104 13 : if (sData.poMask == nullptr)
10105 : {
10106 0 : return false;
10107 : }
10108 13 : sData.pfnProgress = pfnProgress;
10109 13 : sData.pProgressData = pProgressData;
10110 13 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10111 26 : GetProcessingChunkSize(nMaxChunkSize).data(),
10112 13 : PerChunkFunc, &sData))
10113 : {
10114 0 : return false;
10115 : }
10116 :
10117 13 : if (pdfMin)
10118 13 : *pdfMin = sData.dfMin;
10119 :
10120 13 : if (pdfMax)
10121 13 : *pdfMax = sData.dfMax;
10122 :
10123 13 : if (pdfMean)
10124 11 : *pdfMean = sData.dfMean;
10125 :
10126 : const double dfStdDev =
10127 13 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10128 13 : if (pdfStdDev)
10129 11 : *pdfStdDev = dfStdDev;
10130 :
10131 13 : if (pnValidCount)
10132 11 : *pnValidCount = sData.nValidCount;
10133 :
10134 13 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10135 13 : sData.nValidCount, papszOptions);
10136 :
10137 13 : return true;
10138 : }
10139 :
10140 : /************************************************************************/
10141 : /* SetStatistics() */
10142 : /************************************************************************/
10143 : //! @cond Doxygen_Suppress
10144 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10145 : double /* dfMax */, double /* dfMean */,
10146 : double /* dfStdDev */,
10147 : GUInt64 /* nValidCount */,
10148 : CSLConstList /* papszOptions */)
10149 : {
10150 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10151 5 : return false;
10152 : }
10153 :
10154 : //! @endcond
10155 :
10156 : /************************************************************************/
10157 : /* ClearStatistics() */
10158 : /************************************************************************/
10159 :
10160 : /**
10161 : * \brief Clear statistics.
10162 : *
10163 : * @since GDAL 3.4
10164 : */
10165 0 : void GDALMDArray::ClearStatistics()
10166 : {
10167 0 : }
10168 :
10169 : /************************************************************************/
10170 : /* GetCoordinateVariables() */
10171 : /************************************************************************/
10172 :
10173 : /**
10174 : * \brief Return coordinate variables.
10175 : *
10176 : * Coordinate variables are an alternate way of indexing an array that can
10177 : * be sometimes used. For example, an array collected through remote sensing
10178 : * might be indexed by (scanline, pixel). But there can be
10179 : * a longitude and latitude arrays alongside that are also both indexed by
10180 : * (scanline, pixel), and are referenced from operational arrays for
10181 : * reprojection purposes.
10182 : *
10183 : * For netCDF, this will return the arrays referenced by the "coordinates"
10184 : * attribute.
10185 : *
10186 : * This method is the same as the C function
10187 : * GDALMDArrayGetCoordinateVariables().
10188 : *
10189 : * @return a vector of arrays
10190 : *
10191 : * @since GDAL 3.4
10192 : */
10193 :
10194 : std::vector<std::shared_ptr<GDALMDArray>>
10195 13 : GDALMDArray::GetCoordinateVariables() const
10196 : {
10197 13 : return {};
10198 : }
10199 :
10200 : /************************************************************************/
10201 : /* ~GDALExtendedDataType() */
10202 : /************************************************************************/
10203 :
10204 : GDALExtendedDataType::~GDALExtendedDataType() = default;
10205 :
10206 : /************************************************************************/
10207 : /* GDALExtendedDataType() */
10208 : /************************************************************************/
10209 :
10210 9520 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10211 9520 : GDALExtendedDataTypeSubType eSubType)
10212 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10213 9520 : m_nMaxStringLength(nMaxStringLength)
10214 : {
10215 9520 : }
10216 :
10217 : /************************************************************************/
10218 : /* GDALExtendedDataType() */
10219 : /************************************************************************/
10220 :
10221 37220 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10222 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10223 37220 : m_nSize(GDALGetDataTypeSizeBytes(eType))
10224 : {
10225 37220 : }
10226 :
10227 : /************************************************************************/
10228 : /* GDALExtendedDataType() */
10229 : /************************************************************************/
10230 :
10231 63 : GDALExtendedDataType::GDALExtendedDataType(
10232 : const std::string &osName, GDALDataType eBaseType,
10233 63 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10234 : : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10235 63 : m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10236 : {
10237 63 : }
10238 :
10239 : /************************************************************************/
10240 : /* GDALExtendedDataType() */
10241 : /************************************************************************/
10242 :
10243 849 : GDALExtendedDataType::GDALExtendedDataType(
10244 : const std::string &osName, size_t nTotalSize,
10245 849 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10246 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10247 849 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10248 : {
10249 849 : }
10250 :
10251 : /************************************************************************/
10252 : /* GDALExtendedDataType() */
10253 : /************************************************************************/
10254 :
10255 : /** Copy constructor. */
10256 17277 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10257 34554 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
10258 17277 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10259 17277 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10260 17277 : m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10261 : {
10262 17277 : if (m_eClass == GEDTC_COMPOUND)
10263 : {
10264 521 : for (const auto &elt : other.m_aoComponents)
10265 : {
10266 341 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10267 : }
10268 : }
10269 17277 : }
10270 :
10271 : /************************************************************************/
10272 : /* operator= () */
10273 : /************************************************************************/
10274 :
10275 : /** Copy assignment. */
10276 : GDALExtendedDataType &
10277 1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10278 : {
10279 1045 : if (this != &other)
10280 : {
10281 1045 : m_osName = other.m_osName;
10282 1045 : m_eClass = other.m_eClass;
10283 1045 : m_eSubType = other.m_eSubType;
10284 1045 : m_eNumericDT = other.m_eNumericDT;
10285 1045 : m_nSize = other.m_nSize;
10286 1045 : m_nMaxStringLength = other.m_nMaxStringLength;
10287 1045 : m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10288 1045 : m_aoComponents.clear();
10289 1045 : if (m_eClass == GEDTC_COMPOUND)
10290 : {
10291 0 : for (const auto &elt : other.m_aoComponents)
10292 : {
10293 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10294 : }
10295 : }
10296 : }
10297 1045 : return *this;
10298 : }
10299 :
10300 : /************************************************************************/
10301 : /* operator= () */
10302 : /************************************************************************/
10303 :
10304 : /** Move assignment. */
10305 : GDALExtendedDataType &
10306 15239 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10307 : {
10308 15239 : m_osName = std::move(other.m_osName);
10309 15239 : m_eClass = other.m_eClass;
10310 15239 : m_eSubType = other.m_eSubType;
10311 15239 : m_eNumericDT = other.m_eNumericDT;
10312 15239 : m_nSize = other.m_nSize;
10313 15239 : m_nMaxStringLength = other.m_nMaxStringLength;
10314 15239 : m_aoComponents = std::move(other.m_aoComponents);
10315 15239 : m_poRAT = std::move(other.m_poRAT);
10316 15239 : other.m_eClass = GEDTC_NUMERIC;
10317 15239 : other.m_eNumericDT = GDT_Unknown;
10318 15239 : other.m_nSize = 0;
10319 15239 : other.m_nMaxStringLength = 0;
10320 15239 : return *this;
10321 : }
10322 :
10323 : /************************************************************************/
10324 : /* Create() */
10325 : /************************************************************************/
10326 :
10327 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10328 : *
10329 : * This is the same as the C function GDALExtendedDataTypeCreate()
10330 : *
10331 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10332 : * GDT_TypeCount
10333 : */
10334 37213 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10335 : {
10336 37213 : return GDALExtendedDataType(eType);
10337 : }
10338 :
10339 : /************************************************************************/
10340 : /* Create() */
10341 : /************************************************************************/
10342 :
10343 : /** Return a new GDALExtendedDataType from a raster attribute table.
10344 : *
10345 : * @param osName Type name
10346 : * @param eBaseType Base integer data type.
10347 : * @param poRAT Raster attribute table. Must not be NULL.
10348 : * @since 3.12
10349 : */
10350 : GDALExtendedDataType
10351 63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10352 : std::unique_ptr<GDALRasterAttributeTable> poRAT)
10353 : {
10354 63 : return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10355 : }
10356 :
10357 : /************************************************************************/
10358 : /* Create() */
10359 : /************************************************************************/
10360 :
10361 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10362 : *
10363 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10364 : *
10365 : * @param osName Type name.
10366 : * @param nTotalSize Total size of the type in bytes.
10367 : * Should be large enough to store all components.
10368 : * @param components Components of the compound type.
10369 : */
10370 856 : GDALExtendedDataType GDALExtendedDataType::Create(
10371 : const std::string &osName, size_t nTotalSize,
10372 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10373 : {
10374 856 : size_t nLastOffset = 0;
10375 : // Some arbitrary threshold to avoid potential integer overflows
10376 856 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10377 : {
10378 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10379 2 : return GDALExtendedDataType(GDT_Unknown);
10380 : }
10381 4076 : for (const auto &comp : components)
10382 : {
10383 : // Check alignment too ?
10384 3223 : if (comp->GetOffset() < nLastOffset)
10385 : {
10386 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10387 1 : return GDALExtendedDataType(GDT_Unknown);
10388 : }
10389 3222 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10390 : }
10391 853 : if (nTotalSize < nLastOffset)
10392 : {
10393 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10394 1 : return GDALExtendedDataType(GDT_Unknown);
10395 : }
10396 852 : if (nTotalSize == 0 || components.empty())
10397 : {
10398 3 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10399 3 : return GDALExtendedDataType(GDT_Unknown);
10400 : }
10401 849 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10402 : }
10403 :
10404 : /************************************************************************/
10405 : /* Create() */
10406 : /************************************************************************/
10407 :
10408 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10409 : *
10410 : * This is the same as the C function GDALExtendedDataTypeCreateString().
10411 : *
10412 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
10413 : * unknown/unlimited
10414 : * @param eSubType Subtype.
10415 : */
10416 : GDALExtendedDataType
10417 9520 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10418 : GDALExtendedDataTypeSubType eSubType)
10419 : {
10420 9520 : return GDALExtendedDataType(nMaxStringLength, eSubType);
10421 : }
10422 :
10423 : /************************************************************************/
10424 : /* operator==() */
10425 : /************************************************************************/
10426 :
10427 : /** Equality operator.
10428 : *
10429 : * This is the same as the C function GDALExtendedDataTypeEquals().
10430 : */
10431 2714 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10432 : {
10433 2687 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10434 5401 : m_nSize != other.m_nSize || m_osName != other.m_osName)
10435 : {
10436 254 : return false;
10437 : }
10438 2460 : if (m_eClass == GEDTC_NUMERIC)
10439 : {
10440 873 : return m_eNumericDT == other.m_eNumericDT;
10441 : }
10442 1587 : if (m_eClass == GEDTC_STRING)
10443 : {
10444 1369 : return true;
10445 : }
10446 218 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10447 218 : if (m_aoComponents.size() != other.m_aoComponents.size())
10448 : {
10449 2 : return false;
10450 : }
10451 1139 : for (size_t i = 0; i < m_aoComponents.size(); i++)
10452 : {
10453 923 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10454 : {
10455 0 : return false;
10456 : }
10457 : }
10458 216 : return true;
10459 : }
10460 :
10461 : /************************************************************************/
10462 : /* CanConvertTo() */
10463 : /************************************************************************/
10464 :
10465 : /** Return whether this data type can be converted to the other one.
10466 : *
10467 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10468 : *
10469 : * @param other Target data type for the conversion being considered.
10470 : */
10471 8489 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10472 : {
10473 8489 : if (m_eClass == GEDTC_NUMERIC)
10474 : {
10475 5926 : if (m_eNumericDT == GDT_Unknown)
10476 0 : return false;
10477 5926 : if (other.m_eClass == GEDTC_NUMERIC &&
10478 5823 : other.m_eNumericDT == GDT_Unknown)
10479 0 : return false;
10480 6029 : return other.m_eClass == GEDTC_NUMERIC ||
10481 6029 : other.m_eClass == GEDTC_STRING;
10482 : }
10483 2563 : if (m_eClass == GEDTC_STRING)
10484 : {
10485 2385 : return other.m_eClass == m_eClass;
10486 : }
10487 178 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10488 178 : if (other.m_eClass != GEDTC_COMPOUND)
10489 0 : return false;
10490 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10491 356 : srcComponents;
10492 905 : for (const auto &srcComp : m_aoComponents)
10493 : {
10494 727 : srcComponents[srcComp->GetName()] = &srcComp;
10495 : }
10496 497 : for (const auto &dstComp : other.m_aoComponents)
10497 : {
10498 320 : auto oIter = srcComponents.find(dstComp->GetName());
10499 320 : if (oIter == srcComponents.end())
10500 1 : return false;
10501 319 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10502 0 : return false;
10503 : }
10504 177 : return true;
10505 : }
10506 :
10507 : /************************************************************************/
10508 : /* NeedsFreeDynamicMemory() */
10509 : /************************************************************************/
10510 :
10511 : /** Return whether the data type holds dynamically allocated memory, that
10512 : * needs to be freed with FreeDynamicMemory().
10513 : *
10514 : */
10515 3726 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10516 : {
10517 3726 : switch (m_eClass)
10518 : {
10519 934 : case GEDTC_STRING:
10520 934 : return true;
10521 :
10522 2682 : case GEDTC_NUMERIC:
10523 2682 : return false;
10524 :
10525 110 : case GEDTC_COMPOUND:
10526 : {
10527 223 : for (const auto &comp : m_aoComponents)
10528 : {
10529 209 : if (comp->GetType().NeedsFreeDynamicMemory())
10530 96 : return true;
10531 : }
10532 : }
10533 : }
10534 14 : return false;
10535 : }
10536 :
10537 : /************************************************************************/
10538 : /* FreeDynamicMemory() */
10539 : /************************************************************************/
10540 :
10541 : /** Release the dynamic memory (strings typically) from a raw value.
10542 : *
10543 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10544 : *
10545 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10546 : */
10547 3859 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10548 : {
10549 3859 : switch (m_eClass)
10550 : {
10551 2784 : case GEDTC_STRING:
10552 : {
10553 : char *pszStr;
10554 2784 : memcpy(&pszStr, pBuffer, sizeof(char *));
10555 2784 : if (pszStr)
10556 : {
10557 2241 : VSIFree(pszStr);
10558 : }
10559 2784 : break;
10560 : }
10561 :
10562 899 : case GEDTC_NUMERIC:
10563 : {
10564 899 : break;
10565 : }
10566 :
10567 176 : case GEDTC_COMPOUND:
10568 : {
10569 176 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10570 938 : for (const auto &comp : m_aoComponents)
10571 : {
10572 1524 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10573 762 : comp->GetOffset());
10574 : }
10575 176 : break;
10576 : }
10577 : }
10578 3859 : }
10579 :
10580 : /************************************************************************/
10581 : /* ~GDALEDTComponent() */
10582 : /************************************************************************/
10583 :
10584 : GDALEDTComponent::~GDALEDTComponent() = default;
10585 :
10586 : /************************************************************************/
10587 : /* GDALEDTComponent() */
10588 : /************************************************************************/
10589 :
10590 : /** constructor of a GDALEDTComponent
10591 : *
10592 : * This is the same as the C function GDALEDTComponendCreate()
10593 : *
10594 : * @param name Component name
10595 : * @param offset Offset in byte of the component in the compound data type.
10596 : * In case of nesting of compound data type, this should be
10597 : * the offset to the immediate belonging data type, not to the
10598 : * higher level one.
10599 : * @param type Component data type.
10600 : */
10601 3214 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10602 3214 : const GDALExtendedDataType &type)
10603 3214 : : m_osName(name), m_nOffset(offset), m_oType(type)
10604 : {
10605 3214 : }
10606 :
10607 : /************************************************************************/
10608 : /* GDALEDTComponent() */
10609 : /************************************************************************/
10610 :
10611 : /** Copy constructor. */
10612 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10613 :
10614 : /************************************************************************/
10615 : /* operator==() */
10616 : /************************************************************************/
10617 :
10618 : /** Equality operator.
10619 : */
10620 923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10621 : {
10622 1846 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10623 1846 : m_oType == other.m_oType;
10624 : }
10625 :
10626 : /************************************************************************/
10627 : /* ~GDALDimension() */
10628 : /************************************************************************/
10629 :
10630 : GDALDimension::~GDALDimension() = default;
10631 :
10632 : /************************************************************************/
10633 : /* GDALDimension() */
10634 : /************************************************************************/
10635 :
10636 : //! @cond Doxygen_Suppress
10637 : /** Constructor.
10638 : *
10639 : * @param osParentName Parent name
10640 : * @param osName name
10641 : * @param osType type. See GetType().
10642 : * @param osDirection direction. See GetDirection().
10643 : * @param nSize size.
10644 : */
10645 8486 : GDALDimension::GDALDimension(const std::string &osParentName,
10646 : const std::string &osName,
10647 : const std::string &osType,
10648 8486 : const std::string &osDirection, GUInt64 nSize)
10649 : : m_osName(osName),
10650 : m_osFullName(
10651 8486 : !osParentName.empty()
10652 12503 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10653 : : osName),
10654 29475 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10655 : {
10656 8486 : }
10657 :
10658 : //! @endcond
10659 :
10660 : /************************************************************************/
10661 : /* GetIndexingVariable() */
10662 : /************************************************************************/
10663 :
10664 : /** Return the variable that is used to index the dimension (if there is one).
10665 : *
10666 : * This is the array, typically one-dimensional, describing the values taken
10667 : * by the dimension.
10668 : */
10669 73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10670 : {
10671 73 : return nullptr;
10672 : }
10673 :
10674 : /************************************************************************/
10675 : /* SetIndexingVariable() */
10676 : /************************************************************************/
10677 :
10678 : /** Set the variable that is used to index the dimension.
10679 : *
10680 : * This is the array, typically one-dimensional, describing the values taken
10681 : * by the dimension.
10682 : *
10683 : * Optionally implemented by drivers.
10684 : *
10685 : * Drivers known to implement it: MEM.
10686 : *
10687 : * @param poArray Variable to use to index the dimension.
10688 : * @return true in case of success.
10689 : */
10690 3 : bool GDALDimension::SetIndexingVariable(
10691 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10692 : {
10693 3 : CPLError(CE_Failure, CPLE_NotSupported,
10694 : "SetIndexingVariable() not implemented");
10695 3 : return false;
10696 : }
10697 :
10698 : /************************************************************************/
10699 : /* Rename() */
10700 : /************************************************************************/
10701 :
10702 : /** Rename the dimension.
10703 : *
10704 : * This is not implemented by all drivers.
10705 : *
10706 : * Drivers known to implement it: MEM, netCDF, ZARR.
10707 : *
10708 : * This is the same as the C function GDALDimensionRename().
10709 : *
10710 : * @param osNewName New name.
10711 : *
10712 : * @return true in case of success
10713 : * @since GDAL 3.8
10714 : */
10715 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10716 : {
10717 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10718 0 : return false;
10719 : }
10720 :
10721 : /************************************************************************/
10722 : /* BaseRename() */
10723 : /************************************************************************/
10724 :
10725 : //! @cond Doxygen_Suppress
10726 8 : void GDALDimension::BaseRename(const std::string &osNewName)
10727 : {
10728 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
10729 8 : m_osFullName += osNewName;
10730 8 : m_osName = osNewName;
10731 8 : }
10732 :
10733 : //! @endcond
10734 :
10735 : //! @cond Doxygen_Suppress
10736 : /************************************************************************/
10737 : /* ParentRenamed() */
10738 : /************************************************************************/
10739 :
10740 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
10741 : {
10742 8 : m_osFullName = osNewParentFullName;
10743 8 : m_osFullName += "/";
10744 8 : m_osFullName += m_osName;
10745 8 : }
10746 :
10747 : //! @endcond
10748 :
10749 : //! @cond Doxygen_Suppress
10750 : /************************************************************************/
10751 : /* ParentDeleted() */
10752 : /************************************************************************/
10753 :
10754 4 : void GDALDimension::ParentDeleted()
10755 : {
10756 4 : }
10757 :
10758 : //! @endcond
10759 :
10760 : /************************************************************************/
10761 : /************************************************************************/
10762 : /************************************************************************/
10763 : /* C API */
10764 : /************************************************************************/
10765 : /************************************************************************/
10766 : /************************************************************************/
10767 :
10768 : /************************************************************************/
10769 : /* GDALExtendedDataTypeCreate() */
10770 : /************************************************************************/
10771 :
10772 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10773 : *
10774 : * This is the same as the C++ method GDALExtendedDataType::Create()
10775 : *
10776 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10777 : *
10778 : * @param eType Numeric data type. Must be different from GDT_Unknown and
10779 : * GDT_TypeCount
10780 : *
10781 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10782 : */
10783 1996 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
10784 : {
10785 1996 : if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
10786 : {
10787 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10788 : "Illegal GDT_Unknown/GDT_TypeCount argument");
10789 0 : return nullptr;
10790 : }
10791 : return new GDALExtendedDataTypeHS(
10792 1996 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
10793 : }
10794 :
10795 : /************************************************************************/
10796 : /* GDALExtendedDataTypeCreateString() */
10797 : /************************************************************************/
10798 :
10799 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10800 : *
10801 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10802 : *
10803 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10804 : *
10805 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10806 : */
10807 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
10808 : {
10809 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10810 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
10811 : }
10812 :
10813 : /************************************************************************/
10814 : /* GDALExtendedDataTypeCreateStringEx() */
10815 : /************************************************************************/
10816 :
10817 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10818 : *
10819 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10820 : *
10821 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10822 : *
10823 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10824 : * @since GDAL 3.4
10825 : */
10826 : GDALExtendedDataTypeH
10827 188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
10828 : GDALExtendedDataTypeSubType eSubType)
10829 : {
10830 188 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10831 188 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
10832 : }
10833 :
10834 : /************************************************************************/
10835 : /* GDALExtendedDataTypeCreateCompound() */
10836 : /************************************************************************/
10837 :
10838 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10839 : *
10840 : * This is the same as the C++ method GDALExtendedDataType::Create(const
10841 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
10842 : *
10843 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10844 : *
10845 : * @param pszName Type name.
10846 : * @param nTotalSize Total size of the type in bytes.
10847 : * Should be large enough to store all components.
10848 : * @param nComponents Number of components in comps array.
10849 : * @param comps Components.
10850 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10851 : */
10852 : GDALExtendedDataTypeH
10853 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
10854 : size_t nComponents,
10855 : const GDALEDTComponentH *comps)
10856 : {
10857 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
10858 54 : for (size_t i = 0; i < nComponents; i++)
10859 : {
10860 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
10861 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
10862 : }
10863 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
10864 66 : std::move(compsCpp));
10865 22 : if (dt.GetClass() != GEDTC_COMPOUND)
10866 6 : return nullptr;
10867 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
10868 : }
10869 :
10870 : /************************************************************************/
10871 : /* GDALExtendedDataTypeRelease() */
10872 : /************************************************************************/
10873 :
10874 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
10875 : *
10876 : * Note: when applied on a object coming from a driver, this does not
10877 : * destroy the object in the file, database, etc...
10878 : */
10879 6604 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
10880 : {
10881 6604 : delete hEDT;
10882 6604 : }
10883 :
10884 : /************************************************************************/
10885 : /* GDALExtendedDataTypeGetName() */
10886 : /************************************************************************/
10887 :
10888 : /** Return type name.
10889 : *
10890 : * This is the same as the C++ method GDALExtendedDataType::GetName()
10891 : */
10892 8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
10893 : {
10894 8 : VALIDATE_POINTER1(hEDT, __func__, "");
10895 8 : return hEDT->m_poImpl->GetName().c_str();
10896 : }
10897 :
10898 : /************************************************************************/
10899 : /* GDALExtendedDataTypeGetClass() */
10900 : /************************************************************************/
10901 :
10902 : /** Return type class.
10903 : *
10904 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
10905 : */
10906 : GDALExtendedDataTypeClass
10907 9175 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
10908 : {
10909 9175 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
10910 9175 : return hEDT->m_poImpl->GetClass();
10911 : }
10912 :
10913 : /************************************************************************/
10914 : /* GDALExtendedDataTypeGetNumericDataType() */
10915 : /************************************************************************/
10916 :
10917 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
10918 : *
10919 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
10920 : */
10921 2069 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
10922 : {
10923 2069 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
10924 2069 : return hEDT->m_poImpl->GetNumericDataType();
10925 : }
10926 :
10927 : /************************************************************************/
10928 : /* GDALExtendedDataTypeGetSize() */
10929 : /************************************************************************/
10930 :
10931 : /** Return data type size in bytes.
10932 : *
10933 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
10934 : */
10935 2538 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
10936 : {
10937 2538 : VALIDATE_POINTER1(hEDT, __func__, 0);
10938 2538 : return hEDT->m_poImpl->GetSize();
10939 : }
10940 :
10941 : /************************************************************************/
10942 : /* GDALExtendedDataTypeGetMaxStringLength() */
10943 : /************************************************************************/
10944 :
10945 : /** Return the maximum length of a string in bytes.
10946 : *
10947 : * 0 indicates unknown/unlimited string.
10948 : *
10949 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
10950 : */
10951 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
10952 : {
10953 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
10954 3 : return hEDT->m_poImpl->GetMaxStringLength();
10955 : }
10956 :
10957 : /************************************************************************/
10958 : /* GDALExtendedDataTypeCanConvertTo() */
10959 : /************************************************************************/
10960 :
10961 : /** Return whether this data type can be converted to the other one.
10962 : *
10963 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
10964 : *
10965 : * @param hSourceEDT Source data type for the conversion being considered.
10966 : * @param hTargetEDT Target data type for the conversion being considered.
10967 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
10968 : */
10969 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
10970 : GDALExtendedDataTypeH hTargetEDT)
10971 : {
10972 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
10973 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
10974 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
10975 : }
10976 :
10977 : /************************************************************************/
10978 : /* GDALExtendedDataTypeEquals() */
10979 : /************************************************************************/
10980 :
10981 : /** Return whether this data type is equal to another one.
10982 : *
10983 : * This is the same as the C++ method GDALExtendedDataType::operator==()
10984 : *
10985 : * @param hFirstEDT First data type.
10986 : * @param hSecondEDT Second data type.
10987 : * @return TRUE if they are equal. FALSE otherwise.
10988 : */
10989 100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
10990 : GDALExtendedDataTypeH hSecondEDT)
10991 : {
10992 100 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
10993 100 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
10994 100 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
10995 : }
10996 :
10997 : /************************************************************************/
10998 : /* GDALExtendedDataTypeGetSubType() */
10999 : /************************************************************************/
11000 :
11001 : /** Return the subtype of a type.
11002 : *
11003 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11004 : *
11005 : * @param hEDT Data type.
11006 : * @return subtype.
11007 : * @since 3.4
11008 : */
11009 : GDALExtendedDataTypeSubType
11010 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11011 : {
11012 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11013 104 : return hEDT->m_poImpl->GetSubType();
11014 : }
11015 :
11016 : /************************************************************************/
11017 : /* GDALExtendedDataTypeGetRAT() */
11018 : /************************************************************************/
11019 :
11020 : /** Return associated raster attribute table, when there is one.
11021 : *
11022 : * * For the netCDF driver, the RAT will capture enumerated types, with
11023 : * a "value" column with an integer value and a "name" column with the
11024 : * associated name.
11025 : * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11026 : *
11027 : * @param hEDT Data type.
11028 : * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11029 : * @since 3.12
11030 : */
11031 1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11032 : {
11033 1 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11034 1 : return GDALRasterAttributeTable::ToHandle(
11035 2 : const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11036 : }
11037 :
11038 : /************************************************************************/
11039 : /* GDALExtendedDataTypeGetComponents() */
11040 : /************************************************************************/
11041 :
11042 : /** Return the components of the data type (only valid when GetClass() ==
11043 : * GEDTC_COMPOUND)
11044 : *
11045 : * The returned array and its content must be freed with
11046 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11047 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11048 : * individual array members).
11049 : *
11050 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11051 : *
11052 : * @param hEDT Data type
11053 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11054 : * @return an array of *pnCount components.
11055 : */
11056 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11057 : size_t *pnCount)
11058 : {
11059 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11060 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11061 44 : const auto &components = hEDT->m_poImpl->GetComponents();
11062 : auto ret = static_cast<GDALEDTComponentH *>(
11063 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11064 131 : for (size_t i = 0; i < components.size(); i++)
11065 : {
11066 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
11067 : }
11068 44 : *pnCount = components.size();
11069 44 : return ret;
11070 : }
11071 :
11072 : /************************************************************************/
11073 : /* GDALExtendedDataTypeFreeComponents() */
11074 : /************************************************************************/
11075 :
11076 : /** Free the return of GDALExtendedDataTypeGetComponents().
11077 : *
11078 : * @param components return value of GDALExtendedDataTypeGetComponents()
11079 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11080 : */
11081 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11082 : size_t nCount)
11083 : {
11084 131 : for (size_t i = 0; i < nCount; i++)
11085 : {
11086 87 : delete components[i];
11087 : }
11088 44 : CPLFree(components);
11089 44 : }
11090 :
11091 : /************************************************************************/
11092 : /* GDALEDTComponentCreate() */
11093 : /************************************************************************/
11094 :
11095 : /** Create a new GDALEDTComponent.
11096 : *
11097 : * The returned value must be freed with GDALEDTComponentRelease().
11098 : *
11099 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11100 : */
11101 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11102 : GDALExtendedDataTypeH hType)
11103 : {
11104 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11105 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
11106 : return new GDALEDTComponentHS(
11107 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11108 : }
11109 :
11110 : /************************************************************************/
11111 : /* GDALEDTComponentRelease() */
11112 : /************************************************************************/
11113 :
11114 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11115 : *
11116 : * Note: when applied on a object coming from a driver, this does not
11117 : * destroy the object in the file, database, etc...
11118 : */
11119 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11120 : {
11121 61 : delete hComp;
11122 61 : }
11123 :
11124 : /************************************************************************/
11125 : /* GDALEDTComponentGetName() */
11126 : /************************************************************************/
11127 :
11128 : /** Return the name.
11129 : *
11130 : * The returned pointer is valid until hComp is released.
11131 : *
11132 : * This is the same as the C++ method GDALEDTComponent::GetName().
11133 : */
11134 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11135 : {
11136 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11137 33 : return hComp->m_poImpl->GetName().c_str();
11138 : }
11139 :
11140 : /************************************************************************/
11141 : /* GDALEDTComponentGetOffset() */
11142 : /************************************************************************/
11143 :
11144 : /** Return the offset (in bytes) of the component in the compound data type.
11145 : *
11146 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
11147 : */
11148 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11149 : {
11150 31 : VALIDATE_POINTER1(hComp, __func__, 0);
11151 31 : return hComp->m_poImpl->GetOffset();
11152 : }
11153 :
11154 : /************************************************************************/
11155 : /* GDALEDTComponentGetType() */
11156 : /************************************************************************/
11157 :
11158 : /** Return the data type of the component.
11159 : *
11160 : * This is the same as the C++ method GDALEDTComponent::GetType().
11161 : */
11162 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11163 : {
11164 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
11165 : return new GDALExtendedDataTypeHS(
11166 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11167 : }
11168 :
11169 : /************************************************************************/
11170 : /* GDALGroupRelease() */
11171 : /************************************************************************/
11172 :
11173 : /** Release the GDAL in-memory object associated with a GDALGroupH.
11174 : *
11175 : * Note: when applied on a object coming from a driver, this does not
11176 : * destroy the object in the file, database, etc...
11177 : */
11178 1439 : void GDALGroupRelease(GDALGroupH hGroup)
11179 : {
11180 1439 : delete hGroup;
11181 1439 : }
11182 :
11183 : /************************************************************************/
11184 : /* GDALGroupGetName() */
11185 : /************************************************************************/
11186 :
11187 : /** Return the name of the group.
11188 : *
11189 : * The returned pointer is valid until hGroup is released.
11190 : *
11191 : * This is the same as the C++ method GDALGroup::GetName().
11192 : */
11193 95 : const char *GDALGroupGetName(GDALGroupH hGroup)
11194 : {
11195 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11196 95 : return hGroup->m_poImpl->GetName().c_str();
11197 : }
11198 :
11199 : /************************************************************************/
11200 : /* GDALGroupGetFullName() */
11201 : /************************************************************************/
11202 :
11203 : /** Return the full name of the group.
11204 : *
11205 : * The returned pointer is valid until hGroup is released.
11206 : *
11207 : * This is the same as the C++ method GDALGroup::GetFullName().
11208 : */
11209 47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
11210 : {
11211 47 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11212 47 : return hGroup->m_poImpl->GetFullName().c_str();
11213 : }
11214 :
11215 : /************************************************************************/
11216 : /* GDALGroupGetMDArrayNames() */
11217 : /************************************************************************/
11218 :
11219 : /** Return the list of multidimensional array names contained in this group.
11220 : *
11221 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11222 : *
11223 : * @return the array names, to be freed with CSLDestroy()
11224 : */
11225 329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11226 : {
11227 329 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11228 658 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11229 658 : CPLStringList res;
11230 829 : for (const auto &name : names)
11231 : {
11232 500 : res.AddString(name.c_str());
11233 : }
11234 329 : return res.StealList();
11235 : }
11236 :
11237 : /************************************************************************/
11238 : /* GDALGroupGetMDArrayFullNamesRecursive() */
11239 : /************************************************************************/
11240 :
11241 : /** Return the list of multidimensional array full names contained in this
11242 : * group and its subgroups.
11243 : *
11244 : * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11245 : *
11246 : * @return the array names, to be freed with CSLDestroy()
11247 : *
11248 : * @since 3.11
11249 : */
11250 1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11251 : CSLConstList papszGroupOptions,
11252 : CSLConstList papszArrayOptions)
11253 : {
11254 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11255 1 : auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11256 2 : papszGroupOptions, papszArrayOptions);
11257 2 : CPLStringList res;
11258 5 : for (const auto &name : names)
11259 : {
11260 4 : res.AddString(name.c_str());
11261 : }
11262 1 : return res.StealList();
11263 : }
11264 :
11265 : /************************************************************************/
11266 : /* GDALGroupOpenMDArray() */
11267 : /************************************************************************/
11268 :
11269 : /** Open and return a multidimensional array.
11270 : *
11271 : * This is the same as the C++ method GDALGroup::OpenMDArray().
11272 : *
11273 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11274 : */
11275 806 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11276 : CSLConstList papszOptions)
11277 : {
11278 806 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11279 806 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11280 2418 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11281 2418 : papszOptions);
11282 806 : if (!array)
11283 30 : return nullptr;
11284 776 : return new GDALMDArrayHS(array);
11285 : }
11286 :
11287 : /************************************************************************/
11288 : /* GDALGroupOpenMDArrayFromFullname() */
11289 : /************************************************************************/
11290 :
11291 : /** Open and return a multidimensional array from its fully qualified name.
11292 : *
11293 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11294 : *
11295 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11296 : *
11297 : * @since GDAL 3.2
11298 : */
11299 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11300 : const char *pszFullname,
11301 : CSLConstList papszOptions)
11302 : {
11303 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11304 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11305 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11306 48 : std::string(pszFullname), papszOptions);
11307 16 : if (!array)
11308 2 : return nullptr;
11309 14 : return new GDALMDArrayHS(array);
11310 : }
11311 :
11312 : /************************************************************************/
11313 : /* GDALGroupResolveMDArray() */
11314 : /************************************************************************/
11315 :
11316 : /** Locate an array in a group and its subgroups by name.
11317 : *
11318 : * See GDALGroup::ResolveMDArray() for description of the behavior.
11319 : * @since GDAL 3.2
11320 : */
11321 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11322 : const char *pszStartingPoint,
11323 : CSLConstList papszOptions)
11324 : {
11325 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11326 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11327 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11328 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
11329 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
11330 19 : if (!array)
11331 2 : return nullptr;
11332 17 : return new GDALMDArrayHS(array);
11333 : }
11334 :
11335 : /************************************************************************/
11336 : /* GDALGroupGetGroupNames() */
11337 : /************************************************************************/
11338 :
11339 : /** Return the list of sub-groups contained in this group.
11340 : *
11341 : * This is the same as the C++ method GDALGroup::GetGroupNames().
11342 : *
11343 : * @return the group names, to be freed with CSLDestroy()
11344 : */
11345 97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11346 : {
11347 97 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11348 194 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11349 194 : CPLStringList res;
11350 219 : for (const auto &name : names)
11351 : {
11352 122 : res.AddString(name.c_str());
11353 : }
11354 97 : return res.StealList();
11355 : }
11356 :
11357 : /************************************************************************/
11358 : /* GDALGroupOpenGroup() */
11359 : /************************************************************************/
11360 :
11361 : /** Open and return a sub-group.
11362 : *
11363 : * This is the same as the C++ method GDALGroup::OpenGroup().
11364 : *
11365 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11366 : */
11367 163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11368 : CSLConstList papszOptions)
11369 : {
11370 163 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11371 163 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11372 : auto subGroup =
11373 489 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11374 163 : if (!subGroup)
11375 30 : return nullptr;
11376 133 : return new GDALGroupHS(subGroup);
11377 : }
11378 :
11379 : /************************************************************************/
11380 : /* GDALGroupGetVectorLayerNames() */
11381 : /************************************************************************/
11382 :
11383 : /** Return the list of layer names contained in this group.
11384 : *
11385 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11386 : *
11387 : * @return the group names, to be freed with CSLDestroy()
11388 : * @since 3.4
11389 : */
11390 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11391 : CSLConstList papszOptions)
11392 : {
11393 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11394 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11395 16 : CPLStringList res;
11396 18 : for (const auto &name : names)
11397 : {
11398 10 : res.AddString(name.c_str());
11399 : }
11400 8 : return res.StealList();
11401 : }
11402 :
11403 : /************************************************************************/
11404 : /* GDALGroupOpenVectorLayer() */
11405 : /************************************************************************/
11406 :
11407 : /** Open and return a vector layer.
11408 : *
11409 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11410 : *
11411 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11412 : * the returned handled if only valid while the parent GDALDatasetH is kept
11413 : * opened.
11414 : *
11415 : * @return the vector layer, or nullptr.
11416 : * @since 3.4
11417 : */
11418 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11419 : const char *pszVectorLayerName,
11420 : CSLConstList papszOptions)
11421 : {
11422 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11423 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11424 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11425 24 : std::string(pszVectorLayerName), papszOptions));
11426 : }
11427 :
11428 : /************************************************************************/
11429 : /* GDALGroupOpenMDArrayFromFullname() */
11430 : /************************************************************************/
11431 :
11432 : /** Open and return a sub-group from its fully qualified name.
11433 : *
11434 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11435 : *
11436 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11437 : *
11438 : * @since GDAL 3.2
11439 : */
11440 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11441 : const char *pszFullname,
11442 : CSLConstList papszOptions)
11443 : {
11444 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11445 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11446 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11447 9 : std::string(pszFullname), papszOptions);
11448 3 : if (!subGroup)
11449 2 : return nullptr;
11450 1 : return new GDALGroupHS(subGroup);
11451 : }
11452 :
11453 : /************************************************************************/
11454 : /* GDALGroupGetDimensions() */
11455 : /************************************************************************/
11456 :
11457 : /** Return the list of dimensions contained in this group and used by its
11458 : * arrays.
11459 : *
11460 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11461 : * array itself needs to be freed, CPLFree() should be called (and
11462 : * GDALDimensionRelease() on individual array members).
11463 : *
11464 : * This is the same as the C++ method GDALGroup::GetDimensions().
11465 : *
11466 : * @param hGroup Group.
11467 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11468 : * @param papszOptions Driver specific options determining how dimensions
11469 : * should be retrieved. Pass nullptr for default behavior.
11470 : *
11471 : * @return an array of *pnCount dimensions.
11472 : */
11473 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11474 : CSLConstList papszOptions)
11475 : {
11476 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11477 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11478 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11479 : auto ret = static_cast<GDALDimensionH *>(
11480 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11481 230 : for (size_t i = 0; i < dims.size(); i++)
11482 : {
11483 157 : ret[i] = new GDALDimensionHS(dims[i]);
11484 : }
11485 73 : *pnCount = dims.size();
11486 73 : return ret;
11487 : }
11488 :
11489 : /************************************************************************/
11490 : /* GDALGroupGetAttribute() */
11491 : /************************************************************************/
11492 :
11493 : /** Return an attribute by its name.
11494 : *
11495 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11496 : *
11497 : * The returned attribute must be freed with GDALAttributeRelease().
11498 : */
11499 80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11500 : {
11501 80 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11502 80 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11503 240 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11504 80 : if (attr)
11505 76 : return new GDALAttributeHS(attr);
11506 4 : return nullptr;
11507 : }
11508 :
11509 : /************************************************************************/
11510 : /* GDALGroupGetAttributes() */
11511 : /************************************************************************/
11512 :
11513 : /** Return the list of attributes contained in this group.
11514 : *
11515 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11516 : * array itself needs to be freed, CPLFree() should be called (and
11517 : * GDALAttributeRelease() on individual array members).
11518 : *
11519 : * This is the same as the C++ method GDALGroup::GetAttributes().
11520 : *
11521 : * @param hGroup Group.
11522 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11523 : * @param papszOptions Driver specific options determining how attributes
11524 : * should be retrieved. Pass nullptr for default behavior.
11525 : *
11526 : * @return an array of *pnCount attributes.
11527 : */
11528 71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11529 : CSLConstList papszOptions)
11530 : {
11531 71 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11532 71 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11533 71 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11534 : auto ret = static_cast<GDALAttributeH *>(
11535 71 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11536 229 : for (size_t i = 0; i < attrs.size(); i++)
11537 : {
11538 158 : ret[i] = new GDALAttributeHS(attrs[i]);
11539 : }
11540 71 : *pnCount = attrs.size();
11541 71 : return ret;
11542 : }
11543 :
11544 : /************************************************************************/
11545 : /* GDALGroupGetStructuralInfo() */
11546 : /************************************************************************/
11547 :
11548 : /** Return structural information on the group.
11549 : *
11550 : * This may be the compression, etc..
11551 : *
11552 : * The return value should not be freed and is valid until GDALGroup is
11553 : * released or this function called again.
11554 : *
11555 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11556 : */
11557 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11558 : {
11559 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11560 4 : return hGroup->m_poImpl->GetStructuralInfo();
11561 : }
11562 :
11563 : /************************************************************************/
11564 : /* GDALGroupGetDataTypeCount() */
11565 : /************************************************************************/
11566 :
11567 : /** Return the number of data types associated with the group
11568 : * (typically enumerations).
11569 : *
11570 : * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11571 : *
11572 : * @since 3.12
11573 : */
11574 4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11575 : {
11576 4 : VALIDATE_POINTER1(hGroup, __func__, 0);
11577 4 : return hGroup->m_poImpl->GetDataTypes().size();
11578 : }
11579 :
11580 : /************************************************************************/
11581 : /* GDALGroupGetDataType() */
11582 : /************************************************************************/
11583 :
11584 : /** Return one of the data types associated with the group.
11585 : *
11586 : * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11587 : *
11588 : * @return a type to release with GDALExtendedDataTypeRelease() once done,
11589 : * or nullptr in case of error.
11590 : * @since 3.12
11591 : */
11592 1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11593 : {
11594 1 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11595 1 : if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11596 0 : return nullptr;
11597 1 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11598 1 : *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11599 : }
11600 :
11601 : /************************************************************************/
11602 : /* GDALReleaseAttributes() */
11603 : /************************************************************************/
11604 :
11605 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11606 : *
11607 : * @param attributes return pointer of above methods
11608 : * @param nCount *pnCount value returned by above methods
11609 : */
11610 129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11611 : {
11612 416 : for (size_t i = 0; i < nCount; i++)
11613 : {
11614 287 : delete attributes[i];
11615 : }
11616 129 : CPLFree(attributes);
11617 129 : }
11618 :
11619 : /************************************************************************/
11620 : /* GDALGroupCreateGroup() */
11621 : /************************************************************************/
11622 :
11623 : /** Create a sub-group within a group.
11624 : *
11625 : * This is the same as the C++ method GDALGroup::CreateGroup().
11626 : *
11627 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11628 : */
11629 176 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11630 : CSLConstList papszOptions)
11631 : {
11632 176 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11633 176 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11634 528 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11635 528 : papszOptions);
11636 176 : if (!ret)
11637 49 : return nullptr;
11638 127 : return new GDALGroupHS(ret);
11639 : }
11640 :
11641 : /************************************************************************/
11642 : /* GDALGroupDeleteGroup() */
11643 : /************************************************************************/
11644 :
11645 : /** Delete a sub-group from a group.
11646 : *
11647 : * After this call, if a previously obtained instance of the deleted object
11648 : * is still alive, no method other than for freeing it should be invoked.
11649 : *
11650 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11651 : *
11652 : * @return true in case of success.
11653 : * @since GDAL 3.8
11654 : */
11655 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11656 : CSLConstList papszOptions)
11657 : {
11658 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11659 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11660 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11661 20 : papszOptions);
11662 : }
11663 :
11664 : /************************************************************************/
11665 : /* GDALGroupCreateDimension() */
11666 : /************************************************************************/
11667 :
11668 : /** Create a dimension within a group.
11669 : *
11670 : * This is the same as the C++ method GDALGroup::CreateDimension().
11671 : *
11672 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11673 : */
11674 664 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11675 : const char *pszType,
11676 : const char *pszDirection, GUInt64 nSize,
11677 : CSLConstList papszOptions)
11678 : {
11679 664 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11680 664 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11681 664 : auto ret = hGroup->m_poImpl->CreateDimension(
11682 1328 : std::string(pszName), std::string(pszType ? pszType : ""),
11683 2656 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11684 664 : if (!ret)
11685 9 : return nullptr;
11686 655 : return new GDALDimensionHS(ret);
11687 : }
11688 :
11689 : /************************************************************************/
11690 : /* GDALGroupCreateMDArray() */
11691 : /************************************************************************/
11692 :
11693 : /** Create a multidimensional array within a group.
11694 : *
11695 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11696 : *
11697 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11698 : */
11699 607 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11700 : size_t nDimensions,
11701 : GDALDimensionH *pahDimensions,
11702 : GDALExtendedDataTypeH hEDT,
11703 : CSLConstList papszOptions)
11704 : {
11705 607 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11706 607 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11707 607 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11708 1214 : std::vector<std::shared_ptr<GDALDimension>> dims;
11709 607 : dims.reserve(nDimensions);
11710 1427 : for (size_t i = 0; i < nDimensions; i++)
11711 820 : dims.push_back(pahDimensions[i]->m_poImpl);
11712 1821 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11713 1821 : *(hEDT->m_poImpl), papszOptions);
11714 607 : if (!ret)
11715 65 : return nullptr;
11716 542 : return new GDALMDArrayHS(ret);
11717 : }
11718 :
11719 : /************************************************************************/
11720 : /* GDALGroupDeleteMDArray() */
11721 : /************************************************************************/
11722 :
11723 : /** Delete an array from a group.
11724 : *
11725 : * After this call, if a previously obtained instance of the deleted object
11726 : * is still alive, no method other than for freeing it should be invoked.
11727 : *
11728 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
11729 : *
11730 : * @return true in case of success.
11731 : * @since GDAL 3.8
11732 : */
11733 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
11734 : CSLConstList papszOptions)
11735 : {
11736 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11737 20 : VALIDATE_POINTER1(pszName, __func__, false);
11738 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
11739 : }
11740 :
11741 : /************************************************************************/
11742 : /* GDALGroupCreateAttribute() */
11743 : /************************************************************************/
11744 :
11745 : /** Create a attribute within a group.
11746 : *
11747 : * This is the same as the C++ method GDALGroup::CreateAttribute().
11748 : *
11749 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11750 : */
11751 120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
11752 : size_t nDimensions,
11753 : const GUInt64 *panDimensions,
11754 : GDALExtendedDataTypeH hEDT,
11755 : CSLConstList papszOptions)
11756 : {
11757 120 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11758 120 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11759 240 : std::vector<GUInt64> dims;
11760 120 : dims.reserve(nDimensions);
11761 166 : for (size_t i = 0; i < nDimensions; i++)
11762 46 : dims.push_back(panDimensions[i]);
11763 120 : auto ret = hGroup->m_poImpl->CreateAttribute(
11764 360 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11765 120 : if (!ret)
11766 14 : return nullptr;
11767 106 : return new GDALAttributeHS(ret);
11768 : }
11769 :
11770 : /************************************************************************/
11771 : /* GDALGroupDeleteAttribute() */
11772 : /************************************************************************/
11773 :
11774 : /** Delete an attribute from a group.
11775 : *
11776 : * After this call, if a previously obtained instance of the deleted object
11777 : * is still alive, no method other than for freeing it should be invoked.
11778 : *
11779 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
11780 : *
11781 : * @return true in case of success.
11782 : * @since GDAL 3.8
11783 : */
11784 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
11785 : CSLConstList papszOptions)
11786 : {
11787 25 : VALIDATE_POINTER1(hGroup, __func__, false);
11788 25 : VALIDATE_POINTER1(pszName, __func__, false);
11789 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
11790 25 : papszOptions);
11791 : }
11792 :
11793 : /************************************************************************/
11794 : /* GDALGroupRename() */
11795 : /************************************************************************/
11796 :
11797 : /** Rename the group.
11798 : *
11799 : * This is not implemented by all drivers.
11800 : *
11801 : * Drivers known to implement it: MEM, netCDF.
11802 : *
11803 : * This is the same as the C++ method GDALGroup::Rename()
11804 : *
11805 : * @return true in case of success
11806 : * @since GDAL 3.8
11807 : */
11808 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
11809 : {
11810 45 : VALIDATE_POINTER1(hGroup, __func__, false);
11811 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
11812 45 : return hGroup->m_poImpl->Rename(pszNewName);
11813 : }
11814 :
11815 : /************************************************************************/
11816 : /* GDALGroupSubsetDimensionFromSelection() */
11817 : /************************************************************************/
11818 :
11819 : /** Return a virtual group whose one dimension has been subset according to a
11820 : * selection.
11821 : *
11822 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
11823 : *
11824 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
11825 : */
11826 : GDALGroupH
11827 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
11828 : const char *pszSelection,
11829 : CPL_UNUSED CSLConstList papszOptions)
11830 : {
11831 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11832 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
11833 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
11834 42 : std::string(pszSelection));
11835 14 : if (!hNewGroup)
11836 8 : return nullptr;
11837 6 : return new GDALGroupHS(hNewGroup);
11838 : }
11839 :
11840 : /************************************************************************/
11841 : /* GDALMDArrayRelease() */
11842 : /************************************************************************/
11843 :
11844 : /** Release the GDAL in-memory object associated with a GDALMDArray.
11845 : *
11846 : * Note: when applied on a object coming from a driver, this does not
11847 : * destroy the object in the file, database, etc...
11848 : */
11849 2015 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
11850 : {
11851 2015 : delete hMDArray;
11852 2015 : }
11853 :
11854 : /************************************************************************/
11855 : /* GDALMDArrayGetName() */
11856 : /************************************************************************/
11857 :
11858 : /** Return array name.
11859 : *
11860 : * This is the same as the C++ method GDALMDArray::GetName()
11861 : */
11862 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
11863 : {
11864 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11865 83 : return hArray->m_poImpl->GetName().c_str();
11866 : }
11867 :
11868 : /************************************************************************/
11869 : /* GDALMDArrayGetFullName() */
11870 : /************************************************************************/
11871 :
11872 : /** Return array full name.
11873 : *
11874 : * This is the same as the C++ method GDALMDArray::GetFullName()
11875 : */
11876 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
11877 : {
11878 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11879 50 : return hArray->m_poImpl->GetFullName().c_str();
11880 : }
11881 :
11882 : /************************************************************************/
11883 : /* GDALMDArrayGetName() */
11884 : /************************************************************************/
11885 :
11886 : /** Return the total number of values in the array.
11887 : *
11888 : * This is the same as the C++ method
11889 : * GDALAbstractMDArray::GetTotalElementsCount()
11890 : */
11891 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
11892 : {
11893 6 : VALIDATE_POINTER1(hArray, __func__, 0);
11894 6 : return hArray->m_poImpl->GetTotalElementsCount();
11895 : }
11896 :
11897 : /************************************************************************/
11898 : /* GDALMDArrayGetDimensionCount() */
11899 : /************************************************************************/
11900 :
11901 : /** Return the number of dimensions.
11902 : *
11903 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
11904 : */
11905 10368 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
11906 : {
11907 10368 : VALIDATE_POINTER1(hArray, __func__, 0);
11908 10368 : return hArray->m_poImpl->GetDimensionCount();
11909 : }
11910 :
11911 : /************************************************************************/
11912 : /* GDALMDArrayGetDimensions() */
11913 : /************************************************************************/
11914 :
11915 : /** Return the dimensions of the array
11916 : *
11917 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11918 : * array itself needs to be freed, CPLFree() should be called (and
11919 : * GDALDimensionRelease() on individual array members).
11920 : *
11921 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
11922 : *
11923 : * @param hArray Array.
11924 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11925 : *
11926 : * @return an array of *pnCount dimensions.
11927 : */
11928 2299 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
11929 : {
11930 2299 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11931 2299 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11932 2299 : const auto &dims(hArray->m_poImpl->GetDimensions());
11933 : auto ret = static_cast<GDALDimensionH *>(
11934 2299 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11935 6465 : for (size_t i = 0; i < dims.size(); i++)
11936 : {
11937 4166 : ret[i] = new GDALDimensionHS(dims[i]);
11938 : }
11939 2299 : *pnCount = dims.size();
11940 2299 : return ret;
11941 : }
11942 :
11943 : /************************************************************************/
11944 : /* GDALReleaseDimensions() */
11945 : /************************************************************************/
11946 :
11947 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
11948 : *
11949 : * @param dims return pointer of above methods
11950 : * @param nCount *pnCount value returned by above methods
11951 : */
11952 2372 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
11953 : {
11954 6695 : for (size_t i = 0; i < nCount; i++)
11955 : {
11956 4323 : delete dims[i];
11957 : }
11958 2372 : CPLFree(dims);
11959 2372 : }
11960 :
11961 : /************************************************************************/
11962 : /* GDALMDArrayGetDataType() */
11963 : /************************************************************************/
11964 :
11965 : /** Return the data type
11966 : *
11967 : * The return must be freed with GDALExtendedDataTypeRelease().
11968 : */
11969 3909 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
11970 : {
11971 3909 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11972 : return new GDALExtendedDataTypeHS(
11973 3909 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
11974 : }
11975 :
11976 : /************************************************************************/
11977 : /* GDALMDArrayRead() */
11978 : /************************************************************************/
11979 :
11980 : /** Read part or totality of a multidimensional array.
11981 : *
11982 : * This is the same as the C++ method GDALAbstractMDArray::Read()
11983 : *
11984 : * @return TRUE in case of success.
11985 : */
11986 1956 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11987 : const size_t *count, const GInt64 *arrayStep,
11988 : const GPtrDiff_t *bufferStride,
11989 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
11990 : const void *pDstBufferAllocStart,
11991 : size_t nDstBufferAllocSize)
11992 : {
11993 1956 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11994 1956 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11995 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11996 : {
11997 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11998 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11999 : }
12000 1956 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12001 1956 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12002 : // coverity[var_deref_model]
12003 3912 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12004 1956 : *(bufferDataType->m_poImpl), pDstBuffer,
12005 1956 : pDstBufferAllocStart, nDstBufferAllocSize);
12006 : }
12007 :
12008 : /************************************************************************/
12009 : /* GDALMDArrayWrite() */
12010 : /************************************************************************/
12011 :
12012 : /** Write part or totality of a multidimensional array.
12013 : *
12014 : * This is the same as the C++ method GDALAbstractMDArray::Write()
12015 : *
12016 : * @return TRUE in case of success.
12017 : */
12018 558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12019 : const size_t *count, const GInt64 *arrayStep,
12020 : const GPtrDiff_t *bufferStride,
12021 : GDALExtendedDataTypeH bufferDataType,
12022 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12023 : size_t nSrcBufferAllocSize)
12024 : {
12025 558 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12026 558 : if ((arrayStartIdx == nullptr || count == nullptr) &&
12027 0 : hArray->m_poImpl->GetDimensionCount() > 0)
12028 : {
12029 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12030 0 : VALIDATE_POINTER1(count, __func__, FALSE);
12031 : }
12032 558 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12033 558 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12034 : // coverity[var_deref_model]
12035 1116 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12036 558 : bufferStride, *(bufferDataType->m_poImpl),
12037 : pSrcBuffer, pSrcBufferAllocStart,
12038 558 : nSrcBufferAllocSize);
12039 : }
12040 :
12041 : /************************************************************************/
12042 : /* GDALMDArrayAdviseRead() */
12043 : /************************************************************************/
12044 :
12045 : /** Advise driver of upcoming read requests.
12046 : *
12047 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12048 : *
12049 : * @return TRUE in case of success.
12050 : *
12051 : * @since GDAL 3.2
12052 : */
12053 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12054 : const size_t *count)
12055 : {
12056 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12057 : }
12058 :
12059 : /************************************************************************/
12060 : /* GDALMDArrayAdviseReadEx() */
12061 : /************************************************************************/
12062 :
12063 : /** Advise driver of upcoming read requests.
12064 : *
12065 : * This is the same as the C++ method GDALMDArray::AdviseRead()
12066 : *
12067 : * @return TRUE in case of success.
12068 : *
12069 : * @since GDAL 3.4
12070 : */
12071 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12072 : const size_t *count, CSLConstList papszOptions)
12073 : {
12074 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12075 : // coverity[var_deref_model]
12076 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12077 : }
12078 :
12079 : /************************************************************************/
12080 : /* GDALMDArrayGetAttribute() */
12081 : /************************************************************************/
12082 :
12083 : /** Return an attribute by its name.
12084 : *
12085 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12086 : *
12087 : * The returned attribute must be freed with GDALAttributeRelease().
12088 : */
12089 119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12090 : {
12091 119 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12092 119 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12093 357 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12094 119 : if (attr)
12095 110 : return new GDALAttributeHS(attr);
12096 9 : return nullptr;
12097 : }
12098 :
12099 : /************************************************************************/
12100 : /* GDALMDArrayGetAttributes() */
12101 : /************************************************************************/
12102 :
12103 : /** Return the list of attributes contained in this array.
12104 : *
12105 : * The returned array must be freed with GDALReleaseAttributes(). If only the
12106 : * array itself needs to be freed, CPLFree() should be called (and
12107 : * GDALAttributeRelease() on individual array members).
12108 : *
12109 : * This is the same as the C++ method GDALMDArray::GetAttributes().
12110 : *
12111 : * @param hArray Array.
12112 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12113 : * @param papszOptions Driver specific options determining how attributes
12114 : * should be retrieved. Pass nullptr for default behavior.
12115 : *
12116 : * @return an array of *pnCount attributes.
12117 : */
12118 58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12119 : CSLConstList papszOptions)
12120 : {
12121 58 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12122 58 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12123 58 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12124 : auto ret = static_cast<GDALAttributeH *>(
12125 58 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12126 187 : for (size_t i = 0; i < attrs.size(); i++)
12127 : {
12128 129 : ret[i] = new GDALAttributeHS(attrs[i]);
12129 : }
12130 58 : *pnCount = attrs.size();
12131 58 : return ret;
12132 : }
12133 :
12134 : /************************************************************************/
12135 : /* GDALMDArrayCreateAttribute() */
12136 : /************************************************************************/
12137 :
12138 : /** Create a attribute within an array.
12139 : *
12140 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
12141 : *
12142 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12143 : */
12144 150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12145 : const char *pszName,
12146 : size_t nDimensions,
12147 : const GUInt64 *panDimensions,
12148 : GDALExtendedDataTypeH hEDT,
12149 : CSLConstList papszOptions)
12150 : {
12151 150 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12152 150 : VALIDATE_POINTER1(pszName, __func__, nullptr);
12153 150 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
12154 300 : std::vector<GUInt64> dims;
12155 150 : dims.reserve(nDimensions);
12156 175 : for (size_t i = 0; i < nDimensions; i++)
12157 25 : dims.push_back(panDimensions[i]);
12158 150 : auto ret = hArray->m_poImpl->CreateAttribute(
12159 450 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12160 150 : if (!ret)
12161 9 : return nullptr;
12162 141 : return new GDALAttributeHS(ret);
12163 : }
12164 :
12165 : /************************************************************************/
12166 : /* GDALMDArrayDeleteAttribute() */
12167 : /************************************************************************/
12168 :
12169 : /** Delete an attribute from an array.
12170 : *
12171 : * After this call, if a previously obtained instance of the deleted object
12172 : * is still alive, no method other than for freeing it should be invoked.
12173 : *
12174 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12175 : *
12176 : * @return true in case of success.
12177 : * @since GDAL 3.8
12178 : */
12179 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12180 : CSLConstList papszOptions)
12181 : {
12182 24 : VALIDATE_POINTER1(hArray, __func__, false);
12183 24 : VALIDATE_POINTER1(pszName, __func__, false);
12184 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12185 24 : papszOptions);
12186 : }
12187 :
12188 : /************************************************************************/
12189 : /* GDALMDArrayGetRawNoDataValue() */
12190 : /************************************************************************/
12191 :
12192 : /** Return the nodata value as a "raw" value.
12193 : *
12194 : * The value returned might be nullptr in case of no nodata value. When
12195 : * a nodata value is registered, a non-nullptr will be returned whose size in
12196 : * bytes is GetDataType().GetSize().
12197 : *
12198 : * The returned value should not be modified or freed.
12199 : *
12200 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12201 : *
12202 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12203 : */
12204 76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12205 : {
12206 76 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12207 76 : return hArray->m_poImpl->GetRawNoDataValue();
12208 : }
12209 :
12210 : /************************************************************************/
12211 : /* GDALMDArrayGetNoDataValueAsDouble() */
12212 : /************************************************************************/
12213 :
12214 : /** Return the nodata value as a double.
12215 : *
12216 : * The value returned might be nullptr in case of no nodata value. When
12217 : * a nodata value is registered, a non-nullptr will be returned whose size in
12218 : * bytes is GetDataType().GetSize().
12219 : *
12220 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12221 : *
12222 : * @param hArray Array handle.
12223 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12224 : * if a nodata value exists and can be converted to double. Might be nullptr.
12225 : *
12226 : * @return the nodata value as a double. A 0.0 value might also indicate the
12227 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12228 : * will be set to false then).
12229 : */
12230 120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12231 : int *pbHasNoDataValue)
12232 : {
12233 120 : VALIDATE_POINTER1(hArray, __func__, 0);
12234 120 : bool bHasNodataValue = false;
12235 120 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12236 120 : if (pbHasNoDataValue)
12237 120 : *pbHasNoDataValue = bHasNodataValue;
12238 120 : return ret;
12239 : }
12240 :
12241 : /************************************************************************/
12242 : /* GDALMDArrayGetNoDataValueAsInt64() */
12243 : /************************************************************************/
12244 :
12245 : /** Return the nodata value as a Int64.
12246 : *
12247 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12248 : *
12249 : * @param hArray Array handle.
12250 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12251 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
12252 : *
12253 : * @return the nodata value as a Int64.
12254 : * @since GDAL 3.5
12255 : */
12256 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12257 : int *pbHasNoDataValue)
12258 : {
12259 11 : VALIDATE_POINTER1(hArray, __func__, 0);
12260 11 : bool bHasNodataValue = false;
12261 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12262 11 : if (pbHasNoDataValue)
12263 11 : *pbHasNoDataValue = bHasNodataValue;
12264 11 : return ret;
12265 : }
12266 :
12267 : /************************************************************************/
12268 : /* GDALMDArrayGetNoDataValueAsUInt64() */
12269 : /************************************************************************/
12270 :
12271 : /** Return the nodata value as a UInt64.
12272 : *
12273 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12274 : *
12275 : * @param hArray Array handle.
12276 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12277 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12278 : *
12279 : * @return the nodata value as a UInt64.
12280 : * @since GDAL 3.5
12281 : */
12282 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12283 : int *pbHasNoDataValue)
12284 : {
12285 7 : VALIDATE_POINTER1(hArray, __func__, 0);
12286 7 : bool bHasNodataValue = false;
12287 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12288 7 : if (pbHasNoDataValue)
12289 7 : *pbHasNoDataValue = bHasNodataValue;
12290 7 : return ret;
12291 : }
12292 :
12293 : /************************************************************************/
12294 : /* GDALMDArraySetRawNoDataValue() */
12295 : /************************************************************************/
12296 :
12297 : /** Set the nodata value as a "raw" value.
12298 : *
12299 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12300 : * void*).
12301 : *
12302 : * @return TRUE in case of success.
12303 : */
12304 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12305 : {
12306 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12307 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12308 : }
12309 :
12310 : /************************************************************************/
12311 : /* GDALMDArraySetNoDataValueAsDouble() */
12312 : /************************************************************************/
12313 :
12314 : /** Set the nodata value as a double.
12315 : *
12316 : * If the natural data type of the attribute/array is not double, type
12317 : * conversion will occur to the type returned by GetDataType().
12318 : *
12319 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12320 : *
12321 : * @return TRUE in case of success.
12322 : */
12323 51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12324 : {
12325 51 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12326 51 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12327 : }
12328 :
12329 : /************************************************************************/
12330 : /* GDALMDArraySetNoDataValueAsInt64() */
12331 : /************************************************************************/
12332 :
12333 : /** Set the nodata value as a Int64.
12334 : *
12335 : * If the natural data type of the attribute/array is not Int64, type conversion
12336 : * will occur to the type returned by GetDataType().
12337 : *
12338 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12339 : *
12340 : * @return TRUE in case of success.
12341 : * @since GDAL 3.5
12342 : */
12343 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12344 : {
12345 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12346 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12347 : }
12348 :
12349 : /************************************************************************/
12350 : /* GDALMDArraySetNoDataValueAsUInt64() */
12351 : /************************************************************************/
12352 :
12353 : /** Set the nodata value as a UInt64.
12354 : *
12355 : * If the natural data type of the attribute/array is not UInt64, type
12356 : * conversion will occur to the type returned by GetDataType().
12357 : *
12358 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12359 : *
12360 : * @return TRUE in case of success.
12361 : * @since GDAL 3.5
12362 : */
12363 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12364 : uint64_t nNoDataValue)
12365 : {
12366 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12367 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12368 : }
12369 :
12370 : /************************************************************************/
12371 : /* GDALMDArrayResize() */
12372 : /************************************************************************/
12373 :
12374 : /** Resize an array to new dimensions.
12375 : *
12376 : * Not all drivers may allow this operation, and with restrictions (e.g.
12377 : * for netCDF, this is limited to growing of "unlimited" dimensions)
12378 : *
12379 : * Resizing a dimension used in other arrays will cause those other arrays
12380 : * to be resized.
12381 : *
12382 : * This is the same as the C++ method GDALMDArray::Resize().
12383 : *
12384 : * @param hArray Array.
12385 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
12386 : * new size of each indexing dimension.
12387 : * @param papszOptions Options. (Driver specific)
12388 : * @return true in case of success.
12389 : * @since GDAL 3.7
12390 : */
12391 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12392 : CSLConstList papszOptions)
12393 : {
12394 42 : VALIDATE_POINTER1(hArray, __func__, false);
12395 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12396 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12397 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12398 : {
12399 83 : anNewDimSizes[i] = panNewDimSizes[i];
12400 : }
12401 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12402 : }
12403 :
12404 : /************************************************************************/
12405 : /* GDALMDArraySetScale() */
12406 : /************************************************************************/
12407 :
12408 : /** Set the scale value to apply to raw values.
12409 : *
12410 : * unscaled_value = raw_value * GetScale() + GetOffset()
12411 : *
12412 : * This is the same as the C++ method GDALMDArray::SetScale().
12413 : *
12414 : * @return TRUE in case of success.
12415 : */
12416 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12417 : {
12418 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12419 0 : return hArray->m_poImpl->SetScale(dfScale);
12420 : }
12421 :
12422 : /************************************************************************/
12423 : /* GDALMDArraySetScaleEx() */
12424 : /************************************************************************/
12425 :
12426 : /** Set the scale value to apply to raw values.
12427 : *
12428 : * unscaled_value = raw_value * GetScale() + GetOffset()
12429 : *
12430 : * This is the same as the C++ method GDALMDArray::SetScale().
12431 : *
12432 : * @return TRUE in case of success.
12433 : * @since GDAL 3.3
12434 : */
12435 21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12436 : GDALDataType eStorageType)
12437 : {
12438 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12439 21 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12440 : }
12441 :
12442 : /************************************************************************/
12443 : /* GDALMDArraySetOffset() */
12444 : /************************************************************************/
12445 :
12446 : /** Set the scale value to apply to raw values.
12447 : *
12448 : * unscaled_value = raw_value * GetScale() + GetOffset()
12449 : *
12450 : * This is the same as the C++ method GDALMDArray::SetOffset().
12451 : *
12452 : * @return TRUE in case of success.
12453 : */
12454 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12455 : {
12456 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12457 0 : return hArray->m_poImpl->SetOffset(dfOffset);
12458 : }
12459 :
12460 : /************************************************************************/
12461 : /* GDALMDArraySetOffsetEx() */
12462 : /************************************************************************/
12463 :
12464 : /** Set the scale value to apply to raw values.
12465 : *
12466 : * unscaled_value = raw_value * GetOffset() + GetOffset()
12467 : *
12468 : * This is the same as the C++ method GDALMDArray::SetOffset().
12469 : *
12470 : * @return TRUE in case of success.
12471 : * @since GDAL 3.3
12472 : */
12473 21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12474 : GDALDataType eStorageType)
12475 : {
12476 21 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12477 21 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12478 : }
12479 :
12480 : /************************************************************************/
12481 : /* GDALMDArrayGetScale() */
12482 : /************************************************************************/
12483 :
12484 : /** Get the scale value to apply to raw values.
12485 : *
12486 : * unscaled_value = raw_value * GetScale() + GetOffset()
12487 : *
12488 : * This is the same as the C++ method GDALMDArray::GetScale().
12489 : *
12490 : * @return the scale value
12491 : */
12492 103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12493 : {
12494 103 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12495 103 : bool bHasValue = false;
12496 103 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12497 103 : if (pbHasValue)
12498 103 : *pbHasValue = bHasValue;
12499 103 : return dfRet;
12500 : }
12501 :
12502 : /************************************************************************/
12503 : /* GDALMDArrayGetScaleEx() */
12504 : /************************************************************************/
12505 :
12506 : /** Get the scale value to apply to raw values.
12507 : *
12508 : * unscaled_value = raw_value * GetScale() + GetScale()
12509 : *
12510 : * This is the same as the C++ method GDALMDArray::GetScale().
12511 : *
12512 : * @return the scale value
12513 : * @since GDAL 3.3
12514 : */
12515 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12516 : GDALDataType *peStorageType)
12517 : {
12518 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12519 5 : bool bHasValue = false;
12520 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12521 5 : if (pbHasValue)
12522 5 : *pbHasValue = bHasValue;
12523 5 : return dfRet;
12524 : }
12525 :
12526 : /************************************************************************/
12527 : /* GDALMDArrayGetOffset() */
12528 : /************************************************************************/
12529 :
12530 : /** Get the scale value to apply to raw values.
12531 : *
12532 : * unscaled_value = raw_value * GetScale() + GetOffset()
12533 : *
12534 : * This is the same as the C++ method GDALMDArray::GetOffset().
12535 : *
12536 : * @return the scale value
12537 : */
12538 100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12539 : {
12540 100 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12541 100 : bool bHasValue = false;
12542 100 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12543 100 : if (pbHasValue)
12544 100 : *pbHasValue = bHasValue;
12545 100 : return dfRet;
12546 : }
12547 :
12548 : /************************************************************************/
12549 : /* GDALMDArrayGetOffsetEx() */
12550 : /************************************************************************/
12551 :
12552 : /** Get the scale value to apply to raw values.
12553 : *
12554 : * unscaled_value = raw_value * GetScale() + GetOffset()
12555 : *
12556 : * This is the same as the C++ method GDALMDArray::GetOffset().
12557 : *
12558 : * @return the scale value
12559 : * @since GDAL 3.3
12560 : */
12561 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12562 : GDALDataType *peStorageType)
12563 : {
12564 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12565 5 : bool bHasValue = false;
12566 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12567 5 : if (pbHasValue)
12568 5 : *pbHasValue = bHasValue;
12569 5 : return dfRet;
12570 : }
12571 :
12572 : /************************************************************************/
12573 : /* GDALMDArrayGetBlockSize() */
12574 : /************************************************************************/
12575 :
12576 : /** Return the "natural" block size of the array along all dimensions.
12577 : *
12578 : * Some drivers might organize the array in tiles/blocks and reading/writing
12579 : * aligned on those tile/block boundaries will be more efficient.
12580 : *
12581 : * The returned number of elements in the vector is the same as
12582 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12583 : * the natural block size along the considered dimension.
12584 : * "Flat" arrays will typically return a vector of values set to 0.
12585 : *
12586 : * The default implementation will return a vector of values set to 0.
12587 : *
12588 : * This method is used by GetProcessingChunkSize().
12589 : *
12590 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12591 : * theoretical case of a 32-bit platform, this might exceed its size_t
12592 : * allocation capabilities.
12593 : *
12594 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12595 : *
12596 : * @return the block size, in number of elements along each dimension.
12597 : */
12598 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12599 : {
12600 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12601 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12602 93 : auto res = hArray->m_poImpl->GetBlockSize();
12603 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12604 285 : for (size_t i = 0; i < res.size(); i++)
12605 : {
12606 192 : ret[i] = res[i];
12607 : }
12608 93 : *pnCount = res.size();
12609 93 : return ret;
12610 : }
12611 :
12612 : /***********************************************************************/
12613 : /* GDALMDArrayGetProcessingChunkSize() */
12614 : /************************************************************************/
12615 :
12616 : /** \brief Return an optimal chunk size for read/write operations, given the
12617 : * natural block size and memory constraints specified.
12618 : *
12619 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12620 : * multiple of those returned by GetBlockSize() (unless the block define by
12621 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12622 : * returned by this method).
12623 : *
12624 : * This is the same as the C++ method
12625 : * GDALAbstractMDArray::GetProcessingChunkSize().
12626 : *
12627 : * @param hArray Array.
12628 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12629 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12630 : * chunk.
12631 : *
12632 : * @return the chunk size, in number of elements along each dimension.
12633 : */
12634 :
12635 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12636 : size_t nMaxChunkMemory)
12637 : {
12638 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12639 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12640 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12641 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12642 3 : for (size_t i = 0; i < res.size(); i++)
12643 : {
12644 2 : ret[i] = res[i];
12645 : }
12646 1 : *pnCount = res.size();
12647 1 : return ret;
12648 : }
12649 :
12650 : /************************************************************************/
12651 : /* GDALMDArrayGetStructuralInfo() */
12652 : /************************************************************************/
12653 :
12654 : /** Return structural information on the array.
12655 : *
12656 : * This may be the compression, etc..
12657 : *
12658 : * The return value should not be freed and is valid until GDALMDArray is
12659 : * released or this function called again.
12660 : *
12661 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12662 : */
12663 15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12664 : {
12665 15 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12666 15 : return hArray->m_poImpl->GetStructuralInfo();
12667 : }
12668 :
12669 : /************************************************************************/
12670 : /* GDALMDArrayGetView() */
12671 : /************************************************************************/
12672 :
12673 : /** Return a view of the array using slicing or field access.
12674 : *
12675 : * The returned object should be released with GDALMDArrayRelease().
12676 : *
12677 : * This is the same as the C++ method GDALMDArray::GetView().
12678 : */
12679 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12680 : {
12681 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12682 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12683 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12684 430 : if (!sliced)
12685 22 : return nullptr;
12686 408 : return new GDALMDArrayHS(sliced);
12687 : }
12688 :
12689 : /************************************************************************/
12690 : /* GDALMDArrayTranspose() */
12691 : /************************************************************************/
12692 :
12693 : /** Return a view of the array whose axis have been reordered.
12694 : *
12695 : * The returned object should be released with GDALMDArrayRelease().
12696 : *
12697 : * This is the same as the C++ method GDALMDArray::Transpose().
12698 : */
12699 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12700 : const int *panMapNewAxisToOldAxis)
12701 : {
12702 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12703 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12704 44 : if (nNewAxisCount)
12705 : {
12706 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12707 : nNewAxisCount * sizeof(int));
12708 : }
12709 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12710 44 : if (!reordered)
12711 7 : return nullptr;
12712 37 : return new GDALMDArrayHS(reordered);
12713 : }
12714 :
12715 : /************************************************************************/
12716 : /* GDALMDArrayGetUnscaled() */
12717 : /************************************************************************/
12718 :
12719 : /** Return an array that is the unscaled version of the current one.
12720 : *
12721 : * That is each value of the unscaled array will be
12722 : * unscaled_value = raw_value * GetScale() + GetOffset()
12723 : *
12724 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
12725 : * from unscaled values to raw values.
12726 : *
12727 : * The returned object should be released with GDALMDArrayRelease().
12728 : *
12729 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
12730 : */
12731 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
12732 : {
12733 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12734 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
12735 13 : if (!unscaled)
12736 0 : return nullptr;
12737 13 : return new GDALMDArrayHS(unscaled);
12738 : }
12739 :
12740 : /************************************************************************/
12741 : /* GDALMDArrayGetMask() */
12742 : /************************************************************************/
12743 :
12744 : /** Return an array that is a mask for the current array
12745 : *
12746 : * This array will be of type Byte, with values set to 0 to indicate invalid
12747 : * pixels of the current array, and values set to 1 to indicate valid pixels.
12748 : *
12749 : * The returned object should be released with GDALMDArrayRelease().
12750 : *
12751 : * This is the same as the C++ method GDALMDArray::GetMask().
12752 : */
12753 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
12754 : {
12755 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12756 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
12757 35 : if (!unscaled)
12758 7 : return nullptr;
12759 28 : return new GDALMDArrayHS(unscaled);
12760 : }
12761 :
12762 : /************************************************************************/
12763 : /* GDALMDArrayGetResampled() */
12764 : /************************************************************************/
12765 :
12766 : /** Return an array that is a resampled / reprojected view of the current array
12767 : *
12768 : * This is the same as the C++ method GDALMDArray::GetResampled().
12769 : *
12770 : * Currently this method can only resample along the last 2 dimensions, unless
12771 : * orthorectifying a NASA EMIT dataset.
12772 : *
12773 : * The returned object should be released with GDALMDArrayRelease().
12774 : *
12775 : * @since 3.4
12776 : */
12777 34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
12778 : const GDALDimensionH *pahNewDims,
12779 : GDALRIOResampleAlg resampleAlg,
12780 : OGRSpatialReferenceH hTargetSRS,
12781 : CSLConstList papszOptions)
12782 : {
12783 34 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12784 34 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
12785 68 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
12786 112 : for (size_t i = 0; i < nNewDimCount; ++i)
12787 : {
12788 78 : if (pahNewDims[i])
12789 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
12790 : }
12791 34 : auto poNewArray = hArray->m_poImpl->GetResampled(
12792 34 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
12793 68 : papszOptions);
12794 34 : if (!poNewArray)
12795 8 : return nullptr;
12796 26 : return new GDALMDArrayHS(poNewArray);
12797 : }
12798 :
12799 : /************************************************************************/
12800 : /* GDALMDArraySetUnit() */
12801 : /************************************************************************/
12802 :
12803 : /** Set the variable unit.
12804 : *
12805 : * Values should conform as much as possible with those allowed by
12806 : * the NetCDF CF conventions:
12807 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12808 : * but others might be returned.
12809 : *
12810 : * Few examples are "meter", "degrees", "second", ...
12811 : * Empty value means unknown.
12812 : *
12813 : * This is the same as the C function GDALMDArraySetUnit()
12814 : *
12815 : * @param hArray array.
12816 : * @param pszUnit unit name.
12817 : * @return TRUE in case of success.
12818 : */
12819 15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
12820 : {
12821 15 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12822 15 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
12823 : }
12824 :
12825 : /************************************************************************/
12826 : /* GDALMDArrayGetUnit() */
12827 : /************************************************************************/
12828 :
12829 : /** Return the array unit.
12830 : *
12831 : * Values should conform as much as possible with those allowed by
12832 : * the NetCDF CF conventions:
12833 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12834 : * but others might be returned.
12835 : *
12836 : * Few examples are "meter", "degrees", "second", ...
12837 : * Empty value means unknown.
12838 : *
12839 : * The return value should not be freed and is valid until GDALMDArray is
12840 : * released or this function called again.
12841 : *
12842 : * This is the same as the C++ method GDALMDArray::GetUnit().
12843 : */
12844 111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
12845 : {
12846 111 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12847 111 : return hArray->m_poImpl->GetUnit().c_str();
12848 : }
12849 :
12850 : /************************************************************************/
12851 : /* GDALMDArrayGetSpatialRef() */
12852 : /************************************************************************/
12853 :
12854 : /** Assign a spatial reference system object to the array.
12855 : *
12856 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
12857 : * @return TRUE in case of success.
12858 : */
12859 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
12860 : {
12861 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12862 60 : return hArray->m_poImpl->SetSpatialRef(
12863 60 : OGRSpatialReference::FromHandle(hSRS));
12864 : }
12865 :
12866 : /************************************************************************/
12867 : /* GDALMDArrayGetSpatialRef() */
12868 : /************************************************************************/
12869 :
12870 : /** Return the spatial reference system object associated with the array.
12871 : *
12872 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
12873 : *
12874 : * The returned object must be freed with OSRDestroySpatialReference().
12875 : */
12876 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
12877 : {
12878 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12879 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
12880 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
12881 : }
12882 :
12883 : /************************************************************************/
12884 : /* GDALMDArrayGetStatistics() */
12885 : /************************************************************************/
12886 :
12887 : /**
12888 : * \brief Fetch statistics.
12889 : *
12890 : * This is the same as the C++ method GDALMDArray::GetStatistics().
12891 : *
12892 : * @since GDAL 3.2
12893 : */
12894 :
12895 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
12896 : int bApproxOK, int bForce, double *pdfMin,
12897 : double *pdfMax, double *pdfMean,
12898 : double *pdfStdDev, GUInt64 *pnValidCount,
12899 : GDALProgressFunc pfnProgress,
12900 : void *pProgressData)
12901 : {
12902 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
12903 30 : return hArray->m_poImpl->GetStatistics(
12904 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
12905 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
12906 : }
12907 :
12908 : /************************************************************************/
12909 : /* GDALMDArrayComputeStatistics() */
12910 : /************************************************************************/
12911 :
12912 : /**
12913 : * \brief Compute statistics.
12914 : *
12915 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12916 : *
12917 : * @since GDAL 3.2
12918 : * @see GDALMDArrayComputeStatisticsEx()
12919 : */
12920 :
12921 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12922 : int bApproxOK, double *pdfMin, double *pdfMax,
12923 : double *pdfMean, double *pdfStdDev,
12924 : GUInt64 *pnValidCount,
12925 : GDALProgressFunc pfnProgress,
12926 : void *pProgressData)
12927 : {
12928 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12929 0 : return hArray->m_poImpl->ComputeStatistics(
12930 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12931 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
12932 : }
12933 :
12934 : /************************************************************************/
12935 : /* GDALMDArrayComputeStatisticsEx() */
12936 : /************************************************************************/
12937 :
12938 : /**
12939 : * \brief Compute statistics.
12940 : *
12941 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
12942 : *
12943 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12944 : *
12945 : * @since GDAL 3.8
12946 : */
12947 :
12948 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12949 : int bApproxOK, double *pdfMin,
12950 : double *pdfMax, double *pdfMean,
12951 : double *pdfStdDev, GUInt64 *pnValidCount,
12952 : GDALProgressFunc pfnProgress,
12953 : void *pProgressData,
12954 : CSLConstList papszOptions)
12955 : {
12956 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12957 8 : return hArray->m_poImpl->ComputeStatistics(
12958 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12959 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
12960 : }
12961 :
12962 : /************************************************************************/
12963 : /* GDALMDArrayGetCoordinateVariables() */
12964 : /************************************************************************/
12965 :
12966 : /** Return coordinate variables.
12967 : *
12968 : * The returned array must be freed with GDALReleaseArrays(). If only the array
12969 : * itself needs to be freed, CPLFree() should be called (and
12970 : * GDALMDArrayRelease() on individual array members).
12971 : *
12972 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
12973 : *
12974 : * @param hArray Array.
12975 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12976 : *
12977 : * @return an array of *pnCount arrays.
12978 : * @since 3.4
12979 : */
12980 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
12981 : size_t *pnCount)
12982 : {
12983 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12984 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12985 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
12986 : auto ret = static_cast<GDALMDArrayH *>(
12987 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
12988 29 : for (size_t i = 0; i < coordinates.size(); i++)
12989 : {
12990 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
12991 : }
12992 13 : *pnCount = coordinates.size();
12993 13 : return ret;
12994 : }
12995 :
12996 : /************************************************************************/
12997 : /* GDALMDArrayGetGridded() */
12998 : /************************************************************************/
12999 :
13000 : /** Return a gridded array from scattered point data, that is from an array
13001 : * whose last dimension is the indexing variable of X and Y arrays.
13002 : *
13003 : * The returned object should be released with GDALMDArrayRelease().
13004 : *
13005 : * This is the same as the C++ method GDALMDArray::GetGridded().
13006 : *
13007 : * @since GDAL 3.7
13008 : */
13009 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13010 : const char *pszGridOptions,
13011 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13012 : CSLConstList papszOptions)
13013 : {
13014 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13015 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13016 22 : auto gridded = hArray->m_poImpl->GetGridded(
13017 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13018 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13019 22 : if (!gridded)
13020 19 : return nullptr;
13021 3 : return new GDALMDArrayHS(gridded);
13022 : }
13023 :
13024 : /************************************************************************/
13025 : /* GDALMDArrayGetMeshGrid() */
13026 : /************************************************************************/
13027 :
13028 : /** Return a list of multidimensional arrays from a list of one-dimensional
13029 : * arrays.
13030 : *
13031 : * This is typically used to transform one-dimensional longitude, latitude
13032 : * arrays into 2D ones.
13033 : *
13034 : * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13035 : * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13036 : * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13037 : * repeated to fill the matrix along the first dimension for x1, the second
13038 : * for x2 and so on.
13039 : *
13040 : * For example, if x = [1, 2], and y = [3, 4, 5],
13041 : * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13042 : * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13043 : * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13044 : *
13045 : * and
13046 : * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13047 : * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13048 : * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13049 : *
13050 : * The currently supported options are:
13051 : * <ul>
13052 : * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13053 : * output.
13054 : * </li>
13055 : * </ul>
13056 : *
13057 : * This is the same as
13058 : * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13059 : * function.
13060 : *
13061 : * The returned array (of arrays) must be freed with GDALReleaseArrays().
13062 : * If only the array itself needs to be freed, CPLFree() should be called
13063 : * (and GDALMDArrayRelease() on individual array members).
13064 : *
13065 : * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13066 : *
13067 : * @param pahInputArrays Input arrays
13068 : * @param nCountInputArrays Number of input arrays
13069 : * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13070 : * @param papszOptions NULL, or NULL terminated list of options.
13071 : *
13072 : * @return an array of *pnCountOutputArrays arrays.
13073 : * @since 3.10
13074 : */
13075 7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13076 : size_t nCountInputArrays,
13077 : size_t *pnCountOutputArrays,
13078 : CSLConstList papszOptions)
13079 : {
13080 7 : VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13081 7 : VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13082 :
13083 14 : std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13084 20 : for (size_t i = 0; i < nCountInputArrays; ++i)
13085 13 : apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13086 :
13087 : const auto apoOutputArrays =
13088 7 : GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13089 : auto ret = static_cast<GDALMDArrayH *>(
13090 7 : CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13091 17 : for (size_t i = 0; i < apoOutputArrays.size(); i++)
13092 : {
13093 10 : ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13094 : }
13095 7 : *pnCountOutputArrays = apoOutputArrays.size();
13096 7 : return ret;
13097 : }
13098 :
13099 : /************************************************************************/
13100 : /* GDALReleaseArrays() */
13101 : /************************************************************************/
13102 :
13103 : /** Free the return of GDALMDArrayGetCoordinateVariables()
13104 : *
13105 : * @param arrays return pointer of above methods
13106 : * @param nCount *pnCount value returned by above methods
13107 : */
13108 20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13109 : {
13110 46 : for (size_t i = 0; i < nCount; i++)
13111 : {
13112 26 : delete arrays[i];
13113 : }
13114 20 : CPLFree(arrays);
13115 20 : }
13116 :
13117 : /************************************************************************/
13118 : /* GDALMDArrayCache() */
13119 : /************************************************************************/
13120 :
13121 : /**
13122 : * \brief Cache the content of the array into an auxiliary filename.
13123 : *
13124 : * This is the same as the C++ method GDALMDArray::Cache().
13125 : *
13126 : * @since GDAL 3.4
13127 : */
13128 :
13129 7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13130 : {
13131 7 : VALIDATE_POINTER1(hArray, __func__, FALSE);
13132 7 : return hArray->m_poImpl->Cache(papszOptions);
13133 : }
13134 :
13135 : /************************************************************************/
13136 : /* GDALMDArrayRename() */
13137 : /************************************************************************/
13138 :
13139 : /** Rename the array.
13140 : *
13141 : * This is not implemented by all drivers.
13142 : *
13143 : * Drivers known to implement it: MEM, netCDF, Zarr.
13144 : *
13145 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13146 : *
13147 : * @return true in case of success
13148 : * @since GDAL 3.8
13149 : */
13150 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13151 : {
13152 28 : VALIDATE_POINTER1(hArray, __func__, false);
13153 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
13154 28 : return hArray->m_poImpl->Rename(pszNewName);
13155 : }
13156 :
13157 : /************************************************************************/
13158 : /* GDALAttributeRelease() */
13159 : /************************************************************************/
13160 :
13161 : /** Release the GDAL in-memory object associated with a GDALAttribute.
13162 : *
13163 : * Note: when applied on a object coming from a driver, this does not
13164 : * destroy the object in the file, database, etc...
13165 : */
13166 720 : void GDALAttributeRelease(GDALAttributeH hAttr)
13167 : {
13168 720 : delete hAttr;
13169 720 : }
13170 :
13171 : /************************************************************************/
13172 : /* GDALAttributeGetName() */
13173 : /************************************************************************/
13174 :
13175 : /** Return the name of the attribute.
13176 : *
13177 : * The returned pointer is valid until hAttr is released.
13178 : *
13179 : * This is the same as the C++ method GDALAttribute::GetName().
13180 : */
13181 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
13182 : {
13183 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13184 361 : return hAttr->m_poImpl->GetName().c_str();
13185 : }
13186 :
13187 : /************************************************************************/
13188 : /* GDALAttributeGetFullName() */
13189 : /************************************************************************/
13190 :
13191 : /** Return the full name of the attribute.
13192 : *
13193 : * The returned pointer is valid until hAttr is released.
13194 : *
13195 : * This is the same as the C++ method GDALAttribute::GetFullName().
13196 : */
13197 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13198 : {
13199 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13200 49 : return hAttr->m_poImpl->GetFullName().c_str();
13201 : }
13202 :
13203 : /************************************************************************/
13204 : /* GDALAttributeGetTotalElementsCount() */
13205 : /************************************************************************/
13206 :
13207 : /** Return the total number of values in the attribute.
13208 : *
13209 : * This is the same as the C++ method
13210 : * GDALAbstractMDArray::GetTotalElementsCount()
13211 : */
13212 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13213 : {
13214 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
13215 176 : return hAttr->m_poImpl->GetTotalElementsCount();
13216 : }
13217 :
13218 : /************************************************************************/
13219 : /* GDALAttributeGetDimensionCount() */
13220 : /************************************************************************/
13221 :
13222 : /** Return the number of dimensions.
13223 : *
13224 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13225 : */
13226 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13227 : {
13228 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
13229 12 : return hAttr->m_poImpl->GetDimensionCount();
13230 : }
13231 :
13232 : /************************************************************************/
13233 : /* GDALAttributeGetDimensionsSize() */
13234 : /************************************************************************/
13235 :
13236 : /** Return the dimension sizes of the attribute.
13237 : *
13238 : * The returned array must be freed with CPLFree()
13239 : *
13240 : * @param hAttr Attribute.
13241 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13242 : *
13243 : * @return an array of *pnCount values.
13244 : */
13245 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13246 : {
13247 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13248 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13249 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
13250 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13251 22 : for (size_t i = 0; i < dims.size(); i++)
13252 : {
13253 11 : ret[i] = dims[i]->GetSize();
13254 : }
13255 11 : *pnCount = dims.size();
13256 11 : return ret;
13257 : }
13258 :
13259 : /************************************************************************/
13260 : /* GDALAttributeGetDataType() */
13261 : /************************************************************************/
13262 :
13263 : /** Return the data type
13264 : *
13265 : * The return must be freed with GDALExtendedDataTypeRelease().
13266 : */
13267 427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13268 : {
13269 427 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13270 : return new GDALExtendedDataTypeHS(
13271 427 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13272 : }
13273 :
13274 : /************************************************************************/
13275 : /* GDALAttributeReadAsRaw() */
13276 : /************************************************************************/
13277 :
13278 : /** Return the raw value of an attribute.
13279 : *
13280 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13281 : *
13282 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
13283 : *
13284 : * @param hAttr Attribute.
13285 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13286 : *
13287 : * @return a buffer of *pnSize bytes.
13288 : */
13289 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13290 : {
13291 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13292 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
13293 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
13294 6 : *pnSize = res.size();
13295 6 : auto ret = res.StealData();
13296 6 : if (!ret)
13297 : {
13298 0 : *pnSize = 0;
13299 0 : return nullptr;
13300 : }
13301 6 : return ret;
13302 : }
13303 :
13304 : /************************************************************************/
13305 : /* GDALAttributeFreeRawResult() */
13306 : /************************************************************************/
13307 :
13308 : /** Free the return of GDALAttributeAsRaw()
13309 : */
13310 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13311 : CPL_UNUSED size_t nSize)
13312 : {
13313 6 : VALIDATE_POINTER0(hAttr, __func__);
13314 6 : if (raw)
13315 : {
13316 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
13317 6 : const auto nDTSize(dt.GetSize());
13318 6 : GByte *pabyPtr = raw;
13319 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13320 6 : CPLAssert(nSize == nDTSize * nEltCount);
13321 12 : for (size_t i = 0; i < nEltCount; ++i)
13322 : {
13323 6 : dt.FreeDynamicMemory(pabyPtr);
13324 6 : pabyPtr += nDTSize;
13325 : }
13326 6 : CPLFree(raw);
13327 : }
13328 : }
13329 :
13330 : /************************************************************************/
13331 : /* GDALAttributeReadAsString() */
13332 : /************************************************************************/
13333 :
13334 : /** Return the value of an attribute as a string.
13335 : *
13336 : * The returned string should not be freed, and its lifetime does not
13337 : * excess a next call to ReadAsString() on the same object, or the deletion
13338 : * of the object itself.
13339 : *
13340 : * This function will only return the first element if there are several.
13341 : *
13342 : * This is the same as the C++ method GDALAttribute::ReadAsString()
13343 : *
13344 : * @return a string, or nullptr.
13345 : */
13346 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13347 : {
13348 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13349 107 : return hAttr->m_poImpl->ReadAsString();
13350 : }
13351 :
13352 : /************************************************************************/
13353 : /* GDALAttributeReadAsInt() */
13354 : /************************************************************************/
13355 :
13356 : /** Return the value of an attribute as a integer.
13357 : *
13358 : * This function will only return the first element if there are several.
13359 : *
13360 : * It can fail if its value can not be converted to integer.
13361 : *
13362 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
13363 : *
13364 : * @return a integer, or INT_MIN in case of error.
13365 : */
13366 22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13367 : {
13368 22 : VALIDATE_POINTER1(hAttr, __func__, 0);
13369 22 : return hAttr->m_poImpl->ReadAsInt();
13370 : }
13371 :
13372 : /************************************************************************/
13373 : /* GDALAttributeReadAsInt64() */
13374 : /************************************************************************/
13375 :
13376 : /** Return the value of an attribute as a int64_t.
13377 : *
13378 : * This function will only return the first element if there are several.
13379 : *
13380 : * It can fail if its value can not be converted to integer.
13381 : *
13382 : * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13383 : *
13384 : * @return an int64_t, or INT64_MIN in case of error.
13385 : */
13386 15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13387 : {
13388 15 : VALIDATE_POINTER1(hAttr, __func__, 0);
13389 15 : return hAttr->m_poImpl->ReadAsInt64();
13390 : }
13391 :
13392 : /************************************************************************/
13393 : /* GDALAttributeReadAsDouble() */
13394 : /************************************************************************/
13395 :
13396 : /** Return the value of an attribute as a double.
13397 : *
13398 : * This function will only return the first element if there are several.
13399 : *
13400 : * It can fail if its value can not be converted to double.
13401 : *
13402 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13403 : *
13404 : * @return a double value.
13405 : */
13406 40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13407 : {
13408 40 : VALIDATE_POINTER1(hAttr, __func__, 0);
13409 40 : return hAttr->m_poImpl->ReadAsDouble();
13410 : }
13411 :
13412 : /************************************************************************/
13413 : /* GDALAttributeReadAsStringArray() */
13414 : /************************************************************************/
13415 :
13416 : /** Return the value of an attribute as an array of strings.
13417 : *
13418 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13419 : *
13420 : * The return value must be freed with CSLDestroy().
13421 : */
13422 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13423 : {
13424 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13425 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
13426 : }
13427 :
13428 : /************************************************************************/
13429 : /* GDALAttributeReadAsIntArray() */
13430 : /************************************************************************/
13431 :
13432 : /** Return the value of an attribute as an array of integers.
13433 : *
13434 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13435 : *
13436 : * @param hAttr Attribute
13437 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13438 : * @return array to be freed with CPLFree(), or nullptr.
13439 : */
13440 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13441 : {
13442 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13443 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13444 15 : *pnCount = 0;
13445 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13446 15 : if (tmp.empty())
13447 0 : return nullptr;
13448 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13449 15 : if (!ret)
13450 0 : return nullptr;
13451 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13452 15 : *pnCount = tmp.size();
13453 15 : return ret;
13454 : }
13455 :
13456 : /************************************************************************/
13457 : /* GDALAttributeReadAsInt64Array() */
13458 : /************************************************************************/
13459 :
13460 : /** Return the value of an attribute as an array of int64_t.
13461 : *
13462 : * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13463 : *
13464 : * @param hAttr Attribute
13465 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13466 : * @return array to be freed with CPLFree(), or nullptr.
13467 : */
13468 14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13469 : {
13470 14 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13471 14 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13472 14 : *pnCount = 0;
13473 28 : auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13474 14 : if (tmp.empty())
13475 0 : return nullptr;
13476 : auto ret = static_cast<int64_t *>(
13477 14 : VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13478 14 : if (!ret)
13479 0 : return nullptr;
13480 14 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13481 14 : *pnCount = tmp.size();
13482 14 : return ret;
13483 : }
13484 :
13485 : /************************************************************************/
13486 : /* GDALAttributeReadAsDoubleArray() */
13487 : /************************************************************************/
13488 :
13489 : /** Return the value of an attribute as an array of doubles.
13490 : *
13491 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13492 : *
13493 : * @param hAttr Attribute
13494 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13495 : * @return array to be freed with CPLFree(), or nullptr.
13496 : */
13497 29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13498 : {
13499 29 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
13500 29 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
13501 29 : *pnCount = 0;
13502 58 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13503 29 : if (tmp.empty())
13504 0 : return nullptr;
13505 : auto ret =
13506 29 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13507 29 : if (!ret)
13508 0 : return nullptr;
13509 29 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13510 29 : *pnCount = tmp.size();
13511 29 : return ret;
13512 : }
13513 :
13514 : /************************************************************************/
13515 : /* GDALAttributeWriteRaw() */
13516 : /************************************************************************/
13517 :
13518 : /** Write an attribute from raw values expressed in GetDataType()
13519 : *
13520 : * The values should be provided in the type of GetDataType() and there should
13521 : * be exactly GetTotalElementsCount() of them.
13522 : * If GetDataType() is a string, each value should be a char* pointer.
13523 : *
13524 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13525 : *
13526 : * @param hAttr Attribute
13527 : * @param pabyValue Buffer of nLen bytes.
13528 : * @param nLength Size of pabyValue in bytes. Should be equal to
13529 : * GetTotalElementsCount() * GetDataType().GetSize()
13530 : * @return TRUE in case of success.
13531 : */
13532 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13533 : size_t nLength)
13534 : {
13535 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13536 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
13537 : }
13538 :
13539 : /************************************************************************/
13540 : /* GDALAttributeWriteString() */
13541 : /************************************************************************/
13542 :
13543 : /** Write an attribute from a string value.
13544 : *
13545 : * Type conversion will be performed if needed. If the attribute contains
13546 : * multiple values, only the first one will be updated.
13547 : *
13548 : * This is the same as the C++ method GDALAttribute::Write(const char*)
13549 : *
13550 : * @param hAttr Attribute
13551 : * @param pszVal Pointer to a string.
13552 : * @return TRUE in case of success.
13553 : */
13554 175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13555 : {
13556 175 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13557 175 : return hAttr->m_poImpl->Write(pszVal);
13558 : }
13559 :
13560 : /************************************************************************/
13561 : /* GDALAttributeWriteInt() */
13562 : /************************************************************************/
13563 :
13564 : /** Write an attribute from a integer value.
13565 : *
13566 : * Type conversion will be performed if needed. If the attribute contains
13567 : * multiple values, only the first one will be updated.
13568 : *
13569 : * This is the same as the C++ method GDALAttribute::WriteInt()
13570 : *
13571 : * @param hAttr Attribute
13572 : * @param nVal Value.
13573 : * @return TRUE in case of success.
13574 : */
13575 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13576 : {
13577 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13578 22 : return hAttr->m_poImpl->WriteInt(nVal);
13579 : }
13580 :
13581 : /************************************************************************/
13582 : /* GDALAttributeWriteInt64() */
13583 : /************************************************************************/
13584 :
13585 : /** Write an attribute from an int64_t value.
13586 : *
13587 : * Type conversion will be performed if needed. If the attribute contains
13588 : * multiple values, only the first one will be updated.
13589 : *
13590 : * This is the same as the C++ method GDALAttribute::WriteLong()
13591 : *
13592 : * @param hAttr Attribute
13593 : * @param nVal Value.
13594 : * @return TRUE in case of success.
13595 : */
13596 11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13597 : {
13598 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13599 11 : return hAttr->m_poImpl->WriteInt64(nVal);
13600 : }
13601 :
13602 : /************************************************************************/
13603 : /* GDALAttributeWriteDouble() */
13604 : /************************************************************************/
13605 :
13606 : /** Write an attribute from a double value.
13607 : *
13608 : * Type conversion will be performed if needed. If the attribute contains
13609 : * multiple values, only the first one will be updated.
13610 : *
13611 : * This is the same as the C++ method GDALAttribute::Write(double);
13612 : *
13613 : * @param hAttr Attribute
13614 : * @param dfVal Value.
13615 : *
13616 : * @return TRUE in case of success.
13617 : */
13618 11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13619 : {
13620 11 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13621 11 : return hAttr->m_poImpl->Write(dfVal);
13622 : }
13623 :
13624 : /************************************************************************/
13625 : /* GDALAttributeWriteStringArray() */
13626 : /************************************************************************/
13627 :
13628 : /** Write an attribute from an array of strings.
13629 : *
13630 : * Type conversion will be performed if needed.
13631 : *
13632 : * Exactly GetTotalElementsCount() strings must be provided
13633 : *
13634 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13635 : *
13636 : * @param hAttr Attribute
13637 : * @param papszValues Array of strings.
13638 : * @return TRUE in case of success.
13639 : */
13640 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13641 : CSLConstList papszValues)
13642 : {
13643 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13644 8 : return hAttr->m_poImpl->Write(papszValues);
13645 : }
13646 :
13647 : /************************************************************************/
13648 : /* GDALAttributeWriteIntArray() */
13649 : /************************************************************************/
13650 :
13651 : /** Write an attribute from an array of int.
13652 : *
13653 : * Type conversion will be performed if needed.
13654 : *
13655 : * Exactly GetTotalElementsCount() strings must be provided
13656 : *
13657 : * This is the same as the C++ method GDALAttribute::Write(const int *,
13658 : * size_t)
13659 : *
13660 : * @param hAttr Attribute
13661 : * @param panValues Array of int.
13662 : * @param nCount Should be equal to GetTotalElementsCount().
13663 : * @return TRUE in case of success.
13664 : */
13665 9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13666 : size_t nCount)
13667 : {
13668 9 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13669 9 : return hAttr->m_poImpl->Write(panValues, nCount);
13670 : }
13671 :
13672 : /************************************************************************/
13673 : /* GDALAttributeWriteInt64Array() */
13674 : /************************************************************************/
13675 :
13676 : /** Write an attribute from an array of int64_t.
13677 : *
13678 : * Type conversion will be performed if needed.
13679 : *
13680 : * Exactly GetTotalElementsCount() strings must be provided
13681 : *
13682 : * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13683 : * size_t)
13684 : *
13685 : * @param hAttr Attribute
13686 : * @param panValues Array of int64_t.
13687 : * @param nCount Should be equal to GetTotalElementsCount().
13688 : * @return TRUE in case of success.
13689 : */
13690 10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13691 : size_t nCount)
13692 : {
13693 10 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13694 10 : return hAttr->m_poImpl->Write(panValues, nCount);
13695 : }
13696 :
13697 : /************************************************************************/
13698 : /* GDALAttributeWriteDoubleArray() */
13699 : /************************************************************************/
13700 :
13701 : /** Write an attribute from an array of double.
13702 : *
13703 : * Type conversion will be performed if needed.
13704 : *
13705 : * Exactly GetTotalElementsCount() strings must be provided
13706 : *
13707 : * This is the same as the C++ method GDALAttribute::Write(const double *,
13708 : * size_t)
13709 : *
13710 : * @param hAttr Attribute
13711 : * @param padfValues Array of double.
13712 : * @param nCount Should be equal to GetTotalElementsCount().
13713 : * @return TRUE in case of success.
13714 : */
13715 7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
13716 : const double *padfValues, size_t nCount)
13717 : {
13718 7 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
13719 7 : return hAttr->m_poImpl->Write(padfValues, nCount);
13720 : }
13721 :
13722 : /************************************************************************/
13723 : /* GDALAttributeRename() */
13724 : /************************************************************************/
13725 :
13726 : /** Rename the attribute.
13727 : *
13728 : * This is not implemented by all drivers.
13729 : *
13730 : * Drivers known to implement it: MEM, netCDF.
13731 : *
13732 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
13733 : *
13734 : * @return true in case of success
13735 : * @since GDAL 3.8
13736 : */
13737 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
13738 : {
13739 27 : VALIDATE_POINTER1(hAttr, __func__, false);
13740 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
13741 27 : return hAttr->m_poImpl->Rename(pszNewName);
13742 : }
13743 :
13744 : /************************************************************************/
13745 : /* GDALDimensionRelease() */
13746 : /************************************************************************/
13747 :
13748 : /** Release the GDAL in-memory object associated with a GDALDimension.
13749 : *
13750 : * Note: when applied on a object coming from a driver, this does not
13751 : * destroy the object in the file, database, etc...
13752 : */
13753 4908 : void GDALDimensionRelease(GDALDimensionH hDim)
13754 : {
13755 4908 : delete hDim;
13756 4908 : }
13757 :
13758 : /************************************************************************/
13759 : /* GDALDimensionGetName() */
13760 : /************************************************************************/
13761 :
13762 : /** Return dimension name.
13763 : *
13764 : * This is the same as the C++ method GDALDimension::GetName()
13765 : */
13766 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
13767 : {
13768 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13769 284 : return hDim->m_poImpl->GetName().c_str();
13770 : }
13771 :
13772 : /************************************************************************/
13773 : /* GDALDimensionGetFullName() */
13774 : /************************************************************************/
13775 :
13776 : /** Return dimension full name.
13777 : *
13778 : * This is the same as the C++ method GDALDimension::GetFullName()
13779 : */
13780 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
13781 : {
13782 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13783 80 : return hDim->m_poImpl->GetFullName().c_str();
13784 : }
13785 :
13786 : /************************************************************************/
13787 : /* GDALDimensionGetType() */
13788 : /************************************************************************/
13789 :
13790 : /** Return dimension type.
13791 : *
13792 : * This is the same as the C++ method GDALDimension::GetType()
13793 : */
13794 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
13795 : {
13796 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13797 62 : return hDim->m_poImpl->GetType().c_str();
13798 : }
13799 :
13800 : /************************************************************************/
13801 : /* GDALDimensionGetDirection() */
13802 : /************************************************************************/
13803 :
13804 : /** Return dimension direction.
13805 : *
13806 : * This is the same as the C++ method GDALDimension::GetDirection()
13807 : */
13808 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
13809 : {
13810 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13811 32 : return hDim->m_poImpl->GetDirection().c_str();
13812 : }
13813 :
13814 : /************************************************************************/
13815 : /* GDALDimensionGetSize() */
13816 : /************************************************************************/
13817 :
13818 : /** Return the size, that is the number of values along the dimension.
13819 : *
13820 : * This is the same as the C++ method GDALDimension::GetSize()
13821 : */
13822 3681 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
13823 : {
13824 3681 : VALIDATE_POINTER1(hDim, __func__, 0);
13825 3681 : return hDim->m_poImpl->GetSize();
13826 : }
13827 :
13828 : /************************************************************************/
13829 : /* GDALDimensionGetIndexingVariable() */
13830 : /************************************************************************/
13831 :
13832 : /** Return the variable that is used to index the dimension (if there is one).
13833 : *
13834 : * This is the array, typically one-dimensional, describing the values taken
13835 : * by the dimension.
13836 : *
13837 : * The returned value should be freed with GDALMDArrayRelease().
13838 : *
13839 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
13840 : */
13841 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
13842 : {
13843 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13844 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
13845 118 : if (!var)
13846 10 : return nullptr;
13847 108 : return new GDALMDArrayHS(var);
13848 : }
13849 :
13850 : /************************************************************************/
13851 : /* GDALDimensionSetIndexingVariable() */
13852 : /************************************************************************/
13853 :
13854 : /** Set the variable that is used to index the dimension.
13855 : *
13856 : * This is the array, typically one-dimensional, describing the values taken
13857 : * by the dimension.
13858 : *
13859 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
13860 : *
13861 : * @return TRUE in case of success.
13862 : */
13863 23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
13864 : {
13865 23 : VALIDATE_POINTER1(hDim, __func__, FALSE);
13866 69 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
13867 46 : : nullptr);
13868 : }
13869 :
13870 : /************************************************************************/
13871 : /* GDALDimensionRename() */
13872 : /************************************************************************/
13873 :
13874 : /** Rename the dimension.
13875 : *
13876 : * This is not implemented by all drivers.
13877 : *
13878 : * Drivers known to implement it: MEM, netCDF.
13879 : *
13880 : * This is the same as the C++ method GDALDimension::Rename()
13881 : *
13882 : * @return true in case of success
13883 : * @since GDAL 3.8
13884 : */
13885 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
13886 : {
13887 31 : VALIDATE_POINTER1(hDim, __func__, false);
13888 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
13889 31 : return hDim->m_poImpl->Rename(pszNewName);
13890 : }
13891 :
13892 : /************************************************************************/
13893 : /* GDALDatasetGetRootGroup() */
13894 : /************************************************************************/
13895 :
13896 : /** Return the root GDALGroup of this dataset.
13897 : *
13898 : * Only valid for multidimensional datasets.
13899 : *
13900 : * The returned value must be freed with GDALGroupRelease().
13901 : *
13902 : * This is the same as the C++ method GDALDataset::GetRootGroup().
13903 : *
13904 : * @since GDAL 3.1
13905 : */
13906 1176 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
13907 : {
13908 1176 : VALIDATE_POINTER1(hDS, __func__, nullptr);
13909 1176 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
13910 1176 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
13911 : }
13912 :
13913 : /************************************************************************/
13914 : /* GDALRasterBandAsMDArray() */
13915 : /************************************************************************/
13916 :
13917 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
13918 : *
13919 : * The band must be linked to a GDALDataset. If this dataset is not already
13920 : * marked as shared, it will be, so that the returned array holds a reference
13921 : * to it.
13922 : *
13923 : * If the dataset has a geotransform attached, the X and Y dimensions of the
13924 : * returned array will have an associated indexing variable.
13925 : *
13926 : * The returned pointer must be released with GDALMDArrayRelease().
13927 : *
13928 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
13929 : *
13930 : * @return a new array, or NULL.
13931 : *
13932 : * @since GDAL 3.1
13933 : */
13934 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
13935 : {
13936 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
13937 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
13938 21 : if (!poArray)
13939 0 : return nullptr;
13940 21 : return new GDALMDArrayHS(poArray);
13941 : }
13942 :
13943 : /************************************************************************/
13944 : /* GDALMDArrayAsClassicDataset() */
13945 : /************************************************************************/
13946 :
13947 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13948 : *
13949 : * Only 2D or more arrays are supported.
13950 : *
13951 : * In the case of > 2D arrays, additional dimensions will be represented as
13952 : * raster bands.
13953 : *
13954 : * The "reverse" method is GDALRasterBand::AsMDArray().
13955 : *
13956 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13957 : *
13958 : * @param hArray Array.
13959 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13960 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13961 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13962 : */
13963 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
13964 : size_t iYDim)
13965 : {
13966 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13967 0 : return GDALDataset::ToHandle(
13968 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
13969 : }
13970 :
13971 : /************************************************************************/
13972 : /* GDALMDArrayAsClassicDatasetEx() */
13973 : /************************************************************************/
13974 :
13975 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13976 : *
13977 : * Only 2D or more arrays are supported.
13978 : *
13979 : * In the case of > 2D arrays, additional dimensions will be represented as
13980 : * raster bands.
13981 : *
13982 : * The "reverse" method is GDALRasterBand::AsMDArray().
13983 : *
13984 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13985 : * @param hArray Array.
13986 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13987 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13988 : * Ignored if the dimension count is 1.
13989 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
13990 : * BAND_IMAGERY_METADATA option.
13991 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
13992 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13993 : * @since GDAL 3.8
13994 : */
13995 71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
13996 : size_t iYDim, GDALGroupH hRootGroup,
13997 : CSLConstList papszOptions)
13998 : {
13999 71 : VALIDATE_POINTER1(hArray, __func__, nullptr);
14000 142 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14001 142 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14002 142 : papszOptions));
14003 : }
14004 :
14005 : //! @cond Doxygen_Suppress
14006 :
14007 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14008 : const std::string &osName,
14009 : const std::string &osValue,
14010 180 : GDALExtendedDataTypeSubType eSubType)
14011 : : GDALAbstractMDArray(osParentName, osName),
14012 : GDALAttribute(osParentName, osName),
14013 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14014 : {
14015 180 : }
14016 :
14017 : const std::vector<std::shared_ptr<GDALDimension>> &
14018 30 : GDALAttributeString::GetDimensions() const
14019 : {
14020 30 : return m_dims;
14021 : }
14022 :
14023 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14024 : {
14025 21 : return m_dt;
14026 : }
14027 :
14028 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14029 : const GPtrDiff_t *,
14030 : const GDALExtendedDataType &bufferDataType,
14031 : void *pDstBuffer) const
14032 : {
14033 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
14034 0 : return false;
14035 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14036 10 : if (!pszStr)
14037 0 : return false;
14038 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14039 10 : *static_cast<char **>(pDstBuffer) = pszStr;
14040 10 : return true;
14041 : }
14042 :
14043 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14044 : const std::string &osName,
14045 66 : double dfValue)
14046 : : GDALAbstractMDArray(osParentName, osName),
14047 : GDALAttribute(osParentName, osName),
14048 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14049 : {
14050 66 : }
14051 :
14052 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14053 : const std::string &osName,
14054 27 : int nValue)
14055 : : GDALAbstractMDArray(osParentName, osName),
14056 : GDALAttribute(osParentName, osName),
14057 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14058 : {
14059 27 : }
14060 :
14061 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14062 : const std::string &osName,
14063 7 : const std::vector<GUInt32> &anValues)
14064 : : GDALAbstractMDArray(osParentName, osName),
14065 : GDALAttribute(osParentName, osName),
14066 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14067 : {
14068 7 : m_dims.push_back(std::make_shared<GDALDimension>(
14069 14 : std::string(), "dim0", std::string(), std::string(),
14070 7 : m_anValuesUInt32.size()));
14071 7 : }
14072 :
14073 : const std::vector<std::shared_ptr<GDALDimension>> &
14074 14 : GDALAttributeNumeric::GetDimensions() const
14075 : {
14076 14 : return m_dims;
14077 : }
14078 :
14079 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14080 : {
14081 8 : return m_dt;
14082 : }
14083 :
14084 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14085 : const size_t *count, const GInt64 *arrayStep,
14086 : const GPtrDiff_t *bufferStride,
14087 : const GDALExtendedDataType &bufferDataType,
14088 : void *pDstBuffer) const
14089 : {
14090 4 : if (m_dims.empty())
14091 : {
14092 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
14093 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14094 : bufferDataType);
14095 : else
14096 : {
14097 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14098 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14099 : bufferDataType);
14100 : }
14101 : }
14102 : else
14103 : {
14104 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14105 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14106 30 : for (size_t i = 0; i < count[0]; ++i)
14107 : {
14108 29 : GDALExtendedDataType::CopyValue(
14109 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14110 29 : i * arrayStep[0])],
14111 29 : m_dt, pabyDstBuffer, bufferDataType);
14112 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14113 : }
14114 : }
14115 4 : return true;
14116 : }
14117 :
14118 194 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14119 : const std::string &osParentName, const std::string &osName,
14120 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14121 194 : double dfIncrement, double dfOffsetInIncrement)
14122 : : GDALAbstractMDArray(osParentName, osName),
14123 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14124 : m_dfIncrement(dfIncrement),
14125 388 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14126 : {
14127 194 : }
14128 :
14129 194 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14130 : const std::string &osParentName, const std::string &osName,
14131 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14132 : double dfIncrement, double dfOffsetInIncrement)
14133 : {
14134 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14135 194 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14136 194 : poArray->SetSelf(poArray);
14137 194 : return poArray;
14138 : }
14139 :
14140 : const std::vector<std::shared_ptr<GDALDimension>> &
14141 788 : GDALMDArrayRegularlySpaced::GetDimensions() const
14142 : {
14143 788 : return m_dims;
14144 : }
14145 :
14146 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14147 : {
14148 316 : return m_dt;
14149 : }
14150 :
14151 : std::vector<std::shared_ptr<GDALAttribute>>
14152 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14153 : {
14154 4 : return m_attributes;
14155 : }
14156 :
14157 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
14158 : const std::shared_ptr<GDALAttribute> &poAttr)
14159 : {
14160 0 : m_attributes.emplace_back(poAttr);
14161 0 : }
14162 :
14163 188 : bool GDALMDArrayRegularlySpaced::IRead(
14164 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14165 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14166 : void *pDstBuffer) const
14167 : {
14168 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14169 14719 : for (size_t i = 0; i < count[0]; i++)
14170 : {
14171 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
14172 14531 : m_dfOffsetInIncrement) *
14173 14531 : m_dfIncrement;
14174 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14175 : bufferDataType);
14176 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14177 : }
14178 188 : return true;
14179 : }
14180 :
14181 3009 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14182 : const std::string &osParentName, const std::string &osName,
14183 3009 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14184 3009 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14185 : {
14186 3009 : }
14187 :
14188 : std::shared_ptr<GDALMDArray>
14189 723 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14190 : {
14191 723 : return m_poIndexingVariable.lock();
14192 : }
14193 :
14194 : // cppcheck-suppress passedByValue
14195 500 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14196 : std::shared_ptr<GDALMDArray> poIndexingVariable)
14197 : {
14198 500 : m_poIndexingVariable = poIndexingVariable;
14199 500 : return true;
14200 : }
14201 :
14202 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14203 : {
14204 33 : m_nSize = nNewSize;
14205 33 : }
14206 :
14207 : /************************************************************************/
14208 : /* GDALPamMultiDim::Private */
14209 : /************************************************************************/
14210 :
14211 : struct GDALPamMultiDim::Private
14212 : {
14213 : std::string m_osFilename{};
14214 : std::string m_osPamFilename{};
14215 :
14216 : struct Statistics
14217 : {
14218 : bool bHasStats = false;
14219 : bool bApproxStats = false;
14220 : double dfMin = 0;
14221 : double dfMax = 0;
14222 : double dfMean = 0;
14223 : double dfStdDev = 0;
14224 : GUInt64 nValidCount = 0;
14225 : };
14226 :
14227 : struct ArrayInfo
14228 : {
14229 : std::shared_ptr<OGRSpatialReference> poSRS{};
14230 : // cppcheck-suppress unusedStructMember
14231 : Statistics stats{};
14232 : };
14233 :
14234 : typedef std::pair<std::string, std::string> NameContext;
14235 : std::map<NameContext, ArrayInfo> m_oMapArray{};
14236 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14237 : bool m_bDirty = false;
14238 : bool m_bLoaded = false;
14239 : };
14240 :
14241 : /************************************************************************/
14242 : /* GDALPamMultiDim */
14243 : /************************************************************************/
14244 :
14245 1449 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14246 1449 : : d(new Private())
14247 : {
14248 1449 : d->m_osFilename = osFilename;
14249 1449 : }
14250 :
14251 : /************************************************************************/
14252 : /* GDALPamMultiDim::~GDALPamMultiDim() */
14253 : /************************************************************************/
14254 :
14255 1449 : GDALPamMultiDim::~GDALPamMultiDim()
14256 : {
14257 1449 : if (d->m_bDirty)
14258 30 : Save();
14259 1449 : }
14260 :
14261 : /************************************************************************/
14262 : /* GDALPamMultiDim::Load() */
14263 : /************************************************************************/
14264 :
14265 107 : void GDALPamMultiDim::Load()
14266 : {
14267 107 : if (d->m_bLoaded)
14268 96 : return;
14269 45 : d->m_bLoaded = true;
14270 :
14271 45 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14272 45 : d->m_osPamFilename =
14273 90 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14274 45 : CPLXMLTreeCloser oTree(nullptr);
14275 : {
14276 90 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14277 45 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14278 : }
14279 45 : if (!oTree)
14280 : {
14281 34 : return;
14282 : }
14283 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14284 11 : if (!poPAMMultiDim)
14285 0 : return;
14286 35 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14287 24 : psIter = psIter->psNext)
14288 : {
14289 24 : if (psIter->eType == CXT_Element &&
14290 24 : strcmp(psIter->pszValue, "Array") == 0)
14291 : {
14292 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14293 13 : if (!pszName)
14294 0 : continue;
14295 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14296 : const auto oKey =
14297 26 : std::pair<std::string, std::string>(pszName, pszContext);
14298 :
14299 : /* --------------------------------------------------------------------
14300 : */
14301 : /* Check for an SRS node. */
14302 : /* --------------------------------------------------------------------
14303 : */
14304 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14305 13 : if (psSRSNode)
14306 : {
14307 : std::shared_ptr<OGRSpatialReference> poSRS =
14308 6 : std::make_shared<OGRSpatialReference>();
14309 3 : poSRS->SetFromUserInput(
14310 : CPLGetXMLValue(psSRSNode, nullptr, ""),
14311 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14312 3 : const char *pszMapping = CPLGetXMLValue(
14313 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14314 3 : if (pszMapping)
14315 : {
14316 : char **papszTokens =
14317 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14318 6 : std::vector<int> anMapping;
14319 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
14320 : {
14321 6 : anMapping.push_back(atoi(papszTokens[i]));
14322 : }
14323 3 : CSLDestroy(papszTokens);
14324 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14325 : }
14326 : else
14327 : {
14328 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14329 : }
14330 :
14331 : const char *pszCoordinateEpoch =
14332 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14333 3 : if (pszCoordinateEpoch)
14334 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14335 :
14336 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14337 : }
14338 :
14339 : const CPLXMLNode *psStatistics =
14340 13 : CPLGetXMLNode(psIter, "Statistics");
14341 13 : if (psStatistics)
14342 : {
14343 7 : Private::Statistics sStats;
14344 7 : sStats.bHasStats = true;
14345 7 : sStats.bApproxStats = CPLTestBool(
14346 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14347 7 : sStats.dfMin =
14348 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14349 7 : sStats.dfMax =
14350 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14351 7 : sStats.dfMean =
14352 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14353 7 : sStats.dfStdDev =
14354 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14355 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14356 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14357 7 : d->m_oMapArray[oKey].stats = sStats;
14358 13 : }
14359 : }
14360 : else
14361 : {
14362 11 : CPLXMLNode *psNextBackup = psIter->psNext;
14363 11 : psIter->psNext = nullptr;
14364 11 : d->m_apoOtherNodes.emplace_back(
14365 11 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14366 11 : psIter->psNext = psNextBackup;
14367 : }
14368 : }
14369 : }
14370 :
14371 : /************************************************************************/
14372 : /* GDALPamMultiDim::Save() */
14373 : /************************************************************************/
14374 :
14375 30 : void GDALPamMultiDim::Save()
14376 : {
14377 : CPLXMLTreeCloser oTree(
14378 60 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14379 34 : for (const auto &poOtherNode : d->m_apoOtherNodes)
14380 : {
14381 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14382 : }
14383 112 : for (const auto &kv : d->m_oMapArray)
14384 : {
14385 : CPLXMLNode *psArrayNode =
14386 82 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14387 82 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14388 82 : if (!kv.first.second.empty())
14389 : {
14390 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
14391 : kv.first.second.c_str());
14392 : }
14393 82 : if (kv.second.poSRS)
14394 : {
14395 71 : char *pszWKT = nullptr;
14396 : {
14397 142 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14398 71 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14399 71 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14400 : }
14401 : CPLXMLNode *psSRSNode =
14402 71 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14403 71 : CPLFree(pszWKT);
14404 : const auto &mapping =
14405 71 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14406 142 : CPLString osMapping;
14407 213 : for (size_t i = 0; i < mapping.size(); ++i)
14408 : {
14409 142 : if (!osMapping.empty())
14410 71 : osMapping += ",";
14411 142 : osMapping += CPLSPrintf("%d", mapping[i]);
14412 : }
14413 71 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14414 : osMapping.c_str());
14415 :
14416 : const double dfCoordinateEpoch =
14417 71 : kv.second.poSRS->GetCoordinateEpoch();
14418 71 : if (dfCoordinateEpoch > 0)
14419 : {
14420 : std::string osCoordinateEpoch =
14421 2 : CPLSPrintf("%f", dfCoordinateEpoch);
14422 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
14423 : {
14424 6 : while (osCoordinateEpoch.back() == '0')
14425 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14426 : }
14427 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14428 : osCoordinateEpoch.c_str());
14429 : }
14430 : }
14431 :
14432 82 : if (kv.second.stats.bHasStats)
14433 : {
14434 : CPLXMLNode *psMDArray =
14435 8 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14436 8 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14437 8 : kv.second.stats.bApproxStats ? "1"
14438 : : "0");
14439 8 : CPLCreateXMLElementAndValue(
14440 : psMDArray, "Minimum",
14441 8 : CPLSPrintf("%.17g", kv.second.stats.dfMin));
14442 8 : CPLCreateXMLElementAndValue(
14443 : psMDArray, "Maximum",
14444 8 : CPLSPrintf("%.17g", kv.second.stats.dfMax));
14445 8 : CPLCreateXMLElementAndValue(
14446 8 : psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14447 8 : CPLCreateXMLElementAndValue(
14448 : psMDArray, "StdDev",
14449 8 : CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14450 8 : CPLCreateXMLElementAndValue(
14451 : psMDArray, "ValidSampleCount",
14452 8 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14453 : }
14454 : }
14455 :
14456 : int bSaved;
14457 60 : CPLErrorAccumulator oErrorAccumulator;
14458 : {
14459 30 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14460 30 : CPL_IGNORE_RET_VAL(oAccumulator);
14461 : bSaved =
14462 30 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14463 : }
14464 :
14465 30 : const char *pszNewPam = nullptr;
14466 30 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14467 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14468 : {
14469 0 : CPLErrorReset();
14470 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14471 : }
14472 : else
14473 : {
14474 30 : oErrorAccumulator.ReplayErrors();
14475 : }
14476 30 : }
14477 :
14478 : /************************************************************************/
14479 : /* GDALPamMultiDim::GetSpatialRef() */
14480 : /************************************************************************/
14481 :
14482 : std::shared_ptr<OGRSpatialReference>
14483 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14484 : const std::string &osContext)
14485 : {
14486 10 : Load();
14487 : auto oIter =
14488 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14489 10 : if (oIter != d->m_oMapArray.end())
14490 2 : return oIter->second.poSRS;
14491 8 : return nullptr;
14492 : }
14493 :
14494 : /************************************************************************/
14495 : /* GDALPamMultiDim::SetSpatialRef() */
14496 : /************************************************************************/
14497 :
14498 72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14499 : const std::string &osContext,
14500 : const OGRSpatialReference *poSRS)
14501 : {
14502 72 : Load();
14503 72 : d->m_bDirty = true;
14504 72 : if (poSRS && !poSRS->IsEmpty())
14505 71 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14506 : poSRS->Clone());
14507 : else
14508 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14509 1 : .poSRS.reset();
14510 72 : }
14511 :
14512 : /************************************************************************/
14513 : /* GetStatistics() */
14514 : /************************************************************************/
14515 :
14516 16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14517 : const std::string &osContext,
14518 : bool bApproxOK, double *pdfMin,
14519 : double *pdfMax, double *pdfMean,
14520 : double *pdfStdDev, GUInt64 *pnValidCount)
14521 : {
14522 16 : Load();
14523 : auto oIter =
14524 16 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14525 16 : if (oIter == d->m_oMapArray.end())
14526 9 : return CE_Failure;
14527 7 : const auto &stats = oIter->second.stats;
14528 7 : if (!stats.bHasStats)
14529 1 : return CE_Failure;
14530 6 : if (!bApproxOK && stats.bApproxStats)
14531 0 : return CE_Failure;
14532 6 : if (pdfMin)
14533 6 : *pdfMin = stats.dfMin;
14534 6 : if (pdfMax)
14535 6 : *pdfMax = stats.dfMax;
14536 6 : if (pdfMean)
14537 6 : *pdfMean = stats.dfMean;
14538 6 : if (pdfStdDev)
14539 6 : *pdfStdDev = stats.dfStdDev;
14540 6 : if (pnValidCount)
14541 6 : *pnValidCount = stats.nValidCount;
14542 6 : return CE_None;
14543 : }
14544 :
14545 : /************************************************************************/
14546 : /* SetStatistics() */
14547 : /************************************************************************/
14548 :
14549 8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14550 : const std::string &osContext,
14551 : bool bApproxStats, double dfMin,
14552 : double dfMax, double dfMean,
14553 : double dfStdDev, GUInt64 nValidCount)
14554 : {
14555 8 : Load();
14556 8 : d->m_bDirty = true;
14557 : auto &stats =
14558 8 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14559 8 : stats.bHasStats = true;
14560 8 : stats.bApproxStats = bApproxStats;
14561 8 : stats.dfMin = dfMin;
14562 8 : stats.dfMax = dfMax;
14563 8 : stats.dfMean = dfMean;
14564 8 : stats.dfStdDev = dfStdDev;
14565 8 : stats.nValidCount = nValidCount;
14566 8 : }
14567 :
14568 : /************************************************************************/
14569 : /* ClearStatistics() */
14570 : /************************************************************************/
14571 :
14572 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14573 : const std::string &osContext)
14574 : {
14575 0 : Load();
14576 0 : d->m_bDirty = true;
14577 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14578 : false;
14579 0 : }
14580 :
14581 : /************************************************************************/
14582 : /* ClearStatistics() */
14583 : /************************************************************************/
14584 :
14585 1 : void GDALPamMultiDim::ClearStatistics()
14586 : {
14587 1 : Load();
14588 1 : d->m_bDirty = true;
14589 3 : for (auto &kv : d->m_oMapArray)
14590 2 : kv.second.stats.bHasStats = false;
14591 1 : }
14592 :
14593 : /************************************************************************/
14594 : /* GetPAM() */
14595 : /************************************************************************/
14596 :
14597 : /*static*/ std::shared_ptr<GDALPamMultiDim>
14598 815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14599 : {
14600 815 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14601 815 : if (poPamArray)
14602 567 : return poPamArray->GetPAM();
14603 248 : return nullptr;
14604 : }
14605 :
14606 : /************************************************************************/
14607 : /* GDALPamMDArray */
14608 : /************************************************************************/
14609 :
14610 3755 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14611 : const std::string &osName,
14612 : const std::shared_ptr<GDALPamMultiDim> &poPam,
14613 0 : const std::string &osContext)
14614 : :
14615 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14616 : GDALAbstractMDArray(osParentName, osName),
14617 : #endif
14618 3755 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14619 : {
14620 3755 : }
14621 :
14622 : /************************************************************************/
14623 : /* GDALPamMDArray::SetSpatialRef() */
14624 : /************************************************************************/
14625 :
14626 72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14627 : {
14628 72 : if (!m_poPam)
14629 0 : return false;
14630 72 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14631 72 : return true;
14632 : }
14633 :
14634 : /************************************************************************/
14635 : /* GDALPamMDArray::GetSpatialRef() */
14636 : /************************************************************************/
14637 :
14638 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14639 : {
14640 10 : if (!m_poPam)
14641 0 : return nullptr;
14642 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14643 : }
14644 :
14645 : /************************************************************************/
14646 : /* GetStatistics() */
14647 : /************************************************************************/
14648 :
14649 16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14650 : double *pdfMin, double *pdfMax,
14651 : double *pdfMean, double *pdfStdDev,
14652 : GUInt64 *pnValidCount,
14653 : GDALProgressFunc pfnProgress,
14654 : void *pProgressData)
14655 : {
14656 16 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14657 : bApproxOK, pdfMin, pdfMax, pdfMean,
14658 16 : pdfStdDev, pnValidCount) == CE_None)
14659 : {
14660 6 : return CE_None;
14661 : }
14662 10 : if (!bForce)
14663 4 : return CE_Warning;
14664 :
14665 6 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14666 : pdfMean, pdfStdDev, pnValidCount,
14667 6 : pfnProgress, pProgressData);
14668 : }
14669 :
14670 : /************************************************************************/
14671 : /* SetStatistics() */
14672 : /************************************************************************/
14673 :
14674 8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14675 : double dfMax, double dfMean, double dfStdDev,
14676 : GUInt64 nValidCount,
14677 : CSLConstList /* papszOptions */)
14678 : {
14679 8 : if (!m_poPam)
14680 0 : return false;
14681 8 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14682 : dfMax, dfMean, dfStdDev, nValidCount);
14683 8 : return true;
14684 : }
14685 :
14686 : /************************************************************************/
14687 : /* ClearStatistics() */
14688 : /************************************************************************/
14689 :
14690 0 : void GDALPamMDArray::ClearStatistics()
14691 : {
14692 0 : if (!m_poPam)
14693 0 : return;
14694 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
14695 : }
14696 :
14697 : //! @endcond
|