Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Name: gdalmultidim.cpp
5 : * Project: GDAL Core
6 : * Purpose: GDAL Core C++/Private implementation for multidimensional support
7 : * Author: Even Rouault <even.rouault at spatialys.com>
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include <assert.h>
32 : #include <algorithm>
33 : #include <limits>
34 : #include <queue>
35 : #include <set>
36 : #include <utility>
37 : #include <time.h>
38 :
39 : #include <ctype.h> // isalnum
40 :
41 : #include "cpl_error_internal.h"
42 : #include "gdal_priv.h"
43 : #include "gdal_pam.h"
44 : #include "gdal_utils.h"
45 : #include "cpl_safemaths.hpp"
46 : #include "memmultidim.h"
47 : #include "ogrsf_frmts.h"
48 : #include "gdalmultidim_priv.h"
49 :
50 : #if defined(__clang__) || defined(_MSC_VER)
51 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
52 : #endif
53 :
54 : /************************************************************************/
55 : /* GDALMDArrayUnscaled */
56 : /************************************************************************/
57 :
58 : class GDALMDArrayUnscaled final : public GDALPamMDArray
59 : {
60 : private:
61 : std::shared_ptr<GDALMDArray> m_poParent{};
62 : const GDALExtendedDataType m_dt;
63 : bool m_bHasNoData;
64 : const double m_dfScale;
65 : const double m_dfOffset;
66 : std::vector<GByte> m_abyRawNoData{};
67 :
68 : protected:
69 13 : explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
70 : double dfScale, double dfOffset,
71 : double dfOverriddenDstNodata, GDALDataType eDT)
72 26 : : GDALAbstractMDArray(std::string(),
73 26 : "Unscaled view of " + poParent->GetFullName()),
74 : GDALPamMDArray(
75 26 : std::string(), "Unscaled view of " + poParent->GetFullName(),
76 26 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
77 13 : m_poParent(std::move(poParent)),
78 : m_dt(GDALExtendedDataType::Create(eDT)),
79 13 : m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
80 78 : m_dfScale(dfScale), m_dfOffset(dfOffset)
81 : {
82 13 : m_abyRawNoData.resize(m_dt.GetSize());
83 : const auto eNonComplexDT =
84 13 : GDALGetNonComplexDataType(m_dt.GetNumericDataType());
85 26 : GDALCopyWords(&dfOverriddenDstNodata, GDT_Float64, 0,
86 13 : m_abyRawNoData.data(), eNonComplexDT,
87 : GDALGetDataTypeSizeBytes(eNonComplexDT),
88 13 : GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
89 13 : }
90 :
91 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
92 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
93 : const GDALExtendedDataType &bufferDataType,
94 : void *pDstBuffer) const override;
95 :
96 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
97 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
98 : const GDALExtendedDataType &bufferDataType,
99 : const void *pSrcBuffer) override;
100 :
101 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
102 : CSLConstList papszOptions) const override
103 : {
104 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
105 : }
106 :
107 : public:
108 : static std::shared_ptr<GDALMDArrayUnscaled>
109 13 : Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
110 : double dfOffset, double dfDstNodata, GDALDataType eDT)
111 : {
112 : auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
113 13 : poParent, dfScale, dfOffset, dfDstNodata, eDT)));
114 13 : newAr->SetSelf(newAr);
115 13 : return newAr;
116 : }
117 :
118 1 : bool IsWritable() const override
119 : {
120 1 : return m_poParent->IsWritable();
121 : }
122 :
123 15 : const std::string &GetFilename() const override
124 : {
125 15 : return m_poParent->GetFilename();
126 : }
127 :
128 : const std::vector<std::shared_ptr<GDALDimension>> &
129 220 : GetDimensions() const override
130 : {
131 220 : return m_poParent->GetDimensions();
132 : }
133 :
134 103 : const GDALExtendedDataType &GetDataType() const override
135 : {
136 103 : return m_dt;
137 : }
138 :
139 1 : const std::string &GetUnit() const override
140 : {
141 1 : return m_poParent->GetUnit();
142 : }
143 :
144 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
145 : {
146 1 : return m_poParent->GetSpatialRef();
147 : }
148 :
149 6 : const void *GetRawNoDataValue() const override
150 : {
151 6 : return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
152 : }
153 :
154 1 : bool SetRawNoDataValue(const void *pRawNoData) override
155 : {
156 1 : m_bHasNoData = true;
157 1 : memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
158 1 : return true;
159 : }
160 :
161 4 : std::vector<GUInt64> GetBlockSize() const override
162 : {
163 4 : return m_poParent->GetBlockSize();
164 : }
165 :
166 : std::shared_ptr<GDALAttribute>
167 0 : GetAttribute(const std::string &osName) const override
168 : {
169 0 : return m_poParent->GetAttribute(osName);
170 : }
171 :
172 : std::vector<std::shared_ptr<GDALAttribute>>
173 1 : GetAttributes(CSLConstList papszOptions = nullptr) const override
174 : {
175 1 : return m_poParent->GetAttributes(papszOptions);
176 : }
177 :
178 0 : bool SetUnit(const std::string &osUnit) override
179 : {
180 0 : return m_poParent->SetUnit(osUnit);
181 : }
182 :
183 0 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override
184 : {
185 0 : return m_poParent->SetSpatialRef(poSRS);
186 : }
187 :
188 : std::shared_ptr<GDALAttribute>
189 1 : CreateAttribute(const std::string &osName,
190 : const std::vector<GUInt64> &anDimensions,
191 : const GDALExtendedDataType &oDataType,
192 : CSLConstList papszOptions = nullptr) override
193 : {
194 1 : return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
195 1 : papszOptions);
196 : }
197 : };
198 :
199 : /************************************************************************/
200 : /* ~GDALIHasAttribute() */
201 : /************************************************************************/
202 :
203 : GDALIHasAttribute::~GDALIHasAttribute() = default;
204 :
205 : /************************************************************************/
206 : /* GetAttribute() */
207 : /************************************************************************/
208 :
209 : /** Return an attribute by its name.
210 : *
211 : * If the attribute does not exist, nullptr should be silently returned.
212 : *
213 : * @note Driver implementation: this method will fallback to
214 : * GetAttributeFromAttributes() is not explicitly implemented
215 : *
216 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
217 : *
218 : * This is the same as the C function GDALGroupGetAttribute() or
219 : * GDALMDArrayGetAttribute().
220 : *
221 : * @param osName Attribute name
222 : * @return the attribute, or nullptr if it does not exist or an error occurred.
223 : */
224 : std::shared_ptr<GDALAttribute>
225 712 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
226 : {
227 712 : return GetAttributeFromAttributes(osName);
228 : }
229 :
230 : /************************************************************************/
231 : /* GetAttributeFromAttributes() */
232 : /************************************************************************/
233 :
234 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
235 : */
236 : std::shared_ptr<GDALAttribute>
237 712 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
238 : {
239 1424 : auto attrs(GetAttributes());
240 3921 : for (const auto &attr : attrs)
241 : {
242 3707 : if (attr->GetName() == osName)
243 498 : return attr;
244 : }
245 214 : return nullptr;
246 : }
247 :
248 : /************************************************************************/
249 : /* GetAttributes() */
250 : /************************************************************************/
251 :
252 : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
253 : *
254 : * If the attribute does not exist, nullptr should be silently returned.
255 : *
256 : * @note Driver implementation: optionally implemented. If implemented,
257 : * GetAttribute() should also be implemented.
258 : *
259 : * Drivers known to implement it for groups and arrays: MEM, netCDF.
260 : *
261 : * This is the same as the C function GDALGroupGetAttributes() or
262 : * GDALMDArrayGetAttributes().
263 :
264 : * @param papszOptions Driver specific options determining how attributes
265 : * should be retrieved. Pass nullptr for default behavior.
266 : *
267 : * @return the attributes.
268 : */
269 : std::vector<std::shared_ptr<GDALAttribute>>
270 23 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
271 : {
272 23 : return {};
273 : }
274 :
275 : /************************************************************************/
276 : /* CreateAttribute() */
277 : /************************************************************************/
278 :
279 : /** Create an attribute within a GDALMDArray or GDALGroup.
280 : *
281 : * The attribute might not be "physically" created until a value is written
282 : * into it.
283 : *
284 : * Optionally implemented.
285 : *
286 : * Drivers known to implement it: MEM, netCDF
287 : *
288 : * This is the same as the C function GDALGroupCreateAttribute() or
289 : * GDALMDArrayCreateAttribute()
290 : *
291 : * @param osName Attribute name.
292 : * @param anDimensions List of dimension sizes, ordered from the slowest varying
293 : * dimension first to the fastest varying dimension last.
294 : * Empty for a scalar attribute (common case)
295 : * @param oDataType Attribute data type.
296 : * @param papszOptions Driver specific options determining how the attribute.
297 : * should be created.
298 : *
299 : * @return the new attribute, or nullptr if case of error
300 : */
301 0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
302 : CPL_UNUSED const std::string &osName,
303 : CPL_UNUSED const std::vector<GUInt64> &anDimensions,
304 : CPL_UNUSED const GDALExtendedDataType &oDataType,
305 : CPL_UNUSED CSLConstList papszOptions)
306 : {
307 0 : CPLError(CE_Failure, CPLE_NotSupported,
308 : "CreateAttribute() not implemented");
309 0 : return nullptr;
310 : }
311 :
312 : /************************************************************************/
313 : /* DeleteAttribute() */
314 : /************************************************************************/
315 :
316 : /** Delete an attribute from a GDALMDArray or GDALGroup.
317 : *
318 : * Optionally implemented.
319 : *
320 : * After this call, if a previously obtained instance of the deleted object
321 : * is still alive, no method other than for freeing it should be invoked.
322 : *
323 : * Drivers known to implement it: MEM, netCDF
324 : *
325 : * This is the same as the C function GDALGroupDeleteAttribute() or
326 : * GDALMDArrayDeleteAttribute()
327 : *
328 : * @param osName Attribute name.
329 : * @param papszOptions Driver specific options determining how the attribute.
330 : * should be deleted.
331 : *
332 : * @return true in case of success
333 : * @since GDAL 3.8
334 : */
335 0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
336 : CPL_UNUSED CSLConstList papszOptions)
337 : {
338 0 : CPLError(CE_Failure, CPLE_NotSupported,
339 : "DeleteAttribute() not implemented");
340 0 : return false;
341 : }
342 :
343 : /************************************************************************/
344 : /* GDALGroup() */
345 : /************************************************************************/
346 :
347 : //! @cond Doxygen_Suppress
348 6451 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
349 6451 : const std::string &osContext)
350 6451 : : m_osName(osParentName.empty() ? "/" : osName),
351 : m_osFullName(
352 12902 : !osParentName.empty()
353 9925 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
354 : : "/"),
355 16376 : m_osContext(osContext)
356 : {
357 6451 : }
358 :
359 : //! @endcond
360 :
361 : /************************************************************************/
362 : /* ~GDALGroup() */
363 : /************************************************************************/
364 :
365 : GDALGroup::~GDALGroup() = default;
366 :
367 : /************************************************************************/
368 : /* GetMDArrayNames() */
369 : /************************************************************************/
370 :
371 : /** Return the list of multidimensional array names contained in this group.
372 : *
373 : * @note Driver implementation: optionally implemented. If implemented,
374 : * OpenMDArray() should also be implemented.
375 : *
376 : * Drivers known to implement it: MEM, netCDF.
377 : *
378 : * This is the same as the C function GDALGroupGetMDArrayNames().
379 : *
380 : * @param papszOptions Driver specific options determining how arrays
381 : * should be retrieved. Pass nullptr for default behavior.
382 : *
383 : * @return the array names.
384 : */
385 : std::vector<std::string>
386 0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
387 : {
388 0 : return {};
389 : }
390 :
391 : /************************************************************************/
392 : /* OpenMDArray() */
393 : /************************************************************************/
394 :
395 : /** Open and return a multidimensional array.
396 : *
397 : * @note Driver implementation: optionally implemented. If implemented,
398 : * GetMDArrayNames() should also be implemented.
399 : *
400 : * Drivers known to implement it: MEM, netCDF.
401 : *
402 : * This is the same as the C function GDALGroupOpenMDArray().
403 : *
404 : * @param osName Array name.
405 : * @param papszOptions Driver specific options determining how the array should
406 : * be opened. Pass nullptr for default behavior.
407 : *
408 : * @return the array, or nullptr.
409 : */
410 : std::shared_ptr<GDALMDArray>
411 0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
412 : CPL_UNUSED CSLConstList papszOptions) const
413 : {
414 0 : return nullptr;
415 : }
416 :
417 : /************************************************************************/
418 : /* GetGroupNames() */
419 : /************************************************************************/
420 :
421 : /** Return the list of sub-groups contained in this group.
422 : *
423 : * @note Driver implementation: optionally implemented. If implemented,
424 : * OpenGroup() should also be implemented.
425 : *
426 : * Drivers known to implement it: MEM, netCDF.
427 : *
428 : * This is the same as the C function GDALGroupGetGroupNames().
429 : *
430 : * @param papszOptions Driver specific options determining how groups
431 : * should be retrieved. Pass nullptr for default behavior.
432 : *
433 : * @return the group names.
434 : */
435 : std::vector<std::string>
436 4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
437 : {
438 4 : return {};
439 : }
440 :
441 : /************************************************************************/
442 : /* OpenGroup() */
443 : /************************************************************************/
444 :
445 : /** Open and return a sub-group.
446 : *
447 : * @note Driver implementation: optionally implemented. If implemented,
448 : * GetGroupNames() should also be implemented.
449 : *
450 : * Drivers known to implement it: MEM, netCDF.
451 : *
452 : * This is the same as the C function GDALGroupOpenGroup().
453 : *
454 : * @param osName Sub-group name.
455 : * @param papszOptions Driver specific options determining how the sub-group
456 : * should be opened. Pass nullptr for default behavior.
457 : *
458 : * @return the group, or nullptr.
459 : */
460 : std::shared_ptr<GDALGroup>
461 4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
462 : CPL_UNUSED CSLConstList papszOptions) const
463 : {
464 4 : return nullptr;
465 : }
466 :
467 : /************************************************************************/
468 : /* GetVectorLayerNames() */
469 : /************************************************************************/
470 :
471 : /** Return the list of layer names contained in this group.
472 : *
473 : * @note Driver implementation: optionally implemented. If implemented,
474 : * OpenVectorLayer() should also be implemented.
475 : *
476 : * Drivers known to implement it: OpenFileGDB, FileGDB
477 : *
478 : * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
479 : * GDALDataset::GetLayer() should then be used.
480 : *
481 : * This is the same as the C function GDALGroupGetVectorLayerNames().
482 : *
483 : * @param papszOptions Driver specific options determining how layers
484 : * should be retrieved. Pass nullptr for default behavior.
485 : *
486 : * @return the vector layer names.
487 : * @since GDAL 3.4
488 : */
489 : std::vector<std::string>
490 1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
491 : {
492 1 : return {};
493 : }
494 :
495 : /************************************************************************/
496 : /* OpenVectorLayer() */
497 : /************************************************************************/
498 :
499 : /** Open and return a vector layer.
500 : *
501 : * Due to the historical ownership of OGRLayer* by GDALDataset*, the
502 : * lifetime of the returned OGRLayer* is linked to the one of the owner
503 : * dataset (contrary to the general design of this class where objects can be
504 : * used independently of the object that returned them)
505 : *
506 : * @note Driver implementation: optionally implemented. If implemented,
507 : * GetVectorLayerNames() should also be implemented.
508 : *
509 : * Drivers known to implement it: MEM, netCDF.
510 : *
511 : * This is the same as the C function GDALGroupOpenVectorLayer().
512 : *
513 : * @param osName Vector layer name.
514 : * @param papszOptions Driver specific options determining how the layer should
515 : * be opened. Pass nullptr for default behavior.
516 : *
517 : * @return the group, or nullptr.
518 : */
519 2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
520 : CPL_UNUSED CSLConstList papszOptions) const
521 : {
522 2 : return nullptr;
523 : }
524 :
525 : /************************************************************************/
526 : /* GetDimensions() */
527 : /************************************************************************/
528 :
529 : /** Return the list of dimensions contained in this group and used by its
530 : * arrays.
531 : *
532 : * This is for dimensions that can potentially be used by several arrays.
533 : * Not all drivers might implement this. To retrieve the dimensions used by
534 : * a specific array, use GDALMDArray::GetDimensions().
535 : *
536 : * Drivers known to implement it: MEM, netCDF
537 : *
538 : * This is the same as the C function GDALGroupGetDimensions().
539 : *
540 : * @param papszOptions Driver specific options determining how groups
541 : * should be retrieved. Pass nullptr for default behavior.
542 : *
543 : * @return the dimensions.
544 : */
545 : std::vector<std::shared_ptr<GDALDimension>>
546 11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
547 : {
548 11 : return {};
549 : }
550 :
551 : /************************************************************************/
552 : /* GetStructuralInfo() */
553 : /************************************************************************/
554 :
555 : /** Return structural information on the group.
556 : *
557 : * This may be the compression, etc..
558 : *
559 : * The return value should not be freed and is valid until GDALGroup is
560 : * released or this function called again.
561 : *
562 : * This is the same as the C function GDALGroupGetStructuralInfo().
563 : */
564 29 : CSLConstList GDALGroup::GetStructuralInfo() const
565 : {
566 29 : return nullptr;
567 : }
568 :
569 : /************************************************************************/
570 : /* CreateGroup() */
571 : /************************************************************************/
572 :
573 : /** Create a sub-group within a group.
574 : *
575 : * Optionally implemented by drivers.
576 : *
577 : * Drivers known to implement it: MEM, netCDF
578 : *
579 : * This is the same as the C function GDALGroupCreateGroup().
580 : *
581 : * @param osName Sub-group name.
582 : * @param papszOptions Driver specific options determining how the sub-group
583 : * should be created.
584 : *
585 : * @return the new sub-group, or nullptr in case of error.
586 : */
587 : std::shared_ptr<GDALGroup>
588 0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
589 : CPL_UNUSED CSLConstList papszOptions)
590 : {
591 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
592 0 : return nullptr;
593 : }
594 :
595 : /************************************************************************/
596 : /* DeleteGroup() */
597 : /************************************************************************/
598 :
599 : /** Delete a sub-group from a group.
600 : *
601 : * Optionally implemented.
602 : *
603 : * After this call, if a previously obtained instance of the deleted object
604 : * is still alive, no method other than for freeing it should be invoked.
605 : *
606 : * Drivers known to implement it: MEM, Zarr
607 : *
608 : * This is the same as the C function GDALGroupDeleteGroup().
609 : *
610 : * @param osName Sub-group name.
611 : * @param papszOptions Driver specific options determining how the group.
612 : * should be deleted.
613 : *
614 : * @return true in case of success
615 : * @since GDAL 3.8
616 : */
617 0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
618 : CPL_UNUSED CSLConstList papszOptions)
619 : {
620 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
621 0 : return false;
622 : }
623 :
624 : /************************************************************************/
625 : /* CreateDimension() */
626 : /************************************************************************/
627 :
628 : /** Create a dimension within a group.
629 : *
630 : * @note Driver implementation: drivers supporting CreateDimension() should
631 : * implement this method, but do not have necessarily to implement
632 : * GDALGroup::GetDimensions().
633 : *
634 : * Drivers known to implement it: MEM, netCDF
635 : *
636 : * This is the same as the C function GDALGroupCreateDimension().
637 : *
638 : * @param osName Dimension name.
639 : * @param osType Dimension type (might be empty, and ignored by drivers)
640 : * @param osDirection Dimension direction (might be empty, and ignored by
641 : * drivers)
642 : * @param nSize Number of values indexed by this dimension. Should be > 0.
643 : * @param papszOptions Driver specific options determining how the dimension
644 : * should be created.
645 : *
646 : * @return the new dimension, or nullptr if case of error
647 : */
648 0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
649 : CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
650 : CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
651 : CPL_UNUSED CSLConstList papszOptions)
652 : {
653 0 : CPLError(CE_Failure, CPLE_NotSupported,
654 : "CreateDimension() not implemented");
655 0 : return nullptr;
656 : }
657 :
658 : /************************************************************************/
659 : /* CreateMDArray() */
660 : /************************************************************************/
661 :
662 : /** Create a multidimensional array within a group.
663 : *
664 : * It is recommended that the GDALDimension objects passed in aoDimensions
665 : * belong to this group, either by retrieving them with GetDimensions()
666 : * or creating a new one with CreateDimension().
667 : *
668 : * Optionally implemented.
669 : *
670 : * Drivers known to implement it: MEM, netCDF
671 : *
672 : * This is the same as the C function GDALGroupCreateMDArray().
673 : *
674 : * @note Driver implementation: drivers should take into account the possibility
675 : * that GDALDimension object passed in aoDimensions might belong to a different
676 : * group / dataset / driver and act accordingly.
677 : *
678 : * @param osName Array name.
679 : * @param aoDimensions List of dimensions, ordered from the slowest varying
680 : * dimension first to the fastest varying dimension last.
681 : * Might be empty for a scalar array (if supported by
682 : * driver)
683 : * @param oDataType Array data type.
684 : * @param papszOptions Driver specific options determining how the array
685 : * should be created.
686 : *
687 : * @return the new array, or nullptr if case of error
688 : */
689 0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
690 : CPL_UNUSED const std::string &osName,
691 : CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
692 : CPL_UNUSED const GDALExtendedDataType &oDataType,
693 : CPL_UNUSED CSLConstList papszOptions)
694 : {
695 0 : CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
696 0 : return nullptr;
697 : }
698 :
699 : /************************************************************************/
700 : /* DeleteMDArray() */
701 : /************************************************************************/
702 :
703 : /** Delete an array from a group.
704 : *
705 : * Optionally implemented.
706 : *
707 : * After this call, if a previously obtained instance of the deleted object
708 : * is still alive, no method other than for freeing it should be invoked.
709 : *
710 : * Drivers known to implement it: MEM, Zarr
711 : *
712 : * This is the same as the C function GDALGroupDeleteMDArray().
713 : *
714 : * @param osName Arrayname.
715 : * @param papszOptions Driver specific options determining how the array.
716 : * should be deleted.
717 : *
718 : * @return true in case of success
719 : * @since GDAL 3.8
720 : */
721 0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
722 : CPL_UNUSED CSLConstList papszOptions)
723 : {
724 0 : CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
725 0 : return false;
726 : }
727 :
728 : /************************************************************************/
729 : /* GetTotalCopyCost() */
730 : /************************************************************************/
731 :
732 : /** Return a total "cost" to copy the group.
733 : *
734 : * Used as a parameter for CopFrom()
735 : */
736 22 : GUInt64 GDALGroup::GetTotalCopyCost() const
737 : {
738 22 : GUInt64 nCost = COPY_COST;
739 22 : nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
740 :
741 44 : auto groupNames = GetGroupNames();
742 26 : for (const auto &name : groupNames)
743 : {
744 8 : auto subGroup = OpenGroup(name);
745 4 : if (subGroup)
746 : {
747 4 : nCost += subGroup->GetTotalCopyCost();
748 : }
749 : }
750 :
751 22 : auto arrayNames = GetMDArrayNames();
752 61 : for (const auto &name : arrayNames)
753 : {
754 78 : auto array = OpenMDArray(name);
755 39 : if (array)
756 : {
757 39 : nCost += array->GetTotalCopyCost();
758 : }
759 : }
760 44 : return nCost;
761 : }
762 :
763 : /************************************************************************/
764 : /* CopyFrom() */
765 : /************************************************************************/
766 :
767 : /** Copy the content of a group into a new (generally empty) group.
768 : *
769 : * @param poDstRootGroup Destination root group. Must NOT be nullptr.
770 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
771 : * of some output drivers this is not recommended)
772 : * @param poSrcGroup Source group. Must NOT be nullptr.
773 : * @param bStrict Whether to enable stict mode. In strict mode, any error will
774 : * stop the copy. In relaxed mode, the copy will be attempted to
775 : * be pursued.
776 : * @param nCurCost Should be provided as a variable initially set to 0.
777 : * @param nTotalCost Total cost from GetTotalCopyCost().
778 : * @param pfnProgress Progress callback, or nullptr.
779 : * @param pProgressData Progress user data, or nulptr.
780 : * @param papszOptions Creation options. Currently, only array creation
781 : * options are supported. They must be prefixed with
782 : * "ARRAY:" . The scope may be further restricted to arrays of a certain
783 : * dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
784 : * For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
785 : * restrict BLOCKSIZE=256,256 to arrays of dimension 2.
786 : * Restriction to arrays of a given name is done with adding
787 : * "IF(NAME={name}):" after "ARRAY:". {name} can also be
788 : * a full qualified name.
789 : * A non-driver specific ARRAY option, "AUTOSCALE=YES" can
790 : * be used to ask (non indexing) variables of type Float32 or Float64 to be
791 : * scaled to UInt16 with scale and offset values being computed from the minimum
792 : * and maximum of the source array. The integer data type used can be set with
793 : * AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
794 : *
795 : * @return true in case of success (or partial success if bStrict == false).
796 : */
797 22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
798 : GDALDataset *poSrcDS,
799 : const std::shared_ptr<GDALGroup> &poSrcGroup,
800 : bool bStrict, GUInt64 &nCurCost,
801 : const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
802 : void *pProgressData, CSLConstList papszOptions)
803 : {
804 22 : if (pfnProgress == nullptr)
805 0 : pfnProgress = GDALDummyProgress;
806 :
807 : #define EXIT_OR_CONTINUE_IF_NULL(x) \
808 : if (!(x)) \
809 : { \
810 : if (bStrict) \
811 : return false; \
812 : continue; \
813 : } \
814 : (void)0
815 :
816 : try
817 : {
818 22 : nCurCost += GDALGroup::COPY_COST;
819 :
820 44 : const auto srcDims = poSrcGroup->GetDimensions();
821 : std::map<std::string, std::shared_ptr<GDALDimension>>
822 44 : mapExistingDstDims;
823 44 : std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
824 56 : for (const auto &dim : srcDims)
825 : {
826 : auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
827 34 : dim->GetDirection(), dim->GetSize());
828 34 : EXIT_OR_CONTINUE_IF_NULL(dstDim);
829 34 : mapExistingDstDims[dim->GetName()] = std::move(dstDim);
830 68 : auto poIndexingVarSrc(dim->GetIndexingVariable());
831 34 : if (poIndexingVarSrc)
832 : {
833 : mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
834 16 : ->GetName()] =
835 32 : dim->GetName();
836 : }
837 : }
838 :
839 44 : auto attrs = poSrcGroup->GetAttributes();
840 28 : for (const auto &attr : attrs)
841 : {
842 : auto dstAttr =
843 6 : CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
844 12 : attr->GetDataType());
845 6 : EXIT_OR_CONTINUE_IF_NULL(dstAttr);
846 6 : auto raw(attr->ReadAsRaw());
847 6 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
848 0 : return false;
849 : }
850 22 : if (!attrs.empty())
851 : {
852 4 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
853 4 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
854 0 : return false;
855 : }
856 :
857 : const auto CopyArray =
858 39 : [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
859 : &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
860 : papszOptions, bStrict, &nCurCost,
861 356 : nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
862 : {
863 : // Map source dimensions to target dimensions
864 78 : std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
865 39 : const auto &srcArrayDims(srcArray->GetDimensions());
866 99 : for (const auto &dim : srcArrayDims)
867 : {
868 : auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
869 60 : dim->GetFullName());
870 60 : if (dstDim && dstDim->GetSize() == dim->GetSize())
871 : {
872 50 : dstArrayDims.emplace_back(dstDim);
873 : }
874 : else
875 : {
876 10 : auto oIter = mapExistingDstDims.find(dim->GetName());
877 19 : if (oIter != mapExistingDstDims.end() &&
878 9 : oIter->second->GetSize() == dim->GetSize())
879 : {
880 8 : dstArrayDims.emplace_back(oIter->second);
881 : }
882 : else
883 : {
884 2 : std::string newDimName;
885 2 : if (oIter == mapExistingDstDims.end())
886 : {
887 1 : newDimName = dim->GetName();
888 : }
889 : else
890 : {
891 1 : std::string newDimNamePrefix(srcArray->GetName() +
892 3 : '_' + dim->GetName());
893 1 : newDimName = newDimNamePrefix;
894 1 : int nIterCount = 2;
895 1 : while (mapExistingDstDims.find(newDimName) !=
896 2 : mapExistingDstDims.end())
897 : {
898 0 : newDimName = newDimNamePrefix +
899 0 : CPLSPrintf("_%d", nIterCount);
900 0 : nIterCount++;
901 : }
902 : }
903 4 : dstDim = CreateDimension(newDimName, dim->GetType(),
904 : dim->GetDirection(),
905 4 : dim->GetSize());
906 2 : if (!dstDim)
907 0 : return false;
908 2 : mapExistingDstDims[newDimName] = dstDim;
909 2 : dstArrayDims.emplace_back(dstDim);
910 : }
911 : }
912 : }
913 :
914 78 : CPLStringList aosArrayCO;
915 39 : bool bAutoScale = false;
916 39 : GDALDataType eAutoScaleType = GDT_UInt16;
917 46 : for (const char *pszItem : cpl::Iterate(papszOptions))
918 : {
919 7 : if (STARTS_WITH_CI(pszItem, "ARRAY:"))
920 : {
921 7 : const char *pszOption = pszItem + strlen("ARRAY:");
922 7 : if (STARTS_WITH_CI(pszOption, "IF(DIM="))
923 : {
924 1 : const char *pszNext = strchr(pszOption, ':');
925 1 : if (pszNext != nullptr)
926 : {
927 1 : int nDim = atoi(pszOption + strlen("IF(DIM="));
928 1 : if (static_cast<size_t>(nDim) ==
929 1 : dstArrayDims.size())
930 : {
931 1 : pszOption = pszNext + 1;
932 : }
933 : else
934 : {
935 0 : pszOption = nullptr;
936 : }
937 : }
938 : }
939 6 : else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
940 : {
941 2 : const char *pszName = pszOption + strlen("IF(NAME=");
942 2 : const char *pszNext = strchr(pszName, ':');
943 2 : if (pszNext != nullptr && pszNext > pszName &&
944 2 : pszNext[-1] == ')')
945 : {
946 4 : CPLString osName;
947 2 : osName.assign(pszName, pszNext - pszName - 1);
948 3 : if (osName == srcArray->GetName() ||
949 1 : osName == srcArray->GetFullName())
950 : {
951 2 : pszOption = pszNext + 1;
952 : }
953 : else
954 : {
955 0 : pszOption = nullptr;
956 : }
957 : }
958 : }
959 7 : if (pszOption)
960 : {
961 7 : if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
962 : {
963 : bAutoScale =
964 2 : CPLTestBool(pszOption + strlen("AUTOSCALE="));
965 : }
966 5 : else if (STARTS_WITH_CI(pszOption,
967 : "AUTOSCALE_DATA_TYPE="))
968 : {
969 1 : const char *pszDataType =
970 : pszOption + strlen("AUTOSCALE_DATA_TYPE=");
971 1 : eAutoScaleType = GDALGetDataTypeByName(pszDataType);
972 2 : if (GDALDataTypeIsComplex(eAutoScaleType) ||
973 1 : GDALDataTypeIsFloating(eAutoScaleType))
974 : {
975 0 : CPLError(CE_Failure, CPLE_NotSupported,
976 : "Unsupported value for "
977 : "AUTOSCALE_DATA_TYPE");
978 0 : return false;
979 : }
980 : }
981 : else
982 : {
983 4 : aosArrayCO.AddString(pszOption);
984 : }
985 : }
986 : }
987 : }
988 :
989 : auto oIterDimName =
990 39 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
991 39 : const auto &srcArrayType = srcArray->GetDataType();
992 :
993 39 : std::shared_ptr<GDALMDArray> dstArray;
994 :
995 : // Only autoscale non-indexing variables
996 39 : bool bHasOffset = false;
997 39 : bool bHasScale = false;
998 4 : if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
999 2 : (srcArrayType.GetNumericDataType() == GDT_Float32 ||
1000 0 : srcArrayType.GetNumericDataType() == GDT_Float64) &&
1001 2 : srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1002 43 : srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1003 41 : oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1004 : {
1005 2 : constexpr bool bApproxOK = false;
1006 2 : constexpr bool bForce = true;
1007 2 : double dfMin = 0.0;
1008 2 : double dfMax = 0.0;
1009 2 : if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1010 : nullptr, nullptr, nullptr, nullptr,
1011 2 : nullptr) != CE_None)
1012 : {
1013 0 : CPLError(CE_Failure, CPLE_AppDefined,
1014 : "Could not retrieve statistics for array %s",
1015 0 : srcArray->GetName().c_str());
1016 0 : return false;
1017 : }
1018 2 : double dfDTMin = 0;
1019 2 : double dfDTMax = 0;
1020 : #define setDTMinMax(ctype) \
1021 : do \
1022 : { \
1023 : dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min()); \
1024 : dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max()); \
1025 : } while (0)
1026 :
1027 2 : switch (eAutoScaleType)
1028 : {
1029 0 : case GDT_Byte:
1030 0 : setDTMinMax(GByte);
1031 0 : break;
1032 0 : case GDT_Int8:
1033 0 : setDTMinMax(GInt8);
1034 0 : break;
1035 1 : case GDT_UInt16:
1036 1 : setDTMinMax(GUInt16);
1037 1 : break;
1038 1 : case GDT_Int16:
1039 1 : setDTMinMax(GInt16);
1040 1 : break;
1041 0 : case GDT_UInt32:
1042 0 : setDTMinMax(GUInt32);
1043 0 : break;
1044 0 : case GDT_Int32:
1045 0 : setDTMinMax(GInt32);
1046 0 : break;
1047 0 : case GDT_UInt64:
1048 0 : setDTMinMax(std::uint64_t);
1049 0 : break;
1050 0 : case GDT_Int64:
1051 0 : setDTMinMax(std::int64_t);
1052 0 : break;
1053 0 : case GDT_Float32:
1054 : case GDT_Float64:
1055 : case GDT_Unknown:
1056 : case GDT_CInt16:
1057 : case GDT_CInt32:
1058 : case GDT_CFloat32:
1059 : case GDT_CFloat64:
1060 : case GDT_TypeCount:
1061 0 : CPLAssert(false);
1062 : }
1063 :
1064 : dstArray =
1065 4 : CreateMDArray(srcArray->GetName(), dstArrayDims,
1066 4 : GDALExtendedDataType::Create(eAutoScaleType),
1067 4 : aosArrayCO.List());
1068 2 : if (!dstArray)
1069 0 : return !bStrict;
1070 :
1071 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1072 : {
1073 : // If there's a nodata value in the source array, reserve
1074 : // DTMax for that purpose in the target scaled array
1075 1 : if (!dstArray->SetNoDataValue(dfDTMax))
1076 : {
1077 0 : CPLError(CE_Failure, CPLE_AppDefined,
1078 : "Cannot set nodata value");
1079 0 : return false;
1080 : }
1081 1 : dfDTMax--;
1082 : }
1083 2 : const double dfScale =
1084 2 : dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1085 2 : const double dfOffset = dfMin - dfDTMin * dfScale;
1086 :
1087 4 : if (!dstArray->SetOffset(dfOffset) ||
1088 2 : !dstArray->SetScale(dfScale))
1089 : {
1090 0 : CPLError(CE_Failure, CPLE_AppDefined,
1091 : "Cannot set scale/offset");
1092 0 : return false;
1093 : }
1094 :
1095 2 : auto poUnscaled = dstArray->GetUnscaled();
1096 2 : if (srcArray->GetRawNoDataValue() != nullptr)
1097 : {
1098 1 : poUnscaled->SetNoDataValue(
1099 : srcArray->GetNoDataValueAsDouble());
1100 : }
1101 :
1102 : // Copy source array into unscaled array
1103 4 : if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1104 : nCurCost, nTotalCost, pfnProgress,
1105 2 : pProgressData))
1106 : {
1107 0 : return false;
1108 : }
1109 : }
1110 : else
1111 : {
1112 74 : dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1113 74 : srcArrayType, aosArrayCO.List());
1114 37 : if (!dstArray)
1115 0 : return !bStrict;
1116 :
1117 74 : if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1118 : nCurCost, nTotalCost, pfnProgress,
1119 37 : pProgressData))
1120 : {
1121 0 : return false;
1122 : }
1123 : }
1124 :
1125 : // If this array is the indexing variable of a dimension, link them
1126 : // together.
1127 39 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1128 : {
1129 : auto oCorrespondingDimIter =
1130 16 : mapExistingDstDims.find(oIterDimName->second);
1131 16 : if (oCorrespondingDimIter != mapExistingDstDims.end())
1132 : {
1133 : CPLErrorStateBackuper oErrorStateBackuper(
1134 16 : CPLQuietErrorHandler);
1135 32 : oCorrespondingDimIter->second->SetIndexingVariable(
1136 16 : std::move(dstArray));
1137 : }
1138 : }
1139 :
1140 39 : return true;
1141 22 : };
1142 :
1143 44 : const auto arrayNames = poSrcGroup->GetMDArrayNames();
1144 :
1145 : // Start by copying arrays that are indexing variables of dimensions
1146 61 : for (const auto &name : arrayNames)
1147 : {
1148 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1149 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1150 :
1151 : const auto oIterDimName =
1152 39 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1153 39 : if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1154 : {
1155 16 : if (!CopyArray(srcArray))
1156 0 : return false;
1157 : }
1158 : }
1159 :
1160 : // Then copy regular arrays
1161 61 : for (const auto &name : arrayNames)
1162 : {
1163 39 : auto srcArray = poSrcGroup->OpenMDArray(name);
1164 39 : EXIT_OR_CONTINUE_IF_NULL(srcArray);
1165 :
1166 : const auto oIterDimName =
1167 39 : mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1168 39 : if (oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1169 : {
1170 23 : if (!CopyArray(srcArray))
1171 0 : return false;
1172 : }
1173 : }
1174 :
1175 44 : const auto groupNames = poSrcGroup->GetGroupNames();
1176 26 : for (const auto &name : groupNames)
1177 : {
1178 4 : auto srcSubGroup = poSrcGroup->OpenGroup(name);
1179 4 : EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1180 4 : auto dstSubGroup = CreateGroup(name);
1181 4 : EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1182 8 : if (!dstSubGroup->CopyFrom(
1183 : poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1184 4 : nTotalCost, pfnProgress, pProgressData, papszOptions))
1185 0 : return false;
1186 : }
1187 :
1188 22 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1189 0 : return false;
1190 :
1191 22 : return true;
1192 : }
1193 0 : catch (const std::exception &e)
1194 : {
1195 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1196 0 : return false;
1197 : }
1198 : }
1199 :
1200 : /************************************************************************/
1201 : /* GetInnerMostGroup() */
1202 : /************************************************************************/
1203 :
1204 : //! @cond Doxygen_Suppress
1205 : const GDALGroup *
1206 989 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1207 : std::shared_ptr<GDALGroup> &curGroupHolder,
1208 : std::string &osLastPart) const
1209 : {
1210 989 : if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1211 6 : return nullptr;
1212 983 : const GDALGroup *poCurGroup = this;
1213 : CPLStringList aosTokens(
1214 1966 : CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1215 983 : if (aosTokens.size() == 0)
1216 : {
1217 0 : return nullptr;
1218 : }
1219 :
1220 1297 : for (int i = 0; i < aosTokens.size() - 1; i++)
1221 : {
1222 317 : curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1223 317 : if (!curGroupHolder)
1224 : {
1225 3 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1226 : aosTokens[i]);
1227 3 : return nullptr;
1228 : }
1229 314 : poCurGroup = curGroupHolder.get();
1230 : }
1231 980 : osLastPart = aosTokens[aosTokens.size() - 1];
1232 980 : return poCurGroup;
1233 : }
1234 :
1235 : //! @endcond
1236 :
1237 : /************************************************************************/
1238 : /* OpenMDArrayFromFullname() */
1239 : /************************************************************************/
1240 :
1241 : /** Get an array from its fully qualified name */
1242 : std::shared_ptr<GDALMDArray>
1243 336 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1244 : CSLConstList papszOptions) const
1245 : {
1246 672 : std::string osName;
1247 336 : std::shared_ptr<GDALGroup> curGroupHolder;
1248 336 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1249 336 : if (poGroup == nullptr)
1250 5 : return nullptr;
1251 331 : return poGroup->OpenMDArray(osName, papszOptions);
1252 : }
1253 :
1254 : /************************************************************************/
1255 : /* ResolveMDArray() */
1256 : /************************************************************************/
1257 :
1258 : /** Locate an array in a group and its subgroups by name.
1259 : *
1260 : * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1261 : * used
1262 : * Otherwise the search will start from the group identified by osStartingPath,
1263 : * and an array whose name is osName will be looked for in this group (if
1264 : * osStartingPath is empty or "/", then the current group is used). If there
1265 : * is no match, then a recursive descendent search will be made in its
1266 : * subgroups. If there is no match in the subgroups, then the parent (if
1267 : * existing) of the group pointed by osStartingPath will be used as the new
1268 : * starting point for the search.
1269 : *
1270 : * @param osName name, qualified or not
1271 : * @param osStartingPath fully qualified name of the (sub-)group from which
1272 : * the search should be started. If this is a non-empty
1273 : * string, the group on which this method is called should
1274 : * nominally be the root group (otherwise the path will
1275 : * be interpreted as from the current group)
1276 : * @param papszOptions options to pass to OpenMDArray()
1277 : * @since GDAL 3.2
1278 : */
1279 : std::shared_ptr<GDALMDArray>
1280 19 : GDALGroup::ResolveMDArray(const std::string &osName,
1281 : const std::string &osStartingPath,
1282 : CSLConstList papszOptions) const
1283 : {
1284 19 : if (!osName.empty() && osName[0] == '/')
1285 : {
1286 1 : auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1287 1 : if (poArray)
1288 1 : return poArray;
1289 : }
1290 36 : std::string osPath(osStartingPath);
1291 36 : std::set<std::string> oSetAlreadyVisited;
1292 :
1293 : while (true)
1294 : {
1295 0 : std::shared_ptr<GDALGroup> curGroupHolder;
1296 0 : std::shared_ptr<GDALGroup> poGroup;
1297 :
1298 22 : std::queue<std::shared_ptr<GDALGroup>> oQueue;
1299 22 : bool goOn = false;
1300 22 : if (osPath.empty() || osPath == "/")
1301 : {
1302 11 : goOn = true;
1303 : }
1304 : else
1305 : {
1306 22 : std::string osLastPart;
1307 : const GDALGroup *poGroupPtr =
1308 11 : GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1309 11 : if (poGroupPtr)
1310 11 : poGroup = poGroupPtr->OpenGroup(osLastPart);
1311 22 : if (poGroup && oSetAlreadyVisited.find(poGroup->GetFullName()) ==
1312 33 : oSetAlreadyVisited.end())
1313 : {
1314 11 : oQueue.push(poGroup);
1315 11 : goOn = true;
1316 : }
1317 : }
1318 :
1319 22 : if (goOn)
1320 : {
1321 17 : do
1322 : {
1323 : const GDALGroup *groupPtr;
1324 39 : if (!oQueue.empty())
1325 : {
1326 28 : poGroup = oQueue.front();
1327 28 : oQueue.pop();
1328 28 : groupPtr = poGroup.get();
1329 : }
1330 : else
1331 : {
1332 11 : groupPtr = this;
1333 : }
1334 :
1335 39 : auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1336 39 : if (poArray)
1337 16 : return poArray;
1338 :
1339 46 : const auto aosGroupNames = groupPtr->GetGroupNames();
1340 47 : for (const auto &osGroupName : aosGroupNames)
1341 : {
1342 48 : auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1343 48 : if (poSubGroup &&
1344 24 : oSetAlreadyVisited.find(poSubGroup->GetFullName()) ==
1345 72 : oSetAlreadyVisited.end())
1346 : {
1347 24 : oQueue.push(poSubGroup);
1348 24 : oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1349 : }
1350 : }
1351 23 : } while (!oQueue.empty());
1352 : }
1353 :
1354 6 : if (osPath.empty() || osPath == "/")
1355 2 : break;
1356 :
1357 4 : const auto nPos = osPath.rfind('/');
1358 4 : if (nPos == 0)
1359 1 : osPath = "/";
1360 : else
1361 : {
1362 3 : if (nPos == std::string::npos)
1363 0 : break;
1364 3 : osPath.resize(nPos);
1365 : }
1366 4 : }
1367 2 : return nullptr;
1368 : }
1369 :
1370 : /************************************************************************/
1371 : /* OpenGroupFromFullname() */
1372 : /************************************************************************/
1373 :
1374 : /** Get a group from its fully qualified name.
1375 : * @since GDAL 3.2
1376 : */
1377 : std::shared_ptr<GDALGroup>
1378 517 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1379 : CSLConstList papszOptions) const
1380 : {
1381 1034 : std::string osName;
1382 517 : std::shared_ptr<GDALGroup> curGroupHolder;
1383 517 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1384 517 : if (poGroup == nullptr)
1385 2 : return nullptr;
1386 515 : return poGroup->OpenGroup(osName, papszOptions);
1387 : }
1388 :
1389 : /************************************************************************/
1390 : /* OpenDimensionFromFullname() */
1391 : /************************************************************************/
1392 :
1393 : /** Get a dimension from its fully qualified name */
1394 : std::shared_ptr<GDALDimension>
1395 125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1396 : {
1397 250 : std::string osName;
1398 125 : std::shared_ptr<GDALGroup> curGroupHolder;
1399 125 : auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1400 125 : if (poGroup == nullptr)
1401 2 : return nullptr;
1402 246 : auto dims(poGroup->GetDimensions());
1403 203 : for (auto &dim : dims)
1404 : {
1405 164 : if (dim->GetName() == osName)
1406 84 : return dim;
1407 : }
1408 39 : return nullptr;
1409 : }
1410 :
1411 : /************************************************************************/
1412 : /* ClearStatistics() */
1413 : /************************************************************************/
1414 :
1415 : /**
1416 : * \brief Clear statistics.
1417 : *
1418 : * @since GDAL 3.4
1419 : */
1420 0 : void GDALGroup::ClearStatistics()
1421 : {
1422 0 : auto groupNames = GetGroupNames();
1423 0 : for (const auto &name : groupNames)
1424 : {
1425 0 : auto subGroup = OpenGroup(name);
1426 0 : if (subGroup)
1427 : {
1428 0 : subGroup->ClearStatistics();
1429 : }
1430 : }
1431 :
1432 0 : auto arrayNames = GetMDArrayNames();
1433 0 : for (const auto &name : arrayNames)
1434 : {
1435 0 : auto array = OpenMDArray(name);
1436 0 : if (array)
1437 : {
1438 0 : array->ClearStatistics();
1439 : }
1440 : }
1441 0 : }
1442 :
1443 : /************************************************************************/
1444 : /* Rename() */
1445 : /************************************************************************/
1446 :
1447 : /** Rename the group.
1448 : *
1449 : * This is not implemented by all drivers.
1450 : *
1451 : * Drivers known to implement it: MEM, netCDF, ZARR.
1452 : *
1453 : * This is the same as the C function GDALGroupRename().
1454 : *
1455 : * @param osNewName New name.
1456 : *
1457 : * @return true in case of success
1458 : * @since GDAL 3.8
1459 : */
1460 0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1461 : {
1462 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1463 0 : return false;
1464 : }
1465 :
1466 : /************************************************************************/
1467 : /* BaseRename() */
1468 : /************************************************************************/
1469 :
1470 : //! @cond Doxygen_Suppress
1471 8 : void GDALGroup::BaseRename(const std::string &osNewName)
1472 : {
1473 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
1474 8 : m_osFullName += osNewName;
1475 8 : m_osName = osNewName;
1476 :
1477 8 : NotifyChildrenOfRenaming();
1478 8 : }
1479 :
1480 : //! @endcond
1481 :
1482 : /************************************************************************/
1483 : /* ParentRenamed() */
1484 : /************************************************************************/
1485 :
1486 : //! @cond Doxygen_Suppress
1487 7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1488 : {
1489 7 : m_osFullName = osNewParentFullName;
1490 7 : m_osFullName += "/";
1491 7 : m_osFullName += m_osName;
1492 :
1493 7 : NotifyChildrenOfRenaming();
1494 7 : }
1495 :
1496 : //! @endcond
1497 :
1498 : /************************************************************************/
1499 : /* Deleted() */
1500 : /************************************************************************/
1501 :
1502 : //! @cond Doxygen_Suppress
1503 22 : void GDALGroup::Deleted()
1504 : {
1505 22 : m_bValid = false;
1506 :
1507 22 : NotifyChildrenOfDeletion();
1508 22 : }
1509 :
1510 : //! @endcond
1511 :
1512 : /************************************************************************/
1513 : /* ParentDeleted() */
1514 : /************************************************************************/
1515 :
1516 : //! @cond Doxygen_Suppress
1517 3 : void GDALGroup::ParentDeleted()
1518 : {
1519 3 : Deleted();
1520 3 : }
1521 :
1522 : //! @endcond
1523 :
1524 : /************************************************************************/
1525 : /* CheckValidAndErrorOutIfNot() */
1526 : /************************************************************************/
1527 :
1528 : //! @cond Doxygen_Suppress
1529 11467 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
1530 : {
1531 11467 : if (!m_bValid)
1532 : {
1533 14 : CPLError(CE_Failure, CPLE_AppDefined,
1534 : "This object has been deleted. No action on it is possible");
1535 : }
1536 11467 : return m_bValid;
1537 : }
1538 :
1539 : //! @endcond
1540 :
1541 : /************************************************************************/
1542 : /* ~GDALAbstractMDArray() */
1543 : /************************************************************************/
1544 :
1545 : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1546 :
1547 : /************************************************************************/
1548 : /* GDALAbstractMDArray() */
1549 : /************************************************************************/
1550 :
1551 : //! @cond Doxygen_Suppress
1552 17871 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1553 17871 : const std::string &osName)
1554 : : m_osName(osName),
1555 : m_osFullName(
1556 17871 : !osParentName.empty()
1557 34193 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1558 69935 : : osName)
1559 : {
1560 17871 : }
1561 :
1562 : //! @endcond
1563 :
1564 : /************************************************************************/
1565 : /* GetDimensions() */
1566 : /************************************************************************/
1567 :
1568 : /** \fn GDALAbstractMDArray::GetDimensions() const
1569 : * \brief Return the dimensions of an attribute/array.
1570 : *
1571 : * This is the same as the C functions GDALMDArrayGetDimensions() and
1572 : * similar to GDALAttributeGetDimensionsSize().
1573 : */
1574 :
1575 : /************************************************************************/
1576 : /* GetDataType() */
1577 : /************************************************************************/
1578 :
1579 : /** \fn GDALAbstractMDArray::GetDataType() const
1580 : * \brief Return the data type of an attribute/array.
1581 : *
1582 : * This is the same as the C functions GDALMDArrayGetDataType() and
1583 : * GDALAttributeGetDataType()
1584 : */
1585 :
1586 : /************************************************************************/
1587 : /* GetDimensionCount() */
1588 : /************************************************************************/
1589 :
1590 : /** Return the number of dimensions.
1591 : *
1592 : * Default implementation is GetDimensions().size(), and may be overridden by
1593 : * drivers if they have a faster / less expensive implementations.
1594 : *
1595 : * This is the same as the C function GDALMDArrayGetDimensionCount() or
1596 : * GDALAttributeGetDimensionCount().
1597 : *
1598 : */
1599 21450 : size_t GDALAbstractMDArray::GetDimensionCount() const
1600 : {
1601 21450 : return GetDimensions().size();
1602 : }
1603 :
1604 : /************************************************************************/
1605 : /* Rename() */
1606 : /************************************************************************/
1607 :
1608 : /** Rename the attribute/array.
1609 : *
1610 : * This is not implemented by all drivers.
1611 : *
1612 : * Drivers known to implement it: MEM, netCDF, Zarr.
1613 : *
1614 : * This is the same as the C functions GDALMDArrayRename() or
1615 : * GDALAttributeRename().
1616 : *
1617 : * @param osNewName New name.
1618 : *
1619 : * @return true in case of success
1620 : * @since GDAL 3.8
1621 : */
1622 0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1623 : {
1624 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1625 0 : return false;
1626 : }
1627 :
1628 : /************************************************************************/
1629 : /* CopyValue() */
1630 : /************************************************************************/
1631 :
1632 : /** Convert a value from a source type to a destination type.
1633 : *
1634 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1635 : * that must be freed with CPLFree().
1636 : */
1637 77583 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
1638 : const GDALExtendedDataType &srcType,
1639 : void *pDst,
1640 : const GDALExtendedDataType &dstType)
1641 : {
1642 152162 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1643 74579 : dstType.GetClass() == GEDTC_NUMERIC)
1644 : {
1645 74389 : GDALCopyWords(pSrc, srcType.GetNumericDataType(), 0, pDst,
1646 : dstType.GetNumericDataType(), 0, 1);
1647 74389 : return true;
1648 : }
1649 6058 : if (srcType.GetClass() == GEDTC_STRING &&
1650 2864 : dstType.GetClass() == GEDTC_STRING)
1651 : {
1652 : const char *srcStrPtr;
1653 2480 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1654 2480 : char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1655 2480 : *reinterpret_cast<void **>(pDst) = pszDup;
1656 2480 : return true;
1657 : }
1658 904 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1659 190 : dstType.GetClass() == GEDTC_STRING)
1660 : {
1661 190 : const char *str = nullptr;
1662 190 : switch (srcType.GetNumericDataType())
1663 : {
1664 0 : case GDT_Unknown:
1665 0 : break;
1666 0 : case GDT_Byte:
1667 0 : str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1668 0 : break;
1669 3 : case GDT_Int8:
1670 3 : str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1671 3 : break;
1672 48 : case GDT_UInt16:
1673 48 : str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1674 48 : break;
1675 0 : case GDT_Int16:
1676 0 : str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1677 0 : break;
1678 8 : case GDT_UInt32:
1679 8 : str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1680 8 : break;
1681 36 : case GDT_Int32:
1682 36 : str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1683 36 : break;
1684 0 : case GDT_UInt64:
1685 : str =
1686 0 : CPLSPrintf(CPL_FRMT_GUIB,
1687 : static_cast<GUIntBig>(
1688 : *static_cast<const std::uint64_t *>(pSrc)));
1689 0 : break;
1690 0 : case GDT_Int64:
1691 0 : str = CPLSPrintf(CPL_FRMT_GIB,
1692 : static_cast<GIntBig>(
1693 : *static_cast<const std::int64_t *>(pSrc)));
1694 0 : break;
1695 17 : case GDT_Float32:
1696 17 : str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1697 17 : break;
1698 76 : case GDT_Float64:
1699 76 : str = CPLSPrintf("%.18g", *static_cast<const double *>(pSrc));
1700 76 : break;
1701 2 : case GDT_CInt16:
1702 : {
1703 2 : const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1704 2 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1705 2 : break;
1706 : }
1707 0 : case GDT_CInt32:
1708 : {
1709 0 : const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1710 0 : str = CPLSPrintf("%d+%dj", src[0], src[1]);
1711 0 : break;
1712 : }
1713 0 : case GDT_CFloat32:
1714 : {
1715 0 : const float *src = static_cast<const float *>(pSrc);
1716 0 : str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1717 0 : break;
1718 : }
1719 0 : case GDT_CFloat64:
1720 : {
1721 0 : const double *src = static_cast<const double *>(pSrc);
1722 0 : str = CPLSPrintf("%.18g+%.18gj", src[0], src[1]);
1723 0 : break;
1724 : }
1725 0 : case GDT_TypeCount:
1726 0 : CPLAssert(false);
1727 : break;
1728 : }
1729 190 : char *pszDup = str ? CPLStrdup(str) : nullptr;
1730 190 : *reinterpret_cast<void **>(pDst) = pszDup;
1731 190 : return true;
1732 : }
1733 908 : if (srcType.GetClass() == GEDTC_STRING &&
1734 384 : dstType.GetClass() == GEDTC_NUMERIC)
1735 : {
1736 : const char *srcStrPtr;
1737 384 : memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1738 384 : if (dstType.GetNumericDataType() == GDT_Int64)
1739 : {
1740 2 : *(static_cast<int64_t *>(pDst)) =
1741 2 : srcStrPtr == nullptr ? 0
1742 1 : : static_cast<int64_t>(atoll(srcStrPtr));
1743 : }
1744 382 : else if (dstType.GetNumericDataType() == GDT_UInt64)
1745 : {
1746 2 : *(static_cast<uint64_t *>(pDst)) =
1747 2 : srcStrPtr == nullptr
1748 2 : ? 0
1749 1 : : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1750 : }
1751 : else
1752 : {
1753 : // FIXME GDT_UInt64
1754 380 : const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1755 380 : GDALCopyWords(&dfVal, GDT_Float64, 0, pDst,
1756 : dstType.GetNumericDataType(), 0, 1);
1757 : }
1758 384 : return true;
1759 : }
1760 280 : if (srcType.GetClass() == GEDTC_COMPOUND &&
1761 140 : dstType.GetClass() == GEDTC_COMPOUND)
1762 : {
1763 140 : const auto &srcComponents = srcType.GetComponents();
1764 140 : const auto &dstComponents = dstType.GetComponents();
1765 140 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1766 140 : GByte *pabyDst = static_cast<GByte *>(pDst);
1767 :
1768 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1769 280 : srcComponentMap;
1770 562 : for (const auto &srcComp : srcComponents)
1771 : {
1772 422 : srcComponentMap[srcComp->GetName()] = &srcComp;
1773 : }
1774 414 : for (const auto &dstComp : dstComponents)
1775 : {
1776 274 : auto oIter = srcComponentMap.find(dstComp->GetName());
1777 274 : if (oIter == srcComponentMap.end())
1778 0 : return false;
1779 274 : const auto &srcComp = *(oIter->second);
1780 822 : if (!GDALExtendedDataType::CopyValue(
1781 274 : pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1782 274 : pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1783 : {
1784 0 : return false;
1785 : };
1786 : }
1787 140 : return true;
1788 : }
1789 :
1790 0 : return false;
1791 : }
1792 :
1793 : /************************************************************************/
1794 : /* CopyValues() */
1795 : /************************************************************************/
1796 :
1797 : /** Convert severals value from a source type to a destination type.
1798 : *
1799 : * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1800 : * that must be freed with CPLFree().
1801 : */
1802 316 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
1803 : const GDALExtendedDataType &srcType,
1804 : GPtrDiff_t nSrcStrideInElts, void *pDst,
1805 : const GDALExtendedDataType &dstType,
1806 : GPtrDiff_t nDstStrideInElts,
1807 : size_t nValues)
1808 : {
1809 : const auto nSrcStrideInBytes =
1810 316 : nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1811 : const auto nDstStrideInBytes =
1812 316 : nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1813 582 : if (srcType.GetClass() == GEDTC_NUMERIC &&
1814 266 : dstType.GetClass() == GEDTC_NUMERIC &&
1815 266 : nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1816 266 : nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1817 848 : nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1818 266 : nDstStrideInBytes <= std::numeric_limits<int>::max())
1819 : {
1820 266 : GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1821 : static_cast<int>(nSrcStrideInBytes), pDst,
1822 : dstType.GetNumericDataType(),
1823 : static_cast<int>(nDstStrideInBytes), nValues);
1824 : }
1825 : else
1826 : {
1827 50 : const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1828 50 : GByte *pabyDst = static_cast<GByte *>(pDst);
1829 100 : for (size_t i = 0; i < nValues; ++i)
1830 : {
1831 50 : if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1832 0 : return false;
1833 50 : pabySrc += nSrcStrideInBytes;
1834 50 : pabyDst += nDstStrideInBytes;
1835 : }
1836 : }
1837 316 : return true;
1838 : }
1839 :
1840 : /************************************************************************/
1841 : /* CheckReadWriteParams() */
1842 : /************************************************************************/
1843 : //! @cond Doxygen_Suppress
1844 7299 : bool GDALAbstractMDArray::CheckReadWriteParams(
1845 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1846 : const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1847 : const void *buffer, const void *buffer_alloc_start,
1848 : size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1849 : std::vector<GPtrDiff_t> &tmp_bufferStride) const
1850 : {
1851 0 : const auto lamda_error = []()
1852 : {
1853 0 : CPLError(CE_Failure, CPLE_AppDefined,
1854 : "Not all elements pointed by buffer will fit in "
1855 : "[buffer_alloc_start, "
1856 : "buffer_alloc_start + buffer_alloc_size[");
1857 0 : };
1858 :
1859 7299 : const auto &dims = GetDimensions();
1860 7299 : if (dims.empty())
1861 : {
1862 2694 : if (buffer_alloc_start)
1863 : {
1864 2353 : const size_t elementSize = bufferDataType.GetSize();
1865 2353 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1866 2353 : const GByte *paby_buffer_alloc_start =
1867 : static_cast<const GByte *>(buffer_alloc_start);
1868 2353 : const GByte *paby_buffer_alloc_end =
1869 : paby_buffer_alloc_start + buffer_alloc_size;
1870 :
1871 2353 : if (paby_buffer < paby_buffer_alloc_start ||
1872 2353 : paby_buffer + elementSize > paby_buffer_alloc_end)
1873 : {
1874 0 : lamda_error();
1875 0 : return false;
1876 : }
1877 : }
1878 2694 : return true;
1879 : }
1880 :
1881 4605 : if (arrayStep == nullptr)
1882 : {
1883 1191 : tmp_arrayStep.resize(dims.size(), 1);
1884 1191 : arrayStep = tmp_arrayStep.data();
1885 : }
1886 13147 : for (size_t i = 0; i < dims.size(); i++)
1887 : {
1888 8542 : if (count[i] == 0)
1889 : {
1890 0 : CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1891 : static_cast<unsigned>(i));
1892 0 : return false;
1893 : }
1894 : }
1895 4605 : bool bufferStride_all_positive = true;
1896 4605 : if (bufferStride == nullptr)
1897 : {
1898 902 : GPtrDiff_t stride = 1;
1899 : // To compute strides we must proceed from the fastest varying dimension
1900 : // (the last one), and then reverse the result
1901 2061 : for (size_t i = dims.size(); i != 0;)
1902 : {
1903 1159 : --i;
1904 1159 : tmp_bufferStride.push_back(stride);
1905 1159 : GUInt64 newStride = 0;
1906 : bool bOK;
1907 : try
1908 : {
1909 1159 : newStride = (CPLSM(static_cast<GUInt64>(stride)) *
1910 2318 : CPLSM(static_cast<GUInt64>(count[i])))
1911 1159 : .v();
1912 1159 : bOK = static_cast<size_t>(newStride) == newStride &&
1913 1159 : newStride < std::numeric_limits<size_t>::max() / 2;
1914 : }
1915 0 : catch (...)
1916 : {
1917 0 : bOK = false;
1918 : }
1919 1159 : if (!bOK)
1920 : {
1921 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1922 0 : return false;
1923 : }
1924 1159 : stride = static_cast<GPtrDiff_t>(newStride);
1925 : }
1926 902 : std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1927 902 : bufferStride = tmp_bufferStride.data();
1928 : }
1929 : else
1930 : {
1931 11084 : for (size_t i = 0; i < dims.size(); i++)
1932 : {
1933 7382 : if (bufferStride[i] < 0)
1934 : {
1935 1 : bufferStride_all_positive = false;
1936 1 : break;
1937 : }
1938 : }
1939 : }
1940 13118 : for (size_t i = 0; i < dims.size(); i++)
1941 : {
1942 8523 : if (arrayStartIdx[i] >= dims[i]->GetSize())
1943 : {
1944 2 : CPLError(CE_Failure, CPLE_AppDefined,
1945 : "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1946 : static_cast<unsigned>(i),
1947 2 : static_cast<GUInt64>(arrayStartIdx[i]),
1948 2 : static_cast<GUInt64>(dims[i]->GetSize()));
1949 2 : return false;
1950 : }
1951 : bool bOverflow;
1952 8521 : if (arrayStep[i] >= 0)
1953 : {
1954 : try
1955 : {
1956 7929 : bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
1957 7931 : CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1958 31719 : CPLSM(static_cast<GUInt64>(arrayStep[i])))
1959 7929 : .v() >= dims[i]->GetSize();
1960 : }
1961 1 : catch (...)
1962 : {
1963 1 : bOverflow = true;
1964 : }
1965 7930 : if (bOverflow)
1966 : {
1967 5 : CPLError(CE_Failure, CPLE_AppDefined,
1968 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
1969 : ">= " CPL_FRMT_GUIB,
1970 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1971 : static_cast<unsigned>(i),
1972 5 : static_cast<GUInt64>(dims[i]->GetSize()));
1973 5 : return false;
1974 : }
1975 : }
1976 : else
1977 : {
1978 : try
1979 : {
1980 591 : bOverflow =
1981 591 : arrayStartIdx[i] <
1982 591 : (CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1983 1182 : CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
1984 : ? (static_cast<GUInt64>(1) << 63)
1985 1182 : : static_cast<GUInt64>(-arrayStep[i])))
1986 591 : .v();
1987 : }
1988 0 : catch (...)
1989 : {
1990 0 : bOverflow = true;
1991 : }
1992 591 : if (bOverflow)
1993 : {
1994 3 : CPLError(
1995 : CE_Failure, CPLE_AppDefined,
1996 : "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
1997 : static_cast<unsigned>(i), static_cast<unsigned>(i),
1998 : static_cast<unsigned>(i));
1999 3 : return false;
2000 : }
2001 : }
2002 : }
2003 :
2004 4595 : if (buffer_alloc_start)
2005 : {
2006 2445 : const size_t elementSize = bufferDataType.GetSize();
2007 2445 : const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2008 2445 : const GByte *paby_buffer_alloc_start =
2009 : static_cast<const GByte *>(buffer_alloc_start);
2010 2445 : const GByte *paby_buffer_alloc_end =
2011 : paby_buffer_alloc_start + buffer_alloc_size;
2012 2445 : if (bufferStride_all_positive)
2013 : {
2014 2445 : if (paby_buffer < paby_buffer_alloc_start)
2015 : {
2016 0 : lamda_error();
2017 0 : return false;
2018 : }
2019 2445 : GUInt64 nOffset = elementSize;
2020 7102 : for (size_t i = 0; i < dims.size(); i++)
2021 : {
2022 : try
2023 : {
2024 4657 : nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
2025 4657 : CPLSM(static_cast<GUInt64>(bufferStride[i])) *
2026 9314 : CPLSM(static_cast<GUInt64>(count[i] - 1)) *
2027 18628 : CPLSM(static_cast<GUInt64>(elementSize)))
2028 4657 : .v();
2029 : }
2030 0 : catch (...)
2031 : {
2032 0 : lamda_error();
2033 0 : return false;
2034 : }
2035 : }
2036 : #if SIZEOF_VOIDP == 4
2037 : if (static_cast<size_t>(nOffset) != nOffset)
2038 : {
2039 : lamda_error();
2040 : return false;
2041 : }
2042 : #endif
2043 2445 : if (paby_buffer + nOffset > paby_buffer_alloc_end)
2044 : {
2045 0 : lamda_error();
2046 0 : return false;
2047 : }
2048 : }
2049 0 : else if (dims.size() < 31)
2050 : {
2051 : // Check all corners of the hypercube
2052 0 : const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2053 0 : for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2054 : {
2055 0 : const GByte *paby = paby_buffer;
2056 0 : for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2057 : i++)
2058 : {
2059 0 : if (iCornerCode & (1U << i))
2060 : {
2061 : // We should check for integer overflows
2062 0 : paby += bufferStride[i] * (count[i] - 1) * elementSize;
2063 : }
2064 : }
2065 0 : if (paby < paby_buffer_alloc_start ||
2066 0 : paby + elementSize > paby_buffer_alloc_end)
2067 : {
2068 0 : lamda_error();
2069 0 : return false;
2070 : }
2071 : }
2072 : }
2073 : }
2074 :
2075 4595 : return true;
2076 : }
2077 :
2078 : //! @endcond
2079 :
2080 : /************************************************************************/
2081 : /* Read() */
2082 : /************************************************************************/
2083 :
2084 : /** Read part or totality of a multidimensional array or attribute.
2085 : *
2086 : * This will extract the content of a hyper-rectangle from the array into
2087 : * a user supplied buffer.
2088 : *
2089 : * If bufferDataType is of type string, the values written in pDstBuffer
2090 : * will be char* pointers and the strings should be freed with CPLFree().
2091 : *
2092 : * This is the same as the C function GDALMDArrayRead().
2093 : *
2094 : * @param arrayStartIdx Values representing the starting index to read
2095 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2096 : * Array of GetDimensionCount() values. Must not be
2097 : * nullptr, unless for a zero-dimensional array.
2098 : *
2099 : * @param count Values representing the number of values to extract in
2100 : * each dimension.
2101 : * Array of GetDimensionCount() values. Must not be
2102 : * nullptr, unless for a zero-dimensional array.
2103 : *
2104 : * @param arrayStep Spacing between values to extract in each dimension.
2105 : * The spacing is in number of array elements, not bytes.
2106 : * If provided, must contain GetDimensionCount() values.
2107 : * If set to nullptr, [1, 1, ... 1] will be used as a
2108 : * default to indicate consecutive elements.
2109 : *
2110 : * @param bufferStride Spacing between values to store in pDstBuffer.
2111 : * The spacing is in number of array elements, not bytes.
2112 : * If provided, must contain GetDimensionCount() values.
2113 : * Negative values are possible (for example to reorder
2114 : * from bottom-to-top to top-to-bottom).
2115 : * If set to nullptr, will be set so that pDstBuffer is
2116 : * written in a compact way, with elements of the last /
2117 : * fastest varying dimension being consecutive.
2118 : *
2119 : * @param bufferDataType Data type of values in pDstBuffer.
2120 : *
2121 : * @param pDstBuffer User buffer to store the values read. Should be big
2122 : * enough to store the number of values indicated by
2123 : * count[] and with the spacing of bufferStride[].
2124 : *
2125 : * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2126 : * validty of pDstBuffer. pDstBufferAllocStart
2127 : * should be the pointer returned by the malloc() or equivalent call used to
2128 : * allocate the buffer. It will generally be equal to pDstBuffer (when
2129 : * bufferStride[] values are all positive), but not necessarily. If specified,
2130 : * nDstBufferAllocSize should be also set to the appropriate value. If no
2131 : * validation is needed, nullptr can be passed.
2132 : *
2133 : * @param nDstBufferAllocSize Optional buffer size, that can be used to
2134 : * validate the validty of pDstBuffer. This is the size of the buffer starting
2135 : * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2136 : * set to the appropriate value.
2137 : * If no validation is needed, 0 can be passed.
2138 : *
2139 : * @return true in case of success.
2140 : */
2141 1992 : bool GDALAbstractMDArray::Read(
2142 : const GUInt64 *arrayStartIdx, const size_t *count,
2143 : const GInt64 *arrayStep, // step in elements
2144 : const GPtrDiff_t *bufferStride, // stride in elements
2145 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2146 : const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2147 : {
2148 1992 : if (!GetDataType().CanConvertTo(bufferDataType))
2149 : {
2150 0 : CPLError(CE_Failure, CPLE_AppDefined,
2151 : "Array data type is not convertible to buffer data type");
2152 0 : return false;
2153 : }
2154 :
2155 3984 : std::vector<GInt64> tmp_arrayStep;
2156 3984 : std::vector<GPtrDiff_t> tmp_bufferStride;
2157 1992 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2158 : bufferDataType, pDstBuffer, pDstBufferAllocStart,
2159 : nDstBufferAllocSize, tmp_arrayStep,
2160 : tmp_bufferStride))
2161 : {
2162 0 : return false;
2163 : }
2164 :
2165 1992 : return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2166 1992 : pDstBuffer);
2167 : }
2168 :
2169 : /************************************************************************/
2170 : /* IWrite() */
2171 : /************************************************************************/
2172 :
2173 : //! @cond Doxygen_Suppress
2174 1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2175 : const GInt64 *, const GPtrDiff_t *,
2176 : const GDALExtendedDataType &, const void *)
2177 : {
2178 1 : CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2179 1 : return false;
2180 : }
2181 :
2182 : //! @endcond
2183 :
2184 : /************************************************************************/
2185 : /* Write() */
2186 : /************************************************************************/
2187 :
2188 : /** Write part or totality of a multidimensional array or attribute.
2189 : *
2190 : * This will set the content of a hyper-rectangle into the array from
2191 : * a user supplied buffer.
2192 : *
2193 : * If bufferDataType is of type string, the values read from pSrcBuffer
2194 : * will be char* pointers.
2195 : *
2196 : * This is the same as the C function GDALMDArrayWrite().
2197 : *
2198 : * @param arrayStartIdx Values representing the starting index to write
2199 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2200 : * Array of GetDimensionCount() values. Must not be
2201 : * nullptr, unless for a zero-dimensional array.
2202 : *
2203 : * @param count Values representing the number of values to write in
2204 : * each dimension.
2205 : * Array of GetDimensionCount() values. Must not be
2206 : * nullptr, unless for a zero-dimensional array.
2207 : *
2208 : * @param arrayStep Spacing between values to write in each dimension.
2209 : * The spacing is in number of array elements, not bytes.
2210 : * If provided, must contain GetDimensionCount() values.
2211 : * If set to nullptr, [1, 1, ... 1] will be used as a
2212 : * default to indicate consecutive elements.
2213 : *
2214 : * @param bufferStride Spacing between values to read from pSrcBuffer.
2215 : * The spacing is in number of array elements, not bytes.
2216 : * If provided, must contain GetDimensionCount() values.
2217 : * Negative values are possible (for example to reorder
2218 : * from bottom-to-top to top-to-bottom).
2219 : * If set to nullptr, will be set so that pSrcBuffer is
2220 : * written in a compact way, with elements of the last /
2221 : * fastest varying dimension being consecutive.
2222 : *
2223 : * @param bufferDataType Data type of values in pSrcBuffer.
2224 : *
2225 : * @param pSrcBuffer User buffer to read the values from. Should be big
2226 : * enough to store the number of values indicated by
2227 : * count[] and with the spacing of bufferStride[].
2228 : *
2229 : * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2230 : * validty of pSrcBuffer. pSrcBufferAllocStart
2231 : * should be the pointer returned by the malloc() or equivalent call used to
2232 : * allocate the buffer. It will generally be equal to pSrcBuffer (when
2233 : * bufferStride[] values are all positive), but not necessarily. If specified,
2234 : * nSrcBufferAllocSize should be also set to the appropriate value. If no
2235 : * validation is needed, nullptr can be passed.
2236 : *
2237 : * @param nSrcBufferAllocSize Optional buffer size, that can be used to
2238 : * validate the validty of pSrcBuffer. This is the size of the buffer starting
2239 : * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2240 : * set to the appropriate value.
2241 : * If no validation is needed, 0 can be passed.
2242 : *
2243 : * @return true in case of success.
2244 : */
2245 1670 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2246 : const size_t *count, const GInt64 *arrayStep,
2247 : const GPtrDiff_t *bufferStride,
2248 : const GDALExtendedDataType &bufferDataType,
2249 : const void *pSrcBuffer,
2250 : const void *pSrcBufferAllocStart,
2251 : size_t nSrcBufferAllocSize)
2252 : {
2253 1670 : if (!bufferDataType.CanConvertTo(GetDataType()))
2254 : {
2255 0 : CPLError(CE_Failure, CPLE_AppDefined,
2256 : "Buffer data type is not convertible to array data type");
2257 0 : return false;
2258 : }
2259 :
2260 3340 : std::vector<GInt64> tmp_arrayStep;
2261 3340 : std::vector<GPtrDiff_t> tmp_bufferStride;
2262 1670 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2263 : bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2264 : nSrcBufferAllocSize, tmp_arrayStep,
2265 : tmp_bufferStride))
2266 : {
2267 0 : return false;
2268 : }
2269 :
2270 1670 : return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2271 1670 : pSrcBuffer);
2272 : }
2273 :
2274 : /************************************************************************/
2275 : /* GetTotalElementsCount() */
2276 : /************************************************************************/
2277 :
2278 : /** Return the total number of values in the array.
2279 : *
2280 : * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2281 : * and GDALAttributeGetTotalElementsCount().
2282 : *
2283 : */
2284 949 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2285 : {
2286 949 : const auto &dims = GetDimensions();
2287 949 : if (dims.empty())
2288 499 : return 1;
2289 450 : GUInt64 nElts = 1;
2290 1010 : for (const auto &dim : dims)
2291 : {
2292 : try
2293 : {
2294 560 : nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
2295 1680 : CPLSM(static_cast<GUInt64>(dim->GetSize())))
2296 560 : .v();
2297 : }
2298 0 : catch (...)
2299 : {
2300 0 : return 0;
2301 : }
2302 : }
2303 450 : return nElts;
2304 : }
2305 :
2306 : /************************************************************************/
2307 : /* GetBlockSize() */
2308 : /************************************************************************/
2309 :
2310 : /** Return the "natural" block size of the array along all dimensions.
2311 : *
2312 : * Some drivers might organize the array in tiles/blocks and reading/writing
2313 : * aligned on those tile/block boundaries will be more efficient.
2314 : *
2315 : * The returned number of elements in the vector is the same as
2316 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2317 : * the natural block size along the considered dimension.
2318 : * "Flat" arrays will typically return a vector of values set to 0.
2319 : *
2320 : * The default implementation will return a vector of values set to 0.
2321 : *
2322 : * This method is used by GetProcessingChunkSize().
2323 : *
2324 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2325 : * theoretical case of a 32-bit platform, this might exceed its size_t
2326 : * allocation capabilities.
2327 : *
2328 : * This is the same as the C function GDALMDArrayGetBlockSize().
2329 : *
2330 : * @return the block size, in number of elements along each dimension.
2331 : */
2332 215 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2333 : {
2334 215 : return std::vector<GUInt64>(GetDimensionCount());
2335 : }
2336 :
2337 : /************************************************************************/
2338 : /* GetProcessingChunkSize() */
2339 : /************************************************************************/
2340 :
2341 : /** \brief Return an optimal chunk size for read/write operations, given the
2342 : * natural block size and memory constraints specified.
2343 : *
2344 : * This method will use GetBlockSize() to define a chunk whose dimensions are
2345 : * multiple of those returned by GetBlockSize() (unless the block define by
2346 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2347 : * returned by this method).
2348 : *
2349 : * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2350 : *
2351 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2352 : * chunk.
2353 : *
2354 : * @return the chunk size, in number of elements along each dimension.
2355 : */
2356 : std::vector<size_t>
2357 60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2358 : {
2359 60 : const auto &dims = GetDimensions();
2360 60 : const auto &nDTSize = GetDataType().GetSize();
2361 60 : std::vector<size_t> anChunkSize;
2362 120 : auto blockSize = GetBlockSize();
2363 60 : CPLAssert(blockSize.size() == dims.size());
2364 60 : size_t nChunkSize = nDTSize;
2365 60 : bool bOverflow = false;
2366 60 : constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2367 : // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2368 : // [1, min(sizet_max, dim_size[i])]
2369 : // Also make sure that the product of all anChunkSize[i]) fits on size_t
2370 173 : for (size_t i = 0; i < dims.size(); i++)
2371 : {
2372 : const auto sizeDimI =
2373 226 : std::max(static_cast<size_t>(1),
2374 226 : static_cast<size_t>(
2375 226 : std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2376 113 : std::min(blockSize[i], dims[i]->GetSize()))));
2377 113 : anChunkSize.push_back(sizeDimI);
2378 113 : if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2379 : {
2380 4 : bOverflow = true;
2381 : }
2382 : else
2383 : {
2384 109 : nChunkSize *= sizeDimI;
2385 : }
2386 : }
2387 60 : if (nChunkSize == 0)
2388 0 : return anChunkSize;
2389 :
2390 : // If the product of all anChunkSize[i] does not fit on size_t, then
2391 : // set lowest anChunkSize[i] to 1.
2392 60 : if (bOverflow)
2393 : {
2394 2 : nChunkSize = nDTSize;
2395 2 : bOverflow = false;
2396 8 : for (size_t i = dims.size(); i > 0;)
2397 : {
2398 6 : --i;
2399 6 : if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2400 : {
2401 4 : bOverflow = true;
2402 4 : anChunkSize[i] = 1;
2403 : }
2404 : else
2405 : {
2406 2 : nChunkSize *= anChunkSize[i];
2407 : }
2408 : }
2409 : }
2410 :
2411 60 : nChunkSize = nDTSize;
2412 120 : std::vector<size_t> anAccBlockSizeFromStart;
2413 173 : for (size_t i = 0; i < dims.size(); i++)
2414 : {
2415 113 : nChunkSize *= anChunkSize[i];
2416 113 : anAccBlockSizeFromStart.push_back(nChunkSize);
2417 : }
2418 60 : if (nChunkSize <= nMaxChunkMemory / 2)
2419 : {
2420 56 : size_t nVoxelsFromEnd = 1;
2421 161 : for (size_t i = dims.size(); i > 0;)
2422 : {
2423 105 : --i;
2424 : const auto nCurBlockSize =
2425 105 : anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2426 105 : const auto nMul = nMaxChunkMemory / nCurBlockSize;
2427 105 : if (nMul >= 2)
2428 : {
2429 97 : const auto nSizeThisDim(dims[i]->GetSize());
2430 : const auto nBlocksThisDim =
2431 97 : DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2432 97 : anChunkSize[i] = static_cast<size_t>(std::min(
2433 97 : anChunkSize[i] *
2434 194 : std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2435 97 : nSizeThisDim));
2436 : }
2437 105 : nVoxelsFromEnd *= anChunkSize[i];
2438 : }
2439 : }
2440 60 : return anChunkSize;
2441 : }
2442 :
2443 : /************************************************************************/
2444 : /* BaseRename() */
2445 : /************************************************************************/
2446 :
2447 : //! @cond Doxygen_Suppress
2448 18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2449 : {
2450 18 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
2451 18 : m_osFullName += osNewName;
2452 18 : m_osName = osNewName;
2453 :
2454 18 : NotifyChildrenOfRenaming();
2455 18 : }
2456 :
2457 : //! @endcond
2458 :
2459 : //! @cond Doxygen_Suppress
2460 : /************************************************************************/
2461 : /* ParentRenamed() */
2462 : /************************************************************************/
2463 :
2464 50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2465 : {
2466 50 : m_osFullName = osNewParentFullName;
2467 50 : m_osFullName += "/";
2468 50 : m_osFullName += m_osName;
2469 :
2470 50 : NotifyChildrenOfRenaming();
2471 50 : }
2472 :
2473 : //! @endcond
2474 :
2475 : /************************************************************************/
2476 : /* Deleted() */
2477 : /************************************************************************/
2478 :
2479 : //! @cond Doxygen_Suppress
2480 52 : void GDALAbstractMDArray::Deleted()
2481 : {
2482 52 : m_bValid = false;
2483 :
2484 52 : NotifyChildrenOfDeletion();
2485 52 : }
2486 :
2487 : //! @endcond
2488 :
2489 : /************************************************************************/
2490 : /* ParentDeleted() */
2491 : /************************************************************************/
2492 :
2493 : //! @cond Doxygen_Suppress
2494 28 : void GDALAbstractMDArray::ParentDeleted()
2495 : {
2496 28 : Deleted();
2497 28 : }
2498 :
2499 : //! @endcond
2500 :
2501 : /************************************************************************/
2502 : /* CheckValidAndErrorOutIfNot() */
2503 : /************************************************************************/
2504 :
2505 : //! @cond Doxygen_Suppress
2506 5379 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2507 : {
2508 5379 : if (!m_bValid)
2509 : {
2510 26 : CPLError(CE_Failure, CPLE_AppDefined,
2511 : "This object has been deleted. No action on it is possible");
2512 : }
2513 5379 : return m_bValid;
2514 : }
2515 :
2516 : //! @endcond
2517 :
2518 : /************************************************************************/
2519 : /* SetUnit() */
2520 : /************************************************************************/
2521 :
2522 : /** Set the variable unit.
2523 : *
2524 : * Values should conform as much as possible with those allowed by
2525 : * the NetCDF CF conventions:
2526 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2527 : * but others might be returned.
2528 : *
2529 : * Few examples are "meter", "degrees", "second", ...
2530 : * Empty value means unknown.
2531 : *
2532 : * This is the same as the C function GDALMDArraySetUnit()
2533 : *
2534 : * @note Driver implementation: optionally implemented.
2535 : *
2536 : * @param osUnit unit name.
2537 : * @return true in case of success.
2538 : */
2539 0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2540 : {
2541 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2542 0 : return false;
2543 : }
2544 :
2545 : /************************************************************************/
2546 : /* GetUnit() */
2547 : /************************************************************************/
2548 :
2549 : /** Return the array unit.
2550 : *
2551 : * Values should conform as much as possible with those allowed by
2552 : * the NetCDF CF conventions:
2553 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2554 : * but others might be returned.
2555 : *
2556 : * Few examples are "meter", "degrees", "second", ...
2557 : * Empty value means unknown.
2558 : *
2559 : * This is the same as the C function GDALMDArrayGetUnit()
2560 : */
2561 5 : const std::string &GDALMDArray::GetUnit() const
2562 : {
2563 5 : static const std::string emptyString;
2564 5 : return emptyString;
2565 : }
2566 :
2567 : /************************************************************************/
2568 : /* SetSpatialRef() */
2569 : /************************************************************************/
2570 :
2571 : /** Assign a spatial reference system object to the array.
2572 : *
2573 : * This is the same as the C function GDALMDArraySetSpatialRef().
2574 : */
2575 0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2576 : {
2577 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2578 0 : return false;
2579 : }
2580 :
2581 : /************************************************************************/
2582 : /* GetSpatialRef() */
2583 : /************************************************************************/
2584 :
2585 : /** Return the spatial reference system object associated with the array.
2586 : *
2587 : * This is the same as the C function GDALMDArrayGetSpatialRef().
2588 : */
2589 4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2590 : {
2591 4 : return nullptr;
2592 : }
2593 :
2594 : /************************************************************************/
2595 : /* GetRawNoDataValue() */
2596 : /************************************************************************/
2597 :
2598 : /** Return the nodata value as a "raw" value.
2599 : *
2600 : * The value returned might be nullptr in case of no nodata value. When
2601 : * a nodata value is registered, a non-nullptr will be returned whose size in
2602 : * bytes is GetDataType().GetSize().
2603 : *
2604 : * The returned value should not be modified or freed. It is valid until
2605 : * the array is destroyed, or the next call to GetRawNoDataValue() or
2606 : * SetRawNoDataValue(), or any similar methods.
2607 : *
2608 : * @note Driver implementation: this method shall be implemented if nodata
2609 : * is supported.
2610 : *
2611 : * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2612 : *
2613 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2614 : */
2615 5 : const void *GDALMDArray::GetRawNoDataValue() const
2616 : {
2617 5 : return nullptr;
2618 : }
2619 :
2620 : /************************************************************************/
2621 : /* GetNoDataValueAsDouble() */
2622 : /************************************************************************/
2623 :
2624 : /** Return the nodata value as a double.
2625 : *
2626 : * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2627 : *
2628 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2629 : * a nodata value exists and can be converted to double. Might be nullptr.
2630 : *
2631 : * @return the nodata value as a double. A 0.0 value might also indicate the
2632 : * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2633 : * set to false then).
2634 : */
2635 22389 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2636 : {
2637 22389 : const void *pNoData = GetRawNoDataValue();
2638 22389 : double dfNoData = 0.0;
2639 22389 : const auto &eDT = GetDataType();
2640 22389 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2641 22389 : if (ok)
2642 : {
2643 22152 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2644 : GDT_Float64, 0, 1);
2645 : }
2646 22389 : if (pbHasNoData)
2647 372 : *pbHasNoData = ok;
2648 22389 : return dfNoData;
2649 : }
2650 :
2651 : /************************************************************************/
2652 : /* GetNoDataValueAsInt64() */
2653 : /************************************************************************/
2654 :
2655 : /** Return the nodata value as a Int64.
2656 : *
2657 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2658 : * a nodata value exists and can be converted to Int64. Might be nullptr.
2659 : *
2660 : * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2661 : *
2662 : * @return the nodata value as a Int64
2663 : *
2664 : * @since GDAL 3.5
2665 : */
2666 12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2667 : {
2668 12 : const void *pNoData = GetRawNoDataValue();
2669 12 : int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2670 12 : const auto &eDT = GetDataType();
2671 12 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2672 12 : if (ok)
2673 : {
2674 8 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData, GDT_Int64,
2675 : 0, 1);
2676 : }
2677 12 : if (pbHasNoData)
2678 12 : *pbHasNoData = ok;
2679 12 : return nNoData;
2680 : }
2681 :
2682 : /************************************************************************/
2683 : /* GetNoDataValueAsUInt64() */
2684 : /************************************************************************/
2685 :
2686 : /** Return the nodata value as a UInt64.
2687 : *
2688 : * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2689 :
2690 : * @param pbHasNoData Pointer to a output boolean that will be set to true if
2691 : * a nodata value exists and can be converted to UInt64. Might be nullptr.
2692 : *
2693 : * @return the nodata value as a UInt64
2694 : *
2695 : * @since GDAL 3.5
2696 : */
2697 8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2698 : {
2699 8 : const void *pNoData = GetRawNoDataValue();
2700 8 : uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2701 8 : const auto &eDT = GetDataType();
2702 8 : const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2703 8 : if (ok)
2704 : {
2705 6 : GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2706 : GDT_UInt64, 0, 1);
2707 : }
2708 8 : if (pbHasNoData)
2709 8 : *pbHasNoData = ok;
2710 8 : return nNoData;
2711 : }
2712 :
2713 : /************************************************************************/
2714 : /* SetRawNoDataValue() */
2715 : /************************************************************************/
2716 :
2717 : /** Set the nodata value as a "raw" value.
2718 : *
2719 : * The value passed might be nullptr in case of no nodata value. When
2720 : * a nodata value is registered, a non-nullptr whose size in
2721 : * bytes is GetDataType().GetSize() must be passed.
2722 : *
2723 : * This is the same as the C function GDALMDArraySetRawNoDataValue().
2724 : *
2725 : * @note Driver implementation: this method shall be implemented if setting
2726 : nodata
2727 : * is supported.
2728 :
2729 : * @return true in case of success.
2730 : */
2731 0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2732 : {
2733 0 : CPLError(CE_Failure, CPLE_NotSupported,
2734 : "SetRawNoDataValue() not implemented");
2735 0 : return false;
2736 : }
2737 :
2738 : /************************************************************************/
2739 : /* SetNoDataValue() */
2740 : /************************************************************************/
2741 :
2742 : /** Set the nodata value as a double.
2743 : *
2744 : * If the natural data type of the attribute/array is not double, type
2745 : * conversion will occur to the type returned by GetDataType().
2746 : *
2747 : * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2748 : *
2749 : * @return true in case of success.
2750 : */
2751 56 : bool GDALMDArray::SetNoDataValue(double dfNoData)
2752 : {
2753 56 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2754 56 : bool bRet = false;
2755 56 : if (GDALExtendedDataType::CopyValue(
2756 112 : &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2757 56 : GetDataType()))
2758 : {
2759 56 : bRet = SetRawNoDataValue(pRawNoData);
2760 : }
2761 56 : CPLFree(pRawNoData);
2762 56 : return bRet;
2763 : }
2764 :
2765 : /************************************************************************/
2766 : /* SetNoDataValue() */
2767 : /************************************************************************/
2768 :
2769 : /** Set the nodata value as a Int64.
2770 : *
2771 : * If the natural data type of the attribute/array is not Int64, type conversion
2772 : * will occur to the type returned by GetDataType().
2773 : *
2774 : * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2775 : *
2776 : * @return true in case of success.
2777 : *
2778 : * @since GDAL 3.5
2779 : */
2780 3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2781 : {
2782 3 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2783 3 : bool bRet = false;
2784 3 : if (GDALExtendedDataType::CopyValue(&nNoData,
2785 6 : GDALExtendedDataType::Create(GDT_Int64),
2786 3 : pRawNoData, GetDataType()))
2787 : {
2788 3 : bRet = SetRawNoDataValue(pRawNoData);
2789 : }
2790 3 : CPLFree(pRawNoData);
2791 3 : return bRet;
2792 : }
2793 :
2794 : /************************************************************************/
2795 : /* SetNoDataValue() */
2796 : /************************************************************************/
2797 :
2798 : /** Set the nodata value as a Int64.
2799 : *
2800 : * If the natural data type of the attribute/array is not Int64, type conversion
2801 : * will occur to the type returned by GetDataType().
2802 : *
2803 : * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2804 : *
2805 : * @return true in case of success.
2806 : *
2807 : * @since GDAL 3.5
2808 : */
2809 1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2810 : {
2811 1 : void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2812 1 : bool bRet = false;
2813 1 : if (GDALExtendedDataType::CopyValue(
2814 2 : &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2815 1 : GetDataType()))
2816 : {
2817 1 : bRet = SetRawNoDataValue(pRawNoData);
2818 : }
2819 1 : CPLFree(pRawNoData);
2820 1 : return bRet;
2821 : }
2822 :
2823 : /************************************************************************/
2824 : /* Resize() */
2825 : /************************************************************************/
2826 :
2827 : /** Resize an array to new dimensions.
2828 : *
2829 : * Not all drivers may allow this operation, and with restrictions (e.g.
2830 : * for netCDF, this is limited to growing of "unlimited" dimensions)
2831 : *
2832 : * Resizing a dimension used in other arrays will cause those other arrays
2833 : * to be resized.
2834 : *
2835 : * This is the same as the C function GDALMDArrayResize().
2836 : *
2837 : * @param anNewDimSizes Array of GetDimensionCount() values containing the
2838 : * new size of each indexing dimension.
2839 : * @param papszOptions Options. (Driver specific)
2840 : * @return true in case of success.
2841 : * @since GDAL 3.7
2842 : */
2843 0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2844 : CPL_UNUSED CSLConstList papszOptions)
2845 : {
2846 0 : CPLError(CE_Failure, CPLE_NotSupported,
2847 : "Resize() is not supported for this array");
2848 0 : return false;
2849 : }
2850 :
2851 : /************************************************************************/
2852 : /* SetScale() */
2853 : /************************************************************************/
2854 :
2855 : /** Set the scale value to apply to raw values.
2856 : *
2857 : * unscaled_value = raw_value * GetScale() + GetOffset()
2858 : *
2859 : * This is the same as the C function GDALMDArraySetScale() /
2860 : * GDALMDArraySetScaleEx().
2861 : *
2862 : * @note Driver implementation: this method shall be implemented if setting
2863 : * scale is supported.
2864 : *
2865 : * @param dfScale scale
2866 : * @param eStorageType Data type to which create the potential attribute that
2867 : * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2868 : * implementation will decide automatically the data type. Note that changing
2869 : * the data type after initial setting might not be supported.
2870 : * @return true in case of success.
2871 : */
2872 0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2873 : CPL_UNUSED GDALDataType eStorageType)
2874 : {
2875 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2876 0 : return false;
2877 : }
2878 :
2879 : /************************************************************************/
2880 : /* SetOffset) */
2881 : /************************************************************************/
2882 :
2883 : /** Set the offset value to apply to raw values.
2884 : *
2885 : * unscaled_value = raw_value * GetScale() + GetOffset()
2886 : *
2887 : * This is the same as the C function GDALMDArraySetOffset() /
2888 : * GDALMDArraySetOffsetEx().
2889 : *
2890 : * @note Driver implementation: this method shall be implemented if setting
2891 : * offset is supported.
2892 : *
2893 : * @param dfOffset Offset
2894 : * @param eStorageType Data type to which create the potential attribute that
2895 : * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2896 : * implementation will decide automatically the data type. Note that changing
2897 : * the data type after initial setting might not be supported.
2898 : * @return true in case of success.
2899 : */
2900 0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2901 : CPL_UNUSED GDALDataType eStorageType)
2902 : {
2903 0 : CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2904 0 : return false;
2905 : }
2906 :
2907 : /************************************************************************/
2908 : /* GetScale() */
2909 : /************************************************************************/
2910 :
2911 : /** Get the scale value to apply to raw values.
2912 : *
2913 : * unscaled_value = raw_value * GetScale() + GetOffset()
2914 : *
2915 : * This is the same as the C function GDALMDArrayGetScale().
2916 : *
2917 : * @note Driver implementation: this method shall be implemented if gettings
2918 : * scale is supported.
2919 : *
2920 : * @param pbHasScale Pointer to a output boolean that will be set to true if
2921 : * a scale value exists. Might be nullptr.
2922 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2923 : * the storage type of the scale value, when known/relevant. Otherwise will be
2924 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2925 : *
2926 : * @return the scale value. A 1.0 value might also indicate the
2927 : * absence of a scale value.
2928 : */
2929 13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
2930 : CPL_UNUSED GDALDataType *peStorageType) const
2931 : {
2932 13 : if (pbHasScale)
2933 13 : *pbHasScale = false;
2934 13 : return 1.0;
2935 : }
2936 :
2937 : /************************************************************************/
2938 : /* GetOffset() */
2939 : /************************************************************************/
2940 :
2941 : /** Get the offset value to apply to raw values.
2942 : *
2943 : * unscaled_value = raw_value * GetScale() + GetOffset()
2944 : *
2945 : * This is the same as the C function GDALMDArrayGetOffset().
2946 : *
2947 : * @note Driver implementation: this method shall be implemented if gettings
2948 : * offset is supported.
2949 : *
2950 : * @param pbHasOffset Pointer to a output boolean that will be set to true if
2951 : * a offset value exists. Might be nullptr.
2952 : * @param peStorageType Pointer to a output GDALDataType that will be set to
2953 : * the storage type of the offset value, when known/relevant. Otherwise will be
2954 : * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2955 : *
2956 : * @return the offset value. A 0.0 value might also indicate the
2957 : * absence of a offset value.
2958 : */
2959 13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
2960 : CPL_UNUSED GDALDataType *peStorageType) const
2961 : {
2962 13 : if (pbHasOffset)
2963 13 : *pbHasOffset = false;
2964 13 : return 0.0;
2965 : }
2966 :
2967 : /************************************************************************/
2968 : /* ProcessPerChunk() */
2969 : /************************************************************************/
2970 :
2971 : namespace
2972 : {
2973 : enum class Caller
2974 : {
2975 : CALLER_END_OF_LOOP,
2976 : CALLER_IN_LOOP,
2977 : };
2978 : }
2979 :
2980 : /** \brief Call a user-provided function to operate on an array chunk by chunk.
2981 : *
2982 : * This method is to be used when doing operations on an array, or a subset of
2983 : * it, in a chunk by chunk way.
2984 : *
2985 : * @param arrayStartIdx Values representing the starting index to use
2986 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
2987 : * Array of GetDimensionCount() values. Must not be
2988 : * nullptr, unless for a zero-dimensional array.
2989 : *
2990 : * @param count Values representing the number of values to use in
2991 : * each dimension.
2992 : * Array of GetDimensionCount() values. Must not be
2993 : * nullptr, unless for a zero-dimensional array.
2994 : *
2995 : * @param chunkSize Values representing the chunk size in each dimension.
2996 : * Might typically the output of GetProcessingChunkSize().
2997 : * Array of GetDimensionCount() values. Must not be
2998 : * nullptr, unless for a zero-dimensional array.
2999 : *
3000 : * @param pfnFunc User-provided function of type FuncProcessPerChunkType.
3001 : * Must NOT be nullptr.
3002 : *
3003 : * @param pUserData Pointer to pass as the value of the pUserData argument
3004 : * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3005 : *
3006 : * @return true in case of success.
3007 : */
3008 58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3009 : const GUInt64 *count,
3010 : const size_t *chunkSize,
3011 : FuncProcessPerChunkType pfnFunc,
3012 : void *pUserData)
3013 : {
3014 58 : const auto &dims = GetDimensions();
3015 58 : if (dims.empty())
3016 : {
3017 2 : return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3018 : }
3019 :
3020 : // Sanity check
3021 56 : size_t nTotalChunkSize = 1;
3022 146 : for (size_t i = 0; i < dims.size(); i++)
3023 : {
3024 97 : const auto nSizeThisDim(dims[i]->GetSize());
3025 97 : if (count[i] == 0 || count[i] > nSizeThisDim ||
3026 95 : arrayStartIdx[i] > nSizeThisDim - count[i])
3027 : {
3028 4 : CPLError(CE_Failure, CPLE_AppDefined,
3029 : "Inconsistent arrayStartIdx[] / count[] values "
3030 : "regarding array size");
3031 4 : return false;
3032 : }
3033 184 : if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3034 91 : chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3035 : {
3036 3 : CPLError(CE_Failure, CPLE_AppDefined,
3037 : "Inconsistent chunkSize[] values");
3038 3 : return false;
3039 : }
3040 90 : nTotalChunkSize *= chunkSize[i];
3041 : }
3042 :
3043 49 : size_t dimIdx = 0;
3044 98 : std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3045 98 : std::vector<size_t> chunkCount(dims.size());
3046 :
3047 : struct Stack
3048 : {
3049 : GUInt64 nBlockCounter = 0;
3050 : GUInt64 nBlocksMinusOne = 0;
3051 : size_t first_count = 0; // only used if nBlocks > 1
3052 : Caller return_point = Caller::CALLER_END_OF_LOOP;
3053 : };
3054 :
3055 98 : std::vector<Stack> stack(dims.size());
3056 49 : GUInt64 iCurChunk = 0;
3057 49 : GUInt64 nChunkCount = 1;
3058 138 : for (size_t i = 0; i < dims.size(); i++)
3059 : {
3060 89 : const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3061 89 : const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3062 89 : stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3063 89 : nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3064 89 : if (stack[i].nBlocksMinusOne == 0)
3065 : {
3066 84 : chunkArrayStartIdx[i] = arrayStartIdx[i];
3067 84 : chunkCount[i] = static_cast<size_t>(count[i]);
3068 : }
3069 : else
3070 : {
3071 5 : stack[i].first_count = static_cast<size_t>(
3072 5 : (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3073 : }
3074 : }
3075 :
3076 49 : lbl_next_depth:
3077 248 : if (dimIdx == dims.size())
3078 : {
3079 82 : ++iCurChunk;
3080 82 : if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3081 : iCurChunk, nChunkCount, pUserData))
3082 : {
3083 0 : return false;
3084 : }
3085 : }
3086 : else
3087 : {
3088 166 : if (stack[dimIdx].nBlocksMinusOne != 0)
3089 : {
3090 11 : stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3091 11 : chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3092 11 : chunkCount[dimIdx] = stack[dimIdx].first_count;
3093 11 : stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3094 : while (true)
3095 : {
3096 33 : dimIdx++;
3097 33 : goto lbl_next_depth;
3098 33 : lbl_return_to_caller_in_loop:
3099 33 : --stack[dimIdx].nBlockCounter;
3100 33 : if (stack[dimIdx].nBlockCounter == 0)
3101 11 : break;
3102 22 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3103 22 : chunkCount[dimIdx] = chunkSize[dimIdx];
3104 : }
3105 :
3106 11 : chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3107 22 : chunkCount[dimIdx] =
3108 11 : static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3109 11 : chunkArrayStartIdx[dimIdx]);
3110 11 : stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3111 : }
3112 166 : dimIdx++;
3113 166 : goto lbl_next_depth;
3114 166 : lbl_return_to_caller_end_of_loop:
3115 166 : if (dimIdx == 0)
3116 49 : goto end;
3117 : }
3118 :
3119 199 : dimIdx--;
3120 : // cppcheck-suppress negativeContainerIndex
3121 199 : switch (stack[dimIdx].return_point)
3122 : {
3123 166 : case Caller::CALLER_END_OF_LOOP:
3124 166 : goto lbl_return_to_caller_end_of_loop;
3125 33 : case Caller::CALLER_IN_LOOP:
3126 33 : goto lbl_return_to_caller_in_loop;
3127 : }
3128 49 : end:
3129 49 : return true;
3130 : }
3131 :
3132 : /************************************************************************/
3133 : /* GDALAttribute() */
3134 : /************************************************************************/
3135 :
3136 : //! @cond Doxygen_Suppress
3137 11863 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3138 0 : CPL_UNUSED const std::string &osName)
3139 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3140 11863 : : GDALAbstractMDArray(osParentName, osName)
3141 : #endif
3142 : {
3143 11863 : }
3144 :
3145 : //! @endcond
3146 :
3147 : /************************************************************************/
3148 : /* GetDimensionSize() */
3149 : /************************************************************************/
3150 :
3151 : /** Return the size of the dimensions of the attribute.
3152 : *
3153 : * This will be an empty array for a scalar (single value) attribute.
3154 : *
3155 : * This is the same as the C function GDALAttributeGetDimensionsSize().
3156 : */
3157 321 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3158 : {
3159 321 : const auto &dims = GetDimensions();
3160 321 : std::vector<GUInt64> ret;
3161 321 : ret.reserve(dims.size());
3162 390 : for (const auto &dim : dims)
3163 69 : ret.push_back(dim->GetSize());
3164 321 : return ret;
3165 : }
3166 :
3167 : /************************************************************************/
3168 : /* GDALRawResult() */
3169 : /************************************************************************/
3170 :
3171 : //! @cond Doxygen_Suppress
3172 148 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3173 148 : size_t nEltCount)
3174 296 : : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3175 148 : m_raw(raw)
3176 : {
3177 148 : }
3178 :
3179 : //! @endcond
3180 :
3181 : /************************************************************************/
3182 : /* GDALRawResult() */
3183 : /************************************************************************/
3184 :
3185 : /** Move constructor. */
3186 0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
3187 0 : : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3188 0 : m_nSize(other.m_nSize), m_raw(other.m_raw)
3189 : {
3190 0 : other.m_nEltCount = 0;
3191 0 : other.m_nSize = 0;
3192 0 : other.m_raw = nullptr;
3193 0 : }
3194 :
3195 : /************************************************************************/
3196 : /* FreeMe() */
3197 : /************************************************************************/
3198 :
3199 148 : void GDALRawResult::FreeMe()
3200 : {
3201 148 : if (m_raw && m_dt.NeedsFreeDynamicMemory())
3202 : {
3203 47 : GByte *pabyPtr = m_raw;
3204 47 : const auto nDTSize(m_dt.GetSize());
3205 94 : for (size_t i = 0; i < m_nEltCount; ++i)
3206 : {
3207 47 : m_dt.FreeDynamicMemory(pabyPtr);
3208 47 : pabyPtr += nDTSize;
3209 : }
3210 : }
3211 148 : VSIFree(m_raw);
3212 148 : }
3213 :
3214 : /************************************************************************/
3215 : /* operator=() */
3216 : /************************************************************************/
3217 :
3218 : /** Move assignment. */
3219 0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3220 : {
3221 0 : FreeMe();
3222 0 : m_dt = std::move(other.m_dt);
3223 0 : m_nEltCount = other.m_nEltCount;
3224 0 : m_nSize = other.m_nSize;
3225 0 : m_raw = other.m_raw;
3226 0 : other.m_nEltCount = 0;
3227 0 : other.m_nSize = 0;
3228 0 : other.m_raw = nullptr;
3229 0 : return *this;
3230 : }
3231 :
3232 : /************************************************************************/
3233 : /* ~GDALRawResult() */
3234 : /************************************************************************/
3235 :
3236 : /** Destructor. */
3237 148 : GDALRawResult::~GDALRawResult()
3238 : {
3239 148 : FreeMe();
3240 148 : }
3241 :
3242 : /************************************************************************/
3243 : /* StealData() */
3244 : /************************************************************************/
3245 :
3246 : //! @cond Doxygen_Suppress
3247 : /** Return buffer to caller which becomes owner of it.
3248 : * Only to be used by GDALAttributeReadAsRaw().
3249 : */
3250 6 : GByte *GDALRawResult::StealData()
3251 : {
3252 6 : GByte *ret = m_raw;
3253 6 : m_raw = nullptr;
3254 6 : m_nEltCount = 0;
3255 6 : m_nSize = 0;
3256 6 : return ret;
3257 : }
3258 :
3259 : //! @endcond
3260 :
3261 : /************************************************************************/
3262 : /* ReadAsRaw() */
3263 : /************************************************************************/
3264 :
3265 : /** Return the raw value of an attribute.
3266 : *
3267 : *
3268 : * This is the same as the C function GDALAttributeReadAsRaw().
3269 : */
3270 148 : GDALRawResult GDALAttribute::ReadAsRaw() const
3271 : {
3272 148 : const auto nEltCount(GetTotalElementsCount());
3273 148 : const auto &dt(GetDataType());
3274 148 : const auto nDTSize(dt.GetSize());
3275 : GByte *res = static_cast<GByte *>(
3276 148 : VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3277 148 : if (!res)
3278 0 : return GDALRawResult(nullptr, dt, 0);
3279 148 : const auto &dims = GetDimensions();
3280 148 : const auto nDims = GetDimensionCount();
3281 296 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3282 296 : std::vector<size_t> count(1 + nDims);
3283 167 : for (size_t i = 0; i < nDims; i++)
3284 : {
3285 19 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3286 : }
3287 148 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3288 148 : &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3289 : {
3290 0 : VSIFree(res);
3291 0 : return GDALRawResult(nullptr, dt, 0);
3292 : }
3293 148 : return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3294 : }
3295 :
3296 : /************************************************************************/
3297 : /* ReadAsString() */
3298 : /************************************************************************/
3299 :
3300 : /** Return the value of an attribute as a string.
3301 : *
3302 : * The returned string should not be freed, and its lifetime does not
3303 : * excess a next call to ReadAsString() on the same object, or the deletion
3304 : * of the object itself.
3305 : *
3306 : * This function will only return the first element if there are several.
3307 : *
3308 : * This is the same as the C function GDALAttributeReadAsString()
3309 : *
3310 : * @return a string, or nullptr.
3311 : */
3312 1195 : const char *GDALAttribute::ReadAsString() const
3313 : {
3314 1195 : const auto nDims = GetDimensionCount();
3315 2390 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3316 2390 : std::vector<size_t> count(1 + nDims, 1);
3317 1195 : char *szRet = nullptr;
3318 1195 : if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3319 1195 : GDALExtendedDataType::CreateString(), &szRet, &szRet,
3320 3584 : sizeof(szRet)) ||
3321 1194 : szRet == nullptr)
3322 : {
3323 4 : return nullptr;
3324 : }
3325 1191 : m_osCachedVal = szRet;
3326 1191 : CPLFree(szRet);
3327 1191 : return m_osCachedVal.c_str();
3328 : }
3329 :
3330 : /************************************************************************/
3331 : /* ReadAsInt() */
3332 : /************************************************************************/
3333 :
3334 : /** Return the value of an attribute as a integer.
3335 : *
3336 : * This function will only return the first element if there are several.
3337 : *
3338 : * It can fail if its value can be converted to integer.
3339 : *
3340 : * This is the same as the C function GDALAttributeReadAsInt()
3341 : *
3342 : * @return a integer, or INT_MIN in case of error.
3343 : */
3344 161 : int GDALAttribute::ReadAsInt() const
3345 : {
3346 161 : const auto nDims = GetDimensionCount();
3347 322 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3348 161 : std::vector<size_t> count(1 + nDims, 1);
3349 161 : int nRet = INT_MIN;
3350 161 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3351 322 : GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3352 322 : return nRet;
3353 : }
3354 :
3355 : /************************************************************************/
3356 : /* ReadAsDouble() */
3357 : /************************************************************************/
3358 :
3359 : /** Return the value of an attribute as a double.
3360 : *
3361 : * This function will only return the first element if there are several.
3362 : *
3363 : * It can fail if its value can be converted to double.
3364 : *
3365 : * This is the same as the C function GDALAttributeReadAsInt()
3366 : *
3367 : * @return a double value.
3368 : */
3369 259 : double GDALAttribute::ReadAsDouble() const
3370 : {
3371 259 : const auto nDims = GetDimensionCount();
3372 518 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3373 259 : std::vector<size_t> count(1 + nDims, 1);
3374 259 : double dfRet = 0;
3375 259 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3376 259 : GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3377 259 : sizeof(dfRet));
3378 518 : return dfRet;
3379 : }
3380 :
3381 : /************************************************************************/
3382 : /* ReadAsStringArray() */
3383 : /************************************************************************/
3384 :
3385 : /** Return the value of an attribute as an array of strings.
3386 : *
3387 : * This is the same as the C function GDALAttributeReadAsStringArray()
3388 : */
3389 97 : CPLStringList GDALAttribute::ReadAsStringArray() const
3390 : {
3391 97 : const auto nElts = GetTotalElementsCount();
3392 97 : if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3393 0 : return CPLStringList();
3394 : char **papszList = static_cast<char **>(
3395 97 : VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3396 97 : const auto &dims = GetDimensions();
3397 97 : const auto nDims = GetDimensionCount();
3398 194 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3399 194 : std::vector<size_t> count(1 + nDims);
3400 149 : for (size_t i = 0; i < nDims; i++)
3401 : {
3402 52 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3403 : }
3404 97 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3405 97 : GDALExtendedDataType::CreateString(), papszList, papszList,
3406 97 : sizeof(char *) * static_cast<int>(nElts));
3407 253 : for (int i = 0; i < static_cast<int>(nElts); i++)
3408 : {
3409 156 : if (papszList[i] == nullptr)
3410 13 : papszList[i] = CPLStrdup("");
3411 : }
3412 97 : return CPLStringList(papszList);
3413 : }
3414 :
3415 : /************************************************************************/
3416 : /* ReadAsIntArray() */
3417 : /************************************************************************/
3418 :
3419 : /** Return the value of an attribute as an array of integers.
3420 : *
3421 : * This is the same as the C function GDALAttributeReadAsIntArray().
3422 : */
3423 15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
3424 : {
3425 15 : const auto nElts = GetTotalElementsCount();
3426 : #if SIZEOF_VOIDP == 4
3427 : if (nElts > static_cast<size_t>(nElts))
3428 : return {};
3429 : #endif
3430 15 : std::vector<int> res(static_cast<size_t>(nElts));
3431 15 : const auto &dims = GetDimensions();
3432 15 : const auto nDims = GetDimensionCount();
3433 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3434 30 : std::vector<size_t> count(1 + nDims);
3435 32 : for (size_t i = 0; i < nDims; i++)
3436 : {
3437 17 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3438 : }
3439 15 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3440 30 : GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3441 15 : res.size() * sizeof(res[0]));
3442 30 : return res;
3443 : }
3444 :
3445 : /************************************************************************/
3446 : /* ReadAsDoubleArray() */
3447 : /************************************************************************/
3448 :
3449 : /** Return the value of an attribute as an array of double.
3450 : *
3451 : * This is the same as the C function GDALAttributeReadAsDoubleArray().
3452 : */
3453 79 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3454 : {
3455 79 : const auto nElts = GetTotalElementsCount();
3456 : #if SIZEOF_VOIDP == 4
3457 : if (nElts > static_cast<size_t>(nElts))
3458 : return {};
3459 : #endif
3460 79 : std::vector<double> res(static_cast<size_t>(nElts));
3461 79 : const auto &dims = GetDimensions();
3462 79 : const auto nDims = GetDimensionCount();
3463 158 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3464 158 : std::vector<size_t> count(1 + nDims);
3465 142 : for (size_t i = 0; i < nDims; i++)
3466 : {
3467 63 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3468 : }
3469 79 : Read(startIdx.data(), count.data(), nullptr, nullptr,
3470 158 : GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3471 79 : res.size() * sizeof(res[0]));
3472 158 : return res;
3473 : }
3474 :
3475 : /************************************************************************/
3476 : /* Write() */
3477 : /************************************************************************/
3478 :
3479 : /** Write an attribute from raw values expressed in GetDataType()
3480 : *
3481 : * The values should be provided in the type of GetDataType() and there should
3482 : * be exactly GetTotalElementsCount() of them.
3483 : * If GetDataType() is a string, each value should be a char* pointer.
3484 : *
3485 : * This is the same as the C function GDALAttributeWriteRaw().
3486 : *
3487 : * @param pabyValue Buffer of nLen bytes.
3488 : * @param nLen Size of pabyValue in bytes. Should be equal to
3489 : * GetTotalElementsCount() * GetDataType().GetSize()
3490 : * @return true in case of success.
3491 : */
3492 91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3493 : {
3494 91 : if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3495 : {
3496 0 : CPLError(CE_Failure, CPLE_AppDefined,
3497 : "Length is not of expected value");
3498 0 : return false;
3499 : }
3500 91 : const auto &dims = GetDimensions();
3501 91 : const auto nDims = GetDimensionCount();
3502 182 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3503 182 : std::vector<size_t> count(1 + nDims);
3504 114 : for (size_t i = 0; i < nDims; i++)
3505 : {
3506 23 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3507 : }
3508 91 : return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3509 91 : pabyValue, pabyValue, nLen);
3510 : }
3511 :
3512 : /************************************************************************/
3513 : /* Write() */
3514 : /************************************************************************/
3515 :
3516 : /** Write an attribute from a string value.
3517 : *
3518 : * Type conversion will be performed if needed. If the attribute contains
3519 : * multiple values, only the first one will be updated.
3520 : *
3521 : * This is the same as the C function GDALAttributeWriteString().
3522 : *
3523 : * @param pszValue Pointer to a string.
3524 : * @return true in case of success.
3525 : */
3526 303 : bool GDALAttribute::Write(const char *pszValue)
3527 : {
3528 303 : const auto nDims = GetDimensionCount();
3529 606 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3530 303 : std::vector<size_t> count(1 + nDims, 1);
3531 303 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3532 606 : GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3533 606 : sizeof(pszValue));
3534 : }
3535 :
3536 : /************************************************************************/
3537 : /* WriteInt() */
3538 : /************************************************************************/
3539 :
3540 : /** Write an attribute from a integer value.
3541 : *
3542 : * Type conversion will be performed if needed. If the attribute contains
3543 : * multiple values, only the first one will be updated.
3544 : *
3545 : * This is the same as the C function GDALAttributeWriteInt().
3546 : *
3547 : * @param nVal Value.
3548 : * @return true in case of success.
3549 : */
3550 22 : bool GDALAttribute::WriteInt(int nVal)
3551 : {
3552 22 : const auto nDims = GetDimensionCount();
3553 44 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3554 22 : std::vector<size_t> count(1 + nDims, 1);
3555 22 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3556 44 : GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3557 44 : sizeof(nVal));
3558 : }
3559 :
3560 : /************************************************************************/
3561 : /* Write() */
3562 : /************************************************************************/
3563 :
3564 : /** Write an attribute from a double value.
3565 : *
3566 : * Type conversion will be performed if needed. If the attribute contains
3567 : * multiple values, only the first one will be updated.
3568 : *
3569 : * This is the same as the C function GDALAttributeWriteDouble().
3570 : *
3571 : * @param dfVal Value.
3572 : * @return true in case of success.
3573 : */
3574 41 : bool GDALAttribute::Write(double dfVal)
3575 : {
3576 41 : const auto nDims = GetDimensionCount();
3577 82 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3578 41 : std::vector<size_t> count(1 + nDims, 1);
3579 41 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3580 82 : GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3581 82 : sizeof(dfVal));
3582 : }
3583 :
3584 : /************************************************************************/
3585 : /* Write() */
3586 : /************************************************************************/
3587 :
3588 : /** Write an attribute from an array of strings.
3589 : *
3590 : * Type conversion will be performed if needed.
3591 : *
3592 : * Exactly GetTotalElementsCount() strings must be provided
3593 : *
3594 : * This is the same as the C function GDALAttributeWriteStringArray().
3595 : *
3596 : * @param vals Array of strings.
3597 : * @return true in case of success.
3598 : */
3599 8 : bool GDALAttribute::Write(CSLConstList vals)
3600 : {
3601 8 : if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3602 : {
3603 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3604 1 : return false;
3605 : }
3606 7 : const auto nDims = GetDimensionCount();
3607 14 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3608 7 : std::vector<size_t> count(1 + nDims);
3609 7 : const auto &dims = GetDimensions();
3610 15 : for (size_t i = 0; i < nDims; i++)
3611 8 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3612 7 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3613 7 : GDALExtendedDataType::CreateString(), vals, vals,
3614 14 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3615 : }
3616 :
3617 : /************************************************************************/
3618 : /* Write() */
3619 : /************************************************************************/
3620 :
3621 : /** Write an attribute from an array of double.
3622 : *
3623 : * Type conversion will be performed if needed.
3624 : *
3625 : * Exactly GetTotalElementsCount() strings must be provided
3626 : *
3627 : * This is the same as the C function GDALAttributeWriteDoubleArray()
3628 : *
3629 : * @param vals Array of double.
3630 : * @param nVals Should be equal to GetTotalElementsCount().
3631 : * @return true in case of success.
3632 : */
3633 17 : bool GDALAttribute::Write(const double *vals, size_t nVals)
3634 : {
3635 17 : if (nVals != GetTotalElementsCount())
3636 : {
3637 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3638 2 : return false;
3639 : }
3640 15 : const auto nDims = GetDimensionCount();
3641 30 : std::vector<GUInt64> startIdx(1 + nDims, 0);
3642 15 : std::vector<size_t> count(1 + nDims);
3643 15 : const auto &dims = GetDimensions();
3644 31 : for (size_t i = 0; i < nDims; i++)
3645 16 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3646 15 : return Write(startIdx.data(), count.data(), nullptr, nullptr,
3647 15 : GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3648 30 : static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3649 : }
3650 :
3651 : /************************************************************************/
3652 : /* GDALMDArray() */
3653 : /************************************************************************/
3654 :
3655 : //! @cond Doxygen_Suppress
3656 6008 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3657 : CPL_UNUSED const std::string &osName,
3658 0 : const std::string &osContext)
3659 : :
3660 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3661 : GDALAbstractMDArray(osParentName, osName),
3662 : #endif
3663 6008 : m_osContext(osContext)
3664 : {
3665 6008 : }
3666 :
3667 : //! @endcond
3668 :
3669 : /************************************************************************/
3670 : /* GetTotalCopyCost() */
3671 : /************************************************************************/
3672 :
3673 : /** Return a total "cost" to copy the array.
3674 : *
3675 : * Used as a parameter for CopyFrom()
3676 : */
3677 43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
3678 : {
3679 86 : return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3680 86 : GetTotalElementsCount() * GetDataType().GetSize();
3681 : }
3682 :
3683 : /************************************************************************/
3684 : /* CopyFromAllExceptValues() */
3685 : /************************************************************************/
3686 :
3687 : //! @cond Doxygen_Suppress
3688 :
3689 144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3690 : bool bStrict, GUInt64 &nCurCost,
3691 : const GUInt64 nTotalCost,
3692 : GDALProgressFunc pfnProgress,
3693 : void *pProgressData)
3694 : {
3695 : // Nodata setting must be one of the first things done for TileDB
3696 144 : const void *pNoData = poSrcArray->GetRawNoDataValue();
3697 144 : if (pNoData && poSrcArray->GetDataType() == GetDataType())
3698 : {
3699 13 : SetRawNoDataValue(pNoData);
3700 : }
3701 :
3702 144 : const bool bThisIsUnscaledArray =
3703 144 : dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3704 288 : auto attrs = poSrcArray->GetAttributes();
3705 191 : for (const auto &attr : attrs)
3706 : {
3707 47 : const auto &osAttrName = attr->GetName();
3708 47 : if (bThisIsUnscaledArray)
3709 : {
3710 6 : if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3711 7 : osAttrName == "valid_min" || osAttrName == "valid_max" ||
3712 1 : osAttrName == "valid_range")
3713 : {
3714 1 : continue;
3715 : }
3716 : }
3717 :
3718 46 : auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3719 92 : attr->GetDataType());
3720 46 : if (!dstAttr)
3721 : {
3722 0 : if (bStrict)
3723 0 : return false;
3724 0 : continue;
3725 : }
3726 46 : auto raw = attr->ReadAsRaw();
3727 46 : if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3728 0 : return false;
3729 : }
3730 144 : if (!attrs.empty())
3731 : {
3732 26 : nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3733 46 : if (pfnProgress &&
3734 20 : !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3735 0 : return false;
3736 : }
3737 :
3738 144 : auto srcSRS = poSrcArray->GetSpatialRef();
3739 144 : if (srcSRS)
3740 : {
3741 11 : SetSpatialRef(srcSRS.get());
3742 : }
3743 :
3744 144 : const std::string &osUnit(poSrcArray->GetUnit());
3745 144 : if (!osUnit.empty())
3746 : {
3747 18 : SetUnit(osUnit);
3748 : }
3749 :
3750 144 : bool bGotValue = false;
3751 144 : GDALDataType eOffsetStorageType = GDT_Unknown;
3752 : const double dfOffset =
3753 144 : poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3754 144 : if (bGotValue)
3755 : {
3756 3 : SetOffset(dfOffset, eOffsetStorageType);
3757 : }
3758 :
3759 144 : bGotValue = false;
3760 144 : GDALDataType eScaleStorageType = GDT_Unknown;
3761 144 : const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3762 144 : if (bGotValue)
3763 : {
3764 3 : SetScale(dfScale, eScaleStorageType);
3765 : }
3766 :
3767 144 : return true;
3768 : }
3769 :
3770 : //! @endcond
3771 :
3772 : /************************************************************************/
3773 : /* CopyFrom() */
3774 : /************************************************************************/
3775 :
3776 : /** Copy the content of an array into a new (generally empty) array.
3777 : *
3778 : * @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
3779 : * of some output drivers this is not recommended)
3780 : * @param poSrcArray Source array. Should NOT be nullptr.
3781 : * @param bStrict Whether to enable stict mode. In strict mode, any error will
3782 : * stop the copy. In relaxed mode, the copy will be attempted to
3783 : * be pursued.
3784 : * @param nCurCost Should be provided as a variable initially set to 0.
3785 : * @param nTotalCost Total cost from GetTotalCopyCost().
3786 : * @param pfnProgress Progress callback, or nullptr.
3787 : * @param pProgressData Progress user data, or nulptr.
3788 : *
3789 : * @return true in case of success (or partial success if bStrict == false).
3790 : */
3791 41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
3792 : const GDALMDArray *poSrcArray, bool bStrict,
3793 : GUInt64 &nCurCost, const GUInt64 nTotalCost,
3794 : GDALProgressFunc pfnProgress, void *pProgressData)
3795 : {
3796 41 : if (pfnProgress == nullptr)
3797 4 : pfnProgress = GDALDummyProgress;
3798 :
3799 41 : nCurCost += GDALMDArray::COPY_COST;
3800 :
3801 41 : if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
3802 : pfnProgress, pProgressData))
3803 : {
3804 0 : return false;
3805 : }
3806 :
3807 41 : const auto &dims = poSrcArray->GetDimensions();
3808 41 : const auto nDTSize = poSrcArray->GetDataType().GetSize();
3809 41 : if (dims.empty())
3810 : {
3811 2 : std::vector<GByte> abyTmp(nDTSize);
3812 2 : if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
3813 2 : GetDataType(), &abyTmp[0]) &&
3814 2 : Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
3815 4 : &abyTmp[0])) &&
3816 : bStrict)
3817 : {
3818 0 : return false;
3819 : }
3820 2 : nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
3821 2 : if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3822 0 : return false;
3823 : }
3824 : else
3825 : {
3826 39 : std::vector<GUInt64> arrayStartIdx(dims.size());
3827 39 : std::vector<GUInt64> count(dims.size());
3828 106 : for (size_t i = 0; i < dims.size(); i++)
3829 : {
3830 67 : count[i] = static_cast<size_t>(dims[i]->GetSize());
3831 : }
3832 :
3833 : struct CopyFunc
3834 : {
3835 : GDALMDArray *poDstArray = nullptr;
3836 : std::vector<GByte> abyTmp{};
3837 : GDALProgressFunc pfnProgress = nullptr;
3838 : void *pProgressData = nullptr;
3839 : GUInt64 nCurCost = 0;
3840 : GUInt64 nTotalCost = 0;
3841 : GUInt64 nTotalBytesThisArray = 0;
3842 : bool bStop = false;
3843 :
3844 57 : static bool f(GDALAbstractMDArray *l_poSrcArray,
3845 : const GUInt64 *chunkArrayStartIdx,
3846 : const size_t *chunkCount, GUInt64 iCurChunk,
3847 : GUInt64 nChunkCount, void *pUserData)
3848 : {
3849 57 : const auto &dt(l_poSrcArray->GetDataType());
3850 57 : auto data = static_cast<CopyFunc *>(pUserData);
3851 57 : auto poDstArray = data->poDstArray;
3852 57 : if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
3853 57 : nullptr, dt, &data->abyTmp[0]))
3854 : {
3855 0 : return false;
3856 : }
3857 : bool bRet =
3858 57 : poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
3859 57 : nullptr, dt, &data->abyTmp[0]);
3860 57 : if (dt.NeedsFreeDynamicMemory())
3861 : {
3862 2 : const auto l_nDTSize = dt.GetSize();
3863 2 : GByte *ptr = &data->abyTmp[0];
3864 2 : const size_t l_nDims(l_poSrcArray->GetDimensionCount());
3865 2 : size_t nEltCount = 1;
3866 4 : for (size_t i = 0; i < l_nDims; ++i)
3867 : {
3868 2 : nEltCount *= chunkCount[i];
3869 : }
3870 10 : for (size_t i = 0; i < nEltCount; i++)
3871 : {
3872 8 : dt.FreeDynamicMemory(ptr);
3873 8 : ptr += l_nDTSize;
3874 : }
3875 : }
3876 57 : if (!bRet)
3877 : {
3878 0 : return false;
3879 : }
3880 :
3881 57 : double dfCurCost =
3882 57 : double(data->nCurCost) + double(iCurChunk) / nChunkCount *
3883 57 : data->nTotalBytesThisArray;
3884 57 : if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
3885 : data->pProgressData))
3886 : {
3887 0 : data->bStop = true;
3888 0 : return false;
3889 : }
3890 :
3891 57 : return true;
3892 : }
3893 : };
3894 :
3895 39 : CopyFunc copyFunc;
3896 39 : copyFunc.poDstArray = this;
3897 39 : copyFunc.nCurCost = nCurCost;
3898 39 : copyFunc.nTotalCost = nTotalCost;
3899 39 : copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
3900 39 : copyFunc.pfnProgress = pfnProgress;
3901 39 : copyFunc.pProgressData = pProgressData;
3902 : const char *pszSwathSize =
3903 39 : CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
3904 : const size_t nMaxChunkSize =
3905 : pszSwathSize
3906 39 : ? static_cast<size_t>(
3907 1 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
3908 1 : CPLAtoGIntBig(pszSwathSize)))
3909 : : static_cast<size_t>(
3910 38 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
3911 38 : GDALGetCacheMax64() / 4));
3912 39 : const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
3913 39 : size_t nRealChunkSize = nDTSize;
3914 106 : for (const auto &nChunkSize : anChunkSizes)
3915 : {
3916 67 : nRealChunkSize *= nChunkSize;
3917 : }
3918 : try
3919 : {
3920 39 : copyFunc.abyTmp.resize(nRealChunkSize);
3921 : }
3922 0 : catch (const std::exception &)
3923 : {
3924 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3925 : "Cannot allocate temporary buffer");
3926 0 : nCurCost += copyFunc.nTotalBytesThisArray;
3927 0 : return false;
3928 : }
3929 116 : if (copyFunc.nTotalBytesThisArray != 0 &&
3930 38 : !const_cast<GDALMDArray *>(poSrcArray)
3931 38 : ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
3932 : anChunkSizes.data(), CopyFunc::f,
3933 77 : ©Func) &&
3934 0 : (bStrict || copyFunc.bStop))
3935 : {
3936 0 : nCurCost += copyFunc.nTotalBytesThisArray;
3937 0 : return false;
3938 : }
3939 39 : nCurCost += copyFunc.nTotalBytesThisArray;
3940 : }
3941 :
3942 41 : return true;
3943 : }
3944 :
3945 : /************************************************************************/
3946 : /* GetStructuralInfo() */
3947 : /************************************************************************/
3948 :
3949 : /** Return structural information on the array.
3950 : *
3951 : * This may be the compression, etc..
3952 : *
3953 : * The return value should not be freed and is valid until GDALMDArray is
3954 : * released or this function called again.
3955 : *
3956 : * This is the same as the C function GDALMDArrayGetStructuralInfo().
3957 : */
3958 55 : CSLConstList GDALMDArray::GetStructuralInfo() const
3959 : {
3960 55 : return nullptr;
3961 : }
3962 :
3963 : /************************************************************************/
3964 : /* AdviseRead() */
3965 : /************************************************************************/
3966 :
3967 : /** Advise driver of upcoming read requests.
3968 : *
3969 : * Some GDAL drivers operate more efficiently if they know in advance what
3970 : * set of upcoming read requests will be made. The AdviseRead() method allows
3971 : * an application to notify the driver of the region of interest.
3972 : *
3973 : * Many drivers just ignore the AdviseRead() call, but it can dramatically
3974 : * accelerate access via some drivers. One such case is when reading through
3975 : * a DAP dataset with the netCDF driver (a in-memory cache array is then created
3976 : * with the region of interest defined by AdviseRead())
3977 : *
3978 : * This is the same as the C function GDALMDArrayAdviseRead().
3979 : *
3980 : * @param arrayStartIdx Values representing the starting index to read
3981 : * in each dimension (in [0, aoDims[i].GetSize()-1] range).
3982 : * Array of GetDimensionCount() values.
3983 : * Can be nullptr as a synonymous for [0 for i in
3984 : * range(GetDimensionCount() ]
3985 : *
3986 : * @param count Values representing the number of values to extract in
3987 : * each dimension.
3988 : * Array of GetDimensionCount() values.
3989 : * Can be nullptr as a synonymous for
3990 : * [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
3991 : * range(GetDimensionCount() ]
3992 : *
3993 : * @param papszOptions Driver specific options, or nullptr. Consult driver
3994 : * documentation.
3995 : *
3996 : * @return true in case of success (ignoring the advice is a success)
3997 : *
3998 : * @since GDAL 3.2
3999 : */
4000 25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4001 : CSLConstList papszOptions) const
4002 : {
4003 25 : const auto nDimCount = GetDimensionCount();
4004 25 : if (nDimCount == 0)
4005 2 : return true;
4006 :
4007 46 : std::vector<GUInt64> tmp_arrayStartIdx;
4008 23 : if (arrayStartIdx == nullptr)
4009 : {
4010 0 : tmp_arrayStartIdx.resize(nDimCount);
4011 0 : arrayStartIdx = tmp_arrayStartIdx.data();
4012 : }
4013 :
4014 46 : std::vector<size_t> tmp_count;
4015 23 : if (count == nullptr)
4016 : {
4017 0 : tmp_count.resize(nDimCount);
4018 0 : const auto &dims = GetDimensions();
4019 0 : for (size_t i = 0; i < nDimCount; i++)
4020 : {
4021 0 : const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4022 : #if SIZEOF_VOIDP < 8
4023 : if (nSize != static_cast<size_t>(nSize))
4024 : {
4025 : CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4026 : return false;
4027 : }
4028 : #endif
4029 0 : tmp_count[i] = static_cast<size_t>(nSize);
4030 : }
4031 0 : count = tmp_count.data();
4032 : }
4033 :
4034 46 : std::vector<GInt64> tmp_arrayStep;
4035 46 : std::vector<GPtrDiff_t> tmp_bufferStride;
4036 23 : const GInt64 *arrayStep = nullptr;
4037 23 : const GPtrDiff_t *bufferStride = nullptr;
4038 23 : if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4039 46 : GDALExtendedDataType::Create(GDT_Unknown),
4040 : nullptr, nullptr, 0, tmp_arrayStep,
4041 : tmp_bufferStride))
4042 : {
4043 1 : return false;
4044 : }
4045 :
4046 22 : return IAdviseRead(arrayStartIdx, count, papszOptions);
4047 : }
4048 :
4049 : /************************************************************************/
4050 : /* IAdviseRead() */
4051 : /************************************************************************/
4052 :
4053 : //! @cond Doxygen_Suppress
4054 3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4055 : CSLConstList /* papszOptions*/) const
4056 : {
4057 3 : return true;
4058 : }
4059 :
4060 : //! @endcond
4061 :
4062 : /************************************************************************/
4063 : /* MassageName() */
4064 : /************************************************************************/
4065 :
4066 : //! @cond Doxygen_Suppress
4067 32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4068 : {
4069 32 : std::string ret;
4070 604 : for (const char ch : inputName)
4071 : {
4072 572 : if (!isalnum(static_cast<unsigned char>(ch)))
4073 138 : ret += '_';
4074 : else
4075 434 : ret += ch;
4076 : }
4077 32 : return ret;
4078 : }
4079 :
4080 : //! @endcond
4081 :
4082 : /************************************************************************/
4083 : /* GetCacheRootGroup() */
4084 : /************************************************************************/
4085 :
4086 : //! @cond Doxygen_Suppress
4087 : std::shared_ptr<GDALGroup>
4088 1300 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4089 : std::string &osCacheFilenameOut) const
4090 : {
4091 1300 : const auto &osFilename = GetFilename();
4092 1300 : if (osFilename.empty())
4093 : {
4094 0 : CPLError(CE_Failure, CPLE_AppDefined,
4095 : "Cannot cache an array with an empty filename");
4096 0 : return nullptr;
4097 : }
4098 :
4099 1300 : osCacheFilenameOut = osFilename + ".gmac";
4100 1300 : const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4101 1300 : if (pszProxy != nullptr)
4102 7 : osCacheFilenameOut = pszProxy;
4103 :
4104 1300 : std::unique_ptr<GDALDataset> poDS;
4105 : VSIStatBufL sStat;
4106 1300 : if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4107 : {
4108 28 : poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4109 : GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4110 : nullptr, nullptr, nullptr));
4111 : }
4112 1300 : if (poDS)
4113 : {
4114 28 : CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4115 28 : return poDS->GetRootGroup();
4116 : }
4117 :
4118 1272 : if (bCanCreate)
4119 : {
4120 4 : const char *pszDrvName = "netCDF";
4121 4 : GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4122 4 : if (poDrv == nullptr)
4123 : {
4124 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4125 : pszDrvName);
4126 0 : return nullptr;
4127 : }
4128 : {
4129 8 : CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4130 8 : CPLErrorStateBackuper oErrorStateBackuper;
4131 4 : poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4132 : nullptr, nullptr));
4133 : }
4134 4 : if (!poDS)
4135 : {
4136 1 : pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4137 1 : if (pszProxy)
4138 : {
4139 1 : osCacheFilenameOut = pszProxy;
4140 1 : poDS.reset(poDrv->CreateMultiDimensional(
4141 : osCacheFilenameOut.c_str(), nullptr, nullptr));
4142 : }
4143 : }
4144 4 : if (poDS)
4145 : {
4146 4 : CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4147 4 : return poDS->GetRootGroup();
4148 : }
4149 : else
4150 : {
4151 0 : CPLError(CE_Failure, CPLE_AppDefined,
4152 : "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4153 : "configuration option to write the cache in "
4154 : "another directory",
4155 : osCacheFilenameOut.c_str());
4156 : }
4157 : }
4158 :
4159 1268 : return nullptr;
4160 : }
4161 :
4162 : //! @endcond
4163 :
4164 : /************************************************************************/
4165 : /* Cache() */
4166 : /************************************************************************/
4167 :
4168 : /** Cache the content of the array into an auxiliary filename.
4169 : *
4170 : * The main purpose of this method is to be able to cache views that are
4171 : * expensive to compute, such as transposed arrays.
4172 : *
4173 : * The array will be stored in a file whose name is the one of
4174 : * GetFilename(), with an extra .gmac extension (stands for GDAL
4175 : * Multidimensional Array Cache). The cache is a netCDF dataset.
4176 : *
4177 : * If the .gmac file cannot be written next to the dataset, the
4178 : * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4179 : * directory.
4180 : *
4181 : * The GDALMDArray::Read() method will automatically use the cache when it
4182 : * exists. There is no timestamp checks between the source array and the cached
4183 : * array. If the source arrays changes, the cache must be manually deleted.
4184 : *
4185 : * This is the same as the C function GDALMDArrayCache()
4186 : *
4187 : * @note Driver implementation: optionally implemented.
4188 : *
4189 : * @param papszOptions List of options, null terminated, or NULL. Currently
4190 : * the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4191 : * to specify the block size of the cached array.
4192 : * @return true in case of success.
4193 : */
4194 6 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
4195 : {
4196 12 : std::string osCacheFilename;
4197 12 : auto poRG = GetCacheRootGroup(true, osCacheFilename);
4198 6 : if (!poRG)
4199 0 : return false;
4200 :
4201 12 : const std::string osCachedArrayName(MassageName(GetFullName()));
4202 6 : if (poRG->OpenMDArray(osCachedArrayName))
4203 : {
4204 2 : CPLError(CE_Failure, CPLE_NotSupported,
4205 : "An array with same name %s already exists in %s",
4206 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4207 2 : return false;
4208 : }
4209 :
4210 8 : CPLStringList aosOptions;
4211 4 : aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4212 4 : const auto &aoDims = GetDimensions();
4213 8 : std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4214 4 : if (!aoDims.empty())
4215 : {
4216 : std::string osBlockSize(
4217 4 : CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4218 4 : if (osBlockSize.empty())
4219 : {
4220 6 : const auto anBlockSize = GetBlockSize();
4221 3 : int idxDim = 0;
4222 10 : for (auto nBlockSize : anBlockSize)
4223 : {
4224 7 : if (idxDim > 0)
4225 4 : osBlockSize += ',';
4226 7 : if (nBlockSize == 0)
4227 7 : nBlockSize = 256;
4228 7 : nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4229 : osBlockSize +=
4230 7 : std::to_string(static_cast<uint64_t>(nBlockSize));
4231 7 : idxDim++;
4232 : }
4233 : }
4234 4 : aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4235 :
4236 4 : int idxDim = 0;
4237 13 : for (const auto &poDim : aoDims)
4238 : {
4239 9 : auto poNewDim = poRG->CreateDimension(
4240 18 : osCachedArrayName + '_' + std::to_string(idxDim),
4241 18 : poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4242 9 : if (!poNewDim)
4243 0 : return false;
4244 9 : aoNewDims.emplace_back(poNewDim);
4245 9 : idxDim++;
4246 : }
4247 : }
4248 :
4249 4 : auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4250 8 : GetDataType(), aosOptions.List());
4251 4 : if (!poCachedArray)
4252 : {
4253 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4254 : osCachedArrayName.c_str(), osCacheFilename.c_str());
4255 0 : return false;
4256 : }
4257 :
4258 4 : GUInt64 nCost = 0;
4259 8 : return poCachedArray->CopyFrom(nullptr, this,
4260 : false, // strict
4261 4 : nCost, GetTotalCopyCost(), nullptr, nullptr);
4262 : }
4263 :
4264 : /************************************************************************/
4265 : /* Read() */
4266 : /************************************************************************/
4267 :
4268 3614 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4269 : const GInt64 *arrayStep, // step in elements
4270 : const GPtrDiff_t *bufferStride, // stride in elements
4271 : const GDALExtendedDataType &bufferDataType,
4272 : void *pDstBuffer, const void *pDstBufferAllocStart,
4273 : size_t nDstBufferAllocSize) const
4274 : {
4275 3614 : if (!m_bHasTriedCachedArray)
4276 : {
4277 1578 : m_bHasTriedCachedArray = true;
4278 1578 : if (IsCacheable())
4279 : {
4280 1578 : const auto &osFilename = GetFilename();
4281 2678 : if (!osFilename.empty() &&
4282 1100 : !EQUAL(CPLGetExtension(osFilename.c_str()), "gmac"))
4283 : {
4284 2180 : std::string osCacheFilename;
4285 2180 : auto poRG = GetCacheRootGroup(false, osCacheFilename);
4286 1090 : if (poRG)
4287 : {
4288 : const std::string osCachedArrayName(
4289 32 : MassageName(GetFullName()));
4290 16 : m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4291 16 : if (m_poCachedArray)
4292 : {
4293 6 : const auto &dims = GetDimensions();
4294 : const auto &cachedDims =
4295 6 : m_poCachedArray->GetDimensions();
4296 6 : const size_t nDims = dims.size();
4297 : bool ok =
4298 12 : m_poCachedArray->GetDataType() == GetDataType() &&
4299 6 : cachedDims.size() == nDims;
4300 19 : for (size_t i = 0; ok && i < nDims; ++i)
4301 : {
4302 13 : ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4303 : }
4304 6 : if (ok)
4305 : {
4306 6 : CPLDebug("GDAL", "Cached array for %s found in %s",
4307 : osCachedArrayName.c_str(),
4308 : osCacheFilename.c_str());
4309 : }
4310 : else
4311 : {
4312 0 : CPLError(CE_Warning, CPLE_AppDefined,
4313 : "Cached array %s in %s has incompatible "
4314 : "characteristics with current array.",
4315 : osCachedArrayName.c_str(),
4316 : osCacheFilename.c_str());
4317 0 : m_poCachedArray.reset();
4318 : }
4319 : }
4320 : }
4321 : }
4322 : }
4323 : }
4324 :
4325 3614 : const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4326 3614 : if (!array->GetDataType().CanConvertTo(bufferDataType))
4327 : {
4328 0 : CPLError(CE_Failure, CPLE_AppDefined,
4329 : "Array data type is not convertible to buffer data type");
4330 0 : return false;
4331 : }
4332 :
4333 7228 : std::vector<GInt64> tmp_arrayStep;
4334 7228 : std::vector<GPtrDiff_t> tmp_bufferStride;
4335 3614 : if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4336 : bufferStride, bufferDataType, pDstBuffer,
4337 : pDstBufferAllocStart, nDstBufferAllocSize,
4338 : tmp_arrayStep, tmp_bufferStride))
4339 : {
4340 9 : return false;
4341 : }
4342 :
4343 3605 : return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4344 3605 : bufferDataType, pDstBuffer);
4345 : }
4346 :
4347 : /************************************************************************/
4348 : /* GetRootGroup() */
4349 : /************************************************************************/
4350 :
4351 : /** Return the root group to which this arrays belongs too.
4352 : *
4353 : * Note that arrays may be free standing and some drivers may not implement
4354 : * this method, hence nullptr may be returned.
4355 : *
4356 : * It is used internally by the GetResampled() method to detect if GLT
4357 : * orthorectification is available.
4358 : *
4359 : * @return the root group, or nullptr.
4360 : * @since GDAL 3.8
4361 : */
4362 0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4363 : {
4364 0 : return nullptr;
4365 : }
4366 :
4367 : //! @cond Doxygen_Suppress
4368 :
4369 : /************************************************************************/
4370 : /* IsTransposedRequest() */
4371 : /************************************************************************/
4372 :
4373 640 : bool GDALMDArray::IsTransposedRequest(
4374 : const size_t *count,
4375 : const GPtrDiff_t *bufferStride) const // stride in elements
4376 : {
4377 : /*
4378 : For example:
4379 : count = [2,3,4]
4380 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
4381 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
4382 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
4383 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
4384 : */
4385 640 : const size_t nDims(GetDimensionCount());
4386 640 : size_t nCurStrideForRowMajorStrides = 1;
4387 640 : bool bRowMajorStrides = true;
4388 640 : size_t nElts = 1;
4389 640 : size_t nLastIdx = 0;
4390 1837 : for (size_t i = nDims; i > 0;)
4391 : {
4392 1197 : --i;
4393 1197 : if (bufferStride[i] < 0)
4394 0 : return false;
4395 1197 : if (static_cast<size_t>(bufferStride[i]) !=
4396 : nCurStrideForRowMajorStrides)
4397 : {
4398 209 : bRowMajorStrides = false;
4399 : }
4400 : // Integer overflows have already been checked in CheckReadWriteParams()
4401 1197 : nCurStrideForRowMajorStrides *= count[i];
4402 1197 : nElts *= count[i];
4403 1197 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4404 : }
4405 640 : if (bRowMajorStrides)
4406 503 : return false;
4407 137 : return nLastIdx == nElts - 1;
4408 : }
4409 :
4410 : /************************************************************************/
4411 : /* CopyToFinalBufferSameDataType() */
4412 : /************************************************************************/
4413 :
4414 : template <size_t N>
4415 60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4416 : size_t nDims, const size_t *count,
4417 : const GPtrDiff_t *bufferStride)
4418 : {
4419 120 : std::vector<size_t> anStackCount(nDims);
4420 120 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4421 60 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4422 : #if defined(__GNUC__)
4423 : #pragma GCC diagnostic push
4424 : #pragma GCC diagnostic ignored "-Wnull-dereference"
4425 : #endif
4426 60 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4427 : #if defined(__GNUC__)
4428 : #pragma GCC diagnostic pop
4429 : #endif
4430 60 : size_t iDim = 0;
4431 :
4432 749 : lbl_next_depth:
4433 749 : if (iDim == nDims - 1)
4434 : {
4435 661 : size_t n = count[iDim];
4436 661 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4437 661 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
4438 29186 : while (n > 0)
4439 : {
4440 28525 : --n;
4441 28525 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
4442 28525 : pabyDstBuffer += bufferStrideLastDim;
4443 28525 : pabySrcBuffer += N;
4444 : }
4445 : }
4446 : else
4447 : {
4448 88 : anStackCount[iDim] = count[iDim];
4449 : while (true)
4450 : {
4451 689 : ++iDim;
4452 689 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4453 689 : goto lbl_next_depth;
4454 689 : lbl_return_to_caller_in_loop:
4455 689 : --iDim;
4456 689 : --anStackCount[iDim];
4457 689 : if (anStackCount[iDim] == 0)
4458 88 : break;
4459 601 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4460 : }
4461 : }
4462 749 : if (iDim > 0)
4463 689 : goto lbl_return_to_caller_in_loop;
4464 60 : }
4465 :
4466 : /************************************************************************/
4467 : /* CopyToFinalBuffer() */
4468 : /************************************************************************/
4469 :
4470 117 : static void CopyToFinalBuffer(const void *pSrcBuffer,
4471 : const GDALExtendedDataType &eSrcDataType,
4472 : void *pDstBuffer,
4473 : const GDALExtendedDataType &eDstDataType,
4474 : size_t nDims, const size_t *count,
4475 : const GPtrDiff_t *bufferStride)
4476 : {
4477 117 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4478 : // Use specialized implementation for well-known data types when no
4479 : // type conversion is needed
4480 117 : if (eSrcDataType == eDstDataType)
4481 : {
4482 100 : if (nSrcDataTypeSize == 1)
4483 : {
4484 41 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4485 : count, bufferStride);
4486 60 : return;
4487 : }
4488 59 : else if (nSrcDataTypeSize == 2)
4489 : {
4490 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4491 : count, bufferStride);
4492 1 : return;
4493 : }
4494 58 : else if (nSrcDataTypeSize == 4)
4495 : {
4496 14 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4497 : count, bufferStride);
4498 14 : return;
4499 : }
4500 44 : else if (nSrcDataTypeSize == 8)
4501 : {
4502 4 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4503 : count, bufferStride);
4504 4 : return;
4505 : }
4506 : }
4507 :
4508 57 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
4509 114 : std::vector<size_t> anStackCount(nDims);
4510 114 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4511 57 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4512 57 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4513 57 : size_t iDim = 0;
4514 :
4515 326 : lbl_next_depth:
4516 326 : if (iDim == nDims - 1)
4517 : {
4518 316 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4519 316 : pabyDstBufferStack[iDim], eDstDataType,
4520 316 : bufferStride[iDim], count[iDim]);
4521 316 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4522 : }
4523 : else
4524 : {
4525 10 : anStackCount[iDim] = count[iDim];
4526 : while (true)
4527 : {
4528 269 : ++iDim;
4529 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4530 269 : goto lbl_next_depth;
4531 269 : lbl_return_to_caller_in_loop:
4532 269 : --iDim;
4533 269 : --anStackCount[iDim];
4534 269 : if (anStackCount[iDim] == 0)
4535 10 : break;
4536 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4537 : }
4538 : }
4539 326 : if (iDim > 0)
4540 269 : goto lbl_return_to_caller_in_loop;
4541 : }
4542 :
4543 : /************************************************************************/
4544 : /* Transpose2D() */
4545 : /************************************************************************/
4546 :
4547 : template <class T>
4548 39 : static void Transpose2D(T *dst, const T *src, size_t src_height,
4549 : size_t src_width)
4550 : {
4551 39 : constexpr size_t blocksize = 32;
4552 142 : for (size_t i = 0; i < src_height; i += blocksize)
4553 : {
4554 1264 : for (size_t j = 0; j < src_width; j += blocksize)
4555 : {
4556 : // transpose the block beginning at [i,j]
4557 1161 : const size_t max_k = std::min(i + blocksize, src_height);
4558 36699 : for (size_t k = i; k < max_k; ++k)
4559 : {
4560 35538 : const size_t max_l = std::min(j + blocksize, src_width);
4561 1135486 : for (size_t l = j; l < max_l; ++l)
4562 : {
4563 1099948 : dst[k + l * src_height] = src[l + k * src_width];
4564 : }
4565 : }
4566 : }
4567 : }
4568 39 : }
4569 :
4570 : /************************************************************************/
4571 : /* TransposeLast2Dims() */
4572 : /************************************************************************/
4573 :
4574 19 : static bool TransposeLast2Dims(void *pDstBuffer,
4575 : const GDALExtendedDataType &eDT,
4576 : const size_t nDims, const size_t *count,
4577 : const size_t nEltsNonLast2Dims)
4578 : {
4579 19 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4580 19 : const auto nDTSize = eDT.GetSize();
4581 : void *pTempBufferForLast2DimsTranspose =
4582 19 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4583 19 : if (pTempBufferForLast2DimsTranspose == nullptr)
4584 0 : return false;
4585 :
4586 19 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4587 58 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4588 : {
4589 39 : if (nDTSize == 1)
4590 : {
4591 14 : Transpose2D(
4592 : static_cast<uint8_t *>(pTempBufferForLast2DimsTranspose),
4593 : reinterpret_cast<const uint8_t *>(pabyDstBuffer),
4594 14 : count[nDims - 2], count[nDims - 1]);
4595 : }
4596 25 : else if (nDTSize == 2)
4597 : {
4598 8 : Transpose2D(
4599 : static_cast<uint16_t *>(pTempBufferForLast2DimsTranspose),
4600 : reinterpret_cast<const uint16_t *>(pabyDstBuffer),
4601 8 : count[nDims - 2], count[nDims - 1]);
4602 : }
4603 17 : else if (nDTSize == 4)
4604 : {
4605 9 : Transpose2D(
4606 : static_cast<uint32_t *>(pTempBufferForLast2DimsTranspose),
4607 : reinterpret_cast<const uint32_t *>(pabyDstBuffer),
4608 9 : count[nDims - 2], count[nDims - 1]);
4609 : }
4610 8 : else if (nDTSize == 8)
4611 : {
4612 8 : Transpose2D(
4613 : static_cast<uint64_t *>(pTempBufferForLast2DimsTranspose),
4614 : reinterpret_cast<const uint64_t *>(pabyDstBuffer),
4615 8 : count[nDims - 2], count[nDims - 1]);
4616 : }
4617 : else
4618 : {
4619 0 : CPLAssert(false);
4620 : }
4621 39 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4622 : nDTSize * nEltsLast2Dims);
4623 39 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
4624 : }
4625 :
4626 19 : VSIFree(pTempBufferForLast2DimsTranspose);
4627 :
4628 19 : return true;
4629 : }
4630 :
4631 : /************************************************************************/
4632 : /* ReadForTransposedRequest() */
4633 : /************************************************************************/
4634 :
4635 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
4636 : // transposed view yield to extremely poor/unusable performance. This fixes
4637 : // this by using temporary memory to read in a contiguous buffer in a
4638 : // row-major order, and then do the transposition to the final buffer.
4639 :
4640 136 : bool GDALMDArray::ReadForTransposedRequest(
4641 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4642 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4643 : void *pDstBuffer) const
4644 : {
4645 136 : const size_t nDims(GetDimensionCount());
4646 136 : if (nDims == 0)
4647 : {
4648 0 : CPLAssert(false);
4649 : return false; // shouldn't happen
4650 : }
4651 136 : size_t nElts = 1;
4652 394 : for (size_t i = 0; i < nDims; ++i)
4653 258 : nElts *= count[i];
4654 :
4655 272 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4656 136 : tmpBufferStrides.back() = 1;
4657 258 : for (size_t i = nDims - 1; i > 0;)
4658 : {
4659 122 : --i;
4660 122 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4661 : }
4662 :
4663 136 : const auto &eDT = GetDataType();
4664 136 : const auto nDTSize = eDT.GetSize();
4665 255 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4666 271 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4667 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4668 : {
4669 : // Optimization of the optimization if only the last 2 dims are
4670 : // transposed that saves on temporary buffer allocation
4671 23 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4672 23 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4673 23 : bool bRowMajorStridesForNonLast2Dims = true;
4674 23 : size_t nEltsNonLast2Dims = 1;
4675 40 : for (size_t i = nDims - 2; i > 0;)
4676 : {
4677 17 : --i;
4678 17 : if (static_cast<size_t>(bufferStride[i]) !=
4679 : nCurStrideForRowMajorStrides)
4680 : {
4681 4 : bRowMajorStridesForNonLast2Dims = false;
4682 : }
4683 : // Integer overflows have already been checked in
4684 : // CheckReadWriteParams()
4685 17 : nCurStrideForRowMajorStrides *= count[i];
4686 17 : nEltsNonLast2Dims *= count[i];
4687 : }
4688 23 : if (bRowMajorStridesForNonLast2Dims)
4689 : {
4690 : // We read in the final buffer!
4691 19 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4692 19 : eDT, pDstBuffer))
4693 : {
4694 0 : return false;
4695 : }
4696 :
4697 19 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4698 19 : nEltsNonLast2Dims);
4699 : }
4700 : }
4701 :
4702 117 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4703 117 : if (pTempBuffer == nullptr)
4704 0 : return false;
4705 :
4706 117 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4707 117 : pTempBuffer))
4708 : {
4709 0 : VSIFree(pTempBuffer);
4710 0 : return false;
4711 : }
4712 117 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4713 : count, bufferStride);
4714 :
4715 117 : if (eDT.NeedsFreeDynamicMemory())
4716 : {
4717 46 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4718 92 : for (size_t i = 0; i < nElts; ++i)
4719 : {
4720 46 : eDT.FreeDynamicMemory(pabyPtr);
4721 46 : pabyPtr += nDTSize;
4722 : }
4723 : }
4724 :
4725 117 : VSIFree(pTempBuffer);
4726 117 : return true;
4727 : }
4728 :
4729 : /************************************************************************/
4730 : /* IsStepOneContiguousRowMajorOrderedSameDataType() */
4731 : /************************************************************************/
4732 :
4733 : // Returns true if at all following conditions are met:
4734 : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4735 : // defines a row-major ordered contiguous buffer.
4736 78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4737 : const size_t *count, const GInt64 *arrayStep,
4738 : const GPtrDiff_t *bufferStride,
4739 : const GDALExtendedDataType &bufferDataType) const
4740 : {
4741 78 : if (bufferDataType != GetDataType())
4742 5 : return false;
4743 73 : size_t nExpectedStride = 1;
4744 166 : for (size_t i = GetDimensionCount(); i > 0;)
4745 : {
4746 96 : --i;
4747 96 : if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4748 94 : static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4749 : {
4750 3 : return false;
4751 : }
4752 93 : nExpectedStride *= count[i];
4753 : }
4754 70 : return true;
4755 : }
4756 :
4757 : /************************************************************************/
4758 : /* ReadUsingContiguousIRead() */
4759 : /************************************************************************/
4760 :
4761 : // Used for example by the TileDB driver when requesting it with
4762 : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4763 : // not defining a row-major ordered contiguous buffer.
4764 : // Should only be called when at least one of the above conditions are true,
4765 : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4766 : // returning none.
4767 : // This method will call IRead() again with arrayStep[] == 1,
4768 : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
4769 : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4770 : // content of that temporary buffer onto pDstBuffer.
4771 7 : bool GDALMDArray::ReadUsingContiguousIRead(
4772 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4773 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4774 : void *pDstBuffer) const
4775 : {
4776 7 : const size_t nDims(GetDimensionCount());
4777 14 : std::vector<GUInt64> anTmpStartIdx(nDims);
4778 14 : std::vector<size_t> anTmpCount(nDims);
4779 7 : const auto &oType = GetDataType();
4780 7 : size_t nMemArraySize = oType.GetSize();
4781 14 : std::vector<GPtrDiff_t> anTmpStride(nDims);
4782 7 : GPtrDiff_t nStride = 1;
4783 18 : for (size_t i = nDims; i > 0;)
4784 : {
4785 11 : --i;
4786 11 : if (arrayStep[i] > 0)
4787 9 : anTmpStartIdx[i] = arrayStartIdx[i];
4788 : else
4789 2 : anTmpStartIdx[i] =
4790 2 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4791 : const uint64_t nCount =
4792 11 : (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4793 11 : if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4794 : {
4795 0 : CPLError(CE_Failure, CPLE_AppDefined,
4796 : "Read() failed due to too large memory requirement");
4797 0 : return false;
4798 : }
4799 11 : anTmpCount[i] = static_cast<size_t>(nCount);
4800 11 : nMemArraySize *= anTmpCount[i];
4801 11 : anTmpStride[i] = nStride;
4802 11 : nStride *= anTmpCount[i];
4803 : }
4804 : std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4805 14 : VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4806 7 : if (!pTmpBuffer)
4807 0 : return false;
4808 7 : if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4809 14 : std::vector<GInt64>(nDims, 1).data(), // steps
4810 7 : anTmpStride.data(), oType, pTmpBuffer.get()))
4811 : {
4812 0 : return false;
4813 : }
4814 14 : std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
4815 18 : for (size_t i = 0; i < nDims; ++i)
4816 : {
4817 11 : if (arrayStep[i] > 0)
4818 9 : anTmpStartIdx[i] = 0;
4819 : else
4820 2 : anTmpStartIdx[i] = anTmpCount[i] - 1;
4821 22 : apoTmpDims[i] = std::make_shared<GDALDimension>(
4822 22 : std::string(), std::string(), std::string(), std::string(),
4823 22 : anTmpCount[i]);
4824 : }
4825 : auto poMEMArray =
4826 14 : MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
4827 21 : return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
4828 7 : poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
4829 7 : bufferStride, bufferDataType, pDstBuffer);
4830 : }
4831 :
4832 : //! @endcond
4833 :
4834 : /************************************************************************/
4835 : /* GDALSlicedMDArray */
4836 : /************************************************************************/
4837 :
4838 : class GDALSlicedMDArray final : public GDALPamMDArray
4839 : {
4840 : private:
4841 : std::shared_ptr<GDALMDArray> m_poParent{};
4842 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4843 : std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
4844 : std::vector<Range>
4845 : m_parentRanges{}; // of size m_poParent->GetDimensionCount()
4846 :
4847 : mutable std::vector<GUInt64> m_parentStart;
4848 : mutable std::vector<size_t> m_parentCount;
4849 : mutable std::vector<GInt64> m_parentStep;
4850 : mutable std::vector<GPtrDiff_t> m_parentStride;
4851 :
4852 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
4853 : const GInt64 *arrayStep,
4854 : const GPtrDiff_t *bufferStride) const;
4855 :
4856 : protected:
4857 562 : explicit GDALSlicedMDArray(
4858 : const std::shared_ptr<GDALMDArray> &poParent,
4859 : const std::string &viewExpr,
4860 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4861 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
4862 : std::vector<Range> &&parentRanges)
4863 1686 : : GDALAbstractMDArray(std::string(), "Sliced view of " +
4864 1686 : poParent->GetFullName() +
4865 1124 : " (" + viewExpr + ")"),
4866 1124 : GDALPamMDArray(std::string(),
4867 1124 : "Sliced view of " + poParent->GetFullName() + " (" +
4868 1124 : viewExpr + ")",
4869 1124 : GDALPamMultiDim::GetPAM(poParent),
4870 : poParent->GetContext()),
4871 1124 : m_poParent(std::move(poParent)), m_dims(std::move(dims)),
4872 562 : m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
4873 562 : m_parentRanges(std::move(parentRanges)),
4874 562 : m_parentStart(m_poParent->GetDimensionCount()),
4875 562 : m_parentCount(m_poParent->GetDimensionCount(), 1),
4876 562 : m_parentStep(m_poParent->GetDimensionCount()),
4877 4496 : m_parentStride(m_poParent->GetDimensionCount())
4878 : {
4879 562 : }
4880 :
4881 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
4882 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4883 : const GDALExtendedDataType &bufferDataType,
4884 : void *pDstBuffer) const override;
4885 :
4886 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
4887 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
4888 : const GDALExtendedDataType &bufferDataType,
4889 : const void *pSrcBuffer) override;
4890 :
4891 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4892 : CSLConstList papszOptions) const override;
4893 :
4894 : public:
4895 : static std::shared_ptr<GDALSlicedMDArray>
4896 562 : Create(const std::shared_ptr<GDALMDArray> &poParent,
4897 : const std::string &viewExpr,
4898 : std::vector<std::shared_ptr<GDALDimension>> &&dims,
4899 : std::vector<size_t> &&mapDimIdxToParentDimIdx,
4900 : std::vector<Range> &&parentRanges)
4901 : {
4902 562 : CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
4903 562 : CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
4904 :
4905 : auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
4906 562 : poParent, viewExpr, std::move(dims),
4907 562 : std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
4908 562 : newAr->SetSelf(newAr);
4909 562 : return newAr;
4910 : }
4911 :
4912 38 : bool IsWritable() const override
4913 : {
4914 38 : return m_poParent->IsWritable();
4915 : }
4916 :
4917 922 : const std::string &GetFilename() const override
4918 : {
4919 922 : return m_poParent->GetFilename();
4920 : }
4921 :
4922 : const std::vector<std::shared_ptr<GDALDimension>> &
4923 3535 : GetDimensions() const override
4924 : {
4925 3535 : return m_dims;
4926 : }
4927 :
4928 1298 : const GDALExtendedDataType &GetDataType() const override
4929 : {
4930 1298 : return m_poParent->GetDataType();
4931 : }
4932 :
4933 2 : const std::string &GetUnit() const override
4934 : {
4935 2 : return m_poParent->GetUnit();
4936 : }
4937 :
4938 : // bool SetUnit(const std::string& osUnit) override { return
4939 : // m_poParent->SetUnit(osUnit); }
4940 :
4941 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
4942 : {
4943 4 : auto poSrcSRS = m_poParent->GetSpatialRef();
4944 2 : if (!poSrcSRS)
4945 1 : return nullptr;
4946 2 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
4947 2 : std::vector<int> dstMapping;
4948 3 : for (int srcAxis : srcMapping)
4949 : {
4950 2 : bool bFound = false;
4951 3 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
4952 : {
4953 3 : if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
4954 3 : srcAxis - 1)
4955 : {
4956 2 : dstMapping.push_back(static_cast<int>(i) + 1);
4957 2 : bFound = true;
4958 2 : break;
4959 : }
4960 : }
4961 2 : if (!bFound)
4962 : {
4963 0 : dstMapping.push_back(0);
4964 : }
4965 : }
4966 2 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
4967 1 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
4968 1 : return poClone;
4969 : }
4970 :
4971 32 : const void *GetRawNoDataValue() const override
4972 : {
4973 32 : return m_poParent->GetRawNoDataValue();
4974 : }
4975 :
4976 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
4977 : // m_poParent->SetRawNoDataValue(pRawNoData); }
4978 :
4979 2 : double GetOffset(bool *pbHasOffset,
4980 : GDALDataType *peStorageType) const override
4981 : {
4982 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
4983 : }
4984 :
4985 2 : double GetScale(bool *pbHasScale,
4986 : GDALDataType *peStorageType) const override
4987 : {
4988 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
4989 : }
4990 :
4991 : // bool SetOffset(double dfOffset) override { return
4992 : // m_poParent->SetOffset(dfOffset); }
4993 :
4994 : // bool SetScale(double dfScale) override { return
4995 : // m_poParent->SetScale(dfScale); }
4996 :
4997 174 : std::vector<GUInt64> GetBlockSize() const override
4998 : {
4999 174 : std::vector<GUInt64> ret(GetDimensionCount());
5000 348 : const auto parentBlockSize(m_poParent->GetBlockSize());
5001 526 : for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5002 : {
5003 352 : const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5004 352 : if (iOldAxis != static_cast<size_t>(-1))
5005 : {
5006 352 : ret[i] = parentBlockSize[iOldAxis];
5007 : }
5008 : }
5009 348 : return ret;
5010 : }
5011 :
5012 : std::shared_ptr<GDALAttribute>
5013 6 : GetAttribute(const std::string &osName) const override
5014 : {
5015 6 : return m_poParent->GetAttribute(osName);
5016 : }
5017 :
5018 : std::vector<std::shared_ptr<GDALAttribute>>
5019 23 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5020 : {
5021 23 : return m_poParent->GetAttributes(papszOptions);
5022 : }
5023 : };
5024 :
5025 : /************************************************************************/
5026 : /* PrepareParentArrays() */
5027 : /************************************************************************/
5028 :
5029 467 : void GDALSlicedMDArray::PrepareParentArrays(
5030 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5031 : const GPtrDiff_t *bufferStride) const
5032 : {
5033 467 : const size_t nParentDimCount = m_parentRanges.size();
5034 1454 : for (size_t i = 0; i < nParentDimCount; i++)
5035 : {
5036 : // For dimensions in parent that have no existence in sliced array
5037 987 : m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5038 : }
5039 :
5040 1226 : for (size_t i = 0; i < m_dims.size(); i++)
5041 : {
5042 759 : const auto iParent = m_mapDimIdxToParentDimIdx[i];
5043 759 : if (iParent != static_cast<size_t>(-1))
5044 : {
5045 757 : m_parentStart[iParent] =
5046 757 : m_parentRanges[iParent].m_nIncr >= 0
5047 757 : ? m_parentRanges[iParent].m_nStartIdx +
5048 733 : arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5049 24 : : m_parentRanges[iParent].m_nStartIdx -
5050 48 : arrayStartIdx[i] *
5051 24 : static_cast<GUInt64>(
5052 24 : -m_parentRanges[iParent].m_nIncr);
5053 757 : m_parentCount[iParent] = count[i];
5054 757 : if (arrayStep)
5055 : {
5056 756 : m_parentStep[iParent] =
5057 756 : count[i] == 1 ? 1 :
5058 : // other checks should have ensured this does
5059 : // not overflow
5060 574 : arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5061 : }
5062 757 : if (bufferStride)
5063 : {
5064 756 : m_parentStride[iParent] = bufferStride[i];
5065 : }
5066 : }
5067 : }
5068 467 : }
5069 :
5070 : /************************************************************************/
5071 : /* IRead() */
5072 : /************************************************************************/
5073 :
5074 437 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5075 : const GInt64 *arrayStep,
5076 : const GPtrDiff_t *bufferStride,
5077 : const GDALExtendedDataType &bufferDataType,
5078 : void *pDstBuffer) const
5079 : {
5080 437 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5081 874 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5082 437 : m_parentStep.data(), m_parentStride.data(),
5083 437 : bufferDataType, pDstBuffer);
5084 : }
5085 :
5086 : /************************************************************************/
5087 : /* IWrite() */
5088 : /************************************************************************/
5089 :
5090 29 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5091 : const size_t *count, const GInt64 *arrayStep,
5092 : const GPtrDiff_t *bufferStride,
5093 : const GDALExtendedDataType &bufferDataType,
5094 : const void *pSrcBuffer)
5095 : {
5096 29 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5097 58 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5098 29 : m_parentStep.data(), m_parentStride.data(),
5099 29 : bufferDataType, pSrcBuffer);
5100 : }
5101 :
5102 : /************************************************************************/
5103 : /* IAdviseRead() */
5104 : /************************************************************************/
5105 :
5106 1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5107 : const size_t *count,
5108 : CSLConstList papszOptions) const
5109 : {
5110 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5111 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5112 1 : papszOptions);
5113 : }
5114 :
5115 : /************************************************************************/
5116 : /* CreateSlicedArray() */
5117 : /************************************************************************/
5118 :
5119 : static std::shared_ptr<GDALMDArray>
5120 580 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5121 : const std::string &viewExpr, const std::string &activeSlice,
5122 : bool bRenameDimensions,
5123 : std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5124 : {
5125 580 : const auto &srcDims(self->GetDimensions());
5126 580 : if (srcDims.empty())
5127 : {
5128 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5129 2 : return nullptr;
5130 : }
5131 :
5132 1156 : CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5133 578 : const auto nTokens = static_cast<size_t>(aosTokens.size());
5134 :
5135 1156 : std::vector<std::shared_ptr<GDALDimension>> newDims;
5136 1156 : std::vector<size_t> mapDimIdxToParentDimIdx;
5137 1156 : std::vector<GDALSlicedMDArray::Range> parentRanges;
5138 578 : newDims.reserve(nTokens);
5139 578 : mapDimIdxToParentDimIdx.reserve(nTokens);
5140 578 : parentRanges.reserve(nTokens);
5141 :
5142 578 : bool bGotEllipsis = false;
5143 578 : size_t nCurSrcDim = 0;
5144 1705 : for (size_t i = 0; i < nTokens; i++)
5145 : {
5146 1143 : const char *pszIdxSpec = aosTokens[i];
5147 1143 : if (EQUAL(pszIdxSpec, "..."))
5148 : {
5149 28 : if (bGotEllipsis)
5150 : {
5151 2 : CPLError(CE_Failure, CPLE_AppDefined,
5152 : "Only one single ellipsis is supported");
5153 2 : return nullptr;
5154 : }
5155 26 : bGotEllipsis = true;
5156 26 : const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5157 59 : for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5158 : {
5159 33 : parentRanges.emplace_back(0, 1);
5160 33 : newDims.push_back(srcDims[nCurSrcDim]);
5161 33 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5162 : }
5163 26 : continue;
5164 : }
5165 1115 : else if (EQUAL(pszIdxSpec, "newaxis") ||
5166 1112 : EQUAL(pszIdxSpec, "np.newaxis"))
5167 : {
5168 3 : newDims.push_back(std::make_shared<GDALDimension>(
5169 6 : std::string(), "newaxis", std::string(), std::string(), 1));
5170 3 : mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5171 3 : continue;
5172 : }
5173 1112 : else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5174 : {
5175 320 : if (nCurSrcDim >= srcDims.size())
5176 : {
5177 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5178 : activeSlice.c_str());
5179 7 : return nullptr;
5180 : }
5181 :
5182 318 : auto nVal = CPLAtoGIntBig(pszIdxSpec);
5183 318 : GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5184 318 : if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5185 314 : (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5186 : {
5187 5 : CPLError(CE_Failure, CPLE_AppDefined,
5188 : "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5189 5 : return nullptr;
5190 : }
5191 313 : if (nVal < 0)
5192 0 : nVal += nDimSize;
5193 313 : parentRanges.emplace_back(nVal, 0);
5194 : }
5195 : else
5196 : {
5197 792 : if (nCurSrcDim >= srcDims.size())
5198 : {
5199 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5200 : activeSlice.c_str());
5201 7 : return nullptr;
5202 : }
5203 :
5204 : CPLStringList aosRangeTokens(
5205 791 : CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5206 791 : int nRangeTokens = aosRangeTokens.size();
5207 791 : if (nRangeTokens > 3)
5208 : {
5209 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5210 : pszIdxSpec);
5211 1 : return nullptr;
5212 : }
5213 790 : if (nRangeTokens <= 1)
5214 : {
5215 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5216 : pszIdxSpec);
5217 1 : return nullptr;
5218 : }
5219 789 : const char *pszStart = aosRangeTokens[0];
5220 789 : const char *pszEnd = aosRangeTokens[1];
5221 789 : const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5222 789 : GDALSlicedMDArray::Range range;
5223 789 : const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5224 789 : range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5225 1577 : if (range.m_nIncr == 0 ||
5226 788 : range.m_nIncr == std::numeric_limits<GInt64>::min())
5227 : {
5228 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5229 1 : return nullptr;
5230 : }
5231 788 : auto startIdx(CPLAtoGIntBig(pszStart));
5232 788 : if (startIdx < 0)
5233 : {
5234 0 : if (nDimSize < static_cast<GUInt64>(-startIdx))
5235 0 : startIdx = 0;
5236 : else
5237 0 : startIdx = nDimSize + startIdx;
5238 : }
5239 788 : const bool bPosIncr = range.m_nIncr > 0;
5240 788 : range.m_nStartIdx = startIdx;
5241 1576 : range.m_nStartIdx = EQUAL(pszStart, "")
5242 788 : ? (bPosIncr ? 0 : nDimSize - 1)
5243 : : range.m_nStartIdx;
5244 788 : if (range.m_nStartIdx >= nDimSize - 1)
5245 176 : range.m_nStartIdx = nDimSize - 1;
5246 788 : auto endIdx(CPLAtoGIntBig(pszEnd));
5247 788 : if (endIdx < 0)
5248 : {
5249 1 : const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5250 1 : if (nDimSize < positiveEndIdx)
5251 0 : endIdx = 0;
5252 : else
5253 1 : endIdx = nDimSize - positiveEndIdx;
5254 : }
5255 788 : GUInt64 nEndIdx = endIdx;
5256 788 : nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5257 788 : if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5258 786 : (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5259 : {
5260 3 : CPLError(CE_Failure, CPLE_AppDefined,
5261 : "Output dimension of size 0 is not allowed");
5262 3 : return nullptr;
5263 : }
5264 785 : int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5265 785 : const auto nAbsIncr = std::abs(range.m_nIncr);
5266 785 : const GUInt64 newSize =
5267 : bPosIncr
5268 809 : ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5269 24 : : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5270 1301 : if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5271 516 : newSize == srcDims[nCurSrcDim]->GetSize())
5272 : {
5273 147 : newDims.push_back(srcDims[nCurSrcDim]);
5274 : }
5275 : else
5276 : {
5277 638 : std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5278 638 : if (bRenameDimensions)
5279 : {
5280 : osNewDimName =
5281 1192 : "subset_" + srcDims[nCurSrcDim]->GetName() +
5282 : CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5283 : "_" CPL_FRMT_GUIB,
5284 596 : static_cast<GUIntBig>(range.m_nStartIdx),
5285 596 : static_cast<GIntBig>(range.m_nIncr),
5286 596 : static_cast<GUIntBig>(newSize));
5287 : }
5288 638 : newDims.push_back(std::make_shared<GDALDimension>(
5289 1276 : std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5290 1276 : range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5291 : : std::string(),
5292 : newSize));
5293 : }
5294 785 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5295 785 : parentRanges.emplace_back(range);
5296 : }
5297 :
5298 1098 : nCurSrcDim++;
5299 : }
5300 635 : for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5301 : {
5302 73 : parentRanges.emplace_back(0, 1);
5303 73 : newDims.push_back(srcDims[nCurSrcDim]);
5304 73 : mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5305 : }
5306 :
5307 562 : GDALMDArray::ViewSpec viewSpec;
5308 562 : viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5309 562 : viewSpec.m_parentRanges = parentRanges;
5310 562 : viewSpecs.emplace_back(std::move(viewSpec));
5311 :
5312 1124 : return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5313 562 : std::move(mapDimIdxToParentDimIdx),
5314 1124 : std::move(parentRanges));
5315 : }
5316 :
5317 : /************************************************************************/
5318 : /* GDALExtractFieldMDArray */
5319 : /************************************************************************/
5320 :
5321 : class GDALExtractFieldMDArray final : public GDALPamMDArray
5322 : {
5323 : private:
5324 : std::shared_ptr<GDALMDArray> m_poParent{};
5325 : GDALExtendedDataType m_dt;
5326 : std::string m_srcCompName;
5327 : mutable std::vector<GByte> m_pabyNoData{};
5328 :
5329 : protected:
5330 42 : GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5331 : const std::string &fieldName,
5332 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5333 168 : : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5334 84 : " of " +
5335 42 : poParent->GetFullName()),
5336 : GDALPamMDArray(
5337 84 : std::string(),
5338 84 : "Extract field " + fieldName + " of " + poParent->GetFullName(),
5339 84 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5340 : m_poParent(poParent), m_dt(srcComp->GetType()),
5341 210 : m_srcCompName(srcComp->GetName())
5342 : {
5343 42 : m_pabyNoData.resize(m_dt.GetSize());
5344 42 : }
5345 :
5346 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5347 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5348 : const GDALExtendedDataType &bufferDataType,
5349 : void *pDstBuffer) const override;
5350 :
5351 1 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5352 : CSLConstList papszOptions) const override
5353 : {
5354 1 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5355 : }
5356 :
5357 : public:
5358 : static std::shared_ptr<GDALExtractFieldMDArray>
5359 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5360 : const std::string &fieldName,
5361 : const std::unique_ptr<GDALEDTComponent> &srcComp)
5362 : {
5363 : auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5364 42 : new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5365 42 : newAr->SetSelf(newAr);
5366 42 : return newAr;
5367 : }
5368 :
5369 84 : ~GDALExtractFieldMDArray()
5370 42 : {
5371 42 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5372 84 : }
5373 :
5374 22 : bool IsWritable() const override
5375 : {
5376 22 : return m_poParent->IsWritable();
5377 : }
5378 :
5379 136 : const std::string &GetFilename() const override
5380 : {
5381 136 : return m_poParent->GetFilename();
5382 : }
5383 :
5384 : const std::vector<std::shared_ptr<GDALDimension>> &
5385 211 : GetDimensions() const override
5386 : {
5387 211 : return m_poParent->GetDimensions();
5388 : }
5389 :
5390 159 : const GDALExtendedDataType &GetDataType() const override
5391 : {
5392 159 : return m_dt;
5393 : }
5394 :
5395 2 : const std::string &GetUnit() const override
5396 : {
5397 2 : return m_poParent->GetUnit();
5398 : }
5399 :
5400 2 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5401 : {
5402 2 : return m_poParent->GetSpatialRef();
5403 : }
5404 :
5405 33 : const void *GetRawNoDataValue() const override
5406 : {
5407 33 : const void *parentNoData = m_poParent->GetRawNoDataValue();
5408 33 : if (parentNoData == nullptr)
5409 1 : return nullptr;
5410 :
5411 32 : m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5412 32 : memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5413 :
5414 64 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5415 64 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5416 64 : new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5417 32 : auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5418 96 : std::move(comps)));
5419 :
5420 32 : GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5421 32 : &m_pabyNoData[0], tmpDT);
5422 :
5423 32 : return &m_pabyNoData[0];
5424 : }
5425 :
5426 2 : double GetOffset(bool *pbHasOffset,
5427 : GDALDataType *peStorageType) const override
5428 : {
5429 2 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5430 : }
5431 :
5432 2 : double GetScale(bool *pbHasScale,
5433 : GDALDataType *peStorageType) const override
5434 : {
5435 2 : return m_poParent->GetScale(pbHasScale, peStorageType);
5436 : }
5437 :
5438 23 : std::vector<GUInt64> GetBlockSize() const override
5439 : {
5440 23 : return m_poParent->GetBlockSize();
5441 : }
5442 : };
5443 :
5444 : /************************************************************************/
5445 : /* IRead() */
5446 : /************************************************************************/
5447 :
5448 38 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5449 : const size_t *count,
5450 : const GInt64 *arrayStep,
5451 : const GPtrDiff_t *bufferStride,
5452 : const GDALExtendedDataType &bufferDataType,
5453 : void *pDstBuffer) const
5454 : {
5455 76 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5456 76 : comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5457 76 : new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5458 : auto tmpDT(GDALExtendedDataType::Create(
5459 76 : std::string(), bufferDataType.GetSize(), std::move(comps)));
5460 :
5461 38 : return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5462 76 : tmpDT, pDstBuffer);
5463 : }
5464 :
5465 : /************************************************************************/
5466 : /* CreateFieldNameExtractArray() */
5467 : /************************************************************************/
5468 :
5469 : static std::shared_ptr<GDALMDArray>
5470 43 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5471 : const std::string &fieldName)
5472 : {
5473 43 : CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5474 43 : const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5475 91 : for (const auto &comp : self->GetDataType().GetComponents())
5476 : {
5477 90 : if (comp->GetName() == fieldName)
5478 : {
5479 42 : srcComp = ∁
5480 42 : break;
5481 : }
5482 : }
5483 43 : if (srcComp == nullptr)
5484 : {
5485 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5486 : fieldName.c_str());
5487 1 : return nullptr;
5488 : }
5489 42 : return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5490 : }
5491 :
5492 : /************************************************************************/
5493 : /* GetView() */
5494 : /************************************************************************/
5495 :
5496 : // clang-format off
5497 : /** Return a view of the array using slicing or field access.
5498 : *
5499 : * The slice expression uses the same syntax as NumPy basic slicing and
5500 : * indexing. See
5501 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5502 : * Or it can use field access by name. See
5503 : * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5504 : *
5505 : * Multiple [] bracket elements can be concatenated, with a slice expression
5506 : * or field name inside each.
5507 : *
5508 : * For basic slicing and indexing, inside each [] bracket element, a list of
5509 : * indexes that apply to successive source dimensions, can be specified, using
5510 : * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5511 : * or newaxis, using a comma separator.
5512 : *
5513 : * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5514 : * <ul>
5515 : * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5516 : * at index 1 in the first dimension, and index 2 in the second dimension
5517 : * from the source array. That is 5</li>
5518 : * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5519 : * implemented internally doing this intermediate slicing approach.</li>
5520 : * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5521 : * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5522 : * first dimension. That is [4,5,6,7].</li>
5523 : * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5524 : * second dimension. That is [2,6].</li>
5525 : * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5526 : * the second dimension. That is [[2],[6]].</li>
5527 : * <li>GetView("[::,2]"): Same as
5528 : * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5529 : * ellipsis only expands to one dimension here.</li>
5530 : * <li>GetView("[:,::2]"):
5531 : * returns a 2-dimensional array, with even-indexed elements of the second
5532 : * dimension. That is [[0,2],[4,6]].</li>
5533 : * <li>GetView("[:,1::2]"): returns a
5534 : * 2-dimensional array, with odd-indexed elements of the second dimension. That
5535 : * is [[1,3],[5,7]].</li>
5536 : * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5537 : * array, with elements of the second dimension with index in the range [1,3[.
5538 : * That is [[1,2],[5,6]].</li>
5539 : * <li>GetView("[::-1,:]"): returns a 2-dimensional
5540 : * array, with the values in first dimension reversed. That is
5541 : * [[4,5,6,7],[0,1,2,3]].</li>
5542 : * <li>GetView("[newaxis,...]"): returns a
5543 : * 3-dimensional array, with an addditional dimension of size 1 put at the
5544 : * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5545 : * </ul>
5546 : *
5547 : * One difference with NumPy behavior is that ranges that would result in
5548 : * zero elements are not allowed (dimensions of size 0 not being allowed in the
5549 : * GDAL multidimensional model).
5550 : *
5551 : * For field access, the syntax to use is ["field_name"] or ['field_name'].
5552 : * Multiple field specification is not supported currently.
5553 : *
5554 : * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5555 : *
5556 : * \note When using the GDAL Python bindings, natural Python syntax can be
5557 : * used. That is ar[0,::,1]["foo"] will be internally translated to
5558 : * ar.GetView("[0,::,1]['foo']")
5559 : * \note When using the C++ API and integer indexing only, you may use the
5560 : * at(idx0, idx1, ...) method.
5561 : *
5562 : * The returned array holds a reference to the original one, and thus is
5563 : * a view of it (not a copy). If the content of the original array changes,
5564 : * the content of the view array too. When using basic slicing and indexing,
5565 : * the view can be written if the underlying array is writable.
5566 : *
5567 : * This is the same as the C function GDALMDArrayGetView()
5568 : *
5569 : * @param viewExpr Expression expressing basic slicing and indexing, or field
5570 : * access.
5571 : * @return a new array, that holds a reference to the original one, and thus is
5572 : * a view of it (not a copy), or nullptr in case of error.
5573 : */
5574 : // clang-format on
5575 : std::shared_ptr<GDALMDArray>
5576 566 : GDALMDArray::GetView(const std::string &viewExpr) const
5577 : {
5578 1132 : std::vector<ViewSpec> viewSpecs;
5579 1132 : return GetView(viewExpr, true, viewSpecs);
5580 : }
5581 :
5582 : //! @cond Doxygen_Suppress
5583 : std::shared_ptr<GDALMDArray>
5584 629 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5585 : std::vector<ViewSpec> &viewSpecs) const
5586 : {
5587 1258 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5588 629 : if (!self)
5589 : {
5590 1 : CPLError(CE_Failure, CPLE_AppDefined,
5591 : "Driver implementation issue: m_pSelf not set !");
5592 1 : return nullptr;
5593 : }
5594 628 : std::string curExpr(viewExpr);
5595 : while (true)
5596 : {
5597 631 : if (curExpr.empty() || curExpr[0] != '[')
5598 : {
5599 2 : CPLError(CE_Failure, CPLE_AppDefined,
5600 : "Slice string should start with ['");
5601 628 : return nullptr;
5602 : }
5603 :
5604 629 : std::string fieldName;
5605 : size_t endExpr;
5606 629 : if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5607 : {
5608 47 : if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5609 : {
5610 2 : CPLError(CE_Failure, CPLE_AppDefined,
5611 : "Field access not allowed on non-compound data type");
5612 2 : return nullptr;
5613 : }
5614 45 : size_t idx = 2;
5615 402 : for (; idx < curExpr.size(); idx++)
5616 : {
5617 401 : const char ch = curExpr[idx];
5618 401 : if (ch == curExpr[1])
5619 44 : break;
5620 357 : if (ch == '\\' && idx + 1 < curExpr.size())
5621 : {
5622 1 : fieldName += curExpr[idx + 1];
5623 1 : idx++;
5624 : }
5625 : else
5626 : {
5627 356 : fieldName += ch;
5628 : }
5629 : }
5630 45 : if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5631 : {
5632 2 : CPLError(CE_Failure, CPLE_AppDefined,
5633 : "Invalid field access specification");
5634 2 : return nullptr;
5635 : }
5636 43 : endExpr = idx + 1;
5637 : }
5638 : else
5639 : {
5640 582 : endExpr = curExpr.find(']');
5641 : }
5642 625 : if (endExpr == std::string::npos)
5643 : {
5644 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5645 1 : return nullptr;
5646 : }
5647 624 : if (endExpr == 1)
5648 : {
5649 1 : CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5650 1 : return nullptr;
5651 : }
5652 623 : std::string activeSlice(curExpr.substr(1, endExpr - 1));
5653 :
5654 623 : if (!fieldName.empty())
5655 : {
5656 86 : ViewSpec viewSpec;
5657 43 : viewSpec.m_osFieldName = fieldName;
5658 43 : viewSpecs.emplace_back(std::move(viewSpec));
5659 : }
5660 :
5661 623 : auto newArray = !fieldName.empty()
5662 : ? CreateFieldNameExtractArray(self, fieldName)
5663 : : CreateSlicedArray(self, viewExpr, activeSlice,
5664 623 : bRenameDimensions, viewSpecs);
5665 :
5666 623 : if (endExpr == curExpr.size() - 1)
5667 : {
5668 620 : return newArray;
5669 : }
5670 3 : self = std::move(newArray);
5671 3 : curExpr = curExpr.substr(endExpr + 1);
5672 3 : }
5673 : }
5674 :
5675 : //! @endcond
5676 :
5677 : std::shared_ptr<GDALMDArray>
5678 19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5679 : {
5680 19 : std::string osExpr("[");
5681 19 : bool bFirst = true;
5682 45 : for (const auto &idx : indices)
5683 : {
5684 26 : if (!bFirst)
5685 7 : osExpr += ',';
5686 26 : bFirst = false;
5687 26 : osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5688 : }
5689 57 : return GetView(osExpr + ']');
5690 : }
5691 :
5692 : /************************************************************************/
5693 : /* operator[] */
5694 : /************************************************************************/
5695 :
5696 : /** Return a view of the array using field access
5697 : *
5698 : * Equivalent of GetView("['fieldName']")
5699 : *
5700 : * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5701 : */
5702 : std::shared_ptr<GDALMDArray>
5703 2 : GDALMDArray::operator[](const std::string &fieldName) const
5704 : {
5705 2 : return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5706 4 : .replaceAll('\\', "\\\\")
5707 4 : .replaceAll('\'', "\\\'")
5708 6 : .c_str()));
5709 : }
5710 :
5711 : /************************************************************************/
5712 : /* GDALMDArrayTransposed */
5713 : /************************************************************************/
5714 :
5715 : class GDALMDArrayTransposed final : public GDALPamMDArray
5716 : {
5717 : private:
5718 : std::shared_ptr<GDALMDArray> m_poParent{};
5719 : std::vector<int> m_anMapNewAxisToOldAxis{};
5720 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5721 :
5722 : mutable std::vector<GUInt64> m_parentStart;
5723 : mutable std::vector<size_t> m_parentCount;
5724 : mutable std::vector<GInt64> m_parentStep;
5725 : mutable std::vector<GPtrDiff_t> m_parentStride;
5726 :
5727 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5728 : const GInt64 *arrayStep,
5729 : const GPtrDiff_t *bufferStride) const;
5730 :
5731 : static std::string
5732 84 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5733 : {
5734 84 : std::string ret;
5735 84 : ret += '[';
5736 312 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5737 : {
5738 228 : if (i > 0)
5739 144 : ret += ',';
5740 228 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5741 : }
5742 84 : ret += ']';
5743 84 : return ret;
5744 : }
5745 :
5746 : protected:
5747 42 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5748 : const std::vector<int> &anMapNewAxisToOldAxis,
5749 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
5750 84 : : GDALAbstractMDArray(std::string(),
5751 84 : "Transposed view of " + poParent->GetFullName() +
5752 84 : " along " +
5753 42 : MappingToStr(anMapNewAxisToOldAxis)),
5754 84 : GDALPamMDArray(std::string(),
5755 84 : "Transposed view of " + poParent->GetFullName() +
5756 168 : " along " + MappingToStr(anMapNewAxisToOldAxis),
5757 84 : GDALPamMultiDim::GetPAM(poParent),
5758 : poParent->GetContext()),
5759 42 : m_poParent(std::move(poParent)),
5760 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5761 42 : m_dims(std::move(dims)),
5762 42 : m_parentStart(m_poParent->GetDimensionCount()),
5763 42 : m_parentCount(m_poParent->GetDimensionCount()),
5764 42 : m_parentStep(m_poParent->GetDimensionCount()),
5765 336 : m_parentStride(m_poParent->GetDimensionCount())
5766 : {
5767 42 : }
5768 :
5769 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5770 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5771 : const GDALExtendedDataType &bufferDataType,
5772 : void *pDstBuffer) const override;
5773 :
5774 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5775 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5776 : const GDALExtendedDataType &bufferDataType,
5777 : const void *pSrcBuffer) override;
5778 :
5779 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5780 : CSLConstList papszOptions) const override;
5781 :
5782 : public:
5783 : static std::shared_ptr<GDALMDArrayTransposed>
5784 42 : Create(const std::shared_ptr<GDALMDArray> &poParent,
5785 : const std::vector<int> &anMapNewAxisToOldAxis)
5786 : {
5787 42 : const auto &parentDims(poParent->GetDimensions());
5788 84 : std::vector<std::shared_ptr<GDALDimension>> dims;
5789 156 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
5790 : {
5791 114 : if (iOldAxis < 0)
5792 : {
5793 1 : dims.push_back(std::make_shared<GDALDimension>(
5794 2 : std::string(), "newaxis", std::string(), std::string(), 1));
5795 : }
5796 : else
5797 : {
5798 113 : dims.emplace_back(parentDims[iOldAxis]);
5799 : }
5800 : }
5801 :
5802 : auto newAr(
5803 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5804 42 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
5805 42 : newAr->SetSelf(newAr);
5806 84 : return newAr;
5807 : }
5808 :
5809 1 : bool IsWritable() const override
5810 : {
5811 1 : return m_poParent->IsWritable();
5812 : }
5813 :
5814 84 : const std::string &GetFilename() const override
5815 : {
5816 84 : return m_poParent->GetFilename();
5817 : }
5818 :
5819 : const std::vector<std::shared_ptr<GDALDimension>> &
5820 358 : GetDimensions() const override
5821 : {
5822 358 : return m_dims;
5823 : }
5824 :
5825 141 : const GDALExtendedDataType &GetDataType() const override
5826 : {
5827 141 : return m_poParent->GetDataType();
5828 : }
5829 :
5830 4 : const std::string &GetUnit() const override
5831 : {
5832 4 : return m_poParent->GetUnit();
5833 : }
5834 :
5835 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5836 : {
5837 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
5838 5 : if (!poSrcSRS)
5839 2 : return nullptr;
5840 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5841 6 : std::vector<int> dstMapping;
5842 9 : for (int srcAxis : srcMapping)
5843 : {
5844 6 : bool bFound = false;
5845 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
5846 : {
5847 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
5848 : {
5849 6 : dstMapping.push_back(static_cast<int>(i) + 1);
5850 6 : bFound = true;
5851 6 : break;
5852 : }
5853 : }
5854 6 : if (!bFound)
5855 : {
5856 0 : dstMapping.push_back(0);
5857 : }
5858 : }
5859 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5860 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5861 3 : return poClone;
5862 : }
5863 :
5864 4 : const void *GetRawNoDataValue() const override
5865 : {
5866 4 : return m_poParent->GetRawNoDataValue();
5867 : }
5868 :
5869 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
5870 : // m_poParent->SetRawNoDataValue(pRawNoData); }
5871 :
5872 4 : double GetOffset(bool *pbHasOffset,
5873 : GDALDataType *peStorageType) const override
5874 : {
5875 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
5876 : }
5877 :
5878 4 : double GetScale(bool *pbHasScale,
5879 : GDALDataType *peStorageType) const override
5880 : {
5881 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
5882 : }
5883 :
5884 : // bool SetOffset(double dfOffset) override { return
5885 : // m_poParent->SetOffset(dfOffset); }
5886 :
5887 : // bool SetScale(double dfScale) override { return
5888 : // m_poParent->SetScale(dfScale); }
5889 :
5890 3 : std::vector<GUInt64> GetBlockSize() const override
5891 : {
5892 3 : std::vector<GUInt64> ret(GetDimensionCount());
5893 6 : const auto parentBlockSize(m_poParent->GetBlockSize());
5894 11 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
5895 : {
5896 8 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
5897 8 : if (iOldAxis >= 0)
5898 : {
5899 7 : ret[i] = parentBlockSize[iOldAxis];
5900 : }
5901 : }
5902 6 : return ret;
5903 : }
5904 :
5905 : std::shared_ptr<GDALAttribute>
5906 1 : GetAttribute(const std::string &osName) const override
5907 : {
5908 1 : return m_poParent->GetAttribute(osName);
5909 : }
5910 :
5911 : std::vector<std::shared_ptr<GDALAttribute>>
5912 6 : GetAttributes(CSLConstList papszOptions = nullptr) const override
5913 : {
5914 6 : return m_poParent->GetAttributes(papszOptions);
5915 : }
5916 : };
5917 :
5918 : /************************************************************************/
5919 : /* PrepareParentArrays() */
5920 : /************************************************************************/
5921 :
5922 47 : void GDALMDArrayTransposed::PrepareParentArrays(
5923 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5924 : const GPtrDiff_t *bufferStride) const
5925 : {
5926 176 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
5927 : {
5928 129 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
5929 129 : if (iOldAxis >= 0)
5930 : {
5931 128 : m_parentStart[iOldAxis] = arrayStartIdx[i];
5932 128 : m_parentCount[iOldAxis] = count[i];
5933 128 : if (arrayStep) // only null when called from IAdviseRead()
5934 : {
5935 126 : m_parentStep[iOldAxis] = arrayStep[i];
5936 : }
5937 128 : if (bufferStride) // only null when called from IAdviseRead()
5938 : {
5939 126 : m_parentStride[iOldAxis] = bufferStride[i];
5940 : }
5941 : }
5942 : }
5943 47 : }
5944 :
5945 : /************************************************************************/
5946 : /* IRead() */
5947 : /************************************************************************/
5948 :
5949 44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
5950 : const size_t *count, const GInt64 *arrayStep,
5951 : const GPtrDiff_t *bufferStride,
5952 : const GDALExtendedDataType &bufferDataType,
5953 : void *pDstBuffer) const
5954 : {
5955 44 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5956 88 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5957 44 : m_parentStep.data(), m_parentStride.data(),
5958 44 : bufferDataType, pDstBuffer);
5959 : }
5960 :
5961 : /************************************************************************/
5962 : /* IWrite() */
5963 : /************************************************************************/
5964 :
5965 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
5966 : const size_t *count, const GInt64 *arrayStep,
5967 : const GPtrDiff_t *bufferStride,
5968 : const GDALExtendedDataType &bufferDataType,
5969 : const void *pSrcBuffer)
5970 : {
5971 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5972 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5973 2 : m_parentStep.data(), m_parentStride.data(),
5974 2 : bufferDataType, pSrcBuffer);
5975 : }
5976 :
5977 : /************************************************************************/
5978 : /* IAdviseRead() */
5979 : /************************************************************************/
5980 :
5981 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
5982 : const size_t *count,
5983 : CSLConstList papszOptions) const
5984 : {
5985 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5986 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5987 1 : papszOptions);
5988 : }
5989 :
5990 : /************************************************************************/
5991 : /* Transpose() */
5992 : /************************************************************************/
5993 :
5994 : /** Return a view of the array whose axis have been reordered.
5995 : *
5996 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
5997 : * and GetDimensionCount() - 1, and each only once.
5998 : * -1 can be used as a special index value to ask for the insertion of a new
5999 : * axis of size 1.
6000 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6001 : * index of one of its dimension, it corresponds to the axis of index
6002 : * anMapNewAxisToOldAxis[i] from the current array.
6003 : *
6004 : * This is similar to the numpy.transpose() method
6005 : *
6006 : * The returned array holds a reference to the original one, and thus is
6007 : * a view of it (not a copy). If the content of the original array changes,
6008 : * the content of the view array too. The view can be written if the underlying
6009 : * array is writable.
6010 : *
6011 : * Note that I/O performance in such a transposed view might be poor.
6012 : *
6013 : * This is the same as the C function GDALMDArrayTranspose().
6014 : *
6015 : * @return a new array, that holds a reference to the original one, and thus is
6016 : * a view of it (not a copy), or nullptr in case of error.
6017 : */
6018 : std::shared_ptr<GDALMDArray>
6019 50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6020 : {
6021 100 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6022 50 : if (!self)
6023 : {
6024 0 : CPLError(CE_Failure, CPLE_AppDefined,
6025 : "Driver implementation issue: m_pSelf not set !");
6026 0 : return nullptr;
6027 : }
6028 50 : const int nDims = static_cast<int>(GetDimensionCount());
6029 100 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
6030 50 : int nCountOldAxis = 0;
6031 179 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
6032 : {
6033 133 : if (iOldAxis < -1 || iOldAxis >= nDims)
6034 : {
6035 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6036 4 : return nullptr;
6037 : }
6038 130 : if (iOldAxis >= 0)
6039 : {
6040 128 : if (alreadyUsedOldAxis[iOldAxis])
6041 : {
6042 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6043 : iOldAxis);
6044 1 : return nullptr;
6045 : }
6046 127 : alreadyUsedOldAxis[iOldAxis] = true;
6047 127 : nCountOldAxis++;
6048 : }
6049 : }
6050 46 : if (nCountOldAxis != nDims)
6051 : {
6052 4 : CPLError(CE_Failure, CPLE_AppDefined,
6053 : "One or several original axis missing");
6054 4 : return nullptr;
6055 : }
6056 42 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6057 : }
6058 :
6059 : /************************************************************************/
6060 : /* IRead() */
6061 : /************************************************************************/
6062 :
6063 16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6064 : const size_t *count, const GInt64 *arrayStep,
6065 : const GPtrDiff_t *bufferStride,
6066 : const GDALExtendedDataType &bufferDataType,
6067 : void *pDstBuffer) const
6068 : {
6069 16 : const double dfScale = m_dfScale;
6070 16 : const double dfOffset = m_dfOffset;
6071 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6072 : const auto dtDouble =
6073 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6074 16 : const size_t nDTSize = dtDouble.GetSize();
6075 16 : const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6076 :
6077 16 : double adfSrcNoData[2] = {0, 0};
6078 16 : if (m_bHasNoData)
6079 : {
6080 9 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6081 9 : m_poParent->GetDataType(),
6082 : &adfSrcNoData[0], dtDouble);
6083 : }
6084 :
6085 16 : const auto nDims = GetDimensions().size();
6086 16 : if (nDims == 0)
6087 : {
6088 : double adfVal[2];
6089 9 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6090 : dtDouble, &adfVal[0]))
6091 : {
6092 0 : return false;
6093 : }
6094 9 : if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6095 : {
6096 6 : adfVal[0] = adfVal[0] * dfScale + dfOffset;
6097 6 : if (bDTIsComplex)
6098 : {
6099 2 : adfVal[1] = adfVal[1] * dfScale + dfOffset;
6100 : }
6101 6 : GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6102 : bufferDataType);
6103 : }
6104 : else
6105 : {
6106 3 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6107 : pDstBuffer, bufferDataType);
6108 : }
6109 9 : return true;
6110 : }
6111 :
6112 14 : std::vector<GPtrDiff_t> actualBufferStrideVector;
6113 7 : const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6114 7 : void *pTempBuffer = pDstBuffer;
6115 7 : if (bTempBufferNeeded)
6116 : {
6117 2 : size_t nElts = 1;
6118 2 : actualBufferStrideVector.resize(nDims);
6119 7 : for (size_t i = 0; i < nDims; i++)
6120 5 : nElts *= count[i];
6121 2 : actualBufferStrideVector.back() = 1;
6122 5 : for (size_t i = nDims - 1; i > 0;)
6123 : {
6124 3 : --i;
6125 3 : actualBufferStrideVector[i] =
6126 3 : actualBufferStrideVector[i + 1] * count[i + 1];
6127 : }
6128 2 : actualBufferStridePtr = actualBufferStrideVector.data();
6129 2 : pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6130 2 : if (!pTempBuffer)
6131 0 : return false;
6132 : }
6133 7 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6134 : actualBufferStridePtr, dtDouble, pTempBuffer))
6135 : {
6136 0 : if (bTempBufferNeeded)
6137 0 : VSIFree(pTempBuffer);
6138 0 : return false;
6139 : }
6140 :
6141 : struct Stack
6142 : {
6143 : size_t nIters = 0;
6144 : double *src_ptr = nullptr;
6145 : GByte *dst_ptr = nullptr;
6146 : GPtrDiff_t src_inc_offset = 0;
6147 : GPtrDiff_t dst_inc_offset = 0;
6148 : };
6149 :
6150 7 : std::vector<Stack> stack(nDims);
6151 7 : const size_t nBufferDTSize = bufferDataType.GetSize();
6152 23 : for (size_t i = 0; i < nDims; i++)
6153 : {
6154 32 : stack[i].src_inc_offset =
6155 16 : actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6156 16 : stack[i].dst_inc_offset =
6157 16 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6158 : }
6159 7 : stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6160 7 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6161 :
6162 7 : size_t dimIdx = 0;
6163 7 : const size_t nDimsMinus1 = nDims - 1;
6164 : GByte abyDstNoData[16];
6165 7 : CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6166 7 : GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6167 : bufferDataType);
6168 :
6169 37 : lbl_next_depth:
6170 37 : if (dimIdx == nDimsMinus1)
6171 : {
6172 25 : auto nIters = count[dimIdx];
6173 25 : double *padfVal = stack[dimIdx].src_ptr;
6174 25 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6175 : while (true)
6176 : {
6177 92 : if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6178 : {
6179 88 : padfVal[0] = padfVal[0] * dfScale + dfOffset;
6180 88 : if (bDTIsComplex)
6181 : {
6182 1 : padfVal[1] = padfVal[1] * dfScale + dfOffset;
6183 : }
6184 88 : if (bTempBufferNeeded)
6185 : {
6186 29 : GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6187 : dst_ptr, bufferDataType);
6188 : }
6189 : }
6190 : else
6191 : {
6192 4 : memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6193 : }
6194 :
6195 92 : if ((--nIters) == 0)
6196 25 : break;
6197 67 : padfVal += stack[dimIdx].src_inc_offset;
6198 67 : dst_ptr += stack[dimIdx].dst_inc_offset;
6199 : }
6200 : }
6201 : else
6202 : {
6203 12 : stack[dimIdx].nIters = count[dimIdx];
6204 : while (true)
6205 : {
6206 30 : dimIdx++;
6207 30 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6208 30 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6209 30 : goto lbl_next_depth;
6210 30 : lbl_return_to_caller:
6211 30 : dimIdx--;
6212 30 : if ((--stack[dimIdx].nIters) == 0)
6213 12 : break;
6214 18 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6215 18 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6216 : }
6217 : }
6218 37 : if (dimIdx > 0)
6219 30 : goto lbl_return_to_caller;
6220 :
6221 7 : if (bTempBufferNeeded)
6222 2 : VSIFree(pTempBuffer);
6223 7 : return true;
6224 : }
6225 :
6226 : /************************************************************************/
6227 : /* IWrite() */
6228 : /************************************************************************/
6229 :
6230 16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6231 : const size_t *count, const GInt64 *arrayStep,
6232 : const GPtrDiff_t *bufferStride,
6233 : const GDALExtendedDataType &bufferDataType,
6234 : const void *pSrcBuffer)
6235 : {
6236 16 : const double dfScale = m_dfScale;
6237 16 : const double dfOffset = m_dfOffset;
6238 16 : const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6239 : const auto dtDouble =
6240 32 : GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6241 16 : const size_t nDTSize = dtDouble.GetSize();
6242 16 : const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6243 : const bool bSelfAndParentHaveNoData =
6244 16 : m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6245 16 : double dfNoData = 0;
6246 16 : if (m_bHasNoData)
6247 : {
6248 7 : GDALCopyWords(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6249 : &dfNoData, GDT_Float64, 0, 1);
6250 : }
6251 :
6252 16 : double adfSrcNoData[2] = {0, 0};
6253 16 : if (bSelfAndParentHaveNoData)
6254 : {
6255 7 : GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6256 7 : m_poParent->GetDataType(),
6257 : &adfSrcNoData[0], dtDouble);
6258 : }
6259 :
6260 16 : const auto nDims = GetDimensions().size();
6261 16 : if (nDims == 0)
6262 : {
6263 : double adfVal[2];
6264 10 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6265 : dtDouble);
6266 16 : if (bSelfAndParentHaveNoData &&
6267 6 : (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6268 : {
6269 4 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6270 2 : bufferStride, m_poParent->GetDataType(),
6271 4 : m_poParent->GetRawNoDataValue());
6272 : }
6273 : else
6274 : {
6275 8 : adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6276 8 : if (bDTIsComplex)
6277 : {
6278 2 : adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6279 : }
6280 8 : return m_poParent->Write(arrayStartIdx, count, arrayStep,
6281 8 : bufferStride, dtDouble, &adfVal[0]);
6282 : }
6283 : }
6284 :
6285 12 : std::vector<GPtrDiff_t> tmpBufferStrideVector;
6286 6 : size_t nElts = 1;
6287 6 : tmpBufferStrideVector.resize(nDims);
6288 20 : for (size_t i = 0; i < nDims; i++)
6289 14 : nElts *= count[i];
6290 6 : tmpBufferStrideVector.back() = 1;
6291 14 : for (size_t i = nDims - 1; i > 0;)
6292 : {
6293 8 : --i;
6294 8 : tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6295 : }
6296 6 : const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6297 6 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6298 6 : if (!pTempBuffer)
6299 0 : return false;
6300 :
6301 : struct Stack
6302 : {
6303 : size_t nIters = 0;
6304 : double *dst_ptr = nullptr;
6305 : const GByte *src_ptr = nullptr;
6306 : GPtrDiff_t src_inc_offset = 0;
6307 : GPtrDiff_t dst_inc_offset = 0;
6308 : };
6309 :
6310 6 : std::vector<Stack> stack(nDims);
6311 6 : const size_t nBufferDTSize = bufferDataType.GetSize();
6312 20 : for (size_t i = 0; i < nDims; i++)
6313 : {
6314 28 : stack[i].dst_inc_offset =
6315 14 : tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6316 14 : stack[i].src_inc_offset =
6317 14 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6318 : }
6319 6 : stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6320 6 : stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6321 :
6322 6 : size_t dimIdx = 0;
6323 6 : const size_t nDimsMinus1 = nDims - 1;
6324 :
6325 34 : lbl_next_depth:
6326 34 : if (dimIdx == nDimsMinus1)
6327 : {
6328 23 : auto nIters = count[dimIdx];
6329 23 : double *dst_ptr = stack[dimIdx].dst_ptr;
6330 23 : const GByte *src_ptr = stack[dimIdx].src_ptr;
6331 : while (true)
6332 : {
6333 : double adfVal[2];
6334 : const double *padfSrcVal;
6335 86 : if (bIsBufferDataTypeNativeDataType)
6336 : {
6337 50 : padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6338 : }
6339 : else
6340 : {
6341 36 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6342 : &adfVal[0], dtDouble);
6343 36 : padfSrcVal = adfVal;
6344 : }
6345 :
6346 148 : if (bSelfAndParentHaveNoData &&
6347 62 : (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6348 : {
6349 3 : dst_ptr[0] = adfSrcNoData[0];
6350 3 : if (bDTIsComplex)
6351 : {
6352 1 : dst_ptr[1] = adfSrcNoData[1];
6353 : }
6354 : }
6355 : else
6356 : {
6357 83 : dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6358 83 : if (bDTIsComplex)
6359 : {
6360 1 : dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6361 : }
6362 : }
6363 :
6364 86 : if ((--nIters) == 0)
6365 23 : break;
6366 63 : dst_ptr += stack[dimIdx].dst_inc_offset;
6367 63 : src_ptr += stack[dimIdx].src_inc_offset;
6368 63 : }
6369 : }
6370 : else
6371 : {
6372 11 : stack[dimIdx].nIters = count[dimIdx];
6373 : while (true)
6374 : {
6375 28 : dimIdx++;
6376 28 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6377 28 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6378 28 : goto lbl_next_depth;
6379 28 : lbl_return_to_caller:
6380 28 : dimIdx--;
6381 28 : if ((--stack[dimIdx].nIters) == 0)
6382 11 : break;
6383 17 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6384 17 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6385 : }
6386 : }
6387 34 : if (dimIdx > 0)
6388 28 : goto lbl_return_to_caller;
6389 :
6390 : // If the parent array is not double/complex-double, then convert the
6391 : // values to it, before calling Write(), as some implementations can be
6392 : // very slow when doing the type conversion.
6393 6 : const auto &eParentDT = m_poParent->GetDataType();
6394 6 : const size_t nParentDTSize = eParentDT.GetSize();
6395 6 : if (nParentDTSize <= nDTSize / 2)
6396 : {
6397 : // Copy in-place by making sure that source and target do not overlap
6398 6 : const auto eNumericDT = dtDouble.GetNumericDataType();
6399 6 : const auto eParentNumericDT = eParentDT.GetNumericDataType();
6400 :
6401 : // Copy first element
6402 : {
6403 6 : std::vector<GByte> abyTemp(nParentDTSize);
6404 6 : GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6405 6 : static_cast<int>(nDTSize), &abyTemp[0],
6406 : eParentNumericDT, static_cast<int>(nParentDTSize),
6407 : 1);
6408 6 : memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6409 : }
6410 : // Remaining elements
6411 86 : for (size_t i = 1; i < nElts; ++i)
6412 : {
6413 80 : GDALCopyWords(static_cast<GByte *>(pTempBuffer) + i * nDTSize,
6414 : eNumericDT, 0,
6415 80 : static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6416 : eParentNumericDT, 0, 1);
6417 : }
6418 : }
6419 :
6420 : const bool ret =
6421 6 : m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6422 : eParentDT, pTempBuffer);
6423 :
6424 6 : VSIFree(pTempBuffer);
6425 6 : return ret;
6426 : }
6427 :
6428 : /************************************************************************/
6429 : /* GetUnscaled() */
6430 : /************************************************************************/
6431 :
6432 : /** Return an array that is the unscaled version of the current one.
6433 : *
6434 : * That is each value of the unscaled array will be
6435 : * unscaled_value = raw_value * GetScale() + GetOffset()
6436 : *
6437 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
6438 : * from unscaled values to raw values.
6439 : *
6440 : * This is the same as the C function GDALMDArrayGetUnscaled().
6441 : *
6442 : * @param dfOverriddenScale Custom scale value instead of GetScale()
6443 : * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6444 : * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6445 : * @return a new array, that holds a reference to the original one, and thus is
6446 : * a view of it (not a copy), or nullptr in case of error.
6447 : */
6448 : std::shared_ptr<GDALMDArray>
6449 17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6450 : double dfOverriddenDstNodata) const
6451 : {
6452 34 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6453 17 : if (!self)
6454 : {
6455 0 : CPLError(CE_Failure, CPLE_AppDefined,
6456 : "Driver implementation issue: m_pSelf not set !");
6457 0 : return nullptr;
6458 : }
6459 17 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
6460 : {
6461 0 : CPLError(CE_Failure, CPLE_AppDefined,
6462 : "GetUnscaled() only supports numeric data type");
6463 0 : return nullptr;
6464 : }
6465 : const double dfScale =
6466 17 : std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6467 : const double dfOffset =
6468 17 : std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6469 17 : if (dfScale == 1.0 && dfOffset == 0.0)
6470 4 : return self;
6471 :
6472 13 : GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6473 13 : ? GDT_CFloat64
6474 13 : : GDT_Float64;
6475 14 : if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
6476 1 : GetDataType().GetNumericDataType() == GDT_Float32)
6477 1 : eDT = GDT_Float32;
6478 :
6479 26 : return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6480 13 : dfOverriddenDstNodata, eDT);
6481 : }
6482 :
6483 : /************************************************************************/
6484 : /* GDALMDArrayMask */
6485 : /************************************************************************/
6486 :
6487 : class GDALMDArrayMask final : public GDALPamMDArray
6488 : {
6489 : private:
6490 : std::shared_ptr<GDALMDArray> m_poParent{};
6491 : GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6492 : double m_dfMissingValue = 0.0;
6493 : bool m_bHasMissingValue = false;
6494 : double m_dfFillValue = 0.0;
6495 : bool m_bHasFillValue = false;
6496 : double m_dfValidMin = 0.0;
6497 : bool m_bHasValidMin = false;
6498 : double m_dfValidMax = 0.0;
6499 : bool m_bHasValidMax = false;
6500 : std::vector<uint32_t> m_anValidFlagMasks{};
6501 : std::vector<uint32_t> m_anValidFlagValues{};
6502 :
6503 : bool Init(CSLConstList papszOptions);
6504 :
6505 : template <typename Type>
6506 : void
6507 : ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6508 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6509 : const void *pTempBuffer,
6510 : const GDALExtendedDataType &oTmpBufferDT,
6511 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6512 :
6513 : protected:
6514 45 : explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6515 90 : : GDALAbstractMDArray(std::string(),
6516 90 : "Mask of " + poParent->GetFullName()),
6517 90 : GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6518 90 : GDALPamMultiDim::GetPAM(poParent),
6519 : poParent->GetContext()),
6520 225 : m_poParent(std::move(poParent))
6521 : {
6522 45 : }
6523 :
6524 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6525 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6526 : const GDALExtendedDataType &bufferDataType,
6527 : void *pDstBuffer) const override;
6528 :
6529 0 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6530 : CSLConstList papszOptions) const override
6531 : {
6532 0 : return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6533 : }
6534 :
6535 : public:
6536 : static std::shared_ptr<GDALMDArrayMask>
6537 : Create(const std::shared_ptr<GDALMDArray> &poParent,
6538 : CSLConstList papszOptions);
6539 :
6540 1 : bool IsWritable() const override
6541 : {
6542 1 : return false;
6543 : }
6544 :
6545 48 : const std::string &GetFilename() const override
6546 : {
6547 48 : return m_poParent->GetFilename();
6548 : }
6549 :
6550 : const std::vector<std::shared_ptr<GDALDimension>> &
6551 373 : GetDimensions() const override
6552 : {
6553 373 : return m_poParent->GetDimensions();
6554 : }
6555 :
6556 132 : const GDALExtendedDataType &GetDataType() const override
6557 : {
6558 132 : return m_dt;
6559 : }
6560 :
6561 1 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6562 : {
6563 1 : return m_poParent->GetSpatialRef();
6564 : }
6565 :
6566 2 : std::vector<GUInt64> GetBlockSize() const override
6567 : {
6568 2 : return m_poParent->GetBlockSize();
6569 : }
6570 : };
6571 :
6572 : /************************************************************************/
6573 : /* GDALMDArrayMask::Create() */
6574 : /************************************************************************/
6575 :
6576 : /* static */ std::shared_ptr<GDALMDArrayMask>
6577 45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6578 : CSLConstList papszOptions)
6579 : {
6580 90 : auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6581 45 : newAr->SetSelf(newAr);
6582 45 : if (!newAr->Init(papszOptions))
6583 6 : return nullptr;
6584 39 : return newAr;
6585 : }
6586 :
6587 : /************************************************************************/
6588 : /* GDALMDArrayMask::Init() */
6589 : /************************************************************************/
6590 :
6591 45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6592 : {
6593 : const auto GetSingleValNumericAttr =
6594 180 : [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6595 : {
6596 540 : auto poAttr = m_poParent->GetAttribute(pszAttrName);
6597 180 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6598 : {
6599 22 : const auto anDimSizes = poAttr->GetDimensionsSize();
6600 21 : if (anDimSizes.empty() ||
6601 10 : (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6602 : {
6603 11 : bHasVal = true;
6604 11 : dfVal = poAttr->ReadAsDouble();
6605 : }
6606 : }
6607 180 : };
6608 :
6609 45 : GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6610 45 : m_dfMissingValue);
6611 45 : GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6612 45 : GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6613 45 : GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6614 :
6615 : {
6616 135 : auto poValidRange = m_poParent->GetAttribute("valid_range");
6617 50 : if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6618 55 : poValidRange->GetDimensionsSize()[0] == 2 &&
6619 5 : poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6620 : {
6621 5 : m_bHasValidMin = true;
6622 5 : m_bHasValidMax = true;
6623 5 : auto vals = poValidRange->ReadAsDoubleArray();
6624 5 : CPLAssert(vals.size() == 2);
6625 5 : m_dfValidMin = vals[0];
6626 5 : m_dfValidMax = vals[1];
6627 : }
6628 : }
6629 :
6630 : // Take into account
6631 : // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6632 : // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6633 : const char *pszUnmaskFlags =
6634 45 : CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6635 45 : if (pszUnmaskFlags)
6636 : {
6637 : const auto IsScalarStringAttr =
6638 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6639 : {
6640 26 : return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6641 26 : (poAttr->GetDimensionsSize().empty() ||
6642 13 : (poAttr->GetDimensionsSize().size() == 1 &&
6643 26 : poAttr->GetDimensionsSize()[0] == 1));
6644 : };
6645 :
6646 28 : auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6647 14 : if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6648 : {
6649 1 : CPLError(CE_Failure, CPLE_AppDefined,
6650 : "UNMASK_FLAGS option specified but array has no "
6651 : "flag_meanings attribute");
6652 1 : return false;
6653 : }
6654 13 : const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6655 13 : if (!pszFlagMeanings)
6656 : {
6657 1 : CPLError(CE_Failure, CPLE_AppDefined,
6658 : "Cannot read flag_meanings attribute");
6659 1 : return false;
6660 : }
6661 :
6662 : const auto IsSingleDimNumericAttr =
6663 13 : [](const std::shared_ptr<GDALAttribute> &poAttr)
6664 : {
6665 26 : return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6666 26 : poAttr->GetDimensionsSize().size() == 1;
6667 : };
6668 :
6669 24 : auto poFlagValues = m_poParent->GetAttribute("flag_values");
6670 : const bool bHasFlagValues =
6671 12 : poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6672 :
6673 24 : auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6674 : const bool bHasFlagMasks =
6675 12 : poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6676 :
6677 12 : if (!bHasFlagValues && !bHasFlagMasks)
6678 : {
6679 1 : CPLError(CE_Failure, CPLE_AppDefined,
6680 : "Cannot find flag_values and/or flag_masks attribute");
6681 1 : return false;
6682 : }
6683 :
6684 : const CPLStringList aosUnmaskFlags(
6685 11 : CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6686 : const CPLStringList aosFlagMeanings(
6687 11 : CSLTokenizeString2(pszFlagMeanings, " ", 0));
6688 :
6689 11 : if (bHasFlagValues)
6690 : {
6691 7 : const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6692 : // We could support Int64 or UInt64, but more work...
6693 7 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6694 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6695 : {
6696 0 : CPLError(CE_Failure, CPLE_NotSupported,
6697 : "Unsupported data type for flag_values attribute: %s",
6698 : GDALGetDataTypeName(eType));
6699 0 : return false;
6700 : }
6701 : }
6702 :
6703 11 : if (bHasFlagMasks)
6704 : {
6705 6 : const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6706 : // We could support Int64 or UInt64, but more work...
6707 6 : if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6708 0 : eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6709 : {
6710 0 : CPLError(CE_Failure, CPLE_NotSupported,
6711 : "Unsupported data type for flag_masks attribute: %s",
6712 : GDALGetDataTypeName(eType));
6713 0 : return false;
6714 : }
6715 : }
6716 :
6717 : const std::vector<double> adfValues(
6718 : bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6719 11 : : std::vector<double>());
6720 : const std::vector<double> adfMasks(
6721 : bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6722 11 : : std::vector<double>());
6723 :
6724 18 : if (bHasFlagValues &&
6725 7 : adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6726 : {
6727 1 : CPLError(CE_Failure, CPLE_AppDefined,
6728 : "Number of values in flag_values attribute is different "
6729 : "from the one in flag_meanings");
6730 1 : return false;
6731 : }
6732 :
6733 16 : if (bHasFlagMasks &&
6734 6 : adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6735 : {
6736 1 : CPLError(CE_Failure, CPLE_AppDefined,
6737 : "Number of values in flag_masks attribute is different "
6738 : "from the one in flag_meanings");
6739 1 : return false;
6740 : }
6741 :
6742 19 : for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6743 : {
6744 11 : const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6745 11 : if (nIdxFlag < 0)
6746 : {
6747 1 : CPLError(
6748 : CE_Failure, CPLE_AppDefined,
6749 : "Cannot fing flag %s in flag_meanings = '%s' attribute",
6750 : aosUnmaskFlags[i], pszFlagMeanings);
6751 1 : return false;
6752 : }
6753 :
6754 10 : if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6755 : {
6756 0 : CPLError(CE_Failure, CPLE_AppDefined,
6757 : "Invalid value in flag_values[%d] = %f", nIdxFlag,
6758 0 : adfValues[nIdxFlag]);
6759 0 : return false;
6760 : }
6761 :
6762 10 : if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6763 : {
6764 0 : CPLError(CE_Failure, CPLE_AppDefined,
6765 : "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6766 0 : adfMasks[nIdxFlag]);
6767 0 : return false;
6768 : }
6769 :
6770 10 : if (bHasFlagValues)
6771 : {
6772 12 : m_anValidFlagValues.push_back(
6773 6 : static_cast<uint32_t>(adfValues[nIdxFlag]));
6774 : }
6775 :
6776 10 : if (bHasFlagMasks)
6777 : {
6778 12 : m_anValidFlagMasks.push_back(
6779 6 : static_cast<uint32_t>(adfMasks[nIdxFlag]));
6780 : }
6781 : }
6782 : }
6783 :
6784 39 : return true;
6785 : }
6786 :
6787 : /************************************************************************/
6788 : /* IRead() */
6789 : /************************************************************************/
6790 :
6791 48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6792 : const GInt64 *arrayStep,
6793 : const GPtrDiff_t *bufferStride,
6794 : const GDALExtendedDataType &bufferDataType,
6795 : void *pDstBuffer) const
6796 : {
6797 48 : size_t nElts = 1;
6798 48 : const size_t nDims = GetDimensionCount();
6799 96 : std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6800 132 : for (size_t i = 0; i < nDims; i++)
6801 84 : nElts *= count[i];
6802 48 : if (nDims > 0)
6803 : {
6804 43 : tmpBufferStrideVector.back() = 1;
6805 84 : for (size_t i = nDims - 1; i > 0;)
6806 : {
6807 41 : --i;
6808 41 : tmpBufferStrideVector[i] =
6809 41 : tmpBufferStrideVector[i + 1] * count[i + 1];
6810 : }
6811 : }
6812 :
6813 : /* Optimized case: if we are an integer data type and that there is no */
6814 : /* attribute that can be used to set mask = 0, then fill the mask buffer */
6815 : /* directly */
6816 46 : if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
6817 70 : !m_bHasValidMax && m_anValidFlagValues.empty() &&
6818 32 : m_anValidFlagMasks.empty() &&
6819 103 : m_poParent->GetRawNoDataValue() == nullptr &&
6820 9 : GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
6821 : {
6822 7 : if (bufferDataType == m_dt) // Byte case
6823 : {
6824 4 : bool bContiguous = true;
6825 10 : for (size_t i = 0; i < nDims; i++)
6826 : {
6827 7 : if (bufferStride[i] != tmpBufferStrideVector[i])
6828 : {
6829 1 : bContiguous = false;
6830 1 : break;
6831 : }
6832 : }
6833 4 : if (bContiguous)
6834 : {
6835 : // CPLDebug("GDAL", "GetMask(): contiguous case");
6836 3 : memset(pDstBuffer, 1, nElts);
6837 3 : return true;
6838 : }
6839 : }
6840 :
6841 : struct Stack
6842 : {
6843 : size_t nIters = 0;
6844 : GByte *dst_ptr = nullptr;
6845 : GPtrDiff_t dst_inc_offset = 0;
6846 : };
6847 :
6848 4 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
6849 4 : const size_t nBufferDTSize = bufferDataType.GetSize();
6850 13 : for (size_t i = 0; i < nDims; i++)
6851 : {
6852 9 : stack[i].dst_inc_offset =
6853 9 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6854 : }
6855 4 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6856 :
6857 4 : size_t dimIdx = 0;
6858 4 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
6859 4 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
6860 : GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
6861 4 : CPLAssert(nBufferDTSize <= 16);
6862 4 : const GByte flag = 1;
6863 : // Coverity misses that m_dt is of type Byte
6864 : // coverity[overrun-buffer-val]
6865 4 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
6866 :
6867 28 : lbl_next_depth:
6868 28 : if (dimIdx == nDimsMinus1)
6869 : {
6870 19 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
6871 19 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
6872 :
6873 : while (true)
6874 : {
6875 73 : if (bBufferDataTypeIsByte)
6876 : {
6877 24 : *dst_ptr = flag;
6878 : }
6879 : else
6880 : {
6881 49 : memcpy(dst_ptr, abyOne, nBufferDTSize);
6882 : }
6883 :
6884 73 : if ((--nIters) == 0)
6885 19 : break;
6886 54 : dst_ptr += stack[dimIdx].dst_inc_offset;
6887 : }
6888 : }
6889 : else
6890 : {
6891 9 : stack[dimIdx].nIters = count[dimIdx];
6892 : while (true)
6893 : {
6894 24 : dimIdx++;
6895 24 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6896 24 : goto lbl_next_depth;
6897 24 : lbl_return_to_caller:
6898 24 : dimIdx--;
6899 24 : if ((--stack[dimIdx].nIters) == 0)
6900 9 : break;
6901 15 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6902 : }
6903 : }
6904 28 : if (dimIdx > 0)
6905 24 : goto lbl_return_to_caller;
6906 :
6907 4 : return true;
6908 : }
6909 :
6910 : const auto oTmpBufferDT =
6911 41 : GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
6912 : ? GDALExtendedDataType::Create(GDT_Float64)
6913 82 : : m_poParent->GetDataType();
6914 41 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
6915 41 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
6916 41 : if (!pTempBuffer)
6917 0 : return false;
6918 82 : if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6919 41 : tmpBufferStrideVector.data(), oTmpBufferDT,
6920 : pTempBuffer))
6921 : {
6922 0 : VSIFree(pTempBuffer);
6923 0 : return false;
6924 : }
6925 :
6926 41 : switch (oTmpBufferDT.GetNumericDataType())
6927 : {
6928 6 : case GDT_Byte:
6929 6 : ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
6930 : pTempBuffer, oTmpBufferDT,
6931 : tmpBufferStrideVector);
6932 6 : break;
6933 :
6934 0 : case GDT_Int8:
6935 0 : ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
6936 : pTempBuffer, oTmpBufferDT,
6937 : tmpBufferStrideVector);
6938 0 : break;
6939 :
6940 1 : case GDT_UInt16:
6941 1 : ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
6942 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6943 : tmpBufferStrideVector);
6944 1 : break;
6945 :
6946 14 : case GDT_Int16:
6947 14 : ReadInternal<GInt16>(count, bufferStride, bufferDataType,
6948 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6949 : tmpBufferStrideVector);
6950 14 : break;
6951 :
6952 1 : case GDT_UInt32:
6953 1 : ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
6954 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6955 : tmpBufferStrideVector);
6956 1 : break;
6957 :
6958 5 : case GDT_Int32:
6959 5 : ReadInternal<GInt32>(count, bufferStride, bufferDataType,
6960 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6961 : tmpBufferStrideVector);
6962 5 : break;
6963 :
6964 0 : case GDT_UInt64:
6965 0 : ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
6966 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6967 : tmpBufferStrideVector);
6968 0 : break;
6969 :
6970 0 : case GDT_Int64:
6971 0 : ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
6972 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6973 : tmpBufferStrideVector);
6974 0 : break;
6975 :
6976 7 : case GDT_Float32:
6977 7 : ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
6978 : pTempBuffer, oTmpBufferDT,
6979 : tmpBufferStrideVector);
6980 7 : break;
6981 :
6982 7 : case GDT_Float64:
6983 7 : ReadInternal<double>(count, bufferStride, bufferDataType,
6984 : pDstBuffer, pTempBuffer, oTmpBufferDT,
6985 : tmpBufferStrideVector);
6986 7 : break;
6987 0 : case GDT_Unknown:
6988 : case GDT_CInt16:
6989 : case GDT_CInt32:
6990 : case GDT_CFloat32:
6991 : case GDT_CFloat64:
6992 : case GDT_TypeCount:
6993 0 : CPLAssert(false);
6994 : break;
6995 : }
6996 :
6997 41 : VSIFree(pTempBuffer);
6998 :
6999 41 : return true;
7000 : }
7001 :
7002 : /************************************************************************/
7003 : /* IsValidForDT() */
7004 : /************************************************************************/
7005 :
7006 38 : template <typename Type> static bool IsValidForDT(double dfVal)
7007 : {
7008 38 : if (std::isnan(dfVal))
7009 0 : return false;
7010 38 : if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
7011 0 : return false;
7012 38 : if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
7013 0 : return false;
7014 38 : return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7015 : }
7016 :
7017 9 : template <> bool IsValidForDT<double>(double)
7018 : {
7019 9 : return true;
7020 : }
7021 :
7022 : /************************************************************************/
7023 : /* IsNan() */
7024 : /************************************************************************/
7025 :
7026 1038 : template <typename Type> inline bool IsNan(Type)
7027 : {
7028 1038 : return false;
7029 : }
7030 :
7031 25 : template <> bool IsNan<double>(double val)
7032 : {
7033 25 : return std::isnan(val);
7034 : }
7035 :
7036 26 : template <> bool IsNan<float>(float val)
7037 : {
7038 26 : return std::isnan(val);
7039 : }
7040 :
7041 : /************************************************************************/
7042 : /* ReadInternal() */
7043 : /************************************************************************/
7044 :
7045 : template <typename Type>
7046 41 : void GDALMDArrayMask::ReadInternal(
7047 : const size_t *count, const GPtrDiff_t *bufferStride,
7048 : const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7049 : const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7050 : const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7051 : {
7052 41 : const size_t nDims = GetDimensionCount();
7053 :
7054 205 : const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7055 : {
7056 205 : if (bHasVal)
7057 : {
7058 47 : if (IsValidForDT<Type>(dfVal))
7059 : {
7060 47 : return static_cast<Type>(dfVal);
7061 : }
7062 : else
7063 : {
7064 0 : bHasVal = false;
7065 : }
7066 : }
7067 158 : return 0;
7068 : };
7069 :
7070 41 : const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7071 41 : bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7072 : const Type nNoDataValue =
7073 41 : castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7074 41 : bool bHasMissingValue = m_bHasMissingValue;
7075 41 : const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7076 41 : bool bHasFillValue = m_bHasFillValue;
7077 41 : const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7078 41 : bool bHasValidMin = m_bHasValidMin;
7079 41 : const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7080 41 : bool bHasValidMax = m_bHasValidMax;
7081 41 : const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7082 41 : const bool bHasValidFlags =
7083 41 : !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7084 :
7085 348 : const auto IsValidFlag = [this](Type v)
7086 : {
7087 54 : if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7088 : {
7089 20 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7090 : {
7091 12 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7092 : m_anValidFlagValues[i])
7093 : {
7094 4 : return true;
7095 : }
7096 : }
7097 : }
7098 42 : else if (!m_anValidFlagValues.empty())
7099 : {
7100 49 : for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7101 : {
7102 29 : if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7103 : {
7104 4 : return true;
7105 : }
7106 : }
7107 : }
7108 : else /* if( !m_anValidFlagMasks.empty() ) */
7109 : {
7110 31 : for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7111 : {
7112 22 : if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7113 : {
7114 9 : return true;
7115 : }
7116 : }
7117 : }
7118 37 : return false;
7119 : };
7120 :
7121 : #define GET_MASK_FOR_SAMPLE(v) \
7122 : static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7123 : !(bHasMissingValue && v == nMissingValue) && \
7124 : !(bHasFillValue && v == nFillValue) && \
7125 : !(bHasValidMin && v < nValidMin) && \
7126 : !(bHasValidMax && v > nValidMax) && \
7127 : (!bHasValidFlags || IsValidFlag(v)));
7128 :
7129 41 : const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7130 : /* Optimized case: Byte output and output buffer is contiguous */
7131 41 : if (bBufferDataTypeIsByte)
7132 : {
7133 37 : bool bContiguous = true;
7134 96 : for (size_t i = 0; i < nDims; i++)
7135 : {
7136 60 : if (bufferStride[i] != tmpBufferStrideVector[i])
7137 : {
7138 1 : bContiguous = false;
7139 1 : break;
7140 : }
7141 : }
7142 37 : if (bContiguous)
7143 : {
7144 36 : size_t nElts = 1;
7145 95 : for (size_t i = 0; i < nDims; i++)
7146 59 : nElts *= count[i];
7147 :
7148 670 : for (size_t i = 0; i < nElts; i++)
7149 : {
7150 634 : const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7151 634 : static_cast<GByte *>(pDstBuffer)[i] =
7152 634 : GET_MASK_FOR_SAMPLE(*pSrc);
7153 : }
7154 36 : return;
7155 : }
7156 : }
7157 :
7158 5 : const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7159 :
7160 : struct Stack
7161 : {
7162 : size_t nIters = 0;
7163 : const GByte *src_ptr = nullptr;
7164 : GByte *dst_ptr = nullptr;
7165 : GPtrDiff_t src_inc_offset = 0;
7166 : GPtrDiff_t dst_inc_offset = 0;
7167 : };
7168 :
7169 10 : std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7170 5 : const size_t nBufferDTSize = bufferDataType.GetSize();
7171 15 : for (size_t i = 0; i < nDims; i++)
7172 : {
7173 20 : stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7174 10 : tmpBufferStrideVector[i] * nTmpBufferDTSize);
7175 10 : stack[i].dst_inc_offset =
7176 10 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7177 : }
7178 5 : stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7179 5 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7180 :
7181 5 : size_t dimIdx = 0;
7182 5 : const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7183 : GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
7184 5 : CPLAssert(nBufferDTSize <= 16);
7185 15 : for (GByte flag = 0; flag <= 1; flag++)
7186 : {
7187 : // Coverity misses that m_dt is of type Byte
7188 : // coverity[overrun-buffer-val]
7189 10 : GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
7190 : bufferDataType);
7191 : }
7192 :
7193 43 : lbl_next_depth:
7194 43 : if (dimIdx == nDimsMinus1)
7195 : {
7196 35 : auto nIters = nDims > 0 ? count[dimIdx] : 1;
7197 35 : const GByte *src_ptr = stack[dimIdx].src_ptr;
7198 35 : GByte *dst_ptr = stack[dimIdx].dst_ptr;
7199 :
7200 420 : while (true)
7201 : {
7202 455 : const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7203 455 : const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7204 :
7205 455 : if (bBufferDataTypeIsByte)
7206 : {
7207 24 : *dst_ptr = flag;
7208 : }
7209 : else
7210 : {
7211 431 : memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7212 : }
7213 :
7214 455 : if ((--nIters) == 0)
7215 35 : break;
7216 420 : src_ptr += stack[dimIdx].src_inc_offset;
7217 420 : dst_ptr += stack[dimIdx].dst_inc_offset;
7218 : }
7219 : }
7220 : else
7221 : {
7222 8 : stack[dimIdx].nIters = count[dimIdx];
7223 : while (true)
7224 : {
7225 38 : dimIdx++;
7226 38 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7227 38 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7228 38 : goto lbl_next_depth;
7229 38 : lbl_return_to_caller:
7230 38 : dimIdx--;
7231 38 : if ((--stack[dimIdx].nIters) == 0)
7232 8 : break;
7233 30 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7234 30 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7235 : }
7236 : }
7237 43 : if (dimIdx > 0)
7238 38 : goto lbl_return_to_caller;
7239 : }
7240 :
7241 : /************************************************************************/
7242 : /* GetMask() */
7243 : /************************************************************************/
7244 :
7245 : /** Return an array that is a mask for the current array
7246 :
7247 : This array will be of type Byte, with values set to 0 to indicate invalid
7248 : pixels of the current array, and values set to 1 to indicate valid pixels.
7249 :
7250 : The generic implementation honours the NoDataValue, as well as various
7251 : netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7252 : and valid_range.
7253 :
7254 : Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7255 : can be used to specify strings of the "flag_meanings" attribute
7256 : (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7257 : for which pixels matching any of those flags will be set at 1 in the mask array,
7258 : and pixels matching none of those flags will be set at 0.
7259 : For example, let's consider the following netCDF variable defined with:
7260 : \verbatim
7261 : l2p_flags:valid_min = 0s ;
7262 : l2p_flags:valid_max = 256s ;
7263 : l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7264 : l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7265 : \endverbatim
7266 :
7267 : GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7268 : - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7269 : - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7270 : will be 1.
7271 : - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7272 : will be 0.
7273 :
7274 : This is the same as the C function GDALMDArrayGetMask().
7275 :
7276 : @param papszOptions NULL-terminated list of options, or NULL.
7277 :
7278 : @return a new array, that holds a reference to the original one, and thus is
7279 : a view of it (not a copy), or nullptr in case of error.
7280 : */
7281 : std::shared_ptr<GDALMDArray>
7282 46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
7283 : {
7284 92 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7285 46 : if (!self)
7286 : {
7287 0 : CPLError(CE_Failure, CPLE_AppDefined,
7288 : "Driver implementation issue: m_pSelf not set !");
7289 0 : return nullptr;
7290 : }
7291 46 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
7292 : {
7293 1 : CPLError(CE_Failure, CPLE_AppDefined,
7294 : "GetMask() only supports numeric data type");
7295 1 : return nullptr;
7296 : }
7297 45 : return GDALMDArrayMask::Create(self, papszOptions);
7298 : }
7299 :
7300 : /************************************************************************/
7301 : /* IsRegularlySpaced() */
7302 : /************************************************************************/
7303 :
7304 : /** Returns whether an array is a 1D regularly spaced array.
7305 : *
7306 : * @param[out] dfStart First value in the array
7307 : * @param[out] dfIncrement Increment/spacing between consecutive values.
7308 : * @return true if the array is regularly spaced.
7309 : */
7310 175 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7311 : {
7312 175 : dfStart = 0;
7313 175 : dfIncrement = 0;
7314 175 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7315 0 : return false;
7316 175 : const auto nSize = GetDimensions()[0]->GetSize();
7317 175 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7318 2 : return false;
7319 :
7320 173 : size_t nCount = static_cast<size_t>(nSize);
7321 346 : std::vector<double> adfTmp;
7322 : try
7323 : {
7324 173 : adfTmp.resize(nCount);
7325 : }
7326 0 : catch (const std::exception &)
7327 : {
7328 0 : return false;
7329 : }
7330 :
7331 173 : GUInt64 anStart[1] = {0};
7332 173 : size_t anCount[1] = {nCount};
7333 :
7334 : const auto IsRegularlySpacedInternal =
7335 79790 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7336 : {
7337 245 : dfStart = adfTmp[0];
7338 245 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7339 245 : if (dfIncrement == 0)
7340 : {
7341 3 : return false;
7342 : }
7343 19884 : for (size_t i = 1; i < anCount[0]; i++)
7344 : {
7345 19642 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7346 19642 : 1e-3 * fabs(dfIncrement))
7347 : {
7348 0 : return false;
7349 : }
7350 : }
7351 242 : return true;
7352 173 : };
7353 :
7354 : // First try with the first block(s). This can avoid excessive processing
7355 : // time, for example with Zarr datasets.
7356 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7357 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7358 173 : const auto nBlockSize = GetBlockSize()[0];
7359 173 : if (nCount >= 5 && nBlockSize <= nCount / 2)
7360 : {
7361 : size_t nReducedCount =
7362 75 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7363 436 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7364 361 : nReducedCount *= 2;
7365 75 : anCount[0] = nReducedCount;
7366 75 : if (!Read(anStart, anCount, nullptr, nullptr,
7367 150 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7368 : {
7369 0 : return false;
7370 : }
7371 75 : if (!IsRegularlySpacedInternal())
7372 : {
7373 3 : return false;
7374 : }
7375 :
7376 : // Get next values
7377 72 : anStart[0] = nReducedCount;
7378 72 : anCount[0] = nCount - nReducedCount;
7379 : }
7380 :
7381 170 : if (!Read(anStart, anCount, nullptr, nullptr,
7382 340 : GDALExtendedDataType::Create(GDT_Float64),
7383 170 : &adfTmp[static_cast<size_t>(anStart[0])]))
7384 : {
7385 0 : return false;
7386 : }
7387 :
7388 170 : return IsRegularlySpacedInternal();
7389 : }
7390 :
7391 : /************************************************************************/
7392 : /* GuessGeoTransform() */
7393 : /************************************************************************/
7394 :
7395 : /** Returns whether 2 specified dimensions form a geotransform
7396 : *
7397 : * @param nDimX Index of the X axis.
7398 : * @param nDimY Index of the Y axis.
7399 : * @param bPixelIsPoint Whether the geotransform should be returned
7400 : * with the pixel-is-point (pixel-center) convention
7401 : * (bPixelIsPoint = true), or with the pixel-is-area
7402 : * (top left corner convention)
7403 : * (bPixelIsPoint = false)
7404 : * @param[out] adfGeoTransform Computed geotransform
7405 : * @return true if a geotransform could be computed.
7406 : */
7407 183 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7408 : bool bPixelIsPoint,
7409 : double adfGeoTransform[6]) const
7410 : {
7411 183 : const auto &dims(GetDimensions());
7412 366 : auto poVarX = dims[nDimX]->GetIndexingVariable();
7413 366 : auto poVarY = dims[nDimY]->GetIndexingVariable();
7414 183 : double dfXStart = 0.0;
7415 183 : double dfXSpacing = 0.0;
7416 183 : double dfYStart = 0.0;
7417 183 : double dfYSpacing = 0.0;
7418 415 : if (poVarX && poVarX->GetDimensionCount() == 1 &&
7419 232 : poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7420 292 : poVarY && poVarY->GetDimensionCount() == 1 &&
7421 88 : poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7422 382 : poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7423 83 : poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7424 : {
7425 83 : adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7426 83 : adfGeoTransform[1] = dfXSpacing;
7427 83 : adfGeoTransform[2] = 0;
7428 83 : adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7429 83 : adfGeoTransform[4] = 0;
7430 83 : adfGeoTransform[5] = dfYSpacing;
7431 83 : return true;
7432 : }
7433 100 : return false;
7434 : }
7435 :
7436 : /************************************************************************/
7437 : /* GDALMDArrayResampled */
7438 : /************************************************************************/
7439 :
7440 : class GDALMDArrayResampledDataset;
7441 :
7442 : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7443 : {
7444 : protected:
7445 : CPLErr IReadBlock(int, int, void *) override;
7446 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7447 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
7448 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7449 : GSpacing nLineSpaceBuf,
7450 : GDALRasterIOExtraArg *psExtraArg) override;
7451 :
7452 : public:
7453 : explicit GDALMDArrayResampledDatasetRasterBand(
7454 : GDALMDArrayResampledDataset *poDSIn);
7455 :
7456 : double GetNoDataValue(int *pbHasNoData) override;
7457 : };
7458 :
7459 : class GDALMDArrayResampledDataset final : public GDALPamDataset
7460 : {
7461 : friend class GDALMDArrayResampled;
7462 : friend class GDALMDArrayResampledDatasetRasterBand;
7463 :
7464 : std::shared_ptr<GDALMDArray> m_poArray;
7465 : const size_t m_iXDim;
7466 : const size_t m_iYDim;
7467 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7468 : bool m_bHasGT = false;
7469 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7470 :
7471 : std::vector<GUInt64> m_anOffset{};
7472 : std::vector<size_t> m_anCount{};
7473 : std::vector<GPtrDiff_t> m_anStride{};
7474 :
7475 : std::string m_osFilenameLong{};
7476 : std::string m_osFilenameLat{};
7477 :
7478 : public:
7479 24 : GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7480 : size_t iXDim, size_t iYDim)
7481 24 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7482 24 : m_anOffset(m_poArray->GetDimensionCount(), 0),
7483 24 : m_anCount(m_poArray->GetDimensionCount(), 1),
7484 72 : m_anStride(m_poArray->GetDimensionCount(), 0)
7485 : {
7486 24 : const auto &dims(m_poArray->GetDimensions());
7487 :
7488 24 : nRasterYSize = static_cast<int>(
7489 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7490 24 : nRasterXSize = static_cast<int>(
7491 24 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7492 :
7493 24 : m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7494 24 : m_adfGeoTransform);
7495 :
7496 24 : SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7497 24 : }
7498 :
7499 48 : ~GDALMDArrayResampledDataset()
7500 24 : {
7501 24 : if (!m_osFilenameLong.empty())
7502 5 : VSIUnlink(m_osFilenameLong.c_str());
7503 24 : if (!m_osFilenameLat.empty())
7504 5 : VSIUnlink(m_osFilenameLat.c_str());
7505 48 : }
7506 :
7507 43 : CPLErr GetGeoTransform(double *padfGeoTransform) override
7508 : {
7509 43 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7510 43 : return m_bHasGT ? CE_None : CE_Failure;
7511 : }
7512 :
7513 105 : const OGRSpatialReference *GetSpatialRef() const override
7514 : {
7515 105 : m_poSRS = m_poArray->GetSpatialRef();
7516 105 : if (m_poSRS)
7517 : {
7518 79 : m_poSRS.reset(m_poSRS->Clone());
7519 158 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7520 237 : for (auto &m : axisMapping)
7521 : {
7522 158 : if (m == static_cast<int>(m_iXDim) + 1)
7523 79 : m = 1;
7524 79 : else if (m == static_cast<int>(m_iYDim) + 1)
7525 79 : m = 2;
7526 : }
7527 79 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7528 : }
7529 105 : return m_poSRS.get();
7530 : }
7531 :
7532 5 : void SetGeolocationArray(const std::string &osFilenameLong,
7533 : const std::string &osFilenameLat)
7534 : {
7535 5 : m_osFilenameLong = osFilenameLong;
7536 5 : m_osFilenameLat = osFilenameLat;
7537 10 : CPLStringList aosGeoLoc;
7538 5 : aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7539 5 : aosGeoLoc.SetNameValue("LINE_STEP", "1");
7540 5 : aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7541 5 : aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7542 5 : aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
7543 5 : aosGeoLoc.SetNameValue("X_BAND", "1");
7544 5 : aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7545 5 : aosGeoLoc.SetNameValue("Y_BAND", "1");
7546 5 : aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7547 5 : aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7548 5 : SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7549 5 : }
7550 : };
7551 :
7552 : /************************************************************************/
7553 : /* GDALMDArrayResampledDatasetRasterBand() */
7554 : /************************************************************************/
7555 :
7556 24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7557 24 : GDALMDArrayResampledDataset *poDSIn)
7558 : {
7559 24 : const auto &poArray(poDSIn->m_poArray);
7560 24 : const auto blockSize(poArray->GetBlockSize());
7561 24 : nBlockYSize = (blockSize[poDSIn->m_iYDim])
7562 24 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7563 13 : blockSize[poDSIn->m_iYDim]))
7564 24 : : 1;
7565 24 : nBlockXSize = blockSize[poDSIn->m_iXDim]
7566 13 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7567 13 : blockSize[poDSIn->m_iXDim]))
7568 24 : : poDSIn->GetRasterXSize();
7569 24 : eDataType = poArray->GetDataType().GetNumericDataType();
7570 24 : eAccess = poDSIn->eAccess;
7571 24 : }
7572 :
7573 : /************************************************************************/
7574 : /* GetNoDataValue() */
7575 : /************************************************************************/
7576 :
7577 46 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7578 : {
7579 46 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7580 46 : const auto &poArray(l_poDS->m_poArray);
7581 46 : bool bHasNodata = false;
7582 46 : double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7583 46 : if (pbHasNoData)
7584 44 : *pbHasNoData = bHasNodata;
7585 46 : return dfRes;
7586 : }
7587 :
7588 : /************************************************************************/
7589 : /* IReadBlock() */
7590 : /************************************************************************/
7591 :
7592 0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7593 : int nBlockYOff,
7594 : void *pImage)
7595 : {
7596 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7597 0 : const int nXOff = nBlockXOff * nBlockXSize;
7598 0 : const int nYOff = nBlockYOff * nBlockYSize;
7599 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7600 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7601 : GDALRasterIOExtraArg sExtraArg;
7602 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7603 0 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7604 : nReqXSize, nReqYSize, eDataType, nDTSize,
7605 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7606 : }
7607 :
7608 : /************************************************************************/
7609 : /* IRasterIO() */
7610 : /************************************************************************/
7611 :
7612 35 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7613 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7614 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7615 : GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7616 : GDALRasterIOExtraArg *psExtraArg)
7617 : {
7618 35 : auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7619 35 : const auto &poArray(l_poDS->m_poArray);
7620 35 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7621 35 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7622 35 : nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7623 35 : (nLineSpaceBuf % nBufferDTSize) == 0)
7624 : {
7625 35 : l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7626 35 : l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7627 70 : l_poDS->m_anStride[l_poDS->m_iXDim] =
7628 35 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7629 :
7630 35 : l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7631 35 : l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7632 70 : l_poDS->m_anStride[l_poDS->m_iYDim] =
7633 35 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7634 :
7635 70 : return poArray->Read(l_poDS->m_anOffset.data(),
7636 35 : l_poDS->m_anCount.data(), nullptr,
7637 35 : l_poDS->m_anStride.data(),
7638 70 : GDALExtendedDataType::Create(eBufType), pData)
7639 35 : ? CE_None
7640 35 : : CE_Failure;
7641 : }
7642 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7643 : pData, nBufXSize, nBufYSize, eBufType,
7644 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7645 : }
7646 :
7647 : class GDALMDArrayResampled final : public GDALPamMDArray
7648 : {
7649 : private:
7650 : std::shared_ptr<GDALMDArray> m_poParent{};
7651 : std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7652 : std::vector<GUInt64> m_anBlockSize;
7653 : GDALExtendedDataType m_dt;
7654 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
7655 : std::shared_ptr<GDALMDArray> m_poVarX{};
7656 : std::shared_ptr<GDALMDArray> m_poVarY{};
7657 : std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7658 : std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7659 :
7660 : protected:
7661 21 : GDALMDArrayResampled(
7662 : const std::shared_ptr<GDALMDArray> &poParent,
7663 : const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7664 : const std::vector<GUInt64> &anBlockSize)
7665 42 : : GDALAbstractMDArray(std::string(),
7666 42 : "Resampled view of " + poParent->GetFullName()),
7667 : GDALPamMDArray(
7668 42 : std::string(), "Resampled view of " + poParent->GetFullName(),
7669 42 : GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7670 21 : m_poParent(std::move(poParent)), m_apoDims(apoDims),
7671 105 : m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7672 : {
7673 21 : CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7674 21 : CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7675 21 : }
7676 :
7677 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7678 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7679 : const GDALExtendedDataType &bufferDataType,
7680 : void *pDstBuffer) const override;
7681 :
7682 : public:
7683 : static std::shared_ptr<GDALMDArray>
7684 : Create(const std::shared_ptr<GDALMDArray> &poParent,
7685 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7686 : GDALRIOResampleAlg resampleAlg,
7687 : const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7688 :
7689 42 : ~GDALMDArrayResampled()
7690 21 : {
7691 : // First close the warped VRT
7692 21 : m_poReprojectedDS.reset();
7693 21 : m_poParentDS.reset();
7694 42 : }
7695 :
7696 11 : bool IsWritable() const override
7697 : {
7698 11 : return false;
7699 : }
7700 :
7701 74 : const std::string &GetFilename() const override
7702 : {
7703 74 : return m_poParent->GetFilename();
7704 : }
7705 :
7706 : const std::vector<std::shared_ptr<GDALDimension>> &
7707 257 : GetDimensions() const override
7708 : {
7709 257 : return m_apoDims;
7710 : }
7711 :
7712 109 : const GDALExtendedDataType &GetDataType() const override
7713 : {
7714 109 : return m_dt;
7715 : }
7716 :
7717 21 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7718 : {
7719 21 : return m_poSRS;
7720 : }
7721 :
7722 12 : std::vector<GUInt64> GetBlockSize() const override
7723 : {
7724 12 : return m_anBlockSize;
7725 : }
7726 :
7727 : std::shared_ptr<GDALAttribute>
7728 1 : GetAttribute(const std::string &osName) const override
7729 : {
7730 1 : return m_poParent->GetAttribute(osName);
7731 : }
7732 :
7733 : std::vector<std::shared_ptr<GDALAttribute>>
7734 12 : GetAttributes(CSLConstList papszOptions = nullptr) const override
7735 : {
7736 12 : return m_poParent->GetAttributes(papszOptions);
7737 : }
7738 :
7739 1 : const std::string &GetUnit() const override
7740 : {
7741 1 : return m_poParent->GetUnit();
7742 : }
7743 :
7744 1 : const void *GetRawNoDataValue() const override
7745 : {
7746 1 : return m_poParent->GetRawNoDataValue();
7747 : }
7748 :
7749 1 : double GetOffset(bool *pbHasOffset,
7750 : GDALDataType *peStorageType) const override
7751 : {
7752 1 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
7753 : }
7754 :
7755 1 : double GetScale(bool *pbHasScale,
7756 : GDALDataType *peStorageType) const override
7757 : {
7758 1 : return m_poParent->GetScale(pbHasScale, peStorageType);
7759 : }
7760 : };
7761 :
7762 : /************************************************************************/
7763 : /* GDALMDArrayResampled::Create() */
7764 : /************************************************************************/
7765 :
7766 29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7767 : const std::shared_ptr<GDALMDArray> &poParent,
7768 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7769 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7770 : CSLConstList /* papszOptions */)
7771 : {
7772 29 : const char *pszResampleAlg = "nearest";
7773 29 : bool unsupported = false;
7774 29 : switch (resampleAlg)
7775 : {
7776 16 : case GRIORA_NearestNeighbour:
7777 16 : pszResampleAlg = "nearest";
7778 16 : break;
7779 2 : case GRIORA_Bilinear:
7780 2 : pszResampleAlg = "bilinear";
7781 2 : break;
7782 5 : case GRIORA_Cubic:
7783 5 : pszResampleAlg = "cubic";
7784 5 : break;
7785 1 : case GRIORA_CubicSpline:
7786 1 : pszResampleAlg = "cubicspline";
7787 1 : break;
7788 1 : case GRIORA_Lanczos:
7789 1 : pszResampleAlg = "lanczos";
7790 1 : break;
7791 1 : case GRIORA_Average:
7792 1 : pszResampleAlg = "average";
7793 1 : break;
7794 1 : case GRIORA_Mode:
7795 1 : pszResampleAlg = "mode";
7796 1 : break;
7797 1 : case GRIORA_Gauss:
7798 1 : unsupported = true;
7799 1 : break;
7800 0 : case GRIORA_RESERVED_START:
7801 0 : unsupported = true;
7802 0 : break;
7803 0 : case GRIORA_RESERVED_END:
7804 0 : unsupported = true;
7805 0 : break;
7806 1 : case GRIORA_RMS:
7807 1 : pszResampleAlg = "rms";
7808 1 : break;
7809 : }
7810 29 : if (unsupported)
7811 : {
7812 1 : CPLError(CE_Failure, CPLE_NotSupported,
7813 : "Unsupported resample method for GetResampled()");
7814 1 : return nullptr;
7815 : }
7816 :
7817 28 : if (poParent->GetDimensionCount() < 2)
7818 : {
7819 1 : CPLError(CE_Failure, CPLE_NotSupported,
7820 : "GetResampled() only supports 2 dimensions or more");
7821 1 : return nullptr;
7822 : }
7823 :
7824 27 : const auto &aoParentDims = poParent->GetDimensions();
7825 27 : if (apoNewDimsIn.size() != aoParentDims.size())
7826 : {
7827 2 : CPLError(CE_Failure, CPLE_AppDefined,
7828 : "GetResampled(): apoNewDims size should be the same as "
7829 : "GetDimensionCount()");
7830 2 : return nullptr;
7831 : }
7832 :
7833 50 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
7834 25 : apoNewDims.reserve(apoNewDimsIn.size());
7835 :
7836 50 : std::vector<GUInt64> anBlockSize;
7837 25 : anBlockSize.reserve(apoNewDimsIn.size());
7838 50 : const auto &anParentBlockSize = poParent->GetBlockSize();
7839 :
7840 50 : auto apoParentDims = poParent->GetDimensions();
7841 : // Special case for NASA EMIT datasets
7842 30 : const bool bYXBandOrder = (apoParentDims.size() == 3 &&
7843 7 : apoParentDims[0]->GetName() == "downtrack" &&
7844 32 : apoParentDims[1]->GetName() == "crosstrack" &&
7845 2 : apoParentDims[2]->GetName() == "bands");
7846 :
7847 : const size_t iYDimParent =
7848 25 : bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
7849 : const size_t iXDimParent =
7850 25 : bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
7851 :
7852 77 : for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
7853 : {
7854 53 : if (i == iYDimParent || i == iXDimParent)
7855 48 : continue;
7856 5 : if (apoNewDimsIn[i] == nullptr)
7857 : {
7858 3 : apoNewDims.emplace_back(aoParentDims[i]);
7859 : }
7860 3 : else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
7861 1 : apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
7862 : {
7863 1 : CPLError(CE_Failure, CPLE_AppDefined,
7864 : "GetResampled(): apoNewDims[%u] should be the same "
7865 : "as its parent",
7866 : i);
7867 1 : return nullptr;
7868 : }
7869 : else
7870 : {
7871 1 : apoNewDims.emplace_back(aoParentDims[i]);
7872 : }
7873 4 : anBlockSize.emplace_back(anParentBlockSize[i]);
7874 : }
7875 :
7876 : std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
7877 48 : new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
7878 :
7879 24 : double dfXStart = 0.0;
7880 24 : double dfXSpacing = 0.0;
7881 24 : bool gotXSpacing = false;
7882 48 : auto poNewDimX = apoNewDimsIn[iXDimParent];
7883 24 : if (poNewDimX)
7884 : {
7885 4 : if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
7886 : {
7887 0 : CPLError(CE_Failure, CPLE_NotSupported,
7888 : "Too big size for X dimension");
7889 0 : return nullptr;
7890 : }
7891 4 : auto var = poNewDimX->GetIndexingVariable();
7892 4 : if (var)
7893 : {
7894 2 : if (var->GetDimensionCount() != 1 ||
7895 2 : var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
7896 1 : !var->IsRegularlySpaced(dfXStart, dfXSpacing))
7897 : {
7898 0 : CPLError(CE_Failure, CPLE_NotSupported,
7899 : "New X dimension should be indexed by a regularly "
7900 : "spaced variable");
7901 0 : return nullptr;
7902 : }
7903 1 : gotXSpacing = true;
7904 : }
7905 : }
7906 :
7907 24 : double dfYStart = 0.0;
7908 24 : double dfYSpacing = 0.0;
7909 48 : auto poNewDimY = apoNewDimsIn[iYDimParent];
7910 24 : bool gotYSpacing = false;
7911 24 : if (poNewDimY)
7912 : {
7913 4 : if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
7914 : {
7915 0 : CPLError(CE_Failure, CPLE_NotSupported,
7916 : "Too big size for Y dimension");
7917 0 : return nullptr;
7918 : }
7919 4 : auto var = poNewDimY->GetIndexingVariable();
7920 4 : if (var)
7921 : {
7922 2 : if (var->GetDimensionCount() != 1 ||
7923 2 : var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
7924 1 : !var->IsRegularlySpaced(dfYStart, dfYSpacing))
7925 : {
7926 0 : CPLError(CE_Failure, CPLE_NotSupported,
7927 : "New Y dimension should be indexed by a regularly "
7928 : "spaced variable");
7929 0 : return nullptr;
7930 : }
7931 1 : gotYSpacing = true;
7932 : }
7933 : }
7934 :
7935 : // This limitation could probably be removed
7936 24 : if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
7937 : {
7938 0 : CPLError(CE_Failure, CPLE_NotSupported,
7939 : "Either none of new X or Y dimension should have an indexing "
7940 : "variable, or both should both should have one.");
7941 0 : return nullptr;
7942 : }
7943 :
7944 48 : std::string osDstWKT;
7945 24 : if (poTargetSRS)
7946 : {
7947 2 : char *pszDstWKT = nullptr;
7948 2 : if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
7949 : {
7950 0 : CPLFree(pszDstWKT);
7951 0 : return nullptr;
7952 : }
7953 2 : osDstWKT = pszDstWKT;
7954 2 : CPLFree(pszDstWKT);
7955 : }
7956 :
7957 : // Use coordinate variables for geolocation array
7958 48 : const auto apoCoordinateVars = poParent->GetCoordinateVariables();
7959 24 : bool useGeolocationArray = false;
7960 24 : if (apoCoordinateVars.size() >= 2)
7961 : {
7962 0 : std::shared_ptr<GDALMDArray> poLongVar;
7963 0 : std::shared_ptr<GDALMDArray> poLatVar;
7964 15 : for (const auto &poCoordVar : apoCoordinateVars)
7965 : {
7966 10 : const auto &osName = poCoordVar->GetName();
7967 30 : const auto poAttr = poCoordVar->GetAttribute("standard_name");
7968 20 : std::string osStandardName;
7969 12 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
7970 2 : poAttr->GetDimensionCount() == 0)
7971 : {
7972 2 : const char *pszStandardName = poAttr->ReadAsString();
7973 2 : if (pszStandardName)
7974 2 : osStandardName = pszStandardName;
7975 : }
7976 21 : if (osName == "lon" || osName == "longitude" ||
7977 21 : osName == "Longitude" || osStandardName == "longitude")
7978 : {
7979 5 : poLongVar = poCoordVar;
7980 : }
7981 6 : else if (osName == "lat" || osName == "latitude" ||
7982 6 : osName == "Latitude" || osStandardName == "latitude")
7983 : {
7984 5 : poLatVar = poCoordVar;
7985 : }
7986 : }
7987 5 : if (poLatVar != nullptr && poLongVar != nullptr)
7988 : {
7989 5 : const auto longDimCount = poLongVar->GetDimensionCount();
7990 5 : const auto &longDims = poLongVar->GetDimensions();
7991 5 : const auto latDimCount = poLatVar->GetDimensionCount();
7992 5 : const auto &latDims = poLatVar->GetDimensions();
7993 5 : const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
7994 5 : const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
7995 0 : if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
7996 5 : latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
7997 : {
7998 : // Geolocation arrays are 1D, and of consistent size with
7999 : // the variable
8000 0 : useGeolocationArray = true;
8001 : }
8002 1 : else if ((longDimCount == 2 ||
8003 6 : (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8004 10 : longDims[longDimCount - 2]->GetSize() == yDimSize &&
8005 10 : longDims[longDimCount - 1]->GetSize() == xDimSize &&
8006 1 : (latDimCount == 2 ||
8007 6 : (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8008 15 : latDims[latDimCount - 2]->GetSize() == yDimSize &&
8009 5 : latDims[latDimCount - 1]->GetSize() == xDimSize)
8010 :
8011 : {
8012 : // Geolocation arrays are 2D (or 3D with first dimension of
8013 : // size 1, as found in Sentinel 5P products), and of consistent
8014 : // size with the variable
8015 5 : useGeolocationArray = true;
8016 : }
8017 : else
8018 : {
8019 0 : CPLDebug(
8020 : "GDAL",
8021 : "Longitude and latitude coordinate variables found, "
8022 : "but their characteristics are not compatible of using "
8023 : "them as geolocation arrays");
8024 : }
8025 5 : if (useGeolocationArray)
8026 : {
8027 10 : CPLDebug("GDAL",
8028 : "Setting geolocation array from variables %s and %s",
8029 5 : poLongVar->GetName().c_str(),
8030 5 : poLatVar->GetName().c_str());
8031 : std::string osFilenameLong =
8032 5 : CPLSPrintf("/vsimem/%p/longitude.tif", poParent.get());
8033 : std::string osFilenameLat =
8034 5 : CPLSPrintf("/vsimem/%p/latitude.tif", poParent.get());
8035 : std::unique_ptr<GDALDataset> poTmpLongDS(
8036 : longDimCount == 1
8037 0 : ? poLongVar->AsClassicDataset(0, 0)
8038 20 : : poLongVar->AsClassicDataset(longDimCount - 1,
8039 15 : longDimCount - 2));
8040 5 : auto hTIFFLongDS = GDALTranslate(
8041 : osFilenameLong.c_str(),
8042 : GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8043 : std::unique_ptr<GDALDataset> poTmpLatDS(
8044 0 : latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8045 20 : : poLatVar->AsClassicDataset(
8046 15 : latDimCount - 1, latDimCount - 2));
8047 5 : auto hTIFFLatDS = GDALTranslate(
8048 : osFilenameLat.c_str(),
8049 : GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8050 5 : const bool bError =
8051 5 : (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8052 5 : GDALClose(hTIFFLongDS);
8053 5 : GDALClose(hTIFFLatDS);
8054 5 : if (bError)
8055 : {
8056 0 : VSIUnlink(osFilenameLong.c_str());
8057 0 : VSIUnlink(osFilenameLat.c_str());
8058 0 : return nullptr;
8059 : }
8060 :
8061 5 : poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8062 : }
8063 : }
8064 : else
8065 : {
8066 0 : CPLDebug("GDAL",
8067 : "Coordinate variables available for %s, but "
8068 : "longitude and/or latitude variables were not identified",
8069 0 : poParent->GetName().c_str());
8070 : }
8071 : }
8072 :
8073 : // Build gdalwarp arguments
8074 48 : CPLStringList aosArgv;
8075 :
8076 24 : aosArgv.AddString("-of");
8077 24 : aosArgv.AddString("VRT");
8078 :
8079 24 : aosArgv.AddString("-r");
8080 24 : aosArgv.AddString(pszResampleAlg);
8081 :
8082 24 : if (!osDstWKT.empty())
8083 : {
8084 2 : aosArgv.AddString("-t_srs");
8085 2 : aosArgv.AddString(osDstWKT.c_str());
8086 : }
8087 :
8088 24 : if (useGeolocationArray)
8089 5 : aosArgv.AddString("-geoloc");
8090 :
8091 24 : if (gotXSpacing && gotYSpacing)
8092 : {
8093 1 : const double dfXMin = dfXStart - dfXSpacing / 2;
8094 : const double dfXMax =
8095 1 : dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8096 1 : const double dfYMax = dfYStart - dfYSpacing / 2;
8097 : const double dfYMin =
8098 1 : dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8099 1 : aosArgv.AddString("-te");
8100 1 : aosArgv.AddString(CPLSPrintf("%.18g", dfXMin));
8101 1 : aosArgv.AddString(CPLSPrintf("%.18g", dfYMin));
8102 1 : aosArgv.AddString(CPLSPrintf("%.18g", dfXMax));
8103 1 : aosArgv.AddString(CPLSPrintf("%.18g", dfYMax));
8104 : }
8105 :
8106 24 : if (poNewDimX && poNewDimY)
8107 : {
8108 3 : aosArgv.AddString("-ts");
8109 : aosArgv.AddString(
8110 3 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8111 : aosArgv.AddString(
8112 3 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8113 : }
8114 21 : else if (poNewDimX)
8115 : {
8116 1 : aosArgv.AddString("-ts");
8117 : aosArgv.AddString(
8118 1 : CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8119 1 : aosArgv.AddString("0");
8120 : }
8121 20 : else if (poNewDimY)
8122 : {
8123 1 : aosArgv.AddString("-ts");
8124 1 : aosArgv.AddString("0");
8125 : aosArgv.AddString(
8126 1 : CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8127 : }
8128 :
8129 : // Create a warped VRT dataset
8130 : GDALWarpAppOptions *psOptions =
8131 24 : GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8132 24 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8133 : std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8134 48 : GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8135 24 : GDALWarpAppOptionsFree(psOptions);
8136 24 : if (poReprojectedDS == nullptr)
8137 3 : return nullptr;
8138 :
8139 : int nBlockXSize;
8140 : int nBlockYSize;
8141 21 : poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8142 21 : anBlockSize.emplace_back(nBlockYSize);
8143 21 : anBlockSize.emplace_back(nBlockXSize);
8144 :
8145 21 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8146 21 : CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8147 21 : CPLAssert(eErr == CE_None);
8148 21 : CPL_IGNORE_RET_VAL(eErr);
8149 :
8150 : auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8151 0 : std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8152 42 : poReprojectedDS->GetRasterYSize());
8153 : auto varY = GDALMDArrayRegularlySpaced::Create(
8154 63 : std::string(), poDimY->GetName(), poDimY,
8155 84 : adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8156 21 : poDimY->SetIndexingVariable(varY);
8157 :
8158 : auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8159 0 : std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8160 42 : poReprojectedDS->GetRasterXSize());
8161 : auto varX = GDALMDArrayRegularlySpaced::Create(
8162 63 : std::string(), poDimX->GetName(), poDimX,
8163 84 : adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8164 21 : poDimX->SetIndexingVariable(varX);
8165 :
8166 21 : apoNewDims.emplace_back(poDimY);
8167 21 : apoNewDims.emplace_back(poDimX);
8168 : auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8169 42 : new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8170 21 : newAr->SetSelf(newAr);
8171 21 : if (poTargetSRS)
8172 : {
8173 2 : newAr->m_poSRS.reset(poTargetSRS->Clone());
8174 : }
8175 : else
8176 : {
8177 19 : newAr->m_poSRS = poParent->GetSpatialRef();
8178 : }
8179 21 : newAr->m_poVarX = varX;
8180 21 : newAr->m_poVarY = varY;
8181 21 : newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8182 21 : newAr->m_poParentDS = std::move(poParentDS);
8183 :
8184 : // If the input array is y,x,band ordered, the above newAr is
8185 : // actually band,y,x ordered as it is more convenient for
8186 : // GDALMDArrayResampled::IRead() implementation. But transpose that
8187 : // array to the order of the input array
8188 21 : if (bYXBandOrder)
8189 4 : return newAr->Transpose(std::vector<int>{1, 2, 0});
8190 :
8191 19 : return newAr;
8192 : }
8193 :
8194 : /************************************************************************/
8195 : /* GDALMDArrayResampled::IRead() */
8196 : /************************************************************************/
8197 :
8198 29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8199 : const size_t *count, const GInt64 *arrayStep,
8200 : const GPtrDiff_t *bufferStride,
8201 : const GDALExtendedDataType &bufferDataType,
8202 : void *pDstBuffer) const
8203 : {
8204 29 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8205 0 : return false;
8206 :
8207 : struct Stack
8208 : {
8209 : size_t nIters = 0;
8210 : GByte *dst_ptr = nullptr;
8211 : GPtrDiff_t dst_inc_offset = 0;
8212 : };
8213 :
8214 29 : const auto nDims = GetDimensionCount();
8215 58 : std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
8216 29 : const size_t nBufferDTSize = bufferDataType.GetSize();
8217 92 : for (size_t i = 0; i < nDims; i++)
8218 : {
8219 63 : stack[i].dst_inc_offset =
8220 63 : static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8221 : }
8222 29 : stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8223 :
8224 29 : size_t dimIdx = 0;
8225 29 : const size_t iDimY = nDims - 2;
8226 29 : const size_t iDimX = nDims - 1;
8227 : // Use an array to avoid a false positive warning from CLang Static
8228 : // Analyzer about flushCaches being never read
8229 29 : bool flushCaches[] = {false};
8230 : const bool bYXBandOrder =
8231 29 : m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8232 :
8233 38 : lbl_next_depth:
8234 38 : if (dimIdx == iDimY)
8235 : {
8236 33 : if (flushCaches[0])
8237 : {
8238 5 : flushCaches[0] = false;
8239 : // When changing of 2D slice, flush GDAL 2D buffers
8240 5 : m_poParentDS->FlushCache(false);
8241 5 : m_poReprojectedDS->FlushCache(false);
8242 : }
8243 :
8244 33 : if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8245 : GF_Read, iDimX, iDimY, arrayStartIdx, count,
8246 : arrayStep, bufferStride, bufferDataType,
8247 33 : stack[dimIdx].dst_ptr))
8248 : {
8249 0 : return false;
8250 : }
8251 : }
8252 : else
8253 : {
8254 5 : stack[dimIdx].nIters = count[dimIdx];
8255 5 : if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8256 5 : arrayStartIdx[dimIdx])
8257 : {
8258 1 : flushCaches[0] = true;
8259 : }
8260 5 : m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8261 5 : arrayStartIdx[dimIdx];
8262 : while (true)
8263 : {
8264 9 : dimIdx++;
8265 9 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8266 9 : goto lbl_next_depth;
8267 9 : lbl_return_to_caller:
8268 9 : dimIdx--;
8269 9 : if ((--stack[dimIdx].nIters) == 0)
8270 5 : break;
8271 4 : flushCaches[0] = true;
8272 4 : ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8273 4 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8274 : }
8275 : }
8276 38 : if (dimIdx > 0)
8277 9 : goto lbl_return_to_caller;
8278 :
8279 29 : return true;
8280 : }
8281 :
8282 : /************************************************************************/
8283 : /* GetResampled() */
8284 : /************************************************************************/
8285 :
8286 : /** Return an array that is a resampled / reprojected view of the current array
8287 : *
8288 : * This is the same as the C function GDALMDArrayGetResampled().
8289 : *
8290 : * Currently this method can only resample along the last 2 dimensions, unless
8291 : * orthorectifying a NASA EMIT dataset.
8292 : *
8293 : * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8294 : * geometry lookup table (GLT) is used for fast orthorectification.
8295 : *
8296 : * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8297 : * apoNewDims[i] can be NULL to let the method automatically
8298 : * determine it.
8299 : * @param resampleAlg Resampling algorithm
8300 : * @param poTargetSRS Target SRS, or nullptr
8301 : * @param papszOptions NULL-terminated list of options, or NULL.
8302 : *
8303 : * @return a new array, that holds a reference to the original one, and thus is
8304 : * a view of it (not a copy), or nullptr in case of error.
8305 : *
8306 : * @since 3.4
8307 : */
8308 34 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8309 : const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8310 : GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8311 : CSLConstList papszOptions) const
8312 : {
8313 68 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8314 34 : if (!self)
8315 : {
8316 0 : CPLError(CE_Failure, CPLE_AppDefined,
8317 : "Driver implementation issue: m_pSelf not set !");
8318 0 : return nullptr;
8319 : }
8320 34 : if (GetDataType().GetClass() != GEDTC_NUMERIC)
8321 : {
8322 0 : CPLError(CE_Failure, CPLE_AppDefined,
8323 : "GetResampled() only supports numeric data type");
8324 0 : return nullptr;
8325 : }
8326 :
8327 : // Special case for NASA EMIT datasets
8328 68 : auto apoDims = GetDimensions();
8329 32 : if (poTargetSRS == nullptr &&
8330 47 : ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8331 12 : apoDims[1]->GetName() == "crosstrack" &&
8332 6 : apoDims[2]->GetName() == "bands" &&
8333 40 : (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8334 1 : apoNewDims ==
8335 38 : std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8336 30 : apoDims[2]})) ||
8337 51 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8338 3 : apoDims[1]->GetName() == "crosstrack" &&
8339 69 : apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8340 9 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
8341 : "EMIT_ORTHORECTIFICATION", "YES")))
8342 : {
8343 5 : auto poRootGroup = GetRootGroup();
8344 5 : if (poRootGroup)
8345 : {
8346 10 : auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8347 10 : auto poLocationGroup = poRootGroup->OpenGroup("location");
8348 5 : if (poAttrGeotransform &&
8349 5 : poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8350 5 : poAttrGeotransform->GetDimensionCount() == 1 &&
8351 15 : poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8352 5 : poLocationGroup)
8353 : {
8354 10 : auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8355 10 : auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8356 15 : if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8357 10 : poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8358 10 : poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8359 15 : poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8360 15 : poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8361 5 : poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8362 : {
8363 : return CreateGLTOrthorectified(
8364 : self, poGLT_X, poGLT_Y,
8365 : /* nGLTIndexOffset = */ -1,
8366 10 : poAttrGeotransform->ReadAsDoubleArray());
8367 : }
8368 : }
8369 : }
8370 : }
8371 :
8372 29 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8373 : "EMIT_ORTHORECTIFICATION", "NO")))
8374 : {
8375 0 : CPLError(CE_Failure, CPLE_AppDefined,
8376 : "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8377 : "parameters are not compatible with it");
8378 0 : return nullptr;
8379 : }
8380 :
8381 : return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8382 29 : poTargetSRS, papszOptions);
8383 : }
8384 :
8385 : /************************************************************************/
8386 : /* GDALDatasetFromArray() */
8387 : /************************************************************************/
8388 :
8389 : class GDALDatasetFromArray;
8390 :
8391 : namespace
8392 : {
8393 : struct MetadataItem
8394 : {
8395 : std::shared_ptr<GDALMDArray> poArray{};
8396 : std::string osName{};
8397 : std::string osDefinition{};
8398 : bool bDefinitionUsesPctForG = false;
8399 : };
8400 : } // namespace
8401 :
8402 : class GDALRasterBandFromArray final : public GDALPamRasterBand
8403 : {
8404 : std::vector<GUInt64> m_anOffset{};
8405 : std::vector<size_t> m_anCount{};
8406 : std::vector<GPtrDiff_t> m_anStride{};
8407 :
8408 : protected:
8409 : CPLErr IReadBlock(int, int, void *) override;
8410 : CPLErr IWriteBlock(int, int, void *) override;
8411 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8412 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
8413 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8414 : GSpacing nLineSpaceBuf,
8415 : GDALRasterIOExtraArg *psExtraArg) override;
8416 :
8417 : public:
8418 : explicit GDALRasterBandFromArray(
8419 : GDALDatasetFromArray *poDSIn,
8420 : const std::vector<GUInt64> &anOtherDimCoord,
8421 : const std::vector<std::vector<MetadataItem>>
8422 : &aoBandParameterMetadataItems,
8423 : double dfDelay, time_t nStartTime, bool &bHasWarned);
8424 :
8425 : double GetNoDataValue(int *pbHasNoData) override;
8426 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8427 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8428 : double GetOffset(int *pbHasOffset) override;
8429 : double GetScale(int *pbHasScale) override;
8430 : const char *GetUnitType() override;
8431 : GDALColorInterp GetColorInterpretation() override;
8432 : };
8433 :
8434 : class GDALDatasetFromArray final : public GDALPamDataset
8435 : {
8436 : friend class GDALRasterBandFromArray;
8437 :
8438 : std::shared_ptr<GDALMDArray> m_poArray;
8439 : size_t m_iXDim;
8440 : size_t m_iYDim;
8441 : double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8442 : bool m_bHasGT = false;
8443 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8444 : GDALMultiDomainMetadata m_oMDD{};
8445 : std::string m_osOvrFilename{};
8446 :
8447 : public:
8448 159 : GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8449 : size_t iXDim, size_t iYDim)
8450 159 : : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8451 : {
8452 : // Initialize an overview filename from the filename of the array
8453 : // and its name.
8454 159 : const std::string &osFilename = m_poArray->GetFilename();
8455 159 : if (!osFilename.empty())
8456 : {
8457 142 : m_osOvrFilename = osFilename;
8458 142 : m_osOvrFilename += '.';
8459 4258 : for (char ch : m_poArray->GetName())
8460 : {
8461 4116 : if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8462 3659 : (ch >= 'a' && ch <= 'z') || ch == '_')
8463 : {
8464 3296 : m_osOvrFilename += ch;
8465 : }
8466 : else
8467 : {
8468 820 : m_osOvrFilename += '_';
8469 : }
8470 : }
8471 142 : m_osOvrFilename += ".ovr";
8472 142 : oOvManager.Initialize(this);
8473 : }
8474 159 : }
8475 :
8476 : static GDALDatasetFromArray *
8477 : Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8478 : size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8479 : CSLConstList papszOptions);
8480 :
8481 318 : ~GDALDatasetFromArray()
8482 159 : {
8483 159 : GDALDatasetFromArray::Close();
8484 318 : }
8485 :
8486 269 : CPLErr Close() override
8487 : {
8488 269 : CPLErr eErr = CE_None;
8489 269 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
8490 : {
8491 269 : if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8492 : CE_None)
8493 0 : eErr = CE_Failure;
8494 269 : m_poArray.reset();
8495 : }
8496 269 : return eErr;
8497 : }
8498 :
8499 49 : CPLErr GetGeoTransform(double *padfGeoTransform) override
8500 : {
8501 49 : memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8502 49 : return m_bHasGT ? CE_None : CE_Failure;
8503 : }
8504 :
8505 53 : const OGRSpatialReference *GetSpatialRef() const override
8506 : {
8507 53 : if (m_poArray->GetDimensionCount() < 2)
8508 3 : return nullptr;
8509 50 : m_poSRS = m_poArray->GetSpatialRef();
8510 50 : if (m_poSRS)
8511 : {
8512 16 : m_poSRS.reset(m_poSRS->Clone());
8513 32 : auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8514 48 : for (auto &m : axisMapping)
8515 : {
8516 32 : if (m == static_cast<int>(m_iXDim) + 1)
8517 16 : m = 1;
8518 16 : else if (m == static_cast<int>(m_iYDim) + 1)
8519 16 : m = 2;
8520 : }
8521 16 : m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8522 : }
8523 50 : return m_poSRS.get();
8524 : }
8525 :
8526 4 : CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8527 : {
8528 4 : return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8529 : }
8530 :
8531 144 : char **GetMetadata(const char *pszDomain) override
8532 : {
8533 144 : return m_oMDD.GetMetadata(pszDomain);
8534 : }
8535 :
8536 200 : const char *GetMetadataItem(const char *pszName,
8537 : const char *pszDomain) override
8538 : {
8539 370 : if (!m_osOvrFilename.empty() && pszName &&
8540 382 : EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8541 12 : EQUAL(pszDomain, "OVERVIEWS"))
8542 : {
8543 12 : return m_osOvrFilename.c_str();
8544 : }
8545 188 : return m_oMDD.GetMetadataItem(pszName, pszDomain);
8546 : }
8547 : };
8548 :
8549 : /************************************************************************/
8550 : /* GDALRasterBandFromArray() */
8551 : /************************************************************************/
8552 :
8553 215 : GDALRasterBandFromArray::GDALRasterBandFromArray(
8554 : GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8555 : const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8556 215 : double dfDelay, time_t nStartTime, bool &bHasWarned)
8557 : {
8558 215 : const auto &poArray(poDSIn->m_poArray);
8559 215 : const auto &dims(poArray->GetDimensions());
8560 215 : const auto nDimCount(dims.size());
8561 430 : const auto blockSize(poArray->GetBlockSize());
8562 204 : nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8563 419 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8564 123 : blockSize[poDSIn->m_iYDim]))
8565 : : 1;
8566 215 : nBlockXSize = blockSize[poDSIn->m_iXDim]
8567 134 : ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8568 134 : blockSize[poDSIn->m_iXDim]))
8569 215 : : poDSIn->GetRasterXSize();
8570 215 : eDataType = poArray->GetDataType().GetNumericDataType();
8571 215 : eAccess = poDSIn->eAccess;
8572 215 : m_anOffset.resize(nDimCount);
8573 215 : m_anCount.resize(nDimCount, 1);
8574 215 : m_anStride.resize(nDimCount);
8575 731 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
8576 : {
8577 516 : if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8578 : {
8579 194 : std::string dimName(dims[i]->GetName());
8580 97 : GUInt64 nIndex = anOtherDimCoord[j];
8581 : // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8582 : // subsetted dimensions as generated by GetView()
8583 97 : if (STARTS_WITH(dimName.c_str(), "subset_"))
8584 : {
8585 : CPLStringList aosTokens(
8586 12 : CSLTokenizeString2(dimName.c_str(), "_", 0));
8587 6 : if (aosTokens.size() == 5)
8588 : {
8589 6 : dimName = aosTokens[1];
8590 18 : const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8591 6 : aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8592 6 : const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8593 6 : nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8594 0 : : nStartDim - (nIndex * -nIncrDim);
8595 : }
8596 : }
8597 97 : if (nDimCount != 3 || dimName != "Band")
8598 : {
8599 44 : SetMetadataItem(
8600 : CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8601 : CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8602 : }
8603 :
8604 97 : auto indexingVar = dims[i]->GetIndexingVariable();
8605 :
8606 : // If the indexing variable is also listed in band parameter arrays,
8607 : // then don't use our default formatting
8608 97 : if (indexingVar)
8609 : {
8610 34 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8611 : {
8612 12 : if (oItem.poArray->GetFullName() ==
8613 12 : indexingVar->GetFullName())
8614 : {
8615 12 : indexingVar.reset();
8616 12 : break;
8617 : }
8618 : }
8619 : }
8620 :
8621 119 : if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8622 22 : indexingVar->GetDimensions()[0]->GetSize() ==
8623 22 : dims[i]->GetSize())
8624 : {
8625 22 : if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8626 : {
8627 0 : if (!bHasWarned)
8628 : {
8629 0 : CPLError(
8630 : CE_Warning, CPLE_AppDefined,
8631 : "Maximum delay to load band metadata from "
8632 : "dimension indexing variables has expired. "
8633 : "Increase the value of the "
8634 : "LOAD_EXTRA_DIM_METADATA_DELAY "
8635 : "option of GDALMDArray::AsClassicDataset() "
8636 : "(also accessible as the "
8637 : "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8638 : "configuration option), "
8639 : "or set it to 'unlimited' for unlimited delay. ");
8640 0 : bHasWarned = true;
8641 : }
8642 : }
8643 : else
8644 : {
8645 22 : size_t nCount = 1;
8646 22 : const auto &dt(indexingVar->GetDataType());
8647 44 : std::vector<GByte> abyTmp(dt.GetSize());
8648 44 : if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8649 22 : nullptr, nullptr, dt, &abyTmp[0]))
8650 : {
8651 22 : char *pszTmp = nullptr;
8652 22 : GDALExtendedDataType::CopyValue(
8653 22 : &abyTmp[0], dt, &pszTmp,
8654 44 : GDALExtendedDataType::CreateString());
8655 22 : if (pszTmp)
8656 : {
8657 22 : SetMetadataItem(
8658 : CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8659 : pszTmp);
8660 22 : CPLFree(pszTmp);
8661 : }
8662 :
8663 22 : const auto &unit(indexingVar->GetUnit());
8664 22 : if (!unit.empty())
8665 : {
8666 12 : SetMetadataItem(
8667 : CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8668 : unit.c_str());
8669 : }
8670 : }
8671 : }
8672 : }
8673 :
8674 113 : for (const auto &oItem : aoBandParameterMetadataItems[j])
8675 : {
8676 32 : CPLString osVal;
8677 :
8678 16 : size_t nCount = 1;
8679 16 : const auto &dt(oItem.poArray->GetDataType());
8680 16 : if (oItem.bDefinitionUsesPctForG)
8681 : {
8682 : // There is one and only one %[x][.y]f|g in osDefinition
8683 12 : std::vector<GByte> abyTmp(dt.GetSize());
8684 12 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8685 6 : nullptr, nullptr, dt, &abyTmp[0]))
8686 : {
8687 6 : double dfVal = 0;
8688 6 : GDALExtendedDataType::CopyValue(
8689 6 : &abyTmp[0], dt, &dfVal,
8690 12 : GDALExtendedDataType::Create(GDT_Float64));
8691 6 : osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8692 : }
8693 : }
8694 : else
8695 : {
8696 : // There should be zero or one %s in osDefinition
8697 10 : char *pszValue = nullptr;
8698 10 : if (dt.GetClass() == GEDTC_STRING)
8699 : {
8700 4 : CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8701 2 : &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8702 : dt, &pszValue));
8703 : }
8704 : else
8705 : {
8706 16 : std::vector<GByte> abyTmp(dt.GetSize());
8707 16 : if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8708 : nullptr, nullptr, dt,
8709 8 : &abyTmp[0]))
8710 : {
8711 8 : GDALExtendedDataType::CopyValue(
8712 8 : &abyTmp[0], dt, &pszValue,
8713 16 : GDALExtendedDataType::CreateString());
8714 : }
8715 : }
8716 :
8717 10 : if (pszValue)
8718 : {
8719 10 : osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8720 10 : CPLFree(pszValue);
8721 : }
8722 : }
8723 16 : if (!osVal.empty())
8724 16 : SetMetadataItem(oItem.osName.c_str(), osVal);
8725 : }
8726 :
8727 97 : m_anOffset[i] = anOtherDimCoord[j];
8728 97 : j++;
8729 : }
8730 : }
8731 215 : }
8732 :
8733 : /************************************************************************/
8734 : /* GetNoDataValue() */
8735 : /************************************************************************/
8736 :
8737 86 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
8738 : {
8739 86 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8740 86 : const auto &poArray(l_poDS->m_poArray);
8741 86 : bool bHasNodata = false;
8742 86 : const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
8743 86 : if (pbHasNoData)
8744 76 : *pbHasNoData = bHasNodata;
8745 86 : return res;
8746 : }
8747 :
8748 : /************************************************************************/
8749 : /* GetNoDataValueAsInt64() */
8750 : /************************************************************************/
8751 :
8752 1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
8753 : {
8754 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8755 1 : const auto &poArray(l_poDS->m_poArray);
8756 1 : bool bHasNodata = false;
8757 1 : const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
8758 1 : if (pbHasNoData)
8759 1 : *pbHasNoData = bHasNodata;
8760 1 : return nodata;
8761 : }
8762 :
8763 : /************************************************************************/
8764 : /* GetNoDataValueAsUInt64() */
8765 : /************************************************************************/
8766 :
8767 1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
8768 : {
8769 1 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8770 1 : const auto &poArray(l_poDS->m_poArray);
8771 1 : bool bHasNodata = false;
8772 1 : const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
8773 1 : if (pbHasNoData)
8774 1 : *pbHasNoData = bHasNodata;
8775 1 : return nodata;
8776 : }
8777 :
8778 : /************************************************************************/
8779 : /* GetOffset() */
8780 : /************************************************************************/
8781 :
8782 29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
8783 : {
8784 29 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8785 29 : const auto &poArray(l_poDS->m_poArray);
8786 29 : bool bHasValue = false;
8787 29 : double dfRes = poArray->GetOffset(&bHasValue);
8788 29 : if (pbHasOffset)
8789 17 : *pbHasOffset = bHasValue;
8790 29 : return dfRes;
8791 : }
8792 :
8793 : /************************************************************************/
8794 : /* GetUnitType() */
8795 : /************************************************************************/
8796 :
8797 36 : const char *GDALRasterBandFromArray::GetUnitType()
8798 : {
8799 36 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8800 36 : const auto &poArray(l_poDS->m_poArray);
8801 36 : return poArray->GetUnit().c_str();
8802 : }
8803 :
8804 : /************************************************************************/
8805 : /* GetScale() */
8806 : /************************************************************************/
8807 :
8808 27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
8809 : {
8810 27 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8811 27 : const auto &poArray(l_poDS->m_poArray);
8812 27 : bool bHasValue = false;
8813 27 : double dfRes = poArray->GetScale(&bHasValue);
8814 27 : if (pbHasScale)
8815 15 : *pbHasScale = bHasValue;
8816 27 : return dfRes;
8817 : }
8818 :
8819 : /************************************************************************/
8820 : /* IReadBlock() */
8821 : /************************************************************************/
8822 :
8823 64 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
8824 : void *pImage)
8825 : {
8826 64 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
8827 64 : const int nXOff = nBlockXOff * nBlockXSize;
8828 64 : const int nYOff = nBlockYOff * nBlockYSize;
8829 64 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
8830 64 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
8831 : GDALRasterIOExtraArg sExtraArg;
8832 64 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
8833 128 : return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
8834 : nReqXSize, nReqYSize, eDataType, nDTSize,
8835 128 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
8836 : }
8837 :
8838 : /************************************************************************/
8839 : /* IWriteBlock() */
8840 : /************************************************************************/
8841 :
8842 0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
8843 : void *pImage)
8844 : {
8845 0 : const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
8846 0 : const int nXOff = nBlockXOff * nBlockXSize;
8847 0 : const int nYOff = nBlockYOff * nBlockYSize;
8848 0 : const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
8849 0 : const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
8850 : GDALRasterIOExtraArg sExtraArg;
8851 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
8852 0 : return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
8853 : nReqXSize, nReqYSize, eDataType, nDTSize,
8854 0 : static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
8855 : }
8856 :
8857 : /************************************************************************/
8858 : /* IRasterIO() */
8859 : /************************************************************************/
8860 :
8861 308 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
8862 : int nYOff, int nXSize, int nYSize,
8863 : void *pData, int nBufXSize,
8864 : int nBufYSize, GDALDataType eBufType,
8865 : GSpacing nPixelSpaceBuf,
8866 : GSpacing nLineSpaceBuf,
8867 : GDALRasterIOExtraArg *psExtraArg)
8868 : {
8869 308 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8870 308 : const auto &poArray(l_poDS->m_poArray);
8871 308 : const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
8872 308 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
8873 308 : (nPixelSpaceBuf % nBufferDTSize) == 0 &&
8874 308 : (nLineSpaceBuf % nBufferDTSize) == 0)
8875 : {
8876 308 : m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
8877 308 : m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
8878 616 : m_anStride[l_poDS->m_iXDim] =
8879 308 : static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
8880 308 : if (poArray->GetDimensionCount() >= 2)
8881 : {
8882 299 : m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
8883 299 : m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
8884 299 : m_anStride[l_poDS->m_iYDim] =
8885 299 : static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
8886 : }
8887 308 : if (eRWFlag == GF_Read)
8888 : {
8889 608 : return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
8890 304 : m_anStride.data(),
8891 608 : GDALExtendedDataType::Create(eBufType), pData)
8892 304 : ? CE_None
8893 304 : : CE_Failure;
8894 : }
8895 : else
8896 : {
8897 8 : return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
8898 4 : m_anStride.data(),
8899 8 : GDALExtendedDataType::Create(eBufType), pData)
8900 4 : ? CE_None
8901 4 : : CE_Failure;
8902 : }
8903 : }
8904 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
8905 : pData, nBufXSize, nBufYSize, eBufType,
8906 0 : nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
8907 : }
8908 :
8909 : /************************************************************************/
8910 : /* GetColorInterpretation() */
8911 : /************************************************************************/
8912 :
8913 44 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
8914 : {
8915 44 : auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
8916 44 : const auto &poArray(l_poDS->m_poArray);
8917 132 : auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
8918 44 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
8919 : {
8920 6 : bool bOK = false;
8921 6 : GUInt64 nStartIndex = 0;
8922 6 : if (poArray->GetDimensionCount() == 2 &&
8923 0 : poAttr->GetDimensionCount() == 0)
8924 : {
8925 0 : bOK = true;
8926 : }
8927 6 : else if (poArray->GetDimensionCount() == 3)
8928 : {
8929 6 : uint64_t nExtraDimSamples = 1;
8930 6 : const auto &apoDims = poArray->GetDimensions();
8931 24 : for (size_t i = 0; i < apoDims.size(); ++i)
8932 : {
8933 18 : if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
8934 6 : nExtraDimSamples *= apoDims[i]->GetSize();
8935 : }
8936 6 : if (poAttr->GetDimensionsSize() ==
8937 12 : std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
8938 : {
8939 6 : bOK = true;
8940 : }
8941 6 : nStartIndex = nBand - 1;
8942 : }
8943 6 : if (bOK)
8944 : {
8945 6 : const auto oStringDT = GDALExtendedDataType::CreateString();
8946 6 : const size_t nCount = 1;
8947 6 : const GInt64 arrayStep = 1;
8948 6 : const GPtrDiff_t bufferStride = 1;
8949 6 : char *pszValue = nullptr;
8950 6 : poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
8951 6 : oStringDT, &pszValue);
8952 6 : if (pszValue)
8953 : {
8954 : const auto eColorInterp =
8955 6 : GDALGetColorInterpretationByName(pszValue);
8956 6 : CPLFree(pszValue);
8957 6 : return eColorInterp;
8958 : }
8959 : }
8960 : }
8961 38 : return GCI_Undefined;
8962 : }
8963 :
8964 : /************************************************************************/
8965 : /* GDALDatasetFromArray::Create() */
8966 : /************************************************************************/
8967 :
8968 182 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
8969 : const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
8970 : const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
8971 :
8972 : {
8973 182 : const auto nDimCount(array->GetDimensionCount());
8974 182 : if (nDimCount == 0)
8975 : {
8976 1 : CPLError(CE_Failure, CPLE_NotSupported,
8977 : "Unsupported number of dimensions");
8978 1 : return nullptr;
8979 : }
8980 361 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
8981 180 : array->GetDataType().GetNumericDataType() == GDT_Unknown)
8982 : {
8983 1 : CPLError(CE_Failure, CPLE_NotSupported,
8984 : "Only arrays with numeric data types "
8985 : "can be exposed as classic GDALDataset");
8986 1 : return nullptr;
8987 : }
8988 180 : if (iXDim >= nDimCount ||
8989 166 : (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
8990 : {
8991 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
8992 6 : return nullptr;
8993 : }
8994 174 : GUInt64 nTotalBands = 1;
8995 174 : const auto &dims(array->GetDimensions());
8996 559 : for (size_t i = 0; i < nDimCount; ++i)
8997 : {
8998 386 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
8999 : {
9000 51 : if (dims[i]->GetSize() > 65536 / nTotalBands)
9001 : {
9002 1 : CPLError(CE_Failure, CPLE_AppDefined,
9003 : "Too many bands. Operate on a sliced view");
9004 1 : return nullptr;
9005 : }
9006 50 : nTotalBands *= dims[i]->GetSize();
9007 : }
9008 : }
9009 :
9010 : const char *pszBandMetadata =
9011 173 : CSLFetchNameValue(papszOptions, "BAND_METADATA");
9012 173 : const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9013 : std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9014 346 : nNewDimCount);
9015 :
9016 173 : if (pszBandMetadata)
9017 : {
9018 21 : if (!poRootGroup)
9019 : {
9020 1 : CPLError(CE_Failure, CPLE_AppDefined,
9021 : "Root group should be provided when BAND_METADATA is set");
9022 14 : return nullptr;
9023 : }
9024 20 : CPLJSONDocument oDoc;
9025 20 : if (!oDoc.LoadMemory(pszBandMetadata))
9026 : {
9027 1 : CPLError(CE_Failure, CPLE_AppDefined,
9028 : "Invalid JSON content for BAND_METADATA");
9029 1 : return nullptr;
9030 : }
9031 19 : auto oRoot = oDoc.GetRoot();
9032 19 : if (oRoot.GetType() != CPLJSONObject::Type::Array)
9033 : {
9034 1 : CPLError(CE_Failure, CPLE_AppDefined,
9035 : "Value of BAND_METADATA should be an array");
9036 1 : return nullptr;
9037 : }
9038 :
9039 18 : std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9040 72 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9041 : {
9042 54 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9043 : {
9044 18 : oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9045 18 : ++j;
9046 : }
9047 : }
9048 :
9049 18 : auto oArray = oRoot.ToArray();
9050 26 : for (int j = 0; j < oArray.Size(); ++j)
9051 : {
9052 19 : const auto oJsonItem = oArray[j];
9053 19 : MetadataItem oItem;
9054 :
9055 38 : auto osBandArrayFullname = oJsonItem.GetString("array");
9056 19 : if (osBandArrayFullname.empty())
9057 : {
9058 1 : CPLError(CE_Failure, CPLE_AppDefined,
9059 : "BAND_METADATA[%d][\"array\"] is missing", j);
9060 1 : return nullptr;
9061 : }
9062 : oItem.poArray =
9063 18 : poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9064 18 : if (!oItem.poArray)
9065 : {
9066 1 : CPLError(CE_Failure, CPLE_AppDefined,
9067 : "Array %s cannot be found",
9068 : osBandArrayFullname.c_str());
9069 1 : return nullptr;
9070 : }
9071 17 : if (oItem.poArray->GetDimensionCount() != 1)
9072 : {
9073 1 : CPLError(CE_Failure, CPLE_AppDefined,
9074 : "Array %s is not a 1D array",
9075 : osBandArrayFullname.c_str());
9076 1 : return nullptr;
9077 : }
9078 : const auto &osAuxArrayDimName =
9079 16 : oItem.poArray->GetDimensions()[0]->GetName();
9080 16 : auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9081 16 : if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9082 : {
9083 1 : CPLError(CE_Failure, CPLE_AppDefined,
9084 : "Dimension %s of array %s is not a non-X/Y dimension "
9085 : "of array %s",
9086 : osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9087 1 : array->GetName().c_str());
9088 1 : return nullptr;
9089 : }
9090 15 : const size_t iExtraDimIdx = oIter->second;
9091 15 : CPLAssert(iExtraDimIdx < nNewDimCount);
9092 :
9093 15 : oItem.osName = oJsonItem.GetString("item_name");
9094 15 : if (oItem.osName.empty())
9095 : {
9096 1 : CPLError(CE_Failure, CPLE_AppDefined,
9097 : "BAND_METADATA[%d][\"item_name\"] is missing", j);
9098 1 : return nullptr;
9099 : }
9100 :
9101 28 : const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9102 :
9103 : // Check correctness of definition
9104 14 : bool bFirstNumericFormatter = true;
9105 14 : std::string osModDefinition;
9106 14 : bool bDefinitionUsesPctForG = false;
9107 72 : for (size_t k = 0; k < osDefinition.size(); ++k)
9108 : {
9109 64 : if (osDefinition[k] == '%')
9110 : {
9111 13 : osModDefinition += osDefinition[k];
9112 13 : if (k + 1 == osDefinition.size())
9113 : {
9114 1 : CPLError(CE_Failure, CPLE_AppDefined,
9115 : "Value of "
9116 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9117 : "%s is invalid at offset %d",
9118 : osAuxArrayDimName.c_str(), j,
9119 : osDefinition.c_str(), int(k));
9120 1 : return nullptr;
9121 : }
9122 12 : ++k;
9123 12 : if (osDefinition[k] == '%')
9124 : {
9125 1 : osModDefinition += osDefinition[k];
9126 1 : continue;
9127 : }
9128 11 : if (!bFirstNumericFormatter)
9129 : {
9130 1 : CPLError(
9131 : CE_Failure, CPLE_AppDefined,
9132 : "Value of "
9133 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
9134 : "invalid at offset %d: %%[x][.y]f|g or %%s "
9135 : "formatters should be specified at most once",
9136 : osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
9137 : int(k));
9138 1 : return nullptr;
9139 : }
9140 10 : bFirstNumericFormatter = false;
9141 13 : for (; k < osDefinition.size(); ++k)
9142 : {
9143 13 : osModDefinition += osDefinition[k];
9144 26 : if (!((osDefinition[k] >= '0' &&
9145 12 : osDefinition[k] <= '9') ||
9146 11 : osDefinition[k] == '.'))
9147 10 : break;
9148 : }
9149 20 : if (k == osDefinition.size() ||
9150 10 : (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9151 5 : osDefinition[k] != 's'))
9152 : {
9153 1 : CPLError(CE_Failure, CPLE_AppDefined,
9154 : "Value of "
9155 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9156 : "%s is invalid at offset %d: only "
9157 : "%%[x][.y]f|g or %%s formatters are accepted",
9158 : osAuxArrayDimName.c_str(), j,
9159 : osDefinition.c_str(), int(k));
9160 1 : return nullptr;
9161 : }
9162 9 : bDefinitionUsesPctForG =
9163 9 : (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9164 9 : if (bDefinitionUsesPctForG)
9165 : {
9166 5 : if (oItem.poArray->GetDataType().GetClass() !=
9167 : GEDTC_NUMERIC)
9168 : {
9169 1 : CPLError(CE_Failure, CPLE_AppDefined,
9170 : "Data type of %s array is not numeric",
9171 : osAuxArrayDimName.c_str());
9172 1 : return nullptr;
9173 : }
9174 : }
9175 : }
9176 56 : else if (osDefinition[k] == '$' &&
9177 56 : k + 1 < osDefinition.size() &&
9178 5 : osDefinition[k + 1] == '{')
9179 : {
9180 5 : const auto nPos = osDefinition.find('}', k);
9181 5 : if (nPos == std::string::npos)
9182 : {
9183 1 : CPLError(CE_Failure, CPLE_AppDefined,
9184 : "Value of "
9185 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9186 : "%s is invalid at offset %d",
9187 : osAuxArrayDimName.c_str(), j,
9188 : osDefinition.c_str(), int(k));
9189 2 : return nullptr;
9190 : }
9191 : const auto osAttrName =
9192 4 : osDefinition.substr(k + 2, nPos - (k + 2));
9193 4 : auto poAttr = oItem.poArray->GetAttribute(osAttrName);
9194 4 : if (!poAttr)
9195 : {
9196 1 : CPLError(CE_Failure, CPLE_AppDefined,
9197 : "Value of "
9198 : "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
9199 : "%s is invalid: %s is not an attribute of %s",
9200 : osAuxArrayDimName.c_str(), j,
9201 : osDefinition.c_str(), osAttrName.c_str(),
9202 : osAuxArrayDimName.c_str());
9203 1 : return nullptr;
9204 : }
9205 3 : k = nPos;
9206 3 : const char *pszValue = poAttr->ReadAsString();
9207 3 : if (!pszValue)
9208 : {
9209 0 : CPLError(CE_Failure, CPLE_AppDefined,
9210 : "Cannot get value of attribute %s of %s as a "
9211 : "string",
9212 : osAttrName.c_str(), osAuxArrayDimName.c_str());
9213 0 : return nullptr;
9214 : }
9215 3 : osModDefinition += pszValue;
9216 : }
9217 : else
9218 : {
9219 46 : osModDefinition += osDefinition[k];
9220 : }
9221 : }
9222 :
9223 8 : oItem.osDefinition = std::move(osModDefinition);
9224 8 : oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9225 :
9226 8 : aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9227 8 : std::move(oItem));
9228 : }
9229 : }
9230 :
9231 318 : auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9232 :
9233 159 : poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9234 :
9235 159 : poDS->nRasterYSize =
9236 159 : nDimCount < 2 ? 1
9237 148 : : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9238 148 : dims[iYDim]->GetSize()));
9239 318 : poDS->nRasterXSize = static_cast<int>(
9240 159 : std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9241 :
9242 318 : std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9243 318 : std::vector<GUInt64> anStackIters(nDimCount);
9244 318 : std::vector<size_t> anMapNewToOld(nNewDimCount);
9245 502 : for (size_t i = 0, j = 0; i < nDimCount; ++i)
9246 : {
9247 343 : if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9248 : {
9249 36 : anMapNewToOld[j] = i;
9250 36 : j++;
9251 : }
9252 : }
9253 :
9254 318 : poDS->m_bHasGT =
9255 159 : array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9256 :
9257 318 : const auto attrs(array->GetAttributes());
9258 228 : for (const auto &attr : attrs)
9259 : {
9260 69 : if (attr->GetName() != "COLOR_INTERPRETATION")
9261 : {
9262 132 : auto stringArray = attr->ReadAsStringArray();
9263 132 : std::string val;
9264 66 : if (stringArray.size() > 1)
9265 : {
9266 22 : val += '{';
9267 : }
9268 154 : for (int i = 0; i < stringArray.size(); ++i)
9269 : {
9270 88 : if (i > 0)
9271 22 : val += ',';
9272 88 : val += stringArray[i];
9273 : }
9274 66 : if (stringArray.size() > 1)
9275 : {
9276 22 : val += '}';
9277 : }
9278 66 : poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9279 : }
9280 : }
9281 :
9282 159 : const char *pszDelay = CSLFetchNameValueDef(
9283 : papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9284 : CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9285 : const double dfDelay =
9286 159 : EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9287 159 : const auto nStartTime = time(nullptr);
9288 159 : bool bHasWarned = false;
9289 : // Instantiate bands by iterating over non-XY variables
9290 159 : size_t iDim = 0;
9291 159 : int nCurBand = 1;
9292 253 : lbl_next_depth:
9293 253 : if (iDim < nNewDimCount)
9294 : {
9295 38 : anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9296 38 : anOtherDimCoord[iDim] = 0;
9297 : while (true)
9298 : {
9299 94 : ++iDim;
9300 94 : goto lbl_next_depth;
9301 94 : lbl_return_to_caller:
9302 94 : --iDim;
9303 94 : --anStackIters[iDim];
9304 94 : if (anStackIters[iDim] == 0)
9305 38 : break;
9306 56 : ++anOtherDimCoord[iDim];
9307 : }
9308 : }
9309 : else
9310 : {
9311 430 : poDS->SetBand(nCurBand, new GDALRasterBandFromArray(
9312 215 : poDS.get(), anOtherDimCoord,
9313 : aoBandParameterMetadataItems, dfDelay,
9314 215 : nStartTime, bHasWarned));
9315 215 : ++nCurBand;
9316 : }
9317 253 : if (iDim > 0)
9318 94 : goto lbl_return_to_caller;
9319 :
9320 159 : if (!array->GetFilename().empty())
9321 : {
9322 142 : poDS->SetPhysicalFilename(array->GetFilename().c_str());
9323 : std::string osDerivedDatasetName(
9324 : CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9325 284 : int(iYDim), array->GetFullName().c_str()));
9326 142 : if (!array->GetContext().empty())
9327 : {
9328 2 : osDerivedDatasetName += " with context ";
9329 2 : osDerivedDatasetName += array->GetContext();
9330 : }
9331 142 : poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9332 142 : poDS->TryLoadXML();
9333 : }
9334 :
9335 159 : return poDS.release();
9336 : }
9337 :
9338 : /************************************************************************/
9339 : /* AsClassicDataset() */
9340 : /************************************************************************/
9341 :
9342 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9343 : *
9344 : * In the case of > 2D arrays, additional dimensions will be represented as
9345 : * raster bands.
9346 : *
9347 : * The "reverse" method is GDALRasterBand::AsMDArray().
9348 : *
9349 : * This is the same as the C function GDALMDArrayAsClassicDataset().
9350 : *
9351 : * @param iXDim Index of the dimension that will be used as the X/width axis.
9352 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
9353 : * Ignored if the dimension count is 1.
9354 : * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9355 : * option.
9356 : * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9357 : * nullptr. Current supported options are:
9358 : * <ul>
9359 : * <li>BAND_METADATA: JSON serialized array defining which
9360 : * arrays of the poRootGroup, indexed by non-X and Y
9361 : * dimensions, should be mapped as band metadata items.
9362 : * Each array item should be an object with the
9363 : * following members:
9364 : * - "array": full name of a band parameter array.
9365 : * Such array must be a one
9366 : * dimensional array, and its dimension must be one of
9367 : * the dimensions of the array on which the method is
9368 : * called (excluding the X and Y dimensons).
9369 : * - "item_name": band metadata item name
9370 : * - "item_value": (optional) String, where "%[x][.y]f",
9371 : * "%[x][.y]g" or "%s" printf-like formatting can be
9372 : * used to format the corresponding value of the
9373 : * parameter array. The percentage character should be
9374 : * repeated: "%%"
9375 : * "${attribute_name}" can also be used to include the
9376 : * value of an attribute for the array.
9377 : * If "item_value" is not provided, a default formatting
9378 : * of the value will be applied.
9379 : *
9380 : * Example:
9381 : * [
9382 : * {
9383 : * "array": "/sensor_band_parameters/wavelengths",
9384 : * "item_name": "WAVELENGTH",
9385 : * "item_value": "%.1f ${units}"
9386 : * },
9387 : * {
9388 : * "array": "/sensor_band_parameters/fwhm",
9389 : * "item_name": "FWHM"
9390 : * },
9391 : * {
9392 : * "array": "/sensor_band_parameters/fwhm",
9393 : * "item_name": "FWHM_UNIT",
9394 : * "item_value": "${units}"
9395 : * }
9396 : * ]
9397 : * </li>
9398 : * <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
9399 : * seconds allowed to set the DIM_{dimname}_VALUE band
9400 : * metadata items from the indexing variable of the
9401 : * dimensions.
9402 : * Default value is 5. 'unlimited' can be used to mean
9403 : * unlimited delay. Can also be defined globally with
9404 : * the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
9405 : * option.</li>
9406 : * </ul>
9407 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9408 : */
9409 : GDALDataset *
9410 182 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
9411 : const std::shared_ptr<GDALGroup> &poRootGroup,
9412 : CSLConstList papszOptions) const
9413 : {
9414 364 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
9415 182 : if (!self)
9416 : {
9417 0 : CPLError(CE_Failure, CPLE_AppDefined,
9418 : "Driver implementation issue: m_pSelf not set !");
9419 0 : return nullptr;
9420 : }
9421 182 : return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
9422 182 : papszOptions);
9423 : }
9424 :
9425 : /************************************************************************/
9426 : /* GetStatistics() */
9427 : /************************************************************************/
9428 :
9429 : /**
9430 : * \brief Fetch statistics.
9431 : *
9432 : * Returns the minimum, maximum, mean and standard deviation of all
9433 : * pixel values in this array.
9434 : *
9435 : * If bForce is FALSE results will only be returned if it can be done
9436 : * quickly (i.e. without scanning the data). If bForce is FALSE and
9437 : * results cannot be returned efficiently, the method will return CE_Warning
9438 : * but no warning will have been issued. This is a non-standard use of
9439 : * the CE_Warning return value to indicate "nothing done".
9440 : *
9441 : * When cached statistics are not available, and bForce is TRUE,
9442 : * ComputeStatistics() is called.
9443 : *
9444 : * Note that file formats using PAM (Persistent Auxiliary Metadata) services
9445 : * will generally cache statistics in the .aux.xml file allowing fast fetch
9446 : * after the first request.
9447 : *
9448 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9449 : *
9450 : * This method is the same as the C function GDALMDArrayGetStatistics().
9451 : *
9452 : * @param bApproxOK Currently ignored. In the future, should be set to true
9453 : * if statistics on the whole array are wished, or to false if a subset of it
9454 : * may be used.
9455 : *
9456 : * @param bForce If false statistics will only be returned if it can
9457 : * be done without rescanning the image.
9458 : *
9459 : * @param pdfMin Location into which to load image minimum (may be NULL).
9460 : *
9461 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9462 : *
9463 : * @param pdfMean Location into which to load image mean (may be NULL).
9464 : *
9465 : * @param pdfStdDev Location into which to load image standard deviation
9466 : * (may be NULL).
9467 : *
9468 : * @param pnValidCount Number of samples whose value is different from the
9469 : * nodata value. (may be NULL)
9470 : *
9471 : * @param pfnProgress a function to call to report progress, or NULL.
9472 : *
9473 : * @param pProgressData application data to pass to the progress function.
9474 : *
9475 : * @return CE_None on success, CE_Warning if no values returned,
9476 : * CE_Failure if an error occurs.
9477 : *
9478 : * @since GDAL 3.2
9479 : */
9480 :
9481 7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
9482 : double *pdfMax, double *pdfMean,
9483 : double *pdfStdDev, GUInt64 *pnValidCount,
9484 : GDALProgressFunc pfnProgress,
9485 : void *pProgressData)
9486 : {
9487 7 : if (!bForce)
9488 1 : return CE_Warning;
9489 :
9490 12 : return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
9491 6 : pnValidCount, pfnProgress, pProgressData, nullptr)
9492 6 : ? CE_None
9493 6 : : CE_Failure;
9494 : }
9495 :
9496 : /************************************************************************/
9497 : /* ComputeStatistics() */
9498 : /************************************************************************/
9499 :
9500 : /**
9501 : * \brief Compute statistics.
9502 : *
9503 : * Returns the minimum, maximum, mean and standard deviation of all
9504 : * pixel values in this array.
9505 : *
9506 : * Pixels taken into account in statistics are those whose mask value
9507 : * (as determined by GetMask()) is non-zero.
9508 : *
9509 : * Once computed, the statistics will generally be "set" back on the
9510 : * owing dataset.
9511 : *
9512 : * Cached statistics can be cleared with GDALDataset::ClearStatistics().
9513 : *
9514 : * This method is the same as the C functions GDALMDArrayComputeStatistics().
9515 : * and GDALMDArrayComputeStatisticsEx().
9516 : *
9517 : * @param bApproxOK Currently ignored. In the future, should be set to true
9518 : * if statistics on the whole array are wished, or to false if a subset of it
9519 : * may be used.
9520 : *
9521 : * @param pdfMin Location into which to load image minimum (may be NULL).
9522 : *
9523 : * @param pdfMax Location into which to load image maximum (may be NULL).-
9524 : *
9525 : * @param pdfMean Location into which to load image mean (may be NULL).
9526 : *
9527 : * @param pdfStdDev Location into which to load image standard deviation
9528 : * (may be NULL).
9529 : *
9530 : * @param pnValidCount Number of samples whose value is different from the
9531 : * nodata value. (may be NULL)
9532 : *
9533 : * @param pfnProgress a function to call to report progress, or NULL.
9534 : *
9535 : * @param pProgressData application data to pass to the progress function.
9536 : *
9537 : * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
9538 : * Options are driver specific. For now the netCDF and Zarr
9539 : * drivers recognize UPDATE_METADATA=YES, whose effect is
9540 : * to add or update the actual_range attribute with the
9541 : * computed min/max, only if done on the full array, in non
9542 : * approximate mode, and the dataset is opened in update
9543 : * mode.
9544 : *
9545 : * @return true on success
9546 : *
9547 : * @since GDAL 3.2
9548 : */
9549 :
9550 10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
9551 : double *pdfMax, double *pdfMean,
9552 : double *pdfStdDev, GUInt64 *pnValidCount,
9553 : GDALProgressFunc pfnProgress,
9554 : void *pProgressData,
9555 : CSLConstList papszOptions)
9556 : {
9557 : struct StatsPerChunkType
9558 : {
9559 : const GDALMDArray *array = nullptr;
9560 : std::shared_ptr<GDALMDArray> poMask{};
9561 : double dfMin = std::numeric_limits<double>::max();
9562 : double dfMax = -std::numeric_limits<double>::max();
9563 : double dfMean = 0.0;
9564 : double dfM2 = 0.0;
9565 : GUInt64 nValidCount = 0;
9566 : std::vector<GByte> abyData{};
9567 : std::vector<double> adfData{};
9568 : std::vector<GByte> abyMaskData{};
9569 : GDALProgressFunc pfnProgress = nullptr;
9570 : void *pProgressData = nullptr;
9571 : };
9572 :
9573 10 : const auto PerChunkFunc = [](GDALAbstractMDArray *,
9574 : const GUInt64 *chunkArrayStartIdx,
9575 : const size_t *chunkCount, GUInt64 iCurChunk,
9576 : GUInt64 nChunkCount, void *pUserData)
9577 : {
9578 10 : StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
9579 10 : const GDALMDArray *array = data->array;
9580 10 : const GDALMDArray *poMask = data->poMask.get();
9581 10 : const size_t nDims = array->GetDimensionCount();
9582 10 : size_t nVals = 1;
9583 27 : for (size_t i = 0; i < nDims; i++)
9584 17 : nVals *= chunkCount[i];
9585 :
9586 : // Get mask
9587 10 : data->abyMaskData.resize(nVals);
9588 10 : if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9589 10 : poMask->GetDataType(), &data->abyMaskData[0])))
9590 : {
9591 0 : return false;
9592 : }
9593 :
9594 : // Get data
9595 10 : const auto &oType = array->GetDataType();
9596 10 : if (oType.GetNumericDataType() == GDT_Float64)
9597 : {
9598 4 : data->adfData.resize(nVals);
9599 4 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9600 4 : oType, &data->adfData[0]))
9601 : {
9602 0 : return false;
9603 : }
9604 : }
9605 : else
9606 : {
9607 6 : data->abyData.resize(nVals * oType.GetSize());
9608 6 : if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
9609 6 : oType, &data->abyData[0]))
9610 : {
9611 0 : return false;
9612 : }
9613 6 : data->adfData.resize(nVals);
9614 6 : GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
9615 6 : static_cast<int>(oType.GetSize()),
9616 6 : &data->adfData[0], GDT_Float64,
9617 : static_cast<int>(sizeof(double)),
9618 : static_cast<GPtrDiff_t>(nVals));
9619 : }
9620 469 : for (size_t i = 0; i < nVals; i++)
9621 : {
9622 459 : if (data->abyMaskData[i])
9623 : {
9624 454 : const double dfValue = data->adfData[i];
9625 454 : data->dfMin = std::min(data->dfMin, dfValue);
9626 454 : data->dfMax = std::max(data->dfMax, dfValue);
9627 454 : data->nValidCount++;
9628 454 : const double dfDelta = dfValue - data->dfMean;
9629 454 : data->dfMean += dfDelta / data->nValidCount;
9630 454 : data->dfM2 += dfDelta * (dfValue - data->dfMean);
9631 : }
9632 : }
9633 10 : if (data->pfnProgress &&
9634 0 : !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
9635 : "", data->pProgressData))
9636 : {
9637 0 : return false;
9638 : }
9639 10 : return true;
9640 : };
9641 :
9642 10 : const auto &oType = GetDataType();
9643 20 : if (oType.GetClass() != GEDTC_NUMERIC ||
9644 10 : GDALDataTypeIsComplex(oType.GetNumericDataType()))
9645 : {
9646 0 : CPLError(
9647 : CE_Failure, CPLE_NotSupported,
9648 : "Statistics can only be computed on non-complex numeric data type");
9649 0 : return false;
9650 : }
9651 :
9652 10 : const size_t nDims = GetDimensionCount();
9653 20 : std::vector<GUInt64> arrayStartIdx(nDims);
9654 20 : std::vector<GUInt64> count(nDims);
9655 10 : const auto &poDims = GetDimensions();
9656 27 : for (size_t i = 0; i < nDims; i++)
9657 : {
9658 17 : count[i] = poDims[i]->GetSize();
9659 : }
9660 10 : const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
9661 : const size_t nMaxChunkSize =
9662 : pszSwathSize
9663 10 : ? static_cast<size_t>(
9664 0 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
9665 0 : CPLAtoGIntBig(pszSwathSize)))
9666 : : static_cast<size_t>(
9667 10 : std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
9668 10 : GDALGetCacheMax64() / 4));
9669 20 : StatsPerChunkType sData;
9670 10 : sData.array = this;
9671 10 : sData.poMask = GetMask(nullptr);
9672 10 : if (sData.poMask == nullptr)
9673 : {
9674 0 : return false;
9675 : }
9676 10 : sData.pfnProgress = pfnProgress;
9677 10 : sData.pProgressData = pProgressData;
9678 10 : if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
9679 20 : GetProcessingChunkSize(nMaxChunkSize).data(),
9680 10 : PerChunkFunc, &sData))
9681 : {
9682 0 : return false;
9683 : }
9684 :
9685 10 : if (pdfMin)
9686 10 : *pdfMin = sData.dfMin;
9687 :
9688 10 : if (pdfMax)
9689 10 : *pdfMax = sData.dfMax;
9690 :
9691 10 : if (pdfMean)
9692 8 : *pdfMean = sData.dfMean;
9693 :
9694 : const double dfStdDev =
9695 10 : sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
9696 10 : if (pdfStdDev)
9697 8 : *pdfStdDev = dfStdDev;
9698 :
9699 10 : if (pnValidCount)
9700 8 : *pnValidCount = sData.nValidCount;
9701 :
9702 10 : SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
9703 10 : sData.nValidCount, papszOptions);
9704 :
9705 10 : return true;
9706 : }
9707 :
9708 : /************************************************************************/
9709 : /* SetStatistics() */
9710 : /************************************************************************/
9711 : //! @cond Doxygen_Suppress
9712 5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
9713 : double /* dfMax */, double /* dfMean */,
9714 : double /* dfStdDev */,
9715 : GUInt64 /* nValidCount */,
9716 : CSLConstList /* papszOptions */)
9717 : {
9718 5 : CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
9719 5 : return false;
9720 : }
9721 :
9722 : //! @endcond
9723 :
9724 : /************************************************************************/
9725 : /* ClearStatistics() */
9726 : /************************************************************************/
9727 :
9728 : /**
9729 : * \brief Clear statistics.
9730 : *
9731 : * @since GDAL 3.4
9732 : */
9733 0 : void GDALMDArray::ClearStatistics()
9734 : {
9735 0 : }
9736 :
9737 : /************************************************************************/
9738 : /* GetCoordinateVariables() */
9739 : /************************************************************************/
9740 :
9741 : /**
9742 : * \brief Return coordinate variables.
9743 : *
9744 : * Coordinate variables are an alternate way of indexing an array that can
9745 : * be sometimes used. For example, an array collected through remote sensing
9746 : * might be indexed by (scanline, pixel). But there can be
9747 : * a longitude and latitude arrays alongside that are also both indexed by
9748 : * (scanline, pixel), and are referenced from operational arrays for
9749 : * reprojection purposes.
9750 : *
9751 : * For netCDF, this will return the arrays referenced by the "coordinates"
9752 : * attribute.
9753 : *
9754 : * This method is the same as the C function
9755 : * GDALMDArrayGetCoordinateVariables().
9756 : *
9757 : * @return a vector of arrays
9758 : *
9759 : * @since GDAL 3.4
9760 : */
9761 :
9762 : std::vector<std::shared_ptr<GDALMDArray>>
9763 13 : GDALMDArray::GetCoordinateVariables() const
9764 : {
9765 13 : return {};
9766 : }
9767 :
9768 : /************************************************************************/
9769 : /* ~GDALExtendedDataType() */
9770 : /************************************************************************/
9771 :
9772 : GDALExtendedDataType::~GDALExtendedDataType() = default;
9773 :
9774 : /************************************************************************/
9775 : /* GDALExtendedDataType() */
9776 : /************************************************************************/
9777 :
9778 7578 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
9779 7578 : GDALExtendedDataTypeSubType eSubType)
9780 : : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
9781 7578 : m_nMaxStringLength(nMaxStringLength)
9782 : {
9783 7578 : }
9784 :
9785 : /************************************************************************/
9786 : /* GDALExtendedDataType() */
9787 : /************************************************************************/
9788 :
9789 30819 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
9790 : : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
9791 30819 : m_nSize(GDALGetDataTypeSizeBytes(eType))
9792 : {
9793 30819 : }
9794 :
9795 : /************************************************************************/
9796 : /* GDALExtendedDataType() */
9797 : /************************************************************************/
9798 :
9799 571 : GDALExtendedDataType::GDALExtendedDataType(
9800 : const std::string &osName, size_t nTotalSize,
9801 571 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
9802 : : m_osName(osName), m_eClass(GEDTC_COMPOUND),
9803 571 : m_aoComponents(std::move(components)), m_nSize(nTotalSize)
9804 : {
9805 571 : }
9806 :
9807 : /************************************************************************/
9808 : /* GDALExtendedDataType() */
9809 : /************************************************************************/
9810 :
9811 : /** Copy constructor. */
9812 15042 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
9813 30084 : : m_osName(other.m_osName), m_eClass(other.m_eClass),
9814 15042 : m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
9815 15042 : m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
9816 : {
9817 15042 : if (m_eClass == GEDTC_COMPOUND)
9818 : {
9819 431 : for (const auto &elt : other.m_aoComponents)
9820 : {
9821 281 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
9822 : }
9823 : }
9824 15042 : }
9825 :
9826 : /************************************************************************/
9827 : /* operator= () */
9828 : /************************************************************************/
9829 :
9830 : /** Copy assignment. */
9831 : GDALExtendedDataType &
9832 606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
9833 : {
9834 606 : if (this != &other)
9835 : {
9836 606 : m_osName = other.m_osName;
9837 606 : m_eClass = other.m_eClass;
9838 606 : m_eSubType = other.m_eSubType;
9839 606 : m_eNumericDT = other.m_eNumericDT;
9840 606 : m_nSize = other.m_nSize;
9841 606 : m_nMaxStringLength = other.m_nMaxStringLength;
9842 606 : m_aoComponents.clear();
9843 606 : if (m_eClass == GEDTC_COMPOUND)
9844 : {
9845 0 : for (const auto &elt : other.m_aoComponents)
9846 : {
9847 0 : m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
9848 : }
9849 : }
9850 : }
9851 606 : return *this;
9852 : }
9853 :
9854 : /************************************************************************/
9855 : /* operator= () */
9856 : /************************************************************************/
9857 :
9858 : /** Move assignment. */
9859 : GDALExtendedDataType &
9860 12464 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
9861 : {
9862 12464 : m_osName = std::move(other.m_osName);
9863 12464 : m_eClass = other.m_eClass;
9864 12464 : m_eSubType = other.m_eSubType;
9865 12464 : m_eNumericDT = other.m_eNumericDT;
9866 12464 : m_nSize = other.m_nSize;
9867 12464 : m_nMaxStringLength = other.m_nMaxStringLength;
9868 12464 : m_aoComponents = std::move(other.m_aoComponents);
9869 12464 : other.m_eClass = GEDTC_NUMERIC;
9870 12464 : other.m_eNumericDT = GDT_Unknown;
9871 12464 : other.m_nSize = 0;
9872 12464 : other.m_nMaxStringLength = 0;
9873 12464 : return *this;
9874 : }
9875 :
9876 : /************************************************************************/
9877 : /* Create() */
9878 : /************************************************************************/
9879 :
9880 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
9881 : *
9882 : * This is the same as the C function GDALExtendedDataTypeCreate()
9883 : *
9884 : * @param eType Numeric data type.
9885 : */
9886 30813 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
9887 : {
9888 30813 : return GDALExtendedDataType(eType);
9889 : }
9890 :
9891 : /************************************************************************/
9892 : /* Create() */
9893 : /************************************************************************/
9894 :
9895 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
9896 : *
9897 : * This is the same as the C function GDALExtendedDataTypeCreateCompound()
9898 : *
9899 : * @param osName Type name.
9900 : * @param nTotalSize Total size of the type in bytes.
9901 : * Should be large enough to store all components.
9902 : * @param components Components of the compound type.
9903 : */
9904 577 : GDALExtendedDataType GDALExtendedDataType::Create(
9905 : const std::string &osName, size_t nTotalSize,
9906 : std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
9907 : {
9908 577 : size_t nLastOffset = 0;
9909 : // Some arbitrary threshold to avoid potential integer overflows
9910 577 : if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
9911 : {
9912 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
9913 2 : return GDALExtendedDataType(GDT_Unknown);
9914 : }
9915 2966 : for (const auto &comp : components)
9916 : {
9917 : // Check alignment too ?
9918 2392 : if (comp->GetOffset() < nLastOffset)
9919 : {
9920 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
9921 1 : return GDALExtendedDataType(GDT_Unknown);
9922 : }
9923 2391 : nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
9924 : }
9925 574 : if (nTotalSize < nLastOffset)
9926 : {
9927 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
9928 1 : return GDALExtendedDataType(GDT_Unknown);
9929 : }
9930 573 : if (nTotalSize == 0 || components.empty())
9931 : {
9932 2 : CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
9933 2 : return GDALExtendedDataType(GDT_Unknown);
9934 : }
9935 571 : return GDALExtendedDataType(osName, nTotalSize, std::move(components));
9936 : }
9937 :
9938 : /************************************************************************/
9939 : /* Create() */
9940 : /************************************************************************/
9941 :
9942 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
9943 : *
9944 : * This is the same as the C function GDALExtendedDataTypeCreateString().
9945 : *
9946 : * @param nMaxStringLength maximum length of a string in bytes. 0 if
9947 : * unknown/unlimited
9948 : * @param eSubType Subtype.
9949 : */
9950 : GDALExtendedDataType
9951 7578 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
9952 : GDALExtendedDataTypeSubType eSubType)
9953 : {
9954 7578 : return GDALExtendedDataType(nMaxStringLength, eSubType);
9955 : }
9956 :
9957 : /************************************************************************/
9958 : /* operator==() */
9959 : /************************************************************************/
9960 :
9961 : /** Equality operator.
9962 : *
9963 : * This is the same as the C function GDALExtendedDataTypeEquals().
9964 : */
9965 2051 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
9966 : {
9967 2024 : if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
9968 4075 : m_nSize != other.m_nSize || m_osName != other.m_osName)
9969 : {
9970 166 : return false;
9971 : }
9972 1885 : if (m_eClass == GEDTC_NUMERIC)
9973 : {
9974 805 : return m_eNumericDT == other.m_eNumericDT;
9975 : }
9976 1080 : if (m_eClass == GEDTC_STRING)
9977 : {
9978 931 : return true;
9979 : }
9980 149 : CPLAssert(m_eClass == GEDTC_COMPOUND);
9981 149 : if (m_aoComponents.size() != other.m_aoComponents.size())
9982 : {
9983 2 : return false;
9984 : }
9985 668 : for (size_t i = 0; i < m_aoComponents.size(); i++)
9986 : {
9987 521 : if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
9988 : {
9989 0 : return false;
9990 : }
9991 : }
9992 147 : return true;
9993 : }
9994 :
9995 : /************************************************************************/
9996 : /* CanConvertTo() */
9997 : /************************************************************************/
9998 :
9999 : /** Return whether this data type can be converted to the other one.
10000 : *
10001 : * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10002 : *
10003 : * @param other Target data type for the conversion being considered.
10004 : */
10005 7521 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10006 : {
10007 7521 : if (m_eClass == GEDTC_NUMERIC)
10008 : {
10009 5337 : if (m_eNumericDT == GDT_Unknown)
10010 0 : return false;
10011 5337 : if (other.m_eClass == GEDTC_NUMERIC &&
10012 5263 : other.m_eNumericDT == GDT_Unknown)
10013 0 : return false;
10014 5411 : return other.m_eClass == GEDTC_NUMERIC ||
10015 5411 : other.m_eClass == GEDTC_STRING;
10016 : }
10017 2184 : if (m_eClass == GEDTC_STRING)
10018 : {
10019 2067 : return other.m_eClass == m_eClass;
10020 : }
10021 117 : CPLAssert(m_eClass == GEDTC_COMPOUND);
10022 117 : if (other.m_eClass != GEDTC_COMPOUND)
10023 0 : return false;
10024 : std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10025 234 : srcComponents;
10026 482 : for (const auto &srcComp : m_aoComponents)
10027 : {
10028 365 : srcComponents[srcComp->GetName()] = &srcComp;
10029 : }
10030 355 : for (const auto &dstComp : other.m_aoComponents)
10031 : {
10032 239 : auto oIter = srcComponents.find(dstComp->GetName());
10033 239 : if (oIter == srcComponents.end())
10034 1 : return false;
10035 238 : if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10036 0 : return false;
10037 : }
10038 116 : return true;
10039 : }
10040 :
10041 : /************************************************************************/
10042 : /* NeedsFreeDynamicMemory() */
10043 : /************************************************************************/
10044 :
10045 : /** Return whether the data type holds dynamically allocated memory, that
10046 : * needs to be freed with FreeDynamicMemory().
10047 : *
10048 : */
10049 3335 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10050 : {
10051 3335 : switch (m_eClass)
10052 : {
10053 804 : case GEDTC_STRING:
10054 804 : return true;
10055 :
10056 2470 : case GEDTC_NUMERIC:
10057 2470 : return false;
10058 :
10059 61 : case GEDTC_COMPOUND:
10060 : {
10061 154 : for (const auto &comp : m_aoComponents)
10062 : {
10063 140 : if (comp->GetType().NeedsFreeDynamicMemory())
10064 47 : return true;
10065 : }
10066 : }
10067 : }
10068 14 : return false;
10069 : }
10070 :
10071 : /************************************************************************/
10072 : /* FreeDynamicMemory() */
10073 : /************************************************************************/
10074 :
10075 : /** Release the dynamic memory (strings typically) from a raw value.
10076 : *
10077 : * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10078 : *
10079 : * @param pBuffer Raw buffer of a single element of an attribute or array value.
10080 : */
10081 3031 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10082 : {
10083 3031 : switch (m_eClass)
10084 : {
10085 2179 : case GEDTC_STRING:
10086 : {
10087 : char *pszStr;
10088 2179 : memcpy(&pszStr, pBuffer, sizeof(char *));
10089 2179 : if (pszStr)
10090 : {
10091 1701 : VSIFree(pszStr);
10092 : }
10093 2179 : break;
10094 : }
10095 :
10096 735 : case GEDTC_NUMERIC:
10097 : {
10098 735 : break;
10099 : }
10100 :
10101 117 : case GEDTC_COMPOUND:
10102 : {
10103 117 : GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10104 507 : for (const auto &comp : m_aoComponents)
10105 : {
10106 780 : comp->GetType().FreeDynamicMemory(pabyBuffer +
10107 390 : comp->GetOffset());
10108 : }
10109 117 : break;
10110 : }
10111 : }
10112 3031 : }
10113 :
10114 : /************************************************************************/
10115 : /* ~GDALEDTComponent() */
10116 : /************************************************************************/
10117 :
10118 : GDALEDTComponent::~GDALEDTComponent() = default;
10119 :
10120 : /************************************************************************/
10121 : /* GDALEDTComponent() */
10122 : /************************************************************************/
10123 :
10124 : /** constructor of a GDALEDTComponent
10125 : *
10126 : * This is the same as the C function GDALEDTComponendCreate()
10127 : *
10128 : * @param name Component name
10129 : * @param offset Offset in byte of the component in the compound data type.
10130 : * In case of nesting of compound data type, this should be
10131 : * the offset to the immediate belonging data type, not to the
10132 : * higher level one.
10133 : * @param type Component data type.
10134 : */
10135 2383 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10136 2383 : const GDALExtendedDataType &type)
10137 2383 : : m_osName(name), m_nOffset(offset), m_oType(type)
10138 : {
10139 2383 : }
10140 :
10141 : /************************************************************************/
10142 : /* GDALEDTComponent() */
10143 : /************************************************************************/
10144 :
10145 : /** Copy constructor. */
10146 : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10147 :
10148 : /************************************************************************/
10149 : /* operator==() */
10150 : /************************************************************************/
10151 :
10152 : /** Equality operator.
10153 : */
10154 521 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10155 : {
10156 1042 : return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10157 1042 : m_oType == other.m_oType;
10158 : }
10159 :
10160 : /************************************************************************/
10161 : /* ~GDALDimension() */
10162 : /************************************************************************/
10163 :
10164 : GDALDimension::~GDALDimension() = default;
10165 :
10166 : /************************************************************************/
10167 : /* GDALDimension() */
10168 : /************************************************************************/
10169 :
10170 : //! @cond Doxygen_Suppress
10171 : /** Constructor.
10172 : *
10173 : * @param osParentName Parent name
10174 : * @param osName name
10175 : * @param osType type. See GetType().
10176 : * @param osDirection direction. See GetDirection().
10177 : * @param nSize size.
10178 : */
10179 7902 : GDALDimension::GDALDimension(const std::string &osParentName,
10180 : const std::string &osName,
10181 : const std::string &osType,
10182 7902 : const std::string &osDirection, GUInt64 nSize)
10183 : : m_osName(osName),
10184 : m_osFullName(
10185 7902 : !osParentName.empty()
10186 11709 : ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10187 : : osName),
10188 27513 : m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10189 : {
10190 7902 : }
10191 :
10192 : //! @endcond
10193 :
10194 : /************************************************************************/
10195 : /* GetIndexingVariable() */
10196 : /************************************************************************/
10197 :
10198 : /** Return the variable that is used to index the dimension (if there is one).
10199 : *
10200 : * This is the array, typically one-dimensional, describing the values taken
10201 : * by the dimension.
10202 : */
10203 29 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10204 : {
10205 29 : return nullptr;
10206 : }
10207 :
10208 : /************************************************************************/
10209 : /* SetIndexingVariable() */
10210 : /************************************************************************/
10211 :
10212 : /** Set the variable that is used to index the dimension.
10213 : *
10214 : * This is the array, typically one-dimensional, describing the values taken
10215 : * by the dimension.
10216 : *
10217 : * Optionally implemented by drivers.
10218 : *
10219 : * Drivers known to implement it: MEM.
10220 : *
10221 : * @param poArray Variable to use to index the dimension.
10222 : * @return true in case of success.
10223 : */
10224 3 : bool GDALDimension::SetIndexingVariable(
10225 : CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10226 : {
10227 3 : CPLError(CE_Failure, CPLE_NotSupported,
10228 : "SetIndexingVariable() not implemented");
10229 3 : return false;
10230 : }
10231 :
10232 : /************************************************************************/
10233 : /* Rename() */
10234 : /************************************************************************/
10235 :
10236 : /** Rename the dimension.
10237 : *
10238 : * This is not implemented by all drivers.
10239 : *
10240 : * Drivers known to implement it: MEM, netCDF, ZARR.
10241 : *
10242 : * This is the same as the C function GDALDimensionRename().
10243 : *
10244 : * @param osNewName New name.
10245 : *
10246 : * @return true in case of success
10247 : * @since GDAL 3.8
10248 : */
10249 0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10250 : {
10251 0 : CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10252 0 : return false;
10253 : }
10254 :
10255 : /************************************************************************/
10256 : /* BaseRename() */
10257 : /************************************************************************/
10258 :
10259 : //! @cond Doxygen_Suppress
10260 8 : void GDALDimension::BaseRename(const std::string &osNewName)
10261 : {
10262 8 : m_osFullName.resize(m_osFullName.size() - m_osName.size());
10263 8 : m_osFullName += osNewName;
10264 8 : m_osName = osNewName;
10265 8 : }
10266 :
10267 : //! @endcond
10268 :
10269 : //! @cond Doxygen_Suppress
10270 : /************************************************************************/
10271 : /* ParentRenamed() */
10272 : /************************************************************************/
10273 :
10274 8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
10275 : {
10276 8 : m_osFullName = osNewParentFullName;
10277 8 : m_osFullName += "/";
10278 8 : m_osFullName += m_osName;
10279 8 : }
10280 :
10281 : //! @endcond
10282 :
10283 : //! @cond Doxygen_Suppress
10284 : /************************************************************************/
10285 : /* ParentDeleted() */
10286 : /************************************************************************/
10287 :
10288 4 : void GDALDimension::ParentDeleted()
10289 : {
10290 4 : }
10291 :
10292 : //! @endcond
10293 :
10294 : /************************************************************************/
10295 : /************************************************************************/
10296 : /************************************************************************/
10297 : /* C API */
10298 : /************************************************************************/
10299 : /************************************************************************/
10300 : /************************************************************************/
10301 :
10302 : /************************************************************************/
10303 : /* GDALExtendedDataTypeCreate() */
10304 : /************************************************************************/
10305 :
10306 : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10307 : *
10308 : * This is the same as the C++ method GDALExtendedDataType::Create()
10309 : *
10310 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10311 : *
10312 : * @param eType Numeric data type.
10313 : *
10314 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10315 : */
10316 1940 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
10317 : {
10318 : return new GDALExtendedDataTypeHS(
10319 1940 : new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
10320 : }
10321 :
10322 : /************************************************************************/
10323 : /* GDALExtendedDataTypeCreateString() */
10324 : /************************************************************************/
10325 :
10326 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10327 : *
10328 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10329 : *
10330 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10331 : *
10332 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10333 : */
10334 0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
10335 : {
10336 0 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10337 0 : GDALExtendedDataType::CreateString(nMaxStringLength)));
10338 : }
10339 :
10340 : /************************************************************************/
10341 : /* GDALExtendedDataTypeCreateStringEx() */
10342 : /************************************************************************/
10343 :
10344 : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
10345 : *
10346 : * This is the same as the C++ method GDALExtendedDataType::CreateString()
10347 : *
10348 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10349 : *
10350 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10351 : * @since GDAL 3.4
10352 : */
10353 : GDALExtendedDataTypeH
10354 185 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
10355 : GDALExtendedDataTypeSubType eSubType)
10356 : {
10357 185 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
10358 185 : GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
10359 : }
10360 :
10361 : /************************************************************************/
10362 : /* GDALExtendedDataTypeCreateCompound() */
10363 : /************************************************************************/
10364 :
10365 : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10366 : *
10367 : * This is the same as the C++ method GDALExtendedDataType::Create(const
10368 : * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
10369 : *
10370 : * The returned handle should be freed with GDALExtendedDataTypeRelease().
10371 : *
10372 : * @param pszName Type name.
10373 : * @param nTotalSize Total size of the type in bytes.
10374 : * Should be large enough to store all components.
10375 : * @param nComponents Number of components in comps array.
10376 : * @param comps Components.
10377 : * @return a new GDALExtendedDataTypeH handle, or nullptr.
10378 : */
10379 : GDALExtendedDataTypeH
10380 22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
10381 : size_t nComponents,
10382 : const GDALEDTComponentH *comps)
10383 : {
10384 44 : std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
10385 54 : for (size_t i = 0; i < nComponents; i++)
10386 : {
10387 64 : compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
10388 64 : new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
10389 : }
10390 : auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
10391 66 : std::move(compsCpp));
10392 22 : if (dt.GetClass() != GEDTC_COMPOUND)
10393 6 : return nullptr;
10394 16 : return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
10395 : }
10396 :
10397 : /************************************************************************/
10398 : /* GDALExtendedDataTypeRelease() */
10399 : /************************************************************************/
10400 :
10401 : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
10402 : *
10403 : * Note: when applied on a object coming from a driver, this does not
10404 : * destroy the object in the file, database, etc...
10405 : */
10406 6371 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
10407 : {
10408 6371 : delete hEDT;
10409 6371 : }
10410 :
10411 : /************************************************************************/
10412 : /* GDALExtendedDataTypeGetName() */
10413 : /************************************************************************/
10414 :
10415 : /** Return type name.
10416 : *
10417 : * This is the same as the C++ method GDALExtendedDataType::GetName()
10418 : */
10419 7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
10420 : {
10421 7 : VALIDATE_POINTER1(hEDT, __func__, "");
10422 7 : return hEDT->m_poImpl->GetName().c_str();
10423 : }
10424 :
10425 : /************************************************************************/
10426 : /* GDALExtendedDataTypeGetClass() */
10427 : /************************************************************************/
10428 :
10429 : /** Return type class.
10430 : *
10431 : * This is the same as the C++ method GDALExtendedDataType::GetClass()
10432 : */
10433 : GDALExtendedDataTypeClass
10434 7401 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
10435 : {
10436 7401 : VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
10437 7401 : return hEDT->m_poImpl->GetClass();
10438 : }
10439 :
10440 : /************************************************************************/
10441 : /* GDALExtendedDataTypeGetNumericDataType() */
10442 : /************************************************************************/
10443 :
10444 : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
10445 : *
10446 : * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
10447 : */
10448 510 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
10449 : {
10450 510 : VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
10451 510 : return hEDT->m_poImpl->GetNumericDataType();
10452 : }
10453 :
10454 : /************************************************************************/
10455 : /* GDALExtendedDataTypeGetSize() */
10456 : /************************************************************************/
10457 :
10458 : /** Return data type size in bytes.
10459 : *
10460 : * This is the same as the C++ method GDALExtendedDataType::GetSize()
10461 : */
10462 2460 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
10463 : {
10464 2460 : VALIDATE_POINTER1(hEDT, __func__, 0);
10465 2460 : return hEDT->m_poImpl->GetSize();
10466 : }
10467 :
10468 : /************************************************************************/
10469 : /* GDALExtendedDataTypeGetMaxStringLength() */
10470 : /************************************************************************/
10471 :
10472 : /** Return the maximum length of a string in bytes.
10473 : *
10474 : * 0 indicates unknown/unlimited string.
10475 : *
10476 : * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
10477 : */
10478 3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
10479 : {
10480 3 : VALIDATE_POINTER1(hEDT, __func__, 0);
10481 3 : return hEDT->m_poImpl->GetMaxStringLength();
10482 : }
10483 :
10484 : /************************************************************************/
10485 : /* GDALExtendedDataTypeCanConvertTo() */
10486 : /************************************************************************/
10487 :
10488 : /** Return whether this data type can be converted to the other one.
10489 : *
10490 : * This is the same as the C function GDALExtendedDataType::CanConvertTo()
10491 : *
10492 : * @param hSourceEDT Source data type for the conversion being considered.
10493 : * @param hTargetEDT Target data type for the conversion being considered.
10494 : * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
10495 : */
10496 7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
10497 : GDALExtendedDataTypeH hTargetEDT)
10498 : {
10499 7 : VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
10500 7 : VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
10501 7 : return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
10502 : }
10503 :
10504 : /************************************************************************/
10505 : /* GDALExtendedDataTypeEquals() */
10506 : /************************************************************************/
10507 :
10508 : /** Return whether this data type is equal to another one.
10509 : *
10510 : * This is the same as the C++ method GDALExtendedDataType::operator==()
10511 : *
10512 : * @param hFirstEDT First data type.
10513 : * @param hSecondEDT Second data type.
10514 : * @return TRUE if they are equal. FALSE otherwise.
10515 : */
10516 98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
10517 : GDALExtendedDataTypeH hSecondEDT)
10518 : {
10519 98 : VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
10520 98 : VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
10521 98 : return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
10522 : }
10523 :
10524 : /************************************************************************/
10525 : /* GDALExtendedDataTypeGetSubType() */
10526 : /************************************************************************/
10527 :
10528 : /** Return the subtype of a type.
10529 : *
10530 : * This is the same as the C++ method GDALExtendedDataType::GetSubType()
10531 : *
10532 : * @param hEDT Data type.
10533 : * @return subtype.
10534 : * @since 3.4
10535 : */
10536 : GDALExtendedDataTypeSubType
10537 104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
10538 : {
10539 104 : VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
10540 104 : return hEDT->m_poImpl->GetSubType();
10541 : }
10542 :
10543 : /************************************************************************/
10544 : /* GDALExtendedDataTypeGetComponents() */
10545 : /************************************************************************/
10546 :
10547 : /** Return the components of the data type (only valid when GetClass() ==
10548 : * GEDTC_COMPOUND)
10549 : *
10550 : * The returned array and its content must be freed with
10551 : * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
10552 : * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
10553 : * individual array members).
10554 : *
10555 : * This is the same as the C++ method GDALExtendedDataType::GetComponents()
10556 : *
10557 : * @param hEDT Data type
10558 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
10559 : * @return an array of *pnCount components.
10560 : */
10561 44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
10562 : size_t *pnCount)
10563 : {
10564 44 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
10565 44 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
10566 44 : const auto &components = hEDT->m_poImpl->GetComponents();
10567 : auto ret = static_cast<GDALEDTComponentH *>(
10568 44 : CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
10569 131 : for (size_t i = 0; i < components.size(); i++)
10570 : {
10571 87 : ret[i] = new GDALEDTComponentHS(*components[i].get());
10572 : }
10573 44 : *pnCount = components.size();
10574 44 : return ret;
10575 : }
10576 :
10577 : /************************************************************************/
10578 : /* GDALExtendedDataTypeFreeComponents() */
10579 : /************************************************************************/
10580 :
10581 : /** Free the return of GDALExtendedDataTypeGetComponents().
10582 : *
10583 : * @param components return value of GDALExtendedDataTypeGetComponents()
10584 : * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
10585 : */
10586 44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
10587 : size_t nCount)
10588 : {
10589 131 : for (size_t i = 0; i < nCount; i++)
10590 : {
10591 87 : delete components[i];
10592 : }
10593 44 : CPLFree(components);
10594 44 : }
10595 :
10596 : /************************************************************************/
10597 : /* GDALEDTComponentCreate() */
10598 : /************************************************************************/
10599 :
10600 : /** Create a new GDALEDTComponent.
10601 : *
10602 : * The returned value must be freed with GDALEDTComponentRelease().
10603 : *
10604 : * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
10605 : */
10606 20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
10607 : GDALExtendedDataTypeH hType)
10608 : {
10609 20 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10610 20 : VALIDATE_POINTER1(hType, __func__, nullptr);
10611 : return new GDALEDTComponentHS(
10612 20 : GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
10613 : }
10614 :
10615 : /************************************************************************/
10616 : /* GDALEDTComponentRelease() */
10617 : /************************************************************************/
10618 :
10619 : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
10620 : *
10621 : * Note: when applied on a object coming from a driver, this does not
10622 : * destroy the object in the file, database, etc...
10623 : */
10624 61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
10625 : {
10626 61 : delete hComp;
10627 61 : }
10628 :
10629 : /************************************************************************/
10630 : /* GDALEDTComponentGetName() */
10631 : /************************************************************************/
10632 :
10633 : /** Return the name.
10634 : *
10635 : * The returned pointer is valid until hComp is released.
10636 : *
10637 : * This is the same as the C++ method GDALEDTComponent::GetName().
10638 : */
10639 33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
10640 : {
10641 33 : VALIDATE_POINTER1(hComp, __func__, nullptr);
10642 33 : return hComp->m_poImpl->GetName().c_str();
10643 : }
10644 :
10645 : /************************************************************************/
10646 : /* GDALEDTComponentGetOffset() */
10647 : /************************************************************************/
10648 :
10649 : /** Return the offset (in bytes) of the component in the compound data type.
10650 : *
10651 : * This is the same as the C++ method GDALEDTComponent::GetOffset().
10652 : */
10653 31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
10654 : {
10655 31 : VALIDATE_POINTER1(hComp, __func__, 0);
10656 31 : return hComp->m_poImpl->GetOffset();
10657 : }
10658 :
10659 : /************************************************************************/
10660 : /* GDALEDTComponentGetType() */
10661 : /************************************************************************/
10662 :
10663 : /** Return the data type of the component.
10664 : *
10665 : * This is the same as the C++ method GDALEDTComponent::GetType().
10666 : */
10667 93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
10668 : {
10669 93 : VALIDATE_POINTER1(hComp, __func__, nullptr);
10670 : return new GDALExtendedDataTypeHS(
10671 93 : new GDALExtendedDataType(hComp->m_poImpl->GetType()));
10672 : }
10673 :
10674 : /************************************************************************/
10675 : /* GDALGroupRelease() */
10676 : /************************************************************************/
10677 :
10678 : /** Release the GDAL in-memory object associated with a GDALGroupH.
10679 : *
10680 : * Note: when applied on a object coming from a driver, this does not
10681 : * destroy the object in the file, database, etc...
10682 : */
10683 1375 : void GDALGroupRelease(GDALGroupH hGroup)
10684 : {
10685 1375 : delete hGroup;
10686 1375 : }
10687 :
10688 : /************************************************************************/
10689 : /* GDALGroupGetName() */
10690 : /************************************************************************/
10691 :
10692 : /** Return the name of the group.
10693 : *
10694 : * The returned pointer is valid until hGroup is released.
10695 : *
10696 : * This is the same as the C++ method GDALGroup::GetName().
10697 : */
10698 87 : const char *GDALGroupGetName(GDALGroupH hGroup)
10699 : {
10700 87 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10701 87 : return hGroup->m_poImpl->GetName().c_str();
10702 : }
10703 :
10704 : /************************************************************************/
10705 : /* GDALGroupGetFullName() */
10706 : /************************************************************************/
10707 :
10708 : /** Return the full name of the group.
10709 : *
10710 : * The returned pointer is valid until hGroup is released.
10711 : *
10712 : * This is the same as the C++ method GDALGroup::GetFullName().
10713 : */
10714 41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
10715 : {
10716 41 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10717 41 : return hGroup->m_poImpl->GetFullName().c_str();
10718 : }
10719 :
10720 : /************************************************************************/
10721 : /* GDALGroupGetMDArrayNames() */
10722 : /************************************************************************/
10723 :
10724 : /** Return the list of multidimensional array names contained in this group.
10725 : *
10726 : * This is the same as the C++ method GDALGroup::GetGroupNames().
10727 : *
10728 : * @return the array names, to be freed with CSLDestroy()
10729 : */
10730 314 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
10731 : {
10732 314 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10733 628 : auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
10734 628 : CPLStringList res;
10735 798 : for (const auto &name : names)
10736 : {
10737 484 : res.AddString(name.c_str());
10738 : }
10739 314 : return res.StealList();
10740 : }
10741 :
10742 : /************************************************************************/
10743 : /* GDALGroupOpenMDArray() */
10744 : /************************************************************************/
10745 :
10746 : /** Open and return a multidimensional array.
10747 : *
10748 : * This is the same as the C++ method GDALGroup::OpenMDArray().
10749 : *
10750 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
10751 : */
10752 755 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
10753 : CSLConstList papszOptions)
10754 : {
10755 755 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10756 755 : VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
10757 2265 : auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
10758 2265 : papszOptions);
10759 755 : if (!array)
10760 28 : return nullptr;
10761 727 : return new GDALMDArrayHS(array);
10762 : }
10763 :
10764 : /************************************************************************/
10765 : /* GDALGroupOpenMDArrayFromFullname() */
10766 : /************************************************************************/
10767 :
10768 : /** Open and return a multidimensional array from its fully qualified name.
10769 : *
10770 : * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
10771 : *
10772 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
10773 : *
10774 : * @since GDAL 3.2
10775 : */
10776 16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
10777 : const char *pszFullname,
10778 : CSLConstList papszOptions)
10779 : {
10780 16 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10781 16 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
10782 16 : auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
10783 48 : std::string(pszFullname), papszOptions);
10784 16 : if (!array)
10785 2 : return nullptr;
10786 14 : return new GDALMDArrayHS(array);
10787 : }
10788 :
10789 : /************************************************************************/
10790 : /* GDALGroupResolveMDArray() */
10791 : /************************************************************************/
10792 :
10793 : /** Locate an array in a group and its subgroups by name.
10794 : *
10795 : * See GDALGroup::ResolveMDArray() for description of the behavior.
10796 : * @since GDAL 3.2
10797 : */
10798 19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
10799 : const char *pszStartingPoint,
10800 : CSLConstList papszOptions)
10801 : {
10802 19 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10803 19 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10804 19 : VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
10805 19 : auto array = hGroup->m_poImpl->ResolveMDArray(
10806 57 : std::string(pszName), std::string(pszStartingPoint), papszOptions);
10807 19 : if (!array)
10808 2 : return nullptr;
10809 17 : return new GDALMDArrayHS(array);
10810 : }
10811 :
10812 : /************************************************************************/
10813 : /* GDALGroupGetGroupNames() */
10814 : /************************************************************************/
10815 :
10816 : /** Return the list of sub-groups contained in this group.
10817 : *
10818 : * This is the same as the C++ method GDALGroup::GetGroupNames().
10819 : *
10820 : * @return the group names, to be freed with CSLDestroy()
10821 : */
10822 95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
10823 : {
10824 95 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10825 190 : auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
10826 190 : CPLStringList res;
10827 215 : for (const auto &name : names)
10828 : {
10829 120 : res.AddString(name.c_str());
10830 : }
10831 95 : return res.StealList();
10832 : }
10833 :
10834 : /************************************************************************/
10835 : /* GDALGroupOpenGroup() */
10836 : /************************************************************************/
10837 :
10838 : /** Open and return a sub-group.
10839 : *
10840 : * This is the same as the C++ method GDALGroup::OpenGroup().
10841 : *
10842 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
10843 : */
10844 157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
10845 : CSLConstList papszOptions)
10846 : {
10847 157 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10848 157 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
10849 : auto subGroup =
10850 471 : hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
10851 157 : if (!subGroup)
10852 28 : return nullptr;
10853 129 : return new GDALGroupHS(subGroup);
10854 : }
10855 :
10856 : /************************************************************************/
10857 : /* GDALGroupGetVectorLayerNames() */
10858 : /************************************************************************/
10859 :
10860 : /** Return the list of layer names contained in this group.
10861 : *
10862 : * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
10863 : *
10864 : * @return the group names, to be freed with CSLDestroy()
10865 : * @since 3.4
10866 : */
10867 8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
10868 : CSLConstList papszOptions)
10869 : {
10870 8 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10871 16 : auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
10872 16 : CPLStringList res;
10873 18 : for (const auto &name : names)
10874 : {
10875 10 : res.AddString(name.c_str());
10876 : }
10877 8 : return res.StealList();
10878 : }
10879 :
10880 : /************************************************************************/
10881 : /* GDALGroupOpenVectorLayer() */
10882 : /************************************************************************/
10883 :
10884 : /** Open and return a vector layer.
10885 : *
10886 : * This is the same as the C++ method GDALGroup::OpenVectorLayer().
10887 : *
10888 : * Note that the vector layer is owned by its parent GDALDatasetH, and thus
10889 : * the returned handled if only valid while the parent GDALDatasetH is kept
10890 : * opened.
10891 : *
10892 : * @return the vector layer, or nullptr.
10893 : * @since 3.4
10894 : */
10895 12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
10896 : const char *pszVectorLayerName,
10897 : CSLConstList papszOptions)
10898 : {
10899 12 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10900 12 : VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
10901 24 : return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
10902 24 : std::string(pszVectorLayerName), papszOptions));
10903 : }
10904 :
10905 : /************************************************************************/
10906 : /* GDALGroupOpenMDArrayFromFullname() */
10907 : /************************************************************************/
10908 :
10909 : /** Open and return a sub-group from its fully qualified name.
10910 : *
10911 : * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
10912 : *
10913 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
10914 : *
10915 : * @since GDAL 3.2
10916 : */
10917 3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
10918 : const char *pszFullname,
10919 : CSLConstList papszOptions)
10920 : {
10921 3 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10922 3 : VALIDATE_POINTER1(pszFullname, __func__, nullptr);
10923 3 : auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
10924 9 : std::string(pszFullname), papszOptions);
10925 3 : if (!subGroup)
10926 2 : return nullptr;
10927 1 : return new GDALGroupHS(subGroup);
10928 : }
10929 :
10930 : /************************************************************************/
10931 : /* GDALGroupGetDimensions() */
10932 : /************************************************************************/
10933 :
10934 : /** Return the list of dimensions contained in this group and used by its
10935 : * arrays.
10936 : *
10937 : * The returned array must be freed with GDALReleaseDimensions(). If only the
10938 : * array itself needs to be freed, CPLFree() should be called (and
10939 : * GDALDimensionRelease() on individual array members).
10940 : *
10941 : * This is the same as the C++ method GDALGroup::GetDimensions().
10942 : *
10943 : * @param hGroup Group.
10944 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
10945 : * @param papszOptions Driver specific options determining how dimensions
10946 : * should be retrieved. Pass nullptr for default behavior.
10947 : *
10948 : * @return an array of *pnCount dimensions.
10949 : */
10950 73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
10951 : CSLConstList papszOptions)
10952 : {
10953 73 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10954 73 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
10955 73 : auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
10956 : auto ret = static_cast<GDALDimensionH *>(
10957 73 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
10958 230 : for (size_t i = 0; i < dims.size(); i++)
10959 : {
10960 157 : ret[i] = new GDALDimensionHS(dims[i]);
10961 : }
10962 73 : *pnCount = dims.size();
10963 73 : return ret;
10964 : }
10965 :
10966 : /************************************************************************/
10967 : /* GDALGroupGetAttribute() */
10968 : /************************************************************************/
10969 :
10970 : /** Return an attribute by its name.
10971 : *
10972 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
10973 : *
10974 : * The returned attribute must be freed with GDALAttributeRelease().
10975 : */
10976 63 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
10977 : {
10978 63 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
10979 63 : VALIDATE_POINTER1(pszName, __func__, nullptr);
10980 189 : auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
10981 63 : if (attr)
10982 59 : return new GDALAttributeHS(attr);
10983 4 : return nullptr;
10984 : }
10985 :
10986 : /************************************************************************/
10987 : /* GDALGroupGetAttributes() */
10988 : /************************************************************************/
10989 :
10990 : /** Return the list of attributes contained in this group.
10991 : *
10992 : * The returned array must be freed with GDALReleaseAttributes(). If only the
10993 : * array itself needs to be freed, CPLFree() should be called (and
10994 : * GDALAttributeRelease() on individual array members).
10995 : *
10996 : * This is the same as the C++ method GDALGroup::GetAttributes().
10997 : *
10998 : * @param hGroup Group.
10999 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11000 : * @param papszOptions Driver specific options determining how attributes
11001 : * should be retrieved. Pass nullptr for default behavior.
11002 : *
11003 : * @return an array of *pnCount attributes.
11004 : */
11005 67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11006 : CSLConstList papszOptions)
11007 : {
11008 67 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11009 67 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11010 67 : auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11011 : auto ret = static_cast<GDALAttributeH *>(
11012 67 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11013 221 : for (size_t i = 0; i < attrs.size(); i++)
11014 : {
11015 154 : ret[i] = new GDALAttributeHS(attrs[i]);
11016 : }
11017 67 : *pnCount = attrs.size();
11018 67 : return ret;
11019 : }
11020 :
11021 : /************************************************************************/
11022 : /* GDALGroupGetStructuralInfo() */
11023 : /************************************************************************/
11024 :
11025 : /** Return structural information on the group.
11026 : *
11027 : * This may be the compression, etc..
11028 : *
11029 : * The return value should not be freed and is valid until GDALGroup is
11030 : * released or this function called again.
11031 : *
11032 : * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11033 : */
11034 4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11035 : {
11036 4 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11037 4 : return hGroup->m_poImpl->GetStructuralInfo();
11038 : }
11039 :
11040 : /************************************************************************/
11041 : /* GDALReleaseAttributes() */
11042 : /************************************************************************/
11043 :
11044 : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11045 : *
11046 : * @param attributes return pointer of above methods
11047 : * @param nCount *pnCount value returned by above methods
11048 : */
11049 124 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11050 : {
11051 406 : for (size_t i = 0; i < nCount; i++)
11052 : {
11053 282 : delete attributes[i];
11054 : }
11055 124 : CPLFree(attributes);
11056 124 : }
11057 :
11058 : /************************************************************************/
11059 : /* GDALGroupCreateGroup() */
11060 : /************************************************************************/
11061 :
11062 : /** Create a sub-group within a group.
11063 : *
11064 : * This is the same as the C++ method GDALGroup::CreateGroup().
11065 : *
11066 : * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11067 : */
11068 173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11069 : CSLConstList papszOptions)
11070 : {
11071 173 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11072 173 : VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11073 519 : auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11074 519 : papszOptions);
11075 173 : if (!ret)
11076 49 : return nullptr;
11077 124 : return new GDALGroupHS(ret);
11078 : }
11079 :
11080 : /************************************************************************/
11081 : /* GDALGroupDeleteGroup() */
11082 : /************************************************************************/
11083 :
11084 : /** Delete a sub-group from a group.
11085 : *
11086 : * After this call, if a previously obtained instance of the deleted object
11087 : * is still alive, no method other than for freeing it should be invoked.
11088 : *
11089 : * This is the same as the C++ method GDALGroup::DeleteGroup().
11090 : *
11091 : * @return true in case of success.
11092 : * @since GDAL 3.8
11093 : */
11094 20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11095 : CSLConstList papszOptions)
11096 : {
11097 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11098 20 : VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11099 40 : return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11100 20 : papszOptions);
11101 : }
11102 :
11103 : /************************************************************************/
11104 : /* GDALGroupCreateDimension() */
11105 : /************************************************************************/
11106 :
11107 : /** Create a dimension within a group.
11108 : *
11109 : * This is the same as the C++ method GDALGroup::CreateDimension().
11110 : *
11111 : * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11112 : */
11113 652 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11114 : const char *pszType,
11115 : const char *pszDirection, GUInt64 nSize,
11116 : CSLConstList papszOptions)
11117 : {
11118 652 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11119 652 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11120 652 : auto ret = hGroup->m_poImpl->CreateDimension(
11121 1304 : std::string(pszName), std::string(pszType ? pszType : ""),
11122 2608 : std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11123 652 : if (!ret)
11124 9 : return nullptr;
11125 643 : return new GDALDimensionHS(ret);
11126 : }
11127 :
11128 : /************************************************************************/
11129 : /* GDALGroupCreateMDArray() */
11130 : /************************************************************************/
11131 :
11132 : /** Create a multidimensional array within a group.
11133 : *
11134 : * This is the same as the C++ method GDALGroup::CreateMDArray().
11135 : *
11136 : * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11137 : */
11138 590 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11139 : size_t nDimensions,
11140 : GDALDimensionH *pahDimensions,
11141 : GDALExtendedDataTypeH hEDT,
11142 : CSLConstList papszOptions)
11143 : {
11144 590 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11145 590 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11146 590 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11147 1180 : std::vector<std::shared_ptr<GDALDimension>> dims;
11148 590 : dims.reserve(nDimensions);
11149 1393 : for (size_t i = 0; i < nDimensions; i++)
11150 803 : dims.push_back(pahDimensions[i]->m_poImpl);
11151 1770 : auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11152 1770 : *(hEDT->m_poImpl), papszOptions);
11153 590 : if (!ret)
11154 65 : return nullptr;
11155 525 : return new GDALMDArrayHS(ret);
11156 : }
11157 :
11158 : /************************************************************************/
11159 : /* GDALGroupDeleteMDArray() */
11160 : /************************************************************************/
11161 :
11162 : /** Delete an array from a group.
11163 : *
11164 : * After this call, if a previously obtained instance of the deleted object
11165 : * is still alive, no method other than for freeing it should be invoked.
11166 : *
11167 : * This is the same as the C++ method GDALGroup::DeleteMDArray().
11168 : *
11169 : * @return true in case of success.
11170 : * @since GDAL 3.8
11171 : */
11172 20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
11173 : CSLConstList papszOptions)
11174 : {
11175 20 : VALIDATE_POINTER1(hGroup, __func__, false);
11176 20 : VALIDATE_POINTER1(pszName, __func__, false);
11177 20 : return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
11178 : }
11179 :
11180 : /************************************************************************/
11181 : /* GDALGroupCreateAttribute() */
11182 : /************************************************************************/
11183 :
11184 : /** Create a attribute within a group.
11185 : *
11186 : * This is the same as the C++ method GDALGroup::CreateAttribute().
11187 : *
11188 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11189 : */
11190 105 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
11191 : size_t nDimensions,
11192 : const GUInt64 *panDimensions,
11193 : GDALExtendedDataTypeH hEDT,
11194 : CSLConstList papszOptions)
11195 : {
11196 105 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11197 105 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11198 210 : std::vector<GUInt64> dims;
11199 105 : dims.reserve(nDimensions);
11200 142 : for (size_t i = 0; i < nDimensions; i++)
11201 37 : dims.push_back(panDimensions[i]);
11202 105 : auto ret = hGroup->m_poImpl->CreateAttribute(
11203 315 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11204 105 : if (!ret)
11205 14 : return nullptr;
11206 91 : return new GDALAttributeHS(ret);
11207 : }
11208 :
11209 : /************************************************************************/
11210 : /* GDALGroupDeleteAttribute() */
11211 : /************************************************************************/
11212 :
11213 : /** Delete an attribute from a group.
11214 : *
11215 : * After this call, if a previously obtained instance of the deleted object
11216 : * is still alive, no method other than for freeing it should be invoked.
11217 : *
11218 : * This is the same as the C++ method GDALGroup::DeleteAttribute().
11219 : *
11220 : * @return true in case of success.
11221 : * @since GDAL 3.8
11222 : */
11223 25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
11224 : CSLConstList papszOptions)
11225 : {
11226 25 : VALIDATE_POINTER1(hGroup, __func__, false);
11227 25 : VALIDATE_POINTER1(pszName, __func__, false);
11228 50 : return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
11229 25 : papszOptions);
11230 : }
11231 :
11232 : /************************************************************************/
11233 : /* GDALGroupRename() */
11234 : /************************************************************************/
11235 :
11236 : /** Rename the group.
11237 : *
11238 : * This is not implemented by all drivers.
11239 : *
11240 : * Drivers known to implement it: MEM, netCDF.
11241 : *
11242 : * This is the same as the C++ method GDALGroup::Rename()
11243 : *
11244 : * @return true in case of success
11245 : * @since GDAL 3.8
11246 : */
11247 45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
11248 : {
11249 45 : VALIDATE_POINTER1(hGroup, __func__, false);
11250 45 : VALIDATE_POINTER1(pszNewName, __func__, false);
11251 45 : return hGroup->m_poImpl->Rename(pszNewName);
11252 : }
11253 :
11254 : /************************************************************************/
11255 : /* GDALGroupSubsetDimensionFromSelection() */
11256 : /************************************************************************/
11257 :
11258 : /** Return a virtual group whose one dimension has been subset according to a
11259 : * selection.
11260 : *
11261 : * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
11262 : *
11263 : * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
11264 : */
11265 : GDALGroupH
11266 14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
11267 : const char *pszSelection,
11268 : CPL_UNUSED CSLConstList papszOptions)
11269 : {
11270 14 : VALIDATE_POINTER1(hGroup, __func__, nullptr);
11271 14 : VALIDATE_POINTER1(pszSelection, __func__, nullptr);
11272 14 : auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
11273 42 : std::string(pszSelection));
11274 14 : if (!hNewGroup)
11275 8 : return nullptr;
11276 6 : return new GDALGroupHS(hNewGroup);
11277 : }
11278 :
11279 : /************************************************************************/
11280 : /* GDALMDArrayRelease() */
11281 : /************************************************************************/
11282 :
11283 : /** Release the GDAL in-memory object associated with a GDALMDArray.
11284 : *
11285 : * Note: when applied on a object coming from a driver, this does not
11286 : * destroy the object in the file, database, etc...
11287 : */
11288 1935 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
11289 : {
11290 1935 : delete hMDArray;
11291 1935 : }
11292 :
11293 : /************************************************************************/
11294 : /* GDALMDArrayGetName() */
11295 : /************************************************************************/
11296 :
11297 : /** Return array name.
11298 : *
11299 : * This is the same as the C++ method GDALMDArray::GetName()
11300 : */
11301 83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
11302 : {
11303 83 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11304 83 : return hArray->m_poImpl->GetName().c_str();
11305 : }
11306 :
11307 : /************************************************************************/
11308 : /* GDALMDArrayGetFullName() */
11309 : /************************************************************************/
11310 :
11311 : /** Return array full name.
11312 : *
11313 : * This is the same as the C++ method GDALMDArray::GetFullName()
11314 : */
11315 50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
11316 : {
11317 50 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11318 50 : return hArray->m_poImpl->GetFullName().c_str();
11319 : }
11320 :
11321 : /************************************************************************/
11322 : /* GDALMDArrayGetName() */
11323 : /************************************************************************/
11324 :
11325 : /** Return the total number of values in the array.
11326 : *
11327 : * This is the same as the C++ method
11328 : * GDALAbstractMDArray::GetTotalElementsCount()
11329 : */
11330 6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
11331 : {
11332 6 : VALIDATE_POINTER1(hArray, __func__, 0);
11333 6 : return hArray->m_poImpl->GetTotalElementsCount();
11334 : }
11335 :
11336 : /************************************************************************/
11337 : /* GDALMDArrayGetDimensionCount() */
11338 : /************************************************************************/
11339 :
11340 : /** Return the number of dimensions.
11341 : *
11342 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
11343 : */
11344 10002 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
11345 : {
11346 10002 : VALIDATE_POINTER1(hArray, __func__, 0);
11347 10002 : return hArray->m_poImpl->GetDimensionCount();
11348 : }
11349 :
11350 : /************************************************************************/
11351 : /* GDALMDArrayGetDimensions() */
11352 : /************************************************************************/
11353 :
11354 : /** Return the dimensions of the array
11355 : *
11356 : * The returned array must be freed with GDALReleaseDimensions(). If only the
11357 : * array itself needs to be freed, CPLFree() should be called (and
11358 : * GDALDimensionRelease() on individual array members).
11359 : *
11360 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
11361 : *
11362 : * @param hArray Array.
11363 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11364 : *
11365 : * @return an array of *pnCount dimensions.
11366 : */
11367 2199 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
11368 : {
11369 2199 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11370 2199 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11371 2199 : const auto &dims(hArray->m_poImpl->GetDimensions());
11372 : auto ret = static_cast<GDALDimensionH *>(
11373 2199 : CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11374 6201 : for (size_t i = 0; i < dims.size(); i++)
11375 : {
11376 4002 : ret[i] = new GDALDimensionHS(dims[i]);
11377 : }
11378 2199 : *pnCount = dims.size();
11379 2199 : return ret;
11380 : }
11381 :
11382 : /************************************************************************/
11383 : /* GDALReleaseDimensions() */
11384 : /************************************************************************/
11385 :
11386 : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
11387 : *
11388 : * @param dims return pointer of above methods
11389 : * @param nCount *pnCount value returned by above methods
11390 : */
11391 2272 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
11392 : {
11393 6431 : for (size_t i = 0; i < nCount; i++)
11394 : {
11395 4159 : delete dims[i];
11396 : }
11397 2272 : CPLFree(dims);
11398 2272 : }
11399 :
11400 : /************************************************************************/
11401 : /* GDALMDArrayGetDataType() */
11402 : /************************************************************************/
11403 :
11404 : /** Return the data type
11405 : *
11406 : * The return must be freed with GDALExtendedDataTypeRelease().
11407 : */
11408 3750 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
11409 : {
11410 3750 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11411 : return new GDALExtendedDataTypeHS(
11412 3750 : new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
11413 : }
11414 :
11415 : /************************************************************************/
11416 : /* GDALMDArrayRead() */
11417 : /************************************************************************/
11418 :
11419 : /** Read part or totality of a multidimensional array.
11420 : *
11421 : * This is the same as the C++ method GDALAbstractMDArray::Read()
11422 : *
11423 : * @return TRUE in case of success.
11424 : */
11425 1882 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11426 : const size_t *count, const GInt64 *arrayStep,
11427 : const GPtrDiff_t *bufferStride,
11428 : GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
11429 : const void *pDstBufferAllocStart,
11430 : size_t nDstBufferAllocSize)
11431 : {
11432 1882 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11433 1882 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11434 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11435 : {
11436 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11437 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11438 : }
11439 1882 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11440 1882 : VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
11441 : // coverity[var_deref_model]
11442 3764 : return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
11443 1882 : *(bufferDataType->m_poImpl), pDstBuffer,
11444 1882 : pDstBufferAllocStart, nDstBufferAllocSize);
11445 : }
11446 :
11447 : /************************************************************************/
11448 : /* GDALMDArrayWrite() */
11449 : /************************************************************************/
11450 :
11451 : /** Write part or totality of a multidimensional array.
11452 : *
11453 : * This is the same as the C++ method GDALAbstractMDArray::Write()
11454 : *
11455 : * @return TRUE in case of success.
11456 : */
11457 547 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11458 : const size_t *count, const GInt64 *arrayStep,
11459 : const GPtrDiff_t *bufferStride,
11460 : GDALExtendedDataTypeH bufferDataType,
11461 : const void *pSrcBuffer, const void *pSrcBufferAllocStart,
11462 : size_t nSrcBufferAllocSize)
11463 : {
11464 547 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11465 547 : if ((arrayStartIdx == nullptr || count == nullptr) &&
11466 0 : hArray->m_poImpl->GetDimensionCount() > 0)
11467 : {
11468 0 : VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
11469 0 : VALIDATE_POINTER1(count, __func__, FALSE);
11470 : }
11471 547 : VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
11472 547 : VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
11473 : // coverity[var_deref_model]
11474 1094 : return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
11475 547 : bufferStride, *(bufferDataType->m_poImpl),
11476 : pSrcBuffer, pSrcBufferAllocStart,
11477 547 : nSrcBufferAllocSize);
11478 : }
11479 :
11480 : /************************************************************************/
11481 : /* GDALMDArrayAdviseRead() */
11482 : /************************************************************************/
11483 :
11484 : /** Advise driver of upcoming read requests.
11485 : *
11486 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11487 : *
11488 : * @return TRUE in case of success.
11489 : *
11490 : * @since GDAL 3.2
11491 : */
11492 0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11493 : const size_t *count)
11494 : {
11495 0 : return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
11496 : }
11497 :
11498 : /************************************************************************/
11499 : /* GDALMDArrayAdviseReadEx() */
11500 : /************************************************************************/
11501 :
11502 : /** Advise driver of upcoming read requests.
11503 : *
11504 : * This is the same as the C++ method GDALMDArray::AdviseRead()
11505 : *
11506 : * @return TRUE in case of success.
11507 : *
11508 : * @since GDAL 3.4
11509 : */
11510 22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
11511 : const size_t *count, CSLConstList papszOptions)
11512 : {
11513 22 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11514 : // coverity[var_deref_model]
11515 22 : return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
11516 : }
11517 :
11518 : /************************************************************************/
11519 : /* GDALMDArrayGetAttribute() */
11520 : /************************************************************************/
11521 :
11522 : /** Return an attribute by its name.
11523 : *
11524 : * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11525 : *
11526 : * The returned attribute must be freed with GDALAttributeRelease().
11527 : */
11528 118 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
11529 : {
11530 118 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11531 118 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11532 354 : auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
11533 118 : if (attr)
11534 109 : return new GDALAttributeHS(attr);
11535 9 : return nullptr;
11536 : }
11537 :
11538 : /************************************************************************/
11539 : /* GDALMDArrayGetAttributes() */
11540 : /************************************************************************/
11541 :
11542 : /** Return the list of attributes contained in this array.
11543 : *
11544 : * The returned array must be freed with GDALReleaseAttributes(). If only the
11545 : * array itself needs to be freed, CPLFree() should be called (and
11546 : * GDALAttributeRelease() on individual array members).
11547 : *
11548 : * This is the same as the C++ method GDALMDArray::GetAttributes().
11549 : *
11550 : * @param hArray Array.
11551 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11552 : * @param papszOptions Driver specific options determining how attributes
11553 : * should be retrieved. Pass nullptr for default behavior.
11554 : *
11555 : * @return an array of *pnCount attributes.
11556 : */
11557 57 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
11558 : CSLConstList papszOptions)
11559 : {
11560 57 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11561 57 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
11562 57 : auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
11563 : auto ret = static_cast<GDALAttributeH *>(
11564 57 : CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11565 185 : for (size_t i = 0; i < attrs.size(); i++)
11566 : {
11567 128 : ret[i] = new GDALAttributeHS(attrs[i]);
11568 : }
11569 57 : *pnCount = attrs.size();
11570 57 : return ret;
11571 : }
11572 :
11573 : /************************************************************************/
11574 : /* GDALMDArrayCreateAttribute() */
11575 : /************************************************************************/
11576 :
11577 : /** Create a attribute within an array.
11578 : *
11579 : * This is the same as the C++ method GDALMDArray::CreateAttribute().
11580 : *
11581 : * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
11582 : */
11583 148 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
11584 : const char *pszName,
11585 : size_t nDimensions,
11586 : const GUInt64 *panDimensions,
11587 : GDALExtendedDataTypeH hEDT,
11588 : CSLConstList papszOptions)
11589 : {
11590 148 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11591 148 : VALIDATE_POINTER1(pszName, __func__, nullptr);
11592 148 : VALIDATE_POINTER1(hEDT, __func__, nullptr);
11593 296 : std::vector<GUInt64> dims;
11594 148 : dims.reserve(nDimensions);
11595 173 : for (size_t i = 0; i < nDimensions; i++)
11596 25 : dims.push_back(panDimensions[i]);
11597 148 : auto ret = hArray->m_poImpl->CreateAttribute(
11598 444 : std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
11599 148 : if (!ret)
11600 9 : return nullptr;
11601 139 : return new GDALAttributeHS(ret);
11602 : }
11603 :
11604 : /************************************************************************/
11605 : /* GDALMDArrayDeleteAttribute() */
11606 : /************************************************************************/
11607 :
11608 : /** Delete an attribute from an array.
11609 : *
11610 : * After this call, if a previously obtained instance of the deleted object
11611 : * is still alive, no method other than for freeing it should be invoked.
11612 : *
11613 : * This is the same as the C++ method GDALMDArray::DeleteAttribute().
11614 : *
11615 : * @return true in case of success.
11616 : * @since GDAL 3.8
11617 : */
11618 24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
11619 : CSLConstList papszOptions)
11620 : {
11621 24 : VALIDATE_POINTER1(hArray, __func__, false);
11622 24 : VALIDATE_POINTER1(pszName, __func__, false);
11623 48 : return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
11624 24 : papszOptions);
11625 : }
11626 :
11627 : /************************************************************************/
11628 : /* GDALMDArrayGetRawNoDataValue() */
11629 : /************************************************************************/
11630 :
11631 : /** Return the nodata value as a "raw" value.
11632 : *
11633 : * The value returned might be nullptr in case of no nodata value. When
11634 : * a nodata value is registered, a non-nullptr will be returned whose size in
11635 : * bytes is GetDataType().GetSize().
11636 : *
11637 : * The returned value should not be modified or freed.
11638 : *
11639 : * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
11640 : *
11641 : * @return nullptr or a pointer to GetDataType().GetSize() bytes.
11642 : */
11643 74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
11644 : {
11645 74 : VALIDATE_POINTER1(hArray, __func__, nullptr);
11646 74 : return hArray->m_poImpl->GetRawNoDataValue();
11647 : }
11648 :
11649 : /************************************************************************/
11650 : /* GDALMDArrayGetNoDataValueAsDouble() */
11651 : /************************************************************************/
11652 :
11653 : /** Return the nodata value as a double.
11654 : *
11655 : * The value returned might be nullptr in case of no nodata value. When
11656 : * a nodata value is registered, a non-nullptr will be returned whose size in
11657 : * bytes is GetDataType().GetSize().
11658 : *
11659 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
11660 : *
11661 : * @param hArray Array handle.
11662 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11663 : * if a nodata value exists and can be converted to double. Might be nullptr.
11664 : *
11665 : * @return the nodata value as a double. A 0.0 value might also indicate the
11666 : * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
11667 : * will be set to false then).
11668 : */
11669 117 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
11670 : int *pbHasNoDataValue)
11671 : {
11672 117 : VALIDATE_POINTER1(hArray, __func__, 0);
11673 117 : bool bHasNodataValue = false;
11674 117 : double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
11675 117 : if (pbHasNoDataValue)
11676 117 : *pbHasNoDataValue = bHasNodataValue;
11677 117 : return ret;
11678 : }
11679 :
11680 : /************************************************************************/
11681 : /* GDALMDArrayGetNoDataValueAsInt64() */
11682 : /************************************************************************/
11683 :
11684 : /** Return the nodata value as a Int64.
11685 : *
11686 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
11687 : *
11688 : * @param hArray Array handle.
11689 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11690 : * if a nodata value exists and can be converted to Int64. Might be nullptr.
11691 : *
11692 : * @return the nodata value as a Int64.
11693 : * @since GDAL 3.5
11694 : */
11695 11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
11696 : int *pbHasNoDataValue)
11697 : {
11698 11 : VALIDATE_POINTER1(hArray, __func__, 0);
11699 11 : bool bHasNodataValue = false;
11700 11 : const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
11701 11 : if (pbHasNoDataValue)
11702 11 : *pbHasNoDataValue = bHasNodataValue;
11703 11 : return ret;
11704 : }
11705 :
11706 : /************************************************************************/
11707 : /* GDALMDArrayGetNoDataValueAsUInt64() */
11708 : /************************************************************************/
11709 :
11710 : /** Return the nodata value as a UInt64.
11711 : *
11712 : * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
11713 : *
11714 : * @param hArray Array handle.
11715 : * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
11716 : * if a nodata value exists and can be converted to UInt64. Might be nullptr.
11717 : *
11718 : * @return the nodata value as a UInt64.
11719 : * @since GDAL 3.5
11720 : */
11721 7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
11722 : int *pbHasNoDataValue)
11723 : {
11724 7 : VALIDATE_POINTER1(hArray, __func__, 0);
11725 7 : bool bHasNodataValue = false;
11726 7 : const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
11727 7 : if (pbHasNoDataValue)
11728 7 : *pbHasNoDataValue = bHasNodataValue;
11729 7 : return ret;
11730 : }
11731 :
11732 : /************************************************************************/
11733 : /* GDALMDArraySetRawNoDataValue() */
11734 : /************************************************************************/
11735 :
11736 : /** Set the nodata value as a "raw" value.
11737 : *
11738 : * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
11739 : * void*).
11740 : *
11741 : * @return TRUE in case of success.
11742 : */
11743 14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
11744 : {
11745 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11746 14 : return hArray->m_poImpl->SetRawNoDataValue(pNoData);
11747 : }
11748 :
11749 : /************************************************************************/
11750 : /* GDALMDArraySetNoDataValueAsDouble() */
11751 : /************************************************************************/
11752 :
11753 : /** Set the nodata value as a double.
11754 : *
11755 : * If the natural data type of the attribute/array is not double, type
11756 : * conversion will occur to the type returned by GetDataType().
11757 : *
11758 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
11759 : *
11760 : * @return TRUE in case of success.
11761 : */
11762 50 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
11763 : {
11764 50 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11765 50 : return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
11766 : }
11767 :
11768 : /************************************************************************/
11769 : /* GDALMDArraySetNoDataValueAsInt64() */
11770 : /************************************************************************/
11771 :
11772 : /** Set the nodata value as a Int64.
11773 : *
11774 : * If the natural data type of the attribute/array is not Int64, type conversion
11775 : * will occur to the type returned by GetDataType().
11776 : *
11777 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
11778 : *
11779 : * @return TRUE in case of success.
11780 : * @since GDAL 3.5
11781 : */
11782 1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
11783 : {
11784 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11785 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
11786 : }
11787 :
11788 : /************************************************************************/
11789 : /* GDALMDArraySetNoDataValueAsUInt64() */
11790 : /************************************************************************/
11791 :
11792 : /** Set the nodata value as a UInt64.
11793 : *
11794 : * If the natural data type of the attribute/array is not UInt64, type
11795 : * conversion will occur to the type returned by GetDataType().
11796 : *
11797 : * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
11798 : *
11799 : * @return TRUE in case of success.
11800 : * @since GDAL 3.5
11801 : */
11802 1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
11803 : uint64_t nNoDataValue)
11804 : {
11805 1 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11806 1 : return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
11807 : }
11808 :
11809 : /************************************************************************/
11810 : /* GDALMDArrayResize() */
11811 : /************************************************************************/
11812 :
11813 : /** Resize an array to new dimensions.
11814 : *
11815 : * Not all drivers may allow this operation, and with restrictions (e.g.
11816 : * for netCDF, this is limited to growing of "unlimited" dimensions)
11817 : *
11818 : * Resizing a dimension used in other arrays will cause those other arrays
11819 : * to be resized.
11820 : *
11821 : * This is the same as the C++ method GDALMDArray::Resize().
11822 : *
11823 : * @param hArray Array.
11824 : * @param panNewDimSizes Array of GetDimensionCount() values containing the
11825 : * new size of each indexing dimension.
11826 : * @param papszOptions Options. (Driver specific)
11827 : * @return true in case of success.
11828 : * @since GDAL 3.7
11829 : */
11830 42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
11831 : CSLConstList papszOptions)
11832 : {
11833 42 : VALIDATE_POINTER1(hArray, __func__, false);
11834 42 : VALIDATE_POINTER1(panNewDimSizes, __func__, false);
11835 84 : std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
11836 125 : for (size_t i = 0; i < anNewDimSizes.size(); ++i)
11837 : {
11838 83 : anNewDimSizes[i] = panNewDimSizes[i];
11839 : }
11840 42 : return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
11841 : }
11842 :
11843 : /************************************************************************/
11844 : /* GDALMDArraySetScale() */
11845 : /************************************************************************/
11846 :
11847 : /** Set the scale value to apply to raw values.
11848 : *
11849 : * unscaled_value = raw_value * GetScale() + GetOffset()
11850 : *
11851 : * This is the same as the C++ method GDALMDArray::SetScale().
11852 : *
11853 : * @return TRUE in case of success.
11854 : */
11855 0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
11856 : {
11857 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11858 0 : return hArray->m_poImpl->SetScale(dfScale);
11859 : }
11860 :
11861 : /************************************************************************/
11862 : /* GDALMDArraySetScaleEx() */
11863 : /************************************************************************/
11864 :
11865 : /** Set the scale value to apply to raw values.
11866 : *
11867 : * unscaled_value = raw_value * GetScale() + GetOffset()
11868 : *
11869 : * This is the same as the C++ method GDALMDArray::SetScale().
11870 : *
11871 : * @return TRUE in case of success.
11872 : * @since GDAL 3.3
11873 : */
11874 20 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
11875 : GDALDataType eStorageType)
11876 : {
11877 20 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11878 20 : return hArray->m_poImpl->SetScale(dfScale, eStorageType);
11879 : }
11880 :
11881 : /************************************************************************/
11882 : /* GDALMDArraySetOffset() */
11883 : /************************************************************************/
11884 :
11885 : /** Set the scale value to apply to raw values.
11886 : *
11887 : * unscaled_value = raw_value * GetScale() + GetOffset()
11888 : *
11889 : * This is the same as the C++ method GDALMDArray::SetOffset().
11890 : *
11891 : * @return TRUE in case of success.
11892 : */
11893 0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
11894 : {
11895 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11896 0 : return hArray->m_poImpl->SetOffset(dfOffset);
11897 : }
11898 :
11899 : /************************************************************************/
11900 : /* GDALMDArraySetOffsetEx() */
11901 : /************************************************************************/
11902 :
11903 : /** Set the scale value to apply to raw values.
11904 : *
11905 : * unscaled_value = raw_value * GetOffset() + GetOffset()
11906 : *
11907 : * This is the same as the C++ method GDALMDArray::SetOffset().
11908 : *
11909 : * @return TRUE in case of success.
11910 : * @since GDAL 3.3
11911 : */
11912 20 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
11913 : GDALDataType eStorageType)
11914 : {
11915 20 : VALIDATE_POINTER1(hArray, __func__, FALSE);
11916 20 : return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
11917 : }
11918 :
11919 : /************************************************************************/
11920 : /* GDALMDArrayGetScale() */
11921 : /************************************************************************/
11922 :
11923 : /** Get the scale value to apply to raw values.
11924 : *
11925 : * unscaled_value = raw_value * GetScale() + GetOffset()
11926 : *
11927 : * This is the same as the C++ method GDALMDArray::GetScale().
11928 : *
11929 : * @return the scale value
11930 : */
11931 102 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
11932 : {
11933 102 : VALIDATE_POINTER1(hArray, __func__, 0.0);
11934 102 : bool bHasValue = false;
11935 102 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
11936 102 : if (pbHasValue)
11937 102 : *pbHasValue = bHasValue;
11938 102 : return dfRet;
11939 : }
11940 :
11941 : /************************************************************************/
11942 : /* GDALMDArrayGetScaleEx() */
11943 : /************************************************************************/
11944 :
11945 : /** Get the scale value to apply to raw values.
11946 : *
11947 : * unscaled_value = raw_value * GetScale() + GetScale()
11948 : *
11949 : * This is the same as the C++ method GDALMDArray::GetScale().
11950 : *
11951 : * @return the scale value
11952 : * @since GDAL 3.3
11953 : */
11954 5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
11955 : GDALDataType *peStorageType)
11956 : {
11957 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
11958 5 : bool bHasValue = false;
11959 5 : double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
11960 5 : if (pbHasValue)
11961 5 : *pbHasValue = bHasValue;
11962 5 : return dfRet;
11963 : }
11964 :
11965 : /************************************************************************/
11966 : /* GDALMDArrayGetOffset() */
11967 : /************************************************************************/
11968 :
11969 : /** Get the scale value to apply to raw values.
11970 : *
11971 : * unscaled_value = raw_value * GetScale() + GetOffset()
11972 : *
11973 : * This is the same as the C++ method GDALMDArray::GetOffset().
11974 : *
11975 : * @return the scale value
11976 : */
11977 99 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
11978 : {
11979 99 : VALIDATE_POINTER1(hArray, __func__, 0.0);
11980 99 : bool bHasValue = false;
11981 99 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
11982 99 : if (pbHasValue)
11983 99 : *pbHasValue = bHasValue;
11984 99 : return dfRet;
11985 : }
11986 :
11987 : /************************************************************************/
11988 : /* GDALMDArrayGetOffsetEx() */
11989 : /************************************************************************/
11990 :
11991 : /** Get the scale value to apply to raw values.
11992 : *
11993 : * unscaled_value = raw_value * GetScale() + GetOffset()
11994 : *
11995 : * This is the same as the C++ method GDALMDArray::GetOffset().
11996 : *
11997 : * @return the scale value
11998 : * @since GDAL 3.3
11999 : */
12000 5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12001 : GDALDataType *peStorageType)
12002 : {
12003 5 : VALIDATE_POINTER1(hArray, __func__, 0.0);
12004 5 : bool bHasValue = false;
12005 5 : double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12006 5 : if (pbHasValue)
12007 5 : *pbHasValue = bHasValue;
12008 5 : return dfRet;
12009 : }
12010 :
12011 : /************************************************************************/
12012 : /* GDALMDArrayGetBlockSize() */
12013 : /************************************************************************/
12014 :
12015 : /** Return the "natural" block size of the array along all dimensions.
12016 : *
12017 : * Some drivers might organize the array in tiles/blocks and reading/writing
12018 : * aligned on those tile/block boundaries will be more efficient.
12019 : *
12020 : * The returned number of elements in the vector is the same as
12021 : * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12022 : * the natural block size along the considered dimension.
12023 : * "Flat" arrays will typically return a vector of values set to 0.
12024 : *
12025 : * The default implementation will return a vector of values set to 0.
12026 : *
12027 : * This method is used by GetProcessingChunkSize().
12028 : *
12029 : * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12030 : * theoretical case of a 32-bit platform, this might exceed its size_t
12031 : * allocation capabilities.
12032 : *
12033 : * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12034 : *
12035 : * @return the block size, in number of elements along each dimension.
12036 : */
12037 93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12038 : {
12039 93 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12040 93 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12041 93 : auto res = hArray->m_poImpl->GetBlockSize();
12042 93 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12043 285 : for (size_t i = 0; i < res.size(); i++)
12044 : {
12045 192 : ret[i] = res[i];
12046 : }
12047 93 : *pnCount = res.size();
12048 93 : return ret;
12049 : }
12050 :
12051 : /***********************************************************************/
12052 : /* GDALMDArrayGetProcessingChunkSize() */
12053 : /************************************************************************/
12054 :
12055 : /** \brief Return an optimal chunk size for read/write operations, given the
12056 : * natural block size and memory constraints specified.
12057 : *
12058 : * This method will use GetBlockSize() to define a chunk whose dimensions are
12059 : * multiple of those returned by GetBlockSize() (unless the block define by
12060 : * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12061 : * returned by this method).
12062 : *
12063 : * This is the same as the C++ method
12064 : * GDALAbstractMDArray::GetProcessingChunkSize().
12065 : *
12066 : * @param hArray Array.
12067 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12068 : * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12069 : * chunk.
12070 : *
12071 : * @return the chunk size, in number of elements along each dimension.
12072 : */
12073 :
12074 1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12075 : size_t nMaxChunkMemory)
12076 : {
12077 1 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12078 1 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12079 1 : auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12080 1 : auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12081 3 : for (size_t i = 0; i < res.size(); i++)
12082 : {
12083 2 : ret[i] = res[i];
12084 : }
12085 1 : *pnCount = res.size();
12086 1 : return ret;
12087 : }
12088 :
12089 : /************************************************************************/
12090 : /* GDALMDArrayGetStructuralInfo() */
12091 : /************************************************************************/
12092 :
12093 : /** Return structural information on the array.
12094 : *
12095 : * This may be the compression, etc..
12096 : *
12097 : * The return value should not be freed and is valid until GDALMDArray is
12098 : * released or this function called again.
12099 : *
12100 : * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12101 : */
12102 5 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12103 : {
12104 5 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12105 5 : return hArray->m_poImpl->GetStructuralInfo();
12106 : }
12107 :
12108 : /************************************************************************/
12109 : /* GDALMDArrayGetView() */
12110 : /************************************************************************/
12111 :
12112 : /** Return a view of the array using slicing or field access.
12113 : *
12114 : * The returned object should be released with GDALMDArrayRelease().
12115 : *
12116 : * This is the same as the C++ method GDALMDArray::GetView().
12117 : */
12118 430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12119 : {
12120 430 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12121 430 : VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12122 1290 : auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12123 430 : if (!sliced)
12124 22 : return nullptr;
12125 408 : return new GDALMDArrayHS(sliced);
12126 : }
12127 :
12128 : /************************************************************************/
12129 : /* GDALMDArrayTranspose() */
12130 : /************************************************************************/
12131 :
12132 : /** Return a view of the array whose axis have been reordered.
12133 : *
12134 : * The returned object should be released with GDALMDArrayRelease().
12135 : *
12136 : * This is the same as the C++ method GDALMDArray::Transpose().
12137 : */
12138 44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12139 : const int *panMapNewAxisToOldAxis)
12140 : {
12141 44 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12142 88 : std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12143 44 : if (nNewAxisCount)
12144 : {
12145 43 : memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12146 : nNewAxisCount * sizeof(int));
12147 : }
12148 88 : auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12149 44 : if (!reordered)
12150 7 : return nullptr;
12151 37 : return new GDALMDArrayHS(reordered);
12152 : }
12153 :
12154 : /************************************************************************/
12155 : /* GDALMDArrayGetUnscaled() */
12156 : /************************************************************************/
12157 :
12158 : /** Return an array that is the unscaled version of the current one.
12159 : *
12160 : * That is each value of the unscaled array will be
12161 : * unscaled_value = raw_value * GetScale() + GetOffset()
12162 : *
12163 : * Starting with GDAL 3.3, the Write() method is implemented and will convert
12164 : * from unscaled values to raw values.
12165 : *
12166 : * The returned object should be released with GDALMDArrayRelease().
12167 : *
12168 : * This is the same as the C++ method GDALMDArray::GetUnscaled().
12169 : */
12170 13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
12171 : {
12172 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12173 26 : auto unscaled = hArray->m_poImpl->GetUnscaled();
12174 13 : if (!unscaled)
12175 0 : return nullptr;
12176 13 : return new GDALMDArrayHS(unscaled);
12177 : }
12178 :
12179 : /************************************************************************/
12180 : /* GDALMDArrayGetMask() */
12181 : /************************************************************************/
12182 :
12183 : /** Return an array that is a mask for the current array
12184 : *
12185 : * This array will be of type Byte, with values set to 0 to indicate invalid
12186 : * pixels of the current array, and values set to 1 to indicate valid pixels.
12187 : *
12188 : * The returned object should be released with GDALMDArrayRelease().
12189 : *
12190 : * This is the same as the C++ method GDALMDArray::GetMask().
12191 : */
12192 35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
12193 : {
12194 35 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12195 70 : auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
12196 35 : if (!unscaled)
12197 7 : return nullptr;
12198 28 : return new GDALMDArrayHS(unscaled);
12199 : }
12200 :
12201 : /************************************************************************/
12202 : /* GDALMDArrayGetResampled() */
12203 : /************************************************************************/
12204 :
12205 : /** Return an array that is a resampled / reprojected view of the current array
12206 : *
12207 : * This is the same as the C++ method GDALMDArray::GetResampled().
12208 : *
12209 : * Currently this method can only resample along the last 2 dimensions, unless
12210 : * orthorectifying a NASA EMIT dataset.
12211 : *
12212 : * The returned object should be released with GDALMDArrayRelease().
12213 : *
12214 : * @since 3.4
12215 : */
12216 30 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
12217 : const GDALDimensionH *pahNewDims,
12218 : GDALRIOResampleAlg resampleAlg,
12219 : OGRSpatialReferenceH hTargetSRS,
12220 : CSLConstList papszOptions)
12221 : {
12222 30 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12223 30 : VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
12224 60 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
12225 96 : for (size_t i = 0; i < nNewDimCount; ++i)
12226 : {
12227 66 : if (pahNewDims[i])
12228 8 : apoNewDims[i] = pahNewDims[i]->m_poImpl;
12229 : }
12230 30 : auto poNewArray = hArray->m_poImpl->GetResampled(
12231 30 : apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
12232 60 : papszOptions);
12233 30 : if (!poNewArray)
12234 8 : return nullptr;
12235 22 : return new GDALMDArrayHS(poNewArray);
12236 : }
12237 :
12238 : /************************************************************************/
12239 : /* GDALMDArraySetUnit() */
12240 : /************************************************************************/
12241 :
12242 : /** Set the variable unit.
12243 : *
12244 : * Values should conform as much as possible with those allowed by
12245 : * the NetCDF CF conventions:
12246 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12247 : * but others might be returned.
12248 : *
12249 : * Few examples are "meter", "degrees", "second", ...
12250 : * Empty value means unknown.
12251 : *
12252 : * This is the same as the C function GDALMDArraySetUnit()
12253 : *
12254 : * @param hArray array.
12255 : * @param pszUnit unit name.
12256 : * @return TRUE in case of success.
12257 : */
12258 14 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
12259 : {
12260 14 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12261 14 : return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
12262 : }
12263 :
12264 : /************************************************************************/
12265 : /* GDALMDArrayGetUnit() */
12266 : /************************************************************************/
12267 :
12268 : /** Return the array unit.
12269 : *
12270 : * Values should conform as much as possible with those allowed by
12271 : * the NetCDF CF conventions:
12272 : * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
12273 : * but others might be returned.
12274 : *
12275 : * Few examples are "meter", "degrees", "second", ...
12276 : * Empty value means unknown.
12277 : *
12278 : * The return value should not be freed and is valid until GDALMDArray is
12279 : * released or this function called again.
12280 : *
12281 : * This is the same as the C++ method GDALMDArray::GetUnit().
12282 : */
12283 110 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
12284 : {
12285 110 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12286 110 : return hArray->m_poImpl->GetUnit().c_str();
12287 : }
12288 :
12289 : /************************************************************************/
12290 : /* GDALMDArrayGetSpatialRef() */
12291 : /************************************************************************/
12292 :
12293 : /** Assign a spatial reference system object to the array.
12294 : *
12295 : * This is the same as the C++ method GDALMDArray::SetSpatialRef().
12296 : * @return TRUE in case of success.
12297 : */
12298 30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
12299 : {
12300 30 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12301 60 : return hArray->m_poImpl->SetSpatialRef(
12302 60 : OGRSpatialReference::FromHandle(hSRS));
12303 : }
12304 :
12305 : /************************************************************************/
12306 : /* GDALMDArrayGetSpatialRef() */
12307 : /************************************************************************/
12308 :
12309 : /** Return the spatial reference system object associated with the array.
12310 : *
12311 : * This is the same as the C++ method GDALMDArray::GetSpatialRef().
12312 : *
12313 : * The returned object must be freed with OSRDestroySpatialReference().
12314 : */
12315 77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
12316 : {
12317 77 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12318 77 : auto poSRS = hArray->m_poImpl->GetSpatialRef();
12319 77 : return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
12320 : }
12321 :
12322 : /************************************************************************/
12323 : /* GDALMDArrayGetStatistics() */
12324 : /************************************************************************/
12325 :
12326 : /**
12327 : * \brief Fetch statistics.
12328 : *
12329 : * This is the same as the C++ method GDALMDArray::GetStatistics().
12330 : *
12331 : * @since GDAL 3.2
12332 : */
12333 :
12334 15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
12335 : int bApproxOK, int bForce, double *pdfMin,
12336 : double *pdfMax, double *pdfMean,
12337 : double *pdfStdDev, GUInt64 *pnValidCount,
12338 : GDALProgressFunc pfnProgress,
12339 : void *pProgressData)
12340 : {
12341 15 : VALIDATE_POINTER1(hArray, __func__, CE_Failure);
12342 30 : return hArray->m_poImpl->GetStatistics(
12343 15 : CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
12344 15 : pdfStdDev, pnValidCount, pfnProgress, pProgressData);
12345 : }
12346 :
12347 : /************************************************************************/
12348 : /* GDALMDArrayComputeStatistics() */
12349 : /************************************************************************/
12350 :
12351 : /**
12352 : * \brief Compute statistics.
12353 : *
12354 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12355 : *
12356 : * @since GDAL 3.2
12357 : * @see GDALMDArrayComputeStatisticsEx()
12358 : */
12359 :
12360 0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12361 : int bApproxOK, double *pdfMin, double *pdfMax,
12362 : double *pdfMean, double *pdfStdDev,
12363 : GUInt64 *pnValidCount,
12364 : GDALProgressFunc pfnProgress,
12365 : void *pProgressData)
12366 : {
12367 0 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12368 0 : return hArray->m_poImpl->ComputeStatistics(
12369 0 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12370 0 : pnValidCount, pfnProgress, pProgressData, nullptr);
12371 : }
12372 :
12373 : /************************************************************************/
12374 : /* GDALMDArrayComputeStatisticsEx() */
12375 : /************************************************************************/
12376 :
12377 : /**
12378 : * \brief Compute statistics.
12379 : *
12380 : * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
12381 : *
12382 : * This is the same as the C++ method GDALMDArray::ComputeStatistics().
12383 : *
12384 : * @since GDAL 3.8
12385 : */
12386 :
12387 4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
12388 : int bApproxOK, double *pdfMin,
12389 : double *pdfMax, double *pdfMean,
12390 : double *pdfStdDev, GUInt64 *pnValidCount,
12391 : GDALProgressFunc pfnProgress,
12392 : void *pProgressData,
12393 : CSLConstList papszOptions)
12394 : {
12395 4 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12396 8 : return hArray->m_poImpl->ComputeStatistics(
12397 4 : CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
12398 8 : pnValidCount, pfnProgress, pProgressData, papszOptions);
12399 : }
12400 :
12401 : /************************************************************************/
12402 : /* GDALMDArrayGetCoordinateVariables() */
12403 : /************************************************************************/
12404 :
12405 : /** Return coordinate variables.
12406 : *
12407 : * The returned array must be freed with GDALReleaseArrays(). If only the array
12408 : * itself needs to be freed, CPLFree() should be called (and
12409 : * GDALMDArrayRelease() on individual array members).
12410 : *
12411 : * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
12412 : *
12413 : * @param hArray Array.
12414 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12415 : *
12416 : * @return an array of *pnCount arrays.
12417 : * @since 3.4
12418 : */
12419 13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
12420 : size_t *pnCount)
12421 : {
12422 13 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12423 13 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12424 13 : const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
12425 : auto ret = static_cast<GDALMDArrayH *>(
12426 13 : CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
12427 29 : for (size_t i = 0; i < coordinates.size(); i++)
12428 : {
12429 16 : ret[i] = new GDALMDArrayHS(coordinates[i]);
12430 : }
12431 13 : *pnCount = coordinates.size();
12432 13 : return ret;
12433 : }
12434 :
12435 : /************************************************************************/
12436 : /* GDALMDArrayGetGridded() */
12437 : /************************************************************************/
12438 :
12439 : /** Return a gridded array from scattered point data, that is from an array
12440 : * whose last dimension is the indexing variable of X and Y arrays.
12441 : *
12442 : * The returned object should be released with GDALMDArrayRelease().
12443 : *
12444 : * This is the same as the C++ method GDALMDArray::GetGridded().
12445 : *
12446 : * @since GDAL 3.7
12447 : */
12448 22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
12449 : const char *pszGridOptions,
12450 : GDALMDArrayH hXArray, GDALMDArrayH hYArray,
12451 : CSLConstList papszOptions)
12452 : {
12453 22 : VALIDATE_POINTER1(hArray, __func__, nullptr);
12454 22 : VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
12455 22 : auto gridded = hArray->m_poImpl->GetGridded(
12456 44 : pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
12457 88 : hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
12458 22 : if (!gridded)
12459 19 : return nullptr;
12460 3 : return new GDALMDArrayHS(gridded);
12461 : }
12462 :
12463 : /************************************************************************/
12464 : /* GDALReleaseArrays() */
12465 : /************************************************************************/
12466 :
12467 : /** Free the return of GDALMDArrayGetCoordinateVariables()
12468 : *
12469 : * @param arrays return pointer of above methods
12470 : * @param nCount *pnCount value returned by above methods
12471 : */
12472 13 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
12473 : {
12474 29 : for (size_t i = 0; i < nCount; i++)
12475 : {
12476 16 : delete arrays[i];
12477 : }
12478 13 : CPLFree(arrays);
12479 13 : }
12480 :
12481 : /************************************************************************/
12482 : /* GDALMDArrayCache() */
12483 : /************************************************************************/
12484 :
12485 : /**
12486 : * \brief Cache the content of the array into an auxiliary filename.
12487 : *
12488 : * This is the same as the C++ method GDALMDArray::Cache().
12489 : *
12490 : * @since GDAL 3.4
12491 : */
12492 :
12493 6 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
12494 : {
12495 6 : VALIDATE_POINTER1(hArray, __func__, FALSE);
12496 6 : return hArray->m_poImpl->Cache(papszOptions);
12497 : }
12498 :
12499 : /************************************************************************/
12500 : /* GDALMDArrayRename() */
12501 : /************************************************************************/
12502 :
12503 : /** Rename the array.
12504 : *
12505 : * This is not implemented by all drivers.
12506 : *
12507 : * Drivers known to implement it: MEM, netCDF, Zarr.
12508 : *
12509 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
12510 : *
12511 : * @return true in case of success
12512 : * @since GDAL 3.8
12513 : */
12514 28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
12515 : {
12516 28 : VALIDATE_POINTER1(hArray, __func__, false);
12517 28 : VALIDATE_POINTER1(pszNewName, __func__, false);
12518 28 : return hArray->m_poImpl->Rename(pszNewName);
12519 : }
12520 :
12521 : /************************************************************************/
12522 : /* GDALAttributeRelease() */
12523 : /************************************************************************/
12524 :
12525 : /** Release the GDAL in-memory object associated with a GDALAttribute.
12526 : *
12527 : * Note: when applied on a object coming from a driver, this does not
12528 : * destroy the object in the file, database, etc...
12529 : */
12530 680 : void GDALAttributeRelease(GDALAttributeH hAttr)
12531 : {
12532 680 : delete hAttr;
12533 680 : }
12534 :
12535 : /************************************************************************/
12536 : /* GDALAttributeGetName() */
12537 : /************************************************************************/
12538 :
12539 : /** Return the name of the attribute.
12540 : *
12541 : * The returned pointer is valid until hAttr is released.
12542 : *
12543 : * This is the same as the C++ method GDALAttribute::GetName().
12544 : */
12545 361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
12546 : {
12547 361 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12548 361 : return hAttr->m_poImpl->GetName().c_str();
12549 : }
12550 :
12551 : /************************************************************************/
12552 : /* GDALAttributeGetFullName() */
12553 : /************************************************************************/
12554 :
12555 : /** Return the full name of the attribute.
12556 : *
12557 : * The returned pointer is valid until hAttr is released.
12558 : *
12559 : * This is the same as the C++ method GDALAttribute::GetFullName().
12560 : */
12561 49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
12562 : {
12563 49 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12564 49 : return hAttr->m_poImpl->GetFullName().c_str();
12565 : }
12566 :
12567 : /************************************************************************/
12568 : /* GDALAttributeGetTotalElementsCount() */
12569 : /************************************************************************/
12570 :
12571 : /** Return the total number of values in the attribute.
12572 : *
12573 : * This is the same as the C++ method
12574 : * GDALAbstractMDArray::GetTotalElementsCount()
12575 : */
12576 176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
12577 : {
12578 176 : VALIDATE_POINTER1(hAttr, __func__, 0);
12579 176 : return hAttr->m_poImpl->GetTotalElementsCount();
12580 : }
12581 :
12582 : /************************************************************************/
12583 : /* GDALAttributeGetDimensionCount() */
12584 : /************************************************************************/
12585 :
12586 : /** Return the number of dimensions.
12587 : *
12588 : * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12589 : */
12590 12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
12591 : {
12592 12 : VALIDATE_POINTER1(hAttr, __func__, 0);
12593 12 : return hAttr->m_poImpl->GetDimensionCount();
12594 : }
12595 :
12596 : /************************************************************************/
12597 : /* GDALAttributeGetDimensionsSize() */
12598 : /************************************************************************/
12599 :
12600 : /** Return the dimension sizes of the attribute.
12601 : *
12602 : * The returned array must be freed with CPLFree()
12603 : *
12604 : * @param hAttr Attribute.
12605 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12606 : *
12607 : * @return an array of *pnCount values.
12608 : */
12609 11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
12610 : {
12611 11 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12612 11 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12613 11 : const auto &dims = hAttr->m_poImpl->GetDimensions();
12614 11 : auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
12615 22 : for (size_t i = 0; i < dims.size(); i++)
12616 : {
12617 11 : ret[i] = dims[i]->GetSize();
12618 : }
12619 11 : *pnCount = dims.size();
12620 11 : return ret;
12621 : }
12622 :
12623 : /************************************************************************/
12624 : /* GDALAttributeGetDataType() */
12625 : /************************************************************************/
12626 :
12627 : /** Return the data type
12628 : *
12629 : * The return must be freed with GDALExtendedDataTypeRelease().
12630 : */
12631 411 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
12632 : {
12633 411 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12634 : return new GDALExtendedDataTypeHS(
12635 411 : new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
12636 : }
12637 :
12638 : /************************************************************************/
12639 : /* GDALAttributeReadAsRaw() */
12640 : /************************************************************************/
12641 :
12642 : /** Return the raw value of an attribute.
12643 : *
12644 : * This is the same as the C++ method GDALAttribute::ReadAsRaw().
12645 : *
12646 : * The returned buffer must be freed with GDALAttributeFreeRawResult()
12647 : *
12648 : * @param hAttr Attribute.
12649 : * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
12650 : *
12651 : * @return a buffer of *pnSize bytes.
12652 : */
12653 6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
12654 : {
12655 6 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12656 6 : VALIDATE_POINTER1(pnSize, __func__, nullptr);
12657 12 : auto res(hAttr->m_poImpl->ReadAsRaw());
12658 6 : *pnSize = res.size();
12659 6 : auto ret = res.StealData();
12660 6 : if (!ret)
12661 : {
12662 0 : *pnSize = 0;
12663 0 : return nullptr;
12664 : }
12665 6 : return ret;
12666 : }
12667 :
12668 : /************************************************************************/
12669 : /* GDALAttributeFreeRawResult() */
12670 : /************************************************************************/
12671 :
12672 : /** Free the return of GDALAttributeAsRaw()
12673 : */
12674 6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
12675 : CPL_UNUSED size_t nSize)
12676 : {
12677 6 : VALIDATE_POINTER0(hAttr, __func__);
12678 6 : if (raw)
12679 : {
12680 6 : const auto &dt(hAttr->m_poImpl->GetDataType());
12681 6 : const auto nDTSize(dt.GetSize());
12682 6 : GByte *pabyPtr = raw;
12683 6 : const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
12684 6 : CPLAssert(nSize == nDTSize * nEltCount);
12685 12 : for (size_t i = 0; i < nEltCount; ++i)
12686 : {
12687 6 : dt.FreeDynamicMemory(pabyPtr);
12688 6 : pabyPtr += nDTSize;
12689 : }
12690 6 : CPLFree(raw);
12691 : }
12692 : }
12693 :
12694 : /************************************************************************/
12695 : /* GDALAttributeReadAsString() */
12696 : /************************************************************************/
12697 :
12698 : /** Return the value of an attribute as a string.
12699 : *
12700 : * The returned string should not be freed, and its lifetime does not
12701 : * excess a next call to ReadAsString() on the same object, or the deletion
12702 : * of the object itself.
12703 : *
12704 : * This function will only return the first element if there are several.
12705 : *
12706 : * This is the same as the C++ method GDALAttribute::ReadAsString()
12707 : *
12708 : * @return a string, or nullptr.
12709 : */
12710 107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
12711 : {
12712 107 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12713 107 : return hAttr->m_poImpl->ReadAsString();
12714 : }
12715 :
12716 : /************************************************************************/
12717 : /* GDALAttributeReadAsInt() */
12718 : /************************************************************************/
12719 :
12720 : /** Return the value of an attribute as a integer.
12721 : *
12722 : * This function will only return the first element if there are several.
12723 : *
12724 : * It can fail if its value can be converted to integer.
12725 : *
12726 : * This is the same as the C++ method GDALAttribute::ReadAsInt()
12727 : *
12728 : * @return a integer, or INT_MIN in case of error.
12729 : */
12730 19 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
12731 : {
12732 19 : VALIDATE_POINTER1(hAttr, __func__, 0);
12733 19 : return hAttr->m_poImpl->ReadAsInt();
12734 : }
12735 :
12736 : /************************************************************************/
12737 : /* GDALAttributeReadAsDouble() */
12738 : /************************************************************************/
12739 :
12740 : /** Return the value of an attribute as a double.
12741 : *
12742 : * This function will only return the first element if there are several.
12743 : *
12744 : * It can fail if its value can be converted to double.
12745 : *
12746 : * This is the same as the C++ method GDALAttribute::ReadAsDouble()
12747 : *
12748 : * @return a double value.
12749 : */
12750 37 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
12751 : {
12752 37 : VALIDATE_POINTER1(hAttr, __func__, 0);
12753 37 : return hAttr->m_poImpl->ReadAsDouble();
12754 : }
12755 :
12756 : /************************************************************************/
12757 : /* GDALAttributeReadAsStringArray() */
12758 : /************************************************************************/
12759 :
12760 : /** Return the value of an attribute as an array of strings.
12761 : *
12762 : * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
12763 : *
12764 : * The return value must be freed with CSLDestroy().
12765 : */
12766 19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
12767 : {
12768 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12769 19 : return hAttr->m_poImpl->ReadAsStringArray().StealList();
12770 : }
12771 :
12772 : /************************************************************************/
12773 : /* GDALAttributeReadAsIntArray() */
12774 : /************************************************************************/
12775 :
12776 : /** Return the value of an attribute as an array of integers.
12777 : *
12778 : * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
12779 : *
12780 : * @param hAttr Attribute
12781 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12782 : * @return array to be freed with CPLFree(), or nullptr.
12783 : */
12784 15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
12785 : {
12786 15 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12787 15 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12788 15 : *pnCount = 0;
12789 30 : auto tmp(hAttr->m_poImpl->ReadAsIntArray());
12790 15 : if (tmp.empty())
12791 0 : return nullptr;
12792 15 : auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
12793 15 : if (!ret)
12794 0 : return nullptr;
12795 15 : memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
12796 15 : *pnCount = tmp.size();
12797 15 : return ret;
12798 : }
12799 :
12800 : /************************************************************************/
12801 : /* GDALAttributeReadAsDoubleArray() */
12802 : /************************************************************************/
12803 :
12804 : /** Return the value of an attribute as an array of doubles.
12805 : *
12806 : * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
12807 : *
12808 : * @param hAttr Attribute
12809 : * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12810 : * @return array to be freed with CPLFree(), or nullptr.
12811 : */
12812 19 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
12813 : {
12814 19 : VALIDATE_POINTER1(hAttr, __func__, nullptr);
12815 19 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
12816 19 : *pnCount = 0;
12817 38 : auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
12818 19 : if (tmp.empty())
12819 0 : return nullptr;
12820 : auto ret =
12821 19 : static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
12822 19 : if (!ret)
12823 0 : return nullptr;
12824 19 : memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
12825 19 : *pnCount = tmp.size();
12826 19 : return ret;
12827 : }
12828 :
12829 : /************************************************************************/
12830 : /* GDALAttributeWriteRaw() */
12831 : /************************************************************************/
12832 :
12833 : /** Write an attribute from raw values expressed in GetDataType()
12834 : *
12835 : * The values should be provided in the type of GetDataType() and there should
12836 : * be exactly GetTotalElementsCount() of them.
12837 : * If GetDataType() is a string, each value should be a char* pointer.
12838 : *
12839 : * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
12840 : *
12841 : * @param hAttr Attribute
12842 : * @param pabyValue Buffer of nLen bytes.
12843 : * @param nLength Size of pabyValue in bytes. Should be equal to
12844 : * GetTotalElementsCount() * GetDataType().GetSize()
12845 : * @return TRUE in case of success.
12846 : */
12847 5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
12848 : size_t nLength)
12849 : {
12850 5 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12851 5 : return hAttr->m_poImpl->Write(pabyValue, nLength);
12852 : }
12853 :
12854 : /************************************************************************/
12855 : /* GDALAttributeWriteString() */
12856 : /************************************************************************/
12857 :
12858 : /** Write an attribute from a string value.
12859 : *
12860 : * Type conversion will be performed if needed. If the attribute contains
12861 : * multiple values, only the first one will be updated.
12862 : *
12863 : * This is the same as the C++ method GDALAttribute::Write(const char*)
12864 : *
12865 : * @param hAttr Attribute
12866 : * @param pszVal Pointer to a string.
12867 : * @return TRUE in case of success.
12868 : */
12869 174 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
12870 : {
12871 174 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12872 174 : return hAttr->m_poImpl->Write(pszVal);
12873 : }
12874 :
12875 : /************************************************************************/
12876 : /* GDALAttributeWriteInt() */
12877 : /************************************************************************/
12878 :
12879 : /** Write an attribute from a integer value.
12880 : *
12881 : * Type conversion will be performed if needed. If the attribute contains
12882 : * multiple values, only the first one will be updated.
12883 : *
12884 : * This is the same as the C++ method GDALAttribute::WriteInt()
12885 : *
12886 : * @param hAttr Attribute
12887 : * @param nVal Value.
12888 : * @return TRUE in case of success.
12889 : */
12890 22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
12891 : {
12892 22 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12893 22 : return hAttr->m_poImpl->WriteInt(nVal);
12894 : }
12895 :
12896 : /************************************************************************/
12897 : /* GDALAttributeWriteDouble() */
12898 : /************************************************************************/
12899 :
12900 : /** Write an attribute from a double value.
12901 : *
12902 : * Type conversion will be performed if needed. If the attribute contains
12903 : * multiple values, only the first one will be updated.
12904 : *
12905 : * This is the same as the C++ method GDALAttribute::Write(double);
12906 : *
12907 : * @param hAttr Attribute
12908 : * @param dfVal Value.
12909 : *
12910 : * @return TRUE in case of success.
12911 : */
12912 16 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
12913 : {
12914 16 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12915 16 : return hAttr->m_poImpl->Write(dfVal);
12916 : }
12917 :
12918 : /************************************************************************/
12919 : /* GDALAttributeWriteStringArray() */
12920 : /************************************************************************/
12921 :
12922 : /** Write an attribute from an array of strings.
12923 : *
12924 : * Type conversion will be performed if needed.
12925 : *
12926 : * Exactly GetTotalElementsCount() strings must be provided
12927 : *
12928 : * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
12929 : *
12930 : * @param hAttr Attribute
12931 : * @param papszValues Array of strings.
12932 : * @return TRUE in case of success.
12933 : */
12934 8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
12935 : CSLConstList papszValues)
12936 : {
12937 8 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12938 8 : return hAttr->m_poImpl->Write(papszValues);
12939 : }
12940 :
12941 : /************************************************************************/
12942 : /* GDALAttributeWriteDoubleArray() */
12943 : /************************************************************************/
12944 :
12945 : /** Write an attribute from an array of double.
12946 : *
12947 : * Type conversion will be performed if needed.
12948 : *
12949 : * Exactly GetTotalElementsCount() strings must be provided
12950 : *
12951 : * This is the same as the C++ method GDALAttribute::Write(const double *,
12952 : * size_t)
12953 : *
12954 : * @param hAttr Attribute
12955 : * @param padfValues Array of double.
12956 : * @param nCount Should be equal to GetTotalElementsCount().
12957 : * @return TRUE in case of success.
12958 : */
12959 17 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
12960 : const double *padfValues, size_t nCount)
12961 : {
12962 17 : VALIDATE_POINTER1(hAttr, __func__, FALSE);
12963 17 : return hAttr->m_poImpl->Write(padfValues, nCount);
12964 : }
12965 :
12966 : /************************************************************************/
12967 : /* GDALAttributeRename() */
12968 : /************************************************************************/
12969 :
12970 : /** Rename the attribute.
12971 : *
12972 : * This is not implemented by all drivers.
12973 : *
12974 : * Drivers known to implement it: MEM, netCDF.
12975 : *
12976 : * This is the same as the C++ method GDALAbstractMDArray::Rename()
12977 : *
12978 : * @return true in case of success
12979 : * @since GDAL 3.8
12980 : */
12981 27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
12982 : {
12983 27 : VALIDATE_POINTER1(hAttr, __func__, false);
12984 27 : VALIDATE_POINTER1(pszNewName, __func__, false);
12985 27 : return hAttr->m_poImpl->Rename(pszNewName);
12986 : }
12987 :
12988 : /************************************************************************/
12989 : /* GDALDimensionRelease() */
12990 : /************************************************************************/
12991 :
12992 : /** Release the GDAL in-memory object associated with a GDALDimension.
12993 : *
12994 : * Note: when applied on a object coming from a driver, this does not
12995 : * destroy the object in the file, database, etc...
12996 : */
12997 4732 : void GDALDimensionRelease(GDALDimensionH hDim)
12998 : {
12999 4732 : delete hDim;
13000 4732 : }
13001 :
13002 : /************************************************************************/
13003 : /* GDALDimensionGetName() */
13004 : /************************************************************************/
13005 :
13006 : /** Return dimension name.
13007 : *
13008 : * This is the same as the C++ method GDALDimension::GetName()
13009 : */
13010 284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
13011 : {
13012 284 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13013 284 : return hDim->m_poImpl->GetName().c_str();
13014 : }
13015 :
13016 : /************************************************************************/
13017 : /* GDALDimensionGetFullName() */
13018 : /************************************************************************/
13019 :
13020 : /** Return dimension full name.
13021 : *
13022 : * This is the same as the C++ method GDALDimension::GetFullName()
13023 : */
13024 80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
13025 : {
13026 80 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13027 80 : return hDim->m_poImpl->GetFullName().c_str();
13028 : }
13029 :
13030 : /************************************************************************/
13031 : /* GDALDimensionGetType() */
13032 : /************************************************************************/
13033 :
13034 : /** Return dimension type.
13035 : *
13036 : * This is the same as the C++ method GDALDimension::GetType()
13037 : */
13038 62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
13039 : {
13040 62 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13041 62 : return hDim->m_poImpl->GetType().c_str();
13042 : }
13043 :
13044 : /************************************************************************/
13045 : /* GDALDimensionGetDirection() */
13046 : /************************************************************************/
13047 :
13048 : /** Return dimension direction.
13049 : *
13050 : * This is the same as the C++ method GDALDimension::GetDirection()
13051 : */
13052 32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
13053 : {
13054 32 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13055 32 : return hDim->m_poImpl->GetDirection().c_str();
13056 : }
13057 :
13058 : /************************************************************************/
13059 : /* GDALDimensionGetSize() */
13060 : /************************************************************************/
13061 :
13062 : /** Return the size, that is the number of values along the dimension.
13063 : *
13064 : * This is the same as the C++ method GDALDimension::GetSize()
13065 : */
13066 3503 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
13067 : {
13068 3503 : VALIDATE_POINTER1(hDim, __func__, 0);
13069 3503 : return hDim->m_poImpl->GetSize();
13070 : }
13071 :
13072 : /************************************************************************/
13073 : /* GDALDimensionGetIndexingVariable() */
13074 : /************************************************************************/
13075 :
13076 : /** Return the variable that is used to index the dimension (if there is one).
13077 : *
13078 : * This is the array, typically one-dimensional, describing the values taken
13079 : * by the dimension.
13080 : *
13081 : * The returned value should be freed with GDALMDArrayRelease().
13082 : *
13083 : * This is the same as the C++ method GDALDimension::GetIndexingVariable()
13084 : */
13085 118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
13086 : {
13087 118 : VALIDATE_POINTER1(hDim, __func__, nullptr);
13088 236 : auto var(hDim->m_poImpl->GetIndexingVariable());
13089 118 : if (!var)
13090 10 : return nullptr;
13091 108 : return new GDALMDArrayHS(var);
13092 : }
13093 :
13094 : /************************************************************************/
13095 : /* GDALDimensionSetIndexingVariable() */
13096 : /************************************************************************/
13097 :
13098 : /** Set the variable that is used to index the dimension.
13099 : *
13100 : * This is the array, typically one-dimensional, describing the values taken
13101 : * by the dimension.
13102 : *
13103 : * This is the same as the C++ method GDALDimension::SetIndexingVariable()
13104 : *
13105 : * @return TRUE in case of success.
13106 : */
13107 22 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
13108 : {
13109 22 : VALIDATE_POINTER1(hDim, __func__, FALSE);
13110 66 : return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
13111 44 : : nullptr);
13112 : }
13113 :
13114 : /************************************************************************/
13115 : /* GDALDimensionRename() */
13116 : /************************************************************************/
13117 :
13118 : /** Rename the dimension.
13119 : *
13120 : * This is not implemented by all drivers.
13121 : *
13122 : * Drivers known to implement it: MEM, netCDF.
13123 : *
13124 : * This is the same as the C++ method GDALDimension::Rename()
13125 : *
13126 : * @return true in case of success
13127 : * @since GDAL 3.8
13128 : */
13129 31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
13130 : {
13131 31 : VALIDATE_POINTER1(hDim, __func__, false);
13132 31 : VALIDATE_POINTER1(pszNewName, __func__, false);
13133 31 : return hDim->m_poImpl->Rename(pszNewName);
13134 : }
13135 :
13136 : /************************************************************************/
13137 : /* GDALDatasetGetRootGroup() */
13138 : /************************************************************************/
13139 :
13140 : /** Return the root GDALGroup of this dataset.
13141 : *
13142 : * Only valid for multidimensional datasets.
13143 : *
13144 : * The returned value must be freed with GDALGroupRelease().
13145 : *
13146 : * This is the same as the C++ method GDALDataset::GetRootGroup().
13147 : *
13148 : * @since GDAL 3.1
13149 : */
13150 1119 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
13151 : {
13152 1119 : VALIDATE_POINTER1(hDS, __func__, nullptr);
13153 1119 : auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
13154 1119 : return poGroup ? new GDALGroupHS(poGroup) : nullptr;
13155 : }
13156 :
13157 : /************************************************************************/
13158 : /* GDALRasterBandAsMDArray() */
13159 : /************************************************************************/
13160 :
13161 : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
13162 : *
13163 : * The band must be linked to a GDALDataset. If this dataset is not already
13164 : * marked as shared, it will be, so that the returned array holds a reference
13165 : * to it.
13166 : *
13167 : * If the dataset has a geotransform attached, the X and Y dimensions of the
13168 : * returned array will have an associated indexing variable.
13169 : *
13170 : * The returned pointer must be released with GDALMDArrayRelease().
13171 : *
13172 : * This is the same as the C++ method GDALRasterBand::AsMDArray().
13173 : *
13174 : * @return a new array, or NULL.
13175 : *
13176 : * @since GDAL 3.1
13177 : */
13178 21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
13179 : {
13180 21 : VALIDATE_POINTER1(hBand, __func__, nullptr);
13181 42 : auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
13182 21 : if (!poArray)
13183 0 : return nullptr;
13184 21 : return new GDALMDArrayHS(poArray);
13185 : }
13186 :
13187 : /************************************************************************/
13188 : /* GDALMDArrayAsClassicDataset() */
13189 : /************************************************************************/
13190 :
13191 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13192 : *
13193 : * Only 2D or more arrays are supported.
13194 : *
13195 : * In the case of > 2D arrays, additional dimensions will be represented as
13196 : * raster bands.
13197 : *
13198 : * The "reverse" method is GDALRasterBand::AsMDArray().
13199 : *
13200 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13201 : *
13202 : * @param hArray Array.
13203 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13204 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13205 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13206 : */
13207 0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
13208 : size_t iYDim)
13209 : {
13210 0 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13211 0 : return GDALDataset::ToHandle(
13212 0 : hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
13213 : }
13214 :
13215 : /************************************************************************/
13216 : /* GDALMDArrayAsClassicDatasetEx() */
13217 : /************************************************************************/
13218 :
13219 : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
13220 : *
13221 : * Only 2D or more arrays are supported.
13222 : *
13223 : * In the case of > 2D arrays, additional dimensions will be represented as
13224 : * raster bands.
13225 : *
13226 : * The "reverse" method is GDALRasterBand::AsMDArray().
13227 : *
13228 : * This is the same as the C++ method GDALMDArray::AsClassicDataset().
13229 : * @param hArray Array.
13230 : * @param iXDim Index of the dimension that will be used as the X/width axis.
13231 : * @param iYDim Index of the dimension that will be used as the Y/height axis.
13232 : * Ignored if the dimension count is 1.
13233 : * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA option.
13234 : * @param papszOptions Cf GDALMDArray::AsClassicDataset()
13235 : * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
13236 : * @since GDAL 3.8
13237 : */
13238 56 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
13239 : size_t iYDim, GDALGroupH hRootGroup,
13240 : CSLConstList papszOptions)
13241 : {
13242 56 : VALIDATE_POINTER1(hArray, __func__, nullptr);
13243 112 : return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
13244 112 : iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
13245 112 : papszOptions));
13246 : }
13247 :
13248 : //! @cond Doxygen_Suppress
13249 :
13250 180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
13251 : const std::string &osName,
13252 : const std::string &osValue,
13253 180 : GDALExtendedDataTypeSubType eSubType)
13254 : : GDALAbstractMDArray(osParentName, osName),
13255 : GDALAttribute(osParentName, osName),
13256 180 : m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
13257 : {
13258 180 : }
13259 :
13260 : const std::vector<std::shared_ptr<GDALDimension>> &
13261 30 : GDALAttributeString::GetDimensions() const
13262 : {
13263 30 : return m_dims;
13264 : }
13265 :
13266 21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
13267 : {
13268 21 : return m_dt;
13269 : }
13270 :
13271 10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
13272 : const GPtrDiff_t *,
13273 : const GDALExtendedDataType &bufferDataType,
13274 : void *pDstBuffer) const
13275 : {
13276 10 : if (bufferDataType.GetClass() != GEDTC_STRING)
13277 0 : return false;
13278 10 : char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
13279 10 : if (!pszStr)
13280 0 : return false;
13281 10 : memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
13282 10 : *static_cast<char **>(pDstBuffer) = pszStr;
13283 10 : return true;
13284 : }
13285 :
13286 66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13287 : const std::string &osName,
13288 66 : double dfValue)
13289 : : GDALAbstractMDArray(osParentName, osName),
13290 : GDALAttribute(osParentName, osName),
13291 66 : m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
13292 : {
13293 66 : }
13294 :
13295 27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13296 : const std::string &osName,
13297 27 : int nValue)
13298 : : GDALAbstractMDArray(osParentName, osName),
13299 : GDALAttribute(osParentName, osName),
13300 27 : m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
13301 : {
13302 27 : }
13303 :
13304 7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
13305 : const std::string &osName,
13306 7 : const std::vector<GUInt32> &anValues)
13307 : : GDALAbstractMDArray(osParentName, osName),
13308 : GDALAttribute(osParentName, osName),
13309 7 : m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
13310 : {
13311 7 : m_dims.push_back(std::make_shared<GDALDimension>(
13312 14 : std::string(), "dim0", std::string(), std::string(),
13313 7 : m_anValuesUInt32.size()));
13314 7 : }
13315 :
13316 : const std::vector<std::shared_ptr<GDALDimension>> &
13317 14 : GDALAttributeNumeric::GetDimensions() const
13318 : {
13319 14 : return m_dims;
13320 : }
13321 :
13322 8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
13323 : {
13324 8 : return m_dt;
13325 : }
13326 :
13327 4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
13328 : const size_t *count, const GInt64 *arrayStep,
13329 : const GPtrDiff_t *bufferStride,
13330 : const GDALExtendedDataType &bufferDataType,
13331 : void *pDstBuffer) const
13332 : {
13333 4 : if (m_dims.empty())
13334 : {
13335 3 : if (m_dt.GetNumericDataType() == GDT_Float64)
13336 0 : GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
13337 : bufferDataType);
13338 : else
13339 : {
13340 3 : CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
13341 3 : GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
13342 : bufferDataType);
13343 : }
13344 : }
13345 : else
13346 : {
13347 1 : CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
13348 1 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13349 30 : for (size_t i = 0; i < count[0]; ++i)
13350 : {
13351 29 : GDALExtendedDataType::CopyValue(
13352 29 : &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
13353 29 : i * arrayStep[0])],
13354 29 : m_dt, pabyDstBuffer, bufferDataType);
13355 29 : pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
13356 : }
13357 : }
13358 4 : return true;
13359 : }
13360 :
13361 168 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
13362 : const std::string &osParentName, const std::string &osName,
13363 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13364 168 : double dfIncrement, double dfOffsetInIncrement)
13365 : : GDALAbstractMDArray(osParentName, osName),
13366 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
13367 : m_dfIncrement(dfIncrement),
13368 336 : m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
13369 : {
13370 168 : }
13371 :
13372 168 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
13373 : const std::string &osParentName, const std::string &osName,
13374 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
13375 : double dfIncrement, double dfOffsetInIncrement)
13376 : {
13377 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
13378 168 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
13379 168 : poArray->SetSelf(poArray);
13380 168 : return poArray;
13381 : }
13382 :
13383 : const std::vector<std::shared_ptr<GDALDimension>> &
13384 754 : GDALMDArrayRegularlySpaced::GetDimensions() const
13385 : {
13386 754 : return m_dims;
13387 : }
13388 :
13389 316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
13390 : {
13391 316 : return m_dt;
13392 : }
13393 :
13394 : std::vector<std::shared_ptr<GDALAttribute>>
13395 4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
13396 : {
13397 4 : return m_attributes;
13398 : }
13399 :
13400 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
13401 : const std::shared_ptr<GDALAttribute> &poAttr)
13402 : {
13403 0 : m_attributes.emplace_back(poAttr);
13404 0 : }
13405 :
13406 188 : bool GDALMDArrayRegularlySpaced::IRead(
13407 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
13408 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
13409 : void *pDstBuffer) const
13410 : {
13411 188 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
13412 14719 : for (size_t i = 0; i < count[0]; i++)
13413 : {
13414 14531 : const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
13415 14531 : m_dfOffsetInIncrement) *
13416 14531 : m_dfIncrement;
13417 14531 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
13418 : bufferDataType);
13419 14531 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
13420 : }
13421 188 : return true;
13422 : }
13423 :
13424 2780 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
13425 : const std::string &osParentName, const std::string &osName,
13426 2780 : const std::string &osType, const std::string &osDirection, GUInt64 nSize)
13427 2780 : : GDALDimension(osParentName, osName, osType, osDirection, nSize)
13428 : {
13429 2780 : }
13430 :
13431 : std::shared_ptr<GDALMDArray>
13432 653 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
13433 : {
13434 653 : return m_poIndexingVariable.lock();
13435 : }
13436 :
13437 : // cppcheck-suppress passedByValue
13438 443 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
13439 : std::shared_ptr<GDALMDArray> poIndexingVariable)
13440 : {
13441 443 : m_poIndexingVariable = poIndexingVariable;
13442 443 : return true;
13443 : }
13444 :
13445 33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
13446 : {
13447 33 : m_nSize = nNewSize;
13448 33 : }
13449 :
13450 : /************************************************************************/
13451 : /* GDALPamMultiDim::Private */
13452 : /************************************************************************/
13453 :
13454 : struct GDALPamMultiDim::Private
13455 : {
13456 : std::string m_osFilename{};
13457 : std::string m_osPamFilename{};
13458 :
13459 : struct Statistics
13460 : {
13461 : bool bHasStats = false;
13462 : bool bApproxStats = false;
13463 : double dfMin = 0;
13464 : double dfMax = 0;
13465 : double dfMean = 0;
13466 : double dfStdDev = 0;
13467 : GUInt64 nValidCount = 0;
13468 : };
13469 :
13470 : struct ArrayInfo
13471 : {
13472 : std::shared_ptr<OGRSpatialReference> poSRS{};
13473 : // cppcheck-suppress unusedStructMember
13474 : Statistics stats{};
13475 : };
13476 :
13477 : typedef std::pair<std::string, std::string> NameContext;
13478 : std::map<NameContext, ArrayInfo> m_oMapArray{};
13479 : std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
13480 : bool m_bDirty = false;
13481 : bool m_bLoaded = false;
13482 : };
13483 :
13484 : /************************************************************************/
13485 : /* GDALPamMultiDim */
13486 : /************************************************************************/
13487 :
13488 1342 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
13489 1342 : : d(new Private())
13490 : {
13491 1342 : d->m_osFilename = osFilename;
13492 1342 : }
13493 :
13494 : /************************************************************************/
13495 : /* GDALPamMultiDim::~GDALPamMultiDim() */
13496 : /************************************************************************/
13497 :
13498 1342 : GDALPamMultiDim::~GDALPamMultiDim()
13499 : {
13500 1342 : if (d->m_bDirty)
13501 28 : Save();
13502 1342 : }
13503 :
13504 : /************************************************************************/
13505 : /* GDALPamMultiDim::Load() */
13506 : /************************************************************************/
13507 :
13508 98 : void GDALPamMultiDim::Load()
13509 : {
13510 98 : if (d->m_bLoaded)
13511 87 : return;
13512 43 : d->m_bLoaded = true;
13513 :
13514 43 : const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
13515 43 : d->m_osPamFilename =
13516 86 : pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
13517 43 : CPLXMLTreeCloser oTree(nullptr);
13518 : {
13519 86 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
13520 43 : oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
13521 : }
13522 43 : if (!oTree)
13523 : {
13524 32 : return;
13525 : }
13526 11 : const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
13527 11 : if (!poPAMMultiDim)
13528 0 : return;
13529 34 : for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
13530 23 : psIter = psIter->psNext)
13531 : {
13532 23 : if (psIter->eType == CXT_Element &&
13533 23 : strcmp(psIter->pszValue, "Array") == 0)
13534 : {
13535 13 : const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
13536 13 : if (!pszName)
13537 0 : continue;
13538 13 : const char *pszContext = CPLGetXMLValue(psIter, "context", "");
13539 : const auto oKey =
13540 26 : std::pair<std::string, std::string>(pszName, pszContext);
13541 :
13542 : /* --------------------------------------------------------------------
13543 : */
13544 : /* Check for an SRS node. */
13545 : /* --------------------------------------------------------------------
13546 : */
13547 13 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
13548 13 : if (psSRSNode)
13549 : {
13550 : std::shared_ptr<OGRSpatialReference> poSRS =
13551 6 : std::make_shared<OGRSpatialReference>();
13552 3 : poSRS->SetFromUserInput(
13553 : CPLGetXMLValue(psSRSNode, nullptr, ""),
13554 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
13555 3 : const char *pszMapping = CPLGetXMLValue(
13556 : psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
13557 3 : if (pszMapping)
13558 : {
13559 : char **papszTokens =
13560 3 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
13561 6 : std::vector<int> anMapping;
13562 9 : for (int i = 0; papszTokens && papszTokens[i]; i++)
13563 : {
13564 6 : anMapping.push_back(atoi(papszTokens[i]));
13565 : }
13566 3 : CSLDestroy(papszTokens);
13567 3 : poSRS->SetDataAxisToSRSAxisMapping(anMapping);
13568 : }
13569 : else
13570 : {
13571 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
13572 : }
13573 :
13574 : const char *pszCoordinateEpoch =
13575 3 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
13576 3 : if (pszCoordinateEpoch)
13577 3 : poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
13578 :
13579 3 : d->m_oMapArray[oKey].poSRS = std::move(poSRS);
13580 : }
13581 :
13582 : const CPLXMLNode *psStatistics =
13583 13 : CPLGetXMLNode(psIter, "Statistics");
13584 13 : if (psStatistics)
13585 : {
13586 7 : Private::Statistics sStats;
13587 7 : sStats.bHasStats = true;
13588 7 : sStats.bApproxStats = CPLTestBool(
13589 : CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
13590 7 : sStats.dfMin =
13591 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
13592 7 : sStats.dfMax =
13593 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
13594 7 : sStats.dfMean =
13595 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
13596 7 : sStats.dfStdDev =
13597 7 : CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
13598 7 : sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
13599 : CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
13600 7 : d->m_oMapArray[oKey].stats = sStats;
13601 13 : }
13602 : }
13603 : else
13604 : {
13605 10 : CPLXMLNode *psNextBackup = psIter->psNext;
13606 10 : psIter->psNext = nullptr;
13607 10 : d->m_apoOtherNodes.emplace_back(
13608 10 : CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
13609 10 : psIter->psNext = psNextBackup;
13610 : }
13611 : }
13612 : }
13613 :
13614 : /************************************************************************/
13615 : /* GDALPamMultiDim::Save() */
13616 : /************************************************************************/
13617 :
13618 28 : void GDALPamMultiDim::Save()
13619 : {
13620 : CPLXMLTreeCloser oTree(
13621 56 : CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
13622 32 : for (const auto &poOtherNode : d->m_apoOtherNodes)
13623 : {
13624 4 : CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
13625 : }
13626 104 : for (const auto &kv : d->m_oMapArray)
13627 : {
13628 : CPLXMLNode *psArrayNode =
13629 76 : CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
13630 76 : CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
13631 76 : if (!kv.first.second.empty())
13632 : {
13633 1 : CPLAddXMLAttributeAndValue(psArrayNode, "context",
13634 : kv.first.second.c_str());
13635 : }
13636 76 : if (kv.second.poSRS)
13637 : {
13638 68 : char *pszWKT = nullptr;
13639 : {
13640 136 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
13641 68 : const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
13642 68 : kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
13643 : }
13644 : CPLXMLNode *psSRSNode =
13645 68 : CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
13646 68 : CPLFree(pszWKT);
13647 : const auto &mapping =
13648 68 : kv.second.poSRS->GetDataAxisToSRSAxisMapping();
13649 136 : CPLString osMapping;
13650 204 : for (size_t i = 0; i < mapping.size(); ++i)
13651 : {
13652 136 : if (!osMapping.empty())
13653 68 : osMapping += ",";
13654 136 : osMapping += CPLSPrintf("%d", mapping[i]);
13655 : }
13656 68 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
13657 : osMapping.c_str());
13658 :
13659 : const double dfCoordinateEpoch =
13660 68 : kv.second.poSRS->GetCoordinateEpoch();
13661 68 : if (dfCoordinateEpoch > 0)
13662 : {
13663 : std::string osCoordinateEpoch =
13664 2 : CPLSPrintf("%f", dfCoordinateEpoch);
13665 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
13666 : {
13667 6 : while (osCoordinateEpoch.back() == '0')
13668 5 : osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
13669 : }
13670 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
13671 : osCoordinateEpoch.c_str());
13672 : }
13673 : }
13674 :
13675 76 : if (kv.second.stats.bHasStats)
13676 : {
13677 : CPLXMLNode *psMDArray =
13678 5 : CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
13679 5 : CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
13680 5 : kv.second.stats.bApproxStats ? "1"
13681 : : "0");
13682 5 : CPLCreateXMLElementAndValue(
13683 : psMDArray, "Minimum",
13684 5 : CPLSPrintf("%.18g", kv.second.stats.dfMin));
13685 5 : CPLCreateXMLElementAndValue(
13686 : psMDArray, "Maximum",
13687 5 : CPLSPrintf("%.18g", kv.second.stats.dfMax));
13688 5 : CPLCreateXMLElementAndValue(
13689 5 : psMDArray, "Mean", CPLSPrintf("%.18g", kv.second.stats.dfMean));
13690 5 : CPLCreateXMLElementAndValue(
13691 : psMDArray, "StdDev",
13692 5 : CPLSPrintf("%.18g", kv.second.stats.dfStdDev));
13693 5 : CPLCreateXMLElementAndValue(
13694 : psMDArray, "ValidSampleCount",
13695 5 : CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
13696 : }
13697 : }
13698 :
13699 56 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
13700 28 : CPLInstallErrorHandlerAccumulator(aoErrors);
13701 : const int bSaved =
13702 28 : CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
13703 28 : CPLUninstallErrorHandlerAccumulator();
13704 :
13705 28 : const char *pszNewPam = nullptr;
13706 28 : if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
13707 0 : ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
13708 : {
13709 0 : CPLErrorReset();
13710 0 : CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
13711 : }
13712 : else
13713 : {
13714 28 : for (const auto &oError : aoErrors)
13715 : {
13716 0 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
13717 : }
13718 : }
13719 28 : }
13720 :
13721 : /************************************************************************/
13722 : /* GDALPamMultiDim::GetSpatialRef() */
13723 : /************************************************************************/
13724 :
13725 : std::shared_ptr<OGRSpatialReference>
13726 10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
13727 : const std::string &osContext)
13728 : {
13729 10 : Load();
13730 : auto oIter =
13731 10 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
13732 10 : if (oIter != d->m_oMapArray.end())
13733 2 : return oIter->second.poSRS;
13734 8 : return nullptr;
13735 : }
13736 :
13737 : /************************************************************************/
13738 : /* GDALPamMultiDim::SetSpatialRef() */
13739 : /************************************************************************/
13740 :
13741 69 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
13742 : const std::string &osContext,
13743 : const OGRSpatialReference *poSRS)
13744 : {
13745 69 : Load();
13746 69 : d->m_bDirty = true;
13747 69 : if (poSRS && !poSRS->IsEmpty())
13748 68 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
13749 : poSRS->Clone());
13750 : else
13751 2 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
13752 1 : .poSRS.reset();
13753 69 : }
13754 :
13755 : /************************************************************************/
13756 : /* GetStatistics() */
13757 : /************************************************************************/
13758 :
13759 13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
13760 : const std::string &osContext,
13761 : bool bApproxOK, double *pdfMin,
13762 : double *pdfMax, double *pdfMean,
13763 : double *pdfStdDev, GUInt64 *pnValidCount)
13764 : {
13765 13 : Load();
13766 : auto oIter =
13767 13 : d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
13768 13 : if (oIter == d->m_oMapArray.end())
13769 6 : return CE_Failure;
13770 7 : const auto &stats = oIter->second.stats;
13771 7 : if (!stats.bHasStats)
13772 1 : return CE_Failure;
13773 6 : if (!bApproxOK && stats.bApproxStats)
13774 0 : return CE_Failure;
13775 6 : if (pdfMin)
13776 6 : *pdfMin = stats.dfMin;
13777 6 : if (pdfMax)
13778 6 : *pdfMax = stats.dfMax;
13779 6 : if (pdfMean)
13780 6 : *pdfMean = stats.dfMean;
13781 6 : if (pdfStdDev)
13782 6 : *pdfStdDev = stats.dfStdDev;
13783 6 : if (pnValidCount)
13784 6 : *pnValidCount = stats.nValidCount;
13785 6 : return CE_None;
13786 : }
13787 :
13788 : /************************************************************************/
13789 : /* SetStatistics() */
13790 : /************************************************************************/
13791 :
13792 5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
13793 : const std::string &osContext,
13794 : bool bApproxStats, double dfMin,
13795 : double dfMax, double dfMean,
13796 : double dfStdDev, GUInt64 nValidCount)
13797 : {
13798 5 : Load();
13799 5 : d->m_bDirty = true;
13800 : auto &stats =
13801 5 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
13802 5 : stats.bHasStats = true;
13803 5 : stats.bApproxStats = bApproxStats;
13804 5 : stats.dfMin = dfMin;
13805 5 : stats.dfMax = dfMax;
13806 5 : stats.dfMean = dfMean;
13807 5 : stats.dfStdDev = dfStdDev;
13808 5 : stats.nValidCount = nValidCount;
13809 5 : }
13810 :
13811 : /************************************************************************/
13812 : /* ClearStatistics() */
13813 : /************************************************************************/
13814 :
13815 0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
13816 : const std::string &osContext)
13817 : {
13818 0 : Load();
13819 0 : d->m_bDirty = true;
13820 0 : d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
13821 : false;
13822 0 : }
13823 :
13824 : /************************************************************************/
13825 : /* ClearStatistics() */
13826 : /************************************************************************/
13827 :
13828 1 : void GDALPamMultiDim::ClearStatistics()
13829 : {
13830 1 : Load();
13831 1 : d->m_bDirty = true;
13832 3 : for (auto &kv : d->m_oMapArray)
13833 2 : kv.second.stats.bHasStats = false;
13834 1 : }
13835 :
13836 : /************************************************************************/
13837 : /* GetPAM() */
13838 : /************************************************************************/
13839 :
13840 : /*static*/ std::shared_ptr<GDALPamMultiDim>
13841 748 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
13842 : {
13843 748 : auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
13844 748 : if (poPamArray)
13845 539 : return poPamArray->GetPAM();
13846 209 : return nullptr;
13847 : }
13848 :
13849 : /************************************************************************/
13850 : /* GDALPamMDArray */
13851 : /************************************************************************/
13852 :
13853 3506 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
13854 : const std::string &osName,
13855 : const std::shared_ptr<GDALPamMultiDim> &poPam,
13856 0 : const std::string &osContext)
13857 : :
13858 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
13859 : GDALAbstractMDArray(osParentName, osName),
13860 : #endif
13861 3506 : GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
13862 : {
13863 3506 : }
13864 :
13865 : /************************************************************************/
13866 : /* GDALPamMDArray::SetSpatialRef() */
13867 : /************************************************************************/
13868 :
13869 69 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
13870 : {
13871 69 : if (!m_poPam)
13872 0 : return false;
13873 69 : m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
13874 69 : return true;
13875 : }
13876 :
13877 : /************************************************************************/
13878 : /* GDALPamMDArray::GetSpatialRef() */
13879 : /************************************************************************/
13880 :
13881 10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
13882 : {
13883 10 : if (!m_poPam)
13884 0 : return nullptr;
13885 10 : return m_poPam->GetSpatialRef(GetFullName(), GetContext());
13886 : }
13887 :
13888 : /************************************************************************/
13889 : /* GetStatistics() */
13890 : /************************************************************************/
13891 :
13892 13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
13893 : double *pdfMin, double *pdfMax,
13894 : double *pdfMean, double *pdfStdDev,
13895 : GUInt64 *pnValidCount,
13896 : GDALProgressFunc pfnProgress,
13897 : void *pProgressData)
13898 : {
13899 13 : if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
13900 : bApproxOK, pdfMin, pdfMax, pdfMean,
13901 13 : pdfStdDev, pnValidCount) == CE_None)
13902 : {
13903 6 : return CE_None;
13904 : }
13905 7 : if (!bForce)
13906 4 : return CE_Warning;
13907 :
13908 3 : return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
13909 : pdfMean, pdfStdDev, pnValidCount,
13910 3 : pfnProgress, pProgressData);
13911 : }
13912 :
13913 : /************************************************************************/
13914 : /* SetStatistics() */
13915 : /************************************************************************/
13916 :
13917 5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
13918 : double dfMax, double dfMean, double dfStdDev,
13919 : GUInt64 nValidCount,
13920 : CSLConstList /* papszOptions */)
13921 : {
13922 5 : if (!m_poPam)
13923 0 : return false;
13924 5 : m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
13925 : dfMax, dfMean, dfStdDev, nValidCount);
13926 5 : return true;
13927 : }
13928 :
13929 : /************************************************************************/
13930 : /* ClearStatistics() */
13931 : /************************************************************************/
13932 :
13933 0 : void GDALPamMDArray::ClearStatistics()
13934 : {
13935 0 : if (!m_poPam)
13936 0 : return;
13937 0 : m_poPam->ClearStatistics(GetFullName(), GetContext());
13938 : }
13939 :
13940 : //! @endcond
|