Coverage Report

Created: 2025-06-22 06:59

/src/gdal/gcore/gdalmultidim.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Name:     gdalmultidim.cpp
4
 * Project:  GDAL Core
5
 * Purpose:  GDAL Core C++/Private implementation for multidimensional support
6
 * Author:   Even Rouault <even.rouault at spatialys.com>
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <assert.h>
15
#include <algorithm>
16
#include <limits>
17
#include <list>
18
#include <queue>
19
#include <set>
20
#include <utility>
21
#include <time.h>
22
23
#include <cmath>
24
#include <ctype.h>  // isalnum
25
26
#include "cpl_error_internal.h"
27
#include "cpl_float.h"
28
#include "gdal_priv.h"
29
#include "gdal_pam.h"
30
#include "gdal_rat.h"
31
#include "gdal_utils.h"
32
#include "cpl_safemaths.hpp"
33
#include "memmultidim.h"
34
#include "ogrsf_frmts.h"
35
#include "gdalmultidim_priv.h"
36
37
#if defined(__clang__) || defined(_MSC_VER)
38
#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
39
#endif
40
41
/************************************************************************/
42
/*                       GDALMDArrayUnscaled                            */
43
/************************************************************************/
44
45
class GDALMDArrayUnscaled final : public GDALPamMDArray
46
{
47
  private:
48
    std::shared_ptr<GDALMDArray> m_poParent{};
49
    const GDALExtendedDataType m_dt;
50
    bool m_bHasNoData;
51
    const double m_dfScale;
52
    const double m_dfOffset;
53
    std::vector<GByte> m_abyRawNoData{};
54
55
  protected:
56
    explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
57
                                 double dfScale, double dfOffset,
58
                                 double dfOverriddenDstNodata, GDALDataType eDT)
59
0
        : GDALAbstractMDArray(std::string(),
60
0
                              "Unscaled view of " + poParent->GetFullName()),
61
0
          GDALPamMDArray(
62
0
              std::string(), "Unscaled view of " + poParent->GetFullName(),
63
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
64
0
          m_poParent(std::move(poParent)),
65
0
          m_dt(GDALExtendedDataType::Create(eDT)),
66
0
          m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
67
0
          m_dfScale(dfScale), m_dfOffset(dfOffset)
68
0
    {
69
0
        m_abyRawNoData.resize(m_dt.GetSize());
70
0
        const auto eNonComplexDT =
71
0
            GDALGetNonComplexDataType(m_dt.GetNumericDataType());
72
0
        GDALCopyWords64(
73
0
            &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
74
0
            eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
75
0
            GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
76
0
    }
77
78
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
79
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
80
               const GDALExtendedDataType &bufferDataType,
81
               void *pDstBuffer) const override;
82
83
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
84
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
85
                const GDALExtendedDataType &bufferDataType,
86
                const void *pSrcBuffer) override;
87
88
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
89
                     CSLConstList papszOptions) const override
90
0
    {
91
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
92
0
    }
93
94
  public:
95
    static std::shared_ptr<GDALMDArrayUnscaled>
96
    Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
97
           double dfOffset, double dfDstNodata, GDALDataType eDT)
98
0
    {
99
0
        auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
100
0
            poParent, dfScale, dfOffset, dfDstNodata, eDT)));
101
0
        newAr->SetSelf(newAr);
102
0
        return newAr;
103
0
    }
104
105
    bool IsWritable() const override
106
0
    {
107
0
        return m_poParent->IsWritable();
108
0
    }
109
110
    const std::string &GetFilename() const override
111
0
    {
112
0
        return m_poParent->GetFilename();
113
0
    }
114
115
    const std::vector<std::shared_ptr<GDALDimension>> &
116
    GetDimensions() const override
117
0
    {
118
0
        return m_poParent->GetDimensions();
119
0
    }
120
121
    const GDALExtendedDataType &GetDataType() const override
122
0
    {
123
0
        return m_dt;
124
0
    }
125
126
    const std::string &GetUnit() const override
127
0
    {
128
0
        return m_poParent->GetUnit();
129
0
    }
130
131
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
132
0
    {
133
0
        return m_poParent->GetSpatialRef();
134
0
    }
135
136
    const void *GetRawNoDataValue() const override
137
0
    {
138
0
        return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
139
0
    }
140
141
    bool SetRawNoDataValue(const void *pRawNoData) override
142
0
    {
143
0
        m_bHasNoData = true;
144
0
        memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
145
0
        return true;
146
0
    }
147
148
    std::vector<GUInt64> GetBlockSize() const override
149
0
    {
150
0
        return m_poParent->GetBlockSize();
151
0
    }
152
153
    std::shared_ptr<GDALAttribute>
154
    GetAttribute(const std::string &osName) const override
155
0
    {
156
0
        return m_poParent->GetAttribute(osName);
157
0
    }
158
159
    std::vector<std::shared_ptr<GDALAttribute>>
160
    GetAttributes(CSLConstList papszOptions = nullptr) const override
161
0
    {
162
0
        return m_poParent->GetAttributes(papszOptions);
163
0
    }
164
165
    bool SetUnit(const std::string &osUnit) override
166
0
    {
167
0
        return m_poParent->SetUnit(osUnit);
168
0
    }
169
170
    bool SetSpatialRef(const OGRSpatialReference *poSRS) override
171
0
    {
172
0
        return m_poParent->SetSpatialRef(poSRS);
173
0
    }
174
175
    std::shared_ptr<GDALAttribute>
176
    CreateAttribute(const std::string &osName,
177
                    const std::vector<GUInt64> &anDimensions,
178
                    const GDALExtendedDataType &oDataType,
179
                    CSLConstList papszOptions = nullptr) override
180
0
    {
181
0
        return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
182
0
                                           papszOptions);
183
0
    }
184
};
185
186
/************************************************************************/
187
/*                         ~GDALIHasAttribute()                         */
188
/************************************************************************/
189
190
0
GDALIHasAttribute::~GDALIHasAttribute() = default;
191
192
/************************************************************************/
193
/*                            GetAttribute()                            */
194
/************************************************************************/
195
196
/** Return an attribute by its name.
197
 *
198
 * If the attribute does not exist, nullptr should be silently returned.
199
 *
200
 * @note Driver implementation: this method will fallback to
201
 * GetAttributeFromAttributes() is not explicitly implemented
202
 *
203
 * Drivers known to implement it for groups and arrays: MEM, netCDF.
204
 *
205
 * This is the same as the C function GDALGroupGetAttribute() or
206
 * GDALMDArrayGetAttribute().
207
 *
208
 * @param osName Attribute name
209
 * @return the attribute, or nullptr if it does not exist or an error occurred.
210
 */
211
std::shared_ptr<GDALAttribute>
212
GDALIHasAttribute::GetAttribute(const std::string &osName) const
213
0
{
214
0
    return GetAttributeFromAttributes(osName);
215
0
}
216
217
/************************************************************************/
218
/*                       GetAttributeFromAttributes()                   */
219
/************************************************************************/
220
221
/** Possible fallback implementation for GetAttribute() using GetAttributes().
222
 */
223
std::shared_ptr<GDALAttribute>
224
GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
225
0
{
226
0
    auto attrs(GetAttributes());
227
0
    for (const auto &attr : attrs)
228
0
    {
229
0
        if (attr->GetName() == osName)
230
0
            return attr;
231
0
    }
232
0
    return nullptr;
233
0
}
234
235
/************************************************************************/
236
/*                           GetAttributes()                            */
237
/************************************************************************/
238
239
/** Return the list of attributes contained in a GDALMDArray or GDALGroup.
240
 *
241
 * If the attribute does not exist, nullptr should be silently returned.
242
 *
243
 * @note Driver implementation: optionally implemented. If implemented,
244
 * GetAttribute() should also be implemented.
245
 *
246
 * Drivers known to implement it for groups and arrays: MEM, netCDF.
247
 *
248
 * This is the same as the C function GDALGroupGetAttributes() or
249
 * GDALMDArrayGetAttributes().
250
251
 * @param papszOptions Driver specific options determining how attributes
252
 * should be retrieved. Pass nullptr for default behavior.
253
 *
254
 * @return the attributes.
255
 */
256
std::vector<std::shared_ptr<GDALAttribute>>
257
GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
258
0
{
259
0
    return {};
260
0
}
261
262
/************************************************************************/
263
/*                             CreateAttribute()                         */
264
/************************************************************************/
265
266
/** Create an attribute within a GDALMDArray or GDALGroup.
267
 *
268
 * The attribute might not be "physically" created until a value is written
269
 * into it.
270
 *
271
 * Optionally implemented.
272
 *
273
 * Drivers known to implement it: MEM, netCDF
274
 *
275
 * This is the same as the C function GDALGroupCreateAttribute() or
276
 * GDALMDArrayCreateAttribute()
277
 *
278
 * @param osName Attribute name.
279
 * @param anDimensions List of dimension sizes, ordered from the slowest varying
280
 *                     dimension first to the fastest varying dimension last.
281
 *                     Empty for a scalar attribute (common case)
282
 * @param oDataType  Attribute data type.
283
 * @param papszOptions Driver specific options determining how the attribute.
284
 * should be created.
285
 *
286
 * @return the new attribute, or nullptr if case of error
287
 */
288
std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
289
    CPL_UNUSED const std::string &osName,
290
    CPL_UNUSED const std::vector<GUInt64> &anDimensions,
291
    CPL_UNUSED const GDALExtendedDataType &oDataType,
292
    CPL_UNUSED CSLConstList papszOptions)
293
0
{
294
0
    CPLError(CE_Failure, CPLE_NotSupported,
295
0
             "CreateAttribute() not implemented");
296
0
    return nullptr;
297
0
}
298
299
/************************************************************************/
300
/*                          DeleteAttribute()                           */
301
/************************************************************************/
302
303
/** Delete an attribute from a GDALMDArray or GDALGroup.
304
 *
305
 * Optionally implemented.
306
 *
307
 * After this call, if a previously obtained instance of the deleted object
308
 * is still alive, no method other than for freeing it should be invoked.
309
 *
310
 * Drivers known to implement it: MEM, netCDF
311
 *
312
 * This is the same as the C function GDALGroupDeleteAttribute() or
313
 * GDALMDArrayDeleteAttribute()
314
 *
315
 * @param osName Attribute name.
316
 * @param papszOptions Driver specific options determining how the attribute.
317
 * should be deleted.
318
 *
319
 * @return true in case of success
320
 * @since GDAL 3.8
321
 */
322
bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
323
                                        CPL_UNUSED CSLConstList papszOptions)
324
0
{
325
0
    CPLError(CE_Failure, CPLE_NotSupported,
326
0
             "DeleteAttribute() not implemented");
327
0
    return false;
328
0
}
329
330
/************************************************************************/
331
/*                            GDALGroup()                               */
332
/************************************************************************/
333
334
//! @cond Doxygen_Suppress
335
GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
336
                     const std::string &osContext)
337
0
    : m_osName(osParentName.empty() ? "/" : osName),
338
      m_osFullName(
339
0
          !osParentName.empty()
340
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
341
0
              : "/"),
342
0
      m_osContext(osContext)
343
0
{
344
0
}
345
346
//! @endcond
347
348
/************************************************************************/
349
/*                            ~GDALGroup()                              */
350
/************************************************************************/
351
352
0
GDALGroup::~GDALGroup() = default;
353
354
/************************************************************************/
355
/*                          GetMDArrayNames()                           */
356
/************************************************************************/
357
358
/** Return the list of multidimensional array names contained in this group.
359
 *
360
 * @note Driver implementation: optionally implemented. If implemented,
361
 * OpenMDArray() should also be implemented.
362
 *
363
 * Drivers known to implement it: MEM, netCDF.
364
 *
365
 * This is the same as the C function GDALGroupGetMDArrayNames().
366
 *
367
 * @param papszOptions Driver specific options determining how arrays
368
 * should be retrieved. Pass nullptr for default behavior.
369
 *
370
 * @return the array names.
371
 */
372
std::vector<std::string>
373
GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
374
0
{
375
0
    return {};
376
0
}
377
378
/************************************************************************/
379
/*                     GetMDArrayFullNamesRecursive()                   */
380
/************************************************************************/
381
382
/** Return the list of multidimensional array full names contained in this
383
 * group and its subgroups.
384
 *
385
 * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
386
 *
387
 * @param papszGroupOptions Driver specific options determining how groups
388
 * should be retrieved. Pass nullptr for default behavior.
389
 * @param papszArrayOptions Driver specific options determining how arrays
390
 * should be retrieved. Pass nullptr for default behavior.
391
 *
392
 * @return the array full names.
393
 *
394
 * @since 3.11
395
 */
396
std::vector<std::string>
397
GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
398
                                        CSLConstList papszArrayOptions) const
399
0
{
400
0
    std::vector<std::string> ret;
401
0
    std::list<std::shared_ptr<GDALGroup>> stackGroups;
402
0
    stackGroups.push_back(nullptr);  // nullptr means this
403
0
    while (!stackGroups.empty())
404
0
    {
405
0
        std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
406
0
        stackGroups.erase(stackGroups.begin());
407
0
        const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
408
0
        for (const std::string &arrayName :
409
0
             poCurGroup->GetMDArrayNames(papszArrayOptions))
410
0
        {
411
0
            std::string osFullName = poCurGroup->GetFullName();
412
0
            if (!osFullName.empty() && osFullName.back() != '/')
413
0
                osFullName += '/';
414
0
            osFullName += arrayName;
415
0
            ret.push_back(std::move(osFullName));
416
0
        }
417
0
        auto insertionPoint = stackGroups.begin();
418
0
        for (const auto &osSubGroup :
419
0
             poCurGroup->GetGroupNames(papszGroupOptions))
420
0
        {
421
0
            auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
422
0
            if (poSubGroup)
423
0
                stackGroups.insert(insertionPoint, std::move(poSubGroup));
424
0
        }
425
0
    }
426
427
0
    return ret;
428
0
}
429
430
/************************************************************************/
431
/*                            OpenMDArray()                             */
432
/************************************************************************/
433
434
/** Open and return a multidimensional array.
435
 *
436
 * @note Driver implementation: optionally implemented. If implemented,
437
 * GetMDArrayNames() should also be implemented.
438
 *
439
 * Drivers known to implement it: MEM, netCDF.
440
 *
441
 * This is the same as the C function GDALGroupOpenMDArray().
442
 *
443
 * @param osName Array name.
444
 * @param papszOptions Driver specific options determining how the array should
445
 * be opened.  Pass nullptr for default behavior.
446
 *
447
 * @return the array, or nullptr.
448
 */
449
std::shared_ptr<GDALMDArray>
450
GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
451
                       CPL_UNUSED CSLConstList papszOptions) const
452
0
{
453
0
    return nullptr;
454
0
}
455
456
/************************************************************************/
457
/*                           GetGroupNames()                            */
458
/************************************************************************/
459
460
/** Return the list of sub-groups contained in this group.
461
 *
462
 * @note Driver implementation: optionally implemented. If implemented,
463
 * OpenGroup() should also be implemented.
464
 *
465
 * Drivers known to implement it: MEM, netCDF.
466
 *
467
 * This is the same as the C function GDALGroupGetGroupNames().
468
 *
469
 * @param papszOptions Driver specific options determining how groups
470
 * should be retrieved. Pass nullptr for default behavior.
471
 *
472
 * @return the group names.
473
 */
474
std::vector<std::string>
475
GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
476
0
{
477
0
    return {};
478
0
}
479
480
/************************************************************************/
481
/*                             OpenGroup()                              */
482
/************************************************************************/
483
484
/** Open and return a sub-group.
485
 *
486
 * @note Driver implementation: optionally implemented. If implemented,
487
 * GetGroupNames() should also be implemented.
488
 *
489
 * Drivers known to implement it: MEM, netCDF.
490
 *
491
 * This is the same as the C function GDALGroupOpenGroup().
492
 *
493
 * @param osName Sub-group name.
494
 * @param papszOptions Driver specific options determining how the sub-group
495
 * should be opened.  Pass nullptr for default behavior.
496
 *
497
 * @return the group, or nullptr.
498
 */
499
std::shared_ptr<GDALGroup>
500
GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
501
                     CPL_UNUSED CSLConstList papszOptions) const
502
0
{
503
0
    return nullptr;
504
0
}
505
506
/************************************************************************/
507
/*                        GetVectorLayerNames()                         */
508
/************************************************************************/
509
510
/** Return the list of layer names contained in this group.
511
 *
512
 * @note Driver implementation: optionally implemented. If implemented,
513
 * OpenVectorLayer() should also be implemented.
514
 *
515
 * Drivers known to implement it: OpenFileGDB, FileGDB
516
 *
517
 * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
518
 * GDALDataset::GetLayer() should then be used.
519
 *
520
 * This is the same as the C function GDALGroupGetVectorLayerNames().
521
 *
522
 * @param papszOptions Driver specific options determining how layers
523
 * should be retrieved. Pass nullptr for default behavior.
524
 *
525
 * @return the vector layer names.
526
 * @since GDAL 3.4
527
 */
528
std::vector<std::string>
529
GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
530
0
{
531
0
    return {};
532
0
}
533
534
/************************************************************************/
535
/*                           OpenVectorLayer()                          */
536
/************************************************************************/
537
538
/** Open and return a vector layer.
539
 *
540
 * Due to the historical ownership of OGRLayer* by GDALDataset*, the
541
 * lifetime of the returned OGRLayer* is linked to the one of the owner
542
 * dataset (contrary to the general design of this class where objects can be
543
 * used independently of the object that returned them)
544
 *
545
 * @note Driver implementation: optionally implemented. If implemented,
546
 * GetVectorLayerNames() should also be implemented.
547
 *
548
 * Drivers known to implement it: MEM, netCDF.
549
 *
550
 * This is the same as the C function GDALGroupOpenVectorLayer().
551
 *
552
 * @param osName Vector layer name.
553
 * @param papszOptions Driver specific options determining how the layer should
554
 * be opened.  Pass nullptr for default behavior.
555
 *
556
 * @return the group, or nullptr.
557
 */
558
OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
559
                                     CPL_UNUSED CSLConstList papszOptions) const
560
0
{
561
0
    return nullptr;
562
0
}
563
564
/************************************************************************/
565
/*                             GetDimensions()                          */
566
/************************************************************************/
567
568
/** Return the list of dimensions contained in this group and used by its
569
 * arrays.
570
 *
571
 * This is for dimensions that can potentially be used by several arrays.
572
 * Not all drivers might implement this. To retrieve the dimensions used by
573
 * a specific array, use GDALMDArray::GetDimensions().
574
 *
575
 * Drivers known to implement it: MEM, netCDF
576
 *
577
 * This is the same as the C function GDALGroupGetDimensions().
578
 *
579
 * @param papszOptions Driver specific options determining how groups
580
 * should be retrieved. Pass nullptr for default behavior.
581
 *
582
 * @return the dimensions.
583
 */
584
std::vector<std::shared_ptr<GDALDimension>>
585
GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
586
0
{
587
0
    return {};
588
0
}
589
590
/************************************************************************/
591
/*                         GetStructuralInfo()                          */
592
/************************************************************************/
593
594
/** Return structural information on the group.
595
 *
596
 * This may be the compression, etc..
597
 *
598
 * The return value should not be freed and is valid until GDALGroup is
599
 * released or this function called again.
600
 *
601
 * This is the same as the C function GDALGroupGetStructuralInfo().
602
 */
603
CSLConstList GDALGroup::GetStructuralInfo() const
604
0
{
605
0
    return nullptr;
606
0
}
607
608
/************************************************************************/
609
/*                              CreateGroup()                           */
610
/************************************************************************/
611
612
/** Create a sub-group within a group.
613
 *
614
 * Optionally implemented by drivers.
615
 *
616
 * Drivers known to implement it: MEM, netCDF
617
 *
618
 * This is the same as the C function GDALGroupCreateGroup().
619
 *
620
 * @param osName Sub-group name.
621
 * @param papszOptions Driver specific options determining how the sub-group
622
 * should be created.
623
 *
624
 * @return the new sub-group, or nullptr in case of error.
625
 */
626
std::shared_ptr<GDALGroup>
627
GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
628
                       CPL_UNUSED CSLConstList papszOptions)
629
0
{
630
0
    CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
631
0
    return nullptr;
632
0
}
633
634
/************************************************************************/
635
/*                          DeleteGroup()                               */
636
/************************************************************************/
637
638
/** Delete a sub-group from a group.
639
 *
640
 * Optionally implemented.
641
 *
642
 * After this call, if a previously obtained instance of the deleted object
643
 * is still alive, no method other than for freeing it should be invoked.
644
 *
645
 * Drivers known to implement it: MEM, Zarr
646
 *
647
 * This is the same as the C function GDALGroupDeleteGroup().
648
 *
649
 * @param osName Sub-group name.
650
 * @param papszOptions Driver specific options determining how the group.
651
 * should be deleted.
652
 *
653
 * @return true in case of success
654
 * @since GDAL 3.8
655
 */
656
bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
657
                            CPL_UNUSED CSLConstList papszOptions)
658
0
{
659
0
    CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
660
0
    return false;
661
0
}
662
663
/************************************************************************/
664
/*                            CreateDimension()                         */
665
/************************************************************************/
666
667
/** Create a dimension within a group.
668
 *
669
 * @note Driver implementation: drivers supporting CreateDimension() should
670
 * implement this method, but do not have necessarily to implement
671
 * GDALGroup::GetDimensions().
672
 *
673
 * Drivers known to implement it: MEM, netCDF
674
 *
675
 * This is the same as the C function GDALGroupCreateDimension().
676
 *
677
 * @param osName Dimension name.
678
 * @param osType Dimension type (might be empty, and ignored by drivers)
679
 * @param osDirection Dimension direction (might be empty, and ignored by
680
 * drivers)
681
 * @param nSize  Number of values indexed by this dimension. Should be > 0.
682
 * @param papszOptions Driver specific options determining how the dimension
683
 * should be created.
684
 *
685
 * @return the new dimension, or nullptr if case of error
686
 */
687
std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
688
    CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
689
    CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
690
    CPL_UNUSED CSLConstList papszOptions)
691
0
{
692
0
    CPLError(CE_Failure, CPLE_NotSupported,
693
0
             "CreateDimension() not implemented");
694
0
    return nullptr;
695
0
}
696
697
/************************************************************************/
698
/*                             CreateMDArray()                          */
699
/************************************************************************/
700
701
/** Create a multidimensional array within a group.
702
 *
703
 * It is recommended that the GDALDimension objects passed in aoDimensions
704
 * belong to this group, either by retrieving them with GetDimensions()
705
 * or creating a new one with CreateDimension().
706
 *
707
 * Optionally implemented.
708
 *
709
 * Drivers known to implement it: MEM, netCDF
710
 *
711
 * This is the same as the C function GDALGroupCreateMDArray().
712
 *
713
 * @note Driver implementation: drivers should take into account the possibility
714
 * that GDALDimension object passed in aoDimensions might belong to a different
715
 * group / dataset / driver and act accordingly.
716
 *
717
 * @param osName Array name.
718
 * @param aoDimensions List of dimensions, ordered from the slowest varying
719
 *                     dimension first to the fastest varying dimension last.
720
 *                     Might be empty for a scalar array (if supported by
721
 * driver)
722
 * @param oDataType  Array data type.
723
 * @param papszOptions Driver specific options determining how the array
724
 * should be created.
725
 *
726
 * @return the new array, or nullptr in case of error
727
 */
728
std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
729
    CPL_UNUSED const std::string &osName,
730
    CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
731
    CPL_UNUSED const GDALExtendedDataType &oDataType,
732
    CPL_UNUSED CSLConstList papszOptions)
733
0
{
734
0
    CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
735
0
    return nullptr;
736
0
}
737
738
/************************************************************************/
739
/*                          DeleteMDArray()                             */
740
/************************************************************************/
741
742
/** Delete an array from a group.
743
 *
744
 * Optionally implemented.
745
 *
746
 * After this call, if a previously obtained instance of the deleted object
747
 * is still alive, no method other than for freeing it should be invoked.
748
 *
749
 * Drivers known to implement it: MEM, Zarr
750
 *
751
 * This is the same as the C function GDALGroupDeleteMDArray().
752
 *
753
 * @param osName Arrayname.
754
 * @param papszOptions Driver specific options determining how the array.
755
 * should be deleted.
756
 *
757
 * @return true in case of success
758
 * @since GDAL 3.8
759
 */
760
bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
761
                              CPL_UNUSED CSLConstList papszOptions)
762
0
{
763
0
    CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
764
0
    return false;
765
0
}
766
767
/************************************************************************/
768
/*                           GetTotalCopyCost()                         */
769
/************************************************************************/
770
771
/** Return a total "cost" to copy the group.
772
 *
773
 * Used as a parameter for CopFrom()
774
 */
775
GUInt64 GDALGroup::GetTotalCopyCost() const
776
0
{
777
0
    GUInt64 nCost = COPY_COST;
778
0
    nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
779
780
0
    auto groupNames = GetGroupNames();
781
0
    for (const auto &name : groupNames)
782
0
    {
783
0
        auto subGroup = OpenGroup(name);
784
0
        if (subGroup)
785
0
        {
786
0
            nCost += subGroup->GetTotalCopyCost();
787
0
        }
788
0
    }
789
790
0
    auto arrayNames = GetMDArrayNames();
791
0
    for (const auto &name : arrayNames)
792
0
    {
793
0
        auto array = OpenMDArray(name);
794
0
        if (array)
795
0
        {
796
0
            nCost += array->GetTotalCopyCost();
797
0
        }
798
0
    }
799
0
    return nCost;
800
0
}
801
802
/************************************************************************/
803
/*                               CopyFrom()                             */
804
/************************************************************************/
805
806
/** Copy the content of a group into a new (generally empty) group.
807
 *
808
 * @param poDstRootGroup Destination root group. Must NOT be nullptr.
809
 * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
810
 *                   of some output drivers this is not recommended)
811
 * @param poSrcGroup Source group. Must NOT be nullptr.
812
 * @param bStrict Whether to enable strict mode. In strict mode, any error will
813
 *                stop the copy. In relaxed mode, the copy will be attempted to
814
 *                be pursued.
815
 * @param nCurCost  Should be provided as a variable initially set to 0.
816
 * @param nTotalCost Total cost from GetTotalCopyCost().
817
 * @param pfnProgress Progress callback, or nullptr.
818
 * @param pProgressData Progress user data, or nulptr.
819
 * @param papszOptions Creation options. Currently, only array creation
820
 *                     options are supported. They must be prefixed with
821
 * "ARRAY:" . The scope may be further restricted to arrays of a certain
822
 *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
823
 *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
824
 *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
825
 *                     Restriction to arrays of a given name is done with adding
826
 *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
827
 *                     a full qualified name.
828
 *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
829
 * be used to ask (non indexing) variables of type Float32 or Float64 to be
830
 * scaled to UInt16 with scale and offset values being computed from the minimum
831
 * and maximum of the source array. The integer data type used can be set with
832
 *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
833
 *
834
 * @return true in case of success (or partial success if bStrict == false).
835
 */
836
bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
837
                         GDALDataset *poSrcDS,
838
                         const std::shared_ptr<GDALGroup> &poSrcGroup,
839
                         bool bStrict, GUInt64 &nCurCost,
840
                         const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
841
                         void *pProgressData, CSLConstList papszOptions)
842
0
{
843
0
    if (pfnProgress == nullptr)
844
0
        pfnProgress = GDALDummyProgress;
845
846
0
#define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
847
0
    if (!(x))                                                                  \
848
0
    {                                                                          \
849
0
        if (bStrict)                                                           \
850
0
            return false;                                                      \
851
0
        continue;                                                              \
852
0
    }                                                                          \
853
0
    (void)0
854
855
0
    try
856
0
    {
857
0
        nCurCost += GDALGroup::COPY_COST;
858
859
0
        const auto srcDims = poSrcGroup->GetDimensions();
860
0
        std::map<std::string, std::shared_ptr<GDALDimension>>
861
0
            mapExistingDstDims;
862
0
        std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
863
0
        for (const auto &dim : srcDims)
864
0
        {
865
0
            auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
866
0
                                          dim->GetDirection(), dim->GetSize());
867
0
            EXIT_OR_CONTINUE_IF_NULL(dstDim);
868
0
            mapExistingDstDims[dim->GetName()] = std::move(dstDim);
869
0
            auto poIndexingVarSrc(dim->GetIndexingVariable());
870
0
            if (poIndexingVarSrc)
871
0
            {
872
0
                mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
873
0
                                                       ->GetName()] =
874
0
                    dim->GetName();
875
0
            }
876
0
        }
877
878
0
        auto attrs = poSrcGroup->GetAttributes();
879
0
        for (const auto &attr : attrs)
880
0
        {
881
0
            auto dstAttr =
882
0
                CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
883
0
                                attr->GetDataType());
884
0
            EXIT_OR_CONTINUE_IF_NULL(dstAttr);
885
0
            auto raw(attr->ReadAsRaw());
886
0
            if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
887
0
                return false;
888
0
        }
889
0
        if (!attrs.empty())
890
0
        {
891
0
            nCurCost += attrs.size() * GDALAttribute::COPY_COST;
892
0
            if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
893
0
                return false;
894
0
        }
895
896
0
        const auto CopyArray =
897
0
            [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
898
0
             &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
899
0
             papszOptions, bStrict, &nCurCost,
900
0
             nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
901
0
        {
902
            // Map source dimensions to target dimensions
903
0
            std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
904
0
            const auto &srcArrayDims(srcArray->GetDimensions());
905
0
            for (const auto &dim : srcArrayDims)
906
0
            {
907
0
                auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
908
0
                    dim->GetFullName());
909
0
                if (dstDim && dstDim->GetSize() == dim->GetSize())
910
0
                {
911
0
                    dstArrayDims.emplace_back(dstDim);
912
0
                }
913
0
                else
914
0
                {
915
0
                    auto oIter = mapExistingDstDims.find(dim->GetName());
916
0
                    if (oIter != mapExistingDstDims.end() &&
917
0
                        oIter->second->GetSize() == dim->GetSize())
918
0
                    {
919
0
                        dstArrayDims.emplace_back(oIter->second);
920
0
                    }
921
0
                    else
922
0
                    {
923
0
                        std::string newDimName;
924
0
                        if (oIter == mapExistingDstDims.end())
925
0
                        {
926
0
                            newDimName = dim->GetName();
927
0
                        }
928
0
                        else
929
0
                        {
930
0
                            std::string newDimNamePrefix(srcArray->GetName() +
931
0
                                                         '_' + dim->GetName());
932
0
                            newDimName = newDimNamePrefix;
933
0
                            int nIterCount = 2;
934
0
                            while (
935
0
                                cpl::contains(mapExistingDstDims, newDimName))
936
0
                            {
937
0
                                newDimName = newDimNamePrefix +
938
0
                                             CPLSPrintf("_%d", nIterCount);
939
0
                                nIterCount++;
940
0
                            }
941
0
                        }
942
0
                        dstDim = CreateDimension(newDimName, dim->GetType(),
943
0
                                                 dim->GetDirection(),
944
0
                                                 dim->GetSize());
945
0
                        if (!dstDim)
946
0
                            return false;
947
0
                        mapExistingDstDims[newDimName] = dstDim;
948
0
                        dstArrayDims.emplace_back(dstDim);
949
0
                    }
950
0
                }
951
0
            }
952
953
0
            CPLStringList aosArrayCO;
954
0
            bool bAutoScale = false;
955
0
            GDALDataType eAutoScaleType = GDT_UInt16;
956
0
            for (const char *pszItem : cpl::Iterate(papszOptions))
957
0
            {
958
0
                if (STARTS_WITH_CI(pszItem, "ARRAY:"))
959
0
                {
960
0
                    const char *pszOption = pszItem + strlen("ARRAY:");
961
0
                    if (STARTS_WITH_CI(pszOption, "IF(DIM="))
962
0
                    {
963
0
                        const char *pszNext = strchr(pszOption, ':');
964
0
                        if (pszNext != nullptr)
965
0
                        {
966
0
                            int nDim = atoi(pszOption + strlen("IF(DIM="));
967
0
                            if (static_cast<size_t>(nDim) ==
968
0
                                dstArrayDims.size())
969
0
                            {
970
0
                                pszOption = pszNext + 1;
971
0
                            }
972
0
                            else
973
0
                            {
974
0
                                pszOption = nullptr;
975
0
                            }
976
0
                        }
977
0
                    }
978
0
                    else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
979
0
                    {
980
0
                        const char *pszName = pszOption + strlen("IF(NAME=");
981
0
                        const char *pszNext = strchr(pszName, ':');
982
0
                        if (pszNext != nullptr && pszNext > pszName &&
983
0
                            pszNext[-1] == ')')
984
0
                        {
985
0
                            CPLString osName;
986
0
                            osName.assign(pszName, pszNext - pszName - 1);
987
0
                            if (osName == srcArray->GetName() ||
988
0
                                osName == srcArray->GetFullName())
989
0
                            {
990
0
                                pszOption = pszNext + 1;
991
0
                            }
992
0
                            else
993
0
                            {
994
0
                                pszOption = nullptr;
995
0
                            }
996
0
                        }
997
0
                    }
998
0
                    if (pszOption)
999
0
                    {
1000
0
                        if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1001
0
                        {
1002
0
                            bAutoScale =
1003
0
                                CPLTestBool(pszOption + strlen("AUTOSCALE="));
1004
0
                        }
1005
0
                        else if (STARTS_WITH_CI(pszOption,
1006
0
                                                "AUTOSCALE_DATA_TYPE="))
1007
0
                        {
1008
0
                            const char *pszDataType =
1009
0
                                pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1010
0
                            eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1011
0
                            if (GDALDataTypeIsComplex(eAutoScaleType) ||
1012
0
                                GDALDataTypeIsFloating(eAutoScaleType))
1013
0
                            {
1014
0
                                CPLError(CE_Failure, CPLE_NotSupported,
1015
0
                                         "Unsupported value for "
1016
0
                                         "AUTOSCALE_DATA_TYPE");
1017
0
                                return false;
1018
0
                            }
1019
0
                        }
1020
0
                        else
1021
0
                        {
1022
0
                            aosArrayCO.AddString(pszOption);
1023
0
                        }
1024
0
                    }
1025
0
                }
1026
0
            }
1027
1028
0
            auto oIterDimName =
1029
0
                mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1030
0
            const auto &srcArrayType = srcArray->GetDataType();
1031
1032
0
            std::shared_ptr<GDALMDArray> dstArray;
1033
1034
            // Only autoscale non-indexing variables
1035
0
            bool bHasOffset = false;
1036
0
            bool bHasScale = false;
1037
0
            if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1038
0
                (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1039
0
                 srcArrayType.GetNumericDataType() == GDT_Float32 ||
1040
0
                 srcArrayType.GetNumericDataType() == GDT_Float64) &&
1041
0
                srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1042
0
                srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1043
0
                oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1044
0
            {
1045
0
                constexpr bool bApproxOK = false;
1046
0
                constexpr bool bForce = true;
1047
0
                double dfMin = 0.0;
1048
0
                double dfMax = 0.0;
1049
0
                if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1050
0
                                            nullptr, nullptr, nullptr, nullptr,
1051
0
                                            nullptr) != CE_None)
1052
0
                {
1053
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1054
0
                             "Could not retrieve statistics for array %s",
1055
0
                             srcArray->GetName().c_str());
1056
0
                    return false;
1057
0
                }
1058
0
                double dfDTMin = 0;
1059
0
                double dfDTMax = 0;
1060
0
#define setDTMinMax(ctype)                                                     \
1061
0
    do                                                                         \
1062
0
    {                                                                          \
1063
0
        dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
1064
0
        dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
1065
0
    } while (0)
1066
1067
0
                switch (eAutoScaleType)
1068
0
                {
1069
0
                    case GDT_Byte:
1070
0
                        setDTMinMax(GByte);
1071
0
                        break;
1072
0
                    case GDT_Int8:
1073
0
                        setDTMinMax(GInt8);
1074
0
                        break;
1075
0
                    case GDT_UInt16:
1076
0
                        setDTMinMax(GUInt16);
1077
0
                        break;
1078
0
                    case GDT_Int16:
1079
0
                        setDTMinMax(GInt16);
1080
0
                        break;
1081
0
                    case GDT_UInt32:
1082
0
                        setDTMinMax(GUInt32);
1083
0
                        break;
1084
0
                    case GDT_Int32:
1085
0
                        setDTMinMax(GInt32);
1086
0
                        break;
1087
0
                    case GDT_UInt64:
1088
0
                        setDTMinMax(std::uint64_t);
1089
0
                        break;
1090
0
                    case GDT_Int64:
1091
0
                        setDTMinMax(std::int64_t);
1092
0
                        break;
1093
0
                    case GDT_Float16:
1094
0
                    case GDT_Float32:
1095
0
                    case GDT_Float64:
1096
0
                    case GDT_Unknown:
1097
0
                    case GDT_CInt16:
1098
0
                    case GDT_CInt32:
1099
0
                    case GDT_CFloat16:
1100
0
                    case GDT_CFloat32:
1101
0
                    case GDT_CFloat64:
1102
0
                    case GDT_TypeCount:
1103
0
                        CPLAssert(false);
1104
0
                }
1105
1106
0
                dstArray =
1107
0
                    CreateMDArray(srcArray->GetName(), dstArrayDims,
1108
0
                                  GDALExtendedDataType::Create(eAutoScaleType),
1109
0
                                  aosArrayCO.List());
1110
0
                if (!dstArray)
1111
0
                    return !bStrict;
1112
1113
0
                if (srcArray->GetRawNoDataValue() != nullptr)
1114
0
                {
1115
                    // If there's a nodata value in the source array, reserve
1116
                    // DTMax for that purpose in the target scaled array
1117
0
                    if (!dstArray->SetNoDataValue(dfDTMax))
1118
0
                    {
1119
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1120
0
                                 "Cannot set nodata value");
1121
0
                        return false;
1122
0
                    }
1123
0
                    dfDTMax--;
1124
0
                }
1125
0
                const double dfScale =
1126
0
                    dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1127
0
                const double dfOffset = dfMin - dfDTMin * dfScale;
1128
1129
0
                if (!dstArray->SetOffset(dfOffset) ||
1130
0
                    !dstArray->SetScale(dfScale))
1131
0
                {
1132
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1133
0
                             "Cannot set scale/offset");
1134
0
                    return false;
1135
0
                }
1136
1137
0
                auto poUnscaled = dstArray->GetUnscaled();
1138
0
                if (srcArray->GetRawNoDataValue() != nullptr)
1139
0
                {
1140
0
                    poUnscaled->SetNoDataValue(
1141
0
                        srcArray->GetNoDataValueAsDouble());
1142
0
                }
1143
1144
                // Copy source array into unscaled array
1145
0
                if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1146
0
                                          nCurCost, nTotalCost, pfnProgress,
1147
0
                                          pProgressData))
1148
0
                {
1149
0
                    return false;
1150
0
                }
1151
0
            }
1152
0
            else
1153
0
            {
1154
0
                dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1155
0
                                         srcArrayType, aosArrayCO.List());
1156
0
                if (!dstArray)
1157
0
                    return !bStrict;
1158
1159
0
                if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1160
0
                                        nCurCost, nTotalCost, pfnProgress,
1161
0
                                        pProgressData))
1162
0
                {
1163
0
                    return false;
1164
0
                }
1165
0
            }
1166
1167
            // If this array is the indexing variable of a dimension, link them
1168
            // together.
1169
0
            if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1170
0
            {
1171
0
                auto oCorrespondingDimIter =
1172
0
                    mapExistingDstDims.find(oIterDimName->second);
1173
0
                if (oCorrespondingDimIter != mapExistingDstDims.end())
1174
0
                {
1175
0
                    CPLErrorStateBackuper oErrorStateBackuper(
1176
0
                        CPLQuietErrorHandler);
1177
0
                    oCorrespondingDimIter->second->SetIndexingVariable(
1178
0
                        std::move(dstArray));
1179
0
                }
1180
0
            }
1181
1182
0
            return true;
1183
0
        };
1184
1185
0
        const auto arrayNames = poSrcGroup->GetMDArrayNames();
1186
1187
        // Start by copying arrays that are indexing variables of dimensions
1188
0
        for (const auto &name : arrayNames)
1189
0
        {
1190
0
            auto srcArray = poSrcGroup->OpenMDArray(name);
1191
0
            EXIT_OR_CONTINUE_IF_NULL(srcArray);
1192
1193
0
            if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1194
0
                              srcArray->GetName()))
1195
0
            {
1196
0
                if (!CopyArray(srcArray))
1197
0
                    return false;
1198
0
            }
1199
0
        }
1200
1201
        // Then copy regular arrays
1202
0
        for (const auto &name : arrayNames)
1203
0
        {
1204
0
            auto srcArray = poSrcGroup->OpenMDArray(name);
1205
0
            EXIT_OR_CONTINUE_IF_NULL(srcArray);
1206
1207
0
            if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1208
0
                               srcArray->GetName()))
1209
0
            {
1210
0
                if (!CopyArray(srcArray))
1211
0
                    return false;
1212
0
            }
1213
0
        }
1214
1215
0
        const auto groupNames = poSrcGroup->GetGroupNames();
1216
0
        for (const auto &name : groupNames)
1217
0
        {
1218
0
            auto srcSubGroup = poSrcGroup->OpenGroup(name);
1219
0
            EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1220
0
            auto dstSubGroup = CreateGroup(name);
1221
0
            EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1222
0
            if (!dstSubGroup->CopyFrom(
1223
0
                    poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1224
0
                    nTotalCost, pfnProgress, pProgressData, papszOptions))
1225
0
                return false;
1226
0
        }
1227
1228
0
        if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1229
0
            return false;
1230
1231
0
        return true;
1232
0
    }
1233
0
    catch (const std::exception &e)
1234
0
    {
1235
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1236
0
        return false;
1237
0
    }
1238
0
}
1239
1240
/************************************************************************/
1241
/*                         GetInnerMostGroup()                          */
1242
/************************************************************************/
1243
1244
//! @cond Doxygen_Suppress
1245
const GDALGroup *
1246
GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1247
                             std::shared_ptr<GDALGroup> &curGroupHolder,
1248
                             std::string &osLastPart) const
1249
0
{
1250
0
    if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1251
0
        return nullptr;
1252
0
    const GDALGroup *poCurGroup = this;
1253
0
    CPLStringList aosTokens(
1254
0
        CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1255
0
    if (aosTokens.size() == 0)
1256
0
    {
1257
0
        return nullptr;
1258
0
    }
1259
1260
0
    for (int i = 0; i < aosTokens.size() - 1; i++)
1261
0
    {
1262
0
        curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1263
0
        if (!curGroupHolder)
1264
0
        {
1265
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1266
0
                     aosTokens[i]);
1267
0
            return nullptr;
1268
0
        }
1269
0
        poCurGroup = curGroupHolder.get();
1270
0
    }
1271
0
    osLastPart = aosTokens[aosTokens.size() - 1];
1272
0
    return poCurGroup;
1273
0
}
1274
1275
//! @endcond
1276
1277
/************************************************************************/
1278
/*                      OpenMDArrayFromFullname()                       */
1279
/************************************************************************/
1280
1281
/** Get an array from its fully qualified name */
1282
std::shared_ptr<GDALMDArray>
1283
GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1284
                                   CSLConstList papszOptions) const
1285
0
{
1286
0
    std::string osName;
1287
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1288
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1289
0
    if (poGroup == nullptr)
1290
0
        return nullptr;
1291
0
    return poGroup->OpenMDArray(osName, papszOptions);
1292
0
}
1293
1294
/************************************************************************/
1295
/*                      OpenAttributeFromFullname()                     */
1296
/************************************************************************/
1297
1298
/** Get an attribute from its fully qualified name */
1299
std::shared_ptr<GDALAttribute>
1300
GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1301
                                     CSLConstList papszOptions) const
1302
0
{
1303
0
    const auto pos = osFullName.rfind('/');
1304
0
    if (pos == std::string::npos)
1305
0
        return nullptr;
1306
0
    const std::string attrName = osFullName.substr(pos + 1);
1307
0
    if (pos == 0)
1308
0
        return GetAttribute(attrName);
1309
0
    const std::string container = osFullName.substr(0, pos);
1310
0
    auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1311
0
    if (poArray)
1312
0
        return poArray->GetAttribute(attrName);
1313
0
    auto poGroup = OpenGroupFromFullname(container, papszOptions);
1314
0
    if (poGroup)
1315
0
        return poGroup->GetAttribute(attrName);
1316
0
    return nullptr;
1317
0
}
1318
1319
/************************************************************************/
1320
/*                          ResolveMDArray()                            */
1321
/************************************************************************/
1322
1323
/** Locate an array in a group and its subgroups by name.
1324
 *
1325
 * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1326
 * used
1327
 * Otherwise the search will start from the group identified by osStartingPath,
1328
 * and an array whose name is osName will be looked for in this group (if
1329
 * osStartingPath is empty or "/", then the current group is used). If there
1330
 * is no match, then a recursive descendent search will be made in its
1331
 * subgroups. If there is no match in the subgroups, then the parent (if
1332
 * existing) of the group pointed by osStartingPath will be used as the new
1333
 * starting point for the search.
1334
 *
1335
 * @param osName name, qualified or not
1336
 * @param osStartingPath fully qualified name of the (sub-)group from which
1337
 *                       the search should be started. If this is a non-empty
1338
 *                       string, the group on which this method is called should
1339
 *                       nominally be the root group (otherwise the path will
1340
 *                       be interpreted as from the current group)
1341
 * @param papszOptions options to pass to OpenMDArray()
1342
 * @since GDAL 3.2
1343
 */
1344
std::shared_ptr<GDALMDArray>
1345
GDALGroup::ResolveMDArray(const std::string &osName,
1346
                          const std::string &osStartingPath,
1347
                          CSLConstList papszOptions) const
1348
0
{
1349
0
    if (!osName.empty() && osName[0] == '/')
1350
0
    {
1351
0
        auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1352
0
        if (poArray)
1353
0
            return poArray;
1354
0
    }
1355
0
    std::string osPath(osStartingPath);
1356
0
    std::set<std::string> oSetAlreadyVisited;
1357
1358
0
    while (true)
1359
0
    {
1360
0
        std::shared_ptr<GDALGroup> curGroupHolder;
1361
0
        std::shared_ptr<GDALGroup> poGroup;
1362
1363
0
        std::queue<std::shared_ptr<GDALGroup>> oQueue;
1364
0
        bool goOn = false;
1365
0
        if (osPath.empty() || osPath == "/")
1366
0
        {
1367
0
            goOn = true;
1368
0
        }
1369
0
        else
1370
0
        {
1371
0
            std::string osLastPart;
1372
0
            const GDALGroup *poGroupPtr =
1373
0
                GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1374
0
            if (poGroupPtr)
1375
0
                poGroup = poGroupPtr->OpenGroup(osLastPart);
1376
0
            if (poGroup &&
1377
0
                !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1378
0
            {
1379
0
                oQueue.push(poGroup);
1380
0
                goOn = true;
1381
0
            }
1382
0
        }
1383
1384
0
        if (goOn)
1385
0
        {
1386
0
            do
1387
0
            {
1388
0
                const GDALGroup *groupPtr;
1389
0
                if (!oQueue.empty())
1390
0
                {
1391
0
                    poGroup = oQueue.front();
1392
0
                    oQueue.pop();
1393
0
                    groupPtr = poGroup.get();
1394
0
                }
1395
0
                else
1396
0
                {
1397
0
                    groupPtr = this;
1398
0
                }
1399
1400
0
                auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1401
0
                if (poArray)
1402
0
                    return poArray;
1403
1404
0
                const auto aosGroupNames = groupPtr->GetGroupNames();
1405
0
                for (const auto &osGroupName : aosGroupNames)
1406
0
                {
1407
0
                    auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1408
0
                    if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1409
0
                                                     poSubGroup->GetFullName()))
1410
0
                    {
1411
0
                        oQueue.push(poSubGroup);
1412
0
                        oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1413
0
                    }
1414
0
                }
1415
0
            } while (!oQueue.empty());
1416
0
        }
1417
1418
0
        if (osPath.empty() || osPath == "/")
1419
0
            break;
1420
1421
0
        const auto nPos = osPath.rfind('/');
1422
0
        if (nPos == 0)
1423
0
            osPath = "/";
1424
0
        else
1425
0
        {
1426
0
            if (nPos == std::string::npos)
1427
0
                break;
1428
0
            osPath.resize(nPos);
1429
0
        }
1430
0
    }
1431
0
    return nullptr;
1432
0
}
1433
1434
/************************************************************************/
1435
/*                       OpenGroupFromFullname()                        */
1436
/************************************************************************/
1437
1438
/** Get a group from its fully qualified name.
1439
 * @since GDAL 3.2
1440
 */
1441
std::shared_ptr<GDALGroup>
1442
GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1443
                                 CSLConstList papszOptions) const
1444
0
{
1445
0
    std::string osName;
1446
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1447
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1448
0
    if (poGroup == nullptr)
1449
0
        return nullptr;
1450
0
    return poGroup->OpenGroup(osName, papszOptions);
1451
0
}
1452
1453
/************************************************************************/
1454
/*                      OpenDimensionFromFullname()                     */
1455
/************************************************************************/
1456
1457
/** Get a dimension from its fully qualified name */
1458
std::shared_ptr<GDALDimension>
1459
GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1460
0
{
1461
0
    std::string osName;
1462
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1463
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1464
0
    if (poGroup == nullptr)
1465
0
        return nullptr;
1466
0
    auto dims(poGroup->GetDimensions());
1467
0
    for (auto &dim : dims)
1468
0
    {
1469
0
        if (dim->GetName() == osName)
1470
0
            return dim;
1471
0
    }
1472
0
    return nullptr;
1473
0
}
1474
1475
/************************************************************************/
1476
/*                           ClearStatistics()                          */
1477
/************************************************************************/
1478
1479
/**
1480
 * \brief Clear statistics.
1481
 *
1482
 * @since GDAL 3.4
1483
 */
1484
void GDALGroup::ClearStatistics()
1485
0
{
1486
0
    auto groupNames = GetGroupNames();
1487
0
    for (const auto &name : groupNames)
1488
0
    {
1489
0
        auto subGroup = OpenGroup(name);
1490
0
        if (subGroup)
1491
0
        {
1492
0
            subGroup->ClearStatistics();
1493
0
        }
1494
0
    }
1495
1496
0
    auto arrayNames = GetMDArrayNames();
1497
0
    for (const auto &name : arrayNames)
1498
0
    {
1499
0
        auto array = OpenMDArray(name);
1500
0
        if (array)
1501
0
        {
1502
0
            array->ClearStatistics();
1503
0
        }
1504
0
    }
1505
0
}
1506
1507
/************************************************************************/
1508
/*                            Rename()                                  */
1509
/************************************************************************/
1510
1511
/** Rename the group.
1512
 *
1513
 * This is not implemented by all drivers.
1514
 *
1515
 * Drivers known to implement it: MEM, netCDF, ZARR.
1516
 *
1517
 * This is the same as the C function GDALGroupRename().
1518
 *
1519
 * @param osNewName New name.
1520
 *
1521
 * @return true in case of success
1522
 * @since GDAL 3.8
1523
 */
1524
bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1525
0
{
1526
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1527
0
    return false;
1528
0
}
1529
1530
/************************************************************************/
1531
/*                         BaseRename()                                 */
1532
/************************************************************************/
1533
1534
//! @cond Doxygen_Suppress
1535
void GDALGroup::BaseRename(const std::string &osNewName)
1536
0
{
1537
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
1538
0
    m_osFullName += osNewName;
1539
0
    m_osName = osNewName;
1540
1541
0
    NotifyChildrenOfRenaming();
1542
0
}
1543
1544
//! @endcond
1545
1546
/************************************************************************/
1547
/*                        ParentRenamed()                               */
1548
/************************************************************************/
1549
1550
//! @cond Doxygen_Suppress
1551
void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1552
0
{
1553
0
    m_osFullName = osNewParentFullName;
1554
0
    m_osFullName += "/";
1555
0
    m_osFullName += m_osName;
1556
1557
0
    NotifyChildrenOfRenaming();
1558
0
}
1559
1560
//! @endcond
1561
1562
/************************************************************************/
1563
/*                             Deleted()                                */
1564
/************************************************************************/
1565
1566
//! @cond Doxygen_Suppress
1567
void GDALGroup::Deleted()
1568
0
{
1569
0
    m_bValid = false;
1570
1571
0
    NotifyChildrenOfDeletion();
1572
0
}
1573
1574
//! @endcond
1575
1576
/************************************************************************/
1577
/*                        ParentDeleted()                               */
1578
/************************************************************************/
1579
1580
//! @cond Doxygen_Suppress
1581
void GDALGroup::ParentDeleted()
1582
0
{
1583
0
    Deleted();
1584
0
}
1585
1586
//! @endcond
1587
1588
/************************************************************************/
1589
/*                     CheckValidAndErrorOutIfNot()                     */
1590
/************************************************************************/
1591
1592
//! @cond Doxygen_Suppress
1593
bool GDALGroup::CheckValidAndErrorOutIfNot() const
1594
0
{
1595
0
    if (!m_bValid)
1596
0
    {
1597
0
        CPLError(CE_Failure, CPLE_AppDefined,
1598
0
                 "This object has been deleted. No action on it is possible");
1599
0
    }
1600
0
    return m_bValid;
1601
0
}
1602
1603
//! @endcond
1604
1605
/************************************************************************/
1606
/*                       ~GDALAbstractMDArray()                         */
1607
/************************************************************************/
1608
1609
0
GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1610
1611
/************************************************************************/
1612
/*                        GDALAbstractMDArray()                         */
1613
/************************************************************************/
1614
1615
//! @cond Doxygen_Suppress
1616
GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1617
                                         const std::string &osName)
1618
0
    : m_osName(osName),
1619
      m_osFullName(
1620
0
          !osParentName.empty()
1621
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1622
0
              : osName)
1623
0
{
1624
0
}
1625
1626
//! @endcond
1627
1628
/************************************************************************/
1629
/*                           GetDimensions()                            */
1630
/************************************************************************/
1631
1632
/** \fn GDALAbstractMDArray::GetDimensions() const
1633
 * \brief Return the dimensions of an attribute/array.
1634
 *
1635
 * This is the same as the C functions GDALMDArrayGetDimensions() and
1636
 * similar to GDALAttributeGetDimensionsSize().
1637
 */
1638
1639
/************************************************************************/
1640
/*                           GetDataType()                              */
1641
/************************************************************************/
1642
1643
/** \fn GDALAbstractMDArray::GetDataType() const
1644
 * \brief Return the data type of an attribute/array.
1645
 *
1646
 * This is the same as the C functions GDALMDArrayGetDataType() and
1647
 * GDALAttributeGetDataType()
1648
 */
1649
1650
/************************************************************************/
1651
/*                        GetDimensionCount()                           */
1652
/************************************************************************/
1653
1654
/** Return the number of dimensions.
1655
 *
1656
 * Default implementation is GetDimensions().size(), and may be overridden by
1657
 * drivers if they have a faster / less expensive implementations.
1658
 *
1659
 * This is the same as the C function GDALMDArrayGetDimensionCount() or
1660
 * GDALAttributeGetDimensionCount().
1661
 *
1662
 */
1663
size_t GDALAbstractMDArray::GetDimensionCount() const
1664
0
{
1665
0
    return GetDimensions().size();
1666
0
}
1667
1668
/************************************************************************/
1669
/*                            Rename()                                  */
1670
/************************************************************************/
1671
1672
/** Rename the attribute/array.
1673
 *
1674
 * This is not implemented by all drivers.
1675
 *
1676
 * Drivers known to implement it: MEM, netCDF, Zarr.
1677
 *
1678
 * This is the same as the C functions GDALMDArrayRename() or
1679
 * GDALAttributeRename().
1680
 *
1681
 * @param osNewName New name.
1682
 *
1683
 * @return true in case of success
1684
 * @since GDAL 3.8
1685
 */
1686
bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1687
0
{
1688
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1689
0
    return false;
1690
0
}
1691
1692
/************************************************************************/
1693
/*                             CopyValue()                              */
1694
/************************************************************************/
1695
1696
/** Convert a value from a source type to a destination type.
1697
 *
1698
 * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1699
 * that must be freed with CPLFree().
1700
 */
1701
bool GDALExtendedDataType::CopyValue(const void *pSrc,
1702
                                     const GDALExtendedDataType &srcType,
1703
                                     void *pDst,
1704
                                     const GDALExtendedDataType &dstType)
1705
0
{
1706
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1707
0
        dstType.GetClass() == GEDTC_NUMERIC)
1708
0
    {
1709
0
        GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1710
0
                        dstType.GetNumericDataType(), 0, 1);
1711
0
        return true;
1712
0
    }
1713
0
    if (srcType.GetClass() == GEDTC_STRING &&
1714
0
        dstType.GetClass() == GEDTC_STRING)
1715
0
    {
1716
0
        const char *srcStrPtr;
1717
0
        memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1718
0
        char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1719
0
        *reinterpret_cast<void **>(pDst) = pszDup;
1720
0
        return true;
1721
0
    }
1722
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1723
0
        dstType.GetClass() == GEDTC_STRING)
1724
0
    {
1725
0
        const char *str = nullptr;
1726
0
        switch (srcType.GetNumericDataType())
1727
0
        {
1728
0
            case GDT_Unknown:
1729
0
                break;
1730
0
            case GDT_Byte:
1731
0
                str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1732
0
                break;
1733
0
            case GDT_Int8:
1734
0
                str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1735
0
                break;
1736
0
            case GDT_UInt16:
1737
0
                str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1738
0
                break;
1739
0
            case GDT_Int16:
1740
0
                str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1741
0
                break;
1742
0
            case GDT_UInt32:
1743
0
                str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1744
0
                break;
1745
0
            case GDT_Int32:
1746
0
                str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1747
0
                break;
1748
0
            case GDT_UInt64:
1749
0
                str =
1750
0
                    CPLSPrintf(CPL_FRMT_GUIB,
1751
0
                               static_cast<GUIntBig>(
1752
0
                                   *static_cast<const std::uint64_t *>(pSrc)));
1753
0
                break;
1754
0
            case GDT_Int64:
1755
0
                str = CPLSPrintf(CPL_FRMT_GIB,
1756
0
                                 static_cast<GIntBig>(
1757
0
                                     *static_cast<const std::int64_t *>(pSrc)));
1758
0
                break;
1759
0
            case GDT_Float16:
1760
0
                str = CPLSPrintf("%.5g",
1761
0
                                 double(*static_cast<const GFloat16 *>(pSrc)));
1762
0
                break;
1763
0
            case GDT_Float32:
1764
0
                str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
1765
0
                break;
1766
0
            case GDT_Float64:
1767
0
                str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1768
0
                break;
1769
0
            case GDT_CInt16:
1770
0
            {
1771
0
                const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1772
0
                str = CPLSPrintf("%d+%dj", src[0], src[1]);
1773
0
                break;
1774
0
            }
1775
0
            case GDT_CInt32:
1776
0
            {
1777
0
                const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1778
0
                str = CPLSPrintf("%d+%dj", src[0], src[1]);
1779
0
                break;
1780
0
            }
1781
0
            case GDT_CFloat16:
1782
0
            {
1783
0
                const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1784
0
                str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1785
0
                break;
1786
0
            }
1787
0
            case GDT_CFloat32:
1788
0
            {
1789
0
                const float *src = static_cast<const float *>(pSrc);
1790
0
                str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1791
0
                break;
1792
0
            }
1793
0
            case GDT_CFloat64:
1794
0
            {
1795
0
                const double *src = static_cast<const double *>(pSrc);
1796
0
                str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1797
0
                break;
1798
0
            }
1799
0
            case GDT_TypeCount:
1800
0
                CPLAssert(false);
1801
0
                break;
1802
0
        }
1803
0
        char *pszDup = str ? CPLStrdup(str) : nullptr;
1804
0
        *reinterpret_cast<void **>(pDst) = pszDup;
1805
0
        return true;
1806
0
    }
1807
0
    if (srcType.GetClass() == GEDTC_STRING &&
1808
0
        dstType.GetClass() == GEDTC_NUMERIC)
1809
0
    {
1810
0
        const char *srcStrPtr;
1811
0
        memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1812
0
        if (dstType.GetNumericDataType() == GDT_Int64)
1813
0
        {
1814
0
            *(static_cast<int64_t *>(pDst)) =
1815
0
                srcStrPtr == nullptr ? 0
1816
0
                                     : static_cast<int64_t>(atoll(srcStrPtr));
1817
0
        }
1818
0
        else if (dstType.GetNumericDataType() == GDT_UInt64)
1819
0
        {
1820
0
            *(static_cast<uint64_t *>(pDst)) =
1821
0
                srcStrPtr == nullptr
1822
0
                    ? 0
1823
0
                    : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1824
0
        }
1825
0
        else
1826
0
        {
1827
            // FIXME GDT_UInt64
1828
0
            const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1829
0
            GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1830
0
                            dstType.GetNumericDataType(), 0, 1);
1831
0
        }
1832
0
        return true;
1833
0
    }
1834
0
    if (srcType.GetClass() == GEDTC_COMPOUND &&
1835
0
        dstType.GetClass() == GEDTC_COMPOUND)
1836
0
    {
1837
0
        const auto &srcComponents = srcType.GetComponents();
1838
0
        const auto &dstComponents = dstType.GetComponents();
1839
0
        const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1840
0
        GByte *pabyDst = static_cast<GByte *>(pDst);
1841
1842
0
        std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1843
0
            srcComponentMap;
1844
0
        for (const auto &srcComp : srcComponents)
1845
0
        {
1846
0
            srcComponentMap[srcComp->GetName()] = &srcComp;
1847
0
        }
1848
0
        for (const auto &dstComp : dstComponents)
1849
0
        {
1850
0
            auto oIter = srcComponentMap.find(dstComp->GetName());
1851
0
            if (oIter == srcComponentMap.end())
1852
0
                return false;
1853
0
            const auto &srcComp = *(oIter->second);
1854
0
            if (!GDALExtendedDataType::CopyValue(
1855
0
                    pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1856
0
                    pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1857
0
            {
1858
0
                return false;
1859
0
            }
1860
0
        }
1861
0
        return true;
1862
0
    }
1863
1864
0
    return false;
1865
0
}
1866
1867
/************************************************************************/
1868
/*                             CopyValues()                             */
1869
/************************************************************************/
1870
1871
/** Convert severals value from a source type to a destination type.
1872
 *
1873
 * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1874
 * that must be freed with CPLFree().
1875
 */
1876
bool GDALExtendedDataType::CopyValues(const void *pSrc,
1877
                                      const GDALExtendedDataType &srcType,
1878
                                      GPtrDiff_t nSrcStrideInElts, void *pDst,
1879
                                      const GDALExtendedDataType &dstType,
1880
                                      GPtrDiff_t nDstStrideInElts,
1881
                                      size_t nValues)
1882
0
{
1883
0
    const auto nSrcStrideInBytes =
1884
0
        nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1885
0
    const auto nDstStrideInBytes =
1886
0
        nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1887
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1888
0
        dstType.GetClass() == GEDTC_NUMERIC &&
1889
0
        nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1890
0
        nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1891
0
        nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1892
0
        nDstStrideInBytes <= std::numeric_limits<int>::max())
1893
0
    {
1894
0
        GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1895
0
                        static_cast<int>(nSrcStrideInBytes), pDst,
1896
0
                        dstType.GetNumericDataType(),
1897
0
                        static_cast<int>(nDstStrideInBytes), nValues);
1898
0
    }
1899
0
    else
1900
0
    {
1901
0
        const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1902
0
        GByte *pabyDst = static_cast<GByte *>(pDst);
1903
0
        for (size_t i = 0; i < nValues; ++i)
1904
0
        {
1905
0
            if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1906
0
                return false;
1907
0
            pabySrc += nSrcStrideInBytes;
1908
0
            pabyDst += nDstStrideInBytes;
1909
0
        }
1910
0
    }
1911
0
    return true;
1912
0
}
1913
1914
/************************************************************************/
1915
/*                       CheckReadWriteParams()                         */
1916
/************************************************************************/
1917
//! @cond Doxygen_Suppress
1918
bool GDALAbstractMDArray::CheckReadWriteParams(
1919
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1920
    const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1921
    const void *buffer, const void *buffer_alloc_start,
1922
    size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1923
    std::vector<GPtrDiff_t> &tmp_bufferStride) const
1924
0
{
1925
0
    const auto lamda_error = []()
1926
0
    {
1927
0
        CPLError(CE_Failure, CPLE_AppDefined,
1928
0
                 "Not all elements pointed by buffer will fit in "
1929
0
                 "[buffer_alloc_start, "
1930
0
                 "buffer_alloc_start + buffer_alloc_size]");
1931
0
    };
1932
1933
0
    const auto &dims = GetDimensions();
1934
0
    if (dims.empty())
1935
0
    {
1936
0
        if (buffer_alloc_start)
1937
0
        {
1938
0
            const size_t elementSize = bufferDataType.GetSize();
1939
0
            const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1940
0
            const GByte *paby_buffer_alloc_start =
1941
0
                static_cast<const GByte *>(buffer_alloc_start);
1942
0
            const GByte *paby_buffer_alloc_end =
1943
0
                paby_buffer_alloc_start + buffer_alloc_size;
1944
1945
0
            if (paby_buffer < paby_buffer_alloc_start ||
1946
0
                paby_buffer + elementSize > paby_buffer_alloc_end)
1947
0
            {
1948
0
                lamda_error();
1949
0
                return false;
1950
0
            }
1951
0
        }
1952
0
        return true;
1953
0
    }
1954
1955
0
    if (arrayStep == nullptr)
1956
0
    {
1957
0
        tmp_arrayStep.resize(dims.size(), 1);
1958
0
        arrayStep = tmp_arrayStep.data();
1959
0
    }
1960
0
    for (size_t i = 0; i < dims.size(); i++)
1961
0
    {
1962
0
        assert(count);
1963
0
        if (count[i] == 0)
1964
0
        {
1965
0
            CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1966
0
                     static_cast<unsigned>(i));
1967
0
            return false;
1968
0
        }
1969
0
    }
1970
0
    bool bufferStride_all_positive = true;
1971
0
    if (bufferStride == nullptr)
1972
0
    {
1973
0
        GPtrDiff_t stride = 1;
1974
0
        assert(dims.empty() || count != nullptr);
1975
        // To compute strides we must proceed from the fastest varying dimension
1976
        // (the last one), and then reverse the result
1977
0
        for (size_t i = dims.size(); i != 0;)
1978
0
        {
1979
0
            --i;
1980
0
            tmp_bufferStride.push_back(stride);
1981
0
            GUInt64 newStride = 0;
1982
0
            bool bOK;
1983
0
            try
1984
0
            {
1985
0
                newStride = (CPLSM(static_cast<uint64_t>(stride)) *
1986
0
                             CPLSM(static_cast<uint64_t>(count[i])))
1987
0
                                .v();
1988
0
                bOK = static_cast<size_t>(newStride) == newStride &&
1989
0
                      newStride < std::numeric_limits<size_t>::max() / 2;
1990
0
            }
1991
0
            catch (...)
1992
0
            {
1993
0
                bOK = false;
1994
0
            }
1995
0
            if (!bOK)
1996
0
            {
1997
0
                CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1998
0
                return false;
1999
0
            }
2000
0
            stride = static_cast<GPtrDiff_t>(newStride);
2001
0
        }
2002
0
        std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2003
0
        bufferStride = tmp_bufferStride.data();
2004
0
    }
2005
0
    else
2006
0
    {
2007
0
        for (size_t i = 0; i < dims.size(); i++)
2008
0
        {
2009
0
            if (bufferStride[i] < 0)
2010
0
            {
2011
0
                bufferStride_all_positive = false;
2012
0
                break;
2013
0
            }
2014
0
        }
2015
0
    }
2016
0
    for (size_t i = 0; i < dims.size(); i++)
2017
0
    {
2018
0
        assert(arrayStartIdx);
2019
0
        assert(count);
2020
0
        if (arrayStartIdx[i] >= dims[i]->GetSize())
2021
0
        {
2022
0
            CPLError(CE_Failure, CPLE_AppDefined,
2023
0
                     "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2024
0
                     static_cast<unsigned>(i),
2025
0
                     static_cast<GUInt64>(arrayStartIdx[i]),
2026
0
                     static_cast<GUInt64>(dims[i]->GetSize()));
2027
0
            return false;
2028
0
        }
2029
0
        bool bOverflow;
2030
0
        if (arrayStep[i] >= 0)
2031
0
        {
2032
0
            try
2033
0
            {
2034
0
                bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2035
0
                             CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2036
0
                                 CPLSM(static_cast<uint64_t>(arrayStep[i])))
2037
0
                                .v() >= dims[i]->GetSize();
2038
0
            }
2039
0
            catch (...)
2040
0
            {
2041
0
                bOverflow = true;
2042
0
            }
2043
0
            if (bOverflow)
2044
0
            {
2045
0
                CPLError(CE_Failure, CPLE_AppDefined,
2046
0
                         "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2047
0
                         ">= " CPL_FRMT_GUIB,
2048
0
                         static_cast<unsigned>(i), static_cast<unsigned>(i),
2049
0
                         static_cast<unsigned>(i),
2050
0
                         static_cast<GUInt64>(dims[i]->GetSize()));
2051
0
                return false;
2052
0
            }
2053
0
        }
2054
0
        else
2055
0
        {
2056
0
            try
2057
0
            {
2058
0
                bOverflow =
2059
0
                    arrayStartIdx[i] <
2060
0
                    (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2061
0
                     CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2062
0
                               ? (static_cast<uint64_t>(1) << 63)
2063
0
                               : static_cast<uint64_t>(-arrayStep[i])))
2064
0
                        .v();
2065
0
            }
2066
0
            catch (...)
2067
0
            {
2068
0
                bOverflow = true;
2069
0
            }
2070
0
            if (bOverflow)
2071
0
            {
2072
0
                CPLError(
2073
0
                    CE_Failure, CPLE_AppDefined,
2074
0
                    "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2075
0
                    static_cast<unsigned>(i), static_cast<unsigned>(i),
2076
0
                    static_cast<unsigned>(i));
2077
0
                return false;
2078
0
            }
2079
0
        }
2080
0
    }
2081
2082
0
    if (buffer_alloc_start)
2083
0
    {
2084
0
        const size_t elementSize = bufferDataType.GetSize();
2085
0
        const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2086
0
        const GByte *paby_buffer_alloc_start =
2087
0
            static_cast<const GByte *>(buffer_alloc_start);
2088
0
        const GByte *paby_buffer_alloc_end =
2089
0
            paby_buffer_alloc_start + buffer_alloc_size;
2090
0
        if (bufferStride_all_positive)
2091
0
        {
2092
0
            if (paby_buffer < paby_buffer_alloc_start)
2093
0
            {
2094
0
                lamda_error();
2095
0
                return false;
2096
0
            }
2097
0
            GUInt64 nOffset = elementSize;
2098
0
            for (size_t i = 0; i < dims.size(); i++)
2099
0
            {
2100
0
                try
2101
0
                {
2102
0
                    nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2103
0
                               CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2104
0
                                   CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2105
0
                                   CPLSM(static_cast<uint64_t>(elementSize)))
2106
0
                                  .v();
2107
0
                }
2108
0
                catch (...)
2109
0
                {
2110
0
                    lamda_error();
2111
0
                    return false;
2112
0
                }
2113
0
            }
2114
#if SIZEOF_VOIDP == 4
2115
            if (static_cast<size_t>(nOffset) != nOffset)
2116
            {
2117
                lamda_error();
2118
                return false;
2119
            }
2120
#endif
2121
0
            if (paby_buffer + nOffset > paby_buffer_alloc_end)
2122
0
            {
2123
0
                lamda_error();
2124
0
                return false;
2125
0
            }
2126
0
        }
2127
0
        else if (dims.size() < 31)
2128
0
        {
2129
            // Check all corners of the hypercube
2130
0
            const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2131
0
            for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2132
0
            {
2133
0
                const GByte *paby = paby_buffer;
2134
0
                for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2135
0
                     i++)
2136
0
                {
2137
0
                    if (iCornerCode & (1U << i))
2138
0
                    {
2139
                        // We should check for integer overflows
2140
0
                        paby += bufferStride[i] * (count[i] - 1) * elementSize;
2141
0
                    }
2142
0
                }
2143
0
                if (paby < paby_buffer_alloc_start ||
2144
0
                    paby + elementSize > paby_buffer_alloc_end)
2145
0
                {
2146
0
                    lamda_error();
2147
0
                    return false;
2148
0
                }
2149
0
            }
2150
0
        }
2151
0
    }
2152
2153
0
    return true;
2154
0
}
2155
2156
//! @endcond
2157
2158
/************************************************************************/
2159
/*                               Read()                                 */
2160
/************************************************************************/
2161
2162
/** Read part or totality of a multidimensional array or attribute.
2163
 *
2164
 * This will extract the content of a hyper-rectangle from the array into
2165
 * a user supplied buffer.
2166
 *
2167
 * If bufferDataType is of type string, the values written in pDstBuffer
2168
 * will be char* pointers and the strings should be freed with CPLFree().
2169
 *
2170
 * This is the same as the C function GDALMDArrayRead().
2171
 *
2172
 * @param arrayStartIdx Values representing the starting index to read
2173
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
2174
 *                      Array of GetDimensionCount() values. Must not be
2175
 *                      nullptr, unless for a zero-dimensional array.
2176
 *
2177
 * @param count         Values representing the number of values to extract in
2178
 *                      each dimension.
2179
 *                      Array of GetDimensionCount() values. Must not be
2180
 *                      nullptr, unless for a zero-dimensional array.
2181
 *
2182
 * @param arrayStep     Spacing between values to extract in each dimension.
2183
 *                      The spacing is in number of array elements, not bytes.
2184
 *                      If provided, must contain GetDimensionCount() values.
2185
 *                      If set to nullptr, [1, 1, ... 1] will be used as a
2186
 * default to indicate consecutive elements.
2187
 *
2188
 * @param bufferStride  Spacing between values to store in pDstBuffer.
2189
 *                      The spacing is in number of array elements, not bytes.
2190
 *                      If provided, must contain GetDimensionCount() values.
2191
 *                      Negative values are possible (for example to reorder
2192
 *                      from bottom-to-top to top-to-bottom).
2193
 *                      If set to nullptr, will be set so that pDstBuffer is
2194
 *                      written in a compact way, with elements of the last /
2195
 *                      fastest varying dimension being consecutive.
2196
 *
2197
 * @param bufferDataType Data type of values in pDstBuffer.
2198
 *
2199
 * @param pDstBuffer    User buffer to store the values read. Should be big
2200
 *                      enough to store the number of values indicated by
2201
 * count[] and with the spacing of bufferStride[].
2202
 *
2203
 * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2204
 *                             validity of pDstBuffer. pDstBufferAllocStart
2205
 * should be the pointer returned by the malloc() or equivalent call used to
2206
 * allocate the buffer. It will generally be equal to pDstBuffer (when
2207
 * bufferStride[] values are all positive), but not necessarily. If specified,
2208
 * nDstBufferAllocSize should be also set to the appropriate value. If no
2209
 * validation is needed, nullptr can be passed.
2210
 *
2211
 * @param nDstBufferAllocSize  Optional buffer size, that can be used to
2212
 * validate the validity of pDstBuffer. This is the size of the buffer starting
2213
 * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2214
 *                             set to the appropriate value.
2215
 *                             If no validation is needed, 0 can be passed.
2216
 *
2217
 * @return true in case of success.
2218
 */
2219
bool GDALAbstractMDArray::Read(
2220
    const GUInt64 *arrayStartIdx, const size_t *count,
2221
    const GInt64 *arrayStep,         // step in elements
2222
    const GPtrDiff_t *bufferStride,  // stride in elements
2223
    const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2224
    const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2225
0
{
2226
0
    if (!GetDataType().CanConvertTo(bufferDataType))
2227
0
    {
2228
0
        CPLError(CE_Failure, CPLE_AppDefined,
2229
0
                 "Array data type is not convertible to buffer data type");
2230
0
        return false;
2231
0
    }
2232
2233
0
    std::vector<GInt64> tmp_arrayStep;
2234
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
2235
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2236
0
                              bufferDataType, pDstBuffer, pDstBufferAllocStart,
2237
0
                              nDstBufferAllocSize, tmp_arrayStep,
2238
0
                              tmp_bufferStride))
2239
0
    {
2240
0
        return false;
2241
0
    }
2242
2243
0
    return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2244
0
                 pDstBuffer);
2245
0
}
2246
2247
/************************************************************************/
2248
/*                                IWrite()                              */
2249
/************************************************************************/
2250
2251
//! @cond Doxygen_Suppress
2252
bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2253
                                 const GInt64 *, const GPtrDiff_t *,
2254
                                 const GDALExtendedDataType &, const void *)
2255
0
{
2256
0
    CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2257
0
    return false;
2258
0
}
2259
2260
//! @endcond
2261
2262
/************************************************************************/
2263
/*                               Write()                                 */
2264
/************************************************************************/
2265
2266
/** Write part or totality of a multidimensional array or attribute.
2267
 *
2268
 * This will set the content of a hyper-rectangle into the array from
2269
 * a user supplied buffer.
2270
 *
2271
 * If bufferDataType is of type string, the values read from pSrcBuffer
2272
 * will be char* pointers.
2273
 *
2274
 * This is the same as the C function GDALMDArrayWrite().
2275
 *
2276
 * @param arrayStartIdx Values representing the starting index to write
2277
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
2278
 *                      Array of GetDimensionCount() values. Must not be
2279
 *                      nullptr, unless for a zero-dimensional array.
2280
 *
2281
 * @param count         Values representing the number of values to write in
2282
 *                      each dimension.
2283
 *                      Array of GetDimensionCount() values. Must not be
2284
 *                      nullptr, unless for a zero-dimensional array.
2285
 *
2286
 * @param arrayStep     Spacing between values to write in each dimension.
2287
 *                      The spacing is in number of array elements, not bytes.
2288
 *                      If provided, must contain GetDimensionCount() values.
2289
 *                      If set to nullptr, [1, 1, ... 1] will be used as a
2290
 * default to indicate consecutive elements.
2291
 *
2292
 * @param bufferStride  Spacing between values to read from pSrcBuffer.
2293
 *                      The spacing is in number of array elements, not bytes.
2294
 *                      If provided, must contain GetDimensionCount() values.
2295
 *                      Negative values are possible (for example to reorder
2296
 *                      from bottom-to-top to top-to-bottom).
2297
 *                      If set to nullptr, will be set so that pSrcBuffer is
2298
 *                      written in a compact way, with elements of the last /
2299
 *                      fastest varying dimension being consecutive.
2300
 *
2301
 * @param bufferDataType Data type of values in pSrcBuffer.
2302
 *
2303
 * @param pSrcBuffer    User buffer to read the values from. Should be big
2304
 *                      enough to store the number of values indicated by
2305
 * count[] and with the spacing of bufferStride[].
2306
 *
2307
 * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2308
 *                             validity of pSrcBuffer. pSrcBufferAllocStart
2309
 * should be the pointer returned by the malloc() or equivalent call used to
2310
 * allocate the buffer. It will generally be equal to pSrcBuffer (when
2311
 * bufferStride[] values are all positive), but not necessarily. If specified,
2312
 * nSrcBufferAllocSize should be also set to the appropriate value. If no
2313
 * validation is needed, nullptr can be passed.
2314
 *
2315
 * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
2316
 * validate the validity of pSrcBuffer. This is the size of the buffer starting
2317
 * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2318
 *                             set to the appropriate value.
2319
 *                             If no validation is needed, 0 can be passed.
2320
 *
2321
 * @return true in case of success.
2322
 */
2323
bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2324
                                const size_t *count, const GInt64 *arrayStep,
2325
                                const GPtrDiff_t *bufferStride,
2326
                                const GDALExtendedDataType &bufferDataType,
2327
                                const void *pSrcBuffer,
2328
                                const void *pSrcBufferAllocStart,
2329
                                size_t nSrcBufferAllocSize)
2330
0
{
2331
0
    if (!bufferDataType.CanConvertTo(GetDataType()))
2332
0
    {
2333
0
        CPLError(CE_Failure, CPLE_AppDefined,
2334
0
                 "Buffer data type is not convertible to array data type");
2335
0
        return false;
2336
0
    }
2337
2338
0
    std::vector<GInt64> tmp_arrayStep;
2339
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
2340
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2341
0
                              bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2342
0
                              nSrcBufferAllocSize, tmp_arrayStep,
2343
0
                              tmp_bufferStride))
2344
0
    {
2345
0
        return false;
2346
0
    }
2347
2348
0
    return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2349
0
                  pSrcBuffer);
2350
0
}
2351
2352
/************************************************************************/
2353
/*                          GetTotalElementsCount()                     */
2354
/************************************************************************/
2355
2356
/** Return the total number of values in the array.
2357
 *
2358
 * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2359
 * and GDALAttributeGetTotalElementsCount().
2360
 *
2361
 */
2362
GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2363
0
{
2364
0
    const auto &dims = GetDimensions();
2365
0
    if (dims.empty())
2366
0
        return 1;
2367
0
    GUInt64 nElts = 1;
2368
0
    for (const auto &dim : dims)
2369
0
    {
2370
0
        try
2371
0
        {
2372
0
            nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2373
0
                     CPLSM(static_cast<uint64_t>(dim->GetSize())))
2374
0
                        .v();
2375
0
        }
2376
0
        catch (...)
2377
0
        {
2378
0
            return 0;
2379
0
        }
2380
0
    }
2381
0
    return nElts;
2382
0
}
2383
2384
/************************************************************************/
2385
/*                           GetBlockSize()                             */
2386
/************************************************************************/
2387
2388
/** Return the "natural" block size of the array along all dimensions.
2389
 *
2390
 * Some drivers might organize the array in tiles/blocks and reading/writing
2391
 * aligned on those tile/block boundaries will be more efficient.
2392
 *
2393
 * The returned number of elements in the vector is the same as
2394
 * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2395
 * the natural block size along the considered dimension.
2396
 * "Flat" arrays will typically return a vector of values set to 0.
2397
 *
2398
 * The default implementation will return a vector of values set to 0.
2399
 *
2400
 * This method is used by GetProcessingChunkSize().
2401
 *
2402
 * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
2403
 * theoretical case of a 32-bit platform, this might exceed its size_t
2404
 * allocation capabilities.
2405
 *
2406
 * This is the same as the C function GDALMDArrayGetBlockSize().
2407
 *
2408
 * @return the block size, in number of elements along each dimension.
2409
 */
2410
std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2411
0
{
2412
0
    return std::vector<GUInt64>(GetDimensionCount());
2413
0
}
2414
2415
/************************************************************************/
2416
/*                       GetProcessingChunkSize()                       */
2417
/************************************************************************/
2418
2419
/** \brief Return an optimal chunk size for read/write operations, given the
2420
 * natural block size and memory constraints specified.
2421
 *
2422
 * This method will use GetBlockSize() to define a chunk whose dimensions are
2423
 * multiple of those returned by GetBlockSize() (unless the block define by
2424
 * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2425
 * returned by this method).
2426
 *
2427
 * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2428
 *
2429
 * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2430
 * chunk.
2431
 *
2432
 * @return the chunk size, in number of elements along each dimension.
2433
 */
2434
std::vector<size_t>
2435
GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2436
0
{
2437
0
    const auto &dims = GetDimensions();
2438
0
    const auto &nDTSize = GetDataType().GetSize();
2439
0
    std::vector<size_t> anChunkSize;
2440
0
    auto blockSize = GetBlockSize();
2441
0
    CPLAssert(blockSize.size() == dims.size());
2442
0
    size_t nChunkSize = nDTSize;
2443
0
    bool bOverflow = false;
2444
0
    constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2445
    // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2446
    // [1, min(sizet_max, dim_size[i])]
2447
    // Also make sure that the product of all anChunkSize[i]) fits on size_t
2448
0
    for (size_t i = 0; i < dims.size(); i++)
2449
0
    {
2450
0
        const auto sizeDimI =
2451
0
            std::max(static_cast<size_t>(1),
2452
0
                     static_cast<size_t>(
2453
0
                         std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2454
0
                                  std::min(blockSize[i], dims[i]->GetSize()))));
2455
0
        anChunkSize.push_back(sizeDimI);
2456
0
        if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2457
0
        {
2458
0
            bOverflow = true;
2459
0
        }
2460
0
        else
2461
0
        {
2462
0
            nChunkSize *= sizeDimI;
2463
0
        }
2464
0
    }
2465
0
    if (nChunkSize == 0)
2466
0
        return anChunkSize;
2467
2468
    // If the product of all anChunkSize[i] does not fit on size_t, then
2469
    // set lowest anChunkSize[i] to 1.
2470
0
    if (bOverflow)
2471
0
    {
2472
0
        nChunkSize = nDTSize;
2473
0
        bOverflow = false;
2474
0
        for (size_t i = dims.size(); i > 0;)
2475
0
        {
2476
0
            --i;
2477
0
            if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2478
0
            {
2479
0
                bOverflow = true;
2480
0
                anChunkSize[i] = 1;
2481
0
            }
2482
0
            else
2483
0
            {
2484
0
                nChunkSize *= anChunkSize[i];
2485
0
            }
2486
0
        }
2487
0
    }
2488
2489
0
    nChunkSize = nDTSize;
2490
0
    std::vector<size_t> anAccBlockSizeFromStart;
2491
0
    for (size_t i = 0; i < dims.size(); i++)
2492
0
    {
2493
0
        nChunkSize *= anChunkSize[i];
2494
0
        anAccBlockSizeFromStart.push_back(nChunkSize);
2495
0
    }
2496
0
    if (nChunkSize <= nMaxChunkMemory / 2)
2497
0
    {
2498
0
        size_t nVoxelsFromEnd = 1;
2499
0
        for (size_t i = dims.size(); i > 0;)
2500
0
        {
2501
0
            --i;
2502
0
            const auto nCurBlockSize =
2503
0
                anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2504
0
            const auto nMul = nMaxChunkMemory / nCurBlockSize;
2505
0
            if (nMul >= 2)
2506
0
            {
2507
0
                const auto nSizeThisDim(dims[i]->GetSize());
2508
0
                const auto nBlocksThisDim =
2509
0
                    DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2510
0
                anChunkSize[i] = static_cast<size_t>(std::min(
2511
0
                    anChunkSize[i] *
2512
0
                        std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2513
0
                    nSizeThisDim));
2514
0
            }
2515
0
            nVoxelsFromEnd *= anChunkSize[i];
2516
0
        }
2517
0
    }
2518
0
    return anChunkSize;
2519
0
}
2520
2521
/************************************************************************/
2522
/*                         BaseRename()                                 */
2523
/************************************************************************/
2524
2525
//! @cond Doxygen_Suppress
2526
void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2527
0
{
2528
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
2529
0
    m_osFullName += osNewName;
2530
0
    m_osName = osNewName;
2531
2532
0
    NotifyChildrenOfRenaming();
2533
0
}
2534
2535
//! @endcond
2536
2537
//! @cond Doxygen_Suppress
2538
/************************************************************************/
2539
/*                          ParentRenamed()                             */
2540
/************************************************************************/
2541
2542
void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2543
0
{
2544
0
    m_osFullName = osNewParentFullName;
2545
0
    m_osFullName += "/";
2546
0
    m_osFullName += m_osName;
2547
2548
0
    NotifyChildrenOfRenaming();
2549
0
}
2550
2551
//! @endcond
2552
2553
/************************************************************************/
2554
/*                             Deleted()                                */
2555
/************************************************************************/
2556
2557
//! @cond Doxygen_Suppress
2558
void GDALAbstractMDArray::Deleted()
2559
0
{
2560
0
    m_bValid = false;
2561
2562
0
    NotifyChildrenOfDeletion();
2563
0
}
2564
2565
//! @endcond
2566
2567
/************************************************************************/
2568
/*                        ParentDeleted()                               */
2569
/************************************************************************/
2570
2571
//! @cond Doxygen_Suppress
2572
void GDALAbstractMDArray::ParentDeleted()
2573
0
{
2574
0
    Deleted();
2575
0
}
2576
2577
//! @endcond
2578
2579
/************************************************************************/
2580
/*                     CheckValidAndErrorOutIfNot()                     */
2581
/************************************************************************/
2582
2583
//! @cond Doxygen_Suppress
2584
bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2585
0
{
2586
0
    if (!m_bValid)
2587
0
    {
2588
0
        CPLError(CE_Failure, CPLE_AppDefined,
2589
0
                 "This object has been deleted. No action on it is possible");
2590
0
    }
2591
0
    return m_bValid;
2592
0
}
2593
2594
//! @endcond
2595
2596
/************************************************************************/
2597
/*                             SetUnit()                                */
2598
/************************************************************************/
2599
2600
/** Set the variable unit.
2601
 *
2602
 * Values should conform as much as possible with those allowed by
2603
 * the NetCDF CF conventions:
2604
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2605
 * but others might be returned.
2606
 *
2607
 * Few examples are "meter", "degrees", "second", ...
2608
 * Empty value means unknown.
2609
 *
2610
 * This is the same as the C function GDALMDArraySetUnit()
2611
 *
2612
 * @note Driver implementation: optionally implemented.
2613
 *
2614
 * @param osUnit unit name.
2615
 * @return true in case of success.
2616
 */
2617
bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2618
0
{
2619
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2620
0
    return false;
2621
0
}
2622
2623
/************************************************************************/
2624
/*                             GetUnit()                                */
2625
/************************************************************************/
2626
2627
/** Return the array unit.
2628
 *
2629
 * Values should conform as much as possible with those allowed by
2630
 * the NetCDF CF conventions:
2631
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2632
 * but others might be returned.
2633
 *
2634
 * Few examples are "meter", "degrees", "second", ...
2635
 * Empty value means unknown.
2636
 *
2637
 * This is the same as the C function GDALMDArrayGetUnit()
2638
 */
2639
const std::string &GDALMDArray::GetUnit() const
2640
0
{
2641
0
    static const std::string emptyString;
2642
0
    return emptyString;
2643
0
}
2644
2645
/************************************************************************/
2646
/*                          SetSpatialRef()                             */
2647
/************************************************************************/
2648
2649
/** Assign a spatial reference system object to the array.
2650
 *
2651
 * This is the same as the C function GDALMDArraySetSpatialRef().
2652
 */
2653
bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2654
0
{
2655
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2656
0
    return false;
2657
0
}
2658
2659
/************************************************************************/
2660
/*                          GetSpatialRef()                             */
2661
/************************************************************************/
2662
2663
/** Return the spatial reference system object associated with the array.
2664
 *
2665
 * This is the same as the C function GDALMDArrayGetSpatialRef().
2666
 */
2667
std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2668
0
{
2669
0
    return nullptr;
2670
0
}
2671
2672
/************************************************************************/
2673
/*                        GetRawNoDataValue()                           */
2674
/************************************************************************/
2675
2676
/** Return the nodata value as a "raw" value.
2677
 *
2678
 * The value returned might be nullptr in case of no nodata value. When
2679
 * a nodata value is registered, a non-nullptr will be returned whose size in
2680
 * bytes is GetDataType().GetSize().
2681
 *
2682
 * The returned value should not be modified or freed. It is valid until
2683
 * the array is destroyed, or the next call to GetRawNoDataValue() or
2684
 * SetRawNoDataValue(), or any similar methods.
2685
 *
2686
 * @note Driver implementation: this method shall be implemented if nodata
2687
 * is supported.
2688
 *
2689
 * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2690
 *
2691
 * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2692
 */
2693
const void *GDALMDArray::GetRawNoDataValue() const
2694
0
{
2695
0
    return nullptr;
2696
0
}
2697
2698
/************************************************************************/
2699
/*                        GetNoDataValueAsDouble()                      */
2700
/************************************************************************/
2701
2702
/** Return the nodata value as a double.
2703
 *
2704
 * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2705
 *
2706
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2707
 * a nodata value exists and can be converted to double. Might be nullptr.
2708
 *
2709
 * @return the nodata value as a double. A 0.0 value might also indicate the
2710
 * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2711
 * set to false then).
2712
 */
2713
double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2714
0
{
2715
0
    const void *pNoData = GetRawNoDataValue();
2716
0
    double dfNoData = 0.0;
2717
0
    const auto &eDT = GetDataType();
2718
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2719
0
    if (ok)
2720
0
    {
2721
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2722
0
                        GDT_Float64, 0, 1);
2723
0
    }
2724
0
    if (pbHasNoData)
2725
0
        *pbHasNoData = ok;
2726
0
    return dfNoData;
2727
0
}
2728
2729
/************************************************************************/
2730
/*                        GetNoDataValueAsInt64()                       */
2731
/************************************************************************/
2732
2733
/** Return the nodata value as a Int64.
2734
 *
2735
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2736
 * a nodata value exists and can be converted to Int64. Might be nullptr.
2737
 *
2738
 * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2739
 *
2740
 * @return the nodata value as a Int64
2741
 *
2742
 * @since GDAL 3.5
2743
 */
2744
int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2745
0
{
2746
0
    const void *pNoData = GetRawNoDataValue();
2747
0
    int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2748
0
    const auto &eDT = GetDataType();
2749
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2750
0
    if (ok)
2751
0
    {
2752
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2753
0
                        GDT_Int64, 0, 1);
2754
0
    }
2755
0
    if (pbHasNoData)
2756
0
        *pbHasNoData = ok;
2757
0
    return nNoData;
2758
0
}
2759
2760
/************************************************************************/
2761
/*                       GetNoDataValueAsUInt64()                       */
2762
/************************************************************************/
2763
2764
/** Return the nodata value as a UInt64.
2765
 *
2766
 * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2767
2768
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2769
 * a nodata value exists and can be converted to UInt64. Might be nullptr.
2770
 *
2771
 * @return the nodata value as a UInt64
2772
 *
2773
 * @since GDAL 3.5
2774
 */
2775
uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2776
0
{
2777
0
    const void *pNoData = GetRawNoDataValue();
2778
0
    uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2779
0
    const auto &eDT = GetDataType();
2780
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2781
0
    if (ok)
2782
0
    {
2783
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2784
0
                        GDT_UInt64, 0, 1);
2785
0
    }
2786
0
    if (pbHasNoData)
2787
0
        *pbHasNoData = ok;
2788
0
    return nNoData;
2789
0
}
2790
2791
/************************************************************************/
2792
/*                        SetRawNoDataValue()                           */
2793
/************************************************************************/
2794
2795
/** Set the nodata value as a "raw" value.
2796
 *
2797
 * The value passed might be nullptr in case of no nodata value. When
2798
 * a nodata value is registered, a non-nullptr whose size in
2799
 * bytes is GetDataType().GetSize() must be passed.
2800
 *
2801
 * This is the same as the C function GDALMDArraySetRawNoDataValue().
2802
 *
2803
 * @note Driver implementation: this method shall be implemented if setting
2804
 nodata
2805
 * is supported.
2806
2807
 * @return true in case of success.
2808
 */
2809
bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2810
0
{
2811
0
    CPLError(CE_Failure, CPLE_NotSupported,
2812
0
             "SetRawNoDataValue() not implemented");
2813
0
    return false;
2814
0
}
2815
2816
/************************************************************************/
2817
/*                           SetNoDataValue()                           */
2818
/************************************************************************/
2819
2820
/** Set the nodata value as a double.
2821
 *
2822
 * If the natural data type of the attribute/array is not double, type
2823
 * conversion will occur to the type returned by GetDataType().
2824
 *
2825
 * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2826
 *
2827
 * @return true in case of success.
2828
 */
2829
bool GDALMDArray::SetNoDataValue(double dfNoData)
2830
0
{
2831
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2832
0
    bool bRet = false;
2833
0
    if (GDALExtendedDataType::CopyValue(
2834
0
            &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2835
0
            GetDataType()))
2836
0
    {
2837
0
        bRet = SetRawNoDataValue(pRawNoData);
2838
0
    }
2839
0
    CPLFree(pRawNoData);
2840
0
    return bRet;
2841
0
}
2842
2843
/************************************************************************/
2844
/*                           SetNoDataValue()                           */
2845
/************************************************************************/
2846
2847
/** Set the nodata value as a Int64.
2848
 *
2849
 * If the natural data type of the attribute/array is not Int64, type conversion
2850
 * will occur to the type returned by GetDataType().
2851
 *
2852
 * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2853
 *
2854
 * @return true in case of success.
2855
 *
2856
 * @since GDAL 3.5
2857
 */
2858
bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2859
0
{
2860
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2861
0
    bool bRet = false;
2862
0
    if (GDALExtendedDataType::CopyValue(&nNoData,
2863
0
                                        GDALExtendedDataType::Create(GDT_Int64),
2864
0
                                        pRawNoData, GetDataType()))
2865
0
    {
2866
0
        bRet = SetRawNoDataValue(pRawNoData);
2867
0
    }
2868
0
    CPLFree(pRawNoData);
2869
0
    return bRet;
2870
0
}
2871
2872
/************************************************************************/
2873
/*                           SetNoDataValue()                           */
2874
/************************************************************************/
2875
2876
/** Set the nodata value as a Int64.
2877
 *
2878
 * If the natural data type of the attribute/array is not Int64, type conversion
2879
 * will occur to the type returned by GetDataType().
2880
 *
2881
 * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2882
 *
2883
 * @return true in case of success.
2884
 *
2885
 * @since GDAL 3.5
2886
 */
2887
bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2888
0
{
2889
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2890
0
    bool bRet = false;
2891
0
    if (GDALExtendedDataType::CopyValue(
2892
0
            &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2893
0
            GetDataType()))
2894
0
    {
2895
0
        bRet = SetRawNoDataValue(pRawNoData);
2896
0
    }
2897
0
    CPLFree(pRawNoData);
2898
0
    return bRet;
2899
0
}
2900
2901
/************************************************************************/
2902
/*                            Resize()                                  */
2903
/************************************************************************/
2904
2905
/** Resize an array to new dimensions.
2906
 *
2907
 * Not all drivers may allow this operation, and with restrictions (e.g.
2908
 * for netCDF, this is limited to growing of "unlimited" dimensions)
2909
 *
2910
 * Resizing a dimension used in other arrays will cause those other arrays
2911
 * to be resized.
2912
 *
2913
 * This is the same as the C function GDALMDArrayResize().
2914
 *
2915
 * @param anNewDimSizes Array of GetDimensionCount() values containing the
2916
 *                      new size of each indexing dimension.
2917
 * @param papszOptions Options. (Driver specific)
2918
 * @return true in case of success.
2919
 * @since GDAL 3.7
2920
 */
2921
bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2922
                         CPL_UNUSED CSLConstList papszOptions)
2923
0
{
2924
0
    CPLError(CE_Failure, CPLE_NotSupported,
2925
0
             "Resize() is not supported for this array");
2926
0
    return false;
2927
0
}
2928
2929
/************************************************************************/
2930
/*                               SetScale()                             */
2931
/************************************************************************/
2932
2933
/** Set the scale value to apply to raw values.
2934
 *
2935
 * unscaled_value = raw_value * GetScale() + GetOffset()
2936
 *
2937
 * This is the same as the C function GDALMDArraySetScale() /
2938
 * GDALMDArraySetScaleEx().
2939
 *
2940
 * @note Driver implementation: this method shall be implemented if setting
2941
 * scale is supported.
2942
 *
2943
 * @param dfScale scale
2944
 * @param eStorageType Data type to which create the potential attribute that
2945
 * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2946
 * implementation will decide automatically the data type. Note that changing
2947
 * the data type after initial setting might not be supported.
2948
 * @return true in case of success.
2949
 */
2950
bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2951
                           CPL_UNUSED GDALDataType eStorageType)
2952
0
{
2953
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2954
0
    return false;
2955
0
}
2956
2957
/************************************************************************/
2958
/*                               SetOffset)                             */
2959
/************************************************************************/
2960
2961
/** Set the offset value to apply to raw values.
2962
 *
2963
 * unscaled_value = raw_value * GetScale() + GetOffset()
2964
 *
2965
 * This is the same as the C function GDALMDArraySetOffset() /
2966
 * GDALMDArraySetOffsetEx().
2967
 *
2968
 * @note Driver implementation: this method shall be implemented if setting
2969
 * offset is supported.
2970
 *
2971
 * @param dfOffset Offset
2972
 * @param eStorageType Data type to which create the potential attribute that
2973
 * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2974
 * implementation will decide automatically the data type. Note that changing
2975
 * the data type after initial setting might not be supported.
2976
 * @return true in case of success.
2977
 */
2978
bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2979
                            CPL_UNUSED GDALDataType eStorageType)
2980
0
{
2981
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2982
0
    return false;
2983
0
}
2984
2985
/************************************************************************/
2986
/*                               GetScale()                             */
2987
/************************************************************************/
2988
2989
/** Get the scale value to apply to raw values.
2990
 *
2991
 * unscaled_value = raw_value * GetScale() + GetOffset()
2992
 *
2993
 * This is the same as the C function GDALMDArrayGetScale().
2994
 *
2995
 * @note Driver implementation: this method shall be implemented if gettings
2996
 * scale is supported.
2997
 *
2998
 * @param pbHasScale Pointer to a output boolean that will be set to true if
2999
 * a scale value exists. Might be nullptr.
3000
 * @param peStorageType Pointer to a output GDALDataType that will be set to
3001
 * the storage type of the scale value, when known/relevant. Otherwise will be
3002
 * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3003
 *
3004
 * @return the scale value. A 1.0 value might also indicate the
3005
 * absence of a scale value.
3006
 */
3007
double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3008
                             CPL_UNUSED GDALDataType *peStorageType) const
3009
0
{
3010
0
    if (pbHasScale)
3011
0
        *pbHasScale = false;
3012
0
    return 1.0;
3013
0
}
3014
3015
/************************************************************************/
3016
/*                               GetOffset()                            */
3017
/************************************************************************/
3018
3019
/** Get the offset value to apply to raw values.
3020
 *
3021
 * unscaled_value = raw_value * GetScale() + GetOffset()
3022
 *
3023
 * This is the same as the C function GDALMDArrayGetOffset().
3024
 *
3025
 * @note Driver implementation: this method shall be implemented if gettings
3026
 * offset is supported.
3027
 *
3028
 * @param pbHasOffset Pointer to a output boolean that will be set to true if
3029
 * a offset value exists. Might be nullptr.
3030
 * @param peStorageType Pointer to a output GDALDataType that will be set to
3031
 * the storage type of the offset value, when known/relevant. Otherwise will be
3032
 * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3033
 *
3034
 * @return the offset value. A 0.0 value might also indicate the
3035
 * absence of a offset value.
3036
 */
3037
double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3038
                              CPL_UNUSED GDALDataType *peStorageType) const
3039
0
{
3040
0
    if (pbHasOffset)
3041
0
        *pbHasOffset = false;
3042
0
    return 0.0;
3043
0
}
3044
3045
/************************************************************************/
3046
/*                         ProcessPerChunk()                            */
3047
/************************************************************************/
3048
3049
namespace
3050
{
3051
enum class Caller
3052
{
3053
    CALLER_END_OF_LOOP,
3054
    CALLER_IN_LOOP,
3055
};
3056
}
3057
3058
/** \brief Call a user-provided function to operate on an array chunk by chunk.
3059
 *
3060
 * This method is to be used when doing operations on an array, or a subset of
3061
 * it, in a chunk by chunk way.
3062
 *
3063
 * @param arrayStartIdx Values representing the starting index to use
3064
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
3065
 *                      Array of GetDimensionCount() values. Must not be
3066
 *                      nullptr, unless for a zero-dimensional array.
3067
 *
3068
 * @param count         Values representing the number of values to use in
3069
 *                      each dimension.
3070
 *                      Array of GetDimensionCount() values. Must not be
3071
 *                      nullptr, unless for a zero-dimensional array.
3072
 *
3073
 * @param chunkSize     Values representing the chunk size in each dimension.
3074
 *                      Might typically the output of GetProcessingChunkSize().
3075
 *                      Array of GetDimensionCount() values. Must not be
3076
 *                      nullptr, unless for a zero-dimensional array.
3077
 *
3078
 * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
3079
 *                      Must NOT be nullptr.
3080
 *
3081
 * @param pUserData     Pointer to pass as the value of the pUserData argument
3082
 * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3083
 *
3084
 * @return true in case of success.
3085
 */
3086
bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3087
                                          const GUInt64 *count,
3088
                                          const size_t *chunkSize,
3089
                                          FuncProcessPerChunkType pfnFunc,
3090
                                          void *pUserData)
3091
0
{
3092
0
    const auto &dims = GetDimensions();
3093
0
    if (dims.empty())
3094
0
    {
3095
0
        return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3096
0
    }
3097
3098
    // Sanity check
3099
0
    size_t nTotalChunkSize = 1;
3100
0
    for (size_t i = 0; i < dims.size(); i++)
3101
0
    {
3102
0
        const auto nSizeThisDim(dims[i]->GetSize());
3103
0
        if (count[i] == 0 || count[i] > nSizeThisDim ||
3104
0
            arrayStartIdx[i] > nSizeThisDim - count[i])
3105
0
        {
3106
0
            CPLError(CE_Failure, CPLE_AppDefined,
3107
0
                     "Inconsistent arrayStartIdx[] / count[] values "
3108
0
                     "regarding array size");
3109
0
            return false;
3110
0
        }
3111
0
        if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3112
0
            chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3113
0
        {
3114
0
            CPLError(CE_Failure, CPLE_AppDefined,
3115
0
                     "Inconsistent chunkSize[] values");
3116
0
            return false;
3117
0
        }
3118
0
        nTotalChunkSize *= chunkSize[i];
3119
0
    }
3120
3121
0
    size_t dimIdx = 0;
3122
0
    std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3123
0
    std::vector<size_t> chunkCount(dims.size());
3124
3125
0
    struct Stack
3126
0
    {
3127
0
        GUInt64 nBlockCounter = 0;
3128
0
        GUInt64 nBlocksMinusOne = 0;
3129
0
        size_t first_count = 0;  // only used if nBlocks > 1
3130
0
        Caller return_point = Caller::CALLER_END_OF_LOOP;
3131
0
    };
3132
3133
0
    std::vector<Stack> stack(dims.size());
3134
0
    GUInt64 iCurChunk = 0;
3135
0
    GUInt64 nChunkCount = 1;
3136
0
    for (size_t i = 0; i < dims.size(); i++)
3137
0
    {
3138
0
        const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3139
0
        const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3140
0
        stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3141
0
        nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3142
0
        if (stack[i].nBlocksMinusOne == 0)
3143
0
        {
3144
0
            chunkArrayStartIdx[i] = arrayStartIdx[i];
3145
0
            chunkCount[i] = static_cast<size_t>(count[i]);
3146
0
        }
3147
0
        else
3148
0
        {
3149
0
            stack[i].first_count = static_cast<size_t>(
3150
0
                (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3151
0
        }
3152
0
    }
3153
3154
0
lbl_next_depth:
3155
0
    if (dimIdx == dims.size())
3156
0
    {
3157
0
        ++iCurChunk;
3158
0
        if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3159
0
                     iCurChunk, nChunkCount, pUserData))
3160
0
        {
3161
0
            return false;
3162
0
        }
3163
0
    }
3164
0
    else
3165
0
    {
3166
0
        if (stack[dimIdx].nBlocksMinusOne != 0)
3167
0
        {
3168
0
            stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3169
0
            chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3170
0
            chunkCount[dimIdx] = stack[dimIdx].first_count;
3171
0
            stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3172
0
            while (true)
3173
0
            {
3174
0
                dimIdx++;
3175
0
                goto lbl_next_depth;
3176
0
            lbl_return_to_caller_in_loop:
3177
0
                --stack[dimIdx].nBlockCounter;
3178
0
                if (stack[dimIdx].nBlockCounter == 0)
3179
0
                    break;
3180
0
                chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3181
0
                chunkCount[dimIdx] = chunkSize[dimIdx];
3182
0
            }
3183
3184
0
            chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3185
0
            chunkCount[dimIdx] =
3186
0
                static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3187
0
                                    chunkArrayStartIdx[dimIdx]);
3188
0
            stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3189
0
        }
3190
0
        dimIdx++;
3191
0
        goto lbl_next_depth;
3192
0
    lbl_return_to_caller_end_of_loop:
3193
0
        if (dimIdx == 0)
3194
0
            goto end;
3195
0
    }
3196
3197
0
    assert(dimIdx > 0);
3198
0
    dimIdx--;
3199
    // cppcheck-suppress negativeContainerIndex
3200
0
    switch (stack[dimIdx].return_point)
3201
0
    {
3202
0
        case Caller::CALLER_END_OF_LOOP:
3203
0
            goto lbl_return_to_caller_end_of_loop;
3204
0
        case Caller::CALLER_IN_LOOP:
3205
0
            goto lbl_return_to_caller_in_loop;
3206
0
    }
3207
0
end:
3208
0
    return true;
3209
0
}
3210
3211
/************************************************************************/
3212
/*                          GDALAttribute()                             */
3213
/************************************************************************/
3214
3215
//! @cond Doxygen_Suppress
3216
GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3217
                             CPL_UNUSED const std::string &osName)
3218
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3219
    : GDALAbstractMDArray(osParentName, osName)
3220
#endif
3221
0
{
3222
0
}
3223
3224
0
GDALAttribute::~GDALAttribute() = default;
3225
3226
//! @endcond
3227
3228
/************************************************************************/
3229
/*                        GetDimensionSize()                            */
3230
/************************************************************************/
3231
3232
/** Return the size of the dimensions of the attribute.
3233
 *
3234
 * This will be an empty array for a scalar (single value) attribute.
3235
 *
3236
 * This is the same as the C function GDALAttributeGetDimensionsSize().
3237
 */
3238
std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3239
0
{
3240
0
    const auto &dims = GetDimensions();
3241
0
    std::vector<GUInt64> ret;
3242
0
    ret.reserve(dims.size());
3243
0
    for (const auto &dim : dims)
3244
0
        ret.push_back(dim->GetSize());
3245
0
    return ret;
3246
0
}
3247
3248
/************************************************************************/
3249
/*                            GDALRawResult()                           */
3250
/************************************************************************/
3251
3252
//! @cond Doxygen_Suppress
3253
GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3254
                             size_t nEltCount)
3255
0
    : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3256
0
      m_raw(raw)
3257
0
{
3258
0
}
3259
3260
//! @endcond
3261
3262
/************************************************************************/
3263
/*                            GDALRawResult()                           */
3264
/************************************************************************/
3265
3266
/** Move constructor. */
3267
GDALRawResult::GDALRawResult(GDALRawResult &&other)
3268
0
    : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3269
0
      m_nSize(other.m_nSize), m_raw(other.m_raw)
3270
0
{
3271
0
    other.m_nEltCount = 0;
3272
0
    other.m_nSize = 0;
3273
0
    other.m_raw = nullptr;
3274
0
}
3275
3276
/************************************************************************/
3277
/*                               FreeMe()                               */
3278
/************************************************************************/
3279
3280
void GDALRawResult::FreeMe()
3281
0
{
3282
0
    if (m_raw && m_dt.NeedsFreeDynamicMemory())
3283
0
    {
3284
0
        GByte *pabyPtr = m_raw;
3285
0
        const auto nDTSize(m_dt.GetSize());
3286
0
        for (size_t i = 0; i < m_nEltCount; ++i)
3287
0
        {
3288
0
            m_dt.FreeDynamicMemory(pabyPtr);
3289
0
            pabyPtr += nDTSize;
3290
0
        }
3291
0
    }
3292
0
    VSIFree(m_raw);
3293
0
}
3294
3295
/************************************************************************/
3296
/*                             operator=()                              */
3297
/************************************************************************/
3298
3299
/** Move assignment. */
3300
GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3301
0
{
3302
0
    FreeMe();
3303
0
    m_dt = std::move(other.m_dt);
3304
0
    m_nEltCount = other.m_nEltCount;
3305
0
    m_nSize = other.m_nSize;
3306
0
    m_raw = other.m_raw;
3307
0
    other.m_nEltCount = 0;
3308
0
    other.m_nSize = 0;
3309
0
    other.m_raw = nullptr;
3310
0
    return *this;
3311
0
}
3312
3313
/************************************************************************/
3314
/*                         ~GDALRawResult()                             */
3315
/************************************************************************/
3316
3317
/** Destructor. */
3318
GDALRawResult::~GDALRawResult()
3319
0
{
3320
0
    FreeMe();
3321
0
}
3322
3323
/************************************************************************/
3324
/*                            StealData()                               */
3325
/************************************************************************/
3326
3327
//! @cond Doxygen_Suppress
3328
/** Return buffer to caller which becomes owner of it.
3329
 * Only to be used by GDALAttributeReadAsRaw().
3330
 */
3331
GByte *GDALRawResult::StealData()
3332
0
{
3333
0
    GByte *ret = m_raw;
3334
0
    m_raw = nullptr;
3335
0
    m_nEltCount = 0;
3336
0
    m_nSize = 0;
3337
0
    return ret;
3338
0
}
3339
3340
//! @endcond
3341
3342
/************************************************************************/
3343
/*                             ReadAsRaw()                              */
3344
/************************************************************************/
3345
3346
/** Return the raw value of an attribute.
3347
 *
3348
 *
3349
 * This is the same as the C function GDALAttributeReadAsRaw().
3350
 */
3351
GDALRawResult GDALAttribute::ReadAsRaw() const
3352
0
{
3353
0
    const auto nEltCount(GetTotalElementsCount());
3354
0
    const auto &dt(GetDataType());
3355
0
    const auto nDTSize(dt.GetSize());
3356
0
    GByte *res = static_cast<GByte *>(
3357
0
        VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3358
0
    if (!res)
3359
0
        return GDALRawResult(nullptr, dt, 0);
3360
0
    const auto &dims = GetDimensions();
3361
0
    const auto nDims = GetDimensionCount();
3362
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3363
0
    std::vector<size_t> count(1 + nDims);
3364
0
    for (size_t i = 0; i < nDims; i++)
3365
0
    {
3366
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3367
0
    }
3368
0
    if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3369
0
              &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3370
0
    {
3371
0
        VSIFree(res);
3372
0
        return GDALRawResult(nullptr, dt, 0);
3373
0
    }
3374
0
    return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3375
0
}
3376
3377
/************************************************************************/
3378
/*                            ReadAsString()                            */
3379
/************************************************************************/
3380
3381
/** Return the value of an attribute as a string.
3382
 *
3383
 * The returned string should not be freed, and its lifetime does not
3384
 * excess a next call to ReadAsString() on the same object, or the deletion
3385
 * of the object itself.
3386
 *
3387
 * This function will only return the first element if there are several.
3388
 *
3389
 * This is the same as the C function GDALAttributeReadAsString()
3390
 *
3391
 * @return a string, or nullptr.
3392
 */
3393
const char *GDALAttribute::ReadAsString() const
3394
0
{
3395
0
    const auto nDims = GetDimensionCount();
3396
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3397
0
    std::vector<size_t> count(1 + nDims, 1);
3398
0
    char *szRet = nullptr;
3399
0
    if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3400
0
              GDALExtendedDataType::CreateString(), &szRet, &szRet,
3401
0
              sizeof(szRet)) ||
3402
0
        szRet == nullptr)
3403
0
    {
3404
0
        return nullptr;
3405
0
    }
3406
0
    m_osCachedVal = szRet;
3407
0
    CPLFree(szRet);
3408
0
    return m_osCachedVal.c_str();
3409
0
}
3410
3411
/************************************************************************/
3412
/*                            ReadAsInt()                               */
3413
/************************************************************************/
3414
3415
/** Return the value of an attribute as a integer.
3416
 *
3417
 * This function will only return the first element if there are several.
3418
 *
3419
 * It can fail if its value can not be converted to integer.
3420
 *
3421
 * This is the same as the C function GDALAttributeReadAsInt()
3422
 *
3423
 * @return a integer, or INT_MIN in case of error.
3424
 */
3425
int GDALAttribute::ReadAsInt() const
3426
0
{
3427
0
    const auto nDims = GetDimensionCount();
3428
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3429
0
    std::vector<size_t> count(1 + nDims, 1);
3430
0
    int nRet = INT_MIN;
3431
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3432
0
         GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3433
0
    return nRet;
3434
0
}
3435
3436
/************************************************************************/
3437
/*                            ReadAsInt64()                             */
3438
/************************************************************************/
3439
3440
/** Return the value of an attribute as an int64_t.
3441
 *
3442
 * This function will only return the first element if there are several.
3443
 *
3444
 * It can fail if its value can not be converted to long.
3445
 *
3446
 * This is the same as the C function GDALAttributeReadAsInt64()
3447
 *
3448
 * @return an int64_t, or INT64_MIN in case of error.
3449
 */
3450
int64_t GDALAttribute::ReadAsInt64() const
3451
0
{
3452
0
    const auto nDims = GetDimensionCount();
3453
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3454
0
    std::vector<size_t> count(1 + nDims, 1);
3455
0
    int64_t nRet = INT64_MIN;
3456
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3457
0
         GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3458
0
    return nRet;
3459
0
}
3460
3461
/************************************************************************/
3462
/*                            ReadAsDouble()                            */
3463
/************************************************************************/
3464
3465
/** Return the value of an attribute as a double.
3466
 *
3467
 * This function will only return the first element if there are several.
3468
 *
3469
 * It can fail if its value can not be converted to double.
3470
 *
3471
 * This is the same as the C function GDALAttributeReadAsInt()
3472
 *
3473
 * @return a double value.
3474
 */
3475
double GDALAttribute::ReadAsDouble() const
3476
0
{
3477
0
    const auto nDims = GetDimensionCount();
3478
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3479
0
    std::vector<size_t> count(1 + nDims, 1);
3480
0
    double dfRet = 0;
3481
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3482
0
         GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3483
0
         sizeof(dfRet));
3484
0
    return dfRet;
3485
0
}
3486
3487
/************************************************************************/
3488
/*                          ReadAsStringArray()                         */
3489
/************************************************************************/
3490
3491
/** Return the value of an attribute as an array of strings.
3492
 *
3493
 * This is the same as the C function GDALAttributeReadAsStringArray()
3494
 */
3495
CPLStringList GDALAttribute::ReadAsStringArray() const
3496
0
{
3497
0
    const auto nElts = GetTotalElementsCount();
3498
0
    if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3499
0
        return CPLStringList();
3500
0
    char **papszList = static_cast<char **>(
3501
0
        VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3502
0
    const auto &dims = GetDimensions();
3503
0
    const auto nDims = GetDimensionCount();
3504
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3505
0
    std::vector<size_t> count(1 + nDims);
3506
0
    for (size_t i = 0; i < nDims; i++)
3507
0
    {
3508
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3509
0
    }
3510
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3511
0
         GDALExtendedDataType::CreateString(), papszList, papszList,
3512
0
         sizeof(char *) * static_cast<int>(nElts));
3513
0
    for (int i = 0; i < static_cast<int>(nElts); i++)
3514
0
    {
3515
0
        if (papszList[i] == nullptr)
3516
0
            papszList[i] = CPLStrdup("");
3517
0
    }
3518
0
    return CPLStringList(papszList);
3519
0
}
3520
3521
/************************************************************************/
3522
/*                          ReadAsIntArray()                            */
3523
/************************************************************************/
3524
3525
/** Return the value of an attribute as an array of integers.
3526
 *
3527
 * This is the same as the C function GDALAttributeReadAsIntArray().
3528
 */
3529
std::vector<int> GDALAttribute::ReadAsIntArray() const
3530
0
{
3531
0
    const auto nElts = GetTotalElementsCount();
3532
#if SIZEOF_VOIDP == 4
3533
    if (nElts > static_cast<size_t>(nElts))
3534
        return {};
3535
#endif
3536
0
    std::vector<int> res(static_cast<size_t>(nElts));
3537
0
    const auto &dims = GetDimensions();
3538
0
    const auto nDims = GetDimensionCount();
3539
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3540
0
    std::vector<size_t> count(1 + nDims);
3541
0
    for (size_t i = 0; i < nDims; i++)
3542
0
    {
3543
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3544
0
    }
3545
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3546
0
         GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3547
0
         res.size() * sizeof(res[0]));
3548
0
    return res;
3549
0
}
3550
3551
/************************************************************************/
3552
/*                          ReadAsInt64Array()                          */
3553
/************************************************************************/
3554
3555
/** Return the value of an attribute as an array of int64_t.
3556
 *
3557
 * This is the same as the C function GDALAttributeReadAsInt64Array().
3558
 */
3559
std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3560
0
{
3561
0
    const auto nElts = GetTotalElementsCount();
3562
#if SIZEOF_VOIDP == 4
3563
    if (nElts > static_cast<size_t>(nElts))
3564
        return {};
3565
#endif
3566
0
    std::vector<int64_t> res(static_cast<size_t>(nElts));
3567
0
    const auto &dims = GetDimensions();
3568
0
    const auto nDims = GetDimensionCount();
3569
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3570
0
    std::vector<size_t> count(1 + nDims);
3571
0
    for (size_t i = 0; i < nDims; i++)
3572
0
    {
3573
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3574
0
    }
3575
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3576
0
         GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3577
0
         res.size() * sizeof(res[0]));
3578
0
    return res;
3579
0
}
3580
3581
/************************************************************************/
3582
/*                         ReadAsDoubleArray()                          */
3583
/************************************************************************/
3584
3585
/** Return the value of an attribute as an array of double.
3586
 *
3587
 * This is the same as the C function GDALAttributeReadAsDoubleArray().
3588
 */
3589
std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3590
0
{
3591
0
    const auto nElts = GetTotalElementsCount();
3592
#if SIZEOF_VOIDP == 4
3593
    if (nElts > static_cast<size_t>(nElts))
3594
        return {};
3595
#endif
3596
0
    std::vector<double> res(static_cast<size_t>(nElts));
3597
0
    const auto &dims = GetDimensions();
3598
0
    const auto nDims = GetDimensionCount();
3599
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3600
0
    std::vector<size_t> count(1 + nDims);
3601
0
    for (size_t i = 0; i < nDims; i++)
3602
0
    {
3603
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3604
0
    }
3605
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3606
0
         GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3607
0
         res.size() * sizeof(res[0]));
3608
0
    return res;
3609
0
}
3610
3611
/************************************************************************/
3612
/*                               Write()                                */
3613
/************************************************************************/
3614
3615
/** Write an attribute from raw values expressed in GetDataType()
3616
 *
3617
 * The values should be provided in the type of GetDataType() and there should
3618
 * be exactly GetTotalElementsCount() of them.
3619
 * If GetDataType() is a string, each value should be a char* pointer.
3620
 *
3621
 * This is the same as the C function GDALAttributeWriteRaw().
3622
 *
3623
 * @param pabyValue Buffer of nLen bytes.
3624
 * @param nLen Size of pabyValue in bytes. Should be equal to
3625
 *             GetTotalElementsCount() * GetDataType().GetSize()
3626
 * @return true in case of success.
3627
 */
3628
bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3629
0
{
3630
0
    if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3631
0
    {
3632
0
        CPLError(CE_Failure, CPLE_AppDefined,
3633
0
                 "Length is not of expected value");
3634
0
        return false;
3635
0
    }
3636
0
    const auto &dims = GetDimensions();
3637
0
    const auto nDims = GetDimensionCount();
3638
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3639
0
    std::vector<size_t> count(1 + nDims);
3640
0
    for (size_t i = 0; i < nDims; i++)
3641
0
    {
3642
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3643
0
    }
3644
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3645
0
                 pabyValue, pabyValue, nLen);
3646
0
}
3647
3648
/************************************************************************/
3649
/*                               Write()                                */
3650
/************************************************************************/
3651
3652
/** Write an attribute from a string value.
3653
 *
3654
 * Type conversion will be performed if needed. If the attribute contains
3655
 * multiple values, only the first one will be updated.
3656
 *
3657
 * This is the same as the C function GDALAttributeWriteString().
3658
 *
3659
 * @param pszValue Pointer to a string.
3660
 * @return true in case of success.
3661
 */
3662
bool GDALAttribute::Write(const char *pszValue)
3663
0
{
3664
0
    const auto nDims = GetDimensionCount();
3665
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3666
0
    std::vector<size_t> count(1 + nDims, 1);
3667
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3668
0
                 GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3669
0
                 sizeof(pszValue));
3670
0
}
3671
3672
/************************************************************************/
3673
/*                              WriteInt()                              */
3674
/************************************************************************/
3675
3676
/** Write an attribute from a integer value.
3677
 *
3678
 * Type conversion will be performed if needed. If the attribute contains
3679
 * multiple values, only the first one will be updated.
3680
 *
3681
 * This is the same as the C function GDALAttributeWriteInt().
3682
 *
3683
 * @param nVal Value.
3684
 * @return true in case of success.
3685
 */
3686
bool GDALAttribute::WriteInt(int nVal)
3687
0
{
3688
0
    const auto nDims = GetDimensionCount();
3689
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3690
0
    std::vector<size_t> count(1 + nDims, 1);
3691
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3692
0
                 GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3693
0
                 sizeof(nVal));
3694
0
}
3695
3696
/************************************************************************/
3697
/*                              WriteInt64()                             */
3698
/************************************************************************/
3699
3700
/** Write an attribute from an int64_t value.
3701
 *
3702
 * Type conversion will be performed if needed. If the attribute contains
3703
 * multiple values, only the first one will be updated.
3704
 *
3705
 * This is the same as the C function GDALAttributeWriteInt().
3706
 *
3707
 * @param nVal Value.
3708
 * @return true in case of success.
3709
 */
3710
bool GDALAttribute::WriteInt64(int64_t nVal)
3711
0
{
3712
0
    const auto nDims = GetDimensionCount();
3713
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3714
0
    std::vector<size_t> count(1 + nDims, 1);
3715
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3716
0
                 GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3717
0
                 sizeof(nVal));
3718
0
}
3719
3720
/************************************************************************/
3721
/*                                Write()                               */
3722
/************************************************************************/
3723
3724
/** Write an attribute from a double value.
3725
 *
3726
 * Type conversion will be performed if needed. If the attribute contains
3727
 * multiple values, only the first one will be updated.
3728
 *
3729
 * This is the same as the C function GDALAttributeWriteDouble().
3730
 *
3731
 * @param dfVal Value.
3732
 * @return true in case of success.
3733
 */
3734
bool GDALAttribute::Write(double dfVal)
3735
0
{
3736
0
    const auto nDims = GetDimensionCount();
3737
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3738
0
    std::vector<size_t> count(1 + nDims, 1);
3739
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3740
0
                 GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3741
0
                 sizeof(dfVal));
3742
0
}
3743
3744
/************************************************************************/
3745
/*                                Write()                               */
3746
/************************************************************************/
3747
3748
/** Write an attribute from an array of strings.
3749
 *
3750
 * Type conversion will be performed if needed.
3751
 *
3752
 * Exactly GetTotalElementsCount() strings must be provided
3753
 *
3754
 * This is the same as the C function GDALAttributeWriteStringArray().
3755
 *
3756
 * @param vals Array of strings.
3757
 * @return true in case of success.
3758
 */
3759
bool GDALAttribute::Write(CSLConstList vals)
3760
0
{
3761
0
    if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3762
0
    {
3763
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3764
0
        return false;
3765
0
    }
3766
0
    const auto nDims = GetDimensionCount();
3767
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3768
0
    std::vector<size_t> count(1 + nDims);
3769
0
    const auto &dims = GetDimensions();
3770
0
    for (size_t i = 0; i < nDims; i++)
3771
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3772
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3773
0
                 GDALExtendedDataType::CreateString(), vals, vals,
3774
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3775
0
}
3776
3777
/************************************************************************/
3778
/*                                Write()                               */
3779
/************************************************************************/
3780
3781
/** Write an attribute from an array of int.
3782
 *
3783
 * Type conversion will be performed if needed.
3784
 *
3785
 * Exactly GetTotalElementsCount() strings must be provided
3786
 *
3787
 * This is the same as the C function GDALAttributeWriteIntArray()
3788
 *
3789
 * @param vals Array of int.
3790
 * @param nVals Should be equal to GetTotalElementsCount().
3791
 * @return true in case of success.
3792
 */
3793
bool GDALAttribute::Write(const int *vals, size_t nVals)
3794
0
{
3795
0
    if (nVals != GetTotalElementsCount())
3796
0
    {
3797
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3798
0
        return false;
3799
0
    }
3800
0
    const auto nDims = GetDimensionCount();
3801
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3802
0
    std::vector<size_t> count(1 + nDims);
3803
0
    const auto &dims = GetDimensions();
3804
0
    for (size_t i = 0; i < nDims; i++)
3805
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3806
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3807
0
                 GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3808
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3809
0
}
3810
3811
/************************************************************************/
3812
/*                                Write()                               */
3813
/************************************************************************/
3814
3815
/** Write an attribute from an array of int64_t.
3816
 *
3817
 * Type conversion will be performed if needed.
3818
 *
3819
 * Exactly GetTotalElementsCount() strings must be provided
3820
 *
3821
 * This is the same as the C function GDALAttributeWriteLongArray()
3822
 *
3823
 * @param vals Array of int64_t.
3824
 * @param nVals Should be equal to GetTotalElementsCount().
3825
 * @return true in case of success.
3826
 */
3827
bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3828
0
{
3829
0
    if (nVals != GetTotalElementsCount())
3830
0
    {
3831
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3832
0
        return false;
3833
0
    }
3834
0
    const auto nDims = GetDimensionCount();
3835
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3836
0
    std::vector<size_t> count(1 + nDims);
3837
0
    const auto &dims = GetDimensions();
3838
0
    for (size_t i = 0; i < nDims; i++)
3839
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3840
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3841
0
                 GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3842
0
                 static_cast<size_t>(GetTotalElementsCount()) *
3843
0
                     sizeof(int64_t));
3844
0
}
3845
3846
/************************************************************************/
3847
/*                                Write()                               */
3848
/************************************************************************/
3849
3850
/** Write an attribute from an array of double.
3851
 *
3852
 * Type conversion will be performed if needed.
3853
 *
3854
 * Exactly GetTotalElementsCount() strings must be provided
3855
 *
3856
 * This is the same as the C function GDALAttributeWriteDoubleArray()
3857
 *
3858
 * @param vals Array of double.
3859
 * @param nVals Should be equal to GetTotalElementsCount().
3860
 * @return true in case of success.
3861
 */
3862
bool GDALAttribute::Write(const double *vals, size_t nVals)
3863
0
{
3864
0
    if (nVals != GetTotalElementsCount())
3865
0
    {
3866
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3867
0
        return false;
3868
0
    }
3869
0
    const auto nDims = GetDimensionCount();
3870
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3871
0
    std::vector<size_t> count(1 + nDims);
3872
0
    const auto &dims = GetDimensions();
3873
0
    for (size_t i = 0; i < nDims; i++)
3874
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3875
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3876
0
                 GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3877
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3878
0
}
3879
3880
/************************************************************************/
3881
/*                           GDALMDArray()                              */
3882
/************************************************************************/
3883
3884
//! @cond Doxygen_Suppress
3885
GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3886
                         CPL_UNUSED const std::string &osName,
3887
                         const std::string &osContext)
3888
    :
3889
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3890
      GDALAbstractMDArray(osParentName, osName),
3891
#endif
3892
0
      m_osContext(osContext)
3893
0
{
3894
0
}
3895
3896
//! @endcond
3897
3898
/************************************************************************/
3899
/*                           GetTotalCopyCost()                         */
3900
/************************************************************************/
3901
3902
/** Return a total "cost" to copy the array.
3903
 *
3904
 * Used as a parameter for CopyFrom()
3905
 */
3906
GUInt64 GDALMDArray::GetTotalCopyCost() const
3907
0
{
3908
0
    return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3909
0
           GetTotalElementsCount() * GetDataType().GetSize();
3910
0
}
3911
3912
/************************************************************************/
3913
/*                       CopyFromAllExceptValues()                      */
3914
/************************************************************************/
3915
3916
//! @cond Doxygen_Suppress
3917
3918
bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3919
                                          bool bStrict, GUInt64 &nCurCost,
3920
                                          const GUInt64 nTotalCost,
3921
                                          GDALProgressFunc pfnProgress,
3922
                                          void *pProgressData)
3923
0
{
3924
    // Nodata setting must be one of the first things done for TileDB
3925
0
    const void *pNoData = poSrcArray->GetRawNoDataValue();
3926
0
    if (pNoData && poSrcArray->GetDataType() == GetDataType())
3927
0
    {
3928
0
        SetRawNoDataValue(pNoData);
3929
0
    }
3930
3931
0
    const bool bThisIsUnscaledArray =
3932
0
        dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3933
0
    auto attrs = poSrcArray->GetAttributes();
3934
0
    for (const auto &attr : attrs)
3935
0
    {
3936
0
        const auto &osAttrName = attr->GetName();
3937
0
        if (bThisIsUnscaledArray)
3938
0
        {
3939
0
            if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3940
0
                osAttrName == "valid_min" || osAttrName == "valid_max" ||
3941
0
                osAttrName == "valid_range")
3942
0
            {
3943
0
                continue;
3944
0
            }
3945
0
        }
3946
3947
0
        auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3948
0
                                       attr->GetDataType());
3949
0
        if (!dstAttr)
3950
0
        {
3951
0
            if (bStrict)
3952
0
                return false;
3953
0
            continue;
3954
0
        }
3955
0
        auto raw = attr->ReadAsRaw();
3956
0
        if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3957
0
            return false;
3958
0
    }
3959
0
    if (!attrs.empty())
3960
0
    {
3961
0
        nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3962
0
        if (pfnProgress &&
3963
0
            !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3964
0
            return false;
3965
0
    }
3966
3967
0
    auto srcSRS = poSrcArray->GetSpatialRef();
3968
0
    if (srcSRS)
3969
0
    {
3970
0
        SetSpatialRef(srcSRS.get());
3971
0
    }
3972
3973
0
    const std::string &osUnit(poSrcArray->GetUnit());
3974
0
    if (!osUnit.empty())
3975
0
    {
3976
0
        SetUnit(osUnit);
3977
0
    }
3978
3979
0
    bool bGotValue = false;
3980
0
    GDALDataType eOffsetStorageType = GDT_Unknown;
3981
0
    const double dfOffset =
3982
0
        poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3983
0
    if (bGotValue)
3984
0
    {
3985
0
        SetOffset(dfOffset, eOffsetStorageType);
3986
0
    }
3987
3988
0
    bGotValue = false;
3989
0
    GDALDataType eScaleStorageType = GDT_Unknown;
3990
0
    const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3991
0
    if (bGotValue)
3992
0
    {
3993
0
        SetScale(dfScale, eScaleStorageType);
3994
0
    }
3995
3996
0
    return true;
3997
0
}
3998
3999
//! @endcond
4000
4001
/************************************************************************/
4002
/*                               CopyFrom()                             */
4003
/************************************************************************/
4004
4005
/** Copy the content of an array into a new (generally empty) array.
4006
 *
4007
 * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
4008
 *                   of some output drivers this is not recommended)
4009
 * @param poSrcArray Source array. Should NOT be nullptr.
4010
 * @param bStrict Whether to enable strict mode. In strict mode, any error will
4011
 *                stop the copy. In relaxed mode, the copy will be attempted to
4012
 *                be pursued.
4013
 * @param nCurCost  Should be provided as a variable initially set to 0.
4014
 * @param nTotalCost Total cost from GetTotalCopyCost().
4015
 * @param pfnProgress Progress callback, or nullptr.
4016
 * @param pProgressData Progress user data, or nulptr.
4017
 *
4018
 * @return true in case of success (or partial success if bStrict == false).
4019
 */
4020
bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4021
                           const GDALMDArray *poSrcArray, bool bStrict,
4022
                           GUInt64 &nCurCost, const GUInt64 nTotalCost,
4023
                           GDALProgressFunc pfnProgress, void *pProgressData)
4024
0
{
4025
0
    if (pfnProgress == nullptr)
4026
0
        pfnProgress = GDALDummyProgress;
4027
4028
0
    nCurCost += GDALMDArray::COPY_COST;
4029
4030
0
    if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4031
0
                                 pfnProgress, pProgressData))
4032
0
    {
4033
0
        return false;
4034
0
    }
4035
4036
0
    const auto &dims = poSrcArray->GetDimensions();
4037
0
    const auto nDTSize = poSrcArray->GetDataType().GetSize();
4038
0
    if (dims.empty())
4039
0
    {
4040
0
        std::vector<GByte> abyTmp(nDTSize);
4041
0
        if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4042
0
                               GetDataType(), &abyTmp[0]) &&
4043
0
              Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4044
0
                    &abyTmp[0])) &&
4045
0
            bStrict)
4046
0
        {
4047
0
            return false;
4048
0
        }
4049
0
        nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4050
0
        if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4051
0
            return false;
4052
0
    }
4053
0
    else
4054
0
    {
4055
0
        std::vector<GUInt64> arrayStartIdx(dims.size());
4056
0
        std::vector<GUInt64> count(dims.size());
4057
0
        for (size_t i = 0; i < dims.size(); i++)
4058
0
        {
4059
0
            count[i] = static_cast<size_t>(dims[i]->GetSize());
4060
0
        }
4061
4062
0
        struct CopyFunc
4063
0
        {
4064
0
            GDALMDArray *poDstArray = nullptr;
4065
0
            std::vector<GByte> abyTmp{};
4066
0
            GDALProgressFunc pfnProgress = nullptr;
4067
0
            void *pProgressData = nullptr;
4068
0
            GUInt64 nCurCost = 0;
4069
0
            GUInt64 nTotalCost = 0;
4070
0
            GUInt64 nTotalBytesThisArray = 0;
4071
0
            bool bStop = false;
4072
4073
0
            static bool f(GDALAbstractMDArray *l_poSrcArray,
4074
0
                          const GUInt64 *chunkArrayStartIdx,
4075
0
                          const size_t *chunkCount, GUInt64 iCurChunk,
4076
0
                          GUInt64 nChunkCount, void *pUserData)
4077
0
            {
4078
0
                const auto &dt(l_poSrcArray->GetDataType());
4079
0
                auto data = static_cast<CopyFunc *>(pUserData);
4080
0
                auto poDstArray = data->poDstArray;
4081
0
                if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4082
0
                                        nullptr, dt, &data->abyTmp[0]))
4083
0
                {
4084
0
                    return false;
4085
0
                }
4086
0
                bool bRet =
4087
0
                    poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4088
0
                                      nullptr, dt, &data->abyTmp[0]);
4089
0
                if (dt.NeedsFreeDynamicMemory())
4090
0
                {
4091
0
                    const auto l_nDTSize = dt.GetSize();
4092
0
                    GByte *ptr = &data->abyTmp[0];
4093
0
                    const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4094
0
                    size_t nEltCount = 1;
4095
0
                    for (size_t i = 0; i < l_nDims; ++i)
4096
0
                    {
4097
0
                        nEltCount *= chunkCount[i];
4098
0
                    }
4099
0
                    for (size_t i = 0; i < nEltCount; i++)
4100
0
                    {
4101
0
                        dt.FreeDynamicMemory(ptr);
4102
0
                        ptr += l_nDTSize;
4103
0
                    }
4104
0
                }
4105
0
                if (!bRet)
4106
0
                {
4107
0
                    return false;
4108
0
                }
4109
4110
0
                double dfCurCost =
4111
0
                    double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4112
0
                                                 data->nTotalBytesThisArray;
4113
0
                if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4114
0
                                       data->pProgressData))
4115
0
                {
4116
0
                    data->bStop = true;
4117
0
                    return false;
4118
0
                }
4119
4120
0
                return true;
4121
0
            }
4122
0
        };
4123
4124
0
        CopyFunc copyFunc;
4125
0
        copyFunc.poDstArray = this;
4126
0
        copyFunc.nCurCost = nCurCost;
4127
0
        copyFunc.nTotalCost = nTotalCost;
4128
0
        copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4129
0
        copyFunc.pfnProgress = pfnProgress;
4130
0
        copyFunc.pProgressData = pProgressData;
4131
0
        const char *pszSwathSize =
4132
0
            CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4133
0
        const size_t nMaxChunkSize =
4134
0
            pszSwathSize
4135
0
                ? static_cast<size_t>(
4136
0
                      std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4137
0
                               CPLAtoGIntBig(pszSwathSize)))
4138
0
                : static_cast<size_t>(
4139
0
                      std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4140
0
                               GDALGetCacheMax64() / 4));
4141
0
        const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4142
0
        size_t nRealChunkSize = nDTSize;
4143
0
        for (const auto &nChunkSize : anChunkSizes)
4144
0
        {
4145
0
            nRealChunkSize *= nChunkSize;
4146
0
        }
4147
0
        try
4148
0
        {
4149
0
            copyFunc.abyTmp.resize(nRealChunkSize);
4150
0
        }
4151
0
        catch (const std::exception &)
4152
0
        {
4153
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
4154
0
                     "Cannot allocate temporary buffer");
4155
0
            nCurCost += copyFunc.nTotalBytesThisArray;
4156
0
            return false;
4157
0
        }
4158
0
        if (copyFunc.nTotalBytesThisArray != 0 &&
4159
0
            !const_cast<GDALMDArray *>(poSrcArray)
4160
0
                 ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4161
0
                                   anChunkSizes.data(), CopyFunc::f,
4162
0
                                   &copyFunc) &&
4163
0
            (bStrict || copyFunc.bStop))
4164
0
        {
4165
0
            nCurCost += copyFunc.nTotalBytesThisArray;
4166
0
            return false;
4167
0
        }
4168
0
        nCurCost += copyFunc.nTotalBytesThisArray;
4169
0
    }
4170
4171
0
    return true;
4172
0
}
4173
4174
/************************************************************************/
4175
/*                         GetStructuralInfo()                          */
4176
/************************************************************************/
4177
4178
/** Return structural information on the array.
4179
 *
4180
 * This may be the compression, etc..
4181
 *
4182
 * The return value should not be freed and is valid until GDALMDArray is
4183
 * released or this function called again.
4184
 *
4185
 * This is the same as the C function GDALMDArrayGetStructuralInfo().
4186
 */
4187
CSLConstList GDALMDArray::GetStructuralInfo() const
4188
0
{
4189
0
    return nullptr;
4190
0
}
4191
4192
/************************************************************************/
4193
/*                          AdviseRead()                                */
4194
/************************************************************************/
4195
4196
/** Advise driver of upcoming read requests.
4197
 *
4198
 * Some GDAL drivers operate more efficiently if they know in advance what
4199
 * set of upcoming read requests will be made.  The AdviseRead() method allows
4200
 * an application to notify the driver of the region of interest.
4201
 *
4202
 * Many drivers just ignore the AdviseRead() call, but it can dramatically
4203
 * accelerate access via some drivers. One such case is when reading through
4204
 * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4205
 * with the region of interest defined by AdviseRead())
4206
 *
4207
 * This is the same as the C function GDALMDArrayAdviseRead().
4208
 *
4209
 * @param arrayStartIdx Values representing the starting index to read
4210
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
4211
 *                      Array of GetDimensionCount() values.
4212
 *                      Can be nullptr as a synonymous for [0 for i in
4213
 * range(GetDimensionCount() ]
4214
 *
4215
 * @param count         Values representing the number of values to extract in
4216
 *                      each dimension.
4217
 *                      Array of GetDimensionCount() values.
4218
 *                      Can be nullptr as a synonymous for
4219
 *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4220
 * range(GetDimensionCount() ]
4221
 *
4222
 * @param papszOptions Driver specific options, or nullptr. Consult driver
4223
 * documentation.
4224
 *
4225
 * @return true in case of success (ignoring the advice is a success)
4226
 *
4227
 * @since GDAL 3.2
4228
 */
4229
bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4230
                             CSLConstList papszOptions) const
4231
0
{
4232
0
    const auto nDimCount = GetDimensionCount();
4233
0
    if (nDimCount == 0)
4234
0
        return true;
4235
4236
0
    std::vector<GUInt64> tmp_arrayStartIdx;
4237
0
    if (arrayStartIdx == nullptr)
4238
0
    {
4239
0
        tmp_arrayStartIdx.resize(nDimCount);
4240
0
        arrayStartIdx = tmp_arrayStartIdx.data();
4241
0
    }
4242
4243
0
    std::vector<size_t> tmp_count;
4244
0
    if (count == nullptr)
4245
0
    {
4246
0
        tmp_count.resize(nDimCount);
4247
0
        const auto &dims = GetDimensions();
4248
0
        for (size_t i = 0; i < nDimCount; i++)
4249
0
        {
4250
0
            const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4251
#if SIZEOF_VOIDP < 8
4252
            if (nSize != static_cast<size_t>(nSize))
4253
            {
4254
                CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4255
                return false;
4256
            }
4257
#endif
4258
0
            tmp_count[i] = static_cast<size_t>(nSize);
4259
0
        }
4260
0
        count = tmp_count.data();
4261
0
    }
4262
4263
0
    std::vector<GInt64> tmp_arrayStep;
4264
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
4265
0
    const GInt64 *arrayStep = nullptr;
4266
0
    const GPtrDiff_t *bufferStride = nullptr;
4267
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4268
0
                              GDALExtendedDataType::Create(GDT_Unknown),
4269
0
                              nullptr, nullptr, 0, tmp_arrayStep,
4270
0
                              tmp_bufferStride))
4271
0
    {
4272
0
        return false;
4273
0
    }
4274
4275
0
    return IAdviseRead(arrayStartIdx, count, papszOptions);
4276
0
}
4277
4278
/************************************************************************/
4279
/*                             IAdviseRead()                            */
4280
/************************************************************************/
4281
4282
//! @cond Doxygen_Suppress
4283
bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4284
                              CSLConstList /* papszOptions*/) const
4285
0
{
4286
0
    return true;
4287
0
}
4288
4289
//! @endcond
4290
4291
/************************************************************************/
4292
/*                            MassageName()                             */
4293
/************************************************************************/
4294
4295
//! @cond Doxygen_Suppress
4296
/*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4297
0
{
4298
0
    std::string ret;
4299
0
    for (const char ch : inputName)
4300
0
    {
4301
0
        if (!isalnum(static_cast<unsigned char>(ch)))
4302
0
            ret += '_';
4303
0
        else
4304
0
            ret += ch;
4305
0
    }
4306
0
    return ret;
4307
0
}
4308
4309
//! @endcond
4310
4311
/************************************************************************/
4312
/*                         GetCacheRootGroup()                          */
4313
/************************************************************************/
4314
4315
//! @cond Doxygen_Suppress
4316
std::shared_ptr<GDALGroup>
4317
GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4318
                               std::string &osCacheFilenameOut) const
4319
0
{
4320
0
    const auto &osFilename = GetFilename();
4321
0
    if (osFilename.empty())
4322
0
    {
4323
0
        CPLError(CE_Failure, CPLE_AppDefined,
4324
0
                 "Cannot cache an array with an empty filename");
4325
0
        return nullptr;
4326
0
    }
4327
4328
0
    osCacheFilenameOut = osFilename + ".gmac";
4329
0
    if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4330
0
    {
4331
0
        const auto nPosQuestionMark = osFilename.find('?');
4332
0
        if (nPosQuestionMark != std::string::npos)
4333
0
        {
4334
0
            osCacheFilenameOut =
4335
0
                osFilename.substr(0, nPosQuestionMark)
4336
0
                    .append(".gmac")
4337
0
                    .append(osFilename.substr(nPosQuestionMark));
4338
0
        }
4339
0
    }
4340
0
    const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4341
0
    if (pszProxy != nullptr)
4342
0
        osCacheFilenameOut = pszProxy;
4343
4344
0
    std::unique_ptr<GDALDataset> poDS;
4345
0
    VSIStatBufL sStat;
4346
0
    if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4347
0
    {
4348
0
        poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4349
0
                                     GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4350
0
                                     nullptr, nullptr, nullptr));
4351
0
    }
4352
0
    if (poDS)
4353
0
    {
4354
0
        CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4355
0
        return poDS->GetRootGroup();
4356
0
    }
4357
4358
0
    if (bCanCreate)
4359
0
    {
4360
0
        const char *pszDrvName = "netCDF";
4361
0
        GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4362
0
        if (poDrv == nullptr)
4363
0
        {
4364
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4365
0
                     pszDrvName);
4366
0
            return nullptr;
4367
0
        }
4368
0
        {
4369
0
            CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4370
0
            CPLErrorStateBackuper oErrorStateBackuper;
4371
0
            poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4372
0
                                                     nullptr, nullptr));
4373
0
        }
4374
0
        if (!poDS)
4375
0
        {
4376
0
            pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4377
0
            if (pszProxy)
4378
0
            {
4379
0
                osCacheFilenameOut = pszProxy;
4380
0
                poDS.reset(poDrv->CreateMultiDimensional(
4381
0
                    osCacheFilenameOut.c_str(), nullptr, nullptr));
4382
0
            }
4383
0
        }
4384
0
        if (poDS)
4385
0
        {
4386
0
            CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4387
0
            return poDS->GetRootGroup();
4388
0
        }
4389
0
        else
4390
0
        {
4391
0
            CPLError(CE_Failure, CPLE_AppDefined,
4392
0
                     "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4393
0
                     "configuration option to write the cache in "
4394
0
                     "another directory",
4395
0
                     osCacheFilenameOut.c_str());
4396
0
        }
4397
0
    }
4398
4399
0
    return nullptr;
4400
0
}
4401
4402
//! @endcond
4403
4404
/************************************************************************/
4405
/*                              Cache()                                 */
4406
/************************************************************************/
4407
4408
/** Cache the content of the array into an auxiliary filename.
4409
 *
4410
 * The main purpose of this method is to be able to cache views that are
4411
 * expensive to compute, such as transposed arrays.
4412
 *
4413
 * The array will be stored in a file whose name is the one of
4414
 * GetFilename(), with an extra .gmac extension (stands for GDAL
4415
 * Multidimensional Array Cache). The cache is a netCDF dataset.
4416
 *
4417
 * If the .gmac file cannot be written next to the dataset, the
4418
 * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4419
 * directory.
4420
 *
4421
 * The GDALMDArray::Read() method will automatically use the cache when it
4422
 * exists. There is no timestamp checks between the source array and the cached
4423
 * array. If the source arrays changes, the cache must be manually deleted.
4424
 *
4425
 * This is the same as the C function GDALMDArrayCache()
4426
 *
4427
 * @note Driver implementation: optionally implemented.
4428
 *
4429
 * @param papszOptions List of options, null terminated, or NULL. Currently
4430
 *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4431
 *                     to specify the block size of the cached array.
4432
 * @return true in case of success.
4433
 */
4434
bool GDALMDArray::Cache(CSLConstList papszOptions) const
4435
0
{
4436
0
    std::string osCacheFilename;
4437
0
    auto poRG = GetCacheRootGroup(true, osCacheFilename);
4438
0
    if (!poRG)
4439
0
        return false;
4440
4441
0
    const std::string osCachedArrayName(MassageName(GetFullName()));
4442
0
    if (poRG->OpenMDArray(osCachedArrayName))
4443
0
    {
4444
0
        CPLError(CE_Failure, CPLE_NotSupported,
4445
0
                 "An array with same name %s already exists in %s",
4446
0
                 osCachedArrayName.c_str(), osCacheFilename.c_str());
4447
0
        return false;
4448
0
    }
4449
4450
0
    CPLStringList aosOptions;
4451
0
    aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4452
0
    const auto &aoDims = GetDimensions();
4453
0
    std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4454
0
    if (!aoDims.empty())
4455
0
    {
4456
0
        std::string osBlockSize(
4457
0
            CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4458
0
        if (osBlockSize.empty())
4459
0
        {
4460
0
            const auto anBlockSize = GetBlockSize();
4461
0
            int idxDim = 0;
4462
0
            for (auto nBlockSize : anBlockSize)
4463
0
            {
4464
0
                if (idxDim > 0)
4465
0
                    osBlockSize += ',';
4466
0
                if (nBlockSize == 0)
4467
0
                    nBlockSize = 256;
4468
0
                nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4469
0
                osBlockSize +=
4470
0
                    std::to_string(static_cast<uint64_t>(nBlockSize));
4471
0
                idxDim++;
4472
0
            }
4473
0
        }
4474
0
        aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4475
4476
0
        int idxDim = 0;
4477
0
        for (const auto &poDim : aoDims)
4478
0
        {
4479
0
            auto poNewDim = poRG->CreateDimension(
4480
0
                osCachedArrayName + '_' + std::to_string(idxDim),
4481
0
                poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4482
0
            if (!poNewDim)
4483
0
                return false;
4484
0
            aoNewDims.emplace_back(poNewDim);
4485
0
            idxDim++;
4486
0
        }
4487
0
    }
4488
4489
0
    auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4490
0
                                             GetDataType(), aosOptions.List());
4491
0
    if (!poCachedArray)
4492
0
    {
4493
0
        CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4494
0
                 osCachedArrayName.c_str(), osCacheFilename.c_str());
4495
0
        return false;
4496
0
    }
4497
4498
0
    GUInt64 nCost = 0;
4499
0
    return poCachedArray->CopyFrom(nullptr, this,
4500
0
                                   false,  // strict
4501
0
                                   nCost, GetTotalCopyCost(), nullptr, nullptr);
4502
0
}
4503
4504
/************************************************************************/
4505
/*                               Read()                                 */
4506
/************************************************************************/
4507
4508
bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4509
                       const GInt64 *arrayStep,         // step in elements
4510
                       const GPtrDiff_t *bufferStride,  // stride in elements
4511
                       const GDALExtendedDataType &bufferDataType,
4512
                       void *pDstBuffer, const void *pDstBufferAllocStart,
4513
                       size_t nDstBufferAllocSize) const
4514
0
{
4515
0
    if (!m_bHasTriedCachedArray)
4516
0
    {
4517
0
        m_bHasTriedCachedArray = true;
4518
0
        if (IsCacheable())
4519
0
        {
4520
0
            const auto &osFilename = GetFilename();
4521
0
            if (!osFilename.empty() &&
4522
0
                !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4523
0
            {
4524
0
                std::string osCacheFilename;
4525
0
                auto poRG = GetCacheRootGroup(false, osCacheFilename);
4526
0
                if (poRG)
4527
0
                {
4528
0
                    const std::string osCachedArrayName(
4529
0
                        MassageName(GetFullName()));
4530
0
                    m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4531
0
                    if (m_poCachedArray)
4532
0
                    {
4533
0
                        const auto &dims = GetDimensions();
4534
0
                        const auto &cachedDims =
4535
0
                            m_poCachedArray->GetDimensions();
4536
0
                        const size_t nDims = dims.size();
4537
0
                        bool ok =
4538
0
                            m_poCachedArray->GetDataType() == GetDataType() &&
4539
0
                            cachedDims.size() == nDims;
4540
0
                        for (size_t i = 0; ok && i < nDims; ++i)
4541
0
                        {
4542
0
                            ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4543
0
                        }
4544
0
                        if (ok)
4545
0
                        {
4546
0
                            CPLDebug("GDAL", "Cached array for %s found in %s",
4547
0
                                     osCachedArrayName.c_str(),
4548
0
                                     osCacheFilename.c_str());
4549
0
                        }
4550
0
                        else
4551
0
                        {
4552
0
                            CPLError(CE_Warning, CPLE_AppDefined,
4553
0
                                     "Cached array %s in %s has incompatible "
4554
0
                                     "characteristics with current array.",
4555
0
                                     osCachedArrayName.c_str(),
4556
0
                                     osCacheFilename.c_str());
4557
0
                            m_poCachedArray.reset();
4558
0
                        }
4559
0
                    }
4560
0
                }
4561
0
            }
4562
0
        }
4563
0
    }
4564
4565
0
    const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4566
0
    if (!array->GetDataType().CanConvertTo(bufferDataType))
4567
0
    {
4568
0
        CPLError(CE_Failure, CPLE_AppDefined,
4569
0
                 "Array data type is not convertible to buffer data type");
4570
0
        return false;
4571
0
    }
4572
4573
0
    std::vector<GInt64> tmp_arrayStep;
4574
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
4575
0
    if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4576
0
                                     bufferStride, bufferDataType, pDstBuffer,
4577
0
                                     pDstBufferAllocStart, nDstBufferAllocSize,
4578
0
                                     tmp_arrayStep, tmp_bufferStride))
4579
0
    {
4580
0
        return false;
4581
0
    }
4582
4583
0
    return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4584
0
                        bufferDataType, pDstBuffer);
4585
0
}
4586
4587
/************************************************************************/
4588
/*                          GetRootGroup()                              */
4589
/************************************************************************/
4590
4591
/** Return the root group to which this arrays belongs too.
4592
 *
4593
 * Note that arrays may be free standing and some drivers may not implement
4594
 * this method, hence nullptr may be returned.
4595
 *
4596
 * It is used internally by the GetResampled() method to detect if GLT
4597
 * orthorectification is available.
4598
 *
4599
 * @return the root group, or nullptr.
4600
 * @since GDAL 3.8
4601
 */
4602
std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4603
0
{
4604
0
    return nullptr;
4605
0
}
4606
4607
//! @cond Doxygen_Suppress
4608
4609
/************************************************************************/
4610
/*                       IsTransposedRequest()                          */
4611
/************************************************************************/
4612
4613
bool GDALMDArray::IsTransposedRequest(
4614
    const size_t *count,
4615
    const GPtrDiff_t *bufferStride) const  // stride in elements
4616
0
{
4617
    /*
4618
    For example:
4619
    count = [2,3,4]
4620
    strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
4621
    stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
4622
    (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
4623
    (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
4624
    */
4625
0
    const size_t nDims(GetDimensionCount());
4626
0
    size_t nCurStrideForRowMajorStrides = 1;
4627
0
    bool bRowMajorStrides = true;
4628
0
    size_t nElts = 1;
4629
0
    size_t nLastIdx = 0;
4630
0
    for (size_t i = nDims; i > 0;)
4631
0
    {
4632
0
        --i;
4633
0
        if (bufferStride[i] < 0)
4634
0
            return false;
4635
0
        if (static_cast<size_t>(bufferStride[i]) !=
4636
0
            nCurStrideForRowMajorStrides)
4637
0
        {
4638
0
            bRowMajorStrides = false;
4639
0
        }
4640
        // Integer overflows have already been checked in CheckReadWriteParams()
4641
0
        nCurStrideForRowMajorStrides *= count[i];
4642
0
        nElts *= count[i];
4643
0
        nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4644
0
    }
4645
0
    if (bRowMajorStrides)
4646
0
        return false;
4647
0
    return nLastIdx == nElts - 1;
4648
0
}
4649
4650
/************************************************************************/
4651
/*                   CopyToFinalBufferSameDataType()                    */
4652
/************************************************************************/
4653
4654
template <size_t N>
4655
void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4656
                                   size_t nDims, const size_t *count,
4657
                                   const GPtrDiff_t *bufferStride)
4658
0
{
4659
0
    std::vector<size_t> anStackCount(nDims);
4660
0
    std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4661
0
    const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4662
0
#if defined(__GNUC__)
4663
0
#pragma GCC diagnostic push
4664
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
4665
0
#endif
4666
0
    pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4667
0
#if defined(__GNUC__)
4668
0
#pragma GCC diagnostic pop
4669
0
#endif
4670
0
    size_t iDim = 0;
4671
4672
0
lbl_next_depth:
4673
0
    if (iDim == nDims - 1)
4674
0
    {
4675
0
        size_t n = count[iDim];
4676
0
        GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4677
0
        const auto bufferStrideLastDim = bufferStride[iDim] * N;
4678
0
        while (n > 0)
4679
0
        {
4680
0
            --n;
4681
0
            memcpy(pabyDstBuffer, pabySrcBuffer, N);
4682
0
            pabyDstBuffer += bufferStrideLastDim;
4683
0
            pabySrcBuffer += N;
4684
0
        }
4685
0
    }
4686
0
    else
4687
0
    {
4688
0
        anStackCount[iDim] = count[iDim];
4689
0
        while (true)
4690
0
        {
4691
0
            ++iDim;
4692
0
            pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4693
0
            goto lbl_next_depth;
4694
0
        lbl_return_to_caller_in_loop:
4695
0
            --iDim;
4696
0
            --anStackCount[iDim];
4697
0
            if (anStackCount[iDim] == 0)
4698
0
                break;
4699
0
            pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4700
0
        }
4701
0
    }
4702
0
    if (iDim > 0)
4703
0
        goto lbl_return_to_caller_in_loop;
4704
0
}
Unexecuted instantiation: void CopyToFinalBufferSameDataType<1ul>(void const*, void*, unsigned long, unsigned long const*, long long const*)
Unexecuted instantiation: void CopyToFinalBufferSameDataType<2ul>(void const*, void*, unsigned long, unsigned long const*, long long const*)
Unexecuted instantiation: void CopyToFinalBufferSameDataType<4ul>(void const*, void*, unsigned long, unsigned long const*, long long const*)
Unexecuted instantiation: void CopyToFinalBufferSameDataType<8ul>(void const*, void*, unsigned long, unsigned long const*, long long const*)
4705
4706
/************************************************************************/
4707
/*                        CopyToFinalBuffer()                           */
4708
/************************************************************************/
4709
4710
static void CopyToFinalBuffer(const void *pSrcBuffer,
4711
                              const GDALExtendedDataType &eSrcDataType,
4712
                              void *pDstBuffer,
4713
                              const GDALExtendedDataType &eDstDataType,
4714
                              size_t nDims, const size_t *count,
4715
                              const GPtrDiff_t *bufferStride)
4716
0
{
4717
0
    const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4718
    // Use specialized implementation for well-known data types when no
4719
    // type conversion is needed
4720
0
    if (eSrcDataType == eDstDataType)
4721
0
    {
4722
0
        if (nSrcDataTypeSize == 1)
4723
0
        {
4724
0
            CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4725
0
                                             count, bufferStride);
4726
0
            return;
4727
0
        }
4728
0
        else if (nSrcDataTypeSize == 2)
4729
0
        {
4730
0
            CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4731
0
                                             count, bufferStride);
4732
0
            return;
4733
0
        }
4734
0
        else if (nSrcDataTypeSize == 4)
4735
0
        {
4736
0
            CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4737
0
                                             count, bufferStride);
4738
0
            return;
4739
0
        }
4740
0
        else if (nSrcDataTypeSize == 8)
4741
0
        {
4742
0
            CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4743
0
                                             count, bufferStride);
4744
0
            return;
4745
0
        }
4746
0
    }
4747
4748
0
    const size_t nDstDataTypeSize(eDstDataType.GetSize());
4749
0
    std::vector<size_t> anStackCount(nDims);
4750
0
    std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4751
0
    const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4752
0
    pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4753
0
    size_t iDim = 0;
4754
4755
0
lbl_next_depth:
4756
0
    if (iDim == nDims - 1)
4757
0
    {
4758
0
        GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4759
0
                                         pabyDstBufferStack[iDim], eDstDataType,
4760
0
                                         bufferStride[iDim], count[iDim]);
4761
0
        pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4762
0
    }
4763
0
    else
4764
0
    {
4765
0
        anStackCount[iDim] = count[iDim];
4766
0
        while (true)
4767
0
        {
4768
0
            ++iDim;
4769
0
            pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4770
0
            goto lbl_next_depth;
4771
0
        lbl_return_to_caller_in_loop:
4772
0
            --iDim;
4773
0
            --anStackCount[iDim];
4774
0
            if (anStackCount[iDim] == 0)
4775
0
                break;
4776
0
            pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4777
0
        }
4778
0
    }
4779
0
    if (iDim > 0)
4780
0
        goto lbl_return_to_caller_in_loop;
4781
0
}
4782
4783
/************************************************************************/
4784
/*                      TransposeLast2Dims()                            */
4785
/************************************************************************/
4786
4787
static bool TransposeLast2Dims(void *pDstBuffer,
4788
                               const GDALExtendedDataType &eDT,
4789
                               const size_t nDims, const size_t *count,
4790
                               const size_t nEltsNonLast2Dims)
4791
0
{
4792
0
    const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4793
0
    const auto nDTSize = eDT.GetSize();
4794
0
    void *pTempBufferForLast2DimsTranspose =
4795
0
        VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4796
0
    if (pTempBufferForLast2DimsTranspose == nullptr)
4797
0
        return false;
4798
4799
0
    GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4800
0
    for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4801
0
    {
4802
0
        GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4803
0
                        pTempBufferForLast2DimsTranspose,
4804
0
                        eDT.GetNumericDataType(), count[nDims - 1],
4805
0
                        count[nDims - 2]);
4806
0
        memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4807
0
               nDTSize * nEltsLast2Dims);
4808
0
        pabyDstBuffer += nDTSize * nEltsLast2Dims;
4809
0
    }
4810
4811
0
    VSIFree(pTempBufferForLast2DimsTranspose);
4812
4813
0
    return true;
4814
0
}
4815
4816
/************************************************************************/
4817
/*                      ReadForTransposedRequest()                      */
4818
/************************************************************************/
4819
4820
// Using the netCDF/HDF5 APIs to read a slice with strides that express a
4821
// transposed view yield to extremely poor/unusable performance. This fixes
4822
// this by using temporary memory to read in a contiguous buffer in a
4823
// row-major order, and then do the transposition to the final buffer.
4824
4825
bool GDALMDArray::ReadForTransposedRequest(
4826
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4827
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4828
    void *pDstBuffer) const
4829
0
{
4830
0
    const size_t nDims(GetDimensionCount());
4831
0
    if (nDims == 0)
4832
0
    {
4833
0
        CPLAssert(false);
4834
0
        return false;  // shouldn't happen
4835
0
    }
4836
0
    size_t nElts = 1;
4837
0
    for (size_t i = 0; i < nDims; ++i)
4838
0
        nElts *= count[i];
4839
4840
0
    std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4841
0
    tmpBufferStrides.back() = 1;
4842
0
    for (size_t i = nDims - 1; i > 0;)
4843
0
    {
4844
0
        --i;
4845
0
        tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4846
0
    }
4847
4848
0
    const auto &eDT = GetDataType();
4849
0
    const auto nDTSize = eDT.GetSize();
4850
0
    if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4851
0
        static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4852
0
        (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4853
0
    {
4854
        // Optimization of the optimization if only the last 2 dims are
4855
        // transposed that saves on temporary buffer allocation
4856
0
        const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4857
0
        size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4858
0
        bool bRowMajorStridesForNonLast2Dims = true;
4859
0
        size_t nEltsNonLast2Dims = 1;
4860
0
        for (size_t i = nDims - 2; i > 0;)
4861
0
        {
4862
0
            --i;
4863
0
            if (static_cast<size_t>(bufferStride[i]) !=
4864
0
                nCurStrideForRowMajorStrides)
4865
0
            {
4866
0
                bRowMajorStridesForNonLast2Dims = false;
4867
0
            }
4868
            // Integer overflows have already been checked in
4869
            // CheckReadWriteParams()
4870
0
            nCurStrideForRowMajorStrides *= count[i];
4871
0
            nEltsNonLast2Dims *= count[i];
4872
0
        }
4873
0
        if (bRowMajorStridesForNonLast2Dims)
4874
0
        {
4875
            // We read in the final buffer!
4876
0
            if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4877
0
                       eDT, pDstBuffer))
4878
0
            {
4879
0
                return false;
4880
0
            }
4881
4882
0
            return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4883
0
                                      nEltsNonLast2Dims);
4884
0
        }
4885
0
    }
4886
4887
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4888
0
    if (pTempBuffer == nullptr)
4889
0
        return false;
4890
4891
0
    if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4892
0
               pTempBuffer))
4893
0
    {
4894
0
        VSIFree(pTempBuffer);
4895
0
        return false;
4896
0
    }
4897
0
    CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4898
0
                      count, bufferStride);
4899
4900
0
    if (eDT.NeedsFreeDynamicMemory())
4901
0
    {
4902
0
        GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4903
0
        for (size_t i = 0; i < nElts; ++i)
4904
0
        {
4905
0
            eDT.FreeDynamicMemory(pabyPtr);
4906
0
            pabyPtr += nDTSize;
4907
0
        }
4908
0
    }
4909
4910
0
    VSIFree(pTempBuffer);
4911
0
    return true;
4912
0
}
4913
4914
/************************************************************************/
4915
/*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
4916
/************************************************************************/
4917
4918
// Returns true if at all following conditions are met:
4919
// arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4920
// defines a row-major ordered contiguous buffer.
4921
bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4922
    const size_t *count, const GInt64 *arrayStep,
4923
    const GPtrDiff_t *bufferStride,
4924
    const GDALExtendedDataType &bufferDataType) const
4925
0
{
4926
0
    if (bufferDataType != GetDataType())
4927
0
        return false;
4928
0
    size_t nExpectedStride = 1;
4929
0
    for (size_t i = GetDimensionCount(); i > 0;)
4930
0
    {
4931
0
        --i;
4932
0
        if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4933
0
            static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4934
0
        {
4935
0
            return false;
4936
0
        }
4937
0
        nExpectedStride *= count[i];
4938
0
    }
4939
0
    return true;
4940
0
}
4941
4942
/************************************************************************/
4943
/*                      ReadUsingContiguousIRead()                      */
4944
/************************************************************************/
4945
4946
// Used for example by the TileDB driver when requesting it with
4947
// arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4948
// not defining a row-major ordered contiguous buffer.
4949
// Should only be called when at least one of the above conditions are true,
4950
// which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4951
// returning none.
4952
// This method will call IRead() again with arrayStep[] == 1,
4953
// bufferDataType == GetDataType() and bufferStride[] defining a row-major
4954
// ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4955
// content of that temporary buffer onto pDstBuffer.
4956
bool GDALMDArray::ReadUsingContiguousIRead(
4957
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4958
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4959
    void *pDstBuffer) const
4960
0
{
4961
0
    const size_t nDims(GetDimensionCount());
4962
0
    std::vector<GUInt64> anTmpStartIdx(nDims);
4963
0
    std::vector<size_t> anTmpCount(nDims);
4964
0
    const auto &oType = GetDataType();
4965
0
    size_t nMemArraySize = oType.GetSize();
4966
0
    std::vector<GPtrDiff_t> anTmpStride(nDims);
4967
0
    GPtrDiff_t nStride = 1;
4968
0
    for (size_t i = nDims; i > 0;)
4969
0
    {
4970
0
        --i;
4971
0
        if (arrayStep[i] > 0)
4972
0
            anTmpStartIdx[i] = arrayStartIdx[i];
4973
0
        else
4974
0
            anTmpStartIdx[i] =
4975
0
                arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
4976
0
        const uint64_t nCount =
4977
0
            (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
4978
0
        if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
4979
0
        {
4980
0
            CPLError(CE_Failure, CPLE_AppDefined,
4981
0
                     "Read() failed due to too large memory requirement");
4982
0
            return false;
4983
0
        }
4984
0
        anTmpCount[i] = static_cast<size_t>(nCount);
4985
0
        nMemArraySize *= anTmpCount[i];
4986
0
        anTmpStride[i] = nStride;
4987
0
        nStride *= anTmpCount[i];
4988
0
    }
4989
0
    std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
4990
0
        VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
4991
0
    if (!pTmpBuffer)
4992
0
        return false;
4993
0
    if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
4994
0
               std::vector<GInt64>(nDims, 1).data(),  // steps
4995
0
               anTmpStride.data(), oType, pTmpBuffer.get()))
4996
0
    {
4997
0
        return false;
4998
0
    }
4999
0
    std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5000
0
    for (size_t i = 0; i < nDims; ++i)
5001
0
    {
5002
0
        if (arrayStep[i] > 0)
5003
0
            anTmpStartIdx[i] = 0;
5004
0
        else
5005
0
            anTmpStartIdx[i] = anTmpCount[i] - 1;
5006
0
        apoTmpDims[i] = std::make_shared<GDALDimension>(
5007
0
            std::string(), std::string(), std::string(), std::string(),
5008
0
            anTmpCount[i]);
5009
0
    }
5010
0
    auto poMEMArray =
5011
0
        MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5012
0
    return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5013
0
           poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5014
0
                            bufferStride, bufferDataType, pDstBuffer);
5015
0
}
5016
5017
//! @endcond
5018
5019
/************************************************************************/
5020
/*                       GDALSlicedMDArray                              */
5021
/************************************************************************/
5022
5023
class GDALSlicedMDArray final : public GDALPamMDArray
5024
{
5025
  private:
5026
    std::shared_ptr<GDALMDArray> m_poParent{};
5027
    std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5028
    std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
5029
    std::vector<Range>
5030
        m_parentRanges{};  // of size m_poParent->GetDimensionCount()
5031
5032
    mutable std::vector<GUInt64> m_parentStart;
5033
    mutable std::vector<size_t> m_parentCount;
5034
    mutable std::vector<GInt64> m_parentStep;
5035
    mutable std::vector<GPtrDiff_t> m_parentStride;
5036
5037
    void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5038
                             const GInt64 *arrayStep,
5039
                             const GPtrDiff_t *bufferStride) const;
5040
5041
  protected:
5042
    explicit GDALSlicedMDArray(
5043
        const std::shared_ptr<GDALMDArray> &poParent,
5044
        const std::string &viewExpr,
5045
        std::vector<std::shared_ptr<GDALDimension>> &&dims,
5046
        std::vector<size_t> &&mapDimIdxToParentDimIdx,
5047
        std::vector<Range> &&parentRanges)
5048
0
        : GDALAbstractMDArray(std::string(), "Sliced view of " +
5049
0
                                                 poParent->GetFullName() +
5050
0
                                                 " (" + viewExpr + ")"),
5051
0
          GDALPamMDArray(std::string(),
5052
0
                         "Sliced view of " + poParent->GetFullName() + " (" +
5053
0
                             viewExpr + ")",
5054
0
                         GDALPamMultiDim::GetPAM(poParent),
5055
0
                         poParent->GetContext()),
5056
0
          m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5057
0
          m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5058
0
          m_parentRanges(std::move(parentRanges)),
5059
0
          m_parentStart(m_poParent->GetDimensionCount()),
5060
0
          m_parentCount(m_poParent->GetDimensionCount(), 1),
5061
0
          m_parentStep(m_poParent->GetDimensionCount()),
5062
0
          m_parentStride(m_poParent->GetDimensionCount())
5063
0
    {
5064
0
    }
5065
5066
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5067
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5068
               const GDALExtendedDataType &bufferDataType,
5069
               void *pDstBuffer) const override;
5070
5071
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5072
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5073
                const GDALExtendedDataType &bufferDataType,
5074
                const void *pSrcBuffer) override;
5075
5076
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5077
                     CSLConstList papszOptions) const override;
5078
5079
  public:
5080
    static std::shared_ptr<GDALSlicedMDArray>
5081
    Create(const std::shared_ptr<GDALMDArray> &poParent,
5082
           const std::string &viewExpr,
5083
           std::vector<std::shared_ptr<GDALDimension>> &&dims,
5084
           std::vector<size_t> &&mapDimIdxToParentDimIdx,
5085
           std::vector<Range> &&parentRanges)
5086
0
    {
5087
0
        CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5088
0
        CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5089
5090
0
        auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5091
0
            poParent, viewExpr, std::move(dims),
5092
0
            std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
5093
0
        newAr->SetSelf(newAr);
5094
0
        return newAr;
5095
0
    }
5096
5097
    bool IsWritable() const override
5098
0
    {
5099
0
        return m_poParent->IsWritable();
5100
0
    }
5101
5102
    const std::string &GetFilename() const override
5103
0
    {
5104
0
        return m_poParent->GetFilename();
5105
0
    }
5106
5107
    const std::vector<std::shared_ptr<GDALDimension>> &
5108
    GetDimensions() const override
5109
0
    {
5110
0
        return m_dims;
5111
0
    }
5112
5113
    const GDALExtendedDataType &GetDataType() const override
5114
0
    {
5115
0
        return m_poParent->GetDataType();
5116
0
    }
5117
5118
    const std::string &GetUnit() const override
5119
0
    {
5120
0
        return m_poParent->GetUnit();
5121
0
    }
5122
5123
    // bool SetUnit(const std::string& osUnit) override  { return
5124
    // m_poParent->SetUnit(osUnit); }
5125
5126
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5127
0
    {
5128
0
        auto poSrcSRS = m_poParent->GetSpatialRef();
5129
0
        if (!poSrcSRS)
5130
0
            return nullptr;
5131
0
        auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5132
0
        std::vector<int> dstMapping;
5133
0
        for (int srcAxis : srcMapping)
5134
0
        {
5135
0
            bool bFound = false;
5136
0
            for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5137
0
            {
5138
0
                if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5139
0
                    srcAxis - 1)
5140
0
                {
5141
0
                    dstMapping.push_back(static_cast<int>(i) + 1);
5142
0
                    bFound = true;
5143
0
                    break;
5144
0
                }
5145
0
            }
5146
0
            if (!bFound)
5147
0
            {
5148
0
                dstMapping.push_back(0);
5149
0
            }
5150
0
        }
5151
0
        auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5152
0
        poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5153
0
        return poClone;
5154
0
    }
5155
5156
    const void *GetRawNoDataValue() const override
5157
0
    {
5158
0
        return m_poParent->GetRawNoDataValue();
5159
0
    }
5160
5161
    // bool SetRawNoDataValue(const void* pRawNoData) override { return
5162
    // m_poParent->SetRawNoDataValue(pRawNoData); }
5163
5164
    double GetOffset(bool *pbHasOffset,
5165
                     GDALDataType *peStorageType) const override
5166
0
    {
5167
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
5168
0
    }
5169
5170
    double GetScale(bool *pbHasScale,
5171
                    GDALDataType *peStorageType) const override
5172
0
    {
5173
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
5174
0
    }
5175
5176
    // bool SetOffset(double dfOffset) override { return
5177
    // m_poParent->SetOffset(dfOffset); }
5178
5179
    // bool SetScale(double dfScale) override { return
5180
    // m_poParent->SetScale(dfScale); }
5181
5182
    std::vector<GUInt64> GetBlockSize() const override
5183
0
    {
5184
0
        std::vector<GUInt64> ret(GetDimensionCount());
5185
0
        const auto parentBlockSize(m_poParent->GetBlockSize());
5186
0
        for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5187
0
        {
5188
0
            const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5189
0
            if (iOldAxis != static_cast<size_t>(-1))
5190
0
            {
5191
0
                ret[i] = parentBlockSize[iOldAxis];
5192
0
            }
5193
0
        }
5194
0
        return ret;
5195
0
    }
5196
5197
    std::shared_ptr<GDALAttribute>
5198
    GetAttribute(const std::string &osName) const override
5199
0
    {
5200
0
        return m_poParent->GetAttribute(osName);
5201
0
    }
5202
5203
    std::vector<std::shared_ptr<GDALAttribute>>
5204
    GetAttributes(CSLConstList papszOptions = nullptr) const override
5205
0
    {
5206
0
        return m_poParent->GetAttributes(papszOptions);
5207
0
    }
5208
};
5209
5210
/************************************************************************/
5211
/*                        PrepareParentArrays()                         */
5212
/************************************************************************/
5213
5214
void GDALSlicedMDArray::PrepareParentArrays(
5215
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5216
    const GPtrDiff_t *bufferStride) const
5217
0
{
5218
0
    const size_t nParentDimCount = m_parentRanges.size();
5219
0
    for (size_t i = 0; i < nParentDimCount; i++)
5220
0
    {
5221
        // For dimensions in parent that have no existence in sliced array
5222
0
        m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5223
0
    }
5224
5225
0
    for (size_t i = 0; i < m_dims.size(); i++)
5226
0
    {
5227
0
        const auto iParent = m_mapDimIdxToParentDimIdx[i];
5228
0
        if (iParent != static_cast<size_t>(-1))
5229
0
        {
5230
0
            m_parentStart[iParent] =
5231
0
                m_parentRanges[iParent].m_nIncr >= 0
5232
0
                    ? m_parentRanges[iParent].m_nStartIdx +
5233
0
                          arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5234
0
                    : m_parentRanges[iParent].m_nStartIdx -
5235
0
                          arrayStartIdx[i] *
5236
0
                              static_cast<GUInt64>(
5237
0
                                  -m_parentRanges[iParent].m_nIncr);
5238
0
            m_parentCount[iParent] = count[i];
5239
0
            if (arrayStep)
5240
0
            {
5241
0
                m_parentStep[iParent] =
5242
0
                    count[i] == 1 ? 1 :
5243
                                  // other checks should have ensured this does
5244
                        // not overflow
5245
0
                        arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5246
0
            }
5247
0
            if (bufferStride)
5248
0
            {
5249
0
                m_parentStride[iParent] = bufferStride[i];
5250
0
            }
5251
0
        }
5252
0
    }
5253
0
}
5254
5255
/************************************************************************/
5256
/*                             IRead()                                  */
5257
/************************************************************************/
5258
5259
bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5260
                              const GInt64 *arrayStep,
5261
                              const GPtrDiff_t *bufferStride,
5262
                              const GDALExtendedDataType &bufferDataType,
5263
                              void *pDstBuffer) const
5264
0
{
5265
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5266
0
    return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5267
0
                            m_parentStep.data(), m_parentStride.data(),
5268
0
                            bufferDataType, pDstBuffer);
5269
0
}
5270
5271
/************************************************************************/
5272
/*                             IWrite()                                  */
5273
/************************************************************************/
5274
5275
bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5276
                               const size_t *count, const GInt64 *arrayStep,
5277
                               const GPtrDiff_t *bufferStride,
5278
                               const GDALExtendedDataType &bufferDataType,
5279
                               const void *pSrcBuffer)
5280
0
{
5281
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5282
0
    return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5283
0
                             m_parentStep.data(), m_parentStride.data(),
5284
0
                             bufferDataType, pSrcBuffer);
5285
0
}
5286
5287
/************************************************************************/
5288
/*                             IAdviseRead()                            */
5289
/************************************************************************/
5290
5291
bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5292
                                    const size_t *count,
5293
                                    CSLConstList papszOptions) const
5294
0
{
5295
0
    PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5296
0
    return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5297
0
                                  papszOptions);
5298
0
}
5299
5300
/************************************************************************/
5301
/*                        CreateSlicedArray()                           */
5302
/************************************************************************/
5303
5304
static std::shared_ptr<GDALMDArray>
5305
CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5306
                  const std::string &viewExpr, const std::string &activeSlice,
5307
                  bool bRenameDimensions,
5308
                  std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5309
0
{
5310
0
    const auto &srcDims(self->GetDimensions());
5311
0
    if (srcDims.empty())
5312
0
    {
5313
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5314
0
        return nullptr;
5315
0
    }
5316
5317
0
    CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5318
0
    const auto nTokens = static_cast<size_t>(aosTokens.size());
5319
5320
0
    std::vector<std::shared_ptr<GDALDimension>> newDims;
5321
0
    std::vector<size_t> mapDimIdxToParentDimIdx;
5322
0
    std::vector<GDALSlicedMDArray::Range> parentRanges;
5323
0
    newDims.reserve(nTokens);
5324
0
    mapDimIdxToParentDimIdx.reserve(nTokens);
5325
0
    parentRanges.reserve(nTokens);
5326
5327
0
    bool bGotEllipsis = false;
5328
0
    size_t nCurSrcDim = 0;
5329
0
    for (size_t i = 0; i < nTokens; i++)
5330
0
    {
5331
0
        const char *pszIdxSpec = aosTokens[i];
5332
0
        if (EQUAL(pszIdxSpec, "..."))
5333
0
        {
5334
0
            if (bGotEllipsis)
5335
0
            {
5336
0
                CPLError(CE_Failure, CPLE_AppDefined,
5337
0
                         "Only one single ellipsis is supported");
5338
0
                return nullptr;
5339
0
            }
5340
0
            bGotEllipsis = true;
5341
0
            const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5342
0
            for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5343
0
            {
5344
0
                parentRanges.emplace_back(0, 1);
5345
0
                newDims.push_back(srcDims[nCurSrcDim]);
5346
0
                mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5347
0
            }
5348
0
            continue;
5349
0
        }
5350
0
        else if (EQUAL(pszIdxSpec, "newaxis") ||
5351
0
                 EQUAL(pszIdxSpec, "np.newaxis"))
5352
0
        {
5353
0
            newDims.push_back(std::make_shared<GDALDimension>(
5354
0
                std::string(), "newaxis", std::string(), std::string(), 1));
5355
0
            mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5356
0
            continue;
5357
0
        }
5358
0
        else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5359
0
        {
5360
0
            if (nCurSrcDim >= srcDims.size())
5361
0
            {
5362
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5363
0
                         activeSlice.c_str());
5364
0
                return nullptr;
5365
0
            }
5366
5367
0
            auto nVal = CPLAtoGIntBig(pszIdxSpec);
5368
0
            GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5369
0
            if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5370
0
                (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5371
0
            {
5372
0
                CPLError(CE_Failure, CPLE_AppDefined,
5373
0
                         "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5374
0
                return nullptr;
5375
0
            }
5376
0
            if (nVal < 0)
5377
0
                nVal += nDimSize;
5378
0
            parentRanges.emplace_back(nVal, 0);
5379
0
        }
5380
0
        else
5381
0
        {
5382
0
            if (nCurSrcDim >= srcDims.size())
5383
0
            {
5384
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5385
0
                         activeSlice.c_str());
5386
0
                return nullptr;
5387
0
            }
5388
5389
0
            CPLStringList aosRangeTokens(
5390
0
                CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5391
0
            int nRangeTokens = aosRangeTokens.size();
5392
0
            if (nRangeTokens > 3)
5393
0
            {
5394
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5395
0
                         pszIdxSpec);
5396
0
                return nullptr;
5397
0
            }
5398
0
            if (nRangeTokens <= 1)
5399
0
            {
5400
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5401
0
                         pszIdxSpec);
5402
0
                return nullptr;
5403
0
            }
5404
0
            const char *pszStart = aosRangeTokens[0];
5405
0
            const char *pszEnd = aosRangeTokens[1];
5406
0
            const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5407
0
            GDALSlicedMDArray::Range range;
5408
0
            const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5409
0
            range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5410
0
            if (range.m_nIncr == 0 ||
5411
0
                range.m_nIncr == std::numeric_limits<GInt64>::min())
5412
0
            {
5413
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5414
0
                return nullptr;
5415
0
            }
5416
0
            auto startIdx(CPLAtoGIntBig(pszStart));
5417
0
            if (startIdx < 0)
5418
0
            {
5419
0
                if (nDimSize < static_cast<GUInt64>(-startIdx))
5420
0
                    startIdx = 0;
5421
0
                else
5422
0
                    startIdx = nDimSize + startIdx;
5423
0
            }
5424
0
            const bool bPosIncr = range.m_nIncr > 0;
5425
0
            range.m_nStartIdx = startIdx;
5426
0
            range.m_nStartIdx = EQUAL(pszStart, "")
5427
0
                                    ? (bPosIncr ? 0 : nDimSize - 1)
5428
0
                                    : range.m_nStartIdx;
5429
0
            if (range.m_nStartIdx >= nDimSize - 1)
5430
0
                range.m_nStartIdx = nDimSize - 1;
5431
0
            auto endIdx(CPLAtoGIntBig(pszEnd));
5432
0
            if (endIdx < 0)
5433
0
            {
5434
0
                const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5435
0
                if (nDimSize < positiveEndIdx)
5436
0
                    endIdx = 0;
5437
0
                else
5438
0
                    endIdx = nDimSize - positiveEndIdx;
5439
0
            }
5440
0
            GUInt64 nEndIdx = endIdx;
5441
0
            nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5442
0
            if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5443
0
                (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5444
0
            {
5445
0
                CPLError(CE_Failure, CPLE_AppDefined,
5446
0
                         "Output dimension of size 0 is not allowed");
5447
0
                return nullptr;
5448
0
            }
5449
0
            int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5450
0
            const auto nAbsIncr = std::abs(range.m_nIncr);
5451
0
            const GUInt64 newSize =
5452
0
                bPosIncr
5453
0
                    ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5454
0
                    : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5455
0
            if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5456
0
                newSize == srcDims[nCurSrcDim]->GetSize())
5457
0
            {
5458
0
                newDims.push_back(srcDims[nCurSrcDim]);
5459
0
            }
5460
0
            else
5461
0
            {
5462
0
                std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
5463
0
                if (bRenameDimensions)
5464
0
                {
5465
0
                    osNewDimName =
5466
0
                        "subset_" + srcDims[nCurSrcDim]->GetName() +
5467
0
                        CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5468
0
                                   "_" CPL_FRMT_GUIB,
5469
0
                                   static_cast<GUIntBig>(range.m_nStartIdx),
5470
0
                                   static_cast<GIntBig>(range.m_nIncr),
5471
0
                                   static_cast<GUIntBig>(newSize));
5472
0
                }
5473
0
                newDims.push_back(std::make_shared<GDALDimension>(
5474
0
                    std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
5475
0
                    range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
5476
0
                                      : std::string(),
5477
0
                    newSize));
5478
0
            }
5479
0
            mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5480
0
            parentRanges.emplace_back(range);
5481
0
        }
5482
5483
0
        nCurSrcDim++;
5484
0
    }
5485
0
    for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5486
0
    {
5487
0
        parentRanges.emplace_back(0, 1);
5488
0
        newDims.push_back(srcDims[nCurSrcDim]);
5489
0
        mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5490
0
    }
5491
5492
0
    GDALMDArray::ViewSpec viewSpec;
5493
0
    viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5494
0
    viewSpec.m_parentRanges = parentRanges;
5495
0
    viewSpecs.emplace_back(std::move(viewSpec));
5496
5497
0
    return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
5498
0
                                     std::move(mapDimIdxToParentDimIdx),
5499
0
                                     std::move(parentRanges));
5500
0
}
5501
5502
/************************************************************************/
5503
/*                       GDALExtractFieldMDArray                        */
5504
/************************************************************************/
5505
5506
class GDALExtractFieldMDArray final : public GDALPamMDArray
5507
{
5508
  private:
5509
    std::shared_ptr<GDALMDArray> m_poParent{};
5510
    GDALExtendedDataType m_dt;
5511
    std::string m_srcCompName;
5512
    mutable std::vector<GByte> m_pabyNoData{};
5513
5514
  protected:
5515
    GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5516
                            const std::string &fieldName,
5517
                            const std::unique_ptr<GDALEDTComponent> &srcComp)
5518
0
        : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5519
0
                                                 " of " +
5520
0
                                                 poParent->GetFullName()),
5521
0
          GDALPamMDArray(
5522
0
              std::string(),
5523
0
              "Extract field " + fieldName + " of " + poParent->GetFullName(),
5524
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5525
0
          m_poParent(poParent), m_dt(srcComp->GetType()),
5526
0
          m_srcCompName(srcComp->GetName())
5527
0
    {
5528
0
        m_pabyNoData.resize(m_dt.GetSize());
5529
0
    }
5530
5531
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5532
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5533
               const GDALExtendedDataType &bufferDataType,
5534
               void *pDstBuffer) const override;
5535
5536
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5537
                     CSLConstList papszOptions) const override
5538
0
    {
5539
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5540
0
    }
5541
5542
  public:
5543
    static std::shared_ptr<GDALExtractFieldMDArray>
5544
    Create(const std::shared_ptr<GDALMDArray> &poParent,
5545
           const std::string &fieldName,
5546
           const std::unique_ptr<GDALEDTComponent> &srcComp)
5547
0
    {
5548
0
        auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5549
0
            new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5550
0
        newAr->SetSelf(newAr);
5551
0
        return newAr;
5552
0
    }
5553
5554
    ~GDALExtractFieldMDArray()
5555
0
    {
5556
0
        m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5557
0
    }
5558
5559
    bool IsWritable() const override
5560
0
    {
5561
0
        return m_poParent->IsWritable();
5562
0
    }
5563
5564
    const std::string &GetFilename() const override
5565
0
    {
5566
0
        return m_poParent->GetFilename();
5567
0
    }
5568
5569
    const std::vector<std::shared_ptr<GDALDimension>> &
5570
    GetDimensions() const override
5571
0
    {
5572
0
        return m_poParent->GetDimensions();
5573
0
    }
5574
5575
    const GDALExtendedDataType &GetDataType() const override
5576
0
    {
5577
0
        return m_dt;
5578
0
    }
5579
5580
    const std::string &GetUnit() const override
5581
0
    {
5582
0
        return m_poParent->GetUnit();
5583
0
    }
5584
5585
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5586
0
    {
5587
0
        return m_poParent->GetSpatialRef();
5588
0
    }
5589
5590
    const void *GetRawNoDataValue() const override
5591
0
    {
5592
0
        const void *parentNoData = m_poParent->GetRawNoDataValue();
5593
0
        if (parentNoData == nullptr)
5594
0
            return nullptr;
5595
5596
0
        m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5597
0
        memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5598
5599
0
        std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5600
0
        comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5601
0
            new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5602
0
        auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5603
0
                                                std::move(comps)));
5604
5605
0
        GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5606
0
                                        &m_pabyNoData[0], tmpDT);
5607
5608
0
        return &m_pabyNoData[0];
5609
0
    }
5610
5611
    double GetOffset(bool *pbHasOffset,
5612
                     GDALDataType *peStorageType) const override
5613
0
    {
5614
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
5615
0
    }
5616
5617
    double GetScale(bool *pbHasScale,
5618
                    GDALDataType *peStorageType) const override
5619
0
    {
5620
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
5621
0
    }
5622
5623
    std::vector<GUInt64> GetBlockSize() const override
5624
0
    {
5625
0
        return m_poParent->GetBlockSize();
5626
0
    }
5627
};
5628
5629
/************************************************************************/
5630
/*                             IRead()                                  */
5631
/************************************************************************/
5632
5633
bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5634
                                    const size_t *count,
5635
                                    const GInt64 *arrayStep,
5636
                                    const GPtrDiff_t *bufferStride,
5637
                                    const GDALExtendedDataType &bufferDataType,
5638
                                    void *pDstBuffer) const
5639
0
{
5640
0
    std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5641
0
    comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5642
0
        new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5643
0
    auto tmpDT(GDALExtendedDataType::Create(
5644
0
        std::string(), bufferDataType.GetSize(), std::move(comps)));
5645
5646
0
    return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5647
0
                            tmpDT, pDstBuffer);
5648
0
}
5649
5650
/************************************************************************/
5651
/*                      CreateFieldNameExtractArray()                   */
5652
/************************************************************************/
5653
5654
static std::shared_ptr<GDALMDArray>
5655
CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5656
                            const std::string &fieldName)
5657
0
{
5658
0
    CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5659
0
    const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5660
0
    for (const auto &comp : self->GetDataType().GetComponents())
5661
0
    {
5662
0
        if (comp->GetName() == fieldName)
5663
0
        {
5664
0
            srcComp = &comp;
5665
0
            break;
5666
0
        }
5667
0
    }
5668
0
    if (srcComp == nullptr)
5669
0
    {
5670
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5671
0
                 fieldName.c_str());
5672
0
        return nullptr;
5673
0
    }
5674
0
    return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5675
0
}
5676
5677
/************************************************************************/
5678
/*                             GetView()                                */
5679
/************************************************************************/
5680
5681
// clang-format off
5682
/** Return a view of the array using slicing or field access.
5683
 *
5684
 * The slice expression uses the same syntax as NumPy basic slicing and
5685
 * indexing. See
5686
 * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5687
 * Or it can use field access by name. See
5688
 * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5689
 *
5690
 * Multiple [] bracket elements can be concatenated, with a slice expression
5691
 * or field name inside each.
5692
 *
5693
 * For basic slicing and indexing, inside each [] bracket element, a list of
5694
 * indexes that apply to successive source dimensions, can be specified, using
5695
 * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5696
 * or newaxis, using a comma separator.
5697
 *
5698
 * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5699
 * <ul>
5700
 * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5701
 *     at index 1 in the first dimension, and index 2 in the second dimension
5702
 *     from the source array. That is 5</li>
5703
 * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5704
 * implemented internally doing this intermediate slicing approach.</li>
5705
 * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5706
 * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5707
 *     first dimension. That is [4,5,6,7].</li>
5708
 * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5709
 *     second dimension. That is [2,6].</li>
5710
 * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5711
 * the second dimension. That is [[2],[6]].</li>
5712
 * <li>GetView("[::,2]"): Same as
5713
 * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5714
 * ellipsis only expands to one dimension here.</li>
5715
 * <li>GetView("[:,::2]"):
5716
 * returns a 2-dimensional array, with even-indexed elements of the second
5717
 * dimension. That is [[0,2],[4,6]].</li>
5718
 * <li>GetView("[:,1::2]"): returns a
5719
 * 2-dimensional array, with odd-indexed elements of the second dimension. That
5720
 * is [[1,3],[5,7]].</li>
5721
 * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5722
 * array, with elements of the second dimension with index in the range [1,3[.
5723
 * That is [[1,2],[5,6]].</li>
5724
 * <li>GetView("[::-1,:]"): returns a 2-dimensional
5725
 * array, with the values in first dimension reversed. That is
5726
 * [[4,5,6,7],[0,1,2,3]].</li>
5727
 * <li>GetView("[newaxis,...]"): returns a
5728
 * 3-dimensional array, with an additional dimension of size 1 put at the
5729
 * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5730
 * </ul>
5731
 *
5732
 * One difference with NumPy behavior is that ranges that would result in
5733
 * zero elements are not allowed (dimensions of size 0 not being allowed in the
5734
 * GDAL multidimensional model).
5735
 *
5736
 * For field access, the syntax to use is ["field_name"] or ['field_name'].
5737
 * Multiple field specification is not supported currently.
5738
 *
5739
 * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5740
 *
5741
 * \note When using the GDAL Python bindings, natural Python syntax can be
5742
 * used. That is ar[0,::,1]["foo"] will be internally translated to
5743
 * ar.GetView("[0,::,1]['foo']")
5744
 * \note When using the C++ API and integer indexing only, you may use the
5745
 * at(idx0, idx1, ...) method.
5746
 *
5747
 * The returned array holds a reference to the original one, and thus is
5748
 * a view of it (not a copy). If the content of the original array changes,
5749
 * the content of the view array too. When using basic slicing and indexing,
5750
 * the view can be written if the underlying array is writable.
5751
 *
5752
 * This is the same as the C function GDALMDArrayGetView()
5753
 *
5754
 * @param viewExpr Expression expressing basic slicing and indexing, or field
5755
 * access.
5756
 * @return a new array, that holds a reference to the original one, and thus is
5757
 * a view of it (not a copy), or nullptr in case of error.
5758
 */
5759
// clang-format on
5760
5761
std::shared_ptr<GDALMDArray>
5762
GDALMDArray::GetView(const std::string &viewExpr) const
5763
0
{
5764
0
    std::vector<ViewSpec> viewSpecs;
5765
0
    return GetView(viewExpr, true, viewSpecs);
5766
0
}
5767
5768
//! @cond Doxygen_Suppress
5769
std::shared_ptr<GDALMDArray>
5770
GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5771
                     std::vector<ViewSpec> &viewSpecs) const
5772
0
{
5773
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5774
0
    if (!self)
5775
0
    {
5776
0
        CPLError(CE_Failure, CPLE_AppDefined,
5777
0
                 "Driver implementation issue: m_pSelf not set !");
5778
0
        return nullptr;
5779
0
    }
5780
0
    std::string curExpr(viewExpr);
5781
0
    while (true)
5782
0
    {
5783
0
        if (curExpr.empty() || curExpr[0] != '[')
5784
0
        {
5785
0
            CPLError(CE_Failure, CPLE_AppDefined,
5786
0
                     "Slice string should start with ['");
5787
0
            return nullptr;
5788
0
        }
5789
5790
0
        std::string fieldName;
5791
0
        size_t endExpr;
5792
0
        if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5793
0
        {
5794
0
            if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5795
0
            {
5796
0
                CPLError(CE_Failure, CPLE_AppDefined,
5797
0
                         "Field access not allowed on non-compound data type");
5798
0
                return nullptr;
5799
0
            }
5800
0
            size_t idx = 2;
5801
0
            for (; idx < curExpr.size(); idx++)
5802
0
            {
5803
0
                const char ch = curExpr[idx];
5804
0
                if (ch == curExpr[1])
5805
0
                    break;
5806
0
                if (ch == '\\' && idx + 1 < curExpr.size())
5807
0
                {
5808
0
                    fieldName += curExpr[idx + 1];
5809
0
                    idx++;
5810
0
                }
5811
0
                else
5812
0
                {
5813
0
                    fieldName += ch;
5814
0
                }
5815
0
            }
5816
0
            if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5817
0
            {
5818
0
                CPLError(CE_Failure, CPLE_AppDefined,
5819
0
                         "Invalid field access specification");
5820
0
                return nullptr;
5821
0
            }
5822
0
            endExpr = idx + 1;
5823
0
        }
5824
0
        else
5825
0
        {
5826
0
            endExpr = curExpr.find(']');
5827
0
        }
5828
0
        if (endExpr == std::string::npos)
5829
0
        {
5830
0
            CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5831
0
            return nullptr;
5832
0
        }
5833
0
        if (endExpr == 1)
5834
0
        {
5835
0
            CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5836
0
            return nullptr;
5837
0
        }
5838
0
        std::string activeSlice(curExpr.substr(1, endExpr - 1));
5839
5840
0
        if (!fieldName.empty())
5841
0
        {
5842
0
            ViewSpec viewSpec;
5843
0
            viewSpec.m_osFieldName = fieldName;
5844
0
            viewSpecs.emplace_back(std::move(viewSpec));
5845
0
        }
5846
5847
0
        auto newArray = !fieldName.empty()
5848
0
                            ? CreateFieldNameExtractArray(self, fieldName)
5849
0
                            : CreateSlicedArray(self, viewExpr, activeSlice,
5850
0
                                                bRenameDimensions, viewSpecs);
5851
5852
0
        if (endExpr == curExpr.size() - 1)
5853
0
        {
5854
0
            return newArray;
5855
0
        }
5856
0
        self = std::move(newArray);
5857
0
        curExpr = curExpr.substr(endExpr + 1);
5858
0
    }
5859
0
}
5860
5861
//! @endcond
5862
5863
std::shared_ptr<GDALMDArray>
5864
GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5865
0
{
5866
0
    std::string osExpr("[");
5867
0
    bool bFirst = true;
5868
0
    for (const auto &idx : indices)
5869
0
    {
5870
0
        if (!bFirst)
5871
0
            osExpr += ',';
5872
0
        bFirst = false;
5873
0
        osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5874
0
    }
5875
0
    return GetView(osExpr + ']');
5876
0
}
5877
5878
/************************************************************************/
5879
/*                            operator[]                                */
5880
/************************************************************************/
5881
5882
/** Return a view of the array using field access
5883
 *
5884
 * Equivalent of GetView("['fieldName']")
5885
 *
5886
 * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
5887
 */
5888
std::shared_ptr<GDALMDArray>
5889
GDALMDArray::operator[](const std::string &fieldName) const
5890
0
{
5891
0
    return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5892
0
                                            .replaceAll('\\', "\\\\")
5893
0
                                            .replaceAll('\'', "\\\'")
5894
0
                                            .c_str()));
5895
0
}
5896
5897
/************************************************************************/
5898
/*                      GDALMDArrayTransposed                           */
5899
/************************************************************************/
5900
5901
class GDALMDArrayTransposed final : public GDALPamMDArray
5902
{
5903
  private:
5904
    std::shared_ptr<GDALMDArray> m_poParent{};
5905
    std::vector<int> m_anMapNewAxisToOldAxis{};
5906
    std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5907
5908
    mutable std::vector<GUInt64> m_parentStart;
5909
    mutable std::vector<size_t> m_parentCount;
5910
    mutable std::vector<GInt64> m_parentStep;
5911
    mutable std::vector<GPtrDiff_t> m_parentStride;
5912
5913
    void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5914
                             const GInt64 *arrayStep,
5915
                             const GPtrDiff_t *bufferStride) const;
5916
5917
    static std::string
5918
    MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5919
0
    {
5920
0
        std::string ret;
5921
0
        ret += '[';
5922
0
        for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5923
0
        {
5924
0
            if (i > 0)
5925
0
                ret += ',';
5926
0
            ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5927
0
        }
5928
0
        ret += ']';
5929
0
        return ret;
5930
0
    }
5931
5932
  protected:
5933
    GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5934
                          const std::vector<int> &anMapNewAxisToOldAxis,
5935
                          std::vector<std::shared_ptr<GDALDimension>> &&dims)
5936
0
        : GDALAbstractMDArray(std::string(),
5937
0
                              "Transposed view of " + poParent->GetFullName() +
5938
0
                                  " along " +
5939
0
                                  MappingToStr(anMapNewAxisToOldAxis)),
5940
0
          GDALPamMDArray(std::string(),
5941
0
                         "Transposed view of " + poParent->GetFullName() +
5942
0
                             " along " + MappingToStr(anMapNewAxisToOldAxis),
5943
0
                         GDALPamMultiDim::GetPAM(poParent),
5944
0
                         poParent->GetContext()),
5945
0
          m_poParent(std::move(poParent)),
5946
0
          m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
5947
0
          m_dims(std::move(dims)),
5948
0
          m_parentStart(m_poParent->GetDimensionCount()),
5949
0
          m_parentCount(m_poParent->GetDimensionCount()),
5950
0
          m_parentStep(m_poParent->GetDimensionCount()),
5951
0
          m_parentStride(m_poParent->GetDimensionCount())
5952
0
    {
5953
0
    }
5954
5955
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5956
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5957
               const GDALExtendedDataType &bufferDataType,
5958
               void *pDstBuffer) const override;
5959
5960
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5961
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5962
                const GDALExtendedDataType &bufferDataType,
5963
                const void *pSrcBuffer) override;
5964
5965
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5966
                     CSLConstList papszOptions) const override;
5967
5968
  public:
5969
    static std::shared_ptr<GDALMDArrayTransposed>
5970
    Create(const std::shared_ptr<GDALMDArray> &poParent,
5971
           const std::vector<int> &anMapNewAxisToOldAxis)
5972
0
    {
5973
0
        const auto &parentDims(poParent->GetDimensions());
5974
0
        std::vector<std::shared_ptr<GDALDimension>> dims;
5975
0
        for (const auto iOldAxis : anMapNewAxisToOldAxis)
5976
0
        {
5977
0
            if (iOldAxis < 0)
5978
0
            {
5979
0
                dims.push_back(std::make_shared<GDALDimension>(
5980
0
                    std::string(), "newaxis", std::string(), std::string(), 1));
5981
0
            }
5982
0
            else
5983
0
            {
5984
0
                dims.emplace_back(parentDims[iOldAxis]);
5985
0
            }
5986
0
        }
5987
5988
0
        auto newAr(
5989
0
            std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
5990
0
                poParent, anMapNewAxisToOldAxis, std::move(dims))));
5991
0
        newAr->SetSelf(newAr);
5992
0
        return newAr;
5993
0
    }
5994
5995
    bool IsWritable() const override
5996
0
    {
5997
0
        return m_poParent->IsWritable();
5998
0
    }
5999
6000
    const std::string &GetFilename() const override
6001
0
    {
6002
0
        return m_poParent->GetFilename();
6003
0
    }
6004
6005
    const std::vector<std::shared_ptr<GDALDimension>> &
6006
    GetDimensions() const override
6007
0
    {
6008
0
        return m_dims;
6009
0
    }
6010
6011
    const GDALExtendedDataType &GetDataType() const override
6012
0
    {
6013
0
        return m_poParent->GetDataType();
6014
0
    }
6015
6016
    const std::string &GetUnit() const override
6017
0
    {
6018
0
        return m_poParent->GetUnit();
6019
0
    }
6020
6021
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6022
0
    {
6023
0
        auto poSrcSRS = m_poParent->GetSpatialRef();
6024
0
        if (!poSrcSRS)
6025
0
            return nullptr;
6026
0
        auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6027
0
        std::vector<int> dstMapping;
6028
0
        for (int srcAxis : srcMapping)
6029
0
        {
6030
0
            bool bFound = false;
6031
0
            for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6032
0
            {
6033
0
                if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6034
0
                {
6035
0
                    dstMapping.push_back(static_cast<int>(i) + 1);
6036
0
                    bFound = true;
6037
0
                    break;
6038
0
                }
6039
0
            }
6040
0
            if (!bFound)
6041
0
            {
6042
0
                dstMapping.push_back(0);
6043
0
            }
6044
0
        }
6045
0
        auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6046
0
        poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6047
0
        return poClone;
6048
0
    }
6049
6050
    const void *GetRawNoDataValue() const override
6051
0
    {
6052
0
        return m_poParent->GetRawNoDataValue();
6053
0
    }
6054
6055
    // bool SetRawNoDataValue(const void* pRawNoData) override { return
6056
    // m_poParent->SetRawNoDataValue(pRawNoData); }
6057
6058
    double GetOffset(bool *pbHasOffset,
6059
                     GDALDataType *peStorageType) const override
6060
0
    {
6061
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
6062
0
    }
6063
6064
    double GetScale(bool *pbHasScale,
6065
                    GDALDataType *peStorageType) const override
6066
0
    {
6067
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
6068
0
    }
6069
6070
    // bool SetOffset(double dfOffset) override { return
6071
    // m_poParent->SetOffset(dfOffset); }
6072
6073
    // bool SetScale(double dfScale) override { return
6074
    // m_poParent->SetScale(dfScale); }
6075
6076
    std::vector<GUInt64> GetBlockSize() const override
6077
0
    {
6078
0
        std::vector<GUInt64> ret(GetDimensionCount());
6079
0
        const auto parentBlockSize(m_poParent->GetBlockSize());
6080
0
        for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6081
0
        {
6082
0
            const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6083
0
            if (iOldAxis >= 0)
6084
0
            {
6085
0
                ret[i] = parentBlockSize[iOldAxis];
6086
0
            }
6087
0
        }
6088
0
        return ret;
6089
0
    }
6090
6091
    std::shared_ptr<GDALAttribute>
6092
    GetAttribute(const std::string &osName) const override
6093
0
    {
6094
0
        return m_poParent->GetAttribute(osName);
6095
0
    }
6096
6097
    std::vector<std::shared_ptr<GDALAttribute>>
6098
    GetAttributes(CSLConstList papszOptions = nullptr) const override
6099
0
    {
6100
0
        return m_poParent->GetAttributes(papszOptions);
6101
0
    }
6102
};
6103
6104
/************************************************************************/
6105
/*                         PrepareParentArrays()                        */
6106
/************************************************************************/
6107
6108
void GDALMDArrayTransposed::PrepareParentArrays(
6109
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6110
    const GPtrDiff_t *bufferStride) const
6111
0
{
6112
0
    for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6113
0
    {
6114
0
        const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6115
0
        if (iOldAxis >= 0)
6116
0
        {
6117
0
            m_parentStart[iOldAxis] = arrayStartIdx[i];
6118
0
            m_parentCount[iOldAxis] = count[i];
6119
0
            if (arrayStep)  // only null when called from IAdviseRead()
6120
0
            {
6121
0
                m_parentStep[iOldAxis] = arrayStep[i];
6122
0
            }
6123
0
            if (bufferStride)  // only null when called from IAdviseRead()
6124
0
            {
6125
0
                m_parentStride[iOldAxis] = bufferStride[i];
6126
0
            }
6127
0
        }
6128
0
    }
6129
0
}
6130
6131
/************************************************************************/
6132
/*                             IRead()                                  */
6133
/************************************************************************/
6134
6135
bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6136
                                  const size_t *count, const GInt64 *arrayStep,
6137
                                  const GPtrDiff_t *bufferStride,
6138
                                  const GDALExtendedDataType &bufferDataType,
6139
                                  void *pDstBuffer) const
6140
0
{
6141
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6142
0
    return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6143
0
                            m_parentStep.data(), m_parentStride.data(),
6144
0
                            bufferDataType, pDstBuffer);
6145
0
}
6146
6147
/************************************************************************/
6148
/*                            IWrite()                                  */
6149
/************************************************************************/
6150
6151
bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6152
                                   const size_t *count, const GInt64 *arrayStep,
6153
                                   const GPtrDiff_t *bufferStride,
6154
                                   const GDALExtendedDataType &bufferDataType,
6155
                                   const void *pSrcBuffer)
6156
0
{
6157
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6158
0
    return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6159
0
                             m_parentStep.data(), m_parentStride.data(),
6160
0
                             bufferDataType, pSrcBuffer);
6161
0
}
6162
6163
/************************************************************************/
6164
/*                             IAdviseRead()                            */
6165
/************************************************************************/
6166
6167
bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6168
                                        const size_t *count,
6169
                                        CSLConstList papszOptions) const
6170
0
{
6171
0
    PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6172
0
    return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6173
0
                                  papszOptions);
6174
0
}
6175
6176
/************************************************************************/
6177
/*                           Transpose()                                */
6178
/************************************************************************/
6179
6180
/** Return a view of the array whose axis have been reordered.
6181
 *
6182
 * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6183
 * and GetDimensionCount() - 1, and each only once.
6184
 * -1 can be used as a special index value to ask for the insertion of a new
6185
 * axis of size 1.
6186
 * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6187
 * index of one of its dimension, it corresponds to the axis of index
6188
 * anMapNewAxisToOldAxis[i] from the current array.
6189
 *
6190
 * This is similar to the numpy.transpose() method
6191
 *
6192
 * The returned array holds a reference to the original one, and thus is
6193
 * a view of it (not a copy). If the content of the original array changes,
6194
 * the content of the view array too. The view can be written if the underlying
6195
 * array is writable.
6196
 *
6197
 * Note that I/O performance in such a transposed view might be poor.
6198
 *
6199
 * This is the same as the C function GDALMDArrayTranspose().
6200
 *
6201
 * @return a new array, that holds a reference to the original one, and thus is
6202
 * a view of it (not a copy), or nullptr in case of error.
6203
 */
6204
std::shared_ptr<GDALMDArray>
6205
GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6206
0
{
6207
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6208
0
    if (!self)
6209
0
    {
6210
0
        CPLError(CE_Failure, CPLE_AppDefined,
6211
0
                 "Driver implementation issue: m_pSelf not set !");
6212
0
        return nullptr;
6213
0
    }
6214
0
    const int nDims = static_cast<int>(GetDimensionCount());
6215
0
    std::vector<bool> alreadyUsedOldAxis(nDims, false);
6216
0
    int nCountOldAxis = 0;
6217
0
    for (const auto iOldAxis : anMapNewAxisToOldAxis)
6218
0
    {
6219
0
        if (iOldAxis < -1 || iOldAxis >= nDims)
6220
0
        {
6221
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6222
0
            return nullptr;
6223
0
        }
6224
0
        if (iOldAxis >= 0)
6225
0
        {
6226
0
            if (alreadyUsedOldAxis[iOldAxis])
6227
0
            {
6228
0
                CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6229
0
                         iOldAxis);
6230
0
                return nullptr;
6231
0
            }
6232
0
            alreadyUsedOldAxis[iOldAxis] = true;
6233
0
            nCountOldAxis++;
6234
0
        }
6235
0
    }
6236
0
    if (nCountOldAxis != nDims)
6237
0
    {
6238
0
        CPLError(CE_Failure, CPLE_AppDefined,
6239
0
                 "One or several original axis missing");
6240
0
        return nullptr;
6241
0
    }
6242
0
    return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6243
0
}
6244
6245
/************************************************************************/
6246
/*                             IRead()                                  */
6247
/************************************************************************/
6248
6249
bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6250
                                const size_t *count, const GInt64 *arrayStep,
6251
                                const GPtrDiff_t *bufferStride,
6252
                                const GDALExtendedDataType &bufferDataType,
6253
                                void *pDstBuffer) const
6254
0
{
6255
0
    const double dfScale = m_dfScale;
6256
0
    const double dfOffset = m_dfOffset;
6257
0
    const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6258
0
    const auto dtDouble =
6259
0
        GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6260
0
    const size_t nDTSize = dtDouble.GetSize();
6261
0
    const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6262
6263
0
    double adfSrcNoData[2] = {0, 0};
6264
0
    if (m_bHasNoData)
6265
0
    {
6266
0
        GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6267
0
                                        m_poParent->GetDataType(),
6268
0
                                        &adfSrcNoData[0], dtDouble);
6269
0
    }
6270
6271
0
    const auto nDims = GetDimensions().size();
6272
0
    if (nDims == 0)
6273
0
    {
6274
0
        double adfVal[2];
6275
0
        if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6276
0
                              dtDouble, &adfVal[0]))
6277
0
        {
6278
0
            return false;
6279
0
        }
6280
0
        if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6281
0
        {
6282
0
            adfVal[0] = adfVal[0] * dfScale + dfOffset;
6283
0
            if (bDTIsComplex)
6284
0
            {
6285
0
                adfVal[1] = adfVal[1] * dfScale + dfOffset;
6286
0
            }
6287
0
            GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6288
0
                                            bufferDataType);
6289
0
        }
6290
0
        else
6291
0
        {
6292
0
            GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6293
0
                                            pDstBuffer, bufferDataType);
6294
0
        }
6295
0
        return true;
6296
0
    }
6297
6298
0
    std::vector<GPtrDiff_t> actualBufferStrideVector;
6299
0
    const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6300
0
    void *pTempBuffer = pDstBuffer;
6301
0
    if (bTempBufferNeeded)
6302
0
    {
6303
0
        size_t nElts = 1;
6304
0
        actualBufferStrideVector.resize(nDims);
6305
0
        for (size_t i = 0; i < nDims; i++)
6306
0
            nElts *= count[i];
6307
0
        actualBufferStrideVector.back() = 1;
6308
0
        for (size_t i = nDims - 1; i > 0;)
6309
0
        {
6310
0
            --i;
6311
0
            actualBufferStrideVector[i] =
6312
0
                actualBufferStrideVector[i + 1] * count[i + 1];
6313
0
        }
6314
0
        actualBufferStridePtr = actualBufferStrideVector.data();
6315
0
        pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6316
0
        if (!pTempBuffer)
6317
0
            return false;
6318
0
    }
6319
0
    if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6320
0
                          actualBufferStridePtr, dtDouble, pTempBuffer))
6321
0
    {
6322
0
        if (bTempBufferNeeded)
6323
0
            VSIFree(pTempBuffer);
6324
0
        return false;
6325
0
    }
6326
6327
0
    struct Stack
6328
0
    {
6329
0
        size_t nIters = 0;
6330
0
        double *src_ptr = nullptr;
6331
0
        GByte *dst_ptr = nullptr;
6332
0
        GPtrDiff_t src_inc_offset = 0;
6333
0
        GPtrDiff_t dst_inc_offset = 0;
6334
0
    };
6335
6336
0
    std::vector<Stack> stack(nDims);
6337
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
6338
0
    for (size_t i = 0; i < nDims; i++)
6339
0
    {
6340
0
        stack[i].src_inc_offset =
6341
0
            actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6342
0
        stack[i].dst_inc_offset =
6343
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6344
0
    }
6345
0
    stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6346
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6347
6348
0
    size_t dimIdx = 0;
6349
0
    const size_t nDimsMinus1 = nDims - 1;
6350
0
    GByte abyDstNoData[16];
6351
0
    CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6352
0
    GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6353
0
                                    bufferDataType);
6354
6355
0
lbl_next_depth:
6356
0
    if (dimIdx == nDimsMinus1)
6357
0
    {
6358
0
        auto nIters = count[dimIdx];
6359
0
        double *padfVal = stack[dimIdx].src_ptr;
6360
0
        GByte *dst_ptr = stack[dimIdx].dst_ptr;
6361
0
        while (true)
6362
0
        {
6363
0
            if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6364
0
            {
6365
0
                padfVal[0] = padfVal[0] * dfScale + dfOffset;
6366
0
                if (bDTIsComplex)
6367
0
                {
6368
0
                    padfVal[1] = padfVal[1] * dfScale + dfOffset;
6369
0
                }
6370
0
                if (bTempBufferNeeded)
6371
0
                {
6372
0
                    GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6373
0
                                                    dst_ptr, bufferDataType);
6374
0
                }
6375
0
            }
6376
0
            else
6377
0
            {
6378
0
                memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6379
0
            }
6380
6381
0
            if ((--nIters) == 0)
6382
0
                break;
6383
0
            padfVal += stack[dimIdx].src_inc_offset;
6384
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
6385
0
        }
6386
0
    }
6387
0
    else
6388
0
    {
6389
0
        stack[dimIdx].nIters = count[dimIdx];
6390
0
        while (true)
6391
0
        {
6392
0
            dimIdx++;
6393
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6394
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6395
0
            goto lbl_next_depth;
6396
0
        lbl_return_to_caller:
6397
0
            dimIdx--;
6398
0
            if ((--stack[dimIdx].nIters) == 0)
6399
0
                break;
6400
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6401
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6402
0
        }
6403
0
    }
6404
0
    if (dimIdx > 0)
6405
0
        goto lbl_return_to_caller;
6406
6407
0
    if (bTempBufferNeeded)
6408
0
        VSIFree(pTempBuffer);
6409
0
    return true;
6410
0
}
6411
6412
/************************************************************************/
6413
/*                             IWrite()                                 */
6414
/************************************************************************/
6415
6416
bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6417
                                 const size_t *count, const GInt64 *arrayStep,
6418
                                 const GPtrDiff_t *bufferStride,
6419
                                 const GDALExtendedDataType &bufferDataType,
6420
                                 const void *pSrcBuffer)
6421
0
{
6422
0
    const double dfScale = m_dfScale;
6423
0
    const double dfOffset = m_dfOffset;
6424
0
    const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6425
0
    const auto dtDouble =
6426
0
        GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6427
0
    const size_t nDTSize = dtDouble.GetSize();
6428
0
    const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6429
0
    const bool bSelfAndParentHaveNoData =
6430
0
        m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6431
0
    double dfNoData = 0;
6432
0
    if (m_bHasNoData)
6433
0
    {
6434
0
        GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6435
0
                        &dfNoData, GDT_Float64, 0, 1);
6436
0
    }
6437
6438
0
    double adfSrcNoData[2] = {0, 0};
6439
0
    if (bSelfAndParentHaveNoData)
6440
0
    {
6441
0
        GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6442
0
                                        m_poParent->GetDataType(),
6443
0
                                        &adfSrcNoData[0], dtDouble);
6444
0
    }
6445
6446
0
    const auto nDims = GetDimensions().size();
6447
0
    if (nDims == 0)
6448
0
    {
6449
0
        double adfVal[2];
6450
0
        GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6451
0
                                        dtDouble);
6452
0
        if (bSelfAndParentHaveNoData &&
6453
0
            (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6454
0
        {
6455
0
            return m_poParent->Write(arrayStartIdx, count, arrayStep,
6456
0
                                     bufferStride, m_poParent->GetDataType(),
6457
0
                                     m_poParent->GetRawNoDataValue());
6458
0
        }
6459
0
        else
6460
0
        {
6461
0
            adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6462
0
            if (bDTIsComplex)
6463
0
            {
6464
0
                adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6465
0
            }
6466
0
            return m_poParent->Write(arrayStartIdx, count, arrayStep,
6467
0
                                     bufferStride, dtDouble, &adfVal[0]);
6468
0
        }
6469
0
    }
6470
6471
0
    std::vector<GPtrDiff_t> tmpBufferStrideVector;
6472
0
    size_t nElts = 1;
6473
0
    tmpBufferStrideVector.resize(nDims);
6474
0
    for (size_t i = 0; i < nDims; i++)
6475
0
        nElts *= count[i];
6476
0
    tmpBufferStrideVector.back() = 1;
6477
0
    for (size_t i = nDims - 1; i > 0;)
6478
0
    {
6479
0
        --i;
6480
0
        tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6481
0
    }
6482
0
    const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6483
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6484
0
    if (!pTempBuffer)
6485
0
        return false;
6486
6487
0
    struct Stack
6488
0
    {
6489
0
        size_t nIters = 0;
6490
0
        double *dst_ptr = nullptr;
6491
0
        const GByte *src_ptr = nullptr;
6492
0
        GPtrDiff_t src_inc_offset = 0;
6493
0
        GPtrDiff_t dst_inc_offset = 0;
6494
0
    };
6495
6496
0
    std::vector<Stack> stack(nDims);
6497
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
6498
0
    for (size_t i = 0; i < nDims; i++)
6499
0
    {
6500
0
        stack[i].dst_inc_offset =
6501
0
            tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6502
0
        stack[i].src_inc_offset =
6503
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6504
0
    }
6505
0
    stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6506
0
    stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6507
6508
0
    size_t dimIdx = 0;
6509
0
    const size_t nDimsMinus1 = nDims - 1;
6510
6511
0
lbl_next_depth:
6512
0
    if (dimIdx == nDimsMinus1)
6513
0
    {
6514
0
        auto nIters = count[dimIdx];
6515
0
        double *dst_ptr = stack[dimIdx].dst_ptr;
6516
0
        const GByte *src_ptr = stack[dimIdx].src_ptr;
6517
0
        while (true)
6518
0
        {
6519
0
            double adfVal[2];
6520
0
            const double *padfSrcVal;
6521
0
            if (bIsBufferDataTypeNativeDataType)
6522
0
            {
6523
0
                padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6524
0
            }
6525
0
            else
6526
0
            {
6527
0
                GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6528
0
                                                &adfVal[0], dtDouble);
6529
0
                padfSrcVal = adfVal;
6530
0
            }
6531
6532
0
            if (bSelfAndParentHaveNoData &&
6533
0
                (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6534
0
            {
6535
0
                dst_ptr[0] = adfSrcNoData[0];
6536
0
                if (bDTIsComplex)
6537
0
                {
6538
0
                    dst_ptr[1] = adfSrcNoData[1];
6539
0
                }
6540
0
            }
6541
0
            else
6542
0
            {
6543
0
                dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6544
0
                if (bDTIsComplex)
6545
0
                {
6546
0
                    dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6547
0
                }
6548
0
            }
6549
6550
0
            if ((--nIters) == 0)
6551
0
                break;
6552
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
6553
0
            src_ptr += stack[dimIdx].src_inc_offset;
6554
0
        }
6555
0
    }
6556
0
    else
6557
0
    {
6558
0
        stack[dimIdx].nIters = count[dimIdx];
6559
0
        while (true)
6560
0
        {
6561
0
            dimIdx++;
6562
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6563
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6564
0
            goto lbl_next_depth;
6565
0
        lbl_return_to_caller:
6566
0
            dimIdx--;
6567
0
            if ((--stack[dimIdx].nIters) == 0)
6568
0
                break;
6569
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6570
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6571
0
        }
6572
0
    }
6573
0
    if (dimIdx > 0)
6574
0
        goto lbl_return_to_caller;
6575
6576
    // If the parent array is not double/complex-double, then convert the
6577
    // values to it, before calling Write(), as some implementations can be
6578
    // very slow when doing the type conversion.
6579
0
    const auto &eParentDT = m_poParent->GetDataType();
6580
0
    const size_t nParentDTSize = eParentDT.GetSize();
6581
0
    if (nParentDTSize <= nDTSize / 2)
6582
0
    {
6583
        // Copy in-place by making sure that source and target do not overlap
6584
0
        const auto eNumericDT = dtDouble.GetNumericDataType();
6585
0
        const auto eParentNumericDT = eParentDT.GetNumericDataType();
6586
6587
        // Copy first element
6588
0
        {
6589
0
            std::vector<GByte> abyTemp(nParentDTSize);
6590
0
            GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6591
0
                            static_cast<int>(nDTSize), &abyTemp[0],
6592
0
                            eParentNumericDT, static_cast<int>(nParentDTSize),
6593
0
                            1);
6594
0
            memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6595
0
        }
6596
        // Remaining elements
6597
0
        for (size_t i = 1; i < nElts; ++i)
6598
0
        {
6599
0
            GDALCopyWords64(
6600
0
                static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6601
0
                static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6602
0
                eParentNumericDT, 0, 1);
6603
0
        }
6604
0
    }
6605
6606
0
    const bool ret =
6607
0
        m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6608
0
                          eParentDT, pTempBuffer);
6609
6610
0
    VSIFree(pTempBuffer);
6611
0
    return ret;
6612
0
}
6613
6614
/************************************************************************/
6615
/*                           GetUnscaled()                              */
6616
/************************************************************************/
6617
6618
/** Return an array that is the unscaled version of the current one.
6619
 *
6620
 * That is each value of the unscaled array will be
6621
 * unscaled_value = raw_value * GetScale() + GetOffset()
6622
 *
6623
 * Starting with GDAL 3.3, the Write() method is implemented and will convert
6624
 * from unscaled values to raw values.
6625
 *
6626
 * This is the same as the C function GDALMDArrayGetUnscaled().
6627
 *
6628
 * @param dfOverriddenScale Custom scale value instead of GetScale()
6629
 * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6630
 * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6631
 * @return a new array, that holds a reference to the original one, and thus is
6632
 * a view of it (not a copy), or nullptr in case of error.
6633
 */
6634
std::shared_ptr<GDALMDArray>
6635
GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6636
                         double dfOverriddenDstNodata) const
6637
0
{
6638
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6639
0
    if (!self)
6640
0
    {
6641
0
        CPLError(CE_Failure, CPLE_AppDefined,
6642
0
                 "Driver implementation issue: m_pSelf not set !");
6643
0
        return nullptr;
6644
0
    }
6645
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
6646
0
    {
6647
0
        CPLError(CE_Failure, CPLE_AppDefined,
6648
0
                 "GetUnscaled() only supports numeric data type");
6649
0
        return nullptr;
6650
0
    }
6651
0
    const double dfScale =
6652
0
        std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6653
0
    const double dfOffset =
6654
0
        std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6655
0
    if (dfScale == 1.0 && dfOffset == 0.0)
6656
0
        return self;
6657
6658
0
    GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6659
0
                           ? GDT_CFloat64
6660
0
                           : GDT_Float64;
6661
0
    if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6662
0
    {
6663
0
        if (GetDataType().GetNumericDataType() == GDT_Float16)
6664
0
            eDT = GDT_Float16;
6665
0
        if (GetDataType().GetNumericDataType() == GDT_Float32)
6666
0
            eDT = GDT_Float32;
6667
0
    }
6668
6669
0
    return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6670
0
                                       dfOverriddenDstNodata, eDT);
6671
0
}
6672
6673
/************************************************************************/
6674
/*                         GDALMDArrayMask                              */
6675
/************************************************************************/
6676
6677
class GDALMDArrayMask final : public GDALPamMDArray
6678
{
6679
  private:
6680
    std::shared_ptr<GDALMDArray> m_poParent{};
6681
    GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
6682
    double m_dfMissingValue = 0.0;
6683
    bool m_bHasMissingValue = false;
6684
    double m_dfFillValue = 0.0;
6685
    bool m_bHasFillValue = false;
6686
    double m_dfValidMin = 0.0;
6687
    bool m_bHasValidMin = false;
6688
    double m_dfValidMax = 0.0;
6689
    bool m_bHasValidMax = false;
6690
    std::vector<uint32_t> m_anValidFlagMasks{};
6691
    std::vector<uint32_t> m_anValidFlagValues{};
6692
6693
    bool Init(CSLConstList papszOptions);
6694
6695
    template <typename Type>
6696
    void
6697
    ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6698
                 const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6699
                 const void *pTempBuffer,
6700
                 const GDALExtendedDataType &oTmpBufferDT,
6701
                 const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6702
6703
  protected:
6704
    explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6705
0
        : GDALAbstractMDArray(std::string(),
6706
0
                              "Mask of " + poParent->GetFullName()),
6707
0
          GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6708
0
                         GDALPamMultiDim::GetPAM(poParent),
6709
0
                         poParent->GetContext()),
6710
0
          m_poParent(std::move(poParent))
6711
0
    {
6712
0
    }
6713
6714
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6715
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6716
               const GDALExtendedDataType &bufferDataType,
6717
               void *pDstBuffer) const override;
6718
6719
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6720
                     CSLConstList papszOptions) const override
6721
0
    {
6722
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6723
0
    }
6724
6725
  public:
6726
    static std::shared_ptr<GDALMDArrayMask>
6727
    Create(const std::shared_ptr<GDALMDArray> &poParent,
6728
           CSLConstList papszOptions);
6729
6730
    bool IsWritable() const override
6731
0
    {
6732
0
        return false;
6733
0
    }
6734
6735
    const std::string &GetFilename() const override
6736
0
    {
6737
0
        return m_poParent->GetFilename();
6738
0
    }
6739
6740
    const std::vector<std::shared_ptr<GDALDimension>> &
6741
    GetDimensions() const override
6742
0
    {
6743
0
        return m_poParent->GetDimensions();
6744
0
    }
6745
6746
    const GDALExtendedDataType &GetDataType() const override
6747
0
    {
6748
0
        return m_dt;
6749
0
    }
6750
6751
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6752
0
    {
6753
0
        return m_poParent->GetSpatialRef();
6754
0
    }
6755
6756
    std::vector<GUInt64> GetBlockSize() const override
6757
0
    {
6758
0
        return m_poParent->GetBlockSize();
6759
0
    }
6760
};
6761
6762
/************************************************************************/
6763
/*                    GDALMDArrayMask::Create()                         */
6764
/************************************************************************/
6765
6766
/* static */ std::shared_ptr<GDALMDArrayMask>
6767
GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6768
                        CSLConstList papszOptions)
6769
0
{
6770
0
    auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6771
0
    newAr->SetSelf(newAr);
6772
0
    if (!newAr->Init(papszOptions))
6773
0
        return nullptr;
6774
0
    return newAr;
6775
0
}
6776
6777
/************************************************************************/
6778
/*                    GDALMDArrayMask::Init()                           */
6779
/************************************************************************/
6780
6781
bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6782
0
{
6783
0
    const auto GetSingleValNumericAttr =
6784
0
        [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6785
0
    {
6786
0
        auto poAttr = m_poParent->GetAttribute(pszAttrName);
6787
0
        if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6788
0
        {
6789
0
            const auto anDimSizes = poAttr->GetDimensionsSize();
6790
0
            if (anDimSizes.empty() ||
6791
0
                (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6792
0
            {
6793
0
                bHasVal = true;
6794
0
                dfVal = poAttr->ReadAsDouble();
6795
0
            }
6796
0
        }
6797
0
    };
6798
6799
0
    GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6800
0
                            m_dfMissingValue);
6801
0
    GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6802
0
    GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6803
0
    GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6804
6805
0
    {
6806
0
        auto poValidRange = m_poParent->GetAttribute("valid_range");
6807
0
        if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6808
0
            poValidRange->GetDimensionsSize()[0] == 2 &&
6809
0
            poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6810
0
        {
6811
0
            m_bHasValidMin = true;
6812
0
            m_bHasValidMax = true;
6813
0
            auto vals = poValidRange->ReadAsDoubleArray();
6814
0
            CPLAssert(vals.size() == 2);
6815
0
            m_dfValidMin = vals[0];
6816
0
            m_dfValidMax = vals[1];
6817
0
        }
6818
0
    }
6819
6820
    // Take into account
6821
    // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6822
    // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6823
0
    const char *pszUnmaskFlags =
6824
0
        CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6825
0
    if (pszUnmaskFlags)
6826
0
    {
6827
0
        const auto IsScalarStringAttr =
6828
0
            [](const std::shared_ptr<GDALAttribute> &poAttr)
6829
0
        {
6830
0
            return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6831
0
                   (poAttr->GetDimensionsSize().empty() ||
6832
0
                    (poAttr->GetDimensionsSize().size() == 1 &&
6833
0
                     poAttr->GetDimensionsSize()[0] == 1));
6834
0
        };
6835
6836
0
        auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6837
0
        if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6838
0
        {
6839
0
            CPLError(CE_Failure, CPLE_AppDefined,
6840
0
                     "UNMASK_FLAGS option specified but array has no "
6841
0
                     "flag_meanings attribute");
6842
0
            return false;
6843
0
        }
6844
0
        const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6845
0
        if (!pszFlagMeanings)
6846
0
        {
6847
0
            CPLError(CE_Failure, CPLE_AppDefined,
6848
0
                     "Cannot read flag_meanings attribute");
6849
0
            return false;
6850
0
        }
6851
6852
0
        const auto IsSingleDimNumericAttr =
6853
0
            [](const std::shared_ptr<GDALAttribute> &poAttr)
6854
0
        {
6855
0
            return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6856
0
                   poAttr->GetDimensionsSize().size() == 1;
6857
0
        };
6858
6859
0
        auto poFlagValues = m_poParent->GetAttribute("flag_values");
6860
0
        const bool bHasFlagValues =
6861
0
            poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6862
6863
0
        auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6864
0
        const bool bHasFlagMasks =
6865
0
            poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6866
6867
0
        if (!bHasFlagValues && !bHasFlagMasks)
6868
0
        {
6869
0
            CPLError(CE_Failure, CPLE_AppDefined,
6870
0
                     "Cannot find flag_values and/or flag_masks attribute");
6871
0
            return false;
6872
0
        }
6873
6874
0
        const CPLStringList aosUnmaskFlags(
6875
0
            CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6876
0
        const CPLStringList aosFlagMeanings(
6877
0
            CSLTokenizeString2(pszFlagMeanings, " ", 0));
6878
6879
0
        if (bHasFlagValues)
6880
0
        {
6881
0
            const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6882
            // We could support Int64 or UInt64, but more work...
6883
0
            if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6884
0
                eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6885
0
            {
6886
0
                CPLError(CE_Failure, CPLE_NotSupported,
6887
0
                         "Unsupported data type for flag_values attribute: %s",
6888
0
                         GDALGetDataTypeName(eType));
6889
0
                return false;
6890
0
            }
6891
0
        }
6892
6893
0
        if (bHasFlagMasks)
6894
0
        {
6895
0
            const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6896
            // We could support Int64 or UInt64, but more work...
6897
0
            if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
6898
0
                eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
6899
0
            {
6900
0
                CPLError(CE_Failure, CPLE_NotSupported,
6901
0
                         "Unsupported data type for flag_masks attribute: %s",
6902
0
                         GDALGetDataTypeName(eType));
6903
0
                return false;
6904
0
            }
6905
0
        }
6906
6907
0
        const std::vector<double> adfValues(
6908
0
            bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6909
0
                           : std::vector<double>());
6910
0
        const std::vector<double> adfMasks(
6911
0
            bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6912
0
                          : std::vector<double>());
6913
6914
0
        if (bHasFlagValues &&
6915
0
            adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6916
0
        {
6917
0
            CPLError(CE_Failure, CPLE_AppDefined,
6918
0
                     "Number of values in flag_values attribute is different "
6919
0
                     "from the one in flag_meanings");
6920
0
            return false;
6921
0
        }
6922
6923
0
        if (bHasFlagMasks &&
6924
0
            adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6925
0
        {
6926
0
            CPLError(CE_Failure, CPLE_AppDefined,
6927
0
                     "Number of values in flag_masks attribute is different "
6928
0
                     "from the one in flag_meanings");
6929
0
            return false;
6930
0
        }
6931
6932
0
        for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6933
0
        {
6934
0
            const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
6935
0
            if (nIdxFlag < 0)
6936
0
            {
6937
0
                CPLError(
6938
0
                    CE_Failure, CPLE_AppDefined,
6939
0
                    "Cannot fing flag %s in flag_meanings = '%s' attribute",
6940
0
                    aosUnmaskFlags[i], pszFlagMeanings);
6941
0
                return false;
6942
0
            }
6943
6944
0
            if (bHasFlagValues && adfValues[nIdxFlag] < 0)
6945
0
            {
6946
0
                CPLError(CE_Failure, CPLE_AppDefined,
6947
0
                         "Invalid value in flag_values[%d] = %f", nIdxFlag,
6948
0
                         adfValues[nIdxFlag]);
6949
0
                return false;
6950
0
            }
6951
6952
0
            if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
6953
0
            {
6954
0
                CPLError(CE_Failure, CPLE_AppDefined,
6955
0
                         "Invalid value in flag_masks[%d] = %f", nIdxFlag,
6956
0
                         adfMasks[nIdxFlag]);
6957
0
                return false;
6958
0
            }
6959
6960
0
            if (bHasFlagValues)
6961
0
            {
6962
0
                m_anValidFlagValues.push_back(
6963
0
                    static_cast<uint32_t>(adfValues[nIdxFlag]));
6964
0
            }
6965
6966
0
            if (bHasFlagMasks)
6967
0
            {
6968
0
                m_anValidFlagMasks.push_back(
6969
0
                    static_cast<uint32_t>(adfMasks[nIdxFlag]));
6970
0
            }
6971
0
        }
6972
0
    }
6973
6974
0
    return true;
6975
0
}
6976
6977
/************************************************************************/
6978
/*                             IRead()                                  */
6979
/************************************************************************/
6980
6981
bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6982
                            const GInt64 *arrayStep,
6983
                            const GPtrDiff_t *bufferStride,
6984
                            const GDALExtendedDataType &bufferDataType,
6985
                            void *pDstBuffer) const
6986
0
{
6987
0
    if (bufferDataType.GetClass() != GEDTC_NUMERIC)
6988
0
    {
6989
0
        CPLError(CE_Failure, CPLE_AppDefined,
6990
0
                 "%s: only reading to a numeric data type is supported",
6991
0
                 __func__);
6992
0
        return false;
6993
0
    }
6994
0
    size_t nElts = 1;
6995
0
    const size_t nDims = GetDimensionCount();
6996
0
    std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
6997
0
    for (size_t i = 0; i < nDims; i++)
6998
0
        nElts *= count[i];
6999
0
    if (nDims > 0)
7000
0
    {
7001
0
        tmpBufferStrideVector.back() = 1;
7002
0
        for (size_t i = nDims - 1; i > 0;)
7003
0
        {
7004
0
            --i;
7005
0
            tmpBufferStrideVector[i] =
7006
0
                tmpBufferStrideVector[i + 1] * count[i + 1];
7007
0
        }
7008
0
    }
7009
7010
    /* Optimized case: if we are an integer data type and that there is no */
7011
    /* attribute that can be used to set mask = 0, then fill the mask buffer */
7012
    /* directly */
7013
0
    if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7014
0
        !m_bHasValidMax && m_anValidFlagValues.empty() &&
7015
0
        m_anValidFlagMasks.empty() &&
7016
0
        m_poParent->GetRawNoDataValue() == nullptr &&
7017
0
        GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7018
0
    {
7019
0
        const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7020
0
        if (bBufferDataTypeIsByte)  // Byte case
7021
0
        {
7022
0
            bool bContiguous = true;
7023
0
            for (size_t i = 0; i < nDims; i++)
7024
0
            {
7025
0
                if (bufferStride[i] != tmpBufferStrideVector[i])
7026
0
                {
7027
0
                    bContiguous = false;
7028
0
                    break;
7029
0
                }
7030
0
            }
7031
0
            if (bContiguous)
7032
0
            {
7033
                // CPLDebug("GDAL", "GetMask(): contiguous case");
7034
0
                memset(pDstBuffer, 1, nElts);
7035
0
                return true;
7036
0
            }
7037
0
        }
7038
7039
0
        struct Stack
7040
0
        {
7041
0
            size_t nIters = 0;
7042
0
            GByte *dst_ptr = nullptr;
7043
0
            GPtrDiff_t dst_inc_offset = 0;
7044
0
        };
7045
7046
0
        std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7047
0
        const size_t nBufferDTSize = bufferDataType.GetSize();
7048
0
        for (size_t i = 0; i < nDims; i++)
7049
0
        {
7050
0
            stack[i].dst_inc_offset =
7051
0
                static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7052
0
        }
7053
0
        stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7054
7055
0
        size_t dimIdx = 0;
7056
0
        const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7057
0
        GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
7058
0
        CPLAssert(nBufferDTSize <= 16);
7059
0
        const GByte flag = 1;
7060
0
        GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
7061
0
                        bufferDataType.GetNumericDataType(), 0, 1);
7062
7063
0
    lbl_next_depth:
7064
0
        if (dimIdx == nDimsMinus1)
7065
0
        {
7066
0
            auto nIters = nDims > 0 ? count[dimIdx] : 1;
7067
0
            GByte *dst_ptr = stack[dimIdx].dst_ptr;
7068
7069
0
            while (true)
7070
0
            {
7071
                // cppcheck-suppress knownConditionTrueFalse
7072
0
                if (bBufferDataTypeIsByte)
7073
0
                {
7074
0
                    *dst_ptr = flag;
7075
0
                }
7076
0
                else
7077
0
                {
7078
0
                    memcpy(dst_ptr, abyOne, nBufferDTSize);
7079
0
                }
7080
7081
0
                if ((--nIters) == 0)
7082
0
                    break;
7083
0
                dst_ptr += stack[dimIdx].dst_inc_offset;
7084
0
            }
7085
0
        }
7086
0
        else
7087
0
        {
7088
0
            stack[dimIdx].nIters = count[dimIdx];
7089
0
            while (true)
7090
0
            {
7091
0
                dimIdx++;
7092
0
                stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7093
0
                goto lbl_next_depth;
7094
0
            lbl_return_to_caller:
7095
0
                dimIdx--;
7096
0
                if ((--stack[dimIdx].nIters) == 0)
7097
0
                    break;
7098
0
                stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7099
0
            }
7100
0
        }
7101
0
        if (dimIdx > 0)
7102
0
            goto lbl_return_to_caller;
7103
7104
0
        return true;
7105
0
    }
7106
7107
0
    const auto oTmpBufferDT =
7108
0
        GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7109
0
            ? GDALExtendedDataType::Create(GDT_Float64)
7110
0
            : m_poParent->GetDataType();
7111
0
    const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7112
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7113
0
    if (!pTempBuffer)
7114
0
        return false;
7115
0
    if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7116
0
                          tmpBufferStrideVector.data(), oTmpBufferDT,
7117
0
                          pTempBuffer))
7118
0
    {
7119
0
        VSIFree(pTempBuffer);
7120
0
        return false;
7121
0
    }
7122
7123
0
    switch (oTmpBufferDT.GetNumericDataType())
7124
0
    {
7125
0
        case GDT_Byte:
7126
0
            ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7127
0
                                pTempBuffer, oTmpBufferDT,
7128
0
                                tmpBufferStrideVector);
7129
0
            break;
7130
7131
0
        case GDT_Int8:
7132
0
            ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7133
0
                                pTempBuffer, oTmpBufferDT,
7134
0
                                tmpBufferStrideVector);
7135
0
            break;
7136
7137
0
        case GDT_UInt16:
7138
0
            ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7139
0
                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
7140
0
                                  tmpBufferStrideVector);
7141
0
            break;
7142
7143
0
        case GDT_Int16:
7144
0
            ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7145
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7146
0
                                 tmpBufferStrideVector);
7147
0
            break;
7148
7149
0
        case GDT_UInt32:
7150
0
            ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7151
0
                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
7152
0
                                  tmpBufferStrideVector);
7153
0
            break;
7154
7155
0
        case GDT_Int32:
7156
0
            ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7157
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7158
0
                                 tmpBufferStrideVector);
7159
0
            break;
7160
7161
0
        case GDT_UInt64:
7162
0
            ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7163
0
                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
7164
0
                                        tmpBufferStrideVector);
7165
0
            break;
7166
7167
0
        case GDT_Int64:
7168
0
            ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7169
0
                                       pDstBuffer, pTempBuffer, oTmpBufferDT,
7170
0
                                       tmpBufferStrideVector);
7171
0
            break;
7172
7173
0
        case GDT_Float16:
7174
0
            ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7175
0
                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
7176
0
                                   tmpBufferStrideVector);
7177
0
            break;
7178
7179
0
        case GDT_Float32:
7180
0
            ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7181
0
                                pTempBuffer, oTmpBufferDT,
7182
0
                                tmpBufferStrideVector);
7183
0
            break;
7184
7185
0
        case GDT_Float64:
7186
0
            ReadInternal<double>(count, bufferStride, bufferDataType,
7187
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7188
0
                                 tmpBufferStrideVector);
7189
0
            break;
7190
0
        case GDT_Unknown:
7191
0
        case GDT_CInt16:
7192
0
        case GDT_CInt32:
7193
0
        case GDT_CFloat16:
7194
0
        case GDT_CFloat32:
7195
0
        case GDT_CFloat64:
7196
0
        case GDT_TypeCount:
7197
0
            CPLAssert(false);
7198
0
            break;
7199
0
    }
7200
7201
0
    VSIFree(pTempBuffer);
7202
7203
0
    return true;
7204
0
}
7205
7206
/************************************************************************/
7207
/*                          IsValidForDT()                              */
7208
/************************************************************************/
7209
7210
template <typename Type> static bool IsValidForDT(double dfVal)
7211
0
{
7212
0
    if (std::isnan(dfVal))
7213
0
        return false;
7214
0
    if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7215
0
        return false;
7216
0
    if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7217
0
        return false;
7218
0
    return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7219
0
}
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<unsigned char>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<signed char>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<unsigned short>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<short>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<unsigned int>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<int>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<unsigned long>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<long>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<cpl::Float16>(double)
Unexecuted instantiation: gdalmultidim.cpp:bool IsValidForDT<float>(double)
7220
7221
template <> bool IsValidForDT<double>(double)
7222
0
{
7223
0
    return true;
7224
0
}
7225
7226
/************************************************************************/
7227
/*                              IsNan()                                 */
7228
/************************************************************************/
7229
7230
template <typename Type> inline bool IsNan(Type)
7231
0
{
7232
0
    return false;
7233
0
}
Unexecuted instantiation: bool IsNan<unsigned char>(unsigned char)
Unexecuted instantiation: bool IsNan<signed char>(signed char)
Unexecuted instantiation: bool IsNan<unsigned short>(unsigned short)
Unexecuted instantiation: bool IsNan<short>(short)
Unexecuted instantiation: bool IsNan<unsigned int>(unsigned int)
Unexecuted instantiation: bool IsNan<int>(int)
Unexecuted instantiation: bool IsNan<unsigned long>(unsigned long)
Unexecuted instantiation: bool IsNan<long>(long)
Unexecuted instantiation: bool IsNan<cpl::Float16>(cpl::Float16)
7234
7235
template <> bool IsNan<double>(double val)
7236
0
{
7237
0
    return std::isnan(val);
7238
0
}
7239
7240
template <> bool IsNan<float>(float val)
7241
0
{
7242
0
    return std::isnan(val);
7243
0
}
7244
7245
/************************************************************************/
7246
/*                         ReadInternal()                               */
7247
/************************************************************************/
7248
7249
template <typename Type>
7250
void GDALMDArrayMask::ReadInternal(
7251
    const size_t *count, const GPtrDiff_t *bufferStride,
7252
    const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7253
    const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7254
    const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7255
0
{
7256
0
    const size_t nDims = GetDimensionCount();
7257
7258
0
    const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7259
0
    {
7260
0
        if (bHasVal)
7261
0
        {
7262
0
            if (IsValidForDT<Type>(dfVal))
7263
0
            {
7264
0
                return static_cast<Type>(dfVal);
7265
0
            }
7266
0
            else
7267
0
            {
7268
0
                bHasVal = false;
7269
0
            }
7270
0
        }
7271
0
        return 0;
7272
0
    };
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<signed char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<cpl::Float16>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<float>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<double>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(bool&, double)#1}::operator()(bool&, double) const
7273
7274
0
    const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7275
0
    bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7276
0
    const Type nNoDataValue =
7277
0
        castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7278
0
    bool bHasMissingValue = m_bHasMissingValue;
7279
0
    const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7280
0
    bool bHasFillValue = m_bHasFillValue;
7281
0
    const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7282
0
    bool bHasValidMin = m_bHasValidMin;
7283
0
    const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7284
0
    bool bHasValidMax = m_bHasValidMax;
7285
0
    const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7286
0
    const bool bHasValidFlags =
7287
0
        !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7288
7289
0
    const auto IsValidFlag = [this](Type v)
7290
0
    {
7291
0
        if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7292
0
        {
7293
0
            for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7294
0
            {
7295
0
                if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7296
0
                    m_anValidFlagValues[i])
7297
0
                {
7298
0
                    return true;
7299
0
                }
7300
0
            }
7301
0
        }
7302
0
        else if (!m_anValidFlagValues.empty())
7303
0
        {
7304
0
            for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7305
0
            {
7306
0
                if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7307
0
                {
7308
0
                    return true;
7309
0
                }
7310
0
            }
7311
0
        }
7312
0
        else /* if( !m_anValidFlagMasks.empty() ) */
7313
0
        {
7314
0
            for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7315
0
            {
7316
0
                if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7317
0
                {
7318
0
                    return true;
7319
0
                }
7320
0
            }
7321
0
        }
7322
0
        return false;
7323
0
    };
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(unsigned char)#1}::operator()(unsigned char) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<signed char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(signed char)#1}::operator()(signed char) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(unsigned short)#1}::operator()(unsigned short) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(short)#1}::operator()(short) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(unsigned int)#1}::operator()(unsigned int) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(int)#1}::operator()(int) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<unsigned long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(unsigned long)#1}::operator()(unsigned long) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(long)#1}::operator()(long) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<cpl::Float16>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(cpl::Float16)#1}::operator()(cpl::Float16) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<float>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: GDALMDArrayMask::ReadInternal<double>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const::{lambda(double)#1}::operator()(double) const
7324
7325
0
#define GET_MASK_FOR_SAMPLE(v)                                                 \
7326
0
    static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7327
0
                       !(bHasMissingValue && v == nMissingValue) &&            \
7328
0
                       !(bHasFillValue && v == nFillValue) &&                  \
7329
0
                       !(bHasValidMin && v < nValidMin) &&                     \
7330
0
                       !(bHasValidMax && v > nValidMax) &&                     \
7331
0
                       (!bHasValidFlags || IsValidFlag(v)));
7332
7333
0
    const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7334
    /* Optimized case: Byte output and output buffer is contiguous */
7335
0
    if (bBufferDataTypeIsByte)
7336
0
    {
7337
0
        bool bContiguous = true;
7338
0
        for (size_t i = 0; i < nDims; i++)
7339
0
        {
7340
0
            if (bufferStride[i] != tmpBufferStrideVector[i])
7341
0
            {
7342
0
                bContiguous = false;
7343
0
                break;
7344
0
            }
7345
0
        }
7346
0
        if (bContiguous)
7347
0
        {
7348
0
            size_t nElts = 1;
7349
0
            for (size_t i = 0; i < nDims; i++)
7350
0
                nElts *= count[i];
7351
7352
0
            for (size_t i = 0; i < nElts; i++)
7353
0
            {
7354
0
                const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7355
0
                static_cast<GByte *>(pDstBuffer)[i] =
7356
0
                    GET_MASK_FOR_SAMPLE(*pSrc);
7357
0
            }
7358
0
            return;
7359
0
        }
7360
0
    }
7361
7362
0
    const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7363
7364
0
    struct Stack
7365
0
    {
7366
0
        size_t nIters = 0;
7367
0
        const GByte *src_ptr = nullptr;
7368
0
        GByte *dst_ptr = nullptr;
7369
0
        GPtrDiff_t src_inc_offset = 0;
7370
0
        GPtrDiff_t dst_inc_offset = 0;
7371
0
    };
7372
7373
0
    std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7374
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
7375
0
    for (size_t i = 0; i < nDims; i++)
7376
0
    {
7377
0
        stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7378
0
            tmpBufferStrideVector[i] * nTmpBufferDTSize);
7379
0
        stack[i].dst_inc_offset =
7380
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7381
0
    }
7382
0
    stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7383
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7384
7385
0
    size_t dimIdx = 0;
7386
0
    const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7387
0
    GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
7388
0
    CPLAssert(nBufferDTSize <= 16);
7389
0
    for (GByte flag = 0; flag <= 1; flag++)
7390
0
    {
7391
0
        GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7392
0
                        bufferDataType.GetNumericDataType(), 0, 1);
7393
0
    }
7394
7395
0
lbl_next_depth:
7396
0
    if (dimIdx == nDimsMinus1)
7397
0
    {
7398
0
        auto nIters = nDims > 0 ? count[dimIdx] : 1;
7399
0
        const GByte *src_ptr = stack[dimIdx].src_ptr;
7400
0
        GByte *dst_ptr = stack[dimIdx].dst_ptr;
7401
7402
0
        while (true)
7403
0
        {
7404
0
            const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7405
0
            const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7406
7407
0
            if (bBufferDataTypeIsByte)
7408
0
            {
7409
0
                *dst_ptr = flag;
7410
0
            }
7411
0
            else
7412
0
            {
7413
0
                memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7414
0
            }
7415
7416
0
            if ((--nIters) == 0)
7417
0
                break;
7418
0
            src_ptr += stack[dimIdx].src_inc_offset;
7419
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
7420
0
        }
7421
0
    }
7422
0
    else
7423
0
    {
7424
0
        stack[dimIdx].nIters = count[dimIdx];
7425
0
        while (true)
7426
0
        {
7427
0
            dimIdx++;
7428
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7429
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7430
0
            goto lbl_next_depth;
7431
0
        lbl_return_to_caller:
7432
0
            dimIdx--;
7433
0
            if ((--stack[dimIdx].nIters) == 0)
7434
0
                break;
7435
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7436
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7437
0
        }
7438
0
    }
7439
0
    if (dimIdx > 0)
7440
0
        goto lbl_return_to_caller;
7441
0
}
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<unsigned char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<signed char>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<unsigned short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<short>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<unsigned int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<int>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<unsigned long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<long>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<cpl::Float16>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<float>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
Unexecuted instantiation: void GDALMDArrayMask::ReadInternal<double>(unsigned long const*, long long const*, GDALExtendedDataType const&, void*, void const*, GDALExtendedDataType const&, std::__1::vector<long long, std::__1::allocator<long long> > const&) const
7442
7443
/************************************************************************/
7444
/*                            GetMask()                                 */
7445
/************************************************************************/
7446
7447
/** Return an array that is a mask for the current array
7448
7449
 This array will be of type Byte, with values set to 0 to indicate invalid
7450
 pixels of the current array, and values set to 1 to indicate valid pixels.
7451
7452
 The generic implementation honours the NoDataValue, as well as various
7453
 netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7454
 and valid_range.
7455
7456
 Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7457
 can be used to specify strings of the "flag_meanings" attribute
7458
 (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7459
 for which pixels matching any of those flags will be set at 1 in the mask array,
7460
 and pixels matching none of those flags will be set at 0.
7461
 For example, let's consider the following netCDF variable defined with:
7462
 \verbatim
7463
 l2p_flags:valid_min = 0s ;
7464
 l2p_flags:valid_max = 256s ;
7465
 l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7466
 l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7467
 \endverbatim
7468
7469
 GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7470
 - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7471
 - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7472
   will be 1.
7473
 - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7474
   will be 0.
7475
7476
 This is the same as the C function GDALMDArrayGetMask().
7477
7478
 @param papszOptions NULL-terminated list of options, or NULL.
7479
7480
 @return a new array, that holds a reference to the original one, and thus is
7481
 a view of it (not a copy), or nullptr in case of error.
7482
*/
7483
std::shared_ptr<GDALMDArray>
7484
GDALMDArray::GetMask(CSLConstList papszOptions) const
7485
0
{
7486
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7487
0
    if (!self)
7488
0
    {
7489
0
        CPLError(CE_Failure, CPLE_AppDefined,
7490
0
                 "Driver implementation issue: m_pSelf not set !");
7491
0
        return nullptr;
7492
0
    }
7493
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
7494
0
    {
7495
0
        CPLError(CE_Failure, CPLE_AppDefined,
7496
0
                 "GetMask() only supports numeric data type");
7497
0
        return nullptr;
7498
0
    }
7499
0
    return GDALMDArrayMask::Create(self, papszOptions);
7500
0
}
7501
7502
/************************************************************************/
7503
/*                         IsRegularlySpaced()                          */
7504
/************************************************************************/
7505
7506
/** Returns whether an array is a 1D regularly spaced array.
7507
 *
7508
 * @param[out] dfStart     First value in the array
7509
 * @param[out] dfIncrement Increment/spacing between consecutive values.
7510
 * @return true if the array is regularly spaced.
7511
 */
7512
bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7513
0
{
7514
0
    dfStart = 0;
7515
0
    dfIncrement = 0;
7516
0
    if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7517
0
        return false;
7518
0
    const auto nSize = GetDimensions()[0]->GetSize();
7519
0
    if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7520
0
        return false;
7521
7522
0
    size_t nCount = static_cast<size_t>(nSize);
7523
0
    std::vector<double> adfTmp;
7524
0
    try
7525
0
    {
7526
0
        adfTmp.resize(nCount);
7527
0
    }
7528
0
    catch (const std::exception &)
7529
0
    {
7530
0
        return false;
7531
0
    }
7532
7533
0
    GUInt64 anStart[1] = {0};
7534
0
    size_t anCount[1] = {nCount};
7535
7536
0
    const auto IsRegularlySpacedInternal =
7537
0
        [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7538
0
    {
7539
0
        dfStart = adfTmp[0];
7540
0
        dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7541
0
        if (dfIncrement == 0)
7542
0
        {
7543
0
            return false;
7544
0
        }
7545
0
        for (size_t i = 1; i < anCount[0]; i++)
7546
0
        {
7547
0
            if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7548
0
                1e-3 * fabs(dfIncrement))
7549
0
            {
7550
0
                return false;
7551
0
            }
7552
0
        }
7553
0
        return true;
7554
0
    };
7555
7556
    // First try with the first block(s). This can avoid excessive processing
7557
    // time, for example with Zarr datasets.
7558
    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7559
    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7560
0
    const auto nBlockSize = GetBlockSize()[0];
7561
0
    if (nCount >= 5 && nBlockSize <= nCount / 2)
7562
0
    {
7563
0
        size_t nReducedCount =
7564
0
            std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7565
0
        while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7566
0
            nReducedCount *= 2;
7567
0
        anCount[0] = nReducedCount;
7568
0
        if (!Read(anStart, anCount, nullptr, nullptr,
7569
0
                  GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7570
0
        {
7571
0
            return false;
7572
0
        }
7573
0
        if (!IsRegularlySpacedInternal())
7574
0
        {
7575
0
            return false;
7576
0
        }
7577
7578
        // Get next values
7579
0
        anStart[0] = nReducedCount;
7580
0
        anCount[0] = nCount - nReducedCount;
7581
0
    }
7582
7583
0
    if (!Read(anStart, anCount, nullptr, nullptr,
7584
0
              GDALExtendedDataType::Create(GDT_Float64),
7585
0
              &adfTmp[static_cast<size_t>(anStart[0])]))
7586
0
    {
7587
0
        return false;
7588
0
    }
7589
7590
0
    return IsRegularlySpacedInternal();
7591
0
}
7592
7593
/************************************************************************/
7594
/*                         GuessGeoTransform()                          */
7595
/************************************************************************/
7596
7597
/** Returns whether 2 specified dimensions form a geotransform
7598
 *
7599
 * @param nDimX                Index of the X axis.
7600
 * @param nDimY                Index of the Y axis.
7601
 * @param bPixelIsPoint        Whether the geotransform should be returned
7602
 *                             with the pixel-is-point (pixel-center) convention
7603
 *                             (bPixelIsPoint = true), or with the pixel-is-area
7604
 *                             (top left corner convention)
7605
 *                             (bPixelIsPoint = false)
7606
 * @param[out] adfGeoTransform Computed geotransform
7607
 * @return true if a geotransform could be computed.
7608
 */
7609
bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7610
                                    bool bPixelIsPoint,
7611
                                    double adfGeoTransform[6]) const
7612
0
{
7613
0
    const auto &dims(GetDimensions());
7614
0
    auto poVarX = dims[nDimX]->GetIndexingVariable();
7615
0
    auto poVarY = dims[nDimY]->GetIndexingVariable();
7616
0
    double dfXStart = 0.0;
7617
0
    double dfXSpacing = 0.0;
7618
0
    double dfYStart = 0.0;
7619
0
    double dfYSpacing = 0.0;
7620
0
    if (poVarX && poVarX->GetDimensionCount() == 1 &&
7621
0
        poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7622
0
        poVarY && poVarY->GetDimensionCount() == 1 &&
7623
0
        poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7624
0
        poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7625
0
        poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7626
0
    {
7627
0
        adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7628
0
        adfGeoTransform[1] = dfXSpacing;
7629
0
        adfGeoTransform[2] = 0;
7630
0
        adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7631
0
        adfGeoTransform[4] = 0;
7632
0
        adfGeoTransform[5] = dfYSpacing;
7633
0
        return true;
7634
0
    }
7635
0
    return false;
7636
0
}
7637
7638
/************************************************************************/
7639
/*                       GDALMDArrayResampled                           */
7640
/************************************************************************/
7641
7642
class GDALMDArrayResampledDataset;
7643
7644
class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7645
{
7646
  protected:
7647
    CPLErr IReadBlock(int, int, void *) override;
7648
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7649
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
7650
                     GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7651
                     GSpacing nLineSpaceBuf,
7652
                     GDALRasterIOExtraArg *psExtraArg) override;
7653
7654
  public:
7655
    explicit GDALMDArrayResampledDatasetRasterBand(
7656
        GDALMDArrayResampledDataset *poDSIn);
7657
7658
    double GetNoDataValue(int *pbHasNoData) override;
7659
};
7660
7661
class GDALMDArrayResampledDataset final : public GDALPamDataset
7662
{
7663
    friend class GDALMDArrayResampled;
7664
    friend class GDALMDArrayResampledDatasetRasterBand;
7665
7666
    std::shared_ptr<GDALMDArray> m_poArray;
7667
    const size_t m_iXDim;
7668
    const size_t m_iYDim;
7669
    double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
7670
    bool m_bHasGT = false;
7671
    mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7672
7673
    std::vector<GUInt64> m_anOffset{};
7674
    std::vector<size_t> m_anCount{};
7675
    std::vector<GPtrDiff_t> m_anStride{};
7676
7677
    std::string m_osFilenameLong{};
7678
    std::string m_osFilenameLat{};
7679
7680
  public:
7681
    GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7682
                                size_t iXDim, size_t iYDim)
7683
0
        : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7684
0
          m_anOffset(m_poArray->GetDimensionCount(), 0),
7685
0
          m_anCount(m_poArray->GetDimensionCount(), 1),
7686
0
          m_anStride(m_poArray->GetDimensionCount(), 0)
7687
0
    {
7688
0
        const auto &dims(m_poArray->GetDimensions());
7689
7690
0
        nRasterYSize = static_cast<int>(
7691
0
            std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7692
0
        nRasterXSize = static_cast<int>(
7693
0
            std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7694
7695
0
        m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
7696
0
                                                m_adfGeoTransform);
7697
7698
0
        SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7699
0
    }
7700
7701
    ~GDALMDArrayResampledDataset() override;
7702
7703
    CPLErr GetGeoTransform(double *padfGeoTransform) override
7704
0
    {
7705
0
        memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
7706
0
        return m_bHasGT ? CE_None : CE_Failure;
7707
0
    }
7708
7709
    const OGRSpatialReference *GetSpatialRef() const override
7710
0
    {
7711
0
        m_poSRS = m_poArray->GetSpatialRef();
7712
0
        if (m_poSRS)
7713
0
        {
7714
0
            m_poSRS.reset(m_poSRS->Clone());
7715
0
            auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7716
0
            for (auto &m : axisMapping)
7717
0
            {
7718
0
                if (m == static_cast<int>(m_iXDim) + 1)
7719
0
                    m = 1;
7720
0
                else if (m == static_cast<int>(m_iYDim) + 1)
7721
0
                    m = 2;
7722
0
            }
7723
0
            m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7724
0
        }
7725
0
        return m_poSRS.get();
7726
0
    }
7727
7728
    void SetGeolocationArray(const std::string &osFilenameLong,
7729
                             const std::string &osFilenameLat)
7730
0
    {
7731
0
        m_osFilenameLong = osFilenameLong;
7732
0
        m_osFilenameLat = osFilenameLat;
7733
0
        CPLStringList aosGeoLoc;
7734
0
        aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7735
0
        aosGeoLoc.SetNameValue("LINE_STEP", "1");
7736
0
        aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7737
0
        aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7738
0
        aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
7739
0
        aosGeoLoc.SetNameValue("X_BAND", "1");
7740
0
        aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7741
0
        aosGeoLoc.SetNameValue("Y_BAND", "1");
7742
0
        aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7743
0
        aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7744
0
        SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7745
0
    }
7746
};
7747
7748
GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7749
0
{
7750
0
    if (!m_osFilenameLong.empty())
7751
0
        VSIUnlink(m_osFilenameLong.c_str());
7752
0
    if (!m_osFilenameLat.empty())
7753
0
        VSIUnlink(m_osFilenameLat.c_str());
7754
0
}
7755
7756
/************************************************************************/
7757
/*                   GDALMDArrayResampledDatasetRasterBand()            */
7758
/************************************************************************/
7759
7760
GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7761
    GDALMDArrayResampledDataset *poDSIn)
7762
0
{
7763
0
    const auto &poArray(poDSIn->m_poArray);
7764
0
    const auto blockSize(poArray->GetBlockSize());
7765
0
    nBlockYSize = (blockSize[poDSIn->m_iYDim])
7766
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7767
0
                                                  blockSize[poDSIn->m_iYDim]))
7768
0
                      : 1;
7769
0
    nBlockXSize = blockSize[poDSIn->m_iXDim]
7770
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7771
0
                                                  blockSize[poDSIn->m_iXDim]))
7772
0
                      : poDSIn->GetRasterXSize();
7773
0
    eDataType = poArray->GetDataType().GetNumericDataType();
7774
0
    eAccess = poDSIn->eAccess;
7775
0
}
7776
7777
/************************************************************************/
7778
/*                           GetNoDataValue()                           */
7779
/************************************************************************/
7780
7781
double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7782
0
{
7783
0
    auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7784
0
    const auto &poArray(l_poDS->m_poArray);
7785
0
    bool bHasNodata = false;
7786
0
    double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7787
0
    if (pbHasNoData)
7788
0
        *pbHasNoData = bHasNodata;
7789
0
    return dfRes;
7790
0
}
7791
7792
/************************************************************************/
7793
/*                            IReadBlock()                              */
7794
/************************************************************************/
7795
7796
CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7797
                                                         int nBlockYOff,
7798
                                                         void *pImage)
7799
0
{
7800
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7801
0
    const int nXOff = nBlockXOff * nBlockXSize;
7802
0
    const int nYOff = nBlockYOff * nBlockYSize;
7803
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7804
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7805
0
    GDALRasterIOExtraArg sExtraArg;
7806
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7807
0
    return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7808
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
7809
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7810
0
}
7811
7812
/************************************************************************/
7813
/*                            IRasterIO()                               */
7814
/************************************************************************/
7815
7816
CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7817
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7818
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7819
    GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7820
    GDALRasterIOExtraArg *psExtraArg)
7821
0
{
7822
0
    auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7823
0
    const auto &poArray(l_poDS->m_poArray);
7824
0
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7825
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7826
0
        nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7827
0
        (nLineSpaceBuf % nBufferDTSize) == 0)
7828
0
    {
7829
0
        l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7830
0
        l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7831
0
        l_poDS->m_anStride[l_poDS->m_iXDim] =
7832
0
            static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7833
7834
0
        l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7835
0
        l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7836
0
        l_poDS->m_anStride[l_poDS->m_iYDim] =
7837
0
            static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7838
7839
0
        return poArray->Read(l_poDS->m_anOffset.data(),
7840
0
                             l_poDS->m_anCount.data(), nullptr,
7841
0
                             l_poDS->m_anStride.data(),
7842
0
                             GDALExtendedDataType::Create(eBufType), pData)
7843
0
                   ? CE_None
7844
0
                   : CE_Failure;
7845
0
    }
7846
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7847
0
                                     pData, nBufXSize, nBufYSize, eBufType,
7848
0
                                     nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7849
0
}
7850
7851
class GDALMDArrayResampled final : public GDALPamMDArray
7852
{
7853
  private:
7854
    std::shared_ptr<GDALMDArray> m_poParent{};
7855
    std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7856
    std::vector<GUInt64> m_anBlockSize;
7857
    GDALExtendedDataType m_dt;
7858
    std::shared_ptr<OGRSpatialReference> m_poSRS{};
7859
    std::shared_ptr<GDALMDArray> m_poVarX{};
7860
    std::shared_ptr<GDALMDArray> m_poVarY{};
7861
    std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7862
    std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7863
7864
  protected:
7865
    GDALMDArrayResampled(
7866
        const std::shared_ptr<GDALMDArray> &poParent,
7867
        const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7868
        const std::vector<GUInt64> &anBlockSize)
7869
0
        : GDALAbstractMDArray(std::string(),
7870
0
                              "Resampled view of " + poParent->GetFullName()),
7871
0
          GDALPamMDArray(
7872
0
              std::string(), "Resampled view of " + poParent->GetFullName(),
7873
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7874
0
          m_poParent(std::move(poParent)), m_apoDims(apoDims),
7875
0
          m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7876
0
    {
7877
0
        CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7878
0
        CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7879
0
    }
7880
7881
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7882
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7883
               const GDALExtendedDataType &bufferDataType,
7884
               void *pDstBuffer) const override;
7885
7886
  public:
7887
    static std::shared_ptr<GDALMDArray>
7888
    Create(const std::shared_ptr<GDALMDArray> &poParent,
7889
           const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7890
           GDALRIOResampleAlg resampleAlg,
7891
           const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7892
7893
    ~GDALMDArrayResampled()
7894
0
    {
7895
        // First close the warped VRT
7896
0
        m_poReprojectedDS.reset();
7897
0
        m_poParentDS.reset();
7898
0
    }
7899
7900
    bool IsWritable() const override
7901
0
    {
7902
0
        return false;
7903
0
    }
7904
7905
    const std::string &GetFilename() const override
7906
0
    {
7907
0
        return m_poParent->GetFilename();
7908
0
    }
7909
7910
    const std::vector<std::shared_ptr<GDALDimension>> &
7911
    GetDimensions() const override
7912
0
    {
7913
0
        return m_apoDims;
7914
0
    }
7915
7916
    const GDALExtendedDataType &GetDataType() const override
7917
0
    {
7918
0
        return m_dt;
7919
0
    }
7920
7921
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
7922
0
    {
7923
0
        return m_poSRS;
7924
0
    }
7925
7926
    std::vector<GUInt64> GetBlockSize() const override
7927
0
    {
7928
0
        return m_anBlockSize;
7929
0
    }
7930
7931
    std::shared_ptr<GDALAttribute>
7932
    GetAttribute(const std::string &osName) const override
7933
0
    {
7934
0
        return m_poParent->GetAttribute(osName);
7935
0
    }
7936
7937
    std::vector<std::shared_ptr<GDALAttribute>>
7938
    GetAttributes(CSLConstList papszOptions = nullptr) const override
7939
0
    {
7940
0
        return m_poParent->GetAttributes(papszOptions);
7941
0
    }
7942
7943
    const std::string &GetUnit() const override
7944
0
    {
7945
0
        return m_poParent->GetUnit();
7946
0
    }
7947
7948
    const void *GetRawNoDataValue() const override
7949
0
    {
7950
0
        return m_poParent->GetRawNoDataValue();
7951
0
    }
7952
7953
    double GetOffset(bool *pbHasOffset,
7954
                     GDALDataType *peStorageType) const override
7955
0
    {
7956
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
7957
0
    }
7958
7959
    double GetScale(bool *pbHasScale,
7960
                    GDALDataType *peStorageType) const override
7961
0
    {
7962
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
7963
0
    }
7964
};
7965
7966
/************************************************************************/
7967
/*                   GDALMDArrayResampled::Create()                     */
7968
/************************************************************************/
7969
7970
std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
7971
    const std::shared_ptr<GDALMDArray> &poParent,
7972
    const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
7973
    GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
7974
    CSLConstList /* papszOptions */)
7975
0
{
7976
0
    const char *pszResampleAlg = "nearest";
7977
0
    bool unsupported = false;
7978
0
    switch (resampleAlg)
7979
0
    {
7980
0
        case GRIORA_NearestNeighbour:
7981
0
            pszResampleAlg = "nearest";
7982
0
            break;
7983
0
        case GRIORA_Bilinear:
7984
0
            pszResampleAlg = "bilinear";
7985
0
            break;
7986
0
        case GRIORA_Cubic:
7987
0
            pszResampleAlg = "cubic";
7988
0
            break;
7989
0
        case GRIORA_CubicSpline:
7990
0
            pszResampleAlg = "cubicspline";
7991
0
            break;
7992
0
        case GRIORA_Lanczos:
7993
0
            pszResampleAlg = "lanczos";
7994
0
            break;
7995
0
        case GRIORA_Average:
7996
0
            pszResampleAlg = "average";
7997
0
            break;
7998
0
        case GRIORA_Mode:
7999
0
            pszResampleAlg = "mode";
8000
0
            break;
8001
0
        case GRIORA_Gauss:
8002
0
            unsupported = true;
8003
0
            break;
8004
0
        case GRIORA_RESERVED_START:
8005
0
            unsupported = true;
8006
0
            break;
8007
0
        case GRIORA_RESERVED_END:
8008
0
            unsupported = true;
8009
0
            break;
8010
0
        case GRIORA_RMS:
8011
0
            pszResampleAlg = "rms";
8012
0
            break;
8013
0
    }
8014
0
    if (unsupported)
8015
0
    {
8016
0
        CPLError(CE_Failure, CPLE_NotSupported,
8017
0
                 "Unsupported resample method for GetResampled()");
8018
0
        return nullptr;
8019
0
    }
8020
8021
0
    if (poParent->GetDimensionCount() < 2)
8022
0
    {
8023
0
        CPLError(CE_Failure, CPLE_NotSupported,
8024
0
                 "GetResampled() only supports 2 dimensions or more");
8025
0
        return nullptr;
8026
0
    }
8027
8028
0
    const auto &aoParentDims = poParent->GetDimensions();
8029
0
    if (apoNewDimsIn.size() != aoParentDims.size())
8030
0
    {
8031
0
        CPLError(CE_Failure, CPLE_AppDefined,
8032
0
                 "GetResampled(): apoNewDims size should be the same as "
8033
0
                 "GetDimensionCount()");
8034
0
        return nullptr;
8035
0
    }
8036
8037
0
    std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8038
0
    apoNewDims.reserve(apoNewDimsIn.size());
8039
8040
0
    std::vector<GUInt64> anBlockSize;
8041
0
    anBlockSize.reserve(apoNewDimsIn.size());
8042
0
    const auto &anParentBlockSize = poParent->GetBlockSize();
8043
8044
0
    auto apoParentDims = poParent->GetDimensions();
8045
    // Special case for NASA EMIT datasets
8046
0
    const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8047
0
                               apoParentDims[0]->GetName() == "downtrack" &&
8048
0
                               apoParentDims[1]->GetName() == "crosstrack" &&
8049
0
                               apoParentDims[2]->GetName() == "bands");
8050
8051
0
    const size_t iYDimParent =
8052
0
        bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8053
0
    const size_t iXDimParent =
8054
0
        bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8055
8056
0
    for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8057
0
    {
8058
0
        if (i == iYDimParent || i == iXDimParent)
8059
0
            continue;
8060
0
        if (apoNewDimsIn[i] == nullptr)
8061
0
        {
8062
0
            apoNewDims.emplace_back(aoParentDims[i]);
8063
0
        }
8064
0
        else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8065
0
                 apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8066
0
        {
8067
0
            CPLError(CE_Failure, CPLE_AppDefined,
8068
0
                     "GetResampled(): apoNewDims[%u] should be the same "
8069
0
                     "as its parent",
8070
0
                     i);
8071
0
            return nullptr;
8072
0
        }
8073
0
        else
8074
0
        {
8075
0
            apoNewDims.emplace_back(aoParentDims[i]);
8076
0
        }
8077
0
        anBlockSize.emplace_back(anParentBlockSize[i]);
8078
0
    }
8079
8080
0
    std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8081
0
        new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8082
8083
0
    double dfXStart = 0.0;
8084
0
    double dfXSpacing = 0.0;
8085
0
    bool gotXSpacing = false;
8086
0
    auto poNewDimX = apoNewDimsIn[iXDimParent];
8087
0
    if (poNewDimX)
8088
0
    {
8089
0
        if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8090
0
        {
8091
0
            CPLError(CE_Failure, CPLE_NotSupported,
8092
0
                     "Too big size for X dimension");
8093
0
            return nullptr;
8094
0
        }
8095
0
        auto var = poNewDimX->GetIndexingVariable();
8096
0
        if (var)
8097
0
        {
8098
0
            if (var->GetDimensionCount() != 1 ||
8099
0
                var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8100
0
                !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8101
0
            {
8102
0
                CPLError(CE_Failure, CPLE_NotSupported,
8103
0
                         "New X dimension should be indexed by a regularly "
8104
0
                         "spaced variable");
8105
0
                return nullptr;
8106
0
            }
8107
0
            gotXSpacing = true;
8108
0
        }
8109
0
    }
8110
8111
0
    double dfYStart = 0.0;
8112
0
    double dfYSpacing = 0.0;
8113
0
    auto poNewDimY = apoNewDimsIn[iYDimParent];
8114
0
    bool gotYSpacing = false;
8115
0
    if (poNewDimY)
8116
0
    {
8117
0
        if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8118
0
        {
8119
0
            CPLError(CE_Failure, CPLE_NotSupported,
8120
0
                     "Too big size for Y dimension");
8121
0
            return nullptr;
8122
0
        }
8123
0
        auto var = poNewDimY->GetIndexingVariable();
8124
0
        if (var)
8125
0
        {
8126
0
            if (var->GetDimensionCount() != 1 ||
8127
0
                var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8128
0
                !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8129
0
            {
8130
0
                CPLError(CE_Failure, CPLE_NotSupported,
8131
0
                         "New Y dimension should be indexed by a regularly "
8132
0
                         "spaced variable");
8133
0
                return nullptr;
8134
0
            }
8135
0
            gotYSpacing = true;
8136
0
        }
8137
0
    }
8138
8139
    // This limitation could probably be removed
8140
0
    if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8141
0
    {
8142
0
        CPLError(CE_Failure, CPLE_NotSupported,
8143
0
                 "Either none of new X or Y dimension should have an indexing "
8144
0
                 "variable, or both should both should have one.");
8145
0
        return nullptr;
8146
0
    }
8147
8148
0
    std::string osDstWKT;
8149
0
    if (poTargetSRS)
8150
0
    {
8151
0
        char *pszDstWKT = nullptr;
8152
0
        if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8153
0
        {
8154
0
            CPLFree(pszDstWKT);
8155
0
            return nullptr;
8156
0
        }
8157
0
        osDstWKT = pszDstWKT;
8158
0
        CPLFree(pszDstWKT);
8159
0
    }
8160
8161
    // Use coordinate variables for geolocation array
8162
0
    const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8163
0
    bool useGeolocationArray = false;
8164
0
    if (apoCoordinateVars.size() >= 2)
8165
0
    {
8166
0
        std::shared_ptr<GDALMDArray> poLongVar;
8167
0
        std::shared_ptr<GDALMDArray> poLatVar;
8168
0
        for (const auto &poCoordVar : apoCoordinateVars)
8169
0
        {
8170
0
            const auto &osName = poCoordVar->GetName();
8171
0
            const auto poAttr = poCoordVar->GetAttribute("standard_name");
8172
0
            std::string osStandardName;
8173
0
            if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8174
0
                poAttr->GetDimensionCount() == 0)
8175
0
            {
8176
0
                const char *pszStandardName = poAttr->ReadAsString();
8177
0
                if (pszStandardName)
8178
0
                    osStandardName = pszStandardName;
8179
0
            }
8180
0
            if (osName == "lon" || osName == "longitude" ||
8181
0
                osName == "Longitude" || osStandardName == "longitude")
8182
0
            {
8183
0
                poLongVar = poCoordVar;
8184
0
            }
8185
0
            else if (osName == "lat" || osName == "latitude" ||
8186
0
                     osName == "Latitude" || osStandardName == "latitude")
8187
0
            {
8188
0
                poLatVar = poCoordVar;
8189
0
            }
8190
0
        }
8191
0
        if (poLatVar != nullptr && poLongVar != nullptr)
8192
0
        {
8193
0
            const auto longDimCount = poLongVar->GetDimensionCount();
8194
0
            const auto &longDims = poLongVar->GetDimensions();
8195
0
            const auto latDimCount = poLatVar->GetDimensionCount();
8196
0
            const auto &latDims = poLatVar->GetDimensions();
8197
0
            const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8198
0
            const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8199
0
            if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8200
0
                latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8201
0
            {
8202
                // Geolocation arrays are 1D, and of consistent size with
8203
                // the variable
8204
0
                useGeolocationArray = true;
8205
0
            }
8206
0
            else if ((longDimCount == 2 ||
8207
0
                      (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8208
0
                     longDims[longDimCount - 2]->GetSize() == yDimSize &&
8209
0
                     longDims[longDimCount - 1]->GetSize() == xDimSize &&
8210
0
                     (latDimCount == 2 ||
8211
0
                      (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8212
0
                     latDims[latDimCount - 2]->GetSize() == yDimSize &&
8213
0
                     latDims[latDimCount - 1]->GetSize() == xDimSize)
8214
8215
0
            {
8216
                // Geolocation arrays are 2D (or 3D with first dimension of
8217
                // size 1, as found in Sentinel 5P products), and of consistent
8218
                // size with the variable
8219
0
                useGeolocationArray = true;
8220
0
            }
8221
0
            else
8222
0
            {
8223
0
                CPLDebug(
8224
0
                    "GDAL",
8225
0
                    "Longitude and latitude coordinate variables found, "
8226
0
                    "but their characteristics are not compatible of using "
8227
0
                    "them as geolocation arrays");
8228
0
            }
8229
0
            if (useGeolocationArray)
8230
0
            {
8231
0
                CPLDebug("GDAL",
8232
0
                         "Setting geolocation array from variables %s and %s",
8233
0
                         poLongVar->GetName().c_str(),
8234
0
                         poLatVar->GetName().c_str());
8235
0
                const std::string osFilenameLong =
8236
0
                    VSIMemGenerateHiddenFilename("longitude.tif");
8237
0
                const std::string osFilenameLat =
8238
0
                    VSIMemGenerateHiddenFilename("latitude.tif");
8239
0
                std::unique_ptr<GDALDataset> poTmpLongDS(
8240
0
                    longDimCount == 1
8241
0
                        ? poLongVar->AsClassicDataset(0, 0)
8242
0
                        : poLongVar->AsClassicDataset(longDimCount - 1,
8243
0
                                                      longDimCount - 2));
8244
0
                auto hTIFFLongDS = GDALTranslate(
8245
0
                    osFilenameLong.c_str(),
8246
0
                    GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8247
0
                std::unique_ptr<GDALDataset> poTmpLatDS(
8248
0
                    latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8249
0
                                     : poLatVar->AsClassicDataset(
8250
0
                                           latDimCount - 1, latDimCount - 2));
8251
0
                auto hTIFFLatDS = GDALTranslate(
8252
0
                    osFilenameLat.c_str(),
8253
0
                    GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8254
0
                const bool bError =
8255
0
                    (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8256
0
                GDALClose(hTIFFLongDS);
8257
0
                GDALClose(hTIFFLatDS);
8258
0
                if (bError)
8259
0
                {
8260
0
                    VSIUnlink(osFilenameLong.c_str());
8261
0
                    VSIUnlink(osFilenameLat.c_str());
8262
0
                    return nullptr;
8263
0
                }
8264
8265
0
                poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8266
0
            }
8267
0
        }
8268
0
        else
8269
0
        {
8270
0
            CPLDebug("GDAL",
8271
0
                     "Coordinate variables available for %s, but "
8272
0
                     "longitude and/or latitude variables were not identified",
8273
0
                     poParent->GetName().c_str());
8274
0
        }
8275
0
    }
8276
8277
    // Build gdalwarp arguments
8278
0
    CPLStringList aosArgv;
8279
8280
0
    aosArgv.AddString("-of");
8281
0
    aosArgv.AddString("VRT");
8282
8283
0
    aosArgv.AddString("-r");
8284
0
    aosArgv.AddString(pszResampleAlg);
8285
8286
0
    if (!osDstWKT.empty())
8287
0
    {
8288
0
        aosArgv.AddString("-t_srs");
8289
0
        aosArgv.AddString(osDstWKT.c_str());
8290
0
    }
8291
8292
0
    if (useGeolocationArray)
8293
0
        aosArgv.AddString("-geoloc");
8294
8295
0
    if (gotXSpacing && gotYSpacing)
8296
0
    {
8297
0
        const double dfXMin = dfXStart - dfXSpacing / 2;
8298
0
        const double dfXMax =
8299
0
            dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8300
0
        const double dfYMax = dfYStart - dfYSpacing / 2;
8301
0
        const double dfYMin =
8302
0
            dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8303
0
        aosArgv.AddString("-te");
8304
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8305
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8306
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8307
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8308
0
    }
8309
8310
0
    if (poNewDimX && poNewDimY)
8311
0
    {
8312
0
        aosArgv.AddString("-ts");
8313
0
        aosArgv.AddString(
8314
0
            CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8315
0
        aosArgv.AddString(
8316
0
            CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8317
0
    }
8318
0
    else if (poNewDimX)
8319
0
    {
8320
0
        aosArgv.AddString("-ts");
8321
0
        aosArgv.AddString(
8322
0
            CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8323
0
        aosArgv.AddString("0");
8324
0
    }
8325
0
    else if (poNewDimY)
8326
0
    {
8327
0
        aosArgv.AddString("-ts");
8328
0
        aosArgv.AddString("0");
8329
0
        aosArgv.AddString(
8330
0
            CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8331
0
    }
8332
8333
    // Create a warped VRT dataset
8334
0
    GDALWarpAppOptions *psOptions =
8335
0
        GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8336
0
    GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8337
0
    std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8338
0
        GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8339
0
    GDALWarpAppOptionsFree(psOptions);
8340
0
    if (poReprojectedDS == nullptr)
8341
0
        return nullptr;
8342
8343
0
    int nBlockXSize;
8344
0
    int nBlockYSize;
8345
0
    poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8346
0
    anBlockSize.emplace_back(nBlockYSize);
8347
0
    anBlockSize.emplace_back(nBlockXSize);
8348
8349
0
    double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
8350
0
    CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
8351
0
    CPLAssert(eErr == CE_None);
8352
0
    CPL_IGNORE_RET_VAL(eErr);
8353
8354
0
    auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8355
0
        std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8356
0
        poReprojectedDS->GetRasterYSize());
8357
0
    auto varY = GDALMDArrayRegularlySpaced::Create(
8358
0
        std::string(), poDimY->GetName(), poDimY,
8359
0
        adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
8360
0
    poDimY->SetIndexingVariable(varY);
8361
8362
0
    auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8363
0
        std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8364
0
        poReprojectedDS->GetRasterXSize());
8365
0
    auto varX = GDALMDArrayRegularlySpaced::Create(
8366
0
        std::string(), poDimX->GetName(), poDimX,
8367
0
        adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
8368
0
    poDimX->SetIndexingVariable(varX);
8369
8370
0
    apoNewDims.emplace_back(poDimY);
8371
0
    apoNewDims.emplace_back(poDimX);
8372
0
    auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8373
0
        new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8374
0
    newAr->SetSelf(newAr);
8375
0
    if (poTargetSRS)
8376
0
    {
8377
0
        newAr->m_poSRS.reset(poTargetSRS->Clone());
8378
0
    }
8379
0
    else
8380
0
    {
8381
0
        newAr->m_poSRS = poParent->GetSpatialRef();
8382
0
    }
8383
0
    newAr->m_poVarX = varX;
8384
0
    newAr->m_poVarY = varY;
8385
0
    newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8386
0
    newAr->m_poParentDS = std::move(poParentDS);
8387
8388
    // If the input array is y,x,band ordered, the above newAr is
8389
    // actually band,y,x ordered as it is more convenient for
8390
    // GDALMDArrayResampled::IRead() implementation. But transpose that
8391
    // array to the order of the input array
8392
0
    if (bYXBandOrder)
8393
0
        return newAr->Transpose(std::vector<int>{1, 2, 0});
8394
8395
0
    return newAr;
8396
0
}
8397
8398
/************************************************************************/
8399
/*                   GDALMDArrayResampled::IRead()                      */
8400
/************************************************************************/
8401
8402
bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8403
                                 const size_t *count, const GInt64 *arrayStep,
8404
                                 const GPtrDiff_t *bufferStride,
8405
                                 const GDALExtendedDataType &bufferDataType,
8406
                                 void *pDstBuffer) const
8407
0
{
8408
0
    if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8409
0
        return false;
8410
8411
0
    struct Stack
8412
0
    {
8413
0
        size_t nIters = 0;
8414
0
        GByte *dst_ptr = nullptr;
8415
0
        GPtrDiff_t dst_inc_offset = 0;
8416
0
    };
8417
8418
0
    const auto nDims = GetDimensionCount();
8419
0
    std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
8420
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
8421
0
    for (size_t i = 0; i < nDims; i++)
8422
0
    {
8423
0
        stack[i].dst_inc_offset =
8424
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8425
0
    }
8426
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8427
8428
0
    size_t dimIdx = 0;
8429
0
    const size_t iDimY = nDims - 2;
8430
0
    const size_t iDimX = nDims - 1;
8431
    // Use an array to avoid a false positive warning from CLang Static
8432
    // Analyzer about flushCaches being never read
8433
0
    bool flushCaches[] = {false};
8434
0
    const bool bYXBandOrder =
8435
0
        m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8436
8437
0
lbl_next_depth:
8438
0
    if (dimIdx == iDimY)
8439
0
    {
8440
0
        if (flushCaches[0])
8441
0
        {
8442
0
            flushCaches[0] = false;
8443
            // When changing of 2D slice, flush GDAL 2D buffers
8444
0
            m_poParentDS->FlushCache(false);
8445
0
            m_poReprojectedDS->FlushCache(false);
8446
0
        }
8447
8448
0
        if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8449
0
                                    GF_Read, iDimX, iDimY, arrayStartIdx, count,
8450
0
                                    arrayStep, bufferStride, bufferDataType,
8451
0
                                    stack[dimIdx].dst_ptr))
8452
0
        {
8453
0
            return false;
8454
0
        }
8455
0
    }
8456
0
    else
8457
0
    {
8458
0
        stack[dimIdx].nIters = count[dimIdx];
8459
0
        if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8460
0
            arrayStartIdx[dimIdx])
8461
0
        {
8462
0
            flushCaches[0] = true;
8463
0
        }
8464
0
        m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8465
0
            arrayStartIdx[dimIdx];
8466
0
        while (true)
8467
0
        {
8468
0
            dimIdx++;
8469
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8470
0
            goto lbl_next_depth;
8471
0
        lbl_return_to_caller:
8472
0
            dimIdx--;
8473
0
            if ((--stack[dimIdx].nIters) == 0)
8474
0
                break;
8475
0
            flushCaches[0] = true;
8476
0
            ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8477
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8478
0
        }
8479
0
    }
8480
0
    if (dimIdx > 0)
8481
0
        goto lbl_return_to_caller;
8482
8483
0
    return true;
8484
0
}
8485
8486
/************************************************************************/
8487
/*                           GetResampled()                             */
8488
/************************************************************************/
8489
8490
/** Return an array that is a resampled / reprojected view of the current array
8491
 *
8492
 * This is the same as the C function GDALMDArrayGetResampled().
8493
 *
8494
 * Currently this method can only resample along the last 2 dimensions, unless
8495
 * orthorectifying a NASA EMIT dataset.
8496
 *
8497
 * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8498
 * geometry lookup table (GLT) is used by default for fast orthorectification.
8499
 *
8500
 * Options available are:
8501
 * <ul>
8502
 * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8503
 * Can be set to NO to use generic reprojection method.
8504
 * </li>
8505
 * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8506
 * orthorectification to take into account the value of the
8507
 * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8508
 * current array along the band dimension are valid.</li>
8509
 * </ul>
8510
 *
8511
 * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8512
 *                   apoNewDims[i] can be NULL to let the method automatically
8513
 *                   determine it.
8514
 * @param resampleAlg Resampling algorithm
8515
 * @param poTargetSRS Target SRS, or nullptr
8516
 * @param papszOptions NULL-terminated list of options, or NULL.
8517
 *
8518
 * @return a new array, that holds a reference to the original one, and thus is
8519
 * a view of it (not a copy), or nullptr in case of error.
8520
 *
8521
 * @since 3.4
8522
 */
8523
std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8524
    const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8525
    GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8526
    CSLConstList papszOptions) const
8527
0
{
8528
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8529
0
    if (!self)
8530
0
    {
8531
0
        CPLError(CE_Failure, CPLE_AppDefined,
8532
0
                 "Driver implementation issue: m_pSelf not set !");
8533
0
        return nullptr;
8534
0
    }
8535
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
8536
0
    {
8537
0
        CPLError(CE_Failure, CPLE_AppDefined,
8538
0
                 "GetResampled() only supports numeric data type");
8539
0
        return nullptr;
8540
0
    }
8541
8542
    // Special case for NASA EMIT datasets
8543
0
    auto apoDims = GetDimensions();
8544
0
    if (poTargetSRS == nullptr &&
8545
0
        ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8546
0
          apoDims[1]->GetName() == "crosstrack" &&
8547
0
          apoDims[2]->GetName() == "bands" &&
8548
0
          (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8549
0
           apoNewDims ==
8550
0
               std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8551
0
                                                           apoDims[2]})) ||
8552
0
         (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8553
0
          apoDims[1]->GetName() == "crosstrack" &&
8554
0
          apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8555
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions,
8556
0
                                         "EMIT_ORTHORECTIFICATION", "YES")))
8557
0
    {
8558
0
        auto poRootGroup = GetRootGroup();
8559
0
        if (poRootGroup)
8560
0
        {
8561
0
            auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8562
0
            auto poLocationGroup = poRootGroup->OpenGroup("location");
8563
0
            if (poAttrGeotransform &&
8564
0
                poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8565
0
                poAttrGeotransform->GetDimensionCount() == 1 &&
8566
0
                poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8567
0
                poLocationGroup)
8568
0
            {
8569
0
                auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8570
0
                auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8571
0
                if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8572
0
                    poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8573
0
                    poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8574
0
                    poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8575
0
                    poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8576
0
                    poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8577
0
                {
8578
0
                    return CreateGLTOrthorectified(
8579
0
                        self, poRootGroup, poGLT_X, poGLT_Y,
8580
0
                        /* nGLTIndexOffset = */ -1,
8581
0
                        poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8582
0
                }
8583
0
            }
8584
0
        }
8585
0
    }
8586
8587
0
    if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8588
0
                                         "EMIT_ORTHORECTIFICATION", "NO")))
8589
0
    {
8590
0
        CPLError(CE_Failure, CPLE_AppDefined,
8591
0
                 "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8592
0
                 "parameters are not compatible with it");
8593
0
        return nullptr;
8594
0
    }
8595
8596
0
    return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8597
0
                                        poTargetSRS, papszOptions);
8598
0
}
8599
8600
/************************************************************************/
8601
/*                         GDALDatasetFromArray()                       */
8602
/************************************************************************/
8603
8604
class GDALDatasetFromArray;
8605
8606
namespace
8607
{
8608
struct MetadataItem
8609
{
8610
    std::shared_ptr<GDALAbstractMDArray> poArray{};
8611
    std::string osName{};
8612
    std::string osDefinition{};
8613
    bool bDefinitionUsesPctForG = false;
8614
};
8615
8616
struct BandImageryMetadata
8617
{
8618
    std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8619
    double dfCentralWavelengthToMicrometer = 1.0;
8620
    std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8621
    double dfFWHMToMicrometer = 1.0;
8622
};
8623
8624
}  // namespace
8625
8626
class GDALRasterBandFromArray final : public GDALPamRasterBand
8627
{
8628
    std::vector<GUInt64> m_anOffset{};
8629
    std::vector<size_t> m_anCount{};
8630
    std::vector<GPtrDiff_t> m_anStride{};
8631
8632
  protected:
8633
    CPLErr IReadBlock(int, int, void *) override;
8634
    CPLErr IWriteBlock(int, int, void *) override;
8635
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8636
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
8637
                     GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8638
                     GSpacing nLineSpaceBuf,
8639
                     GDALRasterIOExtraArg *psExtraArg) override;
8640
8641
  public:
8642
    explicit GDALRasterBandFromArray(
8643
        GDALDatasetFromArray *poDSIn,
8644
        const std::vector<GUInt64> &anOtherDimCoord,
8645
        const std::vector<std::vector<MetadataItem>>
8646
            &aoBandParameterMetadataItems,
8647
        const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8648
        double dfDelay, time_t nStartTime, bool &bHasWarned);
8649
8650
    double GetNoDataValue(int *pbHasNoData) override;
8651
    int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8652
    uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8653
    double GetOffset(int *pbHasOffset) override;
8654
    double GetScale(int *pbHasScale) override;
8655
    const char *GetUnitType() override;
8656
    GDALColorInterp GetColorInterpretation() override;
8657
};
8658
8659
class GDALDatasetFromArray final : public GDALPamDataset
8660
{
8661
    friend class GDALRasterBandFromArray;
8662
8663
    std::shared_ptr<GDALMDArray> m_poArray;
8664
    size_t m_iXDim;
8665
    size_t m_iYDim;
8666
    double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
8667
    bool m_bHasGT = false;
8668
    mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8669
    GDALMultiDomainMetadata m_oMDD{};
8670
    std::string m_osOvrFilename{};
8671
8672
  public:
8673
    GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8674
                         size_t iXDim, size_t iYDim)
8675
0
        : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
8676
0
    {
8677
        // Initialize an overview filename from the filename of the array
8678
        // and its name.
8679
0
        const std::string &osFilename = m_poArray->GetFilename();
8680
0
        if (!osFilename.empty())
8681
0
        {
8682
0
            m_osOvrFilename = osFilename;
8683
0
            m_osOvrFilename += '.';
8684
0
            for (char ch : m_poArray->GetName())
8685
0
            {
8686
0
                if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8687
0
                    (ch >= 'a' && ch <= 'z') || ch == '_')
8688
0
                {
8689
0
                    m_osOvrFilename += ch;
8690
0
                }
8691
0
                else
8692
0
                {
8693
0
                    m_osOvrFilename += '_';
8694
0
                }
8695
0
            }
8696
0
            m_osOvrFilename += ".ovr";
8697
0
            oOvManager.Initialize(this);
8698
0
        }
8699
0
    }
8700
8701
    static GDALDatasetFromArray *
8702
    Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8703
           size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8704
           CSLConstList papszOptions);
8705
8706
    ~GDALDatasetFromArray() override;
8707
8708
    CPLErr Close() override
8709
0
    {
8710
0
        CPLErr eErr = CE_None;
8711
0
        if (nOpenFlags != OPEN_FLAGS_CLOSED)
8712
0
        {
8713
0
            if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8714
0
                CE_None)
8715
0
                eErr = CE_Failure;
8716
0
            m_poArray.reset();
8717
0
        }
8718
0
        return eErr;
8719
0
    }
8720
8721
    CPLErr GetGeoTransform(double *padfGeoTransform) override
8722
0
    {
8723
0
        memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
8724
0
        return m_bHasGT ? CE_None : CE_Failure;
8725
0
    }
8726
8727
    const OGRSpatialReference *GetSpatialRef() const override
8728
0
    {
8729
0
        if (m_poArray->GetDimensionCount() < 2)
8730
0
            return nullptr;
8731
0
        m_poSRS = m_poArray->GetSpatialRef();
8732
0
        if (m_poSRS)
8733
0
        {
8734
0
            m_poSRS.reset(m_poSRS->Clone());
8735
0
            auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8736
0
            for (auto &m : axisMapping)
8737
0
            {
8738
0
                if (m == static_cast<int>(m_iXDim) + 1)
8739
0
                    m = 1;
8740
0
                else if (m == static_cast<int>(m_iYDim) + 1)
8741
0
                    m = 2;
8742
0
            }
8743
0
            m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8744
0
        }
8745
0
        return m_poSRS.get();
8746
0
    }
8747
8748
    CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
8749
0
    {
8750
0
        return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8751
0
    }
8752
8753
    char **GetMetadata(const char *pszDomain) override
8754
0
    {
8755
0
        return m_oMDD.GetMetadata(pszDomain);
8756
0
    }
8757
8758
    const char *GetMetadataItem(const char *pszName,
8759
                                const char *pszDomain) override
8760
0
    {
8761
0
        if (!m_osOvrFilename.empty() && pszName &&
8762
0
            EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8763
0
            EQUAL(pszDomain, "OVERVIEWS"))
8764
0
        {
8765
0
            return m_osOvrFilename.c_str();
8766
0
        }
8767
0
        return m_oMDD.GetMetadataItem(pszName, pszDomain);
8768
0
    }
8769
};
8770
8771
GDALDatasetFromArray::~GDALDatasetFromArray()
8772
0
{
8773
0
    GDALDatasetFromArray::Close();
8774
0
}
8775
8776
/************************************************************************/
8777
/*                      GDALRasterBandFromArray()                       */
8778
/************************************************************************/
8779
8780
GDALRasterBandFromArray::GDALRasterBandFromArray(
8781
    GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8782
    const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8783
    const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8784
    double dfDelay, time_t nStartTime, bool &bHasWarned)
8785
0
{
8786
0
    const auto &poArray(poDSIn->m_poArray);
8787
0
    const auto &dims(poArray->GetDimensions());
8788
0
    const auto nDimCount(dims.size());
8789
0
    const auto blockSize(poArray->GetBlockSize());
8790
0
    nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8791
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8792
0
                                                  blockSize[poDSIn->m_iYDim]))
8793
0
                      : 1;
8794
0
    nBlockXSize = blockSize[poDSIn->m_iXDim]
8795
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8796
0
                                                  blockSize[poDSIn->m_iXDim]))
8797
0
                      : poDSIn->GetRasterXSize();
8798
0
    eDataType = poArray->GetDataType().GetNumericDataType();
8799
0
    eAccess = poDSIn->eAccess;
8800
0
    m_anOffset.resize(nDimCount);
8801
0
    m_anCount.resize(nDimCount, 1);
8802
0
    m_anStride.resize(nDimCount);
8803
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
8804
0
    {
8805
0
        if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8806
0
        {
8807
0
            std::string dimName(dims[i]->GetName());
8808
0
            GUInt64 nIndex = anOtherDimCoord[j];
8809
            // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8810
            // subsetted dimensions as generated by GetView()
8811
0
            if (STARTS_WITH(dimName.c_str(), "subset_"))
8812
0
            {
8813
0
                CPLStringList aosTokens(
8814
0
                    CSLTokenizeString2(dimName.c_str(), "_", 0));
8815
0
                if (aosTokens.size() == 5)
8816
0
                {
8817
0
                    dimName = aosTokens[1];
8818
0
                    const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8819
0
                        aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8820
0
                    const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8821
0
                    nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8822
0
                                          : nStartDim - (nIndex * -nIncrDim);
8823
0
                }
8824
0
            }
8825
0
            if (nDimCount != 3 || dimName != "Band")
8826
0
            {
8827
0
                SetMetadataItem(
8828
0
                    CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8829
0
                    CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8830
0
            }
8831
8832
0
            auto indexingVar = dims[i]->GetIndexingVariable();
8833
8834
            // If the indexing variable is also listed in band parameter arrays,
8835
            // then don't use our default formatting
8836
0
            if (indexingVar)
8837
0
            {
8838
0
                for (const auto &oItem : aoBandParameterMetadataItems[j])
8839
0
                {
8840
0
                    if (oItem.poArray->GetFullName() ==
8841
0
                        indexingVar->GetFullName())
8842
0
                    {
8843
0
                        indexingVar.reset();
8844
0
                        break;
8845
0
                    }
8846
0
                }
8847
0
            }
8848
8849
0
            if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8850
0
                indexingVar->GetDimensions()[0]->GetSize() ==
8851
0
                    dims[i]->GetSize())
8852
0
            {
8853
0
                if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8854
0
                {
8855
0
                    if (!bHasWarned)
8856
0
                    {
8857
0
                        CPLError(
8858
0
                            CE_Warning, CPLE_AppDefined,
8859
0
                            "Maximum delay to load band metadata from "
8860
0
                            "dimension indexing variables has expired. "
8861
0
                            "Increase the value of the "
8862
0
                            "LOAD_EXTRA_DIM_METADATA_DELAY "
8863
0
                            "option of GDALMDArray::AsClassicDataset() "
8864
0
                            "(also accessible as the "
8865
0
                            "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
8866
0
                            "configuration option), "
8867
0
                            "or set it to 'unlimited' for unlimited delay. ");
8868
0
                        bHasWarned = true;
8869
0
                    }
8870
0
                }
8871
0
                else
8872
0
                {
8873
0
                    size_t nCount = 1;
8874
0
                    const auto &dt(indexingVar->GetDataType());
8875
0
                    std::vector<GByte> abyTmp(dt.GetSize());
8876
0
                    if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
8877
0
                                          nullptr, nullptr, dt, &abyTmp[0]))
8878
0
                    {
8879
0
                        char *pszTmp = nullptr;
8880
0
                        GDALExtendedDataType::CopyValue(
8881
0
                            &abyTmp[0], dt, &pszTmp,
8882
0
                            GDALExtendedDataType::CreateString());
8883
0
                        if (pszTmp)
8884
0
                        {
8885
0
                            SetMetadataItem(
8886
0
                                CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
8887
0
                                pszTmp);
8888
0
                            CPLFree(pszTmp);
8889
0
                        }
8890
8891
0
                        const auto &unit(indexingVar->GetUnit());
8892
0
                        if (!unit.empty())
8893
0
                        {
8894
0
                            SetMetadataItem(
8895
0
                                CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
8896
0
                                unit.c_str());
8897
0
                        }
8898
0
                    }
8899
0
                }
8900
0
            }
8901
8902
0
            for (const auto &oItem : aoBandParameterMetadataItems[j])
8903
0
            {
8904
0
                CPLString osVal;
8905
8906
0
                size_t nCount = 1;
8907
0
                const auto &dt(oItem.poArray->GetDataType());
8908
0
                if (oItem.bDefinitionUsesPctForG)
8909
0
                {
8910
                    // There is one and only one %[x][.y]f|g in osDefinition
8911
0
                    std::vector<GByte> abyTmp(dt.GetSize());
8912
0
                    if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8913
0
                                            nullptr, nullptr, dt, &abyTmp[0]))
8914
0
                    {
8915
0
                        double dfVal = 0;
8916
0
                        GDALExtendedDataType::CopyValue(
8917
0
                            &abyTmp[0], dt, &dfVal,
8918
0
                            GDALExtendedDataType::Create(GDT_Float64));
8919
0
                        osVal.Printf(oItem.osDefinition.c_str(), dfVal);
8920
0
                    }
8921
0
                }
8922
0
                else
8923
0
                {
8924
                    // There should be zero or one %s in osDefinition
8925
0
                    char *pszValue = nullptr;
8926
0
                    if (dt.GetClass() == GEDTC_STRING)
8927
0
                    {
8928
0
                        CPL_IGNORE_RET_VAL(oItem.poArray->Read(
8929
0
                            &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
8930
0
                            dt, &pszValue));
8931
0
                    }
8932
0
                    else
8933
0
                    {
8934
0
                        std::vector<GByte> abyTmp(dt.GetSize());
8935
0
                        if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
8936
0
                                                nullptr, nullptr, dt,
8937
0
                                                &abyTmp[0]))
8938
0
                        {
8939
0
                            GDALExtendedDataType::CopyValue(
8940
0
                                &abyTmp[0], dt, &pszValue,
8941
0
                                GDALExtendedDataType::CreateString());
8942
0
                        }
8943
0
                    }
8944
8945
0
                    if (pszValue)
8946
0
                    {
8947
0
                        osVal.Printf(oItem.osDefinition.c_str(), pszValue);
8948
0
                        CPLFree(pszValue);
8949
0
                    }
8950
0
                }
8951
0
                if (!osVal.empty())
8952
0
                    SetMetadataItem(oItem.osName.c_str(), osVal);
8953
0
            }
8954
8955
0
            if (aoBandImageryMetadata[j].poCentralWavelengthArray)
8956
0
            {
8957
0
                auto &poCentralWavelengthArray =
8958
0
                    aoBandImageryMetadata[j].poCentralWavelengthArray;
8959
0
                size_t nCount = 1;
8960
0
                const auto &dt(poCentralWavelengthArray->GetDataType());
8961
0
                std::vector<GByte> abyTmp(dt.GetSize());
8962
0
                if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
8963
0
                                                   &nCount, nullptr, nullptr,
8964
0
                                                   dt, &abyTmp[0]))
8965
0
                {
8966
0
                    double dfVal = 0;
8967
0
                    GDALExtendedDataType::CopyValue(
8968
0
                        &abyTmp[0], dt, &dfVal,
8969
0
                        GDALExtendedDataType::Create(GDT_Float64));
8970
0
                    SetMetadataItem(
8971
0
                        "CENTRAL_WAVELENGTH_UM",
8972
0
                        CPLSPrintf(
8973
0
                            "%g", dfVal * aoBandImageryMetadata[j]
8974
0
                                              .dfCentralWavelengthToMicrometer),
8975
0
                        "IMAGERY");
8976
0
                }
8977
0
            }
8978
8979
0
            if (aoBandImageryMetadata[j].poFWHMArray)
8980
0
            {
8981
0
                auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
8982
0
                size_t nCount = 1;
8983
0
                const auto &dt(poFWHMArray->GetDataType());
8984
0
                std::vector<GByte> abyTmp(dt.GetSize());
8985
0
                if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
8986
0
                                      nullptr, dt, &abyTmp[0]))
8987
0
                {
8988
0
                    double dfVal = 0;
8989
0
                    GDALExtendedDataType::CopyValue(
8990
0
                        &abyTmp[0], dt, &dfVal,
8991
0
                        GDALExtendedDataType::Create(GDT_Float64));
8992
0
                    SetMetadataItem(
8993
0
                        "FWHM_UM",
8994
0
                        CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
8995
0
                                                     .dfFWHMToMicrometer),
8996
0
                        "IMAGERY");
8997
0
                }
8998
0
            }
8999
9000
0
            m_anOffset[i] = anOtherDimCoord[j];
9001
0
            j++;
9002
0
        }
9003
0
    }
9004
0
}
9005
9006
/************************************************************************/
9007
/*                           GetNoDataValue()                           */
9008
/************************************************************************/
9009
9010
double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9011
0
{
9012
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9013
0
    const auto &poArray(l_poDS->m_poArray);
9014
0
    bool bHasNodata = false;
9015
0
    const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9016
0
    if (pbHasNoData)
9017
0
        *pbHasNoData = bHasNodata;
9018
0
    return res;
9019
0
}
9020
9021
/************************************************************************/
9022
/*                       GetNoDataValueAsInt64()                        */
9023
/************************************************************************/
9024
9025
int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9026
0
{
9027
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9028
0
    const auto &poArray(l_poDS->m_poArray);
9029
0
    bool bHasNodata = false;
9030
0
    const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9031
0
    if (pbHasNoData)
9032
0
        *pbHasNoData = bHasNodata;
9033
0
    return nodata;
9034
0
}
9035
9036
/************************************************************************/
9037
/*                      GetNoDataValueAsUInt64()                        */
9038
/************************************************************************/
9039
9040
uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9041
0
{
9042
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9043
0
    const auto &poArray(l_poDS->m_poArray);
9044
0
    bool bHasNodata = false;
9045
0
    const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9046
0
    if (pbHasNoData)
9047
0
        *pbHasNoData = bHasNodata;
9048
0
    return nodata;
9049
0
}
9050
9051
/************************************************************************/
9052
/*                             GetOffset()                              */
9053
/************************************************************************/
9054
9055
double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9056
0
{
9057
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9058
0
    const auto &poArray(l_poDS->m_poArray);
9059
0
    bool bHasValue = false;
9060
0
    double dfRes = poArray->GetOffset(&bHasValue);
9061
0
    if (pbHasOffset)
9062
0
        *pbHasOffset = bHasValue;
9063
0
    return dfRes;
9064
0
}
9065
9066
/************************************************************************/
9067
/*                           GetUnitType()                              */
9068
/************************************************************************/
9069
9070
const char *GDALRasterBandFromArray::GetUnitType()
9071
0
{
9072
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9073
0
    const auto &poArray(l_poDS->m_poArray);
9074
0
    return poArray->GetUnit().c_str();
9075
0
}
9076
9077
/************************************************************************/
9078
/*                             GetScale()                              */
9079
/************************************************************************/
9080
9081
double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9082
0
{
9083
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9084
0
    const auto &poArray(l_poDS->m_poArray);
9085
0
    bool bHasValue = false;
9086
0
    double dfRes = poArray->GetScale(&bHasValue);
9087
0
    if (pbHasScale)
9088
0
        *pbHasScale = bHasValue;
9089
0
    return dfRes;
9090
0
}
9091
9092
/************************************************************************/
9093
/*                            IReadBlock()                              */
9094
/************************************************************************/
9095
9096
CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9097
                                           void *pImage)
9098
0
{
9099
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9100
0
    const int nXOff = nBlockXOff * nBlockXSize;
9101
0
    const int nYOff = nBlockYOff * nBlockYSize;
9102
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9103
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9104
0
    GDALRasterIOExtraArg sExtraArg;
9105
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9106
0
    return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9107
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
9108
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9109
0
}
9110
9111
/************************************************************************/
9112
/*                            IWriteBlock()                             */
9113
/************************************************************************/
9114
9115
CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9116
                                            void *pImage)
9117
0
{
9118
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9119
0
    const int nXOff = nBlockXOff * nBlockXSize;
9120
0
    const int nYOff = nBlockYOff * nBlockYSize;
9121
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9122
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9123
0
    GDALRasterIOExtraArg sExtraArg;
9124
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9125
0
    return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9126
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
9127
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9128
0
}
9129
9130
/************************************************************************/
9131
/*                            IRasterIO()                               */
9132
/************************************************************************/
9133
9134
CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9135
                                          int nYOff, int nXSize, int nYSize,
9136
                                          void *pData, int nBufXSize,
9137
                                          int nBufYSize, GDALDataType eBufType,
9138
                                          GSpacing nPixelSpaceBuf,
9139
                                          GSpacing nLineSpaceBuf,
9140
                                          GDALRasterIOExtraArg *psExtraArg)
9141
0
{
9142
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9143
0
    const auto &poArray(l_poDS->m_poArray);
9144
0
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9145
0
    if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9146
0
        (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9147
0
        (nLineSpaceBuf % nBufferDTSize) == 0)
9148
0
    {
9149
0
        m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9150
0
        m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9151
0
        m_anStride[l_poDS->m_iXDim] =
9152
0
            static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9153
0
        if (poArray->GetDimensionCount() >= 2)
9154
0
        {
9155
0
            m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9156
0
            m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9157
0
            m_anStride[l_poDS->m_iYDim] =
9158
0
                static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9159
0
        }
9160
0
        if (eRWFlag == GF_Read)
9161
0
        {
9162
0
            return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9163
0
                                 m_anStride.data(),
9164
0
                                 GDALExtendedDataType::Create(eBufType), pData)
9165
0
                       ? CE_None
9166
0
                       : CE_Failure;
9167
0
        }
9168
0
        else
9169
0
        {
9170
0
            return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9171
0
                                  m_anStride.data(),
9172
0
                                  GDALExtendedDataType::Create(eBufType), pData)
9173
0
                       ? CE_None
9174
0
                       : CE_Failure;
9175
0
        }
9176
0
    }
9177
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9178
0
                                     pData, nBufXSize, nBufYSize, eBufType,
9179
0
                                     nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9180
0
}
9181
9182
/************************************************************************/
9183
/*                      GetColorInterpretation()                        */
9184
/************************************************************************/
9185
9186
GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9187
0
{
9188
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9189
0
    const auto &poArray(l_poDS->m_poArray);
9190
0
    auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9191
0
    if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9192
0
    {
9193
0
        bool bOK = false;
9194
0
        GUInt64 nStartIndex = 0;
9195
0
        if (poArray->GetDimensionCount() == 2 &&
9196
0
            poAttr->GetDimensionCount() == 0)
9197
0
        {
9198
0
            bOK = true;
9199
0
        }
9200
0
        else if (poArray->GetDimensionCount() == 3)
9201
0
        {
9202
0
            uint64_t nExtraDimSamples = 1;
9203
0
            const auto &apoDims = poArray->GetDimensions();
9204
0
            for (size_t i = 0; i < apoDims.size(); ++i)
9205
0
            {
9206
0
                if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9207
0
                    nExtraDimSamples *= apoDims[i]->GetSize();
9208
0
            }
9209
0
            if (poAttr->GetDimensionsSize() ==
9210
0
                std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9211
0
            {
9212
0
                bOK = true;
9213
0
            }
9214
0
            nStartIndex = nBand - 1;
9215
0
        }
9216
0
        if (bOK)
9217
0
        {
9218
0
            const auto oStringDT = GDALExtendedDataType::CreateString();
9219
0
            const size_t nCount = 1;
9220
0
            const GInt64 arrayStep = 1;
9221
0
            const GPtrDiff_t bufferStride = 1;
9222
0
            char *pszValue = nullptr;
9223
0
            poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9224
0
                         oStringDT, &pszValue);
9225
0
            if (pszValue)
9226
0
            {
9227
0
                const auto eColorInterp =
9228
0
                    GDALGetColorInterpretationByName(pszValue);
9229
0
                CPLFree(pszValue);
9230
0
                return eColorInterp;
9231
0
            }
9232
0
        }
9233
0
    }
9234
0
    return GCI_Undefined;
9235
0
}
9236
9237
/************************************************************************/
9238
/*                    GDALDatasetFromArray::Create()                    */
9239
/************************************************************************/
9240
9241
GDALDatasetFromArray *GDALDatasetFromArray::Create(
9242
    const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9243
    const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9244
9245
0
{
9246
0
    const auto nDimCount(array->GetDimensionCount());
9247
0
    if (nDimCount == 0)
9248
0
    {
9249
0
        CPLError(CE_Failure, CPLE_NotSupported,
9250
0
                 "Unsupported number of dimensions");
9251
0
        return nullptr;
9252
0
    }
9253
0
    if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9254
0
        array->GetDataType().GetNumericDataType() == GDT_Unknown)
9255
0
    {
9256
0
        CPLError(CE_Failure, CPLE_NotSupported,
9257
0
                 "Only arrays with numeric data types "
9258
0
                 "can be exposed as classic GDALDataset");
9259
0
        return nullptr;
9260
0
    }
9261
0
    if (iXDim >= nDimCount ||
9262
0
        (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
9263
0
    {
9264
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9265
0
        return nullptr;
9266
0
    }
9267
0
    GUInt64 nTotalBands = 1;
9268
0
    const auto &dims(array->GetDimensions());
9269
0
    for (size_t i = 0; i < nDimCount; ++i)
9270
0
    {
9271
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9272
0
        {
9273
0
            if (dims[i]->GetSize() > 65536 / nTotalBands)
9274
0
            {
9275
0
                CPLError(CE_Failure, CPLE_AppDefined,
9276
0
                         "Too many bands. Operate on a sliced view");
9277
0
                return nullptr;
9278
0
            }
9279
0
            nTotalBands *= dims[i]->GetSize();
9280
0
        }
9281
0
    }
9282
9283
0
    std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9284
0
    std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9285
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
9286
0
    {
9287
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9288
0
        {
9289
0
            oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9290
0
            oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9291
0
            ++j;
9292
0
        }
9293
0
    }
9294
9295
0
    const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9296
9297
0
    const char *pszBandMetadata =
9298
0
        CSLFetchNameValue(papszOptions, "BAND_METADATA");
9299
0
    std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9300
0
        nNewDimCount);
9301
0
    if (pszBandMetadata)
9302
0
    {
9303
0
        if (!poRootGroup)
9304
0
        {
9305
0
            CPLError(CE_Failure, CPLE_AppDefined,
9306
0
                     "Root group should be provided when BAND_METADATA is set");
9307
0
            return nullptr;
9308
0
        }
9309
0
        CPLJSONDocument oDoc;
9310
0
        if (!oDoc.LoadMemory(pszBandMetadata))
9311
0
        {
9312
0
            CPLError(CE_Failure, CPLE_AppDefined,
9313
0
                     "Invalid JSON content for BAND_METADATA");
9314
0
            return nullptr;
9315
0
        }
9316
0
        auto oRoot = oDoc.GetRoot();
9317
0
        if (oRoot.GetType() != CPLJSONObject::Type::Array)
9318
0
        {
9319
0
            CPLError(CE_Failure, CPLE_AppDefined,
9320
0
                     "Value of BAND_METADATA should be an array");
9321
0
            return nullptr;
9322
0
        }
9323
9324
0
        auto oArray = oRoot.ToArray();
9325
0
        for (int j = 0; j < oArray.Size(); ++j)
9326
0
        {
9327
0
            const auto oJsonItem = oArray[j];
9328
0
            MetadataItem oItem;
9329
0
            size_t iExtraDimIdx = 0;
9330
9331
0
            const auto osBandArrayFullname = oJsonItem.GetString("array");
9332
0
            const auto osBandAttributeName = oJsonItem.GetString("attribute");
9333
0
            std::shared_ptr<GDALMDArray> poArray;
9334
0
            std::shared_ptr<GDALAttribute> poAttribute;
9335
0
            if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9336
0
            {
9337
0
                CPLError(CE_Failure, CPLE_AppDefined,
9338
0
                         "BAND_METADATA[%d][\"array\"] or "
9339
0
                         "BAND_METADATA[%d][\"attribute\"] is missing",
9340
0
                         j, j);
9341
0
                return nullptr;
9342
0
            }
9343
0
            else if (!osBandArrayFullname.empty() &&
9344
0
                     !osBandAttributeName.empty())
9345
0
            {
9346
0
                CPLError(
9347
0
                    CE_Failure, CPLE_AppDefined,
9348
0
                    "BAND_METADATA[%d][\"array\"] and "
9349
0
                    "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9350
0
                    j, j);
9351
0
                return nullptr;
9352
0
            }
9353
0
            else if (!osBandArrayFullname.empty())
9354
0
            {
9355
0
                poArray =
9356
0
                    poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9357
0
                if (!poArray)
9358
0
                {
9359
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9360
0
                             "Array %s cannot be found",
9361
0
                             osBandArrayFullname.c_str());
9362
0
                    return nullptr;
9363
0
                }
9364
0
                if (poArray->GetDimensionCount() != 1)
9365
0
                {
9366
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9367
0
                             "Array %s is not a 1D array",
9368
0
                             osBandArrayFullname.c_str());
9369
0
                    return nullptr;
9370
0
                }
9371
0
                const auto &osAuxArrayDimName =
9372
0
                    poArray->GetDimensions()[0]->GetName();
9373
0
                auto oIter =
9374
0
                    oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9375
0
                if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9376
0
                {
9377
0
                    CPLError(
9378
0
                        CE_Failure, CPLE_AppDefined,
9379
0
                        "Dimension %s of array %s is not a non-X/Y dimension "
9380
0
                        "of array %s",
9381
0
                        osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9382
0
                        array->GetName().c_str());
9383
0
                    return nullptr;
9384
0
                }
9385
0
                iExtraDimIdx = oIter->second;
9386
0
                CPLAssert(iExtraDimIdx < nNewDimCount);
9387
0
            }
9388
0
            else
9389
0
            {
9390
0
                CPLAssert(!osBandAttributeName.empty());
9391
0
                poAttribute = !osBandAttributeName.empty() &&
9392
0
                                      osBandAttributeName[0] == '/'
9393
0
                                  ? poRootGroup->OpenAttributeFromFullname(
9394
0
                                        osBandAttributeName)
9395
0
                                  : array->GetAttribute(osBandAttributeName);
9396
0
                if (!poAttribute)
9397
0
                {
9398
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9399
0
                             "Attribute %s cannot be found",
9400
0
                             osBandAttributeName.c_str());
9401
0
                    return nullptr;
9402
0
                }
9403
0
                const auto aoAttrDims = poAttribute->GetDimensionsSize();
9404
0
                if (aoAttrDims.size() != 1)
9405
0
                {
9406
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9407
0
                             "Attribute %s is not a 1D array",
9408
0
                             osBandAttributeName.c_str());
9409
0
                    return nullptr;
9410
0
                }
9411
0
                bool found = false;
9412
0
                for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9413
0
                {
9414
0
                    if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9415
0
                            ->GetSize() == aoAttrDims[0])
9416
0
                    {
9417
0
                        if (found)
9418
0
                        {
9419
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9420
0
                                     "Several dimensions of %s have the same "
9421
0
                                     "size as attribute %s. Cannot infer which "
9422
0
                                     "one to bind to!",
9423
0
                                     array->GetName().c_str(),
9424
0
                                     osBandAttributeName.c_str());
9425
0
                            return nullptr;
9426
0
                        }
9427
0
                        found = true;
9428
0
                        iExtraDimIdx = iter.second;
9429
0
                    }
9430
0
                }
9431
0
                if (!found)
9432
0
                {
9433
0
                    CPLError(
9434
0
                        CE_Failure, CPLE_AppDefined,
9435
0
                        "No dimension of %s has the same size as attribute %s",
9436
0
                        array->GetName().c_str(), osBandAttributeName.c_str());
9437
0
                    return nullptr;
9438
0
                }
9439
0
            }
9440
9441
0
            oItem.osName = oJsonItem.GetString("item_name");
9442
0
            if (oItem.osName.empty())
9443
0
            {
9444
0
                CPLError(CE_Failure, CPLE_AppDefined,
9445
0
                         "BAND_METADATA[%d][\"item_name\"] is missing", j);
9446
0
                return nullptr;
9447
0
            }
9448
9449
0
            const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9450
9451
            // Check correctness of definition
9452
0
            bool bFirstNumericFormatter = true;
9453
0
            std::string osModDefinition;
9454
0
            bool bDefinitionUsesPctForG = false;
9455
0
            for (size_t k = 0; k < osDefinition.size(); ++k)
9456
0
            {
9457
0
                if (osDefinition[k] == '%')
9458
0
                {
9459
0
                    osModDefinition += osDefinition[k];
9460
0
                    if (k + 1 == osDefinition.size())
9461
0
                    {
9462
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9463
0
                                 "Value of "
9464
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9465
0
                                 "%s is invalid at offset %d",
9466
0
                                 j, osDefinition.c_str(), int(k));
9467
0
                        return nullptr;
9468
0
                    }
9469
0
                    ++k;
9470
0
                    if (osDefinition[k] == '%')
9471
0
                    {
9472
0
                        osModDefinition += osDefinition[k];
9473
0
                        continue;
9474
0
                    }
9475
0
                    if (!bFirstNumericFormatter)
9476
0
                    {
9477
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9478
0
                                 "Value of "
9479
0
                                 "BAND_METADATA[%d][\"item_value\"] = %s is "
9480
0
                                 "invalid at offset %d: %%[x][.y]f|g or %%s "
9481
0
                                 "formatters should be specified at most once",
9482
0
                                 j, osDefinition.c_str(), int(k));
9483
0
                        return nullptr;
9484
0
                    }
9485
0
                    bFirstNumericFormatter = false;
9486
0
                    for (; k < osDefinition.size(); ++k)
9487
0
                    {
9488
0
                        osModDefinition += osDefinition[k];
9489
0
                        if (!((osDefinition[k] >= '0' &&
9490
0
                               osDefinition[k] <= '9') ||
9491
0
                              osDefinition[k] == '.'))
9492
0
                            break;
9493
0
                    }
9494
0
                    if (k == osDefinition.size() ||
9495
0
                        (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9496
0
                         osDefinition[k] != 's'))
9497
0
                    {
9498
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9499
0
                                 "Value of "
9500
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9501
0
                                 "%s is invalid at offset %d: only "
9502
0
                                 "%%[x][.y]f|g or %%s formatters are accepted",
9503
0
                                 j, osDefinition.c_str(), int(k));
9504
0
                        return nullptr;
9505
0
                    }
9506
0
                    bDefinitionUsesPctForG =
9507
0
                        (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9508
0
                    if (bDefinitionUsesPctForG)
9509
0
                    {
9510
0
                        if (poArray &&
9511
0
                            poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9512
0
                        {
9513
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9514
0
                                     "Data type of %s array is not numeric",
9515
0
                                     poArray->GetName().c_str());
9516
0
                            return nullptr;
9517
0
                        }
9518
0
                        else if (poAttribute &&
9519
0
                                 poAttribute->GetDataType().GetClass() !=
9520
0
                                     GEDTC_NUMERIC)
9521
0
                        {
9522
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9523
0
                                     "Data type of %s attribute is not numeric",
9524
0
                                     poAttribute->GetFullName().c_str());
9525
0
                            return nullptr;
9526
0
                        }
9527
0
                    }
9528
0
                }
9529
0
                else if (osDefinition[k] == '$' &&
9530
0
                         k + 1 < osDefinition.size() &&
9531
0
                         osDefinition[k + 1] == '{')
9532
0
                {
9533
0
                    const auto nPos = osDefinition.find('}', k);
9534
0
                    if (nPos == std::string::npos)
9535
0
                    {
9536
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9537
0
                                 "Value of "
9538
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9539
0
                                 "%s is invalid at offset %d",
9540
0
                                 j, osDefinition.c_str(), int(k));
9541
0
                        return nullptr;
9542
0
                    }
9543
0
                    const auto osAttrName =
9544
0
                        osDefinition.substr(k + 2, nPos - (k + 2));
9545
0
                    std::shared_ptr<GDALAttribute> poAttr;
9546
0
                    if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9547
0
                    {
9548
0
                        poAttr = poArray->GetAttribute(osAttrName);
9549
0
                        if (!poAttr)
9550
0
                        {
9551
0
                            CPLError(
9552
0
                                CE_Failure, CPLE_AppDefined,
9553
0
                                "Value of "
9554
0
                                "BAND_METADATA[%d][\"item_value\"] = "
9555
0
                                "%s is invalid: %s is not an attribute of %s",
9556
0
                                j, osDefinition.c_str(), osAttrName.c_str(),
9557
0
                                poArray->GetName().c_str());
9558
0
                            return nullptr;
9559
0
                        }
9560
0
                    }
9561
0
                    else
9562
0
                    {
9563
0
                        poAttr =
9564
0
                            poRootGroup->OpenAttributeFromFullname(osAttrName);
9565
0
                        if (!poAttr)
9566
0
                        {
9567
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9568
0
                                     "Value of "
9569
0
                                     "BAND_METADATA[%d][\"item_value\"] = "
9570
0
                                     "%s is invalid: %s is not an attribute",
9571
0
                                     j, osDefinition.c_str(),
9572
0
                                     osAttrName.c_str());
9573
0
                            return nullptr;
9574
0
                        }
9575
0
                    }
9576
0
                    k = nPos;
9577
0
                    const char *pszValue = poAttr->ReadAsString();
9578
0
                    if (!pszValue)
9579
0
                    {
9580
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9581
0
                                 "Cannot get value of attribute %s as a "
9582
0
                                 "string",
9583
0
                                 osAttrName.c_str());
9584
0
                        return nullptr;
9585
0
                    }
9586
0
                    osModDefinition += pszValue;
9587
0
                }
9588
0
                else
9589
0
                {
9590
0
                    osModDefinition += osDefinition[k];
9591
0
                }
9592
0
            }
9593
9594
0
            if (poArray)
9595
0
                oItem.poArray = std::move(poArray);
9596
0
            else
9597
0
                oItem.poArray = std::move(poAttribute);
9598
0
            oItem.osDefinition = std::move(osModDefinition);
9599
0
            oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9600
9601
0
            aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9602
0
                std::move(oItem));
9603
0
        }
9604
0
    }
9605
9606
0
    std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9607
0
    const char *pszBandImageryMetadata =
9608
0
        CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9609
0
    if (pszBandImageryMetadata)
9610
0
    {
9611
0
        if (!poRootGroup)
9612
0
        {
9613
0
            CPLError(CE_Failure, CPLE_AppDefined,
9614
0
                     "Root group should be provided when BAND_IMAGERY_METADATA "
9615
0
                     "is set");
9616
0
            return nullptr;
9617
0
        }
9618
0
        CPLJSONDocument oDoc;
9619
0
        if (!oDoc.LoadMemory(pszBandImageryMetadata))
9620
0
        {
9621
0
            CPLError(CE_Failure, CPLE_AppDefined,
9622
0
                     "Invalid JSON content for BAND_IMAGERY_METADATA");
9623
0
            return nullptr;
9624
0
        }
9625
0
        auto oRoot = oDoc.GetRoot();
9626
0
        if (oRoot.GetType() != CPLJSONObject::Type::Object)
9627
0
        {
9628
0
            CPLError(CE_Failure, CPLE_AppDefined,
9629
0
                     "Value of BAND_IMAGERY_METADATA should be an object");
9630
0
            return nullptr;
9631
0
        }
9632
0
        for (const auto &oJsonItem : oRoot.GetChildren())
9633
0
        {
9634
0
            if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9635
0
                oJsonItem.GetName() == "FWHM_UM")
9636
0
            {
9637
0
                const auto osBandArrayFullname = oJsonItem.GetString("array");
9638
0
                const auto osBandAttributeName =
9639
0
                    oJsonItem.GetString("attribute");
9640
0
                std::shared_ptr<GDALMDArray> poArray;
9641
0
                std::shared_ptr<GDALAttribute> poAttribute;
9642
0
                size_t iExtraDimIdx = 0;
9643
0
                if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9644
0
                {
9645
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9646
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9647
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9648
0
                             "missing",
9649
0
                             oJsonItem.GetName().c_str(),
9650
0
                             oJsonItem.GetName().c_str());
9651
0
                    return nullptr;
9652
0
                }
9653
0
                else if (!osBandArrayFullname.empty() &&
9654
0
                         !osBandAttributeName.empty())
9655
0
                {
9656
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9657
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9658
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9659
0
                             "mutually exclusive",
9660
0
                             oJsonItem.GetName().c_str(),
9661
0
                             oJsonItem.GetName().c_str());
9662
0
                    return nullptr;
9663
0
                }
9664
0
                else if (!osBandArrayFullname.empty())
9665
0
                {
9666
0
                    poArray = poRootGroup->OpenMDArrayFromFullname(
9667
0
                        osBandArrayFullname);
9668
0
                    if (!poArray)
9669
0
                    {
9670
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9671
0
                                 "Array %s cannot be found",
9672
0
                                 osBandArrayFullname.c_str());
9673
0
                        return nullptr;
9674
0
                    }
9675
0
                    if (poArray->GetDimensionCount() != 1)
9676
0
                    {
9677
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9678
0
                                 "Array %s is not a 1D array",
9679
0
                                 osBandArrayFullname.c_str());
9680
0
                        return nullptr;
9681
0
                    }
9682
0
                    const auto &osAuxArrayDimName =
9683
0
                        poArray->GetDimensions()[0]->GetName();
9684
0
                    auto oIter =
9685
0
                        oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9686
0
                    if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9687
0
                    {
9688
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9689
0
                                 "Dimension \"%s\" of array \"%s\" is not a "
9690
0
                                 "non-X/Y dimension of array \"%s\"",
9691
0
                                 osAuxArrayDimName.c_str(),
9692
0
                                 osBandArrayFullname.c_str(),
9693
0
                                 array->GetName().c_str());
9694
0
                        return nullptr;
9695
0
                    }
9696
0
                    iExtraDimIdx = oIter->second;
9697
0
                    CPLAssert(iExtraDimIdx < nNewDimCount);
9698
0
                }
9699
0
                else
9700
0
                {
9701
0
                    poAttribute =
9702
0
                        !osBandAttributeName.empty() &&
9703
0
                                osBandAttributeName[0] == '/'
9704
0
                            ? poRootGroup->OpenAttributeFromFullname(
9705
0
                                  osBandAttributeName)
9706
0
                            : array->GetAttribute(osBandAttributeName);
9707
0
                    if (!poAttribute)
9708
0
                    {
9709
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9710
0
                                 "Attribute %s cannot be found",
9711
0
                                 osBandAttributeName.c_str());
9712
0
                        return nullptr;
9713
0
                    }
9714
0
                    const auto aoAttrDims = poAttribute->GetDimensionsSize();
9715
0
                    if (aoAttrDims.size() != 1)
9716
0
                    {
9717
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9718
0
                                 "Attribute %s is not a 1D array",
9719
0
                                 osBandAttributeName.c_str());
9720
0
                        return nullptr;
9721
0
                    }
9722
0
                    bool found = false;
9723
0
                    for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9724
0
                    {
9725
0
                        if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9726
0
                                ->GetSize() == aoAttrDims[0])
9727
0
                        {
9728
0
                            if (found)
9729
0
                            {
9730
0
                                CPLError(CE_Failure, CPLE_AppDefined,
9731
0
                                         "Several dimensions of %s have the "
9732
0
                                         "same size as attribute %s. Cannot "
9733
0
                                         "infer which one to bind to!",
9734
0
                                         array->GetName().c_str(),
9735
0
                                         osBandAttributeName.c_str());
9736
0
                                return nullptr;
9737
0
                            }
9738
0
                            found = true;
9739
0
                            iExtraDimIdx = iter.second;
9740
0
                        }
9741
0
                    }
9742
0
                    if (!found)
9743
0
                    {
9744
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9745
0
                                 "No dimension of %s has the same size as "
9746
0
                                 "attribute %s",
9747
0
                                 array->GetName().c_str(),
9748
0
                                 osBandAttributeName.c_str());
9749
0
                        return nullptr;
9750
0
                    }
9751
0
                }
9752
9753
0
                std::string osUnit = oJsonItem.GetString("unit", "um");
9754
0
                if (STARTS_WITH(osUnit.c_str(), "${"))
9755
0
                {
9756
0
                    if (osUnit.back() != '}')
9757
0
                    {
9758
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9759
0
                                 "Value of "
9760
0
                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9761
0
                                 "%s is invalid",
9762
0
                                 oJsonItem.GetName().c_str(), osUnit.c_str());
9763
0
                        return nullptr;
9764
0
                    }
9765
0
                    const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9766
0
                    std::shared_ptr<GDALAttribute> poAttr;
9767
0
                    if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9768
0
                    {
9769
0
                        poAttr = poArray->GetAttribute(osAttrName);
9770
0
                        if (!poAttr)
9771
0
                        {
9772
0
                            CPLError(
9773
0
                                CE_Failure, CPLE_AppDefined,
9774
0
                                "Value of "
9775
0
                                "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9776
0
                                "%s is invalid: %s is not an attribute of %s",
9777
0
                                oJsonItem.GetName().c_str(), osUnit.c_str(),
9778
0
                                osAttrName.c_str(),
9779
0
                                osBandArrayFullname.c_str());
9780
0
                            return nullptr;
9781
0
                        }
9782
0
                    }
9783
0
                    else
9784
0
                    {
9785
0
                        poAttr =
9786
0
                            poRootGroup->OpenAttributeFromFullname(osAttrName);
9787
0
                        if (!poAttr)
9788
0
                        {
9789
0
                            CPLError(
9790
0
                                CE_Failure, CPLE_AppDefined,
9791
0
                                "Value of "
9792
0
                                "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9793
0
                                "%s is invalid: %s is not an attribute",
9794
0
                                oJsonItem.GetName().c_str(), osUnit.c_str(),
9795
0
                                osAttrName.c_str());
9796
0
                            return nullptr;
9797
0
                        }
9798
0
                    }
9799
9800
0
                    const char *pszValue = poAttr->ReadAsString();
9801
0
                    if (!pszValue)
9802
0
                    {
9803
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9804
0
                                 "Cannot get value of attribute %s of %s as a "
9805
0
                                 "string",
9806
0
                                 osAttrName.c_str(),
9807
0
                                 osBandArrayFullname.c_str());
9808
0
                        return nullptr;
9809
0
                    }
9810
0
                    osUnit = pszValue;
9811
0
                }
9812
0
                double dfConvToUM = 1.0;
9813
0
                if (osUnit == "nm" || osUnit == "nanometre" ||
9814
0
                    osUnit == "nanometres" || osUnit == "nanometer" ||
9815
0
                    osUnit == "nanometers")
9816
0
                {
9817
0
                    dfConvToUM = 1e-3;
9818
0
                }
9819
0
                else if (!(osUnit == "um" || osUnit == "micrometre" ||
9820
0
                           osUnit == "micrometres" || osUnit == "micrometer" ||
9821
0
                           osUnit == "micrometers"))
9822
0
                {
9823
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9824
0
                             "Unhandled value for "
9825
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9826
0
                             oJsonItem.GetName().c_str(), osUnit.c_str());
9827
0
                    return nullptr;
9828
0
                }
9829
9830
0
                BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
9831
9832
0
                std::shared_ptr<GDALAbstractMDArray> abstractArray;
9833
0
                if (poArray)
9834
0
                    abstractArray = std::move(poArray);
9835
0
                else
9836
0
                    abstractArray = std::move(poAttribute);
9837
0
                if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
9838
0
                {
9839
0
                    item.poCentralWavelengthArray = std::move(abstractArray);
9840
0
                    item.dfCentralWavelengthToMicrometer = dfConvToUM;
9841
0
                }
9842
0
                else
9843
0
                {
9844
0
                    item.poFWHMArray = std::move(abstractArray);
9845
0
                    item.dfFWHMToMicrometer = dfConvToUM;
9846
0
                }
9847
0
            }
9848
0
            else
9849
0
            {
9850
0
                CPLError(CE_Warning, CPLE_AppDefined,
9851
0
                         "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
9852
0
                         oJsonItem.GetName().c_str());
9853
0
            }
9854
0
        }
9855
0
    }
9856
9857
0
    auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
9858
9859
0
    poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
9860
9861
0
    poDS->nRasterYSize =
9862
0
        nDimCount < 2 ? 1
9863
0
                      : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
9864
0
                                                  dims[iYDim]->GetSize()));
9865
0
    poDS->nRasterXSize = static_cast<int>(
9866
0
        std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
9867
9868
0
    std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
9869
0
    std::vector<GUInt64> anStackIters(nDimCount);
9870
0
    std::vector<size_t> anMapNewToOld(nNewDimCount);
9871
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
9872
0
    {
9873
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9874
0
        {
9875
0
            anMapNewToOld[j] = i;
9876
0
            j++;
9877
0
        }
9878
0
    }
9879
9880
0
    poDS->m_bHasGT =
9881
0
        array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
9882
9883
0
    const auto attrs(array->GetAttributes());
9884
0
    for (const auto &attr : attrs)
9885
0
    {
9886
0
        if (attr->GetName() != "COLOR_INTERPRETATION")
9887
0
        {
9888
0
            auto stringArray = attr->ReadAsStringArray();
9889
0
            std::string val;
9890
0
            if (stringArray.size() > 1)
9891
0
            {
9892
0
                val += '{';
9893
0
            }
9894
0
            for (int i = 0; i < stringArray.size(); ++i)
9895
0
            {
9896
0
                if (i > 0)
9897
0
                    val += ',';
9898
0
                val += stringArray[i];
9899
0
            }
9900
0
            if (stringArray.size() > 1)
9901
0
            {
9902
0
                val += '}';
9903
0
            }
9904
0
            poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
9905
0
        }
9906
0
    }
9907
9908
0
    const char *pszDelay = CSLFetchNameValueDef(
9909
0
        papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
9910
0
        CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
9911
0
    const double dfDelay =
9912
0
        EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
9913
0
    const auto nStartTime = time(nullptr);
9914
0
    bool bHasWarned = false;
9915
    // Instantiate bands by iterating over non-XY variables
9916
0
    size_t iDim = 0;
9917
0
    int nCurBand = 1;
9918
0
lbl_next_depth:
9919
0
    if (iDim < nNewDimCount)
9920
0
    {
9921
0
        anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
9922
0
        anOtherDimCoord[iDim] = 0;
9923
0
        while (true)
9924
0
        {
9925
0
            ++iDim;
9926
0
            goto lbl_next_depth;
9927
0
        lbl_return_to_caller:
9928
0
            --iDim;
9929
0
            --anStackIters[iDim];
9930
0
            if (anStackIters[iDim] == 0)
9931
0
                break;
9932
0
            ++anOtherDimCoord[iDim];
9933
0
        }
9934
0
    }
9935
0
    else
9936
0
    {
9937
0
        poDS->SetBand(nCurBand,
9938
0
                      new GDALRasterBandFromArray(
9939
0
                          poDS.get(), anOtherDimCoord,
9940
0
                          aoBandParameterMetadataItems, aoBandImageryMetadata,
9941
0
                          dfDelay, nStartTime, bHasWarned));
9942
0
        ++nCurBand;
9943
0
    }
9944
0
    if (iDim > 0)
9945
0
        goto lbl_return_to_caller;
9946
9947
0
    if (!array->GetFilename().empty())
9948
0
    {
9949
0
        poDS->SetPhysicalFilename(array->GetFilename().c_str());
9950
0
        std::string osDerivedDatasetName(
9951
0
            CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
9952
0
                       int(iYDim), array->GetFullName().c_str()));
9953
0
        if (!array->GetContext().empty())
9954
0
        {
9955
0
            osDerivedDatasetName += " with context ";
9956
0
            osDerivedDatasetName += array->GetContext();
9957
0
        }
9958
0
        poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
9959
0
        poDS->TryLoadXML();
9960
9961
0
        for (const auto &[pszKey, pszValue] :
9962
0
             cpl::IterateNameValue(static_cast<CSLConstList>(
9963
0
                 poDS->GDALPamDataset::GetMetadata())))
9964
0
        {
9965
0
            poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
9966
0
        }
9967
0
    }
9968
9969
0
    return poDS.release();
9970
0
}
9971
9972
/************************************************************************/
9973
/*                          AsClassicDataset()                         */
9974
/************************************************************************/
9975
9976
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
9977
 *
9978
 * In the case of > 2D arrays, additional dimensions will be represented as
9979
 * raster bands.
9980
 *
9981
 * The "reverse" method is GDALRasterBand::AsMDArray().
9982
 *
9983
 * This is the same as the C function GDALMDArrayAsClassicDataset().
9984
 *
9985
 * @param iXDim Index of the dimension that will be used as the X/width axis.
9986
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
9987
 *              Ignored if the dimension count is 1.
9988
 * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
9989
 *                    and BAND_IMAGERY_METADATA option.
9990
 * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
9991
 *                     nullptr. Current supported options are:
9992
 *                     <ul>
9993
 *                     <li>BAND_METADATA: JSON serialized array defining which
9994
 *                         arrays of the poRootGroup, indexed by non-X and Y
9995
 *                         dimensions, should be mapped as band metadata items.
9996
 *                         Each array item should be an object with the
9997
 *                         following members:
9998
 *                         - "array": full name of a band parameter array.
9999
 *                           Such array must be a one
10000
 *                           dimensional array, and its dimension must be one of
10001
 *                           the dimensions of the array on which the method is
10002
 *                           called (excluding the X and Y dimensons).
10003
 *                         - "attribute": name relative to *this array or full
10004
 *                           name of a single dimension numeric array whose size
10005
 *                           must be one of the dimensions of *this array
10006
 *                           (excluding the X and Y dimensons).
10007
 *                           "array" and "attribute" are mutually exclusive.
10008
 *                         - "item_name": band metadata item name
10009
 *                         - "item_value": (optional) String, where "%[x][.y]f",
10010
 *                           "%[x][.y]g" or "%s" printf-like formatting can be
10011
 *                           used to format the corresponding value of the
10012
 *                           parameter array. The percentage character should be
10013
 *                           repeated: "%%"
10014
 *                           "${attribute_name}" can also be used to include the
10015
 *                           value of an attribute for "array" when set and if
10016
 *                           not starting with '/'. Otherwise if starting with
10017
 *                           '/', it is the full path to the attribute.
10018
 *
10019
 *                           If "item_value" is not provided, a default formatting
10020
 *                           of the value will be applied.
10021
 *
10022
 *                         Example:
10023
 *                         [
10024
 *                            {
10025
 *                              "array": "/sensor_band_parameters/wavelengths",
10026
 *                              "item_name": "WAVELENGTH",
10027
 *                              "item_value": "%.1f ${units}"
10028
 *                            },
10029
 *                            {
10030
 *                              "array": "/sensor_band_parameters/fwhm",
10031
 *                              "item_name": "FWHM"
10032
 *                            },
10033
 *                            {
10034
 *                              "array": "/sensor_band_parameters/fwhm",
10035
 *                              "item_name": "FWHM_UNIT",
10036
 *                              "item_value": "${units}"
10037
 *                            }
10038
 *                         ]
10039
 *
10040
 *                         Example for Planet Labs Tanager radiance products:
10041
 *                         [
10042
 *                            {
10043
 *                              "attribute": "center_wavelengths",
10044
 *                              "item_name": "WAVELENGTH",
10045
 *                              "item_value": "%.1f ${center_wavelengths_units}"
10046
 *                            }
10047
 *                         ]
10048
 *
10049
 *                     </li>
10050
 *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10051
 *                         JSON serialized object defining which arrays of the
10052
 *                         poRootGroup, indexed by non-X and Y dimensions,
10053
 *                         should be mapped as band metadata items in the
10054
 *                         band IMAGERY domain.
10055
 *                         The object currently accepts 2 members:
10056
 *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10057
 *                           micrometers.
10058
 *                         - "FWHM_UM": Full-width half-maximum
10059
 *                           in micrometers.
10060
 *                         The value of each member should be an object with the
10061
 *                         following members:
10062
 *                         - "array": full name of a band parameter array.
10063
 *                           Such array must be a one dimensional array, and its
10064
 *                           dimension must be one of the dimensions of the
10065
 *                           array on which the method is called
10066
 *                           (excluding the X and Y dimensons).
10067
 *                         - "attribute": name relative to *this array or full
10068
 *                           name of a single dimension numeric array whose size
10069
 *                           must be one of the dimensions of *this array
10070
 *                           (excluding the X and Y dimensons).
10071
 *                           "array" and "attribute" are mutually exclusive,
10072
 *                           and one of them is required.
10073
 *                         - "unit": (optional) unit of the values pointed in
10074
 *                           the above array.
10075
 *                           Can be a literal string or a string of the form
10076
 *                           "${attribute_name}" to point to an attribute for
10077
 *                           "array" when set and if no starting
10078
 *                           with '/'. Otherwise if starting with '/', it is
10079
 *                           the full path to the attribute.
10080
 *                           Accepted values are "um", "micrometer"
10081
 *                           (with UK vs US spelling, singular or plural), "nm",
10082
 *                           "nanometer" (with UK vs US spelling, singular or
10083
 *                           plural)
10084
 *                           If not provided, micrometer is assumed.
10085
 *
10086
 *                         Example for EMIT datasets:
10087
 *                         {
10088
 *                            "CENTRAL_WAVELENGTH_UM": {
10089
 *                                "array": "/sensor_band_parameters/wavelengths",
10090
 *                                "unit": "${units}"
10091
 *                            },
10092
 *                            "FWHM_UM": {
10093
 *                                "array": "/sensor_band_parameters/fwhm",
10094
 *                                "unit": "${units}"
10095
 *                            }
10096
 *                         }
10097
 *
10098
 *                         Example for Planet Labs Tanager radiance products:
10099
 *                         {
10100
 *                            "CENTRAL_WAVELENGTH_UM": {
10101
 *                              "attribute": "center_wavelengths",
10102
 *                              "unit": "${center_wavelengths_units}"
10103
 *                            },
10104
 *                            "FWHM_UM": {
10105
 *                              "attribute": "fwhm",
10106
 *                              "unit": "${fwhm_units}"
10107
 *                            }
10108
 *                         }
10109
 *
10110
 *                     </li>
10111
 *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10112
 *                         seconds allowed to set the DIM_{dimname}_VALUE band
10113
 *                         metadata items from the indexing variable of the
10114
 *                         dimensions.
10115
 *                         Default value is 5. 'unlimited' can be used to mean
10116
 *                         unlimited delay. Can also be defined globally with
10117
 *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10118
 *                         option.</li>
10119
 *                     </ul>
10120
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10121
 */
10122
GDALDataset *
10123
GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10124
                              const std::shared_ptr<GDALGroup> &poRootGroup,
10125
                              CSLConstList papszOptions) const
10126
0
{
10127
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10128
0
    if (!self)
10129
0
    {
10130
0
        CPLError(CE_Failure, CPLE_AppDefined,
10131
0
                 "Driver implementation issue: m_pSelf not set !");
10132
0
        return nullptr;
10133
0
    }
10134
0
    return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10135
0
                                        papszOptions);
10136
0
}
10137
10138
/************************************************************************/
10139
/*                           GetStatistics()                            */
10140
/************************************************************************/
10141
10142
/**
10143
 * \brief Fetch statistics.
10144
 *
10145
 * Returns the minimum, maximum, mean and standard deviation of all
10146
 * pixel values in this array.
10147
 *
10148
 * If bForce is FALSE results will only be returned if it can be done
10149
 * quickly (i.e. without scanning the data).  If bForce is FALSE and
10150
 * results cannot be returned efficiently, the method will return CE_Warning
10151
 * but no warning will have been issued.   This is a non-standard use of
10152
 * the CE_Warning return value to indicate "nothing done".
10153
 *
10154
 * When cached statistics are not available, and bForce is TRUE,
10155
 * ComputeStatistics() is called.
10156
 *
10157
 * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10158
 * will generally cache statistics in the .aux.xml file allowing fast fetch
10159
 * after the first request.
10160
 *
10161
 * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10162
 *
10163
 * This method is the same as the C function GDALMDArrayGetStatistics().
10164
 *
10165
 * @param bApproxOK Currently ignored. In the future, should be set to true
10166
 * if statistics on the whole array are wished, or to false if a subset of it
10167
 * may be used.
10168
 *
10169
 * @param bForce If false statistics will only be returned if it can
10170
 * be done without rescanning the image.
10171
 *
10172
 * @param pdfMin Location into which to load image minimum (may be NULL).
10173
 *
10174
 * @param pdfMax Location into which to load image maximum (may be NULL).-
10175
 *
10176
 * @param pdfMean Location into which to load image mean (may be NULL).
10177
 *
10178
 * @param pdfStdDev Location into which to load image standard deviation
10179
 * (may be NULL).
10180
 *
10181
 * @param pnValidCount Number of samples whose value is different from the
10182
 * nodata value. (may be NULL)
10183
 *
10184
 * @param pfnProgress a function to call to report progress, or NULL.
10185
 *
10186
 * @param pProgressData application data to pass to the progress function.
10187
 *
10188
 * @return CE_None on success, CE_Warning if no values returned,
10189
 * CE_Failure if an error occurs.
10190
 *
10191
 * @since GDAL 3.2
10192
 */
10193
10194
CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10195
                                  double *pdfMax, double *pdfMean,
10196
                                  double *pdfStdDev, GUInt64 *pnValidCount,
10197
                                  GDALProgressFunc pfnProgress,
10198
                                  void *pProgressData)
10199
0
{
10200
0
    if (!bForce)
10201
0
        return CE_Warning;
10202
10203
0
    return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10204
0
                             pnValidCount, pfnProgress, pProgressData, nullptr)
10205
0
               ? CE_None
10206
0
               : CE_Failure;
10207
0
}
10208
10209
/************************************************************************/
10210
/*                         ComputeStatistics()                          */
10211
/************************************************************************/
10212
10213
/**
10214
 * \brief Compute statistics.
10215
 *
10216
 * Returns the minimum, maximum, mean and standard deviation of all
10217
 * pixel values in this array.
10218
 *
10219
 * Pixels taken into account in statistics are those whose mask value
10220
 * (as determined by GetMask()) is non-zero.
10221
 *
10222
 * Once computed, the statistics will generally be "set" back on the
10223
 * owing dataset.
10224
 *
10225
 * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10226
 *
10227
 * This method is the same as the C functions GDALMDArrayComputeStatistics().
10228
 * and GDALMDArrayComputeStatisticsEx().
10229
 *
10230
 * @param bApproxOK Currently ignored. In the future, should be set to true
10231
 * if statistics on the whole array are wished, or to false if a subset of it
10232
 * may be used.
10233
 *
10234
 * @param pdfMin Location into which to load image minimum (may be NULL).
10235
 *
10236
 * @param pdfMax Location into which to load image maximum (may be NULL).-
10237
 *
10238
 * @param pdfMean Location into which to load image mean (may be NULL).
10239
 *
10240
 * @param pdfStdDev Location into which to load image standard deviation
10241
 * (may be NULL).
10242
 *
10243
 * @param pnValidCount Number of samples whose value is different from the
10244
 * nodata value. (may be NULL)
10245
 *
10246
 * @param pfnProgress a function to call to report progress, or NULL.
10247
 *
10248
 * @param pProgressData application data to pass to the progress function.
10249
 *
10250
 * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10251
 *                     Options are driver specific. For now the netCDF and Zarr
10252
 *                     drivers recognize UPDATE_METADATA=YES, whose effect is
10253
 *                     to add or update the actual_range attribute with the
10254
 *                     computed min/max, only if done on the full array, in non
10255
 *                     approximate mode, and the dataset is opened in update
10256
 *                     mode.
10257
 *
10258
 * @return true on success
10259
 *
10260
 * @since GDAL 3.2
10261
 */
10262
10263
bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10264
                                    double *pdfMax, double *pdfMean,
10265
                                    double *pdfStdDev, GUInt64 *pnValidCount,
10266
                                    GDALProgressFunc pfnProgress,
10267
                                    void *pProgressData,
10268
                                    CSLConstList papszOptions)
10269
0
{
10270
0
    struct StatsPerChunkType
10271
0
    {
10272
0
        const GDALMDArray *array = nullptr;
10273
0
        std::shared_ptr<GDALMDArray> poMask{};
10274
0
        double dfMin = cpl::NumericLimits<double>::max();
10275
0
        double dfMax = -cpl::NumericLimits<double>::max();
10276
0
        double dfMean = 0.0;
10277
0
        double dfM2 = 0.0;
10278
0
        GUInt64 nValidCount = 0;
10279
0
        std::vector<GByte> abyData{};
10280
0
        std::vector<double> adfData{};
10281
0
        std::vector<GByte> abyMaskData{};
10282
0
        GDALProgressFunc pfnProgress = nullptr;
10283
0
        void *pProgressData = nullptr;
10284
0
    };
10285
10286
0
    const auto PerChunkFunc = [](GDALAbstractMDArray *,
10287
0
                                 const GUInt64 *chunkArrayStartIdx,
10288
0
                                 const size_t *chunkCount, GUInt64 iCurChunk,
10289
0
                                 GUInt64 nChunkCount, void *pUserData)
10290
0
    {
10291
0
        StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10292
0
        const GDALMDArray *array = data->array;
10293
0
        const GDALMDArray *poMask = data->poMask.get();
10294
0
        const size_t nDims = array->GetDimensionCount();
10295
0
        size_t nVals = 1;
10296
0
        for (size_t i = 0; i < nDims; i++)
10297
0
            nVals *= chunkCount[i];
10298
10299
        // Get mask
10300
0
        data->abyMaskData.resize(nVals);
10301
0
        if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10302
0
                           poMask->GetDataType(), &data->abyMaskData[0])))
10303
0
        {
10304
0
            return false;
10305
0
        }
10306
10307
        // Get data
10308
0
        const auto &oType = array->GetDataType();
10309
0
        if (oType.GetNumericDataType() == GDT_Float64)
10310
0
        {
10311
0
            data->adfData.resize(nVals);
10312
0
            if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10313
0
                             oType, &data->adfData[0]))
10314
0
            {
10315
0
                return false;
10316
0
            }
10317
0
        }
10318
0
        else
10319
0
        {
10320
0
            data->abyData.resize(nVals * oType.GetSize());
10321
0
            if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10322
0
                             oType, &data->abyData[0]))
10323
0
            {
10324
0
                return false;
10325
0
            }
10326
0
            data->adfData.resize(nVals);
10327
0
            GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10328
0
                            static_cast<int>(oType.GetSize()),
10329
0
                            &data->adfData[0], GDT_Float64,
10330
0
                            static_cast<int>(sizeof(double)),
10331
0
                            static_cast<GPtrDiff_t>(nVals));
10332
0
        }
10333
0
        for (size_t i = 0; i < nVals; i++)
10334
0
        {
10335
0
            if (data->abyMaskData[i])
10336
0
            {
10337
0
                const double dfValue = data->adfData[i];
10338
0
                data->dfMin = std::min(data->dfMin, dfValue);
10339
0
                data->dfMax = std::max(data->dfMax, dfValue);
10340
0
                data->nValidCount++;
10341
0
                const double dfDelta = dfValue - data->dfMean;
10342
0
                data->dfMean += dfDelta / data->nValidCount;
10343
0
                data->dfM2 += dfDelta * (dfValue - data->dfMean);
10344
0
            }
10345
0
        }
10346
0
        if (data->pfnProgress &&
10347
0
            !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10348
0
                               "", data->pProgressData))
10349
0
        {
10350
0
            return false;
10351
0
        }
10352
0
        return true;
10353
0
    };
10354
10355
0
    const auto &oType = GetDataType();
10356
0
    if (oType.GetClass() != GEDTC_NUMERIC ||
10357
0
        GDALDataTypeIsComplex(oType.GetNumericDataType()))
10358
0
    {
10359
0
        CPLError(
10360
0
            CE_Failure, CPLE_NotSupported,
10361
0
            "Statistics can only be computed on non-complex numeric data type");
10362
0
        return false;
10363
0
    }
10364
10365
0
    const size_t nDims = GetDimensionCount();
10366
0
    std::vector<GUInt64> arrayStartIdx(nDims);
10367
0
    std::vector<GUInt64> count(nDims);
10368
0
    const auto &poDims = GetDimensions();
10369
0
    for (size_t i = 0; i < nDims; i++)
10370
0
    {
10371
0
        count[i] = poDims[i]->GetSize();
10372
0
    }
10373
0
    const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10374
0
    const size_t nMaxChunkSize =
10375
0
        pszSwathSize
10376
0
            ? static_cast<size_t>(
10377
0
                  std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10378
0
                           CPLAtoGIntBig(pszSwathSize)))
10379
0
            : static_cast<size_t>(
10380
0
                  std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10381
0
                           GDALGetCacheMax64() / 4));
10382
0
    StatsPerChunkType sData;
10383
0
    sData.array = this;
10384
0
    sData.poMask = GetMask(nullptr);
10385
0
    if (sData.poMask == nullptr)
10386
0
    {
10387
0
        return false;
10388
0
    }
10389
0
    sData.pfnProgress = pfnProgress;
10390
0
    sData.pProgressData = pProgressData;
10391
0
    if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10392
0
                         GetProcessingChunkSize(nMaxChunkSize).data(),
10393
0
                         PerChunkFunc, &sData))
10394
0
    {
10395
0
        return false;
10396
0
    }
10397
10398
0
    if (pdfMin)
10399
0
        *pdfMin = sData.dfMin;
10400
10401
0
    if (pdfMax)
10402
0
        *pdfMax = sData.dfMax;
10403
10404
0
    if (pdfMean)
10405
0
        *pdfMean = sData.dfMean;
10406
10407
0
    const double dfStdDev =
10408
0
        sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10409
0
    if (pdfStdDev)
10410
0
        *pdfStdDev = dfStdDev;
10411
10412
0
    if (pnValidCount)
10413
0
        *pnValidCount = sData.nValidCount;
10414
10415
0
    SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10416
0
                  sData.nValidCount, papszOptions);
10417
10418
0
    return true;
10419
0
}
10420
10421
/************************************************************************/
10422
/*                            SetStatistics()                           */
10423
/************************************************************************/
10424
//! @cond Doxygen_Suppress
10425
bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10426
                                double /* dfMax */, double /* dfMean */,
10427
                                double /* dfStdDev */,
10428
                                GUInt64 /* nValidCount */,
10429
                                CSLConstList /* papszOptions */)
10430
0
{
10431
0
    CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10432
0
    return false;
10433
0
}
10434
10435
//! @endcond
10436
10437
/************************************************************************/
10438
/*                           ClearStatistics()                          */
10439
/************************************************************************/
10440
10441
/**
10442
 * \brief Clear statistics.
10443
 *
10444
 * @since GDAL 3.4
10445
 */
10446
void GDALMDArray::ClearStatistics()
10447
0
{
10448
0
}
10449
10450
/************************************************************************/
10451
/*                      GetCoordinateVariables()                        */
10452
/************************************************************************/
10453
10454
/**
10455
 * \brief Return coordinate variables.
10456
 *
10457
 * Coordinate variables are an alternate way of indexing an array that can
10458
 * be sometimes used. For example, an array collected through remote sensing
10459
 * might be indexed by (scanline, pixel). But there can be
10460
 * a longitude and latitude arrays alongside that are also both indexed by
10461
 * (scanline, pixel), and are referenced from operational arrays for
10462
 * reprojection purposes.
10463
 *
10464
 * For netCDF, this will return the arrays referenced by the "coordinates"
10465
 * attribute.
10466
 *
10467
 * This method is the same as the C function
10468
 * GDALMDArrayGetCoordinateVariables().
10469
 *
10470
 * @return a vector of arrays
10471
 *
10472
 * @since GDAL 3.4
10473
 */
10474
10475
std::vector<std::shared_ptr<GDALMDArray>>
10476
GDALMDArray::GetCoordinateVariables() const
10477
0
{
10478
0
    return {};
10479
0
}
10480
10481
/************************************************************************/
10482
/*                       ~GDALExtendedDataType()                        */
10483
/************************************************************************/
10484
10485
0
GDALExtendedDataType::~GDALExtendedDataType() = default;
10486
10487
/************************************************************************/
10488
/*                        GDALExtendedDataType()                        */
10489
/************************************************************************/
10490
10491
GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10492
                                           GDALExtendedDataTypeSubType eSubType)
10493
0
    : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10494
0
      m_nMaxStringLength(nMaxStringLength)
10495
0
{
10496
0
}
10497
10498
/************************************************************************/
10499
/*                        GDALExtendedDataType()                        */
10500
/************************************************************************/
10501
10502
GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10503
0
    : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10504
0
      m_nSize(GDALGetDataTypeSizeBytes(eType))
10505
0
{
10506
0
}
10507
10508
/************************************************************************/
10509
/*                        GDALExtendedDataType()                        */
10510
/************************************************************************/
10511
10512
GDALExtendedDataType::GDALExtendedDataType(
10513
    const std::string &osName, GDALDataType eBaseType,
10514
    std::unique_ptr<GDALRasterAttributeTable> poRAT)
10515
0
    : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10516
0
      m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10517
0
{
10518
0
}
10519
10520
/************************************************************************/
10521
/*                        GDALExtendedDataType()                        */
10522
/************************************************************************/
10523
10524
GDALExtendedDataType::GDALExtendedDataType(
10525
    const std::string &osName, size_t nTotalSize,
10526
    std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10527
0
    : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10528
0
      m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10529
0
{
10530
0
}
10531
10532
/************************************************************************/
10533
/*                        GDALExtendedDataType()                        */
10534
/************************************************************************/
10535
10536
/** Copy constructor. */
10537
GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10538
0
    : m_osName(other.m_osName), m_eClass(other.m_eClass),
10539
0
      m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10540
0
      m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10541
0
      m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10542
0
{
10543
0
    if (m_eClass == GEDTC_COMPOUND)
10544
0
    {
10545
0
        for (const auto &elt : other.m_aoComponents)
10546
0
        {
10547
0
            m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10548
0
        }
10549
0
    }
10550
0
}
10551
10552
/************************************************************************/
10553
/*                            operator= ()                              */
10554
/************************************************************************/
10555
10556
/** Copy assignment. */
10557
GDALExtendedDataType &
10558
GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10559
0
{
10560
0
    if (this != &other)
10561
0
    {
10562
0
        m_osName = other.m_osName;
10563
0
        m_eClass = other.m_eClass;
10564
0
        m_eSubType = other.m_eSubType;
10565
0
        m_eNumericDT = other.m_eNumericDT;
10566
0
        m_nSize = other.m_nSize;
10567
0
        m_nMaxStringLength = other.m_nMaxStringLength;
10568
0
        m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10569
0
        m_aoComponents.clear();
10570
0
        if (m_eClass == GEDTC_COMPOUND)
10571
0
        {
10572
0
            for (const auto &elt : other.m_aoComponents)
10573
0
            {
10574
0
                m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10575
0
            }
10576
0
        }
10577
0
    }
10578
0
    return *this;
10579
0
}
10580
10581
/************************************************************************/
10582
/*                            operator= ()                              */
10583
/************************************************************************/
10584
10585
/** Move assignment. */
10586
GDALExtendedDataType &
10587
GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
10588
0
{
10589
0
    m_osName = std::move(other.m_osName);
10590
0
    m_eClass = other.m_eClass;
10591
0
    m_eSubType = other.m_eSubType;
10592
0
    m_eNumericDT = other.m_eNumericDT;
10593
0
    m_nSize = other.m_nSize;
10594
0
    m_nMaxStringLength = other.m_nMaxStringLength;
10595
0
    m_aoComponents = std::move(other.m_aoComponents);
10596
0
    m_poRAT = std::move(other.m_poRAT);
10597
0
    other.m_eClass = GEDTC_NUMERIC;
10598
0
    other.m_eNumericDT = GDT_Unknown;
10599
0
    other.m_nSize = 0;
10600
0
    other.m_nMaxStringLength = 0;
10601
0
    return *this;
10602
0
}
10603
10604
/************************************************************************/
10605
/*                           Create()                                   */
10606
/************************************************************************/
10607
10608
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10609
 *
10610
 * This is the same as the C function GDALExtendedDataTypeCreate()
10611
 *
10612
 * @param eType Numeric data type. Must be different from GDT_Unknown and
10613
 * GDT_TypeCount
10614
 */
10615
GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10616
0
{
10617
0
    return GDALExtendedDataType(eType);
10618
0
}
10619
10620
/************************************************************************/
10621
/*                           Create()                                   */
10622
/************************************************************************/
10623
10624
/** Return a new GDALExtendedDataType from a raster attribute table.
10625
 *
10626
 * @param osName Type name
10627
 * @param eBaseType Base integer data type.
10628
 * @param poRAT Raster attribute table. Must not be NULL.
10629
 * @since 3.12
10630
 */
10631
GDALExtendedDataType
10632
GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10633
                             std::unique_ptr<GDALRasterAttributeTable> poRAT)
10634
0
{
10635
0
    return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10636
0
}
10637
10638
/************************************************************************/
10639
/*                           Create()                                   */
10640
/************************************************************************/
10641
10642
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10643
 *
10644
 * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10645
 *
10646
 * @param osName Type name.
10647
 * @param nTotalSize Total size of the type in bytes.
10648
 *                   Should be large enough to store all components.
10649
 * @param components Components of the compound type.
10650
 */
10651
GDALExtendedDataType GDALExtendedDataType::Create(
10652
    const std::string &osName, size_t nTotalSize,
10653
    std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10654
0
{
10655
0
    size_t nLastOffset = 0;
10656
    // Some arbitrary threshold to avoid potential integer overflows
10657
0
    if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10658
0
    {
10659
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10660
0
        return GDALExtendedDataType(GDT_Unknown);
10661
0
    }
10662
0
    for (const auto &comp : components)
10663
0
    {
10664
        // Check alignment too ?
10665
0
        if (comp->GetOffset() < nLastOffset)
10666
0
        {
10667
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10668
0
            return GDALExtendedDataType(GDT_Unknown);
10669
0
        }
10670
0
        nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10671
0
    }
10672
0
    if (nTotalSize < nLastOffset)
10673
0
    {
10674
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10675
0
        return GDALExtendedDataType(GDT_Unknown);
10676
0
    }
10677
0
    if (nTotalSize == 0 || components.empty())
10678
0
    {
10679
0
        CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10680
0
        return GDALExtendedDataType(GDT_Unknown);
10681
0
    }
10682
0
    return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10683
0
}
10684
10685
/************************************************************************/
10686
/*                           Create()                                   */
10687
/************************************************************************/
10688
10689
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
10690
 *
10691
 * This is the same as the C function GDALExtendedDataTypeCreateString().
10692
 *
10693
 * @param nMaxStringLength maximum length of a string in bytes. 0 if
10694
 * unknown/unlimited
10695
 * @param eSubType Subtype.
10696
 */
10697
GDALExtendedDataType
10698
GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10699
                                   GDALExtendedDataTypeSubType eSubType)
10700
0
{
10701
0
    return GDALExtendedDataType(nMaxStringLength, eSubType);
10702
0
}
10703
10704
/************************************************************************/
10705
/*                           operator==()                               */
10706
/************************************************************************/
10707
10708
/** Equality operator.
10709
 *
10710
 * This is the same as the C function GDALExtendedDataTypeEquals().
10711
 */
10712
bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10713
0
{
10714
0
    if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10715
0
        m_nSize != other.m_nSize || m_osName != other.m_osName)
10716
0
    {
10717
0
        return false;
10718
0
    }
10719
0
    if (m_eClass == GEDTC_NUMERIC)
10720
0
    {
10721
0
        return m_eNumericDT == other.m_eNumericDT;
10722
0
    }
10723
0
    if (m_eClass == GEDTC_STRING)
10724
0
    {
10725
0
        return true;
10726
0
    }
10727
0
    CPLAssert(m_eClass == GEDTC_COMPOUND);
10728
0
    if (m_aoComponents.size() != other.m_aoComponents.size())
10729
0
    {
10730
0
        return false;
10731
0
    }
10732
0
    for (size_t i = 0; i < m_aoComponents.size(); i++)
10733
0
    {
10734
0
        if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10735
0
        {
10736
0
            return false;
10737
0
        }
10738
0
    }
10739
0
    return true;
10740
0
}
10741
10742
/************************************************************************/
10743
/*                        CanConvertTo()                                */
10744
/************************************************************************/
10745
10746
/** Return whether this data type can be converted to the other one.
10747
 *
10748
 * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10749
 *
10750
 * @param other Target data type for the conversion being considered.
10751
 */
10752
bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10753
0
{
10754
0
    if (m_eClass == GEDTC_NUMERIC)
10755
0
    {
10756
0
        if (m_eNumericDT == GDT_Unknown)
10757
0
            return false;
10758
0
        if (other.m_eClass == GEDTC_NUMERIC &&
10759
0
            other.m_eNumericDT == GDT_Unknown)
10760
0
            return false;
10761
0
        return other.m_eClass == GEDTC_NUMERIC ||
10762
0
               other.m_eClass == GEDTC_STRING;
10763
0
    }
10764
0
    if (m_eClass == GEDTC_STRING)
10765
0
    {
10766
0
        return other.m_eClass == m_eClass;
10767
0
    }
10768
0
    CPLAssert(m_eClass == GEDTC_COMPOUND);
10769
0
    if (other.m_eClass != GEDTC_COMPOUND)
10770
0
        return false;
10771
0
    std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10772
0
        srcComponents;
10773
0
    for (const auto &srcComp : m_aoComponents)
10774
0
    {
10775
0
        srcComponents[srcComp->GetName()] = &srcComp;
10776
0
    }
10777
0
    for (const auto &dstComp : other.m_aoComponents)
10778
0
    {
10779
0
        auto oIter = srcComponents.find(dstComp->GetName());
10780
0
        if (oIter == srcComponents.end())
10781
0
            return false;
10782
0
        if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10783
0
            return false;
10784
0
    }
10785
0
    return true;
10786
0
}
10787
10788
/************************************************************************/
10789
/*                     NeedsFreeDynamicMemory()                         */
10790
/************************************************************************/
10791
10792
/** Return whether the data type holds dynamically allocated memory, that
10793
 * needs to be freed with FreeDynamicMemory().
10794
 *
10795
 */
10796
bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10797
0
{
10798
0
    switch (m_eClass)
10799
0
    {
10800
0
        case GEDTC_STRING:
10801
0
            return true;
10802
10803
0
        case GEDTC_NUMERIC:
10804
0
            return false;
10805
10806
0
        case GEDTC_COMPOUND:
10807
0
        {
10808
0
            for (const auto &comp : m_aoComponents)
10809
0
            {
10810
0
                if (comp->GetType().NeedsFreeDynamicMemory())
10811
0
                    return true;
10812
0
            }
10813
0
        }
10814
0
    }
10815
0
    return false;
10816
0
}
10817
10818
/************************************************************************/
10819
/*                        FreeDynamicMemory()                           */
10820
/************************************************************************/
10821
10822
/** Release the dynamic memory (strings typically) from a raw value.
10823
 *
10824
 * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
10825
 *
10826
 * @param pBuffer Raw buffer of a single element of an attribute or array value.
10827
 */
10828
void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
10829
0
{
10830
0
    switch (m_eClass)
10831
0
    {
10832
0
        case GEDTC_STRING:
10833
0
        {
10834
0
            char *pszStr;
10835
0
            memcpy(&pszStr, pBuffer, sizeof(char *));
10836
0
            if (pszStr)
10837
0
            {
10838
0
                VSIFree(pszStr);
10839
0
            }
10840
0
            break;
10841
0
        }
10842
10843
0
        case GEDTC_NUMERIC:
10844
0
        {
10845
0
            break;
10846
0
        }
10847
10848
0
        case GEDTC_COMPOUND:
10849
0
        {
10850
0
            GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
10851
0
            for (const auto &comp : m_aoComponents)
10852
0
            {
10853
0
                comp->GetType().FreeDynamicMemory(pabyBuffer +
10854
0
                                                  comp->GetOffset());
10855
0
            }
10856
0
            break;
10857
0
        }
10858
0
    }
10859
0
}
10860
10861
/************************************************************************/
10862
/*                      ~GDALEDTComponent()                             */
10863
/************************************************************************/
10864
10865
0
GDALEDTComponent::~GDALEDTComponent() = default;
10866
10867
/************************************************************************/
10868
/*                      GDALEDTComponent()                              */
10869
/************************************************************************/
10870
10871
/** constructor of a GDALEDTComponent
10872
 *
10873
 * This is the same as the C function GDALEDTComponendCreate()
10874
 *
10875
 * @param name Component name
10876
 * @param offset Offset in byte of the component in the compound data type.
10877
 *               In case of nesting of compound data type, this should be
10878
 *               the offset to the immediate belonging data type, not to the
10879
 *               higher level one.
10880
 * @param type   Component data type.
10881
 */
10882
GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
10883
                                   const GDALExtendedDataType &type)
10884
0
    : m_osName(name), m_nOffset(offset), m_oType(type)
10885
0
{
10886
0
}
10887
10888
/************************************************************************/
10889
/*                      GDALEDTComponent()                              */
10890
/************************************************************************/
10891
10892
/** Copy constructor. */
10893
0
GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
10894
10895
/************************************************************************/
10896
/*                           operator==()                               */
10897
/************************************************************************/
10898
10899
/** Equality operator.
10900
 */
10901
bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
10902
0
{
10903
0
    return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
10904
0
           m_oType == other.m_oType;
10905
0
}
10906
10907
/************************************************************************/
10908
/*                        ~GDALDimension()                              */
10909
/************************************************************************/
10910
10911
0
GDALDimension::~GDALDimension() = default;
10912
10913
/************************************************************************/
10914
/*                         GDALDimension()                              */
10915
/************************************************************************/
10916
10917
//! @cond Doxygen_Suppress
10918
/** Constructor.
10919
 *
10920
 * @param osParentName Parent name
10921
 * @param osName name
10922
 * @param osType type. See GetType().
10923
 * @param osDirection direction. See GetDirection().
10924
 * @param nSize size.
10925
 */
10926
GDALDimension::GDALDimension(const std::string &osParentName,
10927
                             const std::string &osName,
10928
                             const std::string &osType,
10929
                             const std::string &osDirection, GUInt64 nSize)
10930
0
    : m_osName(osName),
10931
      m_osFullName(
10932
0
          !osParentName.empty()
10933
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
10934
0
              : osName),
10935
0
      m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
10936
0
{
10937
0
}
10938
10939
//! @endcond
10940
10941
/************************************************************************/
10942
/*                         GetIndexingVariable()                        */
10943
/************************************************************************/
10944
10945
/** Return the variable that is used to index the dimension (if there is one).
10946
 *
10947
 * This is the array, typically one-dimensional, describing the values taken
10948
 * by the dimension.
10949
 */
10950
std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
10951
0
{
10952
0
    return nullptr;
10953
0
}
10954
10955
/************************************************************************/
10956
/*                         SetIndexingVariable()                        */
10957
/************************************************************************/
10958
10959
/** Set the variable that is used to index the dimension.
10960
 *
10961
 * This is the array, typically one-dimensional, describing the values taken
10962
 * by the dimension.
10963
 *
10964
 * Optionally implemented by drivers.
10965
 *
10966
 * Drivers known to implement it: MEM.
10967
 *
10968
 * @param poArray Variable to use to index the dimension.
10969
 * @return true in case of success.
10970
 */
10971
bool GDALDimension::SetIndexingVariable(
10972
    CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
10973
0
{
10974
0
    CPLError(CE_Failure, CPLE_NotSupported,
10975
0
             "SetIndexingVariable() not implemented");
10976
0
    return false;
10977
0
}
10978
10979
/************************************************************************/
10980
/*                            Rename()                                  */
10981
/************************************************************************/
10982
10983
/** Rename the dimension.
10984
 *
10985
 * This is not implemented by all drivers.
10986
 *
10987
 * Drivers known to implement it: MEM, netCDF, ZARR.
10988
 *
10989
 * This is the same as the C function GDALDimensionRename().
10990
 *
10991
 * @param osNewName New name.
10992
 *
10993
 * @return true in case of success
10994
 * @since GDAL 3.8
10995
 */
10996
bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
10997
0
{
10998
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
10999
0
    return false;
11000
0
}
11001
11002
/************************************************************************/
11003
/*                         BaseRename()                                 */
11004
/************************************************************************/
11005
11006
//! @cond Doxygen_Suppress
11007
void GDALDimension::BaseRename(const std::string &osNewName)
11008
0
{
11009
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
11010
0
    m_osFullName += osNewName;
11011
0
    m_osName = osNewName;
11012
0
}
11013
11014
//! @endcond
11015
11016
//! @cond Doxygen_Suppress
11017
/************************************************************************/
11018
/*                          ParentRenamed()                             */
11019
/************************************************************************/
11020
11021
void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11022
0
{
11023
0
    m_osFullName = osNewParentFullName;
11024
0
    m_osFullName += "/";
11025
0
    m_osFullName += m_osName;
11026
0
}
11027
11028
//! @endcond
11029
11030
//! @cond Doxygen_Suppress
11031
/************************************************************************/
11032
/*                          ParentDeleted()                             */
11033
/************************************************************************/
11034
11035
void GDALDimension::ParentDeleted()
11036
0
{
11037
0
}
11038
11039
//! @endcond
11040
11041
/************************************************************************/
11042
/************************************************************************/
11043
/************************************************************************/
11044
/*                              C API                                   */
11045
/************************************************************************/
11046
/************************************************************************/
11047
/************************************************************************/
11048
11049
/************************************************************************/
11050
/*                      GDALExtendedDataTypeCreate()                    */
11051
/************************************************************************/
11052
11053
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11054
 *
11055
 * This is the same as the C++ method GDALExtendedDataType::Create()
11056
 *
11057
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11058
 *
11059
 * @param eType Numeric data type. Must be different from GDT_Unknown and
11060
 * GDT_TypeCount
11061
 *
11062
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11063
 */
11064
GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11065
0
{
11066
0
    if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11067
0
    {
11068
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11069
0
                 "Illegal GDT_Unknown/GDT_TypeCount argument");
11070
0
        return nullptr;
11071
0
    }
11072
0
    return new GDALExtendedDataTypeHS(
11073
0
        new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11074
0
}
11075
11076
/************************************************************************/
11077
/*                    GDALExtendedDataTypeCreateString()                */
11078
/************************************************************************/
11079
11080
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
11081
 *
11082
 * This is the same as the C++ method GDALExtendedDataType::CreateString()
11083
 *
11084
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11085
 *
11086
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11087
 */
11088
GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11089
0
{
11090
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11091
0
        GDALExtendedDataType::CreateString(nMaxStringLength)));
11092
0
}
11093
11094
/************************************************************************/
11095
/*                   GDALExtendedDataTypeCreateStringEx()               */
11096
/************************************************************************/
11097
11098
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
11099
 *
11100
 * This is the same as the C++ method GDALExtendedDataType::CreateString()
11101
 *
11102
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11103
 *
11104
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11105
 * @since GDAL 3.4
11106
 */
11107
GDALExtendedDataTypeH
11108
GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11109
                                   GDALExtendedDataTypeSubType eSubType)
11110
0
{
11111
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11112
0
        GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11113
0
}
11114
11115
/************************************************************************/
11116
/*                   GDALExtendedDataTypeCreateCompound()               */
11117
/************************************************************************/
11118
11119
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11120
 *
11121
 * This is the same as the C++ method GDALExtendedDataType::Create(const
11122
 * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11123
 *
11124
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11125
 *
11126
 * @param pszName Type name.
11127
 * @param nTotalSize Total size of the type in bytes.
11128
 *                   Should be large enough to store all components.
11129
 * @param nComponents Number of components in comps array.
11130
 * @param comps Components.
11131
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11132
 */
11133
GDALExtendedDataTypeH
11134
GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11135
                                   size_t nComponents,
11136
                                   const GDALEDTComponentH *comps)
11137
0
{
11138
0
    std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11139
0
    for (size_t i = 0; i < nComponents; i++)
11140
0
    {
11141
0
        compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
11142
0
            new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
11143
0
    }
11144
0
    auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11145
0
                                           std::move(compsCpp));
11146
0
    if (dt.GetClass() != GEDTC_COMPOUND)
11147
0
        return nullptr;
11148
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
11149
0
}
11150
11151
/************************************************************************/
11152
/*                     GDALExtendedDataTypeRelease()                    */
11153
/************************************************************************/
11154
11155
/** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11156
 *
11157
 * Note: when applied on a object coming from a driver, this does not
11158
 * destroy the object in the file, database, etc...
11159
 */
11160
void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11161
0
{
11162
0
    delete hEDT;
11163
0
}
11164
11165
/************************************************************************/
11166
/*                     GDALExtendedDataTypeGetName()                    */
11167
/************************************************************************/
11168
11169
/** Return type name.
11170
 *
11171
 * This is the same as the C++ method GDALExtendedDataType::GetName()
11172
 */
11173
const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11174
0
{
11175
0
    VALIDATE_POINTER1(hEDT, __func__, "");
11176
0
    return hEDT->m_poImpl->GetName().c_str();
11177
0
}
11178
11179
/************************************************************************/
11180
/*                     GDALExtendedDataTypeGetClass()                    */
11181
/************************************************************************/
11182
11183
/** Return type class.
11184
 *
11185
 * This is the same as the C++ method GDALExtendedDataType::GetClass()
11186
 */
11187
GDALExtendedDataTypeClass
11188
GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11189
0
{
11190
0
    VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11191
0
    return hEDT->m_poImpl->GetClass();
11192
0
}
11193
11194
/************************************************************************/
11195
/*               GDALExtendedDataTypeGetNumericDataType()               */
11196
/************************************************************************/
11197
11198
/** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11199
 *
11200
 * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11201
 */
11202
GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11203
0
{
11204
0
    VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11205
0
    return hEDT->m_poImpl->GetNumericDataType();
11206
0
}
11207
11208
/************************************************************************/
11209
/*                   GDALExtendedDataTypeGetSize()                      */
11210
/************************************************************************/
11211
11212
/** Return data type size in bytes.
11213
 *
11214
 * This is the same as the C++ method GDALExtendedDataType::GetSize()
11215
 */
11216
size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11217
0
{
11218
0
    VALIDATE_POINTER1(hEDT, __func__, 0);
11219
0
    return hEDT->m_poImpl->GetSize();
11220
0
}
11221
11222
/************************************************************************/
11223
/*              GDALExtendedDataTypeGetMaxStringLength()                */
11224
/************************************************************************/
11225
11226
/** Return the maximum length of a string in bytes.
11227
 *
11228
 * 0 indicates unknown/unlimited string.
11229
 *
11230
 * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11231
 */
11232
size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11233
0
{
11234
0
    VALIDATE_POINTER1(hEDT, __func__, 0);
11235
0
    return hEDT->m_poImpl->GetMaxStringLength();
11236
0
}
11237
11238
/************************************************************************/
11239
/*                    GDALExtendedDataTypeCanConvertTo()                */
11240
/************************************************************************/
11241
11242
/** Return whether this data type can be converted to the other one.
11243
 *
11244
 * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11245
 *
11246
 * @param hSourceEDT Source data type for the conversion being considered.
11247
 * @param hTargetEDT Target data type for the conversion being considered.
11248
 * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11249
 */
11250
int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11251
                                     GDALExtendedDataTypeH hTargetEDT)
11252
0
{
11253
0
    VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11254
0
    VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11255
0
    return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11256
0
}
11257
11258
/************************************************************************/
11259
/*                        GDALExtendedDataTypeEquals()                  */
11260
/************************************************************************/
11261
11262
/** Return whether this data type is equal to another one.
11263
 *
11264
 * This is the same as the C++ method GDALExtendedDataType::operator==()
11265
 *
11266
 * @param hFirstEDT First data type.
11267
 * @param hSecondEDT Second data type.
11268
 * @return TRUE if they are equal. FALSE otherwise.
11269
 */
11270
int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11271
                               GDALExtendedDataTypeH hSecondEDT)
11272
0
{
11273
0
    VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11274
0
    VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11275
0
    return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11276
0
}
11277
11278
/************************************************************************/
11279
/*                    GDALExtendedDataTypeGetSubType()                  */
11280
/************************************************************************/
11281
11282
/** Return the subtype of a type.
11283
 *
11284
 * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11285
 *
11286
 * @param hEDT Data type.
11287
 * @return subtype.
11288
 * @since 3.4
11289
 */
11290
GDALExtendedDataTypeSubType
11291
GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11292
0
{
11293
0
    VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11294
0
    return hEDT->m_poImpl->GetSubType();
11295
0
}
11296
11297
/************************************************************************/
11298
/*                      GDALExtendedDataTypeGetRAT()                    */
11299
/************************************************************************/
11300
11301
/** Return associated raster attribute table, when there is one.
11302
 *
11303
 * * For the netCDF driver, the RAT will capture enumerated types, with
11304
 * a "value" column with an integer value and a "name" column with the
11305
 * associated name.
11306
 * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11307
 *
11308
 * @param hEDT Data type.
11309
 * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11310
 * @since 3.12
11311
 */
11312
GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11313
0
{
11314
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
11315
0
    return GDALRasterAttributeTable::ToHandle(
11316
0
        const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11317
0
}
11318
11319
/************************************************************************/
11320
/*                     GDALExtendedDataTypeGetComponents()              */
11321
/************************************************************************/
11322
11323
/** Return the components of the data type (only valid when GetClass() ==
11324
 * GEDTC_COMPOUND)
11325
 *
11326
 * The returned array and its content must be freed with
11327
 * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11328
 * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11329
 * individual array members).
11330
 *
11331
 * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11332
 *
11333
 * @param hEDT Data type
11334
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11335
 * @return an array of *pnCount components.
11336
 */
11337
GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11338
                                                     size_t *pnCount)
11339
0
{
11340
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
11341
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
11342
0
    const auto &components = hEDT->m_poImpl->GetComponents();
11343
0
    auto ret = static_cast<GDALEDTComponentH *>(
11344
0
        CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11345
0
    for (size_t i = 0; i < components.size(); i++)
11346
0
    {
11347
0
        ret[i] = new GDALEDTComponentHS(*components[i].get());
11348
0
    }
11349
0
    *pnCount = components.size();
11350
0
    return ret;
11351
0
}
11352
11353
/************************************************************************/
11354
/*                     GDALExtendedDataTypeFreeComponents()             */
11355
/************************************************************************/
11356
11357
/** Free the return of GDALExtendedDataTypeGetComponents().
11358
 *
11359
 * @param components return value of GDALExtendedDataTypeGetComponents()
11360
 * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11361
 */
11362
void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11363
                                        size_t nCount)
11364
0
{
11365
0
    for (size_t i = 0; i < nCount; i++)
11366
0
    {
11367
0
        delete components[i];
11368
0
    }
11369
0
    CPLFree(components);
11370
0
}
11371
11372
/************************************************************************/
11373
/*                         GDALEDTComponentCreate()                     */
11374
/************************************************************************/
11375
11376
/** Create a new GDALEDTComponent.
11377
 *
11378
 * The returned value must be freed with GDALEDTComponentRelease().
11379
 *
11380
 * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11381
 */
11382
GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11383
                                         GDALExtendedDataTypeH hType)
11384
0
{
11385
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11386
0
    VALIDATE_POINTER1(hType, __func__, nullptr);
11387
0
    return new GDALEDTComponentHS(
11388
0
        GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11389
0
}
11390
11391
/************************************************************************/
11392
/*                         GDALEDTComponentRelease()                    */
11393
/************************************************************************/
11394
11395
/** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11396
 *
11397
 * Note: when applied on a object coming from a driver, this does not
11398
 * destroy the object in the file, database, etc...
11399
 */
11400
void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11401
0
{
11402
0
    delete hComp;
11403
0
}
11404
11405
/************************************************************************/
11406
/*                         GDALEDTComponentGetName()                    */
11407
/************************************************************************/
11408
11409
/** Return the name.
11410
 *
11411
 * The returned pointer is valid until hComp is released.
11412
 *
11413
 * This is the same as the C++ method GDALEDTComponent::GetName().
11414
 */
11415
const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11416
0
{
11417
0
    VALIDATE_POINTER1(hComp, __func__, nullptr);
11418
0
    return hComp->m_poImpl->GetName().c_str();
11419
0
}
11420
11421
/************************************************************************/
11422
/*                       GDALEDTComponentGetOffset()                    */
11423
/************************************************************************/
11424
11425
/** Return the offset (in bytes) of the component in the compound data type.
11426
 *
11427
 * This is the same as the C++ method GDALEDTComponent::GetOffset().
11428
 */
11429
size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11430
0
{
11431
0
    VALIDATE_POINTER1(hComp, __func__, 0);
11432
0
    return hComp->m_poImpl->GetOffset();
11433
0
}
11434
11435
/************************************************************************/
11436
/*                       GDALEDTComponentGetType()                      */
11437
/************************************************************************/
11438
11439
/** Return the data type of the component.
11440
 *
11441
 * This is the same as the C++ method GDALEDTComponent::GetType().
11442
 */
11443
GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11444
0
{
11445
0
    VALIDATE_POINTER1(hComp, __func__, nullptr);
11446
0
    return new GDALExtendedDataTypeHS(
11447
0
        new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11448
0
}
11449
11450
/************************************************************************/
11451
/*                           GDALGroupRelease()                         */
11452
/************************************************************************/
11453
11454
/** Release the GDAL in-memory object associated with a GDALGroupH.
11455
 *
11456
 * Note: when applied on a object coming from a driver, this does not
11457
 * destroy the object in the file, database, etc...
11458
 */
11459
void GDALGroupRelease(GDALGroupH hGroup)
11460
0
{
11461
0
    delete hGroup;
11462
0
}
11463
11464
/************************************************************************/
11465
/*                           GDALGroupGetName()                         */
11466
/************************************************************************/
11467
11468
/** Return the name of the group.
11469
 *
11470
 * The returned pointer is valid until hGroup is released.
11471
 *
11472
 * This is the same as the C++ method GDALGroup::GetName().
11473
 */
11474
const char *GDALGroupGetName(GDALGroupH hGroup)
11475
0
{
11476
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11477
0
    return hGroup->m_poImpl->GetName().c_str();
11478
0
}
11479
11480
/************************************************************************/
11481
/*                         GDALGroupGetFullName()                       */
11482
/************************************************************************/
11483
11484
/** Return the full name of the group.
11485
 *
11486
 * The returned pointer is valid until hGroup is released.
11487
 *
11488
 * This is the same as the C++ method GDALGroup::GetFullName().
11489
 */
11490
const char *GDALGroupGetFullName(GDALGroupH hGroup)
11491
0
{
11492
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11493
0
    return hGroup->m_poImpl->GetFullName().c_str();
11494
0
}
11495
11496
/************************************************************************/
11497
/*                          GDALGroupGetMDArrayNames()                  */
11498
/************************************************************************/
11499
11500
/** Return the list of multidimensional array names contained in this group.
11501
 *
11502
 * This is the same as the C++ method GDALGroup::GetGroupNames().
11503
 *
11504
 * @return the array names, to be freed with CSLDestroy()
11505
 */
11506
char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11507
0
{
11508
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11509
0
    auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11510
0
    CPLStringList res;
11511
0
    for (const auto &name : names)
11512
0
    {
11513
0
        res.AddString(name.c_str());
11514
0
    }
11515
0
    return res.StealList();
11516
0
}
11517
11518
/************************************************************************/
11519
/*                  GDALGroupGetMDArrayFullNamesRecursive()             */
11520
/************************************************************************/
11521
11522
/** Return the list of multidimensional array full names contained in this
11523
 * group and its subgroups.
11524
 *
11525
 * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11526
 *
11527
 * @return the array names, to be freed with CSLDestroy()
11528
 *
11529
 * @since 3.11
11530
 */
11531
char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11532
                                             CSLConstList papszGroupOptions,
11533
                                             CSLConstList papszArrayOptions)
11534
0
{
11535
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11536
0
    auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11537
0
        papszGroupOptions, papszArrayOptions);
11538
0
    CPLStringList res;
11539
0
    for (const auto &name : names)
11540
0
    {
11541
0
        res.AddString(name.c_str());
11542
0
    }
11543
0
    return res.StealList();
11544
0
}
11545
11546
/************************************************************************/
11547
/*                          GDALGroupOpenMDArray()                      */
11548
/************************************************************************/
11549
11550
/** Open and return a multidimensional array.
11551
 *
11552
 * This is the same as the C++ method GDALGroup::OpenMDArray().
11553
 *
11554
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11555
 */
11556
GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11557
                                  CSLConstList papszOptions)
11558
0
{
11559
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11560
0
    VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11561
0
    auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11562
0
                                               papszOptions);
11563
0
    if (!array)
11564
0
        return nullptr;
11565
0
    return new GDALMDArrayHS(array);
11566
0
}
11567
11568
/************************************************************************/
11569
/*                  GDALGroupOpenMDArrayFromFullname()                  */
11570
/************************************************************************/
11571
11572
/** Open and return a multidimensional array from its fully qualified name.
11573
 *
11574
 * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11575
 *
11576
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11577
 *
11578
 * @since GDAL 3.2
11579
 */
11580
GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11581
                                              const char *pszFullname,
11582
                                              CSLConstList papszOptions)
11583
0
{
11584
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11585
0
    VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11586
0
    auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11587
0
        std::string(pszFullname), papszOptions);
11588
0
    if (!array)
11589
0
        return nullptr;
11590
0
    return new GDALMDArrayHS(array);
11591
0
}
11592
11593
/************************************************************************/
11594
/*                      GDALGroupResolveMDArray()                       */
11595
/************************************************************************/
11596
11597
/** Locate an array in a group and its subgroups by name.
11598
 *
11599
 * See GDALGroup::ResolveMDArray() for description of the behavior.
11600
 * @since GDAL 3.2
11601
 */
11602
GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11603
                                     const char *pszStartingPoint,
11604
                                     CSLConstList papszOptions)
11605
0
{
11606
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11607
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11608
0
    VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11609
0
    auto array = hGroup->m_poImpl->ResolveMDArray(
11610
0
        std::string(pszName), std::string(pszStartingPoint), papszOptions);
11611
0
    if (!array)
11612
0
        return nullptr;
11613
0
    return new GDALMDArrayHS(array);
11614
0
}
11615
11616
/************************************************************************/
11617
/*                        GDALGroupGetGroupNames()                      */
11618
/************************************************************************/
11619
11620
/** Return the list of sub-groups contained in this group.
11621
 *
11622
 * This is the same as the C++ method GDALGroup::GetGroupNames().
11623
 *
11624
 * @return the group names, to be freed with CSLDestroy()
11625
 */
11626
char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11627
0
{
11628
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11629
0
    auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11630
0
    CPLStringList res;
11631
0
    for (const auto &name : names)
11632
0
    {
11633
0
        res.AddString(name.c_str());
11634
0
    }
11635
0
    return res.StealList();
11636
0
}
11637
11638
/************************************************************************/
11639
/*                           GDALGroupOpenGroup()                       */
11640
/************************************************************************/
11641
11642
/** Open and return a sub-group.
11643
 *
11644
 * This is the same as the C++ method GDALGroup::OpenGroup().
11645
 *
11646
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11647
 */
11648
GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11649
                              CSLConstList papszOptions)
11650
0
{
11651
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11652
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11653
0
    auto subGroup =
11654
0
        hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11655
0
    if (!subGroup)
11656
0
        return nullptr;
11657
0
    return new GDALGroupHS(subGroup);
11658
0
}
11659
11660
/************************************************************************/
11661
/*                   GDALGroupGetVectorLayerNames()                     */
11662
/************************************************************************/
11663
11664
/** Return the list of layer names contained in this group.
11665
 *
11666
 * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11667
 *
11668
 * @return the group names, to be freed with CSLDestroy()
11669
 * @since 3.4
11670
 */
11671
char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11672
                                    CSLConstList papszOptions)
11673
0
{
11674
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11675
0
    auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11676
0
    CPLStringList res;
11677
0
    for (const auto &name : names)
11678
0
    {
11679
0
        res.AddString(name.c_str());
11680
0
    }
11681
0
    return res.StealList();
11682
0
}
11683
11684
/************************************************************************/
11685
/*                      GDALGroupOpenVectorLayer()                      */
11686
/************************************************************************/
11687
11688
/** Open and return a vector layer.
11689
 *
11690
 * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11691
 *
11692
 * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11693
 * the returned handled if only valid while the parent GDALDatasetH is kept
11694
 * opened.
11695
 *
11696
 * @return the vector layer, or nullptr.
11697
 * @since 3.4
11698
 */
11699
OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11700
                                   const char *pszVectorLayerName,
11701
                                   CSLConstList papszOptions)
11702
0
{
11703
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11704
0
    VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11705
0
    return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11706
0
        std::string(pszVectorLayerName), papszOptions));
11707
0
}
11708
11709
/************************************************************************/
11710
/*                       GDALGroupOpenMDArrayFromFullname()             */
11711
/************************************************************************/
11712
11713
/** Open and return a sub-group from its fully qualified name.
11714
 *
11715
 * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11716
 *
11717
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11718
 *
11719
 * @since GDAL 3.2
11720
 */
11721
GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11722
                                          const char *pszFullname,
11723
                                          CSLConstList papszOptions)
11724
0
{
11725
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11726
0
    VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11727
0
    auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11728
0
        std::string(pszFullname), papszOptions);
11729
0
    if (!subGroup)
11730
0
        return nullptr;
11731
0
    return new GDALGroupHS(subGroup);
11732
0
}
11733
11734
/************************************************************************/
11735
/*                         GDALGroupGetDimensions()                     */
11736
/************************************************************************/
11737
11738
/** Return the list of dimensions contained in this group and used by its
11739
 * arrays.
11740
 *
11741
 * The returned array must be freed with GDALReleaseDimensions().  If only the
11742
 * array itself needs to be freed, CPLFree() should be called (and
11743
 * GDALDimensionRelease() on individual array members).
11744
 *
11745
 * This is the same as the C++ method GDALGroup::GetDimensions().
11746
 *
11747
 * @param hGroup Group.
11748
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11749
 * @param papszOptions Driver specific options determining how dimensions
11750
 * should be retrieved. Pass nullptr for default behavior.
11751
 *
11752
 * @return an array of *pnCount dimensions.
11753
 */
11754
GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11755
                                       CSLConstList papszOptions)
11756
0
{
11757
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11758
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
11759
0
    auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11760
0
    auto ret = static_cast<GDALDimensionH *>(
11761
0
        CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11762
0
    for (size_t i = 0; i < dims.size(); i++)
11763
0
    {
11764
0
        ret[i] = new GDALDimensionHS(dims[i]);
11765
0
    }
11766
0
    *pnCount = dims.size();
11767
0
    return ret;
11768
0
}
11769
11770
/************************************************************************/
11771
/*                          GDALGroupGetAttribute()                     */
11772
/************************************************************************/
11773
11774
/** Return an attribute by its name.
11775
 *
11776
 * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11777
 *
11778
 * The returned attribute must be freed with GDALAttributeRelease().
11779
 */
11780
GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11781
0
{
11782
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11783
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11784
0
    auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11785
0
    if (attr)
11786
0
        return new GDALAttributeHS(attr);
11787
0
    return nullptr;
11788
0
}
11789
11790
/************************************************************************/
11791
/*                         GDALGroupGetAttributes()                     */
11792
/************************************************************************/
11793
11794
/** Return the list of attributes contained in this group.
11795
 *
11796
 * The returned array must be freed with GDALReleaseAttributes(). If only the
11797
 * array itself needs to be freed, CPLFree() should be called (and
11798
 * GDALAttributeRelease() on individual array members).
11799
 *
11800
 * This is the same as the C++ method GDALGroup::GetAttributes().
11801
 *
11802
 * @param hGroup Group.
11803
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11804
 * @param papszOptions Driver specific options determining how attributes
11805
 * should be retrieved. Pass nullptr for default behavior.
11806
 *
11807
 * @return an array of *pnCount attributes.
11808
 */
11809
GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11810
                                       CSLConstList papszOptions)
11811
0
{
11812
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11813
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
11814
0
    auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
11815
0
    auto ret = static_cast<GDALAttributeH *>(
11816
0
        CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
11817
0
    for (size_t i = 0; i < attrs.size(); i++)
11818
0
    {
11819
0
        ret[i] = new GDALAttributeHS(attrs[i]);
11820
0
    }
11821
0
    *pnCount = attrs.size();
11822
0
    return ret;
11823
0
}
11824
11825
/************************************************************************/
11826
/*                     GDALGroupGetStructuralInfo()                     */
11827
/************************************************************************/
11828
11829
/** Return structural information on the group.
11830
 *
11831
 * This may be the compression, etc..
11832
 *
11833
 * The return value should not be freed and is valid until GDALGroup is
11834
 * released or this function called again.
11835
 *
11836
 * This is the same as the C++ method GDALGroup::GetStructuralInfo().
11837
 */
11838
CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
11839
0
{
11840
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11841
0
    return hGroup->m_poImpl->GetStructuralInfo();
11842
0
}
11843
11844
/************************************************************************/
11845
/*                   GDALGroupGetDataTypeCount()                        */
11846
/************************************************************************/
11847
11848
/** Return the number of data types associated with the group
11849
 * (typically enumerations).
11850
 *
11851
 * This is the same as the C++ method GDALGroup::GetDataTypes().size().
11852
 *
11853
 * @since 3.12
11854
 */
11855
size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
11856
0
{
11857
0
    VALIDATE_POINTER1(hGroup, __func__, 0);
11858
0
    return hGroup->m_poImpl->GetDataTypes().size();
11859
0
}
11860
11861
/************************************************************************/
11862
/*                      GDALGroupGetDataType()                          */
11863
/************************************************************************/
11864
11865
/** Return one of the data types associated with the group.
11866
 *
11867
 * This is the same as the C++ method GDALGroup::GetDataTypes()[].
11868
 *
11869
 * @return a type to release with GDALExtendedDataTypeRelease() once done,
11870
 * or nullptr in case of error.
11871
 * @since 3.12
11872
 */
11873
GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
11874
0
{
11875
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11876
0
    if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
11877
0
        return nullptr;
11878
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11879
0
        *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
11880
0
}
11881
11882
/************************************************************************/
11883
/*                         GDALReleaseAttributes()                      */
11884
/************************************************************************/
11885
11886
/** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
11887
 *
11888
 * @param attributes return pointer of above methods
11889
 * @param nCount *pnCount value returned by above methods
11890
 */
11891
void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
11892
0
{
11893
0
    for (size_t i = 0; i < nCount; i++)
11894
0
    {
11895
0
        delete attributes[i];
11896
0
    }
11897
0
    CPLFree(attributes);
11898
0
}
11899
11900
/************************************************************************/
11901
/*                         GDALGroupCreateGroup()                       */
11902
/************************************************************************/
11903
11904
/** Create a sub-group within a group.
11905
 *
11906
 * This is the same as the C++ method GDALGroup::CreateGroup().
11907
 *
11908
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11909
 */
11910
GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11911
                                CSLConstList papszOptions)
11912
0
{
11913
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11914
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11915
0
    auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
11916
0
                                             papszOptions);
11917
0
    if (!ret)
11918
0
        return nullptr;
11919
0
    return new GDALGroupHS(ret);
11920
0
}
11921
11922
/************************************************************************/
11923
/*                         GDALGroupDeleteGroup()                       */
11924
/************************************************************************/
11925
11926
/** Delete a sub-group from a group.
11927
 *
11928
 * After this call, if a previously obtained instance of the deleted object
11929
 * is still alive, no method other than for freeing it should be invoked.
11930
 *
11931
 * This is the same as the C++ method GDALGroup::DeleteGroup().
11932
 *
11933
 * @return true in case of success.
11934
 * @since GDAL 3.8
11935
 */
11936
bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11937
                          CSLConstList papszOptions)
11938
0
{
11939
0
    VALIDATE_POINTER1(hGroup, __func__, false);
11940
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, false);
11941
0
    return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
11942
0
                                         papszOptions);
11943
0
}
11944
11945
/************************************************************************/
11946
/*                      GDALGroupCreateDimension()                      */
11947
/************************************************************************/
11948
11949
/** Create a dimension within a group.
11950
 *
11951
 * This is the same as the C++ method GDALGroup::CreateDimension().
11952
 *
11953
 * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
11954
 */
11955
GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
11956
                                        const char *pszType,
11957
                                        const char *pszDirection, GUInt64 nSize,
11958
                                        CSLConstList papszOptions)
11959
0
{
11960
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11961
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11962
0
    auto ret = hGroup->m_poImpl->CreateDimension(
11963
0
        std::string(pszName), std::string(pszType ? pszType : ""),
11964
0
        std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
11965
0
    if (!ret)
11966
0
        return nullptr;
11967
0
    return new GDALDimensionHS(ret);
11968
0
}
11969
11970
/************************************************************************/
11971
/*                      GDALGroupCreateMDArray()                        */
11972
/************************************************************************/
11973
11974
/** Create a multidimensional array within a group.
11975
 *
11976
 * This is the same as the C++ method GDALGroup::CreateMDArray().
11977
 *
11978
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11979
 */
11980
GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
11981
                                    size_t nDimensions,
11982
                                    GDALDimensionH *pahDimensions,
11983
                                    GDALExtendedDataTypeH hEDT,
11984
                                    CSLConstList papszOptions)
11985
0
{
11986
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11987
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11988
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
11989
0
    std::vector<std::shared_ptr<GDALDimension>> dims;
11990
0
    dims.reserve(nDimensions);
11991
0
    for (size_t i = 0; i < nDimensions; i++)
11992
0
        dims.push_back(pahDimensions[i]->m_poImpl);
11993
0
    auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
11994
0
                                               *(hEDT->m_poImpl), papszOptions);
11995
0
    if (!ret)
11996
0
        return nullptr;
11997
0
    return new GDALMDArrayHS(ret);
11998
0
}
11999
12000
/************************************************************************/
12001
/*                         GDALGroupDeleteMDArray()                     */
12002
/************************************************************************/
12003
12004
/** Delete an array from a group.
12005
 *
12006
 * After this call, if a previously obtained instance of the deleted object
12007
 * is still alive, no method other than for freeing it should be invoked.
12008
 *
12009
 * This is the same as the C++ method GDALGroup::DeleteMDArray().
12010
 *
12011
 * @return true in case of success.
12012
 * @since GDAL 3.8
12013
 */
12014
bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12015
                            CSLConstList papszOptions)
12016
0
{
12017
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12018
0
    VALIDATE_POINTER1(pszName, __func__, false);
12019
0
    return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12020
0
}
12021
12022
/************************************************************************/
12023
/*                      GDALGroupCreateAttribute()                      */
12024
/************************************************************************/
12025
12026
/** Create a attribute within a group.
12027
 *
12028
 * This is the same as the C++ method GDALGroup::CreateAttribute().
12029
 *
12030
 * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12031
 */
12032
GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12033
                                        size_t nDimensions,
12034
                                        const GUInt64 *panDimensions,
12035
                                        GDALExtendedDataTypeH hEDT,
12036
                                        CSLConstList papszOptions)
12037
0
{
12038
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12039
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
12040
0
    std::vector<GUInt64> dims;
12041
0
    dims.reserve(nDimensions);
12042
0
    for (size_t i = 0; i < nDimensions; i++)
12043
0
        dims.push_back(panDimensions[i]);
12044
0
    auto ret = hGroup->m_poImpl->CreateAttribute(
12045
0
        std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12046
0
    if (!ret)
12047
0
        return nullptr;
12048
0
    return new GDALAttributeHS(ret);
12049
0
}
12050
12051
/************************************************************************/
12052
/*                         GDALGroupDeleteAttribute()                   */
12053
/************************************************************************/
12054
12055
/** Delete an attribute from a group.
12056
 *
12057
 * After this call, if a previously obtained instance of the deleted object
12058
 * is still alive, no method other than for freeing it should be invoked.
12059
 *
12060
 * This is the same as the C++ method GDALGroup::DeleteAttribute().
12061
 *
12062
 * @return true in case of success.
12063
 * @since GDAL 3.8
12064
 */
12065
bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12066
                              CSLConstList papszOptions)
12067
0
{
12068
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12069
0
    VALIDATE_POINTER1(pszName, __func__, false);
12070
0
    return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12071
0
                                             papszOptions);
12072
0
}
12073
12074
/************************************************************************/
12075
/*                          GDALGroupRename()                           */
12076
/************************************************************************/
12077
12078
/** Rename the group.
12079
 *
12080
 * This is not implemented by all drivers.
12081
 *
12082
 * Drivers known to implement it: MEM, netCDF.
12083
 *
12084
 * This is the same as the C++ method GDALGroup::Rename()
12085
 *
12086
 * @return true in case of success
12087
 * @since GDAL 3.8
12088
 */
12089
bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12090
0
{
12091
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12092
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
12093
0
    return hGroup->m_poImpl->Rename(pszNewName);
12094
0
}
12095
12096
/************************************************************************/
12097
/*                 GDALGroupSubsetDimensionFromSelection()              */
12098
/************************************************************************/
12099
12100
/** Return a virtual group whose one dimension has been subset according to a
12101
 * selection.
12102
 *
12103
 * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12104
 *
12105
 * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12106
 */
12107
GDALGroupH
12108
GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12109
                                      const char *pszSelection,
12110
                                      CPL_UNUSED CSLConstList papszOptions)
12111
0
{
12112
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12113
0
    VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12114
0
    auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12115
0
        std::string(pszSelection));
12116
0
    if (!hNewGroup)
12117
0
        return nullptr;
12118
0
    return new GDALGroupHS(hNewGroup);
12119
0
}
12120
12121
/************************************************************************/
12122
/*                        GDALMDArrayRelease()                          */
12123
/************************************************************************/
12124
12125
/** Release the GDAL in-memory object associated with a GDALMDArray.
12126
 *
12127
 * Note: when applied on a object coming from a driver, this does not
12128
 * destroy the object in the file, database, etc...
12129
 */
12130
void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12131
0
{
12132
0
    delete hMDArray;
12133
0
}
12134
12135
/************************************************************************/
12136
/*                        GDALMDArrayGetName()                          */
12137
/************************************************************************/
12138
12139
/** Return array name.
12140
 *
12141
 * This is the same as the C++ method GDALMDArray::GetName()
12142
 */
12143
const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12144
0
{
12145
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12146
0
    return hArray->m_poImpl->GetName().c_str();
12147
0
}
12148
12149
/************************************************************************/
12150
/*                    GDALMDArrayGetFullName()                          */
12151
/************************************************************************/
12152
12153
/** Return array full name.
12154
 *
12155
 * This is the same as the C++ method GDALMDArray::GetFullName()
12156
 */
12157
const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12158
0
{
12159
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12160
0
    return hArray->m_poImpl->GetFullName().c_str();
12161
0
}
12162
12163
/************************************************************************/
12164
/*                        GDALMDArrayGetName()                          */
12165
/************************************************************************/
12166
12167
/** Return the total number of values in the array.
12168
 *
12169
 * This is the same as the C++ method
12170
 * GDALAbstractMDArray::GetTotalElementsCount()
12171
 */
12172
GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12173
0
{
12174
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12175
0
    return hArray->m_poImpl->GetTotalElementsCount();
12176
0
}
12177
12178
/************************************************************************/
12179
/*                        GDALMDArrayGetDimensionCount()                */
12180
/************************************************************************/
12181
12182
/** Return the number of dimensions.
12183
 *
12184
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12185
 */
12186
size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12187
0
{
12188
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12189
0
    return hArray->m_poImpl->GetDimensionCount();
12190
0
}
12191
12192
/************************************************************************/
12193
/*                        GDALMDArrayGetDimensions()                    */
12194
/************************************************************************/
12195
12196
/** Return the dimensions of the array
12197
 *
12198
 * The returned array must be freed with GDALReleaseDimensions(). If only the
12199
 * array itself needs to be freed, CPLFree() should be called (and
12200
 * GDALDimensionRelease() on individual array members).
12201
 *
12202
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12203
 *
12204
 * @param hArray Array.
12205
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12206
 *
12207
 * @return an array of *pnCount dimensions.
12208
 */
12209
GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12210
0
{
12211
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12212
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12213
0
    const auto &dims(hArray->m_poImpl->GetDimensions());
12214
0
    auto ret = static_cast<GDALDimensionH *>(
12215
0
        CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12216
0
    for (size_t i = 0; i < dims.size(); i++)
12217
0
    {
12218
0
        ret[i] = new GDALDimensionHS(dims[i]);
12219
0
    }
12220
0
    *pnCount = dims.size();
12221
0
    return ret;
12222
0
}
12223
12224
/************************************************************************/
12225
/*                        GDALReleaseDimensions()                       */
12226
/************************************************************************/
12227
12228
/** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12229
 *
12230
 * @param dims return pointer of above methods
12231
 * @param nCount *pnCount value returned by above methods
12232
 */
12233
void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12234
0
{
12235
0
    for (size_t i = 0; i < nCount; i++)
12236
0
    {
12237
0
        delete dims[i];
12238
0
    }
12239
0
    CPLFree(dims);
12240
0
}
12241
12242
/************************************************************************/
12243
/*                        GDALMDArrayGetDataType()                     */
12244
/************************************************************************/
12245
12246
/** Return the data type
12247
 *
12248
 * The return must be freed with GDALExtendedDataTypeRelease().
12249
 */
12250
GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12251
0
{
12252
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12253
0
    return new GDALExtendedDataTypeHS(
12254
0
        new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12255
0
}
12256
12257
/************************************************************************/
12258
/*                          GDALMDArrayRead()                           */
12259
/************************************************************************/
12260
12261
/** Read part or totality of a multidimensional array.
12262
 *
12263
 * This is the same as the C++ method GDALAbstractMDArray::Read()
12264
 *
12265
 * @return TRUE in case of success.
12266
 */
12267
int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12268
                    const size_t *count, const GInt64 *arrayStep,
12269
                    const GPtrDiff_t *bufferStride,
12270
                    GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12271
                    const void *pDstBufferAllocStart,
12272
                    size_t nDstBufferAllocSize)
12273
0
{
12274
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12275
0
    if ((arrayStartIdx == nullptr || count == nullptr) &&
12276
0
        hArray->m_poImpl->GetDimensionCount() > 0)
12277
0
    {
12278
0
        VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12279
0
        VALIDATE_POINTER1(count, __func__, FALSE);
12280
0
    }
12281
0
    VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12282
0
    VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12283
0
    return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12284
0
                                  *(bufferDataType->m_poImpl), pDstBuffer,
12285
0
                                  pDstBufferAllocStart, nDstBufferAllocSize);
12286
0
}
12287
12288
/************************************************************************/
12289
/*                          GDALMDArrayWrite()                           */
12290
/************************************************************************/
12291
12292
/** Write part or totality of a multidimensional array.
12293
 *
12294
 * This is the same as the C++ method GDALAbstractMDArray::Write()
12295
 *
12296
 * @return TRUE in case of success.
12297
 */
12298
int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12299
                     const size_t *count, const GInt64 *arrayStep,
12300
                     const GPtrDiff_t *bufferStride,
12301
                     GDALExtendedDataTypeH bufferDataType,
12302
                     const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12303
                     size_t nSrcBufferAllocSize)
12304
0
{
12305
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12306
0
    if ((arrayStartIdx == nullptr || count == nullptr) &&
12307
0
        hArray->m_poImpl->GetDimensionCount() > 0)
12308
0
    {
12309
0
        VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12310
0
        VALIDATE_POINTER1(count, __func__, FALSE);
12311
0
    }
12312
0
    VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12313
0
    VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12314
0
    return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12315
0
                                   bufferStride, *(bufferDataType->m_poImpl),
12316
0
                                   pSrcBuffer, pSrcBufferAllocStart,
12317
0
                                   nSrcBufferAllocSize);
12318
0
}
12319
12320
/************************************************************************/
12321
/*                       GDALMDArrayAdviseRead()                        */
12322
/************************************************************************/
12323
12324
/** Advise driver of upcoming read requests.
12325
 *
12326
 * This is the same as the C++ method GDALMDArray::AdviseRead()
12327
 *
12328
 * @return TRUE in case of success.
12329
 *
12330
 * @since GDAL 3.2
12331
 */
12332
int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12333
                          const size_t *count)
12334
0
{
12335
0
    return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12336
0
}
12337
12338
/************************************************************************/
12339
/*                      GDALMDArrayAdviseReadEx()                       */
12340
/************************************************************************/
12341
12342
/** Advise driver of upcoming read requests.
12343
 *
12344
 * This is the same as the C++ method GDALMDArray::AdviseRead()
12345
 *
12346
 * @return TRUE in case of success.
12347
 *
12348
 * @since GDAL 3.4
12349
 */
12350
int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12351
                            const size_t *count, CSLConstList papszOptions)
12352
0
{
12353
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12354
0
    return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12355
0
}
12356
12357
/************************************************************************/
12358
/*                         GDALMDArrayGetAttribute()                    */
12359
/************************************************************************/
12360
12361
/** Return an attribute by its name.
12362
 *
12363
 * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12364
 *
12365
 * The returned attribute must be freed with GDALAttributeRelease().
12366
 */
12367
GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12368
0
{
12369
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12370
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12371
0
    auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12372
0
    if (attr)
12373
0
        return new GDALAttributeHS(attr);
12374
0
    return nullptr;
12375
0
}
12376
12377
/************************************************************************/
12378
/*                        GDALMDArrayGetAttributes()                    */
12379
/************************************************************************/
12380
12381
/** Return the list of attributes contained in this array.
12382
 *
12383
 * The returned array must be freed with GDALReleaseAttributes(). If only the
12384
 * array itself needs to be freed, CPLFree() should be called (and
12385
 * GDALAttributeRelease() on individual array members).
12386
 *
12387
 * This is the same as the C++ method GDALMDArray::GetAttributes().
12388
 *
12389
 * @param hArray Array.
12390
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12391
 * @param papszOptions Driver specific options determining how attributes
12392
 * should be retrieved. Pass nullptr for default behavior.
12393
 *
12394
 * @return an array of *pnCount attributes.
12395
 */
12396
GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12397
                                         CSLConstList papszOptions)
12398
0
{
12399
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12400
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12401
0
    auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12402
0
    auto ret = static_cast<GDALAttributeH *>(
12403
0
        CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12404
0
    for (size_t i = 0; i < attrs.size(); i++)
12405
0
    {
12406
0
        ret[i] = new GDALAttributeHS(attrs[i]);
12407
0
    }
12408
0
    *pnCount = attrs.size();
12409
0
    return ret;
12410
0
}
12411
12412
/************************************************************************/
12413
/*                       GDALMDArrayCreateAttribute()                   */
12414
/************************************************************************/
12415
12416
/** Create a attribute within an array.
12417
 *
12418
 * This is the same as the C++ method GDALMDArray::CreateAttribute().
12419
 *
12420
 * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12421
 */
12422
GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12423
                                          const char *pszName,
12424
                                          size_t nDimensions,
12425
                                          const GUInt64 *panDimensions,
12426
                                          GDALExtendedDataTypeH hEDT,
12427
                                          CSLConstList papszOptions)
12428
0
{
12429
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12430
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12431
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
12432
0
    std::vector<GUInt64> dims;
12433
0
    dims.reserve(nDimensions);
12434
0
    for (size_t i = 0; i < nDimensions; i++)
12435
0
        dims.push_back(panDimensions[i]);
12436
0
    auto ret = hArray->m_poImpl->CreateAttribute(
12437
0
        std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12438
0
    if (!ret)
12439
0
        return nullptr;
12440
0
    return new GDALAttributeHS(ret);
12441
0
}
12442
12443
/************************************************************************/
12444
/*                       GDALMDArrayDeleteAttribute()                   */
12445
/************************************************************************/
12446
12447
/** Delete an attribute from an array.
12448
 *
12449
 * After this call, if a previously obtained instance of the deleted object
12450
 * is still alive, no method other than for freeing it should be invoked.
12451
 *
12452
 * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12453
 *
12454
 * @return true in case of success.
12455
 * @since GDAL 3.8
12456
 */
12457
bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12458
                                CSLConstList papszOptions)
12459
0
{
12460
0
    VALIDATE_POINTER1(hArray, __func__, false);
12461
0
    VALIDATE_POINTER1(pszName, __func__, false);
12462
0
    return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12463
0
                                             papszOptions);
12464
0
}
12465
12466
/************************************************************************/
12467
/*                       GDALMDArrayGetRawNoDataValue()                 */
12468
/************************************************************************/
12469
12470
/** Return the nodata value as a "raw" value.
12471
 *
12472
 * The value returned might be nullptr in case of no nodata value. When
12473
 * a nodata value is registered, a non-nullptr will be returned whose size in
12474
 * bytes is GetDataType().GetSize().
12475
 *
12476
 * The returned value should not be modified or freed.
12477
 *
12478
 * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12479
 *
12480
 * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12481
 */
12482
const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12483
0
{
12484
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12485
0
    return hArray->m_poImpl->GetRawNoDataValue();
12486
0
}
12487
12488
/************************************************************************/
12489
/*                      GDALMDArrayGetNoDataValueAsDouble()             */
12490
/************************************************************************/
12491
12492
/** Return the nodata value as a double.
12493
 *
12494
 * The value returned might be nullptr in case of no nodata value. When
12495
 * a nodata value is registered, a non-nullptr will be returned whose size in
12496
 * bytes is GetDataType().GetSize().
12497
 *
12498
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12499
 *
12500
 * @param hArray Array handle.
12501
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12502
 * if a nodata value exists and can be converted to double. Might be nullptr.
12503
 *
12504
 * @return the nodata value as a double. A 0.0 value might also indicate the
12505
 * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12506
 * will be set to false then).
12507
 */
12508
double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12509
                                         int *pbHasNoDataValue)
12510
0
{
12511
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12512
0
    bool bHasNodataValue = false;
12513
0
    double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12514
0
    if (pbHasNoDataValue)
12515
0
        *pbHasNoDataValue = bHasNodataValue;
12516
0
    return ret;
12517
0
}
12518
12519
/************************************************************************/
12520
/*                      GDALMDArrayGetNoDataValueAsInt64()              */
12521
/************************************************************************/
12522
12523
/** Return the nodata value as a Int64.
12524
 *
12525
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12526
 *
12527
 * @param hArray Array handle.
12528
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12529
 * if a nodata value exists and can be converted to Int64. Might be nullptr.
12530
 *
12531
 * @return the nodata value as a Int64.
12532
 * @since GDAL 3.5
12533
 */
12534
int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12535
                                         int *pbHasNoDataValue)
12536
0
{
12537
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12538
0
    bool bHasNodataValue = false;
12539
0
    const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12540
0
    if (pbHasNoDataValue)
12541
0
        *pbHasNoDataValue = bHasNodataValue;
12542
0
    return ret;
12543
0
}
12544
12545
/************************************************************************/
12546
/*                      GDALMDArrayGetNoDataValueAsUInt64()              */
12547
/************************************************************************/
12548
12549
/** Return the nodata value as a UInt64.
12550
 *
12551
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12552
 *
12553
 * @param hArray Array handle.
12554
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12555
 * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12556
 *
12557
 * @return the nodata value as a UInt64.
12558
 * @since GDAL 3.5
12559
 */
12560
uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12561
                                           int *pbHasNoDataValue)
12562
0
{
12563
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12564
0
    bool bHasNodataValue = false;
12565
0
    const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12566
0
    if (pbHasNoDataValue)
12567
0
        *pbHasNoDataValue = bHasNodataValue;
12568
0
    return ret;
12569
0
}
12570
12571
/************************************************************************/
12572
/*                     GDALMDArraySetRawNoDataValue()                   */
12573
/************************************************************************/
12574
12575
/** Set the nodata value as a "raw" value.
12576
 *
12577
 * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12578
 * void*).
12579
 *
12580
 * @return TRUE in case of success.
12581
 */
12582
int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12583
0
{
12584
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12585
0
    return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12586
0
}
12587
12588
/************************************************************************/
12589
/*                   GDALMDArraySetNoDataValueAsDouble()                */
12590
/************************************************************************/
12591
12592
/** Set the nodata value as a double.
12593
 *
12594
 * If the natural data type of the attribute/array is not double, type
12595
 * conversion will occur to the type returned by GetDataType().
12596
 *
12597
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12598
 *
12599
 * @return TRUE in case of success.
12600
 */
12601
int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12602
0
{
12603
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12604
0
    return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12605
0
}
12606
12607
/************************************************************************/
12608
/*                   GDALMDArraySetNoDataValueAsInt64()                 */
12609
/************************************************************************/
12610
12611
/** Set the nodata value as a Int64.
12612
 *
12613
 * If the natural data type of the attribute/array is not Int64, type conversion
12614
 * will occur to the type returned by GetDataType().
12615
 *
12616
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12617
 *
12618
 * @return TRUE in case of success.
12619
 * @since GDAL 3.5
12620
 */
12621
int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12622
0
{
12623
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12624
0
    return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12625
0
}
12626
12627
/************************************************************************/
12628
/*                   GDALMDArraySetNoDataValueAsUInt64()                */
12629
/************************************************************************/
12630
12631
/** Set the nodata value as a UInt64.
12632
 *
12633
 * If the natural data type of the attribute/array is not UInt64, type
12634
 * conversion will occur to the type returned by GetDataType().
12635
 *
12636
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12637
 *
12638
 * @return TRUE in case of success.
12639
 * @since GDAL 3.5
12640
 */
12641
int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12642
                                      uint64_t nNoDataValue)
12643
0
{
12644
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12645
0
    return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12646
0
}
12647
12648
/************************************************************************/
12649
/*                        GDALMDArrayResize()                           */
12650
/************************************************************************/
12651
12652
/** Resize an array to new dimensions.
12653
 *
12654
 * Not all drivers may allow this operation, and with restrictions (e.g.
12655
 * for netCDF, this is limited to growing of "unlimited" dimensions)
12656
 *
12657
 * Resizing a dimension used in other arrays will cause those other arrays
12658
 * to be resized.
12659
 *
12660
 * This is the same as the C++ method GDALMDArray::Resize().
12661
 *
12662
 * @param hArray Array.
12663
 * @param panNewDimSizes Array of GetDimensionCount() values containing the
12664
 *                       new size of each indexing dimension.
12665
 * @param papszOptions Options. (Driver specific)
12666
 * @return true in case of success.
12667
 * @since GDAL 3.7
12668
 */
12669
bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12670
                       CSLConstList papszOptions)
12671
0
{
12672
0
    VALIDATE_POINTER1(hArray, __func__, false);
12673
0
    VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12674
0
    std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12675
0
    for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12676
0
    {
12677
0
        anNewDimSizes[i] = panNewDimSizes[i];
12678
0
    }
12679
0
    return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12680
0
}
12681
12682
/************************************************************************/
12683
/*                          GDALMDArraySetScale()                       */
12684
/************************************************************************/
12685
12686
/** Set the scale value to apply to raw values.
12687
 *
12688
 * unscaled_value = raw_value * GetScale() + GetOffset()
12689
 *
12690
 * This is the same as the C++ method GDALMDArray::SetScale().
12691
 *
12692
 * @return TRUE in case of success.
12693
 */
12694
int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12695
0
{
12696
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12697
0
    return hArray->m_poImpl->SetScale(dfScale);
12698
0
}
12699
12700
/************************************************************************/
12701
/*                        GDALMDArraySetScaleEx()                       */
12702
/************************************************************************/
12703
12704
/** Set the scale value to apply to raw values.
12705
 *
12706
 * unscaled_value = raw_value * GetScale() + GetOffset()
12707
 *
12708
 * This is the same as the C++ method GDALMDArray::SetScale().
12709
 *
12710
 * @return TRUE in case of success.
12711
 * @since GDAL 3.3
12712
 */
12713
int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12714
                          GDALDataType eStorageType)
12715
0
{
12716
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12717
0
    return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12718
0
}
12719
12720
/************************************************************************/
12721
/*                          GDALMDArraySetOffset()                       */
12722
/************************************************************************/
12723
12724
/** Set the scale value to apply to raw values.
12725
 *
12726
 * unscaled_value = raw_value * GetScale() + GetOffset()
12727
 *
12728
 * This is the same as the C++ method GDALMDArray::SetOffset().
12729
 *
12730
 * @return TRUE in case of success.
12731
 */
12732
int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12733
0
{
12734
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12735
0
    return hArray->m_poImpl->SetOffset(dfOffset);
12736
0
}
12737
12738
/************************************************************************/
12739
/*                       GDALMDArraySetOffsetEx()                       */
12740
/************************************************************************/
12741
12742
/** Set the scale value to apply to raw values.
12743
 *
12744
 * unscaled_value = raw_value * GetOffset() + GetOffset()
12745
 *
12746
 * This is the same as the C++ method GDALMDArray::SetOffset().
12747
 *
12748
 * @return TRUE in case of success.
12749
 * @since GDAL 3.3
12750
 */
12751
int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12752
                           GDALDataType eStorageType)
12753
0
{
12754
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12755
0
    return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12756
0
}
12757
12758
/************************************************************************/
12759
/*                          GDALMDArrayGetScale()                       */
12760
/************************************************************************/
12761
12762
/** Get the scale value to apply to raw values.
12763
 *
12764
 * unscaled_value = raw_value * GetScale() + GetOffset()
12765
 *
12766
 * This is the same as the C++ method GDALMDArray::GetScale().
12767
 *
12768
 * @return the scale value
12769
 */
12770
double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12771
0
{
12772
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12773
0
    bool bHasValue = false;
12774
0
    double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12775
0
    if (pbHasValue)
12776
0
        *pbHasValue = bHasValue;
12777
0
    return dfRet;
12778
0
}
12779
12780
/************************************************************************/
12781
/*                        GDALMDArrayGetScaleEx()                       */
12782
/************************************************************************/
12783
12784
/** Get the scale value to apply to raw values.
12785
 *
12786
 * unscaled_value = raw_value * GetScale() + GetScale()
12787
 *
12788
 * This is the same as the C++ method GDALMDArray::GetScale().
12789
 *
12790
 * @return the scale value
12791
 * @since GDAL 3.3
12792
 */
12793
double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12794
                             GDALDataType *peStorageType)
12795
0
{
12796
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12797
0
    bool bHasValue = false;
12798
0
    double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12799
0
    if (pbHasValue)
12800
0
        *pbHasValue = bHasValue;
12801
0
    return dfRet;
12802
0
}
12803
12804
/************************************************************************/
12805
/*                          GDALMDArrayGetOffset()                      */
12806
/************************************************************************/
12807
12808
/** Get the scale value to apply to raw values.
12809
 *
12810
 * unscaled_value = raw_value * GetScale() + GetOffset()
12811
 *
12812
 * This is the same as the C++ method GDALMDArray::GetOffset().
12813
 *
12814
 * @return the scale value
12815
 */
12816
double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
12817
0
{
12818
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12819
0
    bool bHasValue = false;
12820
0
    double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
12821
0
    if (pbHasValue)
12822
0
        *pbHasValue = bHasValue;
12823
0
    return dfRet;
12824
0
}
12825
12826
/************************************************************************/
12827
/*                        GDALMDArrayGetOffsetEx()                      */
12828
/************************************************************************/
12829
12830
/** Get the scale value to apply to raw values.
12831
 *
12832
 * unscaled_value = raw_value * GetScale() + GetOffset()
12833
 *
12834
 * This is the same as the C++ method GDALMDArray::GetOffset().
12835
 *
12836
 * @return the scale value
12837
 * @since GDAL 3.3
12838
 */
12839
double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
12840
                              GDALDataType *peStorageType)
12841
0
{
12842
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12843
0
    bool bHasValue = false;
12844
0
    double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
12845
0
    if (pbHasValue)
12846
0
        *pbHasValue = bHasValue;
12847
0
    return dfRet;
12848
0
}
12849
12850
/************************************************************************/
12851
/*                      GDALMDArrayGetBlockSize()                       */
12852
/************************************************************************/
12853
12854
/** Return the "natural" block size of the array along all dimensions.
12855
 *
12856
 * Some drivers might organize the array in tiles/blocks and reading/writing
12857
 * aligned on those tile/block boundaries will be more efficient.
12858
 *
12859
 * The returned number of elements in the vector is the same as
12860
 * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
12861
 * the natural block size along the considered dimension.
12862
 * "Flat" arrays will typically return a vector of values set to 0.
12863
 *
12864
 * The default implementation will return a vector of values set to 0.
12865
 *
12866
 * This method is used by GetProcessingChunkSize().
12867
 *
12868
 * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
12869
 * theoretical case of a 32-bit platform, this might exceed its size_t
12870
 * allocation capabilities.
12871
 *
12872
 * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
12873
 *
12874
 * @return the block size, in number of elements along each dimension.
12875
 */
12876
GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
12877
0
{
12878
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12879
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12880
0
    auto res = hArray->m_poImpl->GetBlockSize();
12881
0
    auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
12882
0
    for (size_t i = 0; i < res.size(); i++)
12883
0
    {
12884
0
        ret[i] = res[i];
12885
0
    }
12886
0
    *pnCount = res.size();
12887
0
    return ret;
12888
0
}
12889
12890
/***********************************************************************/
12891
/*                   GDALMDArrayGetProcessingChunkSize()               */
12892
/************************************************************************/
12893
12894
/** \brief Return an optimal chunk size for read/write operations, given the
12895
 * natural block size and memory constraints specified.
12896
 *
12897
 * This method will use GetBlockSize() to define a chunk whose dimensions are
12898
 * multiple of those returned by GetBlockSize() (unless the block define by
12899
 * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
12900
 * returned by this method).
12901
 *
12902
 * This is the same as the C++ method
12903
 * GDALAbstractMDArray::GetProcessingChunkSize().
12904
 *
12905
 * @param hArray Array.
12906
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12907
 * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
12908
 * chunk.
12909
 *
12910
 * @return the chunk size, in number of elements along each dimension.
12911
 */
12912
12913
size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
12914
                                          size_t nMaxChunkMemory)
12915
0
{
12916
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12917
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12918
0
    auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
12919
0
    auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
12920
0
    for (size_t i = 0; i < res.size(); i++)
12921
0
    {
12922
0
        ret[i] = res[i];
12923
0
    }
12924
0
    *pnCount = res.size();
12925
0
    return ret;
12926
0
}
12927
12928
/************************************************************************/
12929
/*                     GDALMDArrayGetStructuralInfo()                   */
12930
/************************************************************************/
12931
12932
/** Return structural information on the array.
12933
 *
12934
 * This may be the compression, etc..
12935
 *
12936
 * The return value should not be freed and is valid until GDALMDArray is
12937
 * released or this function called again.
12938
 *
12939
 * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
12940
 */
12941
CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
12942
0
{
12943
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12944
0
    return hArray->m_poImpl->GetStructuralInfo();
12945
0
}
12946
12947
/************************************************************************/
12948
/*                        GDALMDArrayGetView()                          */
12949
/************************************************************************/
12950
12951
/** Return a view of the array using slicing or field access.
12952
 *
12953
 * The returned object should be released with GDALMDArrayRelease().
12954
 *
12955
 * This is the same as the C++ method GDALMDArray::GetView().
12956
 */
12957
GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
12958
0
{
12959
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12960
0
    VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
12961
0
    auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
12962
0
    if (!sliced)
12963
0
        return nullptr;
12964
0
    return new GDALMDArrayHS(sliced);
12965
0
}
12966
12967
/************************************************************************/
12968
/*                       GDALMDArrayTranspose()                         */
12969
/************************************************************************/
12970
12971
/** Return a view of the array whose axis have been reordered.
12972
 *
12973
 * The returned object should be released with GDALMDArrayRelease().
12974
 *
12975
 * This is the same as the C++ method GDALMDArray::Transpose().
12976
 */
12977
GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
12978
                                  const int *panMapNewAxisToOldAxis)
12979
0
{
12980
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12981
0
    std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
12982
0
    if (nNewAxisCount)
12983
0
    {
12984
0
        memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
12985
0
               nNewAxisCount * sizeof(int));
12986
0
    }
12987
0
    auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
12988
0
    if (!reordered)
12989
0
        return nullptr;
12990
0
    return new GDALMDArrayHS(reordered);
12991
0
}
12992
12993
/************************************************************************/
12994
/*                      GDALMDArrayGetUnscaled()                        */
12995
/************************************************************************/
12996
12997
/** Return an array that is the unscaled version of the current one.
12998
 *
12999
 * That is each value of the unscaled array will be
13000
 * unscaled_value = raw_value * GetScale() + GetOffset()
13001
 *
13002
 * Starting with GDAL 3.3, the Write() method is implemented and will convert
13003
 * from unscaled values to raw values.
13004
 *
13005
 * The returned object should be released with GDALMDArrayRelease().
13006
 *
13007
 * This is the same as the C++ method GDALMDArray::GetUnscaled().
13008
 */
13009
GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13010
0
{
13011
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13012
0
    auto unscaled = hArray->m_poImpl->GetUnscaled();
13013
0
    if (!unscaled)
13014
0
        return nullptr;
13015
0
    return new GDALMDArrayHS(unscaled);
13016
0
}
13017
13018
/************************************************************************/
13019
/*                          GDALMDArrayGetMask()                         */
13020
/************************************************************************/
13021
13022
/** Return an array that is a mask for the current array
13023
 *
13024
 * This array will be of type Byte, with values set to 0 to indicate invalid
13025
 * pixels of the current array, and values set to 1 to indicate valid pixels.
13026
 *
13027
 * The returned object should be released with GDALMDArrayRelease().
13028
 *
13029
 * This is the same as the C++ method GDALMDArray::GetMask().
13030
 */
13031
GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13032
0
{
13033
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13034
0
    auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13035
0
    if (!unscaled)
13036
0
        return nullptr;
13037
0
    return new GDALMDArrayHS(unscaled);
13038
0
}
13039
13040
/************************************************************************/
13041
/*                   GDALMDArrayGetResampled()                          */
13042
/************************************************************************/
13043
13044
/** Return an array that is a resampled / reprojected view of the current array
13045
 *
13046
 * This is the same as the C++ method GDALMDArray::GetResampled().
13047
 *
13048
 * Currently this method can only resample along the last 2 dimensions, unless
13049
 * orthorectifying a NASA EMIT dataset.
13050
 *
13051
 * The returned object should be released with GDALMDArrayRelease().
13052
 *
13053
 * @since 3.4
13054
 */
13055
GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13056
                                     const GDALDimensionH *pahNewDims,
13057
                                     GDALRIOResampleAlg resampleAlg,
13058
                                     OGRSpatialReferenceH hTargetSRS,
13059
                                     CSLConstList papszOptions)
13060
0
{
13061
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13062
0
    VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13063
0
    std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13064
0
    for (size_t i = 0; i < nNewDimCount; ++i)
13065
0
    {
13066
0
        if (pahNewDims[i])
13067
0
            apoNewDims[i] = pahNewDims[i]->m_poImpl;
13068
0
    }
13069
0
    auto poNewArray = hArray->m_poImpl->GetResampled(
13070
0
        apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13071
0
        papszOptions);
13072
0
    if (!poNewArray)
13073
0
        return nullptr;
13074
0
    return new GDALMDArrayHS(poNewArray);
13075
0
}
13076
13077
/************************************************************************/
13078
/*                      GDALMDArraySetUnit()                            */
13079
/************************************************************************/
13080
13081
/** Set the variable unit.
13082
 *
13083
 * Values should conform as much as possible with those allowed by
13084
 * the NetCDF CF conventions:
13085
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13086
 * but others might be returned.
13087
 *
13088
 * Few examples are "meter", "degrees", "second", ...
13089
 * Empty value means unknown.
13090
 *
13091
 * This is the same as the C function GDALMDArraySetUnit()
13092
 *
13093
 * @param hArray array.
13094
 * @param pszUnit unit name.
13095
 * @return TRUE in case of success.
13096
 */
13097
int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13098
0
{
13099
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13100
0
    return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13101
0
}
13102
13103
/************************************************************************/
13104
/*                      GDALMDArrayGetUnit()                            */
13105
/************************************************************************/
13106
13107
/** Return the array unit.
13108
 *
13109
 * Values should conform as much as possible with those allowed by
13110
 * the NetCDF CF conventions:
13111
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13112
 * but others might be returned.
13113
 *
13114
 * Few examples are "meter", "degrees", "second", ...
13115
 * Empty value means unknown.
13116
 *
13117
 * The return value should not be freed and is valid until GDALMDArray is
13118
 * released or this function called again.
13119
 *
13120
 * This is the same as the C++ method GDALMDArray::GetUnit().
13121
 */
13122
const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13123
0
{
13124
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13125
0
    return hArray->m_poImpl->GetUnit().c_str();
13126
0
}
13127
13128
/************************************************************************/
13129
/*                      GDALMDArrayGetSpatialRef()                      */
13130
/************************************************************************/
13131
13132
/** Assign a spatial reference system object to the array.
13133
 *
13134
 * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13135
 * @return TRUE in case of success.
13136
 */
13137
int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13138
0
{
13139
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13140
0
    return hArray->m_poImpl->SetSpatialRef(
13141
0
        OGRSpatialReference::FromHandle(hSRS));
13142
0
}
13143
13144
/************************************************************************/
13145
/*                      GDALMDArrayGetSpatialRef()                      */
13146
/************************************************************************/
13147
13148
/** Return the spatial reference system object associated with the array.
13149
 *
13150
 * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13151
 *
13152
 * The returned object must be freed with OSRDestroySpatialReference().
13153
 */
13154
OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13155
0
{
13156
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13157
0
    auto poSRS = hArray->m_poImpl->GetSpatialRef();
13158
0
    return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13159
0
}
13160
13161
/************************************************************************/
13162
/*                      GDALMDArrayGetStatistics()                      */
13163
/************************************************************************/
13164
13165
/**
13166
 * \brief Fetch statistics.
13167
 *
13168
 * This is the same as the C++ method GDALMDArray::GetStatistics().
13169
 *
13170
 * @since GDAL 3.2
13171
 */
13172
13173
CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13174
                                int bApproxOK, int bForce, double *pdfMin,
13175
                                double *pdfMax, double *pdfMean,
13176
                                double *pdfStdDev, GUInt64 *pnValidCount,
13177
                                GDALProgressFunc pfnProgress,
13178
                                void *pProgressData)
13179
0
{
13180
0
    VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13181
0
    return hArray->m_poImpl->GetStatistics(
13182
0
        CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13183
0
        pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13184
0
}
13185
13186
/************************************************************************/
13187
/*                      GDALMDArrayComputeStatistics()                  */
13188
/************************************************************************/
13189
13190
/**
13191
 * \brief Compute statistics.
13192
 *
13193
 * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13194
 *
13195
 * @since GDAL 3.2
13196
 * @see GDALMDArrayComputeStatisticsEx()
13197
 */
13198
13199
int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13200
                                 int bApproxOK, double *pdfMin, double *pdfMax,
13201
                                 double *pdfMean, double *pdfStdDev,
13202
                                 GUInt64 *pnValidCount,
13203
                                 GDALProgressFunc pfnProgress,
13204
                                 void *pProgressData)
13205
0
{
13206
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13207
0
    return hArray->m_poImpl->ComputeStatistics(
13208
0
        CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13209
0
        pnValidCount, pfnProgress, pProgressData, nullptr);
13210
0
}
13211
13212
/************************************************************************/
13213
/*                     GDALMDArrayComputeStatisticsEx()                 */
13214
/************************************************************************/
13215
13216
/**
13217
 * \brief Compute statistics.
13218
 *
13219
 * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13220
 *
13221
 * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13222
 *
13223
 * @since GDAL 3.8
13224
 */
13225
13226
int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13227
                                   int bApproxOK, double *pdfMin,
13228
                                   double *pdfMax, double *pdfMean,
13229
                                   double *pdfStdDev, GUInt64 *pnValidCount,
13230
                                   GDALProgressFunc pfnProgress,
13231
                                   void *pProgressData,
13232
                                   CSLConstList papszOptions)
13233
0
{
13234
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13235
0
    return hArray->m_poImpl->ComputeStatistics(
13236
0
        CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13237
0
        pnValidCount, pfnProgress, pProgressData, papszOptions);
13238
0
}
13239
13240
/************************************************************************/
13241
/*                 GDALMDArrayGetCoordinateVariables()                  */
13242
/************************************************************************/
13243
13244
/** Return coordinate variables.
13245
 *
13246
 * The returned array must be freed with GDALReleaseArrays(). If only the array
13247
 * itself needs to be freed, CPLFree() should be called (and
13248
 * GDALMDArrayRelease() on individual array members).
13249
 *
13250
 * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13251
 *
13252
 * @param hArray Array.
13253
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13254
 *
13255
 * @return an array of *pnCount arrays.
13256
 * @since 3.4
13257
 */
13258
GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13259
                                                size_t *pnCount)
13260
0
{
13261
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13262
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13263
0
    const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13264
0
    auto ret = static_cast<GDALMDArrayH *>(
13265
0
        CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13266
0
    for (size_t i = 0; i < coordinates.size(); i++)
13267
0
    {
13268
0
        ret[i] = new GDALMDArrayHS(coordinates[i]);
13269
0
    }
13270
0
    *pnCount = coordinates.size();
13271
0
    return ret;
13272
0
}
13273
13274
/************************************************************************/
13275
/*                     GDALMDArrayGetGridded()                          */
13276
/************************************************************************/
13277
13278
/** Return a gridded array from scattered point data, that is from an array
13279
 * whose last dimension is the indexing variable of X and Y arrays.
13280
 *
13281
 * The returned object should be released with GDALMDArrayRelease().
13282
 *
13283
 * This is the same as the C++ method GDALMDArray::GetGridded().
13284
 *
13285
 * @since GDAL 3.7
13286
 */
13287
GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13288
                                   const char *pszGridOptions,
13289
                                   GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13290
                                   CSLConstList papszOptions)
13291
0
{
13292
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13293
0
    VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13294
0
    auto gridded = hArray->m_poImpl->GetGridded(
13295
0
        pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13296
0
        hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13297
0
    if (!gridded)
13298
0
        return nullptr;
13299
0
    return new GDALMDArrayHS(gridded);
13300
0
}
13301
13302
/************************************************************************/
13303
/*                      GDALMDArrayGetMeshGrid()                        */
13304
/************************************************************************/
13305
13306
/** Return a list of multidimensional arrays from a list of one-dimensional
13307
 * arrays.
13308
 *
13309
 * This is typically used to transform one-dimensional longitude, latitude
13310
 * arrays into 2D ones.
13311
 *
13312
 * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13313
 * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13314
 * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13315
 * repeated to fill the matrix along the first dimension for x1, the second
13316
 * for x2 and so on.
13317
 *
13318
 * For example, if x = [1, 2], and y = [3, 4, 5],
13319
 * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13320
 * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13321
 * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13322
 *
13323
 * and
13324
 * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13325
 * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13326
 * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13327
 *
13328
 * The currently supported options are:
13329
 * <ul>
13330
 * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13331
 * output.
13332
 * </li>
13333
 * </ul>
13334
 *
13335
 * This is the same as
13336
 * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13337
 * function.
13338
 *
13339
 * The returned array (of arrays) must be freed with GDALReleaseArrays().
13340
 * If only the array itself needs to be freed, CPLFree() should be called
13341
 * (and GDALMDArrayRelease() on individual array members).
13342
 *
13343
 * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13344
 *
13345
 * @param pahInputArrays Input arrays
13346
 * @param nCountInputArrays Number of input arrays
13347
 * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13348
 * @param papszOptions NULL, or NULL terminated list of options.
13349
 *
13350
 * @return an array of *pnCountOutputArrays arrays.
13351
 * @since 3.10
13352
 */
13353
GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13354
                                     size_t nCountInputArrays,
13355
                                     size_t *pnCountOutputArrays,
13356
                                     CSLConstList papszOptions)
13357
0
{
13358
0
    VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13359
0
    VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13360
13361
0
    std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13362
0
    for (size_t i = 0; i < nCountInputArrays; ++i)
13363
0
        apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13364
13365
0
    const auto apoOutputArrays =
13366
0
        GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13367
0
    auto ret = static_cast<GDALMDArrayH *>(
13368
0
        CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13369
0
    for (size_t i = 0; i < apoOutputArrays.size(); i++)
13370
0
    {
13371
0
        ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13372
0
    }
13373
0
    *pnCountOutputArrays = apoOutputArrays.size();
13374
0
    return ret;
13375
0
}
13376
13377
/************************************************************************/
13378
/*                        GDALReleaseArrays()                           */
13379
/************************************************************************/
13380
13381
/** Free the return of GDALMDArrayGetCoordinateVariables()
13382
 *
13383
 * @param arrays return pointer of above methods
13384
 * @param nCount *pnCount value returned by above methods
13385
 */
13386
void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13387
0
{
13388
0
    for (size_t i = 0; i < nCount; i++)
13389
0
    {
13390
0
        delete arrays[i];
13391
0
    }
13392
0
    CPLFree(arrays);
13393
0
}
13394
13395
/************************************************************************/
13396
/*                           GDALMDArrayCache()                         */
13397
/************************************************************************/
13398
13399
/**
13400
 * \brief Cache the content of the array into an auxiliary filename.
13401
 *
13402
 * This is the same as the C++ method GDALMDArray::Cache().
13403
 *
13404
 * @since GDAL 3.4
13405
 */
13406
13407
int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13408
0
{
13409
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13410
0
    return hArray->m_poImpl->Cache(papszOptions);
13411
0
}
13412
13413
/************************************************************************/
13414
/*                       GDALMDArrayRename()                           */
13415
/************************************************************************/
13416
13417
/** Rename the array.
13418
 *
13419
 * This is not implemented by all drivers.
13420
 *
13421
 * Drivers known to implement it: MEM, netCDF, Zarr.
13422
 *
13423
 * This is the same as the C++ method GDALAbstractMDArray::Rename()
13424
 *
13425
 * @return true in case of success
13426
 * @since GDAL 3.8
13427
 */
13428
bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13429
0
{
13430
0
    VALIDATE_POINTER1(hArray, __func__, false);
13431
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
13432
0
    return hArray->m_poImpl->Rename(pszNewName);
13433
0
}
13434
13435
/************************************************************************/
13436
/*                        GDALAttributeRelease()                        */
13437
/************************************************************************/
13438
13439
/** Release the GDAL in-memory object associated with a GDALAttribute.
13440
 *
13441
 * Note: when applied on a object coming from a driver, this does not
13442
 * destroy the object in the file, database, etc...
13443
 */
13444
void GDALAttributeRelease(GDALAttributeH hAttr)
13445
0
{
13446
0
    delete hAttr;
13447
0
}
13448
13449
/************************************************************************/
13450
/*                        GDALAttributeGetName()                        */
13451
/************************************************************************/
13452
13453
/** Return the name of the attribute.
13454
 *
13455
 * The returned pointer is valid until hAttr is released.
13456
 *
13457
 * This is the same as the C++ method GDALAttribute::GetName().
13458
 */
13459
const char *GDALAttributeGetName(GDALAttributeH hAttr)
13460
0
{
13461
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13462
0
    return hAttr->m_poImpl->GetName().c_str();
13463
0
}
13464
13465
/************************************************************************/
13466
/*                      GDALAttributeGetFullName()                      */
13467
/************************************************************************/
13468
13469
/** Return the full name of the attribute.
13470
 *
13471
 * The returned pointer is valid until hAttr is released.
13472
 *
13473
 * This is the same as the C++ method GDALAttribute::GetFullName().
13474
 */
13475
const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13476
0
{
13477
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13478
0
    return hAttr->m_poImpl->GetFullName().c_str();
13479
0
}
13480
13481
/************************************************************************/
13482
/*                   GDALAttributeGetTotalElementsCount()               */
13483
/************************************************************************/
13484
13485
/** Return the total number of values in the attribute.
13486
 *
13487
 * This is the same as the C++ method
13488
 * GDALAbstractMDArray::GetTotalElementsCount()
13489
 */
13490
GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13491
0
{
13492
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13493
0
    return hAttr->m_poImpl->GetTotalElementsCount();
13494
0
}
13495
13496
/************************************************************************/
13497
/*                    GDALAttributeGetDimensionCount()                */
13498
/************************************************************************/
13499
13500
/** Return the number of dimensions.
13501
 *
13502
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13503
 */
13504
size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13505
0
{
13506
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13507
0
    return hAttr->m_poImpl->GetDimensionCount();
13508
0
}
13509
13510
/************************************************************************/
13511
/*                       GDALAttributeGetDimensionsSize()                */
13512
/************************************************************************/
13513
13514
/** Return the dimension sizes of the attribute.
13515
 *
13516
 * The returned array must be freed with CPLFree()
13517
 *
13518
 * @param hAttr Attribute.
13519
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13520
 *
13521
 * @return an array of *pnCount values.
13522
 */
13523
GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13524
0
{
13525
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13526
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13527
0
    const auto &dims = hAttr->m_poImpl->GetDimensions();
13528
0
    auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13529
0
    for (size_t i = 0; i < dims.size(); i++)
13530
0
    {
13531
0
        ret[i] = dims[i]->GetSize();
13532
0
    }
13533
0
    *pnCount = dims.size();
13534
0
    return ret;
13535
0
}
13536
13537
/************************************************************************/
13538
/*                       GDALAttributeGetDataType()                     */
13539
/************************************************************************/
13540
13541
/** Return the data type
13542
 *
13543
 * The return must be freed with GDALExtendedDataTypeRelease().
13544
 */
13545
GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13546
0
{
13547
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13548
0
    return new GDALExtendedDataTypeHS(
13549
0
        new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13550
0
}
13551
13552
/************************************************************************/
13553
/*                       GDALAttributeReadAsRaw()                       */
13554
/************************************************************************/
13555
13556
/** Return the raw value of an attribute.
13557
 *
13558
 * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13559
 *
13560
 * The returned buffer must be freed with GDALAttributeFreeRawResult()
13561
 *
13562
 * @param hAttr Attribute.
13563
 * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13564
 *
13565
 * @return a buffer of *pnSize bytes.
13566
 */
13567
GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13568
0
{
13569
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13570
0
    VALIDATE_POINTER1(pnSize, __func__, nullptr);
13571
0
    auto res(hAttr->m_poImpl->ReadAsRaw());
13572
0
    *pnSize = res.size();
13573
0
    auto ret = res.StealData();
13574
0
    if (!ret)
13575
0
    {
13576
0
        *pnSize = 0;
13577
0
        return nullptr;
13578
0
    }
13579
0
    return ret;
13580
0
}
13581
13582
/************************************************************************/
13583
/*                       GDALAttributeFreeRawResult()                   */
13584
/************************************************************************/
13585
13586
/** Free the return of GDALAttributeAsRaw()
13587
 */
13588
void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13589
                                CPL_UNUSED size_t nSize)
13590
0
{
13591
0
    VALIDATE_POINTER0(hAttr, __func__);
13592
0
    if (raw)
13593
0
    {
13594
0
        const auto &dt(hAttr->m_poImpl->GetDataType());
13595
0
        const auto nDTSize(dt.GetSize());
13596
0
        GByte *pabyPtr = raw;
13597
0
        const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13598
0
        CPLAssert(nSize == nDTSize * nEltCount);
13599
0
        for (size_t i = 0; i < nEltCount; ++i)
13600
0
        {
13601
0
            dt.FreeDynamicMemory(pabyPtr);
13602
0
            pabyPtr += nDTSize;
13603
0
        }
13604
0
        CPLFree(raw);
13605
0
    }
13606
0
}
13607
13608
/************************************************************************/
13609
/*                       GDALAttributeReadAsString()                    */
13610
/************************************************************************/
13611
13612
/** Return the value of an attribute as a string.
13613
 *
13614
 * The returned string should not be freed, and its lifetime does not
13615
 * excess a next call to ReadAsString() on the same object, or the deletion
13616
 * of the object itself.
13617
 *
13618
 * This function will only return the first element if there are several.
13619
 *
13620
 * This is the same as the C++ method GDALAttribute::ReadAsString()
13621
 *
13622
 * @return a string, or nullptr.
13623
 */
13624
const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13625
0
{
13626
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13627
0
    return hAttr->m_poImpl->ReadAsString();
13628
0
}
13629
13630
/************************************************************************/
13631
/*                      GDALAttributeReadAsInt()                        */
13632
/************************************************************************/
13633
13634
/** Return the value of an attribute as a integer.
13635
 *
13636
 * This function will only return the first element if there are several.
13637
 *
13638
 * It can fail if its value can not be converted to integer.
13639
 *
13640
 * This is the same as the C++ method GDALAttribute::ReadAsInt()
13641
 *
13642
 * @return a integer, or INT_MIN in case of error.
13643
 */
13644
int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13645
0
{
13646
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13647
0
    return hAttr->m_poImpl->ReadAsInt();
13648
0
}
13649
13650
/************************************************************************/
13651
/*                      GDALAttributeReadAsInt64()                      */
13652
/************************************************************************/
13653
13654
/** Return the value of an attribute as a int64_t.
13655
 *
13656
 * This function will only return the first element if there are several.
13657
 *
13658
 * It can fail if its value can not be converted to integer.
13659
 *
13660
 * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13661
 *
13662
 * @return an int64_t, or INT64_MIN in case of error.
13663
 */
13664
int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13665
0
{
13666
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13667
0
    return hAttr->m_poImpl->ReadAsInt64();
13668
0
}
13669
13670
/************************************************************************/
13671
/*                       GDALAttributeReadAsDouble()                    */
13672
/************************************************************************/
13673
13674
/** Return the value of an attribute as a double.
13675
 *
13676
 * This function will only return the first element if there are several.
13677
 *
13678
 * It can fail if its value can not be converted to double.
13679
 *
13680
 * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13681
 *
13682
 * @return a double value.
13683
 */
13684
double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13685
0
{
13686
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13687
0
    return hAttr->m_poImpl->ReadAsDouble();
13688
0
}
13689
13690
/************************************************************************/
13691
/*                     GDALAttributeReadAsStringArray()                 */
13692
/************************************************************************/
13693
13694
/** Return the value of an attribute as an array of strings.
13695
 *
13696
 * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13697
 *
13698
 * The return value must be freed with CSLDestroy().
13699
 */
13700
char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13701
0
{
13702
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13703
0
    return hAttr->m_poImpl->ReadAsStringArray().StealList();
13704
0
}
13705
13706
/************************************************************************/
13707
/*                     GDALAttributeReadAsIntArray()                    */
13708
/************************************************************************/
13709
13710
/** Return the value of an attribute as an array of integers.
13711
 *
13712
 * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13713
 *
13714
 * @param hAttr Attribute
13715
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13716
 * @return array to be freed with CPLFree(), or nullptr.
13717
 */
13718
int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13719
0
{
13720
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13721
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13722
0
    *pnCount = 0;
13723
0
    auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13724
0
    if (tmp.empty())
13725
0
        return nullptr;
13726
0
    auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13727
0
    if (!ret)
13728
0
        return nullptr;
13729
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13730
0
    *pnCount = tmp.size();
13731
0
    return ret;
13732
0
}
13733
13734
/************************************************************************/
13735
/*                     GDALAttributeReadAsInt64Array()                  */
13736
/************************************************************************/
13737
13738
/** Return the value of an attribute as an array of int64_t.
13739
 *
13740
 * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13741
 *
13742
 * @param hAttr Attribute
13743
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13744
 * @return array to be freed with CPLFree(), or nullptr.
13745
 */
13746
int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13747
0
{
13748
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13749
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13750
0
    *pnCount = 0;
13751
0
    auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13752
0
    if (tmp.empty())
13753
0
        return nullptr;
13754
0
    auto ret = static_cast<int64_t *>(
13755
0
        VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13756
0
    if (!ret)
13757
0
        return nullptr;
13758
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13759
0
    *pnCount = tmp.size();
13760
0
    return ret;
13761
0
}
13762
13763
/************************************************************************/
13764
/*                     GDALAttributeReadAsDoubleArray()                 */
13765
/************************************************************************/
13766
13767
/** Return the value of an attribute as an array of doubles.
13768
 *
13769
 * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13770
 *
13771
 * @param hAttr Attribute
13772
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13773
 * @return array to be freed with CPLFree(), or nullptr.
13774
 */
13775
double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13776
0
{
13777
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13778
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13779
0
    *pnCount = 0;
13780
0
    auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13781
0
    if (tmp.empty())
13782
0
        return nullptr;
13783
0
    auto ret =
13784
0
        static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13785
0
    if (!ret)
13786
0
        return nullptr;
13787
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13788
0
    *pnCount = tmp.size();
13789
0
    return ret;
13790
0
}
13791
13792
/************************************************************************/
13793
/*                     GDALAttributeWriteRaw()                          */
13794
/************************************************************************/
13795
13796
/** Write an attribute from raw values expressed in GetDataType()
13797
 *
13798
 * The values should be provided in the type of GetDataType() and there should
13799
 * be exactly GetTotalElementsCount() of them.
13800
 * If GetDataType() is a string, each value should be a char* pointer.
13801
 *
13802
 * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13803
 *
13804
 * @param hAttr Attribute
13805
 * @param pabyValue Buffer of nLen bytes.
13806
 * @param nLength Size of pabyValue in bytes. Should be equal to
13807
 *             GetTotalElementsCount() * GetDataType().GetSize()
13808
 * @return TRUE in case of success.
13809
 */
13810
int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13811
                          size_t nLength)
13812
0
{
13813
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13814
0
    return hAttr->m_poImpl->Write(pabyValue, nLength);
13815
0
}
13816
13817
/************************************************************************/
13818
/*                     GDALAttributeWriteString()                       */
13819
/************************************************************************/
13820
13821
/** Write an attribute from a string value.
13822
 *
13823
 * Type conversion will be performed if needed. If the attribute contains
13824
 * multiple values, only the first one will be updated.
13825
 *
13826
 * This is the same as the C++ method GDALAttribute::Write(const char*)
13827
 *
13828
 * @param hAttr Attribute
13829
 * @param pszVal Pointer to a string.
13830
 * @return TRUE in case of success.
13831
 */
13832
int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
13833
0
{
13834
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13835
0
    return hAttr->m_poImpl->Write(pszVal);
13836
0
}
13837
13838
/************************************************************************/
13839
/*                        GDALAttributeWriteInt()                       */
13840
/************************************************************************/
13841
13842
/** Write an attribute from a integer value.
13843
 *
13844
 * Type conversion will be performed if needed. If the attribute contains
13845
 * multiple values, only the first one will be updated.
13846
 *
13847
 * This is the same as the C++ method GDALAttribute::WriteInt()
13848
 *
13849
 * @param hAttr Attribute
13850
 * @param nVal Value.
13851
 * @return TRUE in case of success.
13852
 */
13853
int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
13854
0
{
13855
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13856
0
    return hAttr->m_poImpl->WriteInt(nVal);
13857
0
}
13858
13859
/************************************************************************/
13860
/*                        GDALAttributeWriteInt64()                     */
13861
/************************************************************************/
13862
13863
/** Write an attribute from an int64_t value.
13864
 *
13865
 * Type conversion will be performed if needed. If the attribute contains
13866
 * multiple values, only the first one will be updated.
13867
 *
13868
 * This is the same as the C++ method GDALAttribute::WriteLong()
13869
 *
13870
 * @param hAttr Attribute
13871
 * @param nVal Value.
13872
 * @return TRUE in case of success.
13873
 */
13874
int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
13875
0
{
13876
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13877
0
    return hAttr->m_poImpl->WriteInt64(nVal);
13878
0
}
13879
13880
/************************************************************************/
13881
/*                        GDALAttributeWriteDouble()                    */
13882
/************************************************************************/
13883
13884
/** Write an attribute from a double value.
13885
 *
13886
 * Type conversion will be performed if needed. If the attribute contains
13887
 * multiple values, only the first one will be updated.
13888
 *
13889
 * This is the same as the C++ method GDALAttribute::Write(double);
13890
 *
13891
 * @param hAttr Attribute
13892
 * @param dfVal Value.
13893
 *
13894
 * @return TRUE in case of success.
13895
 */
13896
int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
13897
0
{
13898
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13899
0
    return hAttr->m_poImpl->Write(dfVal);
13900
0
}
13901
13902
/************************************************************************/
13903
/*                       GDALAttributeWriteStringArray()                */
13904
/************************************************************************/
13905
13906
/** Write an attribute from an array of strings.
13907
 *
13908
 * Type conversion will be performed if needed.
13909
 *
13910
 * Exactly GetTotalElementsCount() strings must be provided
13911
 *
13912
 * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
13913
 *
13914
 * @param hAttr Attribute
13915
 * @param papszValues Array of strings.
13916
 * @return TRUE in case of success.
13917
 */
13918
int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
13919
                                  CSLConstList papszValues)
13920
0
{
13921
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13922
0
    return hAttr->m_poImpl->Write(papszValues);
13923
0
}
13924
13925
/************************************************************************/
13926
/*                       GDALAttributeWriteIntArray()                */
13927
/************************************************************************/
13928
13929
/** Write an attribute from an array of int.
13930
 *
13931
 * Type conversion will be performed if needed.
13932
 *
13933
 * Exactly GetTotalElementsCount() strings must be provided
13934
 *
13935
 * This is the same as the C++ method GDALAttribute::Write(const int *,
13936
 * size_t)
13937
 *
13938
 * @param hAttr Attribute
13939
 * @param panValues Array of int.
13940
 * @param nCount Should be equal to GetTotalElementsCount().
13941
 * @return TRUE in case of success.
13942
 */
13943
int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
13944
                               size_t nCount)
13945
0
{
13946
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13947
0
    return hAttr->m_poImpl->Write(panValues, nCount);
13948
0
}
13949
13950
/************************************************************************/
13951
/*                       GDALAttributeWriteInt64Array()                 */
13952
/************************************************************************/
13953
13954
/** Write an attribute from an array of int64_t.
13955
 *
13956
 * Type conversion will be performed if needed.
13957
 *
13958
 * Exactly GetTotalElementsCount() strings must be provided
13959
 *
13960
 * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
13961
 * size_t)
13962
 *
13963
 * @param hAttr Attribute
13964
 * @param panValues Array of int64_t.
13965
 * @param nCount Should be equal to GetTotalElementsCount().
13966
 * @return TRUE in case of success.
13967
 */
13968
int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
13969
                                 size_t nCount)
13970
0
{
13971
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13972
0
    return hAttr->m_poImpl->Write(panValues, nCount);
13973
0
}
13974
13975
/************************************************************************/
13976
/*                       GDALAttributeWriteDoubleArray()                */
13977
/************************************************************************/
13978
13979
/** Write an attribute from an array of double.
13980
 *
13981
 * Type conversion will be performed if needed.
13982
 *
13983
 * Exactly GetTotalElementsCount() strings must be provided
13984
 *
13985
 * This is the same as the C++ method GDALAttribute::Write(const double *,
13986
 * size_t)
13987
 *
13988
 * @param hAttr Attribute
13989
 * @param padfValues Array of double.
13990
 * @param nCount Should be equal to GetTotalElementsCount().
13991
 * @return TRUE in case of success.
13992
 */
13993
int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
13994
                                  const double *padfValues, size_t nCount)
13995
0
{
13996
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
13997
0
    return hAttr->m_poImpl->Write(padfValues, nCount);
13998
0
}
13999
14000
/************************************************************************/
14001
/*                      GDALAttributeRename()                           */
14002
/************************************************************************/
14003
14004
/** Rename the attribute.
14005
 *
14006
 * This is not implemented by all drivers.
14007
 *
14008
 * Drivers known to implement it: MEM, netCDF.
14009
 *
14010
 * This is the same as the C++ method GDALAbstractMDArray::Rename()
14011
 *
14012
 * @return true in case of success
14013
 * @since GDAL 3.8
14014
 */
14015
bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14016
0
{
14017
0
    VALIDATE_POINTER1(hAttr, __func__, false);
14018
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
14019
0
    return hAttr->m_poImpl->Rename(pszNewName);
14020
0
}
14021
14022
/************************************************************************/
14023
/*                        GDALDimensionRelease()                        */
14024
/************************************************************************/
14025
14026
/** Release the GDAL in-memory object associated with a GDALDimension.
14027
 *
14028
 * Note: when applied on a object coming from a driver, this does not
14029
 * destroy the object in the file, database, etc...
14030
 */
14031
void GDALDimensionRelease(GDALDimensionH hDim)
14032
0
{
14033
0
    delete hDim;
14034
0
}
14035
14036
/************************************************************************/
14037
/*                        GDALDimensionGetName()                        */
14038
/************************************************************************/
14039
14040
/** Return dimension name.
14041
 *
14042
 * This is the same as the C++ method GDALDimension::GetName()
14043
 */
14044
const char *GDALDimensionGetName(GDALDimensionH hDim)
14045
0
{
14046
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14047
0
    return hDim->m_poImpl->GetName().c_str();
14048
0
}
14049
14050
/************************************************************************/
14051
/*                      GDALDimensionGetFullName()                      */
14052
/************************************************************************/
14053
14054
/** Return dimension full name.
14055
 *
14056
 * This is the same as the C++ method GDALDimension::GetFullName()
14057
 */
14058
const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14059
0
{
14060
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14061
0
    return hDim->m_poImpl->GetFullName().c_str();
14062
0
}
14063
14064
/************************************************************************/
14065
/*                        GDALDimensionGetType()                        */
14066
/************************************************************************/
14067
14068
/** Return dimension type.
14069
 *
14070
 * This is the same as the C++ method GDALDimension::GetType()
14071
 */
14072
const char *GDALDimensionGetType(GDALDimensionH hDim)
14073
0
{
14074
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14075
0
    return hDim->m_poImpl->GetType().c_str();
14076
0
}
14077
14078
/************************************************************************/
14079
/*                     GDALDimensionGetDirection()                      */
14080
/************************************************************************/
14081
14082
/** Return dimension direction.
14083
 *
14084
 * This is the same as the C++ method GDALDimension::GetDirection()
14085
 */
14086
const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14087
0
{
14088
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14089
0
    return hDim->m_poImpl->GetDirection().c_str();
14090
0
}
14091
14092
/************************************************************************/
14093
/*                        GDALDimensionGetSize()                        */
14094
/************************************************************************/
14095
14096
/** Return the size, that is the number of values along the dimension.
14097
 *
14098
 * This is the same as the C++ method GDALDimension::GetSize()
14099
 */
14100
GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14101
0
{
14102
0
    VALIDATE_POINTER1(hDim, __func__, 0);
14103
0
    return hDim->m_poImpl->GetSize();
14104
0
}
14105
14106
/************************************************************************/
14107
/*                     GDALDimensionGetIndexingVariable()               */
14108
/************************************************************************/
14109
14110
/** Return the variable that is used to index the dimension (if there is one).
14111
 *
14112
 * This is the array, typically one-dimensional, describing the values taken
14113
 * by the dimension.
14114
 *
14115
 * The returned value should be freed with GDALMDArrayRelease().
14116
 *
14117
 * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14118
 */
14119
GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14120
0
{
14121
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14122
0
    auto var(hDim->m_poImpl->GetIndexingVariable());
14123
0
    if (!var)
14124
0
        return nullptr;
14125
0
    return new GDALMDArrayHS(var);
14126
0
}
14127
14128
/************************************************************************/
14129
/*                      GDALDimensionSetIndexingVariable()              */
14130
/************************************************************************/
14131
14132
/** Set the variable that is used to index the dimension.
14133
 *
14134
 * This is the array, typically one-dimensional, describing the values taken
14135
 * by the dimension.
14136
 *
14137
 * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14138
 *
14139
 * @return TRUE in case of success.
14140
 */
14141
int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14142
0
{
14143
0
    VALIDATE_POINTER1(hDim, __func__, FALSE);
14144
0
    return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14145
0
                                                      : nullptr);
14146
0
}
14147
14148
/************************************************************************/
14149
/*                      GDALDimensionRename()                           */
14150
/************************************************************************/
14151
14152
/** Rename the dimension.
14153
 *
14154
 * This is not implemented by all drivers.
14155
 *
14156
 * Drivers known to implement it: MEM, netCDF.
14157
 *
14158
 * This is the same as the C++ method GDALDimension::Rename()
14159
 *
14160
 * @return true in case of success
14161
 * @since GDAL 3.8
14162
 */
14163
bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14164
0
{
14165
0
    VALIDATE_POINTER1(hDim, __func__, false);
14166
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
14167
0
    return hDim->m_poImpl->Rename(pszNewName);
14168
0
}
14169
14170
/************************************************************************/
14171
/*                       GDALDatasetGetRootGroup()                      */
14172
/************************************************************************/
14173
14174
/** Return the root GDALGroup of this dataset.
14175
 *
14176
 * Only valid for multidimensional datasets.
14177
 *
14178
 * The returned value must be freed with GDALGroupRelease().
14179
 *
14180
 * This is the same as the C++ method GDALDataset::GetRootGroup().
14181
 *
14182
 * @since GDAL 3.1
14183
 */
14184
GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14185
0
{
14186
0
    VALIDATE_POINTER1(hDS, __func__, nullptr);
14187
0
    auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14188
0
    return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14189
0
}
14190
14191
/************************************************************************/
14192
/*                      GDALRasterBandAsMDArray()                        */
14193
/************************************************************************/
14194
14195
/** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14196
 *
14197
 * The band must be linked to a GDALDataset. If this dataset is not already
14198
 * marked as shared, it will be, so that the returned array holds a reference
14199
 * to it.
14200
 *
14201
 * If the dataset has a geotransform attached, the X and Y dimensions of the
14202
 * returned array will have an associated indexing variable.
14203
 *
14204
 * The returned pointer must be released with GDALMDArrayRelease().
14205
 *
14206
 * This is the same as the C++ method GDALRasterBand::AsMDArray().
14207
 *
14208
 * @return a new array, or NULL.
14209
 *
14210
 * @since GDAL 3.1
14211
 */
14212
GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14213
0
{
14214
0
    VALIDATE_POINTER1(hBand, __func__, nullptr);
14215
0
    auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14216
0
    if (!poArray)
14217
0
        return nullptr;
14218
0
    return new GDALMDArrayHS(poArray);
14219
0
}
14220
14221
/************************************************************************/
14222
/*                       GDALMDArrayAsClassicDataset()                  */
14223
/************************************************************************/
14224
14225
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
14226
 *
14227
 * Only 2D or more arrays are supported.
14228
 *
14229
 * In the case of > 2D arrays, additional dimensions will be represented as
14230
 * raster bands.
14231
 *
14232
 * The "reverse" method is GDALRasterBand::AsMDArray().
14233
 *
14234
 * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14235
 *
14236
 * @param hArray Array.
14237
 * @param iXDim Index of the dimension that will be used as the X/width axis.
14238
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
14239
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14240
 */
14241
GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14242
                                         size_t iYDim)
14243
0
{
14244
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
14245
0
    return GDALDataset::ToHandle(
14246
0
        hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14247
0
}
14248
14249
/************************************************************************/
14250
/*                     GDALMDArrayAsClassicDatasetEx()                  */
14251
/************************************************************************/
14252
14253
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
14254
 *
14255
 * Only 2D or more arrays are supported.
14256
 *
14257
 * In the case of > 2D arrays, additional dimensions will be represented as
14258
 * raster bands.
14259
 *
14260
 * The "reverse" method is GDALRasterBand::AsMDArray().
14261
 *
14262
 * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14263
 * @param hArray Array.
14264
 * @param iXDim Index of the dimension that will be used as the X/width axis.
14265
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
14266
 *              Ignored if the dimension count is 1.
14267
 * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14268
 *                   BAND_IMAGERY_METADATA option.
14269
 * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14270
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14271
 * @since GDAL 3.8
14272
 */
14273
GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14274
                                           size_t iYDim, GDALGroupH hRootGroup,
14275
                                           CSLConstList papszOptions)
14276
0
{
14277
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
14278
0
    return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14279
0
        iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14280
0
        papszOptions));
14281
0
}
14282
14283
//! @cond Doxygen_Suppress
14284
14285
GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14286
                                         const std::string &osName,
14287
                                         const std::string &osValue,
14288
                                         GDALExtendedDataTypeSubType eSubType)
14289
0
    : GDALAbstractMDArray(osParentName, osName),
14290
0
      GDALAttribute(osParentName, osName),
14291
0
      m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14292
0
{
14293
0
}
Unexecuted instantiation: GDALAttributeString::GDALAttributeString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALExtendedDataTypeSubType)
Unexecuted instantiation: GDALAttributeString::GDALAttributeString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALExtendedDataTypeSubType)
14294
14295
const std::vector<std::shared_ptr<GDALDimension>> &
14296
GDALAttributeString::GetDimensions() const
14297
0
{
14298
0
    return m_dims;
14299
0
}
14300
14301
const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14302
0
{
14303
0
    return m_dt;
14304
0
}
14305
14306
bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14307
                                const GPtrDiff_t *,
14308
                                const GDALExtendedDataType &bufferDataType,
14309
                                void *pDstBuffer) const
14310
0
{
14311
0
    if (bufferDataType.GetClass() != GEDTC_STRING)
14312
0
        return false;
14313
0
    char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14314
0
    if (!pszStr)
14315
0
        return false;
14316
0
    memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14317
0
    *static_cast<char **>(pDstBuffer) = pszStr;
14318
0
    return true;
14319
0
}
14320
14321
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14322
                                           const std::string &osName,
14323
                                           double dfValue)
14324
0
    : GDALAbstractMDArray(osParentName, osName),
14325
0
      GDALAttribute(osParentName, osName),
14326
0
      m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14327
0
{
14328
0
}
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, double)
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, double)
14329
14330
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14331
                                           const std::string &osName,
14332
                                           int nValue)
14333
0
    : GDALAbstractMDArray(osParentName, osName),
14334
0
      GDALAttribute(osParentName, osName),
14335
0
      m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14336
0
{
14337
0
}
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int)
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int)
14338
14339
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14340
                                           const std::string &osName,
14341
                                           const std::vector<GUInt32> &anValues)
14342
0
    : GDALAbstractMDArray(osParentName, osName),
14343
0
      GDALAttribute(osParentName, osName),
14344
0
      m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14345
0
{
14346
0
    m_dims.push_back(std::make_shared<GDALDimension>(
14347
0
        std::string(), "dim0", std::string(), std::string(),
14348
0
        m_anValuesUInt32.size()));
14349
0
}
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
Unexecuted instantiation: GDALAttributeNumeric::GDALAttributeNumeric(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
14350
14351
const std::vector<std::shared_ptr<GDALDimension>> &
14352
GDALAttributeNumeric::GetDimensions() const
14353
0
{
14354
0
    return m_dims;
14355
0
}
14356
14357
const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14358
0
{
14359
0
    return m_dt;
14360
0
}
14361
14362
bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14363
                                 const size_t *count, const GInt64 *arrayStep,
14364
                                 const GPtrDiff_t *bufferStride,
14365
                                 const GDALExtendedDataType &bufferDataType,
14366
                                 void *pDstBuffer) const
14367
0
{
14368
0
    if (m_dims.empty())
14369
0
    {
14370
0
        if (m_dt.GetNumericDataType() == GDT_Float64)
14371
0
            GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14372
0
                                            bufferDataType);
14373
0
        else
14374
0
        {
14375
0
            CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14376
0
            GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14377
0
                                            bufferDataType);
14378
0
        }
14379
0
    }
14380
0
    else
14381
0
    {
14382
0
        CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14383
0
        GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14384
0
        for (size_t i = 0; i < count[0]; ++i)
14385
0
        {
14386
0
            GDALExtendedDataType::CopyValue(
14387
0
                &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14388
0
                                                      i * arrayStep[0])],
14389
0
                m_dt, pabyDstBuffer, bufferDataType);
14390
0
            pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14391
0
        }
14392
0
    }
14393
0
    return true;
14394
0
}
14395
14396
GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14397
    const std::string &osParentName, const std::string &osName,
14398
    const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14399
    double dfIncrement, double dfOffsetInIncrement)
14400
0
    : GDALAbstractMDArray(osParentName, osName),
14401
0
      GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14402
0
      m_dfIncrement(dfIncrement),
14403
0
      m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
14404
0
{
14405
0
}
Unexecuted instantiation: GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<GDALDimension> const&, double, double, double)
Unexecuted instantiation: GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<GDALDimension> const&, double, double, double)
14406
14407
std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14408
    const std::string &osParentName, const std::string &osName,
14409
    const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14410
    double dfIncrement, double dfOffsetInIncrement)
14411
0
{
14412
0
    auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14413
0
        osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14414
0
    poArray->SetSelf(poArray);
14415
0
    return poArray;
14416
0
}
14417
14418
const std::vector<std::shared_ptr<GDALDimension>> &
14419
GDALMDArrayRegularlySpaced::GetDimensions() const
14420
0
{
14421
0
    return m_dims;
14422
0
}
14423
14424
const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14425
0
{
14426
0
    return m_dt;
14427
0
}
14428
14429
std::vector<std::shared_ptr<GDALAttribute>>
14430
GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14431
0
{
14432
0
    return m_attributes;
14433
0
}
14434
14435
void GDALMDArrayRegularlySpaced::AddAttribute(
14436
    const std::shared_ptr<GDALAttribute> &poAttr)
14437
0
{
14438
0
    m_attributes.emplace_back(poAttr);
14439
0
}
14440
14441
bool GDALMDArrayRegularlySpaced::IRead(
14442
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14443
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14444
    void *pDstBuffer) const
14445
0
{
14446
0
    GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14447
0
    for (size_t i = 0; i < count[0]; i++)
14448
0
    {
14449
0
        const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
14450
0
                                          m_dfOffsetInIncrement) *
14451
0
                                             m_dfIncrement;
14452
0
        GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14453
0
                                        bufferDataType);
14454
0
        pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14455
0
    }
14456
0
    return true;
14457
0
}
14458
14459
GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14460
    const std::string &osParentName, const std::string &osName,
14461
    const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14462
0
    : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14463
0
{
14464
0
}
14465
14466
std::shared_ptr<GDALMDArray>
14467
GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14468
0
{
14469
0
    return m_poIndexingVariable.lock();
14470
0
}
14471
14472
// cppcheck-suppress passedByValue
14473
bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14474
    std::shared_ptr<GDALMDArray> poIndexingVariable)
14475
0
{
14476
0
    m_poIndexingVariable = poIndexingVariable;
14477
0
    return true;
14478
0
}
14479
14480
void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14481
0
{
14482
0
    m_nSize = nNewSize;
14483
0
}
14484
14485
/************************************************************************/
14486
/*                       GDALPamMultiDim::Private                       */
14487
/************************************************************************/
14488
14489
struct GDALPamMultiDim::Private
14490
{
14491
    std::string m_osFilename{};
14492
    std::string m_osPamFilename{};
14493
14494
    struct Statistics
14495
    {
14496
        bool bHasStats = false;
14497
        bool bApproxStats = false;
14498
        double dfMin = 0;
14499
        double dfMax = 0;
14500
        double dfMean = 0;
14501
        double dfStdDev = 0;
14502
        GUInt64 nValidCount = 0;
14503
    };
14504
14505
    struct ArrayInfo
14506
    {
14507
        std::shared_ptr<OGRSpatialReference> poSRS{};
14508
        // cppcheck-suppress unusedStructMember
14509
        Statistics stats{};
14510
    };
14511
14512
    typedef std::pair<std::string, std::string> NameContext;
14513
    std::map<NameContext, ArrayInfo> m_oMapArray{};
14514
    std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14515
    bool m_bDirty = false;
14516
    bool m_bLoaded = false;
14517
};
14518
14519
/************************************************************************/
14520
/*                          GDALPamMultiDim                             */
14521
/************************************************************************/
14522
14523
GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14524
0
    : d(new Private())
14525
0
{
14526
0
    d->m_osFilename = osFilename;
14527
0
}
14528
14529
/************************************************************************/
14530
/*                   GDALPamMultiDim::~GDALPamMultiDim()                */
14531
/************************************************************************/
14532
14533
GDALPamMultiDim::~GDALPamMultiDim()
14534
0
{
14535
0
    if (d->m_bDirty)
14536
0
        Save();
14537
0
}
14538
14539
/************************************************************************/
14540
/*                          GDALPamMultiDim::Load()                     */
14541
/************************************************************************/
14542
14543
void GDALPamMultiDim::Load()
14544
0
{
14545
0
    if (d->m_bLoaded)
14546
0
        return;
14547
0
    d->m_bLoaded = true;
14548
14549
0
    const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14550
0
    d->m_osPamFilename =
14551
0
        pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14552
0
    CPLXMLTreeCloser oTree(nullptr);
14553
0
    {
14554
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14555
0
        oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14556
0
    }
14557
0
    if (!oTree)
14558
0
    {
14559
0
        return;
14560
0
    }
14561
0
    const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14562
0
    if (!poPAMMultiDim)
14563
0
        return;
14564
0
    for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14565
0
         psIter = psIter->psNext)
14566
0
    {
14567
0
        if (psIter->eType == CXT_Element &&
14568
0
            strcmp(psIter->pszValue, "Array") == 0)
14569
0
        {
14570
0
            const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14571
0
            if (!pszName)
14572
0
                continue;
14573
0
            const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14574
0
            const auto oKey =
14575
0
                std::pair<std::string, std::string>(pszName, pszContext);
14576
14577
            /* --------------------------------------------------------------------
14578
             */
14579
            /*      Check for an SRS node. */
14580
            /* --------------------------------------------------------------------
14581
             */
14582
0
            const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14583
0
            if (psSRSNode)
14584
0
            {
14585
0
                std::shared_ptr<OGRSpatialReference> poSRS =
14586
0
                    std::make_shared<OGRSpatialReference>();
14587
0
                poSRS->SetFromUserInput(
14588
0
                    CPLGetXMLValue(psSRSNode, nullptr, ""),
14589
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14590
0
                const char *pszMapping = CPLGetXMLValue(
14591
0
                    psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14592
0
                if (pszMapping)
14593
0
                {
14594
0
                    char **papszTokens =
14595
0
                        CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14596
0
                    std::vector<int> anMapping;
14597
0
                    for (int i = 0; papszTokens && papszTokens[i]; i++)
14598
0
                    {
14599
0
                        anMapping.push_back(atoi(papszTokens[i]));
14600
0
                    }
14601
0
                    CSLDestroy(papszTokens);
14602
0
                    poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14603
0
                }
14604
0
                else
14605
0
                {
14606
0
                    poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14607
0
                }
14608
14609
0
                const char *pszCoordinateEpoch =
14610
0
                    CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14611
0
                if (pszCoordinateEpoch)
14612
0
                    poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14613
14614
0
                d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14615
0
            }
14616
14617
0
            const CPLXMLNode *psStatistics =
14618
0
                CPLGetXMLNode(psIter, "Statistics");
14619
0
            if (psStatistics)
14620
0
            {
14621
0
                Private::Statistics sStats;
14622
0
                sStats.bHasStats = true;
14623
0
                sStats.bApproxStats = CPLTestBool(
14624
0
                    CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14625
0
                sStats.dfMin =
14626
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14627
0
                sStats.dfMax =
14628
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14629
0
                sStats.dfMean =
14630
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14631
0
                sStats.dfStdDev =
14632
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14633
0
                sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14634
0
                    CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14635
0
                d->m_oMapArray[oKey].stats = sStats;
14636
0
            }
14637
0
        }
14638
0
        else
14639
0
        {
14640
0
            CPLXMLNode *psNextBackup = psIter->psNext;
14641
0
            psIter->psNext = nullptr;
14642
0
            d->m_apoOtherNodes.emplace_back(
14643
0
                CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14644
0
            psIter->psNext = psNextBackup;
14645
0
        }
14646
0
    }
14647
0
}
14648
14649
/************************************************************************/
14650
/*                          GDALPamMultiDim::Save()                     */
14651
/************************************************************************/
14652
14653
void GDALPamMultiDim::Save()
14654
0
{
14655
0
    CPLXMLTreeCloser oTree(
14656
0
        CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14657
0
    for (const auto &poOtherNode : d->m_apoOtherNodes)
14658
0
    {
14659
0
        CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14660
0
    }
14661
0
    for (const auto &kv : d->m_oMapArray)
14662
0
    {
14663
0
        CPLXMLNode *psArrayNode =
14664
0
            CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14665
0
        CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14666
0
        if (!kv.first.second.empty())
14667
0
        {
14668
0
            CPLAddXMLAttributeAndValue(psArrayNode, "context",
14669
0
                                       kv.first.second.c_str());
14670
0
        }
14671
0
        if (kv.second.poSRS)
14672
0
        {
14673
0
            char *pszWKT = nullptr;
14674
0
            {
14675
0
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14676
0
                const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14677
0
                kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14678
0
            }
14679
0
            CPLXMLNode *psSRSNode =
14680
0
                CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14681
0
            CPLFree(pszWKT);
14682
0
            const auto &mapping =
14683
0
                kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14684
0
            CPLString osMapping;
14685
0
            for (size_t i = 0; i < mapping.size(); ++i)
14686
0
            {
14687
0
                if (!osMapping.empty())
14688
0
                    osMapping += ",";
14689
0
                osMapping += CPLSPrintf("%d", mapping[i]);
14690
0
            }
14691
0
            CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14692
0
                                       osMapping.c_str());
14693
14694
0
            const double dfCoordinateEpoch =
14695
0
                kv.second.poSRS->GetCoordinateEpoch();
14696
0
            if (dfCoordinateEpoch > 0)
14697
0
            {
14698
0
                std::string osCoordinateEpoch =
14699
0
                    CPLSPrintf("%f", dfCoordinateEpoch);
14700
0
                if (osCoordinateEpoch.find('.') != std::string::npos)
14701
0
                {
14702
0
                    while (osCoordinateEpoch.back() == '0')
14703
0
                        osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14704
0
                }
14705
0
                CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14706
0
                                           osCoordinateEpoch.c_str());
14707
0
            }
14708
0
        }
14709
14710
0
        if (kv.second.stats.bHasStats)
14711
0
        {
14712
0
            CPLXMLNode *psMDArray =
14713
0
                CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14714
0
            CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14715
0
                                        kv.second.stats.bApproxStats ? "1"
14716
0
                                                                     : "0");
14717
0
            CPLCreateXMLElementAndValue(
14718
0
                psMDArray, "Minimum",
14719
0
                CPLSPrintf("%.17g", kv.second.stats.dfMin));
14720
0
            CPLCreateXMLElementAndValue(
14721
0
                psMDArray, "Maximum",
14722
0
                CPLSPrintf("%.17g", kv.second.stats.dfMax));
14723
0
            CPLCreateXMLElementAndValue(
14724
0
                psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14725
0
            CPLCreateXMLElementAndValue(
14726
0
                psMDArray, "StdDev",
14727
0
                CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14728
0
            CPLCreateXMLElementAndValue(
14729
0
                psMDArray, "ValidSampleCount",
14730
0
                CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14731
0
        }
14732
0
    }
14733
14734
0
    int bSaved;
14735
0
    CPLErrorAccumulator oErrorAccumulator;
14736
0
    {
14737
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
14738
0
        CPL_IGNORE_RET_VAL(oAccumulator);
14739
0
        bSaved =
14740
0
            CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
14741
0
    }
14742
14743
0
    const char *pszNewPam = nullptr;
14744
0
    if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
14745
0
        ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
14746
0
    {
14747
0
        CPLErrorReset();
14748
0
        CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
14749
0
    }
14750
0
    else
14751
0
    {
14752
0
        oErrorAccumulator.ReplayErrors();
14753
0
    }
14754
0
}
14755
14756
/************************************************************************/
14757
/*                    GDALPamMultiDim::GetSpatialRef()                  */
14758
/************************************************************************/
14759
14760
std::shared_ptr<OGRSpatialReference>
14761
GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
14762
                               const std::string &osContext)
14763
0
{
14764
0
    Load();
14765
0
    auto oIter =
14766
0
        d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14767
0
    if (oIter != d->m_oMapArray.end())
14768
0
        return oIter->second.poSRS;
14769
0
    return nullptr;
14770
0
}
14771
14772
/************************************************************************/
14773
/*                    GDALPamMultiDim::SetSpatialRef()                  */
14774
/************************************************************************/
14775
14776
void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
14777
                                    const std::string &osContext,
14778
                                    const OGRSpatialReference *poSRS)
14779
0
{
14780
0
    Load();
14781
0
    d->m_bDirty = true;
14782
0
    if (poSRS && !poSRS->IsEmpty())
14783
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
14784
0
            poSRS->Clone());
14785
0
    else
14786
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
14787
0
            .poSRS.reset();
14788
0
}
14789
14790
/************************************************************************/
14791
/*                           GetStatistics()                            */
14792
/************************************************************************/
14793
14794
CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
14795
                                      const std::string &osContext,
14796
                                      bool bApproxOK, double *pdfMin,
14797
                                      double *pdfMax, double *pdfMean,
14798
                                      double *pdfStdDev, GUInt64 *pnValidCount)
14799
0
{
14800
0
    Load();
14801
0
    auto oIter =
14802
0
        d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
14803
0
    if (oIter == d->m_oMapArray.end())
14804
0
        return CE_Failure;
14805
0
    const auto &stats = oIter->second.stats;
14806
0
    if (!stats.bHasStats)
14807
0
        return CE_Failure;
14808
0
    if (!bApproxOK && stats.bApproxStats)
14809
0
        return CE_Failure;
14810
0
    if (pdfMin)
14811
0
        *pdfMin = stats.dfMin;
14812
0
    if (pdfMax)
14813
0
        *pdfMax = stats.dfMax;
14814
0
    if (pdfMean)
14815
0
        *pdfMean = stats.dfMean;
14816
0
    if (pdfStdDev)
14817
0
        *pdfStdDev = stats.dfStdDev;
14818
0
    if (pnValidCount)
14819
0
        *pnValidCount = stats.nValidCount;
14820
0
    return CE_None;
14821
0
}
14822
14823
/************************************************************************/
14824
/*                           SetStatistics()                            */
14825
/************************************************************************/
14826
14827
void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
14828
                                    const std::string &osContext,
14829
                                    bool bApproxStats, double dfMin,
14830
                                    double dfMax, double dfMean,
14831
                                    double dfStdDev, GUInt64 nValidCount)
14832
0
{
14833
0
    Load();
14834
0
    d->m_bDirty = true;
14835
0
    auto &stats =
14836
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
14837
0
    stats.bHasStats = true;
14838
0
    stats.bApproxStats = bApproxStats;
14839
0
    stats.dfMin = dfMin;
14840
0
    stats.dfMax = dfMax;
14841
0
    stats.dfMean = dfMean;
14842
0
    stats.dfStdDev = dfStdDev;
14843
0
    stats.nValidCount = nValidCount;
14844
0
}
14845
14846
/************************************************************************/
14847
/*                           ClearStatistics()                          */
14848
/************************************************************************/
14849
14850
void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
14851
                                      const std::string &osContext)
14852
0
{
14853
0
    Load();
14854
0
    d->m_bDirty = true;
14855
0
    d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
14856
0
        false;
14857
0
}
14858
14859
/************************************************************************/
14860
/*                           ClearStatistics()                          */
14861
/************************************************************************/
14862
14863
void GDALPamMultiDim::ClearStatistics()
14864
0
{
14865
0
    Load();
14866
0
    d->m_bDirty = true;
14867
0
    for (auto &kv : d->m_oMapArray)
14868
0
        kv.second.stats.bHasStats = false;
14869
0
}
14870
14871
/************************************************************************/
14872
/*                             GetPAM()                                 */
14873
/************************************************************************/
14874
14875
/*static*/ std::shared_ptr<GDALPamMultiDim>
14876
GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
14877
0
{
14878
0
    auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
14879
0
    if (poPamArray)
14880
0
        return poPamArray->GetPAM();
14881
0
    return nullptr;
14882
0
}
14883
14884
/************************************************************************/
14885
/*                           GDALPamMDArray                             */
14886
/************************************************************************/
14887
14888
GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
14889
                               const std::string &osName,
14890
                               const std::shared_ptr<GDALPamMultiDim> &poPam,
14891
                               const std::string &osContext)
14892
    :
14893
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
14894
      GDALAbstractMDArray(osParentName, osName),
14895
#endif
14896
0
      GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
14897
0
{
14898
0
}
14899
14900
/************************************************************************/
14901
/*                    GDALPamMDArray::SetSpatialRef()                   */
14902
/************************************************************************/
14903
14904
bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
14905
0
{
14906
0
    if (!m_poPam)
14907
0
        return false;
14908
0
    m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
14909
0
    return true;
14910
0
}
14911
14912
/************************************************************************/
14913
/*                    GDALPamMDArray::GetSpatialRef()                   */
14914
/************************************************************************/
14915
14916
std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
14917
0
{
14918
0
    if (!m_poPam)
14919
0
        return nullptr;
14920
0
    return m_poPam->GetSpatialRef(GetFullName(), GetContext());
14921
0
}
14922
14923
/************************************************************************/
14924
/*                           GetStatistics()                            */
14925
/************************************************************************/
14926
14927
CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
14928
                                     double *pdfMin, double *pdfMax,
14929
                                     double *pdfMean, double *pdfStdDev,
14930
                                     GUInt64 *pnValidCount,
14931
                                     GDALProgressFunc pfnProgress,
14932
                                     void *pProgressData)
14933
0
{
14934
0
    if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
14935
0
                                          bApproxOK, pdfMin, pdfMax, pdfMean,
14936
0
                                          pdfStdDev, pnValidCount) == CE_None)
14937
0
    {
14938
0
        return CE_None;
14939
0
    }
14940
0
    if (!bForce)
14941
0
        return CE_Warning;
14942
14943
0
    return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
14944
0
                                      pdfMean, pdfStdDev, pnValidCount,
14945
0
                                      pfnProgress, pProgressData);
14946
0
}
14947
14948
/************************************************************************/
14949
/*                           SetStatistics()                            */
14950
/************************************************************************/
14951
14952
bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
14953
                                   double dfMax, double dfMean, double dfStdDev,
14954
                                   GUInt64 nValidCount,
14955
                                   CSLConstList /* papszOptions */)
14956
0
{
14957
0
    if (!m_poPam)
14958
0
        return false;
14959
0
    m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
14960
0
                           dfMax, dfMean, dfStdDev, nValidCount);
14961
0
    return true;
14962
0
}
14963
14964
/************************************************************************/
14965
/*                           ClearStatistics()                          */
14966
/************************************************************************/
14967
14968
void GDALPamMDArray::ClearStatistics()
14969
0
{
14970
0
    if (!m_poPam)
14971
0
        return;
14972
0
    m_poPam->ClearStatistics(GetFullName(), GetContext());
14973
0
}
14974
14975
//! @endcond