Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdalmultidim.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Name:     gdalmultidim.cpp
4
 * Project:  GDAL Core
5
 * Purpose:  GDAL Core C++/Private implementation for multidimensional support
6
 * Author:   Even Rouault <even.rouault at spatialys.com>
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <assert.h>
15
#include <algorithm>
16
#include <limits>
17
#include <list>
18
#include <queue>
19
#include <set>
20
#include <utility>
21
#include <time.h>
22
23
#include <cmath>
24
#include <ctype.h>  // isalnum
25
26
#include "cpl_error_internal.h"
27
#include "cpl_float.h"
28
#include "gdal_priv.h"
29
#include "gdal_pam.h"
30
#include "gdal_pam_multidim.h"
31
#include "gdal_rat.h"
32
#include "gdal_utils.h"
33
#include "cpl_safemaths.hpp"
34
#include "memmultidim.h"
35
#include "ogrsf_frmts.h"
36
#include "gdalmultidim_priv.h"
37
38
#if defined(__clang__) || defined(_MSC_VER)
39
#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
40
#endif
41
42
/************************************************************************/
43
/*                         GDALMDArrayUnscaled                          */
44
/************************************************************************/
45
46
class GDALMDArrayUnscaled final : public GDALPamMDArray
47
{
48
  private:
49
    std::shared_ptr<GDALMDArray> m_poParent{};
50
    const GDALExtendedDataType m_dt;
51
    bool m_bHasNoData;
52
    const double m_dfScale;
53
    const double m_dfOffset;
54
    std::vector<GByte> m_abyRawNoData{};
55
56
  protected:
57
    explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
58
                                 double dfScale, double dfOffset,
59
                                 double dfOverriddenDstNodata, GDALDataType eDT)
60
0
        : GDALAbstractMDArray(std::string(),
61
0
                              "Unscaled view of " + poParent->GetFullName()),
62
0
          GDALPamMDArray(
63
0
              std::string(), "Unscaled view of " + poParent->GetFullName(),
64
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
65
0
          m_poParent(std::move(poParent)),
66
0
          m_dt(GDALExtendedDataType::Create(eDT)),
67
0
          m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
68
0
          m_dfScale(dfScale), m_dfOffset(dfOffset)
69
0
    {
70
0
        m_abyRawNoData.resize(m_dt.GetSize());
71
0
        const auto eNonComplexDT =
72
0
            GDALGetNonComplexDataType(m_dt.GetNumericDataType());
73
0
        GDALCopyWords64(
74
0
            &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
75
0
            eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
76
0
            GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
77
0
    }
78
79
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
80
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
81
               const GDALExtendedDataType &bufferDataType,
82
               void *pDstBuffer) const override;
83
84
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
85
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
86
                const GDALExtendedDataType &bufferDataType,
87
                const void *pSrcBuffer) override;
88
89
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
90
                     CSLConstList papszOptions) const override
91
0
    {
92
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
93
0
    }
94
95
  public:
96
    static std::shared_ptr<GDALMDArrayUnscaled>
97
    Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
98
           double dfOffset, double dfDstNodata, GDALDataType eDT)
99
0
    {
100
0
        auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
101
0
            poParent, dfScale, dfOffset, dfDstNodata, eDT)));
102
0
        newAr->SetSelf(newAr);
103
0
        return newAr;
104
0
    }
105
106
    bool IsWritable() const override
107
0
    {
108
0
        return m_poParent->IsWritable();
109
0
    }
110
111
    const std::string &GetFilename() const override
112
0
    {
113
0
        return m_poParent->GetFilename();
114
0
    }
115
116
    const std::vector<std::shared_ptr<GDALDimension>> &
117
    GetDimensions() const override
118
0
    {
119
0
        return m_poParent->GetDimensions();
120
0
    }
121
122
    const GDALExtendedDataType &GetDataType() const override
123
0
    {
124
0
        return m_dt;
125
0
    }
126
127
    const std::string &GetUnit() const override
128
0
    {
129
0
        return m_poParent->GetUnit();
130
0
    }
131
132
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
133
0
    {
134
0
        return m_poParent->GetSpatialRef();
135
0
    }
136
137
    const void *GetRawNoDataValue() const override
138
0
    {
139
0
        return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
140
0
    }
141
142
    bool SetRawNoDataValue(const void *pRawNoData) override
143
0
    {
144
0
        m_bHasNoData = true;
145
0
        memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
146
0
        return true;
147
0
    }
148
149
    std::vector<GUInt64> GetBlockSize() const override
150
0
    {
151
0
        return m_poParent->GetBlockSize();
152
0
    }
153
154
    std::shared_ptr<GDALAttribute>
155
    GetAttribute(const std::string &osName) const override
156
0
    {
157
0
        return m_poParent->GetAttribute(osName);
158
0
    }
159
160
    std::vector<std::shared_ptr<GDALAttribute>>
161
    GetAttributes(CSLConstList papszOptions = nullptr) const override
162
0
    {
163
0
        return m_poParent->GetAttributes(papszOptions);
164
0
    }
165
166
    bool SetUnit(const std::string &osUnit) override
167
0
    {
168
0
        return m_poParent->SetUnit(osUnit);
169
0
    }
170
171
    bool SetSpatialRef(const OGRSpatialReference *poSRS) override
172
0
    {
173
0
        return m_poParent->SetSpatialRef(poSRS);
174
0
    }
175
176
    std::shared_ptr<GDALAttribute>
177
    CreateAttribute(const std::string &osName,
178
                    const std::vector<GUInt64> &anDimensions,
179
                    const GDALExtendedDataType &oDataType,
180
                    CSLConstList papszOptions = nullptr) override
181
0
    {
182
0
        return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
183
0
                                           papszOptions);
184
0
    }
185
};
186
187
/************************************************************************/
188
/*                         ~GDALIHasAttribute()                         */
189
/************************************************************************/
190
191
0
GDALIHasAttribute::~GDALIHasAttribute() = default;
192
193
/************************************************************************/
194
/*                            GetAttribute()                            */
195
/************************************************************************/
196
197
/** Return an attribute by its name.
198
 *
199
 * If the attribute does not exist, nullptr should be silently returned.
200
 *
201
 * @note Driver implementation: this method will fallback to
202
 * GetAttributeFromAttributes() is not explicitly implemented
203
 *
204
 * Drivers known to implement it for groups and arrays: MEM, netCDF.
205
 *
206
 * This is the same as the C function GDALGroupGetAttribute() or
207
 * GDALMDArrayGetAttribute().
208
 *
209
 * @param osName Attribute name
210
 * @return the attribute, or nullptr if it does not exist or an error occurred.
211
 */
212
std::shared_ptr<GDALAttribute>
213
GDALIHasAttribute::GetAttribute(const std::string &osName) const
214
0
{
215
0
    return GetAttributeFromAttributes(osName);
216
0
}
217
218
/************************************************************************/
219
/*                     GetAttributeFromAttributes()                     */
220
/************************************************************************/
221
222
/** Possible fallback implementation for GetAttribute() using GetAttributes().
223
 */
224
std::shared_ptr<GDALAttribute>
225
GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
226
0
{
227
0
    auto attrs(GetAttributes());
228
0
    for (const auto &attr : attrs)
229
0
    {
230
0
        if (attr->GetName() == osName)
231
0
            return attr;
232
0
    }
233
0
    return nullptr;
234
0
}
235
236
/************************************************************************/
237
/*                           GetAttributes()                            */
238
/************************************************************************/
239
240
/** Return the list of attributes contained in a GDALMDArray or GDALGroup.
241
 *
242
 * If the attribute does not exist, nullptr should be silently returned.
243
 *
244
 * @note Driver implementation: optionally implemented. If implemented,
245
 * GetAttribute() should also be implemented.
246
 *
247
 * Drivers known to implement it for groups and arrays: MEM, netCDF.
248
 *
249
 * This is the same as the C function GDALGroupGetAttributes() or
250
 * GDALMDArrayGetAttributes().
251
252
 * @param papszOptions Driver specific options determining how attributes
253
 * should be retrieved. Pass nullptr for default behavior.
254
 *
255
 * @return the attributes.
256
 */
257
std::vector<std::shared_ptr<GDALAttribute>>
258
GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
259
0
{
260
0
    return {};
261
0
}
262
263
/************************************************************************/
264
/*                          CreateAttribute()                           */
265
/************************************************************************/
266
267
/** Create an attribute within a GDALMDArray or GDALGroup.
268
 *
269
 * The attribute might not be "physically" created until a value is written
270
 * into it.
271
 *
272
 * Optionally implemented.
273
 *
274
 * Drivers known to implement it: MEM, netCDF
275
 *
276
 * This is the same as the C function GDALGroupCreateAttribute() or
277
 * GDALMDArrayCreateAttribute()
278
 *
279
 * @param osName Attribute name.
280
 * @param anDimensions List of dimension sizes, ordered from the slowest varying
281
 *                     dimension first to the fastest varying dimension last.
282
 *                     Empty for a scalar attribute (common case)
283
 * @param oDataType  Attribute data type.
284
 * @param papszOptions Driver specific options determining how the attribute.
285
 * should be created.
286
 *
287
 * @return the new attribute, or nullptr if case of error
288
 */
289
std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
290
    CPL_UNUSED const std::string &osName,
291
    CPL_UNUSED const std::vector<GUInt64> &anDimensions,
292
    CPL_UNUSED const GDALExtendedDataType &oDataType,
293
    CPL_UNUSED CSLConstList papszOptions)
294
0
{
295
0
    CPLError(CE_Failure, CPLE_NotSupported,
296
0
             "CreateAttribute() not implemented");
297
0
    return nullptr;
298
0
}
299
300
/************************************************************************/
301
/*                          DeleteAttribute()                           */
302
/************************************************************************/
303
304
/** Delete an attribute from a GDALMDArray or GDALGroup.
305
 *
306
 * Optionally implemented.
307
 *
308
 * After this call, if a previously obtained instance of the deleted object
309
 * is still alive, no method other than for freeing it should be invoked.
310
 *
311
 * Drivers known to implement it: MEM, netCDF
312
 *
313
 * This is the same as the C function GDALGroupDeleteAttribute() or
314
 * GDALMDArrayDeleteAttribute()
315
 *
316
 * @param osName Attribute name.
317
 * @param papszOptions Driver specific options determining how the attribute.
318
 * should be deleted.
319
 *
320
 * @return true in case of success
321
 * @since GDAL 3.8
322
 */
323
bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
324
                                        CPL_UNUSED CSLConstList papszOptions)
325
0
{
326
0
    CPLError(CE_Failure, CPLE_NotSupported,
327
0
             "DeleteAttribute() not implemented");
328
0
    return false;
329
0
}
330
331
/************************************************************************/
332
/*                             GDALGroup()                              */
333
/************************************************************************/
334
335
//! @cond Doxygen_Suppress
336
GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
337
                     const std::string &osContext)
338
0
    : m_osName(osParentName.empty() ? "/" : osName),
339
      m_osFullName(
340
0
          !osParentName.empty()
341
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
342
0
              : "/"),
343
0
      m_osContext(osContext)
344
0
{
345
0
}
346
347
//! @endcond
348
349
/************************************************************************/
350
/*                             ~GDALGroup()                             */
351
/************************************************************************/
352
353
0
GDALGroup::~GDALGroup() = default;
354
355
/************************************************************************/
356
/*                          GetMDArrayNames()                           */
357
/************************************************************************/
358
359
/** Return the list of multidimensional array names contained in this group.
360
 *
361
 * @note Driver implementation: optionally implemented. If implemented,
362
 * OpenMDArray() should also be implemented.
363
 *
364
 * Drivers known to implement it: MEM, netCDF.
365
 *
366
 * This is the same as the C function GDALGroupGetMDArrayNames().
367
 *
368
 * @param papszOptions Driver specific options determining how arrays
369
 * should be retrieved. Pass nullptr for default behavior.
370
 *
371
 * @return the array names.
372
 */
373
std::vector<std::string>
374
GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
375
0
{
376
0
    return {};
377
0
}
378
379
/************************************************************************/
380
/*                    GetMDArrayFullNamesRecursive()                    */
381
/************************************************************************/
382
383
/** Return the list of multidimensional array full names contained in this
384
 * group and its subgroups.
385
 *
386
 * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
387
 *
388
 * @param papszGroupOptions Driver specific options determining how groups
389
 * should be retrieved. Pass nullptr for default behavior.
390
 * @param papszArrayOptions Driver specific options determining how arrays
391
 * should be retrieved. Pass nullptr for default behavior.
392
 *
393
 * @return the array full names.
394
 *
395
 * @since 3.11
396
 */
397
std::vector<std::string>
398
GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
399
                                        CSLConstList papszArrayOptions) const
400
0
{
401
0
    std::vector<std::string> ret;
402
0
    std::list<std::shared_ptr<GDALGroup>> stackGroups;
403
0
    stackGroups.push_back(nullptr);  // nullptr means this
404
0
    while (!stackGroups.empty())
405
0
    {
406
0
        std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
407
0
        stackGroups.erase(stackGroups.begin());
408
0
        const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
409
0
        for (const std::string &arrayName :
410
0
             poCurGroup->GetMDArrayNames(papszArrayOptions))
411
0
        {
412
0
            std::string osFullName = poCurGroup->GetFullName();
413
0
            if (!osFullName.empty() && osFullName.back() != '/')
414
0
                osFullName += '/';
415
0
            osFullName += arrayName;
416
0
            ret.push_back(std::move(osFullName));
417
0
        }
418
0
        auto insertionPoint = stackGroups.begin();
419
0
        for (const auto &osSubGroup :
420
0
             poCurGroup->GetGroupNames(papszGroupOptions))
421
0
        {
422
0
            auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
423
0
            if (poSubGroup)
424
0
                stackGroups.insert(insertionPoint, std::move(poSubGroup));
425
0
        }
426
0
    }
427
428
0
    return ret;
429
0
}
430
431
/************************************************************************/
432
/*                            OpenMDArray()                             */
433
/************************************************************************/
434
435
/** Open and return a multidimensional array.
436
 *
437
 * @note Driver implementation: optionally implemented. If implemented,
438
 * GetMDArrayNames() should also be implemented.
439
 *
440
 * Drivers known to implement it: MEM, netCDF.
441
 *
442
 * This is the same as the C function GDALGroupOpenMDArray().
443
 *
444
 * @param osName Array name.
445
 * @param papszOptions Driver specific options determining how the array should
446
 * be opened.  Pass nullptr for default behavior.
447
 *
448
 * @return the array, or nullptr.
449
 */
450
std::shared_ptr<GDALMDArray>
451
GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
452
                       CPL_UNUSED CSLConstList papszOptions) const
453
0
{
454
0
    return nullptr;
455
0
}
456
457
/************************************************************************/
458
/*                           GetGroupNames()                            */
459
/************************************************************************/
460
461
/** Return the list of sub-groups contained in this group.
462
 *
463
 * @note Driver implementation: optionally implemented. If implemented,
464
 * OpenGroup() should also be implemented.
465
 *
466
 * Drivers known to implement it: MEM, netCDF.
467
 *
468
 * This is the same as the C function GDALGroupGetGroupNames().
469
 *
470
 * @param papszOptions Driver specific options determining how groups
471
 * should be retrieved. Pass nullptr for default behavior.
472
 *
473
 * @return the group names.
474
 */
475
std::vector<std::string>
476
GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
477
0
{
478
0
    return {};
479
0
}
480
481
/************************************************************************/
482
/*                             OpenGroup()                              */
483
/************************************************************************/
484
485
/** Open and return a sub-group.
486
 *
487
 * @note Driver implementation: optionally implemented. If implemented,
488
 * GetGroupNames() should also be implemented.
489
 *
490
 * Drivers known to implement it: MEM, netCDF.
491
 *
492
 * This is the same as the C function GDALGroupOpenGroup().
493
 *
494
 * @param osName Sub-group name.
495
 * @param papszOptions Driver specific options determining how the sub-group
496
 * should be opened.  Pass nullptr for default behavior.
497
 *
498
 * @return the group, or nullptr.
499
 */
500
std::shared_ptr<GDALGroup>
501
GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
502
                     CPL_UNUSED CSLConstList papszOptions) const
503
0
{
504
0
    return nullptr;
505
0
}
506
507
/************************************************************************/
508
/*                        GetVectorLayerNames()                         */
509
/************************************************************************/
510
511
/** Return the list of layer names contained in this group.
512
 *
513
 * @note Driver implementation: optionally implemented. If implemented,
514
 * OpenVectorLayer() should also be implemented.
515
 *
516
 * Drivers known to implement it: OpenFileGDB, FileGDB
517
 *
518
 * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
519
 * GDALDataset::GetLayer() should then be used.
520
 *
521
 * This is the same as the C function GDALGroupGetVectorLayerNames().
522
 *
523
 * @param papszOptions Driver specific options determining how layers
524
 * should be retrieved. Pass nullptr for default behavior.
525
 *
526
 * @return the vector layer names.
527
 * @since GDAL 3.4
528
 */
529
std::vector<std::string>
530
GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
531
0
{
532
0
    return {};
533
0
}
534
535
/************************************************************************/
536
/*                          OpenVectorLayer()                           */
537
/************************************************************************/
538
539
/** Open and return a vector layer.
540
 *
541
 * Due to the historical ownership of OGRLayer* by GDALDataset*, the
542
 * lifetime of the returned OGRLayer* is linked to the one of the owner
543
 * dataset (contrary to the general design of this class where objects can be
544
 * used independently of the object that returned them)
545
 *
546
 * @note Driver implementation: optionally implemented. If implemented,
547
 * GetVectorLayerNames() should also be implemented.
548
 *
549
 * Drivers known to implement it: MEM, netCDF.
550
 *
551
 * This is the same as the C function GDALGroupOpenVectorLayer().
552
 *
553
 * @param osName Vector layer name.
554
 * @param papszOptions Driver specific options determining how the layer should
555
 * be opened.  Pass nullptr for default behavior.
556
 *
557
 * @return the group, or nullptr.
558
 */
559
OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
560
                                     CPL_UNUSED CSLConstList papszOptions) const
561
0
{
562
0
    return nullptr;
563
0
}
564
565
/************************************************************************/
566
/*                           GetDimensions()                            */
567
/************************************************************************/
568
569
/** Return the list of dimensions contained in this group and used by its
570
 * arrays.
571
 *
572
 * This is for dimensions that can potentially be used by several arrays.
573
 * Not all drivers might implement this. To retrieve the dimensions used by
574
 * a specific array, use GDALMDArray::GetDimensions().
575
 *
576
 * Drivers known to implement it: MEM, netCDF
577
 *
578
 * This is the same as the C function GDALGroupGetDimensions().
579
 *
580
 * @param papszOptions Driver specific options determining how groups
581
 * should be retrieved. Pass nullptr for default behavior.
582
 *
583
 * @return the dimensions.
584
 */
585
std::vector<std::shared_ptr<GDALDimension>>
586
GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
587
0
{
588
0
    return {};
589
0
}
590
591
/************************************************************************/
592
/*                         GetStructuralInfo()                          */
593
/************************************************************************/
594
595
/** Return structural information on the group.
596
 *
597
 * This may be the compression, etc..
598
 *
599
 * The return value should not be freed and is valid until GDALGroup is
600
 * released or this function called again.
601
 *
602
 * This is the same as the C function GDALGroupGetStructuralInfo().
603
 */
604
CSLConstList GDALGroup::GetStructuralInfo() const
605
0
{
606
0
    return nullptr;
607
0
}
608
609
/************************************************************************/
610
/*                            CreateGroup()                             */
611
/************************************************************************/
612
613
/** Create a sub-group within a group.
614
 *
615
 * Optionally implemented by drivers.
616
 *
617
 * Drivers known to implement it: MEM, netCDF
618
 *
619
 * This is the same as the C function GDALGroupCreateGroup().
620
 *
621
 * @param osName Sub-group name.
622
 * @param papszOptions Driver specific options determining how the sub-group
623
 * should be created.
624
 *
625
 * @return the new sub-group, or nullptr in case of error.
626
 */
627
std::shared_ptr<GDALGroup>
628
GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
629
                       CPL_UNUSED CSLConstList papszOptions)
630
0
{
631
0
    CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
632
0
    return nullptr;
633
0
}
634
635
/************************************************************************/
636
/*                            DeleteGroup()                             */
637
/************************************************************************/
638
639
/** Delete a sub-group from a group.
640
 *
641
 * Optionally implemented.
642
 *
643
 * After this call, if a previously obtained instance of the deleted object
644
 * is still alive, no method other than for freeing it should be invoked.
645
 *
646
 * Drivers known to implement it: MEM, Zarr
647
 *
648
 * This is the same as the C function GDALGroupDeleteGroup().
649
 *
650
 * @param osName Sub-group name.
651
 * @param papszOptions Driver specific options determining how the group.
652
 * should be deleted.
653
 *
654
 * @return true in case of success
655
 * @since GDAL 3.8
656
 */
657
bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
658
                            CPL_UNUSED CSLConstList papszOptions)
659
0
{
660
0
    CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
661
0
    return false;
662
0
}
663
664
/************************************************************************/
665
/*                          CreateDimension()                           */
666
/************************************************************************/
667
668
/** Create a dimension within a group.
669
 *
670
 * @note Driver implementation: drivers supporting CreateDimension() should
671
 * implement this method, but do not have necessarily to implement
672
 * GDALGroup::GetDimensions().
673
 *
674
 * Drivers known to implement it: MEM, netCDF
675
 *
676
 * This is the same as the C function GDALGroupCreateDimension().
677
 *
678
 * @param osName Dimension name.
679
 * @param osType Dimension type (might be empty, and ignored by drivers)
680
 * @param osDirection Dimension direction (might be empty, and ignored by
681
 * drivers)
682
 * @param nSize  Number of values indexed by this dimension. Should be > 0.
683
 * @param papszOptions Driver specific options determining how the dimension
684
 * should be created.
685
 *
686
 * @return the new dimension, or nullptr if case of error
687
 */
688
std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
689
    CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
690
    CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
691
    CPL_UNUSED CSLConstList papszOptions)
692
0
{
693
0
    CPLError(CE_Failure, CPLE_NotSupported,
694
0
             "CreateDimension() not implemented");
695
0
    return nullptr;
696
0
}
697
698
/************************************************************************/
699
/*                           CreateMDArray()                            */
700
/************************************************************************/
701
702
/** Create a multidimensional array within a group.
703
 *
704
 * It is recommended that the GDALDimension objects passed in aoDimensions
705
 * belong to this group, either by retrieving them with GetDimensions()
706
 * or creating a new one with CreateDimension().
707
 *
708
 * Optionally implemented.
709
 *
710
 * Drivers known to implement it: MEM, netCDF
711
 *
712
 * This is the same as the C function GDALGroupCreateMDArray().
713
 *
714
 * @note Driver implementation: drivers should take into account the possibility
715
 * that GDALDimension object passed in aoDimensions might belong to a different
716
 * group / dataset / driver and act accordingly.
717
 *
718
 * @param osName Array name.
719
 * @param aoDimensions List of dimensions, ordered from the slowest varying
720
 *                     dimension first to the fastest varying dimension last.
721
 *                     Might be empty for a scalar array (if supported by
722
 * driver)
723
 * @param oDataType  Array data type.
724
 * @param papszOptions Driver specific options determining how the array
725
 * should be created.
726
 *
727
 * @return the new array, or nullptr in case of error
728
 */
729
std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
730
    CPL_UNUSED const std::string &osName,
731
    CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
732
    CPL_UNUSED const GDALExtendedDataType &oDataType,
733
    CPL_UNUSED CSLConstList papszOptions)
734
0
{
735
0
    CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
736
0
    return nullptr;
737
0
}
738
739
/************************************************************************/
740
/*                           DeleteMDArray()                            */
741
/************************************************************************/
742
743
/** Delete an array from a group.
744
 *
745
 * Optionally implemented.
746
 *
747
 * After this call, if a previously obtained instance of the deleted object
748
 * is still alive, no method other than for freeing it should be invoked.
749
 *
750
 * Drivers known to implement it: MEM, Zarr
751
 *
752
 * This is the same as the C function GDALGroupDeleteMDArray().
753
 *
754
 * @param osName Arrayname.
755
 * @param papszOptions Driver specific options determining how the array.
756
 * should be deleted.
757
 *
758
 * @return true in case of success
759
 * @since GDAL 3.8
760
 */
761
bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
762
                              CPL_UNUSED CSLConstList papszOptions)
763
0
{
764
0
    CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
765
0
    return false;
766
0
}
767
768
/************************************************************************/
769
/*                          GetTotalCopyCost()                          */
770
/************************************************************************/
771
772
/** Return a total "cost" to copy the group.
773
 *
774
 * Used as a parameter for CopFrom()
775
 */
776
GUInt64 GDALGroup::GetTotalCopyCost() const
777
0
{
778
0
    GUInt64 nCost = COPY_COST;
779
0
    nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
780
781
0
    auto groupNames = GetGroupNames();
782
0
    for (const auto &name : groupNames)
783
0
    {
784
0
        auto subGroup = OpenGroup(name);
785
0
        if (subGroup)
786
0
        {
787
0
            nCost += subGroup->GetTotalCopyCost();
788
0
        }
789
0
    }
790
791
0
    auto arrayNames = GetMDArrayNames();
792
0
    for (const auto &name : arrayNames)
793
0
    {
794
0
        auto array = OpenMDArray(name);
795
0
        if (array)
796
0
        {
797
0
            nCost += array->GetTotalCopyCost();
798
0
        }
799
0
    }
800
0
    return nCost;
801
0
}
802
803
/************************************************************************/
804
/*                              CopyFrom()                              */
805
/************************************************************************/
806
807
/** Copy the content of a group into a new (generally empty) group.
808
 *
809
 * @param poDstRootGroup Destination root group. Must NOT be nullptr.
810
 * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
811
 *                   of some output drivers this is not recommended)
812
 * @param poSrcGroup Source group. Must NOT be nullptr.
813
 * @param bStrict Whether to enable strict mode. In strict mode, any error will
814
 *                stop the copy. In relaxed mode, the copy will be attempted to
815
 *                be pursued.
816
 * @param nCurCost  Should be provided as a variable initially set to 0.
817
 * @param nTotalCost Total cost from GetTotalCopyCost().
818
 * @param pfnProgress Progress callback, or nullptr.
819
 * @param pProgressData Progress user data, or nulptr.
820
 * @param papszOptions Creation options. Currently, only array creation
821
 *                     options are supported. They must be prefixed with
822
 * "ARRAY:" . The scope may be further restricted to arrays of a certain
823
 *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
824
 *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
825
 *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
826
 *                     Restriction to arrays of a given name is done with adding
827
 *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
828
 *                     a full qualified name.
829
 *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
830
 * be used to ask (non indexing) variables of type Float32 or Float64 to be
831
 * scaled to UInt16 with scale and offset values being computed from the minimum
832
 * and maximum of the source array. The integer data type used can be set with
833
 *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
834
 *
835
 * @return true in case of success (or partial success if bStrict == false).
836
 */
837
bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
838
                         GDALDataset *poSrcDS,
839
                         const std::shared_ptr<GDALGroup> &poSrcGroup,
840
                         bool bStrict, GUInt64 &nCurCost,
841
                         const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
842
                         void *pProgressData, CSLConstList papszOptions)
843
0
{
844
0
    if (pfnProgress == nullptr)
845
0
        pfnProgress = GDALDummyProgress;
846
847
0
#define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
848
0
    if (!(x))                                                                  \
849
0
    {                                                                          \
850
0
        if (bStrict)                                                           \
851
0
            return false;                                                      \
852
0
        continue;                                                              \
853
0
    }                                                                          \
854
0
    (void)0
855
856
0
    try
857
0
    {
858
0
        nCurCost += GDALGroup::COPY_COST;
859
860
0
        const auto srcDims = poSrcGroup->GetDimensions();
861
0
        std::map<std::string, std::shared_ptr<GDALDimension>>
862
0
            mapExistingDstDims;
863
0
        std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
864
0
        for (const auto &dim : srcDims)
865
0
        {
866
0
            auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
867
0
                                          dim->GetDirection(), dim->GetSize());
868
0
            EXIT_OR_CONTINUE_IF_NULL(dstDim);
869
0
            mapExistingDstDims[dim->GetName()] = std::move(dstDim);
870
0
            auto poIndexingVarSrc(dim->GetIndexingVariable());
871
0
            if (poIndexingVarSrc)
872
0
            {
873
0
                mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
874
0
                                                       ->GetName()] =
875
0
                    dim->GetName();
876
0
            }
877
0
        }
878
879
0
        auto attrs = poSrcGroup->GetAttributes();
880
0
        for (const auto &attr : attrs)
881
0
        {
882
0
            auto dstAttr =
883
0
                CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
884
0
                                attr->GetDataType());
885
0
            EXIT_OR_CONTINUE_IF_NULL(dstAttr);
886
0
            auto raw(attr->ReadAsRaw());
887
0
            if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
888
0
                return false;
889
0
        }
890
0
        if (!attrs.empty())
891
0
        {
892
0
            nCurCost += attrs.size() * GDALAttribute::COPY_COST;
893
0
            if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
894
0
                return false;
895
0
        }
896
897
0
        const auto CopyArray =
898
0
            [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
899
0
             &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
900
0
             papszOptions, bStrict, &nCurCost,
901
0
             nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
902
0
        {
903
            // Map source dimensions to target dimensions
904
0
            std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
905
0
            const auto &srcArrayDims(srcArray->GetDimensions());
906
0
            for (const auto &dim : srcArrayDims)
907
0
            {
908
0
                auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
909
0
                    dim->GetFullName());
910
0
                if (dstDim && dstDim->GetSize() == dim->GetSize())
911
0
                {
912
0
                    dstArrayDims.emplace_back(dstDim);
913
0
                }
914
0
                else
915
0
                {
916
0
                    auto oIter = mapExistingDstDims.find(dim->GetName());
917
0
                    if (oIter != mapExistingDstDims.end() &&
918
0
                        oIter->second->GetSize() == dim->GetSize())
919
0
                    {
920
0
                        dstArrayDims.emplace_back(oIter->second);
921
0
                    }
922
0
                    else
923
0
                    {
924
0
                        std::string newDimName;
925
0
                        if (oIter == mapExistingDstDims.end())
926
0
                        {
927
0
                            newDimName = dim->GetName();
928
0
                        }
929
0
                        else
930
0
                        {
931
0
                            std::string newDimNamePrefix(srcArray->GetName() +
932
0
                                                         '_' + dim->GetName());
933
0
                            newDimName = newDimNamePrefix;
934
0
                            int nIterCount = 2;
935
0
                            while (
936
0
                                cpl::contains(mapExistingDstDims, newDimName))
937
0
                            {
938
0
                                newDimName = newDimNamePrefix +
939
0
                                             CPLSPrintf("_%d", nIterCount);
940
0
                                nIterCount++;
941
0
                            }
942
0
                        }
943
0
                        dstDim = CreateDimension(newDimName, dim->GetType(),
944
0
                                                 dim->GetDirection(),
945
0
                                                 dim->GetSize());
946
0
                        if (!dstDim)
947
0
                            return false;
948
0
                        mapExistingDstDims[newDimName] = dstDim;
949
0
                        dstArrayDims.emplace_back(dstDim);
950
0
                    }
951
0
                }
952
0
            }
953
954
0
            CPLStringList aosArrayCO;
955
0
            bool bAutoScale = false;
956
0
            GDALDataType eAutoScaleType = GDT_UInt16;
957
0
            for (const char *pszItem : cpl::Iterate(papszOptions))
958
0
            {
959
0
                if (STARTS_WITH_CI(pszItem, "ARRAY:"))
960
0
                {
961
0
                    const char *pszOption = pszItem + strlen("ARRAY:");
962
0
                    if (STARTS_WITH_CI(pszOption, "IF(DIM="))
963
0
                    {
964
0
                        const char *pszNext = strchr(pszOption, ':');
965
0
                        if (pszNext != nullptr)
966
0
                        {
967
0
                            int nDim = atoi(pszOption + strlen("IF(DIM="));
968
0
                            if (static_cast<size_t>(nDim) ==
969
0
                                dstArrayDims.size())
970
0
                            {
971
0
                                pszOption = pszNext + 1;
972
0
                            }
973
0
                            else
974
0
                            {
975
0
                                pszOption = nullptr;
976
0
                            }
977
0
                        }
978
0
                    }
979
0
                    else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
980
0
                    {
981
0
                        const char *pszName = pszOption + strlen("IF(NAME=");
982
0
                        const char *pszNext = strchr(pszName, ':');
983
0
                        if (pszNext != nullptr && pszNext > pszName &&
984
0
                            pszNext[-1] == ')')
985
0
                        {
986
0
                            CPLString osName;
987
0
                            osName.assign(pszName, pszNext - pszName - 1);
988
0
                            if (osName == srcArray->GetName() ||
989
0
                                osName == srcArray->GetFullName())
990
0
                            {
991
0
                                pszOption = pszNext + 1;
992
0
                            }
993
0
                            else
994
0
                            {
995
0
                                pszOption = nullptr;
996
0
                            }
997
0
                        }
998
0
                    }
999
0
                    if (pszOption)
1000
0
                    {
1001
0
                        if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
1002
0
                        {
1003
0
                            bAutoScale =
1004
0
                                CPLTestBool(pszOption + strlen("AUTOSCALE="));
1005
0
                        }
1006
0
                        else if (STARTS_WITH_CI(pszOption,
1007
0
                                                "AUTOSCALE_DATA_TYPE="))
1008
0
                        {
1009
0
                            const char *pszDataType =
1010
0
                                pszOption + strlen("AUTOSCALE_DATA_TYPE=");
1011
0
                            eAutoScaleType = GDALGetDataTypeByName(pszDataType);
1012
0
                            if (GDALDataTypeIsComplex(eAutoScaleType) ||
1013
0
                                GDALDataTypeIsFloating(eAutoScaleType))
1014
0
                            {
1015
0
                                CPLError(CE_Failure, CPLE_NotSupported,
1016
0
                                         "Unsupported value for "
1017
0
                                         "AUTOSCALE_DATA_TYPE");
1018
0
                                return false;
1019
0
                            }
1020
0
                        }
1021
0
                        else
1022
0
                        {
1023
0
                            aosArrayCO.AddString(pszOption);
1024
0
                        }
1025
0
                    }
1026
0
                }
1027
0
            }
1028
1029
0
            if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
1030
0
            {
1031
0
                const auto anBlockSize = srcArray->GetBlockSize();
1032
0
                std::string osBlockSize;
1033
0
                for (auto v : anBlockSize)
1034
0
                {
1035
0
                    if (v == 0)
1036
0
                    {
1037
0
                        osBlockSize.clear();
1038
0
                        break;
1039
0
                    }
1040
0
                    if (!osBlockSize.empty())
1041
0
                        osBlockSize += ',';
1042
0
                    osBlockSize += std::to_string(v);
1043
0
                }
1044
0
                if (!osBlockSize.empty())
1045
0
                    aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
1046
0
            }
1047
1048
0
            auto oIterDimName =
1049
0
                mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
1050
0
            const auto &srcArrayType = srcArray->GetDataType();
1051
1052
0
            std::shared_ptr<GDALMDArray> dstArray;
1053
1054
            // Only autoscale non-indexing variables
1055
0
            bool bHasOffset = false;
1056
0
            bool bHasScale = false;
1057
0
            if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
1058
0
                (srcArrayType.GetNumericDataType() == GDT_Float16 ||
1059
0
                 srcArrayType.GetNumericDataType() == GDT_Float32 ||
1060
0
                 srcArrayType.GetNumericDataType() == GDT_Float64) &&
1061
0
                srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
1062
0
                srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
1063
0
                oIterDimName == mapSrcVariableNameToIndexedDimName.end())
1064
0
            {
1065
0
                constexpr bool bApproxOK = false;
1066
0
                constexpr bool bForce = true;
1067
0
                double dfMin = 0.0;
1068
0
                double dfMax = 0.0;
1069
0
                if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
1070
0
                                            nullptr, nullptr, nullptr, nullptr,
1071
0
                                            nullptr) != CE_None)
1072
0
                {
1073
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1074
0
                             "Could not retrieve statistics for array %s",
1075
0
                             srcArray->GetName().c_str());
1076
0
                    return false;
1077
0
                }
1078
0
                double dfDTMin = 0;
1079
0
                double dfDTMax = 0;
1080
0
#define setDTMinMax(ctype)                                                     \
1081
0
    do                                                                         \
1082
0
    {                                                                          \
1083
0
        dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
1084
0
        dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
1085
0
    } while (0)
1086
1087
0
                switch (eAutoScaleType)
1088
0
                {
1089
0
                    case GDT_UInt8:
1090
0
                        setDTMinMax(GByte);
1091
0
                        break;
1092
0
                    case GDT_Int8:
1093
0
                        setDTMinMax(GInt8);
1094
0
                        break;
1095
0
                    case GDT_UInt16:
1096
0
                        setDTMinMax(GUInt16);
1097
0
                        break;
1098
0
                    case GDT_Int16:
1099
0
                        setDTMinMax(GInt16);
1100
0
                        break;
1101
0
                    case GDT_UInt32:
1102
0
                        setDTMinMax(GUInt32);
1103
0
                        break;
1104
0
                    case GDT_Int32:
1105
0
                        setDTMinMax(GInt32);
1106
0
                        break;
1107
0
                    case GDT_UInt64:
1108
0
                        setDTMinMax(std::uint64_t);
1109
0
                        break;
1110
0
                    case GDT_Int64:
1111
0
                        setDTMinMax(std::int64_t);
1112
0
                        break;
1113
0
                    case GDT_Float16:
1114
0
                    case GDT_Float32:
1115
0
                    case GDT_Float64:
1116
0
                    case GDT_Unknown:
1117
0
                    case GDT_CInt16:
1118
0
                    case GDT_CInt32:
1119
0
                    case GDT_CFloat16:
1120
0
                    case GDT_CFloat32:
1121
0
                    case GDT_CFloat64:
1122
0
                    case GDT_TypeCount:
1123
0
                        CPLAssert(false);
1124
0
                }
1125
1126
0
                dstArray =
1127
0
                    CreateMDArray(srcArray->GetName(), dstArrayDims,
1128
0
                                  GDALExtendedDataType::Create(eAutoScaleType),
1129
0
                                  aosArrayCO.List());
1130
0
                if (!dstArray)
1131
0
                    return !bStrict;
1132
1133
0
                if (srcArray->GetRawNoDataValue() != nullptr)
1134
0
                {
1135
                    // If there's a nodata value in the source array, reserve
1136
                    // DTMax for that purpose in the target scaled array
1137
0
                    if (!dstArray->SetNoDataValue(dfDTMax))
1138
0
                    {
1139
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1140
0
                                 "Cannot set nodata value");
1141
0
                        return false;
1142
0
                    }
1143
0
                    dfDTMax--;
1144
0
                }
1145
0
                const double dfScale =
1146
0
                    dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
1147
0
                const double dfOffset = dfMin - dfDTMin * dfScale;
1148
1149
0
                if (!dstArray->SetOffset(dfOffset) ||
1150
0
                    !dstArray->SetScale(dfScale))
1151
0
                {
1152
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1153
0
                             "Cannot set scale/offset");
1154
0
                    return false;
1155
0
                }
1156
1157
0
                auto poUnscaled = dstArray->GetUnscaled();
1158
0
                if (srcArray->GetRawNoDataValue() != nullptr)
1159
0
                {
1160
0
                    poUnscaled->SetNoDataValue(
1161
0
                        srcArray->GetNoDataValueAsDouble());
1162
0
                }
1163
1164
                // Copy source array into unscaled array
1165
0
                if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1166
0
                                          nCurCost, nTotalCost, pfnProgress,
1167
0
                                          pProgressData))
1168
0
                {
1169
0
                    return false;
1170
0
                }
1171
0
            }
1172
0
            else
1173
0
            {
1174
0
                dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
1175
0
                                         srcArrayType, aosArrayCO.List());
1176
0
                if (!dstArray)
1177
0
                    return !bStrict;
1178
1179
0
                if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
1180
0
                                        nCurCost, nTotalCost, pfnProgress,
1181
0
                                        pProgressData))
1182
0
                {
1183
0
                    return false;
1184
0
                }
1185
0
            }
1186
1187
            // If this array is the indexing variable of a dimension, link them
1188
            // together.
1189
0
            if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
1190
0
            {
1191
0
                auto oCorrespondingDimIter =
1192
0
                    mapExistingDstDims.find(oIterDimName->second);
1193
0
                if (oCorrespondingDimIter != mapExistingDstDims.end())
1194
0
                {
1195
0
                    CPLErrorStateBackuper oErrorStateBackuper(
1196
0
                        CPLQuietErrorHandler);
1197
0
                    oCorrespondingDimIter->second->SetIndexingVariable(
1198
0
                        std::move(dstArray));
1199
0
                }
1200
0
            }
1201
1202
0
            return true;
1203
0
        };
1204
1205
0
        const auto arrayNames = poSrcGroup->GetMDArrayNames();
1206
1207
        // Start by copying arrays that are indexing variables of dimensions
1208
0
        for (const auto &name : arrayNames)
1209
0
        {
1210
0
            auto srcArray = poSrcGroup->OpenMDArray(name);
1211
0
            EXIT_OR_CONTINUE_IF_NULL(srcArray);
1212
1213
0
            if (cpl::contains(mapSrcVariableNameToIndexedDimName,
1214
0
                              srcArray->GetName()))
1215
0
            {
1216
0
                if (!CopyArray(srcArray))
1217
0
                    return false;
1218
0
            }
1219
0
        }
1220
1221
        // Then copy regular arrays
1222
0
        for (const auto &name : arrayNames)
1223
0
        {
1224
0
            auto srcArray = poSrcGroup->OpenMDArray(name);
1225
0
            EXIT_OR_CONTINUE_IF_NULL(srcArray);
1226
1227
0
            if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
1228
0
                               srcArray->GetName()))
1229
0
            {
1230
0
                if (!CopyArray(srcArray))
1231
0
                    return false;
1232
0
            }
1233
0
        }
1234
1235
0
        const auto groupNames = poSrcGroup->GetGroupNames();
1236
0
        for (const auto &name : groupNames)
1237
0
        {
1238
0
            auto srcSubGroup = poSrcGroup->OpenGroup(name);
1239
0
            EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
1240
0
            auto dstSubGroup = CreateGroup(name);
1241
0
            EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
1242
0
            if (!dstSubGroup->CopyFrom(
1243
0
                    poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
1244
0
                    nTotalCost, pfnProgress, pProgressData, papszOptions))
1245
0
                return false;
1246
0
        }
1247
1248
0
        if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
1249
0
            return false;
1250
1251
0
        return true;
1252
0
    }
1253
0
    catch (const std::exception &e)
1254
0
    {
1255
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1256
0
        return false;
1257
0
    }
1258
0
}
1259
1260
/************************************************************************/
1261
/*                         GetInnerMostGroup()                          */
1262
/************************************************************************/
1263
1264
//! @cond Doxygen_Suppress
1265
const GDALGroup *
1266
GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
1267
                             std::shared_ptr<GDALGroup> &curGroupHolder,
1268
                             std::string &osLastPart) const
1269
0
{
1270
0
    if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
1271
0
        return nullptr;
1272
0
    const GDALGroup *poCurGroup = this;
1273
0
    CPLStringList aosTokens(
1274
0
        CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
1275
0
    if (aosTokens.size() == 0)
1276
0
    {
1277
0
        return nullptr;
1278
0
    }
1279
1280
0
    for (int i = 0; i < aosTokens.size() - 1; i++)
1281
0
    {
1282
0
        curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
1283
0
        if (!curGroupHolder)
1284
0
        {
1285
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
1286
0
                     aosTokens[i]);
1287
0
            return nullptr;
1288
0
        }
1289
0
        poCurGroup = curGroupHolder.get();
1290
0
    }
1291
0
    osLastPart = aosTokens[aosTokens.size() - 1];
1292
0
    return poCurGroup;
1293
0
}
1294
1295
//! @endcond
1296
1297
/************************************************************************/
1298
/*                      OpenMDArrayFromFullname()                       */
1299
/************************************************************************/
1300
1301
/** Get an array from its fully qualified name */
1302
std::shared_ptr<GDALMDArray>
1303
GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
1304
                                   CSLConstList papszOptions) const
1305
0
{
1306
0
    std::string osName;
1307
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1308
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1309
0
    if (poGroup == nullptr)
1310
0
        return nullptr;
1311
0
    return poGroup->OpenMDArray(osName, papszOptions);
1312
0
}
1313
1314
/************************************************************************/
1315
/*                     OpenAttributeFromFullname()                      */
1316
/************************************************************************/
1317
1318
/** Get an attribute from its fully qualified name */
1319
std::shared_ptr<GDALAttribute>
1320
GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
1321
                                     CSLConstList papszOptions) const
1322
0
{
1323
0
    const auto pos = osFullName.rfind('/');
1324
0
    if (pos == std::string::npos)
1325
0
        return nullptr;
1326
0
    const std::string attrName = osFullName.substr(pos + 1);
1327
0
    if (pos == 0)
1328
0
        return GetAttribute(attrName);
1329
0
    const std::string container = osFullName.substr(0, pos);
1330
0
    auto poArray = OpenMDArrayFromFullname(container, papszOptions);
1331
0
    if (poArray)
1332
0
        return poArray->GetAttribute(attrName);
1333
0
    auto poGroup = OpenGroupFromFullname(container, papszOptions);
1334
0
    if (poGroup)
1335
0
        return poGroup->GetAttribute(attrName);
1336
0
    return nullptr;
1337
0
}
1338
1339
/************************************************************************/
1340
/*                           ResolveMDArray()                           */
1341
/************************************************************************/
1342
1343
/** Locate an array in a group and its subgroups by name.
1344
 *
1345
 * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
1346
 * used
1347
 * Otherwise the search will start from the group identified by osStartingPath,
1348
 * and an array whose name is osName will be looked for in this group (if
1349
 * osStartingPath is empty or "/", then the current group is used). If there
1350
 * is no match, then a recursive descendant search will be made in its
1351
 * subgroups. If there is no match in the subgroups, then the parent (if
1352
 * existing) of the group pointed by osStartingPath will be used as the new
1353
 * starting point for the search.
1354
 *
1355
 * @param osName name, qualified or not
1356
 * @param osStartingPath fully qualified name of the (sub-)group from which
1357
 *                       the search should be started. If this is a non-empty
1358
 *                       string, the group on which this method is called should
1359
 *                       nominally be the root group (otherwise the path will
1360
 *                       be interpreted as from the current group)
1361
 * @param papszOptions options to pass to OpenMDArray()
1362
 * @since GDAL 3.2
1363
 */
1364
std::shared_ptr<GDALMDArray>
1365
GDALGroup::ResolveMDArray(const std::string &osName,
1366
                          const std::string &osStartingPath,
1367
                          CSLConstList papszOptions) const
1368
0
{
1369
0
    if (!osName.empty() && osName[0] == '/')
1370
0
    {
1371
0
        auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
1372
0
        if (poArray)
1373
0
            return poArray;
1374
0
    }
1375
0
    std::string osPath(osStartingPath);
1376
0
    std::set<std::string> oSetAlreadyVisited;
1377
1378
0
    while (true)
1379
0
    {
1380
0
        std::shared_ptr<GDALGroup> curGroupHolder;
1381
0
        std::shared_ptr<GDALGroup> poGroup;
1382
1383
0
        std::queue<std::shared_ptr<GDALGroup>> oQueue;
1384
0
        bool goOn = false;
1385
0
        if (osPath.empty() || osPath == "/")
1386
0
        {
1387
0
            goOn = true;
1388
0
        }
1389
0
        else
1390
0
        {
1391
0
            std::string osLastPart;
1392
0
            const GDALGroup *poGroupPtr =
1393
0
                GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1394
0
            if (poGroupPtr)
1395
0
                poGroup = poGroupPtr->OpenGroup(osLastPart);
1396
0
            if (poGroup &&
1397
0
                !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
1398
0
            {
1399
0
                oQueue.push(poGroup);
1400
0
                goOn = true;
1401
0
            }
1402
0
        }
1403
1404
0
        if (goOn)
1405
0
        {
1406
0
            do
1407
0
            {
1408
0
                const GDALGroup *groupPtr;
1409
0
                if (!oQueue.empty())
1410
0
                {
1411
0
                    poGroup = oQueue.front();
1412
0
                    oQueue.pop();
1413
0
                    groupPtr = poGroup.get();
1414
0
                }
1415
0
                else
1416
0
                {
1417
0
                    groupPtr = this;
1418
0
                }
1419
1420
0
                auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1421
0
                if (poArray)
1422
0
                    return poArray;
1423
1424
0
                const auto aosGroupNames = groupPtr->GetGroupNames();
1425
0
                for (const auto &osGroupName : aosGroupNames)
1426
0
                {
1427
0
                    auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1428
0
                    if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
1429
0
                                                     poSubGroup->GetFullName()))
1430
0
                    {
1431
0
                        oQueue.push(poSubGroup);
1432
0
                        oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1433
0
                    }
1434
0
                }
1435
0
            } while (!oQueue.empty());
1436
0
        }
1437
1438
0
        if (osPath.empty() || osPath == "/")
1439
0
            break;
1440
1441
0
        const auto nPos = osPath.rfind('/');
1442
0
        if (nPos == 0)
1443
0
            osPath = "/";
1444
0
        else
1445
0
        {
1446
0
            if (nPos == std::string::npos)
1447
0
                break;
1448
0
            osPath.resize(nPos);
1449
0
        }
1450
0
    }
1451
0
    return nullptr;
1452
0
}
1453
1454
/************************************************************************/
1455
/*                       OpenGroupFromFullname()                        */
1456
/************************************************************************/
1457
1458
/** Get a group from its fully qualified name.
1459
 * @since GDAL 3.2
1460
 */
1461
std::shared_ptr<GDALGroup>
1462
GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
1463
                                 CSLConstList papszOptions) const
1464
0
{
1465
0
    std::string osName;
1466
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1467
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1468
0
    if (poGroup == nullptr)
1469
0
        return nullptr;
1470
0
    return poGroup->OpenGroup(osName, papszOptions);
1471
0
}
1472
1473
/************************************************************************/
1474
/*                     OpenDimensionFromFullname()                      */
1475
/************************************************************************/
1476
1477
/** Get a dimension from its fully qualified name */
1478
std::shared_ptr<GDALDimension>
1479
GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
1480
0
{
1481
0
    std::string osName;
1482
0
    std::shared_ptr<GDALGroup> curGroupHolder;
1483
0
    auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1484
0
    if (poGroup == nullptr)
1485
0
        return nullptr;
1486
0
    auto dims(poGroup->GetDimensions());
1487
0
    for (auto &dim : dims)
1488
0
    {
1489
0
        if (dim->GetName() == osName)
1490
0
            return dim;
1491
0
    }
1492
0
    return nullptr;
1493
0
}
1494
1495
/************************************************************************/
1496
/*                          ClearStatistics()                           */
1497
/************************************************************************/
1498
1499
/**
1500
 * \brief Clear statistics.
1501
 *
1502
 * @since GDAL 3.4
1503
 */
1504
void GDALGroup::ClearStatistics()
1505
0
{
1506
0
    auto groupNames = GetGroupNames();
1507
0
    for (const auto &name : groupNames)
1508
0
    {
1509
0
        auto subGroup = OpenGroup(name);
1510
0
        if (subGroup)
1511
0
        {
1512
0
            subGroup->ClearStatistics();
1513
0
        }
1514
0
    }
1515
1516
0
    auto arrayNames = GetMDArrayNames();
1517
0
    for (const auto &name : arrayNames)
1518
0
    {
1519
0
        auto array = OpenMDArray(name);
1520
0
        if (array)
1521
0
        {
1522
0
            array->ClearStatistics();
1523
0
        }
1524
0
    }
1525
0
}
1526
1527
/************************************************************************/
1528
/*                               Rename()                               */
1529
/************************************************************************/
1530
1531
/** Rename the group.
1532
 *
1533
 * This is not implemented by all drivers.
1534
 *
1535
 * Drivers known to implement it: MEM, netCDF, ZARR.
1536
 *
1537
 * This is the same as the C function GDALGroupRename().
1538
 *
1539
 * @param osNewName New name.
1540
 *
1541
 * @return true in case of success
1542
 * @since GDAL 3.8
1543
 */
1544
bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
1545
0
{
1546
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1547
0
    return false;
1548
0
}
1549
1550
/************************************************************************/
1551
/*                             BaseRename()                             */
1552
/************************************************************************/
1553
1554
//! @cond Doxygen_Suppress
1555
void GDALGroup::BaseRename(const std::string &osNewName)
1556
0
{
1557
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
1558
0
    m_osFullName += osNewName;
1559
0
    m_osName = osNewName;
1560
1561
0
    NotifyChildrenOfRenaming();
1562
0
}
1563
1564
//! @endcond
1565
1566
/************************************************************************/
1567
/*                           ParentRenamed()                            */
1568
/************************************************************************/
1569
1570
//! @cond Doxygen_Suppress
1571
void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
1572
0
{
1573
0
    m_osFullName = osNewParentFullName;
1574
0
    m_osFullName += "/";
1575
0
    m_osFullName += m_osName;
1576
1577
0
    NotifyChildrenOfRenaming();
1578
0
}
1579
1580
//! @endcond
1581
1582
/************************************************************************/
1583
/*                              Deleted()                               */
1584
/************************************************************************/
1585
1586
//! @cond Doxygen_Suppress
1587
void GDALGroup::Deleted()
1588
0
{
1589
0
    m_bValid = false;
1590
1591
0
    NotifyChildrenOfDeletion();
1592
0
}
1593
1594
//! @endcond
1595
1596
/************************************************************************/
1597
/*                           ParentDeleted()                            */
1598
/************************************************************************/
1599
1600
//! @cond Doxygen_Suppress
1601
void GDALGroup::ParentDeleted()
1602
0
{
1603
0
    Deleted();
1604
0
}
1605
1606
//! @endcond
1607
1608
/************************************************************************/
1609
/*                     CheckValidAndErrorOutIfNot()                     */
1610
/************************************************************************/
1611
1612
//! @cond Doxygen_Suppress
1613
bool GDALGroup::CheckValidAndErrorOutIfNot() const
1614
0
{
1615
0
    if (!m_bValid)
1616
0
    {
1617
0
        CPLError(CE_Failure, CPLE_AppDefined,
1618
0
                 "This object has been deleted. No action on it is possible");
1619
0
    }
1620
0
    return m_bValid;
1621
0
}
1622
1623
//! @endcond
1624
1625
/************************************************************************/
1626
/*                        ~GDALAbstractMDArray()                        */
1627
/************************************************************************/
1628
1629
0
GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1630
1631
/************************************************************************/
1632
/*                        GDALAbstractMDArray()                         */
1633
/************************************************************************/
1634
1635
//! @cond Doxygen_Suppress
1636
GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
1637
                                         const std::string &osName)
1638
0
    : m_osName(osName),
1639
      m_osFullName(
1640
0
          !osParentName.empty()
1641
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
1642
0
              : osName)
1643
0
{
1644
0
}
1645
1646
//! @endcond
1647
1648
/************************************************************************/
1649
/*                           GetDimensions()                            */
1650
/************************************************************************/
1651
1652
/** \fn GDALAbstractMDArray::GetDimensions() const
1653
 * \brief Return the dimensions of an attribute/array.
1654
 *
1655
 * This is the same as the C functions GDALMDArrayGetDimensions() and
1656
 * similar to GDALAttributeGetDimensionsSize().
1657
 */
1658
1659
/************************************************************************/
1660
/*                            GetDataType()                             */
1661
/************************************************************************/
1662
1663
/** \fn GDALAbstractMDArray::GetDataType() const
1664
 * \brief Return the data type of an attribute/array.
1665
 *
1666
 * This is the same as the C functions GDALMDArrayGetDataType() and
1667
 * GDALAttributeGetDataType()
1668
 */
1669
1670
/************************************************************************/
1671
/*                         GetDimensionCount()                          */
1672
/************************************************************************/
1673
1674
/** Return the number of dimensions.
1675
 *
1676
 * Default implementation is GetDimensions().size(), and may be overridden by
1677
 * drivers if they have a faster / less expensive implementations.
1678
 *
1679
 * This is the same as the C function GDALMDArrayGetDimensionCount() or
1680
 * GDALAttributeGetDimensionCount().
1681
 *
1682
 */
1683
size_t GDALAbstractMDArray::GetDimensionCount() const
1684
0
{
1685
0
    return GetDimensions().size();
1686
0
}
1687
1688
/************************************************************************/
1689
/*                               Rename()                               */
1690
/************************************************************************/
1691
1692
/** Rename the attribute/array.
1693
 *
1694
 * This is not implemented by all drivers.
1695
 *
1696
 * Drivers known to implement it: MEM, netCDF, Zarr.
1697
 *
1698
 * This is the same as the C functions GDALMDArrayRename() or
1699
 * GDALAttributeRename().
1700
 *
1701
 * @param osNewName New name.
1702
 *
1703
 * @return true in case of success
1704
 * @since GDAL 3.8
1705
 */
1706
bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
1707
0
{
1708
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
1709
0
    return false;
1710
0
}
1711
1712
/************************************************************************/
1713
/*                             CopyValue()                              */
1714
/************************************************************************/
1715
1716
/** Convert a value from a source type to a destination type.
1717
 *
1718
 * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1719
 * that must be freed with CPLFree().
1720
 */
1721
bool GDALExtendedDataType::CopyValue(const void *pSrc,
1722
                                     const GDALExtendedDataType &srcType,
1723
                                     void *pDst,
1724
                                     const GDALExtendedDataType &dstType)
1725
0
{
1726
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1727
0
        dstType.GetClass() == GEDTC_NUMERIC)
1728
0
    {
1729
0
        GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
1730
0
                        dstType.GetNumericDataType(), 0, 1);
1731
0
        return true;
1732
0
    }
1733
0
    if (srcType.GetClass() == GEDTC_STRING &&
1734
0
        dstType.GetClass() == GEDTC_STRING)
1735
0
    {
1736
0
        const char *srcStrPtr;
1737
0
        memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1738
0
        char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1739
0
        *reinterpret_cast<void **>(pDst) = pszDup;
1740
0
        return true;
1741
0
    }
1742
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1743
0
        dstType.GetClass() == GEDTC_STRING)
1744
0
    {
1745
0
        const char *str = nullptr;
1746
0
        switch (srcType.GetNumericDataType())
1747
0
        {
1748
0
            case GDT_Unknown:
1749
0
                break;
1750
0
            case GDT_UInt8:
1751
0
                str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
1752
0
                break;
1753
0
            case GDT_Int8:
1754
0
                str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
1755
0
                break;
1756
0
            case GDT_UInt16:
1757
0
                str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
1758
0
                break;
1759
0
            case GDT_Int16:
1760
0
                str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
1761
0
                break;
1762
0
            case GDT_UInt32:
1763
0
                str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
1764
0
                break;
1765
0
            case GDT_Int32:
1766
0
                str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
1767
0
                break;
1768
0
            case GDT_UInt64:
1769
0
                str =
1770
0
                    CPLSPrintf(CPL_FRMT_GUIB,
1771
0
                               static_cast<GUIntBig>(
1772
0
                                   *static_cast<const std::uint64_t *>(pSrc)));
1773
0
                break;
1774
0
            case GDT_Int64:
1775
0
                str = CPLSPrintf(CPL_FRMT_GIB,
1776
0
                                 static_cast<GIntBig>(
1777
0
                                     *static_cast<const std::int64_t *>(pSrc)));
1778
0
                break;
1779
0
            case GDT_Float16:
1780
0
                str = CPLSPrintf("%.5g",
1781
0
                                 double(*static_cast<const GFloat16 *>(pSrc)));
1782
0
                break;
1783
0
            case GDT_Float32:
1784
0
                str = CPLSPrintf(
1785
0
                    "%.9g",
1786
0
                    static_cast<double>(*static_cast<const float *>(pSrc)));
1787
0
                break;
1788
0
            case GDT_Float64:
1789
0
                str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
1790
0
                break;
1791
0
            case GDT_CInt16:
1792
0
            {
1793
0
                const GInt16 *src = static_cast<const GInt16 *>(pSrc);
1794
0
                str = CPLSPrintf("%d+%dj", src[0], src[1]);
1795
0
                break;
1796
0
            }
1797
0
            case GDT_CInt32:
1798
0
            {
1799
0
                const GInt32 *src = static_cast<const GInt32 *>(pSrc);
1800
0
                str = CPLSPrintf("%d+%dj", src[0], src[1]);
1801
0
                break;
1802
0
            }
1803
0
            case GDT_CFloat16:
1804
0
            {
1805
0
                const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
1806
0
                str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
1807
0
                break;
1808
0
            }
1809
0
            case GDT_CFloat32:
1810
0
            {
1811
0
                const float *src = static_cast<const float *>(pSrc);
1812
0
                str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
1813
0
                break;
1814
0
            }
1815
0
            case GDT_CFloat64:
1816
0
            {
1817
0
                const double *src = static_cast<const double *>(pSrc);
1818
0
                str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
1819
0
                break;
1820
0
            }
1821
0
            case GDT_TypeCount:
1822
0
                CPLAssert(false);
1823
0
                break;
1824
0
        }
1825
0
        char *pszDup = str ? CPLStrdup(str) : nullptr;
1826
0
        *reinterpret_cast<void **>(pDst) = pszDup;
1827
0
        return true;
1828
0
    }
1829
0
    if (srcType.GetClass() == GEDTC_STRING &&
1830
0
        dstType.GetClass() == GEDTC_NUMERIC)
1831
0
    {
1832
0
        const char *srcStrPtr;
1833
0
        memcpy(&srcStrPtr, pSrc, sizeof(const char *));
1834
0
        if (dstType.GetNumericDataType() == GDT_Int64)
1835
0
        {
1836
0
            *(static_cast<int64_t *>(pDst)) =
1837
0
                srcStrPtr == nullptr ? 0
1838
0
                                     : static_cast<int64_t>(atoll(srcStrPtr));
1839
0
        }
1840
0
        else if (dstType.GetNumericDataType() == GDT_UInt64)
1841
0
        {
1842
0
            *(static_cast<uint64_t *>(pDst)) =
1843
0
                srcStrPtr == nullptr
1844
0
                    ? 0
1845
0
                    : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
1846
0
        }
1847
0
        else
1848
0
        {
1849
0
            const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1850
0
            GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
1851
0
                            dstType.GetNumericDataType(), 0, 1);
1852
0
        }
1853
0
        return true;
1854
0
    }
1855
0
    if (srcType.GetClass() == GEDTC_COMPOUND &&
1856
0
        dstType.GetClass() == GEDTC_COMPOUND)
1857
0
    {
1858
0
        const auto &srcComponents = srcType.GetComponents();
1859
0
        const auto &dstComponents = dstType.GetComponents();
1860
0
        const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1861
0
        GByte *pabyDst = static_cast<GByte *>(pDst);
1862
1863
0
        std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
1864
0
            srcComponentMap;
1865
0
        for (const auto &srcComp : srcComponents)
1866
0
        {
1867
0
            srcComponentMap[srcComp->GetName()] = &srcComp;
1868
0
        }
1869
0
        for (const auto &dstComp : dstComponents)
1870
0
        {
1871
0
            auto oIter = srcComponentMap.find(dstComp->GetName());
1872
0
            if (oIter == srcComponentMap.end())
1873
0
                return false;
1874
0
            const auto &srcComp = *(oIter->second);
1875
0
            if (!GDALExtendedDataType::CopyValue(
1876
0
                    pabySrc + srcComp->GetOffset(), srcComp->GetType(),
1877
0
                    pabyDst + dstComp->GetOffset(), dstComp->GetType()))
1878
0
            {
1879
0
                return false;
1880
0
            }
1881
0
        }
1882
0
        return true;
1883
0
    }
1884
1885
0
    return false;
1886
0
}
1887
1888
/************************************************************************/
1889
/*                             CopyValues()                             */
1890
/************************************************************************/
1891
1892
/** Convert severals value from a source type to a destination type.
1893
 *
1894
 * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1895
 * that must be freed with CPLFree().
1896
 */
1897
bool GDALExtendedDataType::CopyValues(const void *pSrc,
1898
                                      const GDALExtendedDataType &srcType,
1899
                                      GPtrDiff_t nSrcStrideInElts, void *pDst,
1900
                                      const GDALExtendedDataType &dstType,
1901
                                      GPtrDiff_t nDstStrideInElts,
1902
                                      size_t nValues)
1903
0
{
1904
0
    const auto nSrcStrideInBytes =
1905
0
        nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
1906
0
    const auto nDstStrideInBytes =
1907
0
        nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
1908
0
    if (srcType.GetClass() == GEDTC_NUMERIC &&
1909
0
        dstType.GetClass() == GEDTC_NUMERIC &&
1910
0
        nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
1911
0
        nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
1912
0
        nDstStrideInBytes >= std::numeric_limits<int>::min() &&
1913
0
        nDstStrideInBytes <= std::numeric_limits<int>::max())
1914
0
    {
1915
0
        GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
1916
0
                        static_cast<int>(nSrcStrideInBytes), pDst,
1917
0
                        dstType.GetNumericDataType(),
1918
0
                        static_cast<int>(nDstStrideInBytes), nValues);
1919
0
    }
1920
0
    else
1921
0
    {
1922
0
        const GByte *pabySrc = static_cast<const GByte *>(pSrc);
1923
0
        GByte *pabyDst = static_cast<GByte *>(pDst);
1924
0
        for (size_t i = 0; i < nValues; ++i)
1925
0
        {
1926
0
            if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
1927
0
                return false;
1928
0
            pabySrc += nSrcStrideInBytes;
1929
0
            pabyDst += nDstStrideInBytes;
1930
0
        }
1931
0
    }
1932
0
    return true;
1933
0
}
1934
1935
/************************************************************************/
1936
/*                        CheckReadWriteParams()                        */
1937
/************************************************************************/
1938
//! @cond Doxygen_Suppress
1939
bool GDALAbstractMDArray::CheckReadWriteParams(
1940
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
1941
    const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
1942
    const void *buffer, const void *buffer_alloc_start,
1943
    size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
1944
    std::vector<GPtrDiff_t> &tmp_bufferStride) const
1945
0
{
1946
0
    const auto lamda_error = []()
1947
0
    {
1948
0
        CPLError(CE_Failure, CPLE_AppDefined,
1949
0
                 "Not all elements pointed by buffer will fit in "
1950
0
                 "[buffer_alloc_start, "
1951
0
                 "buffer_alloc_start + buffer_alloc_size]");
1952
0
    };
1953
1954
0
    const auto &dims = GetDimensions();
1955
0
    if (dims.empty())
1956
0
    {
1957
0
        if (buffer_alloc_start)
1958
0
        {
1959
0
            const size_t elementSize = bufferDataType.GetSize();
1960
0
            const GByte *paby_buffer = static_cast<const GByte *>(buffer);
1961
0
            const GByte *paby_buffer_alloc_start =
1962
0
                static_cast<const GByte *>(buffer_alloc_start);
1963
0
            const GByte *paby_buffer_alloc_end =
1964
0
                paby_buffer_alloc_start + buffer_alloc_size;
1965
1966
0
            if (paby_buffer < paby_buffer_alloc_start ||
1967
0
                paby_buffer + elementSize > paby_buffer_alloc_end)
1968
0
            {
1969
0
                lamda_error();
1970
0
                return false;
1971
0
            }
1972
0
        }
1973
0
        return true;
1974
0
    }
1975
1976
0
    if (arrayStep == nullptr)
1977
0
    {
1978
0
        tmp_arrayStep.resize(dims.size(), 1);
1979
0
        arrayStep = tmp_arrayStep.data();
1980
0
    }
1981
0
    for (size_t i = 0; i < dims.size(); i++)
1982
0
    {
1983
0
        assert(count);
1984
0
        if (count[i] == 0)
1985
0
        {
1986
0
            CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
1987
0
                     static_cast<unsigned>(i));
1988
0
            return false;
1989
0
        }
1990
0
    }
1991
0
    bool bufferStride_all_positive = true;
1992
0
    if (bufferStride == nullptr)
1993
0
    {
1994
0
        GPtrDiff_t stride = 1;
1995
0
        assert(dims.empty() || count != nullptr);
1996
        // To compute strides we must proceed from the fastest varying dimension
1997
        // (the last one), and then reverse the result
1998
0
        for (size_t i = dims.size(); i != 0;)
1999
0
        {
2000
0
            --i;
2001
0
            tmp_bufferStride.push_back(stride);
2002
0
            GUInt64 newStride = 0;
2003
0
            bool bOK;
2004
0
            try
2005
0
            {
2006
0
                newStride = (CPLSM(static_cast<uint64_t>(stride)) *
2007
0
                             CPLSM(static_cast<uint64_t>(count[i])))
2008
0
                                .v();
2009
0
                bOK = static_cast<size_t>(newStride) == newStride &&
2010
0
                      newStride < std::numeric_limits<size_t>::max() / 2;
2011
0
            }
2012
0
            catch (...)
2013
0
            {
2014
0
                bOK = false;
2015
0
            }
2016
0
            if (!bOK)
2017
0
            {
2018
0
                CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
2019
0
                return false;
2020
0
            }
2021
0
            stride = static_cast<GPtrDiff_t>(newStride);
2022
0
        }
2023
0
        std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
2024
0
        bufferStride = tmp_bufferStride.data();
2025
0
    }
2026
0
    else
2027
0
    {
2028
0
        for (size_t i = 0; i < dims.size(); i++)
2029
0
        {
2030
0
            if (bufferStride[i] < 0)
2031
0
            {
2032
0
                bufferStride_all_positive = false;
2033
0
                break;
2034
0
            }
2035
0
        }
2036
0
    }
2037
0
    for (size_t i = 0; i < dims.size(); i++)
2038
0
    {
2039
0
        assert(arrayStartIdx);
2040
0
        assert(count);
2041
0
        if (arrayStartIdx[i] >= dims[i]->GetSize())
2042
0
        {
2043
0
            CPLError(CE_Failure, CPLE_AppDefined,
2044
0
                     "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
2045
0
                     static_cast<unsigned>(i),
2046
0
                     static_cast<GUInt64>(arrayStartIdx[i]),
2047
0
                     static_cast<GUInt64>(dims[i]->GetSize()));
2048
0
            return false;
2049
0
        }
2050
0
        bool bOverflow;
2051
0
        if (arrayStep[i] >= 0)
2052
0
        {
2053
0
            try
2054
0
            {
2055
0
                bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
2056
0
                             CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2057
0
                                 CPLSM(static_cast<uint64_t>(arrayStep[i])))
2058
0
                                .v() >= dims[i]->GetSize();
2059
0
            }
2060
0
            catch (...)
2061
0
            {
2062
0
                bOverflow = true;
2063
0
            }
2064
0
            if (bOverflow)
2065
0
            {
2066
0
                CPLError(CE_Failure, CPLE_AppDefined,
2067
0
                         "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
2068
0
                         ">= " CPL_FRMT_GUIB,
2069
0
                         static_cast<unsigned>(i), static_cast<unsigned>(i),
2070
0
                         static_cast<unsigned>(i),
2071
0
                         static_cast<GUInt64>(dims[i]->GetSize()));
2072
0
                return false;
2073
0
            }
2074
0
        }
2075
0
        else
2076
0
        {
2077
0
            try
2078
0
            {
2079
0
                bOverflow =
2080
0
                    arrayStartIdx[i] <
2081
0
                    (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2082
0
                     CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
2083
0
                               ? (static_cast<uint64_t>(1) << 63)
2084
0
                               : static_cast<uint64_t>(-arrayStep[i])))
2085
0
                        .v();
2086
0
            }
2087
0
            catch (...)
2088
0
            {
2089
0
                bOverflow = true;
2090
0
            }
2091
0
            if (bOverflow)
2092
0
            {
2093
0
                CPLError(
2094
0
                    CE_Failure, CPLE_AppDefined,
2095
0
                    "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
2096
0
                    static_cast<unsigned>(i), static_cast<unsigned>(i),
2097
0
                    static_cast<unsigned>(i));
2098
0
                return false;
2099
0
            }
2100
0
        }
2101
0
    }
2102
2103
0
    if (buffer_alloc_start)
2104
0
    {
2105
0
        const size_t elementSize = bufferDataType.GetSize();
2106
0
        const GByte *paby_buffer = static_cast<const GByte *>(buffer);
2107
0
        const GByte *paby_buffer_alloc_start =
2108
0
            static_cast<const GByte *>(buffer_alloc_start);
2109
0
        const GByte *paby_buffer_alloc_end =
2110
0
            paby_buffer_alloc_start + buffer_alloc_size;
2111
0
        if (bufferStride_all_positive)
2112
0
        {
2113
0
            if (paby_buffer < paby_buffer_alloc_start)
2114
0
            {
2115
0
                lamda_error();
2116
0
                return false;
2117
0
            }
2118
0
            GUInt64 nOffset = elementSize;
2119
0
            for (size_t i = 0; i < dims.size(); i++)
2120
0
            {
2121
0
                try
2122
0
                {
2123
0
                    nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
2124
0
                               CPLSM(static_cast<uint64_t>(bufferStride[i])) *
2125
0
                                   CPLSM(static_cast<uint64_t>(count[i] - 1)) *
2126
0
                                   CPLSM(static_cast<uint64_t>(elementSize)))
2127
0
                                  .v();
2128
0
                }
2129
0
                catch (...)
2130
0
                {
2131
0
                    lamda_error();
2132
0
                    return false;
2133
0
                }
2134
0
            }
2135
#if SIZEOF_VOIDP == 4
2136
            if (static_cast<size_t>(nOffset) != nOffset)
2137
            {
2138
                lamda_error();
2139
                return false;
2140
            }
2141
#endif
2142
0
            if (paby_buffer + nOffset > paby_buffer_alloc_end)
2143
0
            {
2144
0
                lamda_error();
2145
0
                return false;
2146
0
            }
2147
0
        }
2148
0
        else if (dims.size() < 31)
2149
0
        {
2150
            // Check all corners of the hypercube
2151
0
            const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
2152
0
            for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
2153
0
            {
2154
0
                const GByte *paby = paby_buffer;
2155
0
                for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
2156
0
                     i++)
2157
0
                {
2158
0
                    if (iCornerCode & (1U << i))
2159
0
                    {
2160
                        // We should check for integer overflows
2161
0
                        paby += bufferStride[i] * (count[i] - 1) * elementSize;
2162
0
                    }
2163
0
                }
2164
0
                if (paby < paby_buffer_alloc_start ||
2165
0
                    paby + elementSize > paby_buffer_alloc_end)
2166
0
                {
2167
0
                    lamda_error();
2168
0
                    return false;
2169
0
                }
2170
0
            }
2171
0
        }
2172
0
    }
2173
2174
0
    return true;
2175
0
}
2176
2177
//! @endcond
2178
2179
/************************************************************************/
2180
/*                                Read()                                */
2181
/************************************************************************/
2182
2183
/** Read part or totality of a multidimensional array or attribute.
2184
 *
2185
 * This will extract the content of a hyper-rectangle from the array into
2186
 * a user supplied buffer.
2187
 *
2188
 * If bufferDataType is of type string, the values written in pDstBuffer
2189
 * will be char* pointers and the strings should be freed with CPLFree().
2190
 *
2191
 * This is the same as the C function GDALMDArrayRead().
2192
 *
2193
 * @param arrayStartIdx Values representing the starting index to read
2194
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
2195
 *                      Array of GetDimensionCount() values. Must not be
2196
 *                      nullptr, unless for a zero-dimensional array.
2197
 *
2198
 * @param count         Values representing the number of values to extract in
2199
 *                      each dimension.
2200
 *                      Array of GetDimensionCount() values. Must not be
2201
 *                      nullptr, unless for a zero-dimensional array.
2202
 *
2203
 * @param arrayStep     Spacing between values to extract in each dimension.
2204
 *                      The spacing is in number of array elements, not bytes.
2205
 *                      If provided, must contain GetDimensionCount() values.
2206
 *                      If set to nullptr, [1, 1, ... 1] will be used as a
2207
 * default to indicate consecutive elements.
2208
 *
2209
 * @param bufferStride  Spacing between values to store in pDstBuffer.
2210
 *                      The spacing is in number of array elements, not bytes.
2211
 *                      If provided, must contain GetDimensionCount() values.
2212
 *                      Negative values are possible (for example to reorder
2213
 *                      from bottom-to-top to top-to-bottom).
2214
 *                      If set to nullptr, will be set so that pDstBuffer is
2215
 *                      written in a compact way, with elements of the last /
2216
 *                      fastest varying dimension being consecutive.
2217
 *
2218
 * @param bufferDataType Data type of values in pDstBuffer.
2219
 *
2220
 * @param pDstBuffer    User buffer to store the values read. Should be big
2221
 *                      enough to store the number of values indicated by
2222
 * count[] and with the spacing of bufferStride[].
2223
 *
2224
 * @param pDstBufferAllocStart Optional pointer that can be used to validate the
2225
 *                             validity of pDstBuffer. pDstBufferAllocStart
2226
 * should be the pointer returned by the malloc() or equivalent call used to
2227
 * allocate the buffer. It will generally be equal to pDstBuffer (when
2228
 * bufferStride[] values are all positive), but not necessarily. If specified,
2229
 * nDstBufferAllocSize should be also set to the appropriate value. If no
2230
 * validation is needed, nullptr can be passed.
2231
 *
2232
 * @param nDstBufferAllocSize  Optional buffer size, that can be used to
2233
 * validate the validity of pDstBuffer. This is the size of the buffer starting
2234
 * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
2235
 *                             set to the appropriate value.
2236
 *                             If no validation is needed, 0 can be passed.
2237
 *
2238
 * @return true in case of success.
2239
 */
2240
bool GDALAbstractMDArray::Read(
2241
    const GUInt64 *arrayStartIdx, const size_t *count,
2242
    const GInt64 *arrayStep,         // step in elements
2243
    const GPtrDiff_t *bufferStride,  // stride in elements
2244
    const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
2245
    const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
2246
0
{
2247
0
    if (!GetDataType().CanConvertTo(bufferDataType))
2248
0
    {
2249
0
        CPLError(CE_Failure, CPLE_AppDefined,
2250
0
                 "Array data type is not convertible to buffer data type");
2251
0
        return false;
2252
0
    }
2253
2254
0
    std::vector<GInt64> tmp_arrayStep;
2255
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
2256
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2257
0
                              bufferDataType, pDstBuffer, pDstBufferAllocStart,
2258
0
                              nDstBufferAllocSize, tmp_arrayStep,
2259
0
                              tmp_bufferStride))
2260
0
    {
2261
0
        return false;
2262
0
    }
2263
2264
0
    return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2265
0
                 pDstBuffer);
2266
0
}
2267
2268
/************************************************************************/
2269
/*                               IWrite()                               */
2270
/************************************************************************/
2271
2272
//! @cond Doxygen_Suppress
2273
bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
2274
                                 const GInt64 *, const GPtrDiff_t *,
2275
                                 const GDALExtendedDataType &, const void *)
2276
0
{
2277
0
    CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
2278
0
    return false;
2279
0
}
2280
2281
//! @endcond
2282
2283
/************************************************************************/
2284
/*                               Write()                                */
2285
/************************************************************************/
2286
2287
/** Write part or totality of a multidimensional array or attribute.
2288
 *
2289
 * This will set the content of a hyper-rectangle into the array from
2290
 * a user supplied buffer.
2291
 *
2292
 * If bufferDataType is of type string, the values read from pSrcBuffer
2293
 * will be char* pointers.
2294
 *
2295
 * This is the same as the C function GDALMDArrayWrite().
2296
 *
2297
 * @param arrayStartIdx Values representing the starting index to write
2298
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
2299
 *                      Array of GetDimensionCount() values. Must not be
2300
 *                      nullptr, unless for a zero-dimensional array.
2301
 *
2302
 * @param count         Values representing the number of values to write in
2303
 *                      each dimension.
2304
 *                      Array of GetDimensionCount() values. Must not be
2305
 *                      nullptr, unless for a zero-dimensional array.
2306
 *
2307
 * @param arrayStep     Spacing between values to write in each dimension.
2308
 *                      The spacing is in number of array elements, not bytes.
2309
 *                      If provided, must contain GetDimensionCount() values.
2310
 *                      If set to nullptr, [1, 1, ... 1] will be used as a
2311
 * default to indicate consecutive elements.
2312
 *
2313
 * @param bufferStride  Spacing between values to read from pSrcBuffer.
2314
 *                      The spacing is in number of array elements, not bytes.
2315
 *                      If provided, must contain GetDimensionCount() values.
2316
 *                      Negative values are possible (for example to reorder
2317
 *                      from bottom-to-top to top-to-bottom).
2318
 *                      If set to nullptr, will be set so that pSrcBuffer is
2319
 *                      written in a compact way, with elements of the last /
2320
 *                      fastest varying dimension being consecutive.
2321
 *
2322
 * @param bufferDataType Data type of values in pSrcBuffer.
2323
 *
2324
 * @param pSrcBuffer    User buffer to read the values from. Should be big
2325
 *                      enough to store the number of values indicated by
2326
 * count[] and with the spacing of bufferStride[].
2327
 *
2328
 * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
2329
 *                             validity of pSrcBuffer. pSrcBufferAllocStart
2330
 * should be the pointer returned by the malloc() or equivalent call used to
2331
 * allocate the buffer. It will generally be equal to pSrcBuffer (when
2332
 * bufferStride[] values are all positive), but not necessarily. If specified,
2333
 * nSrcBufferAllocSize should be also set to the appropriate value. If no
2334
 * validation is needed, nullptr can be passed.
2335
 *
2336
 * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
2337
 * validate the validity of pSrcBuffer. This is the size of the buffer starting
2338
 * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
2339
 *                             set to the appropriate value.
2340
 *                             If no validation is needed, 0 can be passed.
2341
 *
2342
 * @return true in case of success.
2343
 */
2344
bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
2345
                                const size_t *count, const GInt64 *arrayStep,
2346
                                const GPtrDiff_t *bufferStride,
2347
                                const GDALExtendedDataType &bufferDataType,
2348
                                const void *pSrcBuffer,
2349
                                const void *pSrcBufferAllocStart,
2350
                                size_t nSrcBufferAllocSize)
2351
0
{
2352
0
    if (!bufferDataType.CanConvertTo(GetDataType()))
2353
0
    {
2354
0
        CPLError(CE_Failure, CPLE_AppDefined,
2355
0
                 "Buffer data type is not convertible to array data type");
2356
0
        return false;
2357
0
    }
2358
2359
0
    std::vector<GInt64> tmp_arrayStep;
2360
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
2361
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
2362
0
                              bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
2363
0
                              nSrcBufferAllocSize, tmp_arrayStep,
2364
0
                              tmp_bufferStride))
2365
0
    {
2366
0
        return false;
2367
0
    }
2368
2369
0
    return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
2370
0
                  pSrcBuffer);
2371
0
}
2372
2373
/************************************************************************/
2374
/*                       GetTotalElementsCount()                        */
2375
/************************************************************************/
2376
2377
/** Return the total number of values in the array.
2378
 *
2379
 * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
2380
 * and GDALAttributeGetTotalElementsCount().
2381
 *
2382
 */
2383
GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
2384
0
{
2385
0
    const auto &dims = GetDimensions();
2386
0
    if (dims.empty())
2387
0
        return 1;
2388
0
    GUInt64 nElts = 1;
2389
0
    for (const auto &dim : dims)
2390
0
    {
2391
0
        try
2392
0
        {
2393
0
            nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
2394
0
                     CPLSM(static_cast<uint64_t>(dim->GetSize())))
2395
0
                        .v();
2396
0
        }
2397
0
        catch (...)
2398
0
        {
2399
0
            return 0;
2400
0
        }
2401
0
    }
2402
0
    return nElts;
2403
0
}
2404
2405
/************************************************************************/
2406
/*                            GetBlockSize()                            */
2407
/************************************************************************/
2408
2409
/** Return the "natural" block size of the array along all dimensions.
2410
 *
2411
 * Some drivers might organize the array in tiles/blocks and reading/writing
2412
 * aligned on those tile/block boundaries will be more efficient.
2413
 *
2414
 * The returned number of elements in the vector is the same as
2415
 * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
2416
 * the natural block size along the considered dimension.
2417
 * "Flat" arrays will typically return a vector of values set to 0.
2418
 *
2419
 * The default implementation will return a vector of values set to 0.
2420
 *
2421
 * This method is used by GetProcessingChunkSize().
2422
 *
2423
 * Pedantic note: the returned type is GUInt64, so in the highly unlikely
2424
 * theoretical case of a 32-bit platform, this might exceed its size_t
2425
 * allocation capabilities.
2426
 *
2427
 * This is the same as the C function GDALMDArrayGetBlockSize().
2428
 *
2429
 * @return the block size, in number of elements along each dimension.
2430
 */
2431
std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
2432
0
{
2433
0
    return std::vector<GUInt64>(GetDimensionCount());
2434
0
}
2435
2436
/************************************************************************/
2437
/*                       GetProcessingChunkSize()                       */
2438
/************************************************************************/
2439
2440
/** \brief Return an optimal chunk size for read/write operations, given the
2441
 * natural block size and memory constraints specified.
2442
 *
2443
 * This method will use GetBlockSize() to define a chunk whose dimensions are
2444
 * multiple of those returned by GetBlockSize() (unless the block define by
2445
 * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
2446
 * returned by this method).
2447
 *
2448
 * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
2449
 *
2450
 * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
2451
 * chunk.
2452
 *
2453
 * @return the chunk size, in number of elements along each dimension.
2454
 */
2455
std::vector<size_t>
2456
GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
2457
0
{
2458
0
    const auto &dims = GetDimensions();
2459
0
    const auto &nDTSize = GetDataType().GetSize();
2460
0
    std::vector<size_t> anChunkSize;
2461
0
    auto blockSize = GetBlockSize();
2462
0
    CPLAssert(blockSize.size() == dims.size());
2463
0
    size_t nChunkSize = nDTSize;
2464
0
    bool bOverflow = false;
2465
0
    constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
2466
    // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
2467
    // [1, min(sizet_max, dim_size[i])]
2468
    // Also make sure that the product of all anChunkSize[i]) fits on size_t
2469
0
    for (size_t i = 0; i < dims.size(); i++)
2470
0
    {
2471
0
        const auto sizeDimI =
2472
0
            std::max(static_cast<size_t>(1),
2473
0
                     static_cast<size_t>(
2474
0
                         std::min(static_cast<GUInt64>(kSIZE_T_MAX),
2475
0
                                  std::min(blockSize[i], dims[i]->GetSize()))));
2476
0
        anChunkSize.push_back(sizeDimI);
2477
0
        if (nChunkSize > kSIZE_T_MAX / sizeDimI)
2478
0
        {
2479
0
            bOverflow = true;
2480
0
        }
2481
0
        else
2482
0
        {
2483
0
            nChunkSize *= sizeDimI;
2484
0
        }
2485
0
    }
2486
0
    if (nChunkSize == 0)
2487
0
        return anChunkSize;
2488
2489
    // If the product of all anChunkSize[i] does not fit on size_t, then
2490
    // set lowest anChunkSize[i] to 1.
2491
0
    if (bOverflow)
2492
0
    {
2493
0
        nChunkSize = nDTSize;
2494
0
        bOverflow = false;
2495
0
        for (size_t i = dims.size(); i > 0;)
2496
0
        {
2497
0
            --i;
2498
0
            if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
2499
0
            {
2500
0
                bOverflow = true;
2501
0
                anChunkSize[i] = 1;
2502
0
            }
2503
0
            else
2504
0
            {
2505
0
                nChunkSize *= anChunkSize[i];
2506
0
            }
2507
0
        }
2508
0
    }
2509
2510
0
    nChunkSize = nDTSize;
2511
0
    std::vector<size_t> anAccBlockSizeFromStart;
2512
0
    for (size_t i = 0; i < dims.size(); i++)
2513
0
    {
2514
0
        nChunkSize *= anChunkSize[i];
2515
0
        anAccBlockSizeFromStart.push_back(nChunkSize);
2516
0
    }
2517
0
    if (nChunkSize <= nMaxChunkMemory / 2)
2518
0
    {
2519
0
        size_t nVoxelsFromEnd = 1;
2520
0
        for (size_t i = dims.size(); i > 0;)
2521
0
        {
2522
0
            --i;
2523
0
            const auto nCurBlockSize =
2524
0
                anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
2525
0
            const auto nMul = nMaxChunkMemory / nCurBlockSize;
2526
0
            if (nMul >= 2)
2527
0
            {
2528
0
                const auto nSizeThisDim(dims[i]->GetSize());
2529
0
                const auto nBlocksThisDim =
2530
0
                    DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
2531
0
                anChunkSize[i] = static_cast<size_t>(std::min(
2532
0
                    anChunkSize[i] *
2533
0
                        std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
2534
0
                    nSizeThisDim));
2535
0
            }
2536
0
            nVoxelsFromEnd *= anChunkSize[i];
2537
0
        }
2538
0
    }
2539
0
    return anChunkSize;
2540
0
}
2541
2542
/************************************************************************/
2543
/*                             BaseRename()                             */
2544
/************************************************************************/
2545
2546
//! @cond Doxygen_Suppress
2547
void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
2548
0
{
2549
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
2550
0
    m_osFullName += osNewName;
2551
0
    m_osName = osNewName;
2552
2553
0
    NotifyChildrenOfRenaming();
2554
0
}
2555
2556
//! @endcond
2557
2558
//! @cond Doxygen_Suppress
2559
/************************************************************************/
2560
/*                           ParentRenamed()                            */
2561
/************************************************************************/
2562
2563
void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
2564
0
{
2565
0
    m_osFullName = osNewParentFullName;
2566
0
    m_osFullName += "/";
2567
0
    m_osFullName += m_osName;
2568
2569
0
    NotifyChildrenOfRenaming();
2570
0
}
2571
2572
//! @endcond
2573
2574
/************************************************************************/
2575
/*                              Deleted()                               */
2576
/************************************************************************/
2577
2578
//! @cond Doxygen_Suppress
2579
void GDALAbstractMDArray::Deleted()
2580
0
{
2581
0
    m_bValid = false;
2582
2583
0
    NotifyChildrenOfDeletion();
2584
0
}
2585
2586
//! @endcond
2587
2588
/************************************************************************/
2589
/*                           ParentDeleted()                            */
2590
/************************************************************************/
2591
2592
//! @cond Doxygen_Suppress
2593
void GDALAbstractMDArray::ParentDeleted()
2594
0
{
2595
0
    Deleted();
2596
0
}
2597
2598
//! @endcond
2599
2600
/************************************************************************/
2601
/*                     CheckValidAndErrorOutIfNot()                     */
2602
/************************************************************************/
2603
2604
//! @cond Doxygen_Suppress
2605
bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
2606
0
{
2607
0
    if (!m_bValid)
2608
0
    {
2609
0
        CPLError(CE_Failure, CPLE_AppDefined,
2610
0
                 "This object has been deleted. No action on it is possible");
2611
0
    }
2612
0
    return m_bValid;
2613
0
}
2614
2615
//! @endcond
2616
2617
/************************************************************************/
2618
/*                              SetUnit()                               */
2619
/************************************************************************/
2620
2621
/** Set the variable unit.
2622
 *
2623
 * Values should conform as much as possible with those allowed by
2624
 * the NetCDF CF conventions:
2625
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2626
 * but others might be returned.
2627
 *
2628
 * Few examples are "meter", "degrees", "second", ...
2629
 * Empty value means unknown.
2630
 *
2631
 * This is the same as the C function GDALMDArraySetUnit()
2632
 *
2633
 * @note Driver implementation: optionally implemented.
2634
 *
2635
 * @param osUnit unit name.
2636
 * @return true in case of success.
2637
 */
2638
bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
2639
0
{
2640
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
2641
0
    return false;
2642
0
}
2643
2644
/************************************************************************/
2645
/*                              GetUnit()                               */
2646
/************************************************************************/
2647
2648
/** Return the array unit.
2649
 *
2650
 * Values should conform as much as possible with those allowed by
2651
 * the NetCDF CF conventions:
2652
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
2653
 * but others might be returned.
2654
 *
2655
 * Few examples are "meter", "degrees", "second", ...
2656
 * Empty value means unknown.
2657
 *
2658
 * This is the same as the C function GDALMDArrayGetUnit()
2659
 */
2660
const std::string &GDALMDArray::GetUnit() const
2661
0
{
2662
0
    static const std::string emptyString;
2663
0
    return emptyString;
2664
0
}
2665
2666
/************************************************************************/
2667
/*                           SetSpatialRef()                            */
2668
/************************************************************************/
2669
2670
/** Assign a spatial reference system object to the array.
2671
 *
2672
 * This is the same as the C function GDALMDArraySetSpatialRef().
2673
 */
2674
bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
2675
0
{
2676
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
2677
0
    return false;
2678
0
}
2679
2680
/************************************************************************/
2681
/*                           GetSpatialRef()                            */
2682
/************************************************************************/
2683
2684
/** Return the spatial reference system object associated with the array.
2685
 *
2686
 * This is the same as the C function GDALMDArrayGetSpatialRef().
2687
 */
2688
std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2689
0
{
2690
0
    return nullptr;
2691
0
}
2692
2693
/************************************************************************/
2694
/*                         GetRawNoDataValue()                          */
2695
/************************************************************************/
2696
2697
/** Return the nodata value as a "raw" value.
2698
 *
2699
 * The value returned might be nullptr in case of no nodata value. When
2700
 * a nodata value is registered, a non-nullptr will be returned whose size in
2701
 * bytes is GetDataType().GetSize().
2702
 *
2703
 * The returned value should not be modified or freed. It is valid until
2704
 * the array is destroyed, or the next call to GetRawNoDataValue() or
2705
 * SetRawNoDataValue(), or any similar methods.
2706
 *
2707
 * @note Driver implementation: this method shall be implemented if nodata
2708
 * is supported.
2709
 *
2710
 * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2711
 *
2712
 * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2713
 */
2714
const void *GDALMDArray::GetRawNoDataValue() const
2715
0
{
2716
0
    return nullptr;
2717
0
}
2718
2719
/************************************************************************/
2720
/*                       GetNoDataValueAsDouble()                       */
2721
/************************************************************************/
2722
2723
/** Return the nodata value as a double.
2724
 *
2725
 * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2726
 *
2727
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2728
 * a nodata value exists and can be converted to double. Might be nullptr.
2729
 *
2730
 * @return the nodata value as a double. A 0.0 value might also indicate the
2731
 * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2732
 * set to false then).
2733
 */
2734
double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
2735
0
{
2736
0
    const void *pNoData = GetRawNoDataValue();
2737
0
    double dfNoData = 0.0;
2738
0
    const auto &eDT = GetDataType();
2739
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2740
0
    if (ok)
2741
0
    {
2742
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
2743
0
                        GDT_Float64, 0, 1);
2744
0
    }
2745
0
    if (pbHasNoData)
2746
0
        *pbHasNoData = ok;
2747
0
    return dfNoData;
2748
0
}
2749
2750
/************************************************************************/
2751
/*                       GetNoDataValueAsInt64()                        */
2752
/************************************************************************/
2753
2754
/** Return the nodata value as a Int64.
2755
 *
2756
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2757
 * a nodata value exists and can be converted to Int64. Might be nullptr.
2758
 *
2759
 * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
2760
 *
2761
 * @return the nodata value as a Int64
2762
 *
2763
 * @since GDAL 3.5
2764
 */
2765
int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
2766
0
{
2767
0
    const void *pNoData = GetRawNoDataValue();
2768
0
    int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
2769
0
    const auto &eDT = GetDataType();
2770
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2771
0
    if (ok)
2772
0
    {
2773
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2774
0
                        GDT_Int64, 0, 1);
2775
0
    }
2776
0
    if (pbHasNoData)
2777
0
        *pbHasNoData = ok;
2778
0
    return nNoData;
2779
0
}
2780
2781
/************************************************************************/
2782
/*                       GetNoDataValueAsUInt64()                       */
2783
/************************************************************************/
2784
2785
/** Return the nodata value as a UInt64.
2786
 *
2787
 * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
2788
2789
 * @param pbHasNoData Pointer to a output boolean that will be set to true if
2790
 * a nodata value exists and can be converted to UInt64. Might be nullptr.
2791
 *
2792
 * @return the nodata value as a UInt64
2793
 *
2794
 * @since GDAL 3.5
2795
 */
2796
uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
2797
0
{
2798
0
    const void *pNoData = GetRawNoDataValue();
2799
0
    uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
2800
0
    const auto &eDT = GetDataType();
2801
0
    const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
2802
0
    if (ok)
2803
0
    {
2804
0
        GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
2805
0
                        GDT_UInt64, 0, 1);
2806
0
    }
2807
0
    if (pbHasNoData)
2808
0
        *pbHasNoData = ok;
2809
0
    return nNoData;
2810
0
}
2811
2812
/************************************************************************/
2813
/*                         SetRawNoDataValue()                          */
2814
/************************************************************************/
2815
2816
/** Set the nodata value as a "raw" value.
2817
 *
2818
 * The value passed might be nullptr in case of no nodata value. When
2819
 * a nodata value is registered, a non-nullptr whose size in
2820
 * bytes is GetDataType().GetSize() must be passed.
2821
 *
2822
 * This is the same as the C function GDALMDArraySetRawNoDataValue().
2823
 *
2824
 * @note Driver implementation: this method shall be implemented if setting
2825
 nodata
2826
 * is supported.
2827
2828
 * @return true in case of success.
2829
 */
2830
bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
2831
0
{
2832
0
    CPLError(CE_Failure, CPLE_NotSupported,
2833
0
             "SetRawNoDataValue() not implemented");
2834
0
    return false;
2835
0
}
2836
2837
/************************************************************************/
2838
/*                           SetNoDataValue()                           */
2839
/************************************************************************/
2840
2841
/** Set the nodata value as a double.
2842
 *
2843
 * If the natural data type of the attribute/array is not double, type
2844
 * conversion will occur to the type returned by GetDataType().
2845
 *
2846
 * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2847
 *
2848
 * @return true in case of success.
2849
 */
2850
bool GDALMDArray::SetNoDataValue(double dfNoData)
2851
0
{
2852
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2853
0
    bool bRet = false;
2854
0
    if (GDALExtendedDataType::CopyValue(
2855
0
            &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
2856
0
            GetDataType()))
2857
0
    {
2858
0
        bRet = SetRawNoDataValue(pRawNoData);
2859
0
    }
2860
0
    CPLFree(pRawNoData);
2861
0
    return bRet;
2862
0
}
2863
2864
/************************************************************************/
2865
/*                           SetNoDataValue()                           */
2866
/************************************************************************/
2867
2868
/** Set the nodata value as a Int64.
2869
 *
2870
 * If the natural data type of the attribute/array is not Int64, type conversion
2871
 * will occur to the type returned by GetDataType().
2872
 *
2873
 * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
2874
 *
2875
 * @return true in case of success.
2876
 *
2877
 * @since GDAL 3.5
2878
 */
2879
bool GDALMDArray::SetNoDataValue(int64_t nNoData)
2880
0
{
2881
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2882
0
    bool bRet = false;
2883
0
    if (GDALExtendedDataType::CopyValue(&nNoData,
2884
0
                                        GDALExtendedDataType::Create(GDT_Int64),
2885
0
                                        pRawNoData, GetDataType()))
2886
0
    {
2887
0
        bRet = SetRawNoDataValue(pRawNoData);
2888
0
    }
2889
0
    CPLFree(pRawNoData);
2890
0
    return bRet;
2891
0
}
2892
2893
/************************************************************************/
2894
/*                           SetNoDataValue()                           */
2895
/************************************************************************/
2896
2897
/** Set the nodata value as a Int64.
2898
 *
2899
 * If the natural data type of the attribute/array is not Int64, type conversion
2900
 * will occur to the type returned by GetDataType().
2901
 *
2902
 * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
2903
 *
2904
 * @return true in case of success.
2905
 *
2906
 * @since GDAL 3.5
2907
 */
2908
bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
2909
0
{
2910
0
    void *pRawNoData = CPLMalloc(GetDataType().GetSize());
2911
0
    bool bRet = false;
2912
0
    if (GDALExtendedDataType::CopyValue(
2913
0
            &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
2914
0
            GetDataType()))
2915
0
    {
2916
0
        bRet = SetRawNoDataValue(pRawNoData);
2917
0
    }
2918
0
    CPLFree(pRawNoData);
2919
0
    return bRet;
2920
0
}
2921
2922
/************************************************************************/
2923
/*                               Resize()                               */
2924
/************************************************************************/
2925
2926
/** Resize an array to new dimensions.
2927
 *
2928
 * Not all drivers may allow this operation, and with restrictions (e.g.
2929
 * for netCDF, this is limited to growing of "unlimited" dimensions)
2930
 *
2931
 * Resizing a dimension used in other arrays will cause those other arrays
2932
 * to be resized.
2933
 *
2934
 * This is the same as the C function GDALMDArrayResize().
2935
 *
2936
 * @param anNewDimSizes Array of GetDimensionCount() values containing the
2937
 *                      new size of each indexing dimension.
2938
 * @param papszOptions Options. (Driver specific)
2939
 * @return true in case of success.
2940
 * @since GDAL 3.7
2941
 */
2942
bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
2943
                         CPL_UNUSED CSLConstList papszOptions)
2944
0
{
2945
0
    CPLError(CE_Failure, CPLE_NotSupported,
2946
0
             "Resize() is not supported for this array");
2947
0
    return false;
2948
0
}
2949
2950
/************************************************************************/
2951
/*                              SetScale()                              */
2952
/************************************************************************/
2953
2954
/** Set the scale value to apply to raw values.
2955
 *
2956
 * unscaled_value = raw_value * GetScale() + GetOffset()
2957
 *
2958
 * This is the same as the C function GDALMDArraySetScale() /
2959
 * GDALMDArraySetScaleEx().
2960
 *
2961
 * @note Driver implementation: this method shall be implemented if setting
2962
 * scale is supported.
2963
 *
2964
 * @param dfScale scale
2965
 * @param eStorageType Data type to which create the potential attribute that
2966
 * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2967
 * implementation will decide automatically the data type. Note that changing
2968
 * the data type after initial setting might not be supported.
2969
 * @return true in case of success.
2970
 */
2971
bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2972
                           CPL_UNUSED GDALDataType eStorageType)
2973
0
{
2974
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2975
0
    return false;
2976
0
}
2977
2978
/************************************************************************/
2979
/*                              SetOffset)                              */
2980
/************************************************************************/
2981
2982
/** Set the offset value to apply to raw values.
2983
 *
2984
 * unscaled_value = raw_value * GetScale() + GetOffset()
2985
 *
2986
 * This is the same as the C function GDALMDArraySetOffset() /
2987
 * GDALMDArraySetOffsetEx().
2988
 *
2989
 * @note Driver implementation: this method shall be implemented if setting
2990
 * offset is supported.
2991
 *
2992
 * @param dfOffset Offset
2993
 * @param eStorageType Data type to which create the potential attribute that
2994
 * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
2995
 * implementation will decide automatically the data type. Note that changing
2996
 * the data type after initial setting might not be supported.
2997
 * @return true in case of success.
2998
 */
2999
bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
3000
                            CPL_UNUSED GDALDataType eStorageType)
3001
0
{
3002
0
    CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
3003
0
    return false;
3004
0
}
3005
3006
/************************************************************************/
3007
/*                              GetScale()                              */
3008
/************************************************************************/
3009
3010
/** Get the scale value to apply to raw values.
3011
 *
3012
 * unscaled_value = raw_value * GetScale() + GetOffset()
3013
 *
3014
 * This is the same as the C function GDALMDArrayGetScale().
3015
 *
3016
 * @note Driver implementation: this method shall be implemented if getting
3017
 * scale is supported.
3018
 *
3019
 * @param pbHasScale Pointer to a output boolean that will be set to true if
3020
 * a scale value exists. Might be nullptr.
3021
 * @param peStorageType Pointer to a output GDALDataType that will be set to
3022
 * the storage type of the scale value, when known/relevant. Otherwise will be
3023
 * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3024
 *
3025
 * @return the scale value. A 1.0 value might also indicate the
3026
 * absence of a scale value.
3027
 */
3028
double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
3029
                             CPL_UNUSED GDALDataType *peStorageType) const
3030
0
{
3031
0
    if (pbHasScale)
3032
0
        *pbHasScale = false;
3033
0
    return 1.0;
3034
0
}
3035
3036
/************************************************************************/
3037
/*                             GetOffset()                              */
3038
/************************************************************************/
3039
3040
/** Get the offset value to apply to raw values.
3041
 *
3042
 * unscaled_value = raw_value * GetScale() + GetOffset()
3043
 *
3044
 * This is the same as the C function GDALMDArrayGetOffset().
3045
 *
3046
 * @note Driver implementation: this method shall be implemented if getting
3047
 * offset is supported.
3048
 *
3049
 * @param pbHasOffset Pointer to a output boolean that will be set to true if
3050
 * a offset value exists. Might be nullptr.
3051
 * @param peStorageType Pointer to a output GDALDataType that will be set to
3052
 * the storage type of the offset value, when known/relevant. Otherwise will be
3053
 * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
3054
 *
3055
 * @return the offset value. A 0.0 value might also indicate the
3056
 * absence of a offset value.
3057
 */
3058
double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
3059
                              CPL_UNUSED GDALDataType *peStorageType) const
3060
0
{
3061
0
    if (pbHasOffset)
3062
0
        *pbHasOffset = false;
3063
0
    return 0.0;
3064
0
}
3065
3066
/************************************************************************/
3067
/*                          ProcessPerChunk()                           */
3068
/************************************************************************/
3069
3070
namespace
3071
{
3072
enum class Caller
3073
{
3074
    CALLER_END_OF_LOOP,
3075
    CALLER_IN_LOOP,
3076
};
3077
}
3078
3079
/** \brief Call a user-provided function to operate on an array chunk by chunk.
3080
 *
3081
 * This method is to be used when doing operations on an array, or a subset of
3082
 * it, in a chunk by chunk way.
3083
 *
3084
 * @param arrayStartIdx Values representing the starting index to use
3085
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
3086
 *                      Array of GetDimensionCount() values. Must not be
3087
 *                      nullptr, unless for a zero-dimensional array.
3088
 *
3089
 * @param count         Values representing the number of values to use in
3090
 *                      each dimension.
3091
 *                      Array of GetDimensionCount() values. Must not be
3092
 *                      nullptr, unless for a zero-dimensional array.
3093
 *
3094
 * @param chunkSize     Values representing the chunk size in each dimension.
3095
 *                      Might typically the output of GetProcessingChunkSize().
3096
 *                      Array of GetDimensionCount() values. Must not be
3097
 *                      nullptr, unless for a zero-dimensional array.
3098
 *
3099
 * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
3100
 *                      Must NOT be nullptr.
3101
 *
3102
 * @param pUserData     Pointer to pass as the value of the pUserData argument
3103
 * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
3104
 *
3105
 * @return true in case of success.
3106
 */
3107
bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
3108
                                          const GUInt64 *count,
3109
                                          const size_t *chunkSize,
3110
                                          FuncProcessPerChunkType pfnFunc,
3111
                                          void *pUserData)
3112
0
{
3113
0
    const auto &dims = GetDimensions();
3114
0
    if (dims.empty())
3115
0
    {
3116
0
        return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
3117
0
    }
3118
3119
    // Sanity check
3120
0
    size_t nTotalChunkSize = 1;
3121
0
    for (size_t i = 0; i < dims.size(); i++)
3122
0
    {
3123
0
        const auto nSizeThisDim(dims[i]->GetSize());
3124
0
        if (count[i] == 0 || count[i] > nSizeThisDim ||
3125
0
            arrayStartIdx[i] > nSizeThisDim - count[i])
3126
0
        {
3127
0
            CPLError(CE_Failure, CPLE_AppDefined,
3128
0
                     "Inconsistent arrayStartIdx[] / count[] values "
3129
0
                     "regarding array size");
3130
0
            return false;
3131
0
        }
3132
0
        if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
3133
0
            chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
3134
0
        {
3135
0
            CPLError(CE_Failure, CPLE_AppDefined,
3136
0
                     "Inconsistent chunkSize[] values");
3137
0
            return false;
3138
0
        }
3139
0
        nTotalChunkSize *= chunkSize[i];
3140
0
    }
3141
3142
0
    size_t dimIdx = 0;
3143
0
    std::vector<GUInt64> chunkArrayStartIdx(dims.size());
3144
0
    std::vector<size_t> chunkCount(dims.size());
3145
3146
0
    struct Stack
3147
0
    {
3148
0
        GUInt64 nBlockCounter = 0;
3149
0
        GUInt64 nBlocksMinusOne = 0;
3150
0
        size_t first_count = 0;  // only used if nBlocks > 1
3151
0
        Caller return_point = Caller::CALLER_END_OF_LOOP;
3152
0
    };
3153
3154
0
    std::vector<Stack> stack(dims.size());
3155
0
    GUInt64 iCurChunk = 0;
3156
0
    GUInt64 nChunkCount = 1;
3157
0
    for (size_t i = 0; i < dims.size(); i++)
3158
0
    {
3159
0
        const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
3160
0
        const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
3161
0
        stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
3162
0
        nChunkCount *= 1 + stack[i].nBlocksMinusOne;
3163
0
        if (stack[i].nBlocksMinusOne == 0)
3164
0
        {
3165
0
            chunkArrayStartIdx[i] = arrayStartIdx[i];
3166
0
            chunkCount[i] = static_cast<size_t>(count[i]);
3167
0
        }
3168
0
        else
3169
0
        {
3170
0
            stack[i].first_count = static_cast<size_t>(
3171
0
                (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
3172
0
        }
3173
0
    }
3174
3175
0
lbl_next_depth:
3176
0
    if (dimIdx == dims.size())
3177
0
    {
3178
0
        ++iCurChunk;
3179
0
        if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
3180
0
                     iCurChunk, nChunkCount, pUserData))
3181
0
        {
3182
0
            return false;
3183
0
        }
3184
0
    }
3185
0
    else
3186
0
    {
3187
0
        if (stack[dimIdx].nBlocksMinusOne != 0)
3188
0
        {
3189
0
            stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
3190
0
            chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
3191
0
            chunkCount[dimIdx] = stack[dimIdx].first_count;
3192
0
            stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
3193
0
            while (true)
3194
0
            {
3195
0
                dimIdx++;
3196
0
                goto lbl_next_depth;
3197
0
            lbl_return_to_caller_in_loop:
3198
0
                --stack[dimIdx].nBlockCounter;
3199
0
                if (stack[dimIdx].nBlockCounter == 0)
3200
0
                    break;
3201
0
                chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3202
0
                chunkCount[dimIdx] = chunkSize[dimIdx];
3203
0
            }
3204
3205
0
            chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
3206
0
            chunkCount[dimIdx] =
3207
0
                static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
3208
0
                                    chunkArrayStartIdx[dimIdx]);
3209
0
            stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
3210
0
        }
3211
0
        dimIdx++;
3212
0
        goto lbl_next_depth;
3213
0
    lbl_return_to_caller_end_of_loop:
3214
0
        if (dimIdx == 0)
3215
0
            goto end;
3216
0
    }
3217
3218
0
    assert(dimIdx > 0);
3219
0
    dimIdx--;
3220
    // cppcheck-suppress negativeContainerIndex
3221
0
    switch (stack[dimIdx].return_point)
3222
0
    {
3223
0
        case Caller::CALLER_END_OF_LOOP:
3224
0
            goto lbl_return_to_caller_end_of_loop;
3225
0
        case Caller::CALLER_IN_LOOP:
3226
0
            goto lbl_return_to_caller_in_loop;
3227
0
    }
3228
0
end:
3229
0
    return true;
3230
0
}
3231
3232
/************************************************************************/
3233
/*                           GDALAttribute()                            */
3234
/************************************************************************/
3235
3236
//! @cond Doxygen_Suppress
3237
GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
3238
                             CPL_UNUSED const std::string &osName)
3239
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3240
    : GDALAbstractMDArray(osParentName, osName)
3241
#endif
3242
0
{
3243
0
}
3244
3245
0
GDALAttribute::~GDALAttribute() = default;
3246
3247
//! @endcond
3248
3249
/************************************************************************/
3250
/*                          GetDimensionSize()                          */
3251
/************************************************************************/
3252
3253
/** Return the size of the dimensions of the attribute.
3254
 *
3255
 * This will be an empty array for a scalar (single value) attribute.
3256
 *
3257
 * This is the same as the C function GDALAttributeGetDimensionsSize().
3258
 */
3259
std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
3260
0
{
3261
0
    const auto &dims = GetDimensions();
3262
0
    std::vector<GUInt64> ret;
3263
0
    ret.reserve(dims.size());
3264
0
    for (const auto &dim : dims)
3265
0
        ret.push_back(dim->GetSize());
3266
0
    return ret;
3267
0
}
3268
3269
/************************************************************************/
3270
/*                           GDALRawResult()                            */
3271
/************************************************************************/
3272
3273
//! @cond Doxygen_Suppress
3274
GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
3275
                             size_t nEltCount)
3276
0
    : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
3277
0
      m_raw(raw)
3278
0
{
3279
0
}
3280
3281
//! @endcond
3282
3283
/************************************************************************/
3284
/*                           GDALRawResult()                            */
3285
/************************************************************************/
3286
3287
/** Move constructor. */
3288
GDALRawResult::GDALRawResult(GDALRawResult &&other)
3289
0
    : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
3290
0
      m_nSize(other.m_nSize), m_raw(other.m_raw)
3291
0
{
3292
0
    other.m_nEltCount = 0;
3293
0
    other.m_nSize = 0;
3294
0
    other.m_raw = nullptr;
3295
0
}
3296
3297
/************************************************************************/
3298
/*                               FreeMe()                               */
3299
/************************************************************************/
3300
3301
void GDALRawResult::FreeMe()
3302
0
{
3303
0
    if (m_raw && m_dt.NeedsFreeDynamicMemory())
3304
0
    {
3305
0
        GByte *pabyPtr = m_raw;
3306
0
        const auto nDTSize(m_dt.GetSize());
3307
0
        for (size_t i = 0; i < m_nEltCount; ++i)
3308
0
        {
3309
0
            m_dt.FreeDynamicMemory(pabyPtr);
3310
0
            pabyPtr += nDTSize;
3311
0
        }
3312
0
    }
3313
0
    VSIFree(m_raw);
3314
0
}
3315
3316
/************************************************************************/
3317
/*                             operator=()                              */
3318
/************************************************************************/
3319
3320
/** Move assignment. */
3321
GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
3322
0
{
3323
0
    FreeMe();
3324
0
    m_dt = std::move(other.m_dt);
3325
0
    m_nEltCount = other.m_nEltCount;
3326
0
    m_nSize = other.m_nSize;
3327
0
    m_raw = other.m_raw;
3328
0
    other.m_nEltCount = 0;
3329
0
    other.m_nSize = 0;
3330
0
    other.m_raw = nullptr;
3331
0
    return *this;
3332
0
}
3333
3334
/************************************************************************/
3335
/*                           ~GDALRawResult()                           */
3336
/************************************************************************/
3337
3338
/** Destructor. */
3339
GDALRawResult::~GDALRawResult()
3340
0
{
3341
0
    FreeMe();
3342
0
}
3343
3344
/************************************************************************/
3345
/*                             StealData()                              */
3346
/************************************************************************/
3347
3348
//! @cond Doxygen_Suppress
3349
/** Return buffer to caller which becomes owner of it.
3350
 * Only to be used by GDALAttributeReadAsRaw().
3351
 */
3352
GByte *GDALRawResult::StealData()
3353
0
{
3354
0
    GByte *ret = m_raw;
3355
0
    m_raw = nullptr;
3356
0
    m_nEltCount = 0;
3357
0
    m_nSize = 0;
3358
0
    return ret;
3359
0
}
3360
3361
//! @endcond
3362
3363
/************************************************************************/
3364
/*                             ReadAsRaw()                              */
3365
/************************************************************************/
3366
3367
/** Return the raw value of an attribute.
3368
 *
3369
 *
3370
 * This is the same as the C function GDALAttributeReadAsRaw().
3371
 */
3372
GDALRawResult GDALAttribute::ReadAsRaw() const
3373
0
{
3374
0
    const auto nEltCount(GetTotalElementsCount());
3375
0
    const auto &dt(GetDataType());
3376
0
    const auto nDTSize(dt.GetSize());
3377
0
    GByte *res = static_cast<GByte *>(
3378
0
        VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
3379
0
    if (!res)
3380
0
        return GDALRawResult(nullptr, dt, 0);
3381
0
    const auto &dims = GetDimensions();
3382
0
    const auto nDims = GetDimensionCount();
3383
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3384
0
    std::vector<size_t> count(1 + nDims);
3385
0
    for (size_t i = 0; i < nDims; i++)
3386
0
    {
3387
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3388
0
    }
3389
0
    if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
3390
0
              &res[0], static_cast<size_t>(nEltCount * nDTSize)))
3391
0
    {
3392
0
        VSIFree(res);
3393
0
        return GDALRawResult(nullptr, dt, 0);
3394
0
    }
3395
0
    return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
3396
0
}
3397
3398
/************************************************************************/
3399
/*                            ReadAsString()                            */
3400
/************************************************************************/
3401
3402
/** Return the value of an attribute as a string.
3403
 *
3404
 * The returned string should not be freed, and its lifetime does not
3405
 * excess a next call to ReadAsString() on the same object, or the deletion
3406
 * of the object itself.
3407
 *
3408
 * This function will only return the first element if there are several.
3409
 *
3410
 * This is the same as the C function GDALAttributeReadAsString()
3411
 *
3412
 * @return a string, or nullptr.
3413
 */
3414
const char *GDALAttribute::ReadAsString() const
3415
0
{
3416
0
    const auto nDims = GetDimensionCount();
3417
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3418
0
    std::vector<size_t> count(1 + nDims, 1);
3419
0
    char *szRet = nullptr;
3420
0
    if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
3421
0
              GDALExtendedDataType::CreateString(), &szRet, &szRet,
3422
0
              sizeof(szRet)) ||
3423
0
        szRet == nullptr)
3424
0
    {
3425
0
        return nullptr;
3426
0
    }
3427
0
    m_osCachedVal = szRet;
3428
0
    CPLFree(szRet);
3429
0
    return m_osCachedVal.c_str();
3430
0
}
3431
3432
/************************************************************************/
3433
/*                             ReadAsInt()                              */
3434
/************************************************************************/
3435
3436
/** Return the value of an attribute as a integer.
3437
 *
3438
 * This function will only return the first element if there are several.
3439
 *
3440
 * It can fail if its value can not be converted to integer.
3441
 *
3442
 * This is the same as the C function GDALAttributeReadAsInt()
3443
 *
3444
 * @return a integer, or INT_MIN in case of error.
3445
 */
3446
int GDALAttribute::ReadAsInt() const
3447
0
{
3448
0
    const auto nDims = GetDimensionCount();
3449
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3450
0
    std::vector<size_t> count(1 + nDims, 1);
3451
0
    int nRet = INT_MIN;
3452
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3453
0
         GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
3454
0
    return nRet;
3455
0
}
3456
3457
/************************************************************************/
3458
/*                            ReadAsInt64()                             */
3459
/************************************************************************/
3460
3461
/** Return the value of an attribute as an int64_t.
3462
 *
3463
 * This function will only return the first element if there are several.
3464
 *
3465
 * It can fail if its value can not be converted to long.
3466
 *
3467
 * This is the same as the C function GDALAttributeReadAsInt64()
3468
 *
3469
 * @return an int64_t, or INT64_MIN in case of error.
3470
 */
3471
int64_t GDALAttribute::ReadAsInt64() const
3472
0
{
3473
0
    const auto nDims = GetDimensionCount();
3474
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3475
0
    std::vector<size_t> count(1 + nDims, 1);
3476
0
    int64_t nRet = INT64_MIN;
3477
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3478
0
         GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
3479
0
    return nRet;
3480
0
}
3481
3482
/************************************************************************/
3483
/*                            ReadAsDouble()                            */
3484
/************************************************************************/
3485
3486
/** Return the value of an attribute as a double.
3487
 *
3488
 * This function will only return the first element if there are several.
3489
 *
3490
 * It can fail if its value can not be converted to double.
3491
 *
3492
 * This is the same as the C function GDALAttributeReadAsInt()
3493
 *
3494
 * @return a double value.
3495
 */
3496
double GDALAttribute::ReadAsDouble() const
3497
0
{
3498
0
    const auto nDims = GetDimensionCount();
3499
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3500
0
    std::vector<size_t> count(1 + nDims, 1);
3501
0
    double dfRet = 0;
3502
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3503
0
         GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
3504
0
         sizeof(dfRet));
3505
0
    return dfRet;
3506
0
}
3507
3508
/************************************************************************/
3509
/*                         ReadAsStringArray()                          */
3510
/************************************************************************/
3511
3512
/** Return the value of an attribute as an array of strings.
3513
 *
3514
 * This is the same as the C function GDALAttributeReadAsStringArray()
3515
 */
3516
CPLStringList GDALAttribute::ReadAsStringArray() const
3517
0
{
3518
0
    const auto nElts = GetTotalElementsCount();
3519
0
    if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
3520
0
        return CPLStringList();
3521
0
    char **papszList = static_cast<char **>(
3522
0
        VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
3523
0
    const auto &dims = GetDimensions();
3524
0
    const auto nDims = GetDimensionCount();
3525
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3526
0
    std::vector<size_t> count(1 + nDims);
3527
0
    for (size_t i = 0; i < nDims; i++)
3528
0
    {
3529
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3530
0
    }
3531
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3532
0
         GDALExtendedDataType::CreateString(), papszList, papszList,
3533
0
         sizeof(char *) * static_cast<int>(nElts));
3534
0
    for (int i = 0; i < static_cast<int>(nElts); i++)
3535
0
    {
3536
0
        if (papszList[i] == nullptr)
3537
0
            papszList[i] = CPLStrdup("");
3538
0
    }
3539
0
    return CPLStringList(papszList);
3540
0
}
3541
3542
/************************************************************************/
3543
/*                           ReadAsIntArray()                           */
3544
/************************************************************************/
3545
3546
/** Return the value of an attribute as an array of integers.
3547
 *
3548
 * This is the same as the C function GDALAttributeReadAsIntArray().
3549
 */
3550
std::vector<int> GDALAttribute::ReadAsIntArray() const
3551
0
{
3552
0
    const auto nElts = GetTotalElementsCount();
3553
#if SIZEOF_VOIDP == 4
3554
    if (nElts > static_cast<size_t>(nElts))
3555
        return {};
3556
#endif
3557
0
    std::vector<int> res(static_cast<size_t>(nElts));
3558
0
    const auto &dims = GetDimensions();
3559
0
    const auto nDims = GetDimensionCount();
3560
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3561
0
    std::vector<size_t> count(1 + nDims);
3562
0
    for (size_t i = 0; i < nDims; i++)
3563
0
    {
3564
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3565
0
    }
3566
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3567
0
         GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
3568
0
         res.size() * sizeof(res[0]));
3569
0
    return res;
3570
0
}
3571
3572
/************************************************************************/
3573
/*                          ReadAsInt64Array()                          */
3574
/************************************************************************/
3575
3576
/** Return the value of an attribute as an array of int64_t.
3577
 *
3578
 * This is the same as the C function GDALAttributeReadAsInt64Array().
3579
 */
3580
std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
3581
0
{
3582
0
    const auto nElts = GetTotalElementsCount();
3583
#if SIZEOF_VOIDP == 4
3584
    if (nElts > static_cast<size_t>(nElts))
3585
        return {};
3586
#endif
3587
0
    std::vector<int64_t> res(static_cast<size_t>(nElts));
3588
0
    const auto &dims = GetDimensions();
3589
0
    const auto nDims = GetDimensionCount();
3590
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3591
0
    std::vector<size_t> count(1 + nDims);
3592
0
    for (size_t i = 0; i < nDims; i++)
3593
0
    {
3594
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3595
0
    }
3596
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3597
0
         GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
3598
0
         res.size() * sizeof(res[0]));
3599
0
    return res;
3600
0
}
3601
3602
/************************************************************************/
3603
/*                         ReadAsDoubleArray()                          */
3604
/************************************************************************/
3605
3606
/** Return the value of an attribute as an array of double.
3607
 *
3608
 * This is the same as the C function GDALAttributeReadAsDoubleArray().
3609
 */
3610
std::vector<double> GDALAttribute::ReadAsDoubleArray() const
3611
0
{
3612
0
    const auto nElts = GetTotalElementsCount();
3613
#if SIZEOF_VOIDP == 4
3614
    if (nElts > static_cast<size_t>(nElts))
3615
        return {};
3616
#endif
3617
0
    std::vector<double> res(static_cast<size_t>(nElts));
3618
0
    const auto &dims = GetDimensions();
3619
0
    const auto nDims = GetDimensionCount();
3620
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3621
0
    std::vector<size_t> count(1 + nDims);
3622
0
    for (size_t i = 0; i < nDims; i++)
3623
0
    {
3624
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3625
0
    }
3626
0
    Read(startIdx.data(), count.data(), nullptr, nullptr,
3627
0
         GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
3628
0
         res.size() * sizeof(res[0]));
3629
0
    return res;
3630
0
}
3631
3632
/************************************************************************/
3633
/*                               Write()                                */
3634
/************************************************************************/
3635
3636
/** Write an attribute from raw values expressed in GetDataType()
3637
 *
3638
 * The values should be provided in the type of GetDataType() and there should
3639
 * be exactly GetTotalElementsCount() of them.
3640
 * If GetDataType() is a string, each value should be a char* pointer.
3641
 *
3642
 * This is the same as the C function GDALAttributeWriteRaw().
3643
 *
3644
 * @param pabyValue Buffer of nLen bytes.
3645
 * @param nLen Size of pabyValue in bytes. Should be equal to
3646
 *             GetTotalElementsCount() * GetDataType().GetSize()
3647
 * @return true in case of success.
3648
 */
3649
bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
3650
0
{
3651
0
    if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
3652
0
    {
3653
0
        CPLError(CE_Failure, CPLE_AppDefined,
3654
0
                 "Length is not of expected value");
3655
0
        return false;
3656
0
    }
3657
0
    const auto &dims = GetDimensions();
3658
0
    const auto nDims = GetDimensionCount();
3659
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3660
0
    std::vector<size_t> count(1 + nDims);
3661
0
    for (size_t i = 0; i < nDims; i++)
3662
0
    {
3663
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3664
0
    }
3665
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
3666
0
                 pabyValue, pabyValue, nLen);
3667
0
}
3668
3669
/************************************************************************/
3670
/*                               Write()                                */
3671
/************************************************************************/
3672
3673
/** Write an attribute from a string value.
3674
 *
3675
 * Type conversion will be performed if needed. If the attribute contains
3676
 * multiple values, only the first one will be updated.
3677
 *
3678
 * This is the same as the C function GDALAttributeWriteString().
3679
 *
3680
 * @param pszValue Pointer to a string.
3681
 * @return true in case of success.
3682
 */
3683
bool GDALAttribute::Write(const char *pszValue)
3684
0
{
3685
0
    const auto nDims = GetDimensionCount();
3686
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3687
0
    std::vector<size_t> count(1 + nDims, 1);
3688
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3689
0
                 GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
3690
0
                 sizeof(pszValue));
3691
0
}
3692
3693
/************************************************************************/
3694
/*                              WriteInt()                              */
3695
/************************************************************************/
3696
3697
/** Write an attribute from a integer value.
3698
 *
3699
 * Type conversion will be performed if needed. If the attribute contains
3700
 * multiple values, only the first one will be updated.
3701
 *
3702
 * This is the same as the C function GDALAttributeWriteInt().
3703
 *
3704
 * @param nVal Value.
3705
 * @return true in case of success.
3706
 */
3707
bool GDALAttribute::WriteInt(int nVal)
3708
0
{
3709
0
    const auto nDims = GetDimensionCount();
3710
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3711
0
    std::vector<size_t> count(1 + nDims, 1);
3712
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3713
0
                 GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
3714
0
                 sizeof(nVal));
3715
0
}
3716
3717
/************************************************************************/
3718
/*                             WriteInt64()                             */
3719
/************************************************************************/
3720
3721
/** Write an attribute from an int64_t value.
3722
 *
3723
 * Type conversion will be performed if needed. If the attribute contains
3724
 * multiple values, only the first one will be updated.
3725
 *
3726
 * This is the same as the C function GDALAttributeWriteInt().
3727
 *
3728
 * @param nVal Value.
3729
 * @return true in case of success.
3730
 */
3731
bool GDALAttribute::WriteInt64(int64_t nVal)
3732
0
{
3733
0
    const auto nDims = GetDimensionCount();
3734
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3735
0
    std::vector<size_t> count(1 + nDims, 1);
3736
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3737
0
                 GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
3738
0
                 sizeof(nVal));
3739
0
}
3740
3741
/************************************************************************/
3742
/*                               Write()                                */
3743
/************************************************************************/
3744
3745
/** Write an attribute from a double value.
3746
 *
3747
 * Type conversion will be performed if needed. If the attribute contains
3748
 * multiple values, only the first one will be updated.
3749
 *
3750
 * This is the same as the C function GDALAttributeWriteDouble().
3751
 *
3752
 * @param dfVal Value.
3753
 * @return true in case of success.
3754
 */
3755
bool GDALAttribute::Write(double dfVal)
3756
0
{
3757
0
    const auto nDims = GetDimensionCount();
3758
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3759
0
    std::vector<size_t> count(1 + nDims, 1);
3760
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3761
0
                 GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
3762
0
                 sizeof(dfVal));
3763
0
}
3764
3765
/************************************************************************/
3766
/*                               Write()                                */
3767
/************************************************************************/
3768
3769
/** Write an attribute from an array of strings.
3770
 *
3771
 * Type conversion will be performed if needed.
3772
 *
3773
 * Exactly GetTotalElementsCount() strings must be provided
3774
 *
3775
 * This is the same as the C function GDALAttributeWriteStringArray().
3776
 *
3777
 * @param vals Array of strings.
3778
 * @return true in case of success.
3779
 */
3780
bool GDALAttribute::Write(CSLConstList vals)
3781
0
{
3782
0
    if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
3783
0
    {
3784
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3785
0
        return false;
3786
0
    }
3787
0
    const auto nDims = GetDimensionCount();
3788
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3789
0
    std::vector<size_t> count(1 + nDims);
3790
0
    const auto &dims = GetDimensions();
3791
0
    for (size_t i = 0; i < nDims; i++)
3792
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3793
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3794
0
                 GDALExtendedDataType::CreateString(), vals, vals,
3795
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
3796
0
}
3797
3798
/************************************************************************/
3799
/*                               Write()                                */
3800
/************************************************************************/
3801
3802
/** Write an attribute from an array of int.
3803
 *
3804
 * Type conversion will be performed if needed.
3805
 *
3806
 * Exactly GetTotalElementsCount() strings must be provided
3807
 *
3808
 * This is the same as the C function GDALAttributeWriteIntArray()
3809
 *
3810
 * @param vals Array of int.
3811
 * @param nVals Should be equal to GetTotalElementsCount().
3812
 * @return true in case of success.
3813
 */
3814
bool GDALAttribute::Write(const int *vals, size_t nVals)
3815
0
{
3816
0
    if (nVals != GetTotalElementsCount())
3817
0
    {
3818
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3819
0
        return false;
3820
0
    }
3821
0
    const auto nDims = GetDimensionCount();
3822
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3823
0
    std::vector<size_t> count(1 + nDims);
3824
0
    const auto &dims = GetDimensions();
3825
0
    for (size_t i = 0; i < nDims; i++)
3826
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3827
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3828
0
                 GDALExtendedDataType::Create(GDT_Int32), vals, vals,
3829
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
3830
0
}
3831
3832
/************************************************************************/
3833
/*                               Write()                                */
3834
/************************************************************************/
3835
3836
/** Write an attribute from an array of int64_t.
3837
 *
3838
 * Type conversion will be performed if needed.
3839
 *
3840
 * Exactly GetTotalElementsCount() strings must be provided
3841
 *
3842
 * This is the same as the C function GDALAttributeWriteLongArray()
3843
 *
3844
 * @param vals Array of int64_t.
3845
 * @param nVals Should be equal to GetTotalElementsCount().
3846
 * @return true in case of success.
3847
 */
3848
bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
3849
0
{
3850
0
    if (nVals != GetTotalElementsCount())
3851
0
    {
3852
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3853
0
        return false;
3854
0
    }
3855
0
    const auto nDims = GetDimensionCount();
3856
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3857
0
    std::vector<size_t> count(1 + nDims);
3858
0
    const auto &dims = GetDimensions();
3859
0
    for (size_t i = 0; i < nDims; i++)
3860
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3861
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3862
0
                 GDALExtendedDataType::Create(GDT_Int64), vals, vals,
3863
0
                 static_cast<size_t>(GetTotalElementsCount()) *
3864
0
                     sizeof(int64_t));
3865
0
}
3866
3867
/************************************************************************/
3868
/*                               Write()                                */
3869
/************************************************************************/
3870
3871
/** Write an attribute from an array of double.
3872
 *
3873
 * Type conversion will be performed if needed.
3874
 *
3875
 * Exactly GetTotalElementsCount() strings must be provided
3876
 *
3877
 * This is the same as the C function GDALAttributeWriteDoubleArray()
3878
 *
3879
 * @param vals Array of double.
3880
 * @param nVals Should be equal to GetTotalElementsCount().
3881
 * @return true in case of success.
3882
 */
3883
bool GDALAttribute::Write(const double *vals, size_t nVals)
3884
0
{
3885
0
    if (nVals != GetTotalElementsCount())
3886
0
    {
3887
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
3888
0
        return false;
3889
0
    }
3890
0
    const auto nDims = GetDimensionCount();
3891
0
    std::vector<GUInt64> startIdx(1 + nDims, 0);
3892
0
    std::vector<size_t> count(1 + nDims);
3893
0
    const auto &dims = GetDimensions();
3894
0
    for (size_t i = 0; i < nDims; i++)
3895
0
        count[i] = static_cast<size_t>(dims[i]->GetSize());
3896
0
    return Write(startIdx.data(), count.data(), nullptr, nullptr,
3897
0
                 GDALExtendedDataType::Create(GDT_Float64), vals, vals,
3898
0
                 static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
3899
0
}
3900
3901
/************************************************************************/
3902
/*                            GDALMDArray()                             */
3903
/************************************************************************/
3904
3905
//! @cond Doxygen_Suppress
3906
GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
3907
                         CPL_UNUSED const std::string &osName,
3908
                         const std::string &osContext)
3909
    :
3910
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
3911
      GDALAbstractMDArray(osParentName, osName),
3912
#endif
3913
0
      m_osContext(osContext)
3914
0
{
3915
0
}
3916
3917
//! @endcond
3918
3919
/************************************************************************/
3920
/*                          GetTotalCopyCost()                          */
3921
/************************************************************************/
3922
3923
/** Return a total "cost" to copy the array.
3924
 *
3925
 * Used as a parameter for CopyFrom()
3926
 */
3927
GUInt64 GDALMDArray::GetTotalCopyCost() const
3928
0
{
3929
0
    return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
3930
0
           GetTotalElementsCount() * GetDataType().GetSize();
3931
0
}
3932
3933
/************************************************************************/
3934
/*                      CopyFromAllExceptValues()                       */
3935
/************************************************************************/
3936
3937
//! @cond Doxygen_Suppress
3938
3939
bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
3940
                                          bool bStrict, GUInt64 &nCurCost,
3941
                                          const GUInt64 nTotalCost,
3942
                                          GDALProgressFunc pfnProgress,
3943
                                          void *pProgressData)
3944
0
{
3945
    // Nodata setting must be one of the first things done for TileDB
3946
0
    const void *pNoData = poSrcArray->GetRawNoDataValue();
3947
0
    if (pNoData && poSrcArray->GetDataType() == GetDataType())
3948
0
    {
3949
0
        SetRawNoDataValue(pNoData);
3950
0
    }
3951
3952
0
    const bool bThisIsUnscaledArray =
3953
0
        dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
3954
0
    auto attrs = poSrcArray->GetAttributes();
3955
0
    for (const auto &attr : attrs)
3956
0
    {
3957
0
        const auto &osAttrName = attr->GetName();
3958
0
        if (bThisIsUnscaledArray)
3959
0
        {
3960
0
            if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
3961
0
                osAttrName == "valid_min" || osAttrName == "valid_max" ||
3962
0
                osAttrName == "valid_range")
3963
0
            {
3964
0
                continue;
3965
0
            }
3966
0
        }
3967
3968
0
        auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
3969
0
                                       attr->GetDataType());
3970
0
        if (!dstAttr)
3971
0
        {
3972
0
            if (bStrict)
3973
0
                return false;
3974
0
            continue;
3975
0
        }
3976
0
        auto raw = attr->ReadAsRaw();
3977
0
        if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
3978
0
            return false;
3979
0
    }
3980
0
    if (!attrs.empty())
3981
0
    {
3982
0
        nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3983
0
        if (pfnProgress &&
3984
0
            !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
3985
0
            return false;
3986
0
    }
3987
3988
0
    auto srcSRS = poSrcArray->GetSpatialRef();
3989
0
    if (srcSRS)
3990
0
    {
3991
0
        SetSpatialRef(srcSRS.get());
3992
0
    }
3993
3994
0
    const std::string &osUnit(poSrcArray->GetUnit());
3995
0
    if (!osUnit.empty())
3996
0
    {
3997
0
        SetUnit(osUnit);
3998
0
    }
3999
4000
0
    bool bGotValue = false;
4001
0
    GDALDataType eOffsetStorageType = GDT_Unknown;
4002
0
    const double dfOffset =
4003
0
        poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
4004
0
    if (bGotValue)
4005
0
    {
4006
0
        SetOffset(dfOffset, eOffsetStorageType);
4007
0
    }
4008
4009
0
    bGotValue = false;
4010
0
    GDALDataType eScaleStorageType = GDT_Unknown;
4011
0
    const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
4012
0
    if (bGotValue)
4013
0
    {
4014
0
        SetScale(dfScale, eScaleStorageType);
4015
0
    }
4016
4017
0
    return true;
4018
0
}
4019
4020
//! @endcond
4021
4022
/************************************************************************/
4023
/*                              CopyFrom()                              */
4024
/************************************************************************/
4025
4026
/** Copy the content of an array into a new (generally empty) array.
4027
 *
4028
 * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
4029
 *                   of some output drivers this is not recommended)
4030
 * @param poSrcArray Source array. Should NOT be nullptr.
4031
 * @param bStrict Whether to enable strict mode. In strict mode, any error will
4032
 *                stop the copy. In relaxed mode, the copy will be attempted to
4033
 *                be pursued.
4034
 * @param nCurCost  Should be provided as a variable initially set to 0.
4035
 * @param nTotalCost Total cost from GetTotalCopyCost().
4036
 * @param pfnProgress Progress callback, or nullptr.
4037
 * @param pProgressData Progress user data, or nulptr.
4038
 *
4039
 * @return true in case of success (or partial success if bStrict == false).
4040
 */
4041
bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
4042
                           const GDALMDArray *poSrcArray, bool bStrict,
4043
                           GUInt64 &nCurCost, const GUInt64 nTotalCost,
4044
                           GDALProgressFunc pfnProgress, void *pProgressData)
4045
0
{
4046
0
    if (pfnProgress == nullptr)
4047
0
        pfnProgress = GDALDummyProgress;
4048
4049
0
    nCurCost += GDALMDArray::COPY_COST;
4050
4051
0
    if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
4052
0
                                 pfnProgress, pProgressData))
4053
0
    {
4054
0
        return false;
4055
0
    }
4056
4057
0
    const auto &dims = poSrcArray->GetDimensions();
4058
0
    const auto nDTSize = poSrcArray->GetDataType().GetSize();
4059
0
    if (dims.empty())
4060
0
    {
4061
0
        std::vector<GByte> abyTmp(nDTSize);
4062
0
        if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
4063
0
                               GetDataType(), &abyTmp[0]) &&
4064
0
              Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
4065
0
                    &abyTmp[0])) &&
4066
0
            bStrict)
4067
0
        {
4068
0
            return false;
4069
0
        }
4070
0
        nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
4071
0
        if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
4072
0
            return false;
4073
0
    }
4074
0
    else
4075
0
    {
4076
0
        std::vector<GUInt64> arrayStartIdx(dims.size());
4077
0
        std::vector<GUInt64> count(dims.size());
4078
0
        for (size_t i = 0; i < dims.size(); i++)
4079
0
        {
4080
0
            count[i] = static_cast<size_t>(dims[i]->GetSize());
4081
0
        }
4082
4083
0
        struct CopyFunc
4084
0
        {
4085
0
            GDALMDArray *poDstArray = nullptr;
4086
0
            std::vector<GByte> abyTmp{};
4087
0
            GDALProgressFunc pfnProgress = nullptr;
4088
0
            void *pProgressData = nullptr;
4089
0
            GUInt64 nCurCost = 0;
4090
0
            GUInt64 nTotalCost = 0;
4091
0
            GUInt64 nTotalBytesThisArray = 0;
4092
0
            bool bStop = false;
4093
4094
0
            static bool f(GDALAbstractMDArray *l_poSrcArray,
4095
0
                          const GUInt64 *chunkArrayStartIdx,
4096
0
                          const size_t *chunkCount, GUInt64 iCurChunk,
4097
0
                          GUInt64 nChunkCount, void *pUserData)
4098
0
            {
4099
0
                const auto &dt(l_poSrcArray->GetDataType());
4100
0
                auto data = static_cast<CopyFunc *>(pUserData);
4101
0
                auto poDstArray = data->poDstArray;
4102
0
                if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
4103
0
                                        nullptr, dt, &data->abyTmp[0]))
4104
0
                {
4105
0
                    return false;
4106
0
                }
4107
0
                bool bRet =
4108
0
                    poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
4109
0
                                      nullptr, dt, &data->abyTmp[0]);
4110
0
                if (dt.NeedsFreeDynamicMemory())
4111
0
                {
4112
0
                    const auto l_nDTSize = dt.GetSize();
4113
0
                    GByte *ptr = &data->abyTmp[0];
4114
0
                    const size_t l_nDims(l_poSrcArray->GetDimensionCount());
4115
0
                    size_t nEltCount = 1;
4116
0
                    for (size_t i = 0; i < l_nDims; ++i)
4117
0
                    {
4118
0
                        nEltCount *= chunkCount[i];
4119
0
                    }
4120
0
                    for (size_t i = 0; i < nEltCount; i++)
4121
0
                    {
4122
0
                        dt.FreeDynamicMemory(ptr);
4123
0
                        ptr += l_nDTSize;
4124
0
                    }
4125
0
                }
4126
0
                if (!bRet)
4127
0
                {
4128
0
                    return false;
4129
0
                }
4130
4131
0
                double dfCurCost =
4132
0
                    double(data->nCurCost) + double(iCurChunk) / nChunkCount *
4133
0
                                                 data->nTotalBytesThisArray;
4134
0
                if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
4135
0
                                       data->pProgressData))
4136
0
                {
4137
0
                    data->bStop = true;
4138
0
                    return false;
4139
0
                }
4140
4141
0
                return true;
4142
0
            }
4143
0
        };
4144
4145
0
        CopyFunc copyFunc;
4146
0
        copyFunc.poDstArray = this;
4147
0
        copyFunc.nCurCost = nCurCost;
4148
0
        copyFunc.nTotalCost = nTotalCost;
4149
0
        copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
4150
0
        copyFunc.pfnProgress = pfnProgress;
4151
0
        copyFunc.pProgressData = pProgressData;
4152
0
        const char *pszSwathSize =
4153
0
            CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
4154
0
        const size_t nMaxChunkSize =
4155
0
            pszSwathSize
4156
0
                ? static_cast<size_t>(
4157
0
                      std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4158
0
                               CPLAtoGIntBig(pszSwathSize)))
4159
0
                : static_cast<size_t>(
4160
0
                      std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
4161
0
                               GDALGetCacheMax64() / 4));
4162
0
        const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
4163
0
        size_t nRealChunkSize = nDTSize;
4164
0
        for (const auto &nChunkSize : anChunkSizes)
4165
0
        {
4166
0
            nRealChunkSize *= nChunkSize;
4167
0
        }
4168
0
        try
4169
0
        {
4170
0
            copyFunc.abyTmp.resize(nRealChunkSize);
4171
0
        }
4172
0
        catch (const std::exception &)
4173
0
        {
4174
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
4175
0
                     "Cannot allocate temporary buffer");
4176
0
            nCurCost += copyFunc.nTotalBytesThisArray;
4177
0
            return false;
4178
0
        }
4179
0
        if (copyFunc.nTotalBytesThisArray != 0 &&
4180
0
            !const_cast<GDALMDArray *>(poSrcArray)
4181
0
                 ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
4182
0
                                   anChunkSizes.data(), CopyFunc::f,
4183
0
                                   &copyFunc) &&
4184
0
            (bStrict || copyFunc.bStop))
4185
0
        {
4186
0
            nCurCost += copyFunc.nTotalBytesThisArray;
4187
0
            return false;
4188
0
        }
4189
0
        nCurCost += copyFunc.nTotalBytesThisArray;
4190
0
    }
4191
4192
0
    return true;
4193
0
}
4194
4195
/************************************************************************/
4196
/*                         GetStructuralInfo()                          */
4197
/************************************************************************/
4198
4199
/** Return structural information on the array.
4200
 *
4201
 * This may be the compression, etc..
4202
 *
4203
 * The return value should not be freed and is valid until GDALMDArray is
4204
 * released or this function called again.
4205
 *
4206
 * This is the same as the C function GDALMDArrayGetStructuralInfo().
4207
 */
4208
CSLConstList GDALMDArray::GetStructuralInfo() const
4209
0
{
4210
0
    return nullptr;
4211
0
}
4212
4213
/************************************************************************/
4214
/*                             AdviseRead()                             */
4215
/************************************************************************/
4216
4217
/** Advise driver of upcoming read requests.
4218
 *
4219
 * Some GDAL drivers operate more efficiently if they know in advance what
4220
 * set of upcoming read requests will be made.  The AdviseRead() method allows
4221
 * an application to notify the driver of the region of interest.
4222
 *
4223
 * Many drivers just ignore the AdviseRead() call, but it can dramatically
4224
 * accelerate access via some drivers. One such case is when reading through
4225
 * a DAP dataset with the netCDF driver (a in-memory cache array is then created
4226
 * with the region of interest defined by AdviseRead())
4227
 *
4228
 * This is the same as the C function GDALMDArrayAdviseRead().
4229
 *
4230
 * @param arrayStartIdx Values representing the starting index to read
4231
 *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
4232
 *                      Array of GetDimensionCount() values.
4233
 *                      Can be nullptr as a synonymous for [0 for i in
4234
 * range(GetDimensionCount() ]
4235
 *
4236
 * @param count         Values representing the number of values to extract in
4237
 *                      each dimension.
4238
 *                      Array of GetDimensionCount() values.
4239
 *                      Can be nullptr as a synonymous for
4240
 *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
4241
 * range(GetDimensionCount() ]
4242
 *
4243
 * @param papszOptions Driver specific options, or nullptr. Consult driver
4244
 * documentation.
4245
 *
4246
 * @return true in case of success (ignoring the advice is a success)
4247
 *
4248
 * @since GDAL 3.2
4249
 */
4250
bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
4251
                             CSLConstList papszOptions) const
4252
0
{
4253
0
    const auto nDimCount = GetDimensionCount();
4254
0
    if (nDimCount == 0)
4255
0
        return true;
4256
4257
0
    std::vector<GUInt64> tmp_arrayStartIdx;
4258
0
    if (arrayStartIdx == nullptr)
4259
0
    {
4260
0
        tmp_arrayStartIdx.resize(nDimCount);
4261
0
        arrayStartIdx = tmp_arrayStartIdx.data();
4262
0
    }
4263
4264
0
    std::vector<size_t> tmp_count;
4265
0
    if (count == nullptr)
4266
0
    {
4267
0
        tmp_count.resize(nDimCount);
4268
0
        const auto &dims = GetDimensions();
4269
0
        for (size_t i = 0; i < nDimCount; i++)
4270
0
        {
4271
0
            const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
4272
#if SIZEOF_VOIDP < 8
4273
            if (nSize != static_cast<size_t>(nSize))
4274
            {
4275
                CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
4276
                return false;
4277
            }
4278
#endif
4279
0
            tmp_count[i] = static_cast<size_t>(nSize);
4280
0
        }
4281
0
        count = tmp_count.data();
4282
0
    }
4283
4284
0
    std::vector<GInt64> tmp_arrayStep;
4285
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
4286
0
    const GInt64 *arrayStep = nullptr;
4287
0
    const GPtrDiff_t *bufferStride = nullptr;
4288
0
    if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
4289
0
                              GDALExtendedDataType::Create(GDT_Unknown),
4290
0
                              nullptr, nullptr, 0, tmp_arrayStep,
4291
0
                              tmp_bufferStride))
4292
0
    {
4293
0
        return false;
4294
0
    }
4295
4296
0
    return IAdviseRead(arrayStartIdx, count, papszOptions);
4297
0
}
4298
4299
/************************************************************************/
4300
/*                            IAdviseRead()                             */
4301
/************************************************************************/
4302
4303
//! @cond Doxygen_Suppress
4304
bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
4305
                              CSLConstList /* papszOptions*/) const
4306
0
{
4307
0
    return true;
4308
0
}
4309
4310
//! @endcond
4311
4312
/************************************************************************/
4313
/*                            MassageName()                             */
4314
/************************************************************************/
4315
4316
//! @cond Doxygen_Suppress
4317
/*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
4318
0
{
4319
0
    std::string ret;
4320
0
    for (const char ch : inputName)
4321
0
    {
4322
0
        if (!isalnum(static_cast<unsigned char>(ch)))
4323
0
            ret += '_';
4324
0
        else
4325
0
            ret += ch;
4326
0
    }
4327
0
    return ret;
4328
0
}
4329
4330
//! @endcond
4331
4332
/************************************************************************/
4333
/*                         GetCacheRootGroup()                          */
4334
/************************************************************************/
4335
4336
//! @cond Doxygen_Suppress
4337
std::shared_ptr<GDALGroup>
4338
GDALMDArray::GetCacheRootGroup(bool bCanCreate,
4339
                               std::string &osCacheFilenameOut) const
4340
0
{
4341
0
    const auto &osFilename = GetFilename();
4342
0
    if (osFilename.empty())
4343
0
    {
4344
0
        CPLError(CE_Failure, CPLE_AppDefined,
4345
0
                 "Cannot cache an array with an empty filename");
4346
0
        return nullptr;
4347
0
    }
4348
4349
0
    osCacheFilenameOut = osFilename + ".gmac";
4350
0
    if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
4351
0
    {
4352
0
        const auto nPosQuestionMark = osFilename.find('?');
4353
0
        if (nPosQuestionMark != std::string::npos)
4354
0
        {
4355
0
            osCacheFilenameOut =
4356
0
                osFilename.substr(0, nPosQuestionMark)
4357
0
                    .append(".gmac")
4358
0
                    .append(osFilename.substr(nPosQuestionMark));
4359
0
        }
4360
0
    }
4361
0
    const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
4362
0
    if (pszProxy != nullptr)
4363
0
        osCacheFilenameOut = pszProxy;
4364
4365
    // .gmac sidecars are local-only; skip stat for non-local filesystems.
4366
0
    if (!bCanCreate && pszProxy == nullptr &&
4367
0
        !VSIIsLocal(osCacheFilenameOut.c_str()))
4368
0
    {
4369
0
        return nullptr;
4370
0
    }
4371
4372
0
    std::unique_ptr<GDALDataset> poDS;
4373
0
    VSIStatBufL sStat;
4374
0
    if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
4375
0
    {
4376
0
        poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
4377
0
                                     GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
4378
0
                                     nullptr, nullptr, nullptr));
4379
0
    }
4380
0
    if (poDS)
4381
0
    {
4382
0
        CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
4383
0
        return poDS->GetRootGroup();
4384
0
    }
4385
4386
0
    if (bCanCreate)
4387
0
    {
4388
0
        const char *pszDrvName = "netCDF";
4389
0
        GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
4390
0
        if (poDrv == nullptr)
4391
0
        {
4392
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
4393
0
                     pszDrvName);
4394
0
            return nullptr;
4395
0
        }
4396
0
        {
4397
0
            CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
4398
0
            CPLErrorStateBackuper oErrorStateBackuper;
4399
0
            poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
4400
0
                                                     nullptr, nullptr));
4401
0
        }
4402
0
        if (!poDS)
4403
0
        {
4404
0
            pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
4405
0
            if (pszProxy)
4406
0
            {
4407
0
                osCacheFilenameOut = pszProxy;
4408
0
                poDS.reset(poDrv->CreateMultiDimensional(
4409
0
                    osCacheFilenameOut.c_str(), nullptr, nullptr));
4410
0
            }
4411
0
        }
4412
0
        if (poDS)
4413
0
        {
4414
0
            CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
4415
0
            return poDS->GetRootGroup();
4416
0
        }
4417
0
        else
4418
0
        {
4419
0
            CPLError(CE_Failure, CPLE_AppDefined,
4420
0
                     "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
4421
0
                     "configuration option to write the cache in "
4422
0
                     "another directory",
4423
0
                     osCacheFilenameOut.c_str());
4424
0
        }
4425
0
    }
4426
4427
0
    return nullptr;
4428
0
}
4429
4430
//! @endcond
4431
4432
/************************************************************************/
4433
/*                               Cache()                                */
4434
/************************************************************************/
4435
4436
/** Cache the content of the array into an auxiliary filename.
4437
 *
4438
 * The main purpose of this method is to be able to cache views that are
4439
 * expensive to compute, such as transposed arrays.
4440
 *
4441
 * The array will be stored in a file whose name is the one of
4442
 * GetFilename(), with an extra .gmac extension (stands for GDAL
4443
 * Multidimensional Array Cache). The cache is a netCDF dataset.
4444
 *
4445
 * If the .gmac file cannot be written next to the dataset, the
4446
 * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
4447
 * directory.
4448
 *
4449
 * The GDALMDArray::Read() method will automatically use the cache when it
4450
 * exists. There is no timestamp checks between the source array and the cached
4451
 * array. If the source arrays changes, the cache must be manually deleted.
4452
 *
4453
 * This is the same as the C function GDALMDArrayCache()
4454
 *
4455
 * @note Driver implementation: optionally implemented.
4456
 *
4457
 * @param papszOptions List of options, null terminated, or NULL. Currently
4458
 *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
4459
 *                     to specify the block size of the cached array.
4460
 * @return true in case of success.
4461
 */
4462
bool GDALMDArray::Cache(CSLConstList papszOptions) const
4463
0
{
4464
0
    std::string osCacheFilename;
4465
0
    auto poRG = GetCacheRootGroup(true, osCacheFilename);
4466
0
    if (!poRG)
4467
0
        return false;
4468
4469
0
    const std::string osCachedArrayName(MassageName(GetFullName()));
4470
0
    if (poRG->OpenMDArray(osCachedArrayName))
4471
0
    {
4472
0
        CPLError(CE_Failure, CPLE_NotSupported,
4473
0
                 "An array with same name %s already exists in %s",
4474
0
                 osCachedArrayName.c_str(), osCacheFilename.c_str());
4475
0
        return false;
4476
0
    }
4477
4478
0
    CPLStringList aosOptions;
4479
0
    aosOptions.SetNameValue("COMPRESS", "DEFLATE");
4480
0
    const auto &aoDims = GetDimensions();
4481
0
    std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
4482
0
    if (!aoDims.empty())
4483
0
    {
4484
0
        std::string osBlockSize(
4485
0
            CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
4486
0
        if (osBlockSize.empty())
4487
0
        {
4488
0
            const auto anBlockSize = GetBlockSize();
4489
0
            int idxDim = 0;
4490
0
            for (auto nBlockSize : anBlockSize)
4491
0
            {
4492
0
                if (idxDim > 0)
4493
0
                    osBlockSize += ',';
4494
0
                if (nBlockSize == 0)
4495
0
                    nBlockSize = 256;
4496
0
                nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
4497
0
                osBlockSize +=
4498
0
                    std::to_string(static_cast<uint64_t>(nBlockSize));
4499
0
                idxDim++;
4500
0
            }
4501
0
        }
4502
0
        aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
4503
4504
0
        int idxDim = 0;
4505
0
        for (const auto &poDim : aoDims)
4506
0
        {
4507
0
            auto poNewDim = poRG->CreateDimension(
4508
0
                osCachedArrayName + '_' + std::to_string(idxDim),
4509
0
                poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
4510
0
            if (!poNewDim)
4511
0
                return false;
4512
0
            aoNewDims.emplace_back(poNewDim);
4513
0
            idxDim++;
4514
0
        }
4515
0
    }
4516
4517
0
    auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
4518
0
                                             GetDataType(), aosOptions.List());
4519
0
    if (!poCachedArray)
4520
0
    {
4521
0
        CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
4522
0
                 osCachedArrayName.c_str(), osCacheFilename.c_str());
4523
0
        return false;
4524
0
    }
4525
4526
0
    GUInt64 nCost = 0;
4527
0
    return poCachedArray->CopyFrom(nullptr, this,
4528
0
                                   false,  // strict
4529
0
                                   nCost, GetTotalCopyCost(), nullptr, nullptr);
4530
0
}
4531
4532
/************************************************************************/
4533
/*                                Read()                                */
4534
/************************************************************************/
4535
4536
bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
4537
                       const GInt64 *arrayStep,         // step in elements
4538
                       const GPtrDiff_t *bufferStride,  // stride in elements
4539
                       const GDALExtendedDataType &bufferDataType,
4540
                       void *pDstBuffer, const void *pDstBufferAllocStart,
4541
                       size_t nDstBufferAllocSize) const
4542
0
{
4543
0
    if (!m_bHasTriedCachedArray)
4544
0
    {
4545
0
        m_bHasTriedCachedArray = true;
4546
0
        if (IsCacheable())
4547
0
        {
4548
0
            const auto &osFilename = GetFilename();
4549
0
            if (!osFilename.empty() &&
4550
0
                !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
4551
0
            {
4552
0
                std::string osCacheFilename;
4553
0
                auto poRG = GetCacheRootGroup(false, osCacheFilename);
4554
0
                if (poRG)
4555
0
                {
4556
0
                    const std::string osCachedArrayName(
4557
0
                        MassageName(GetFullName()));
4558
0
                    m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
4559
0
                    if (m_poCachedArray)
4560
0
                    {
4561
0
                        const auto &dims = GetDimensions();
4562
0
                        const auto &cachedDims =
4563
0
                            m_poCachedArray->GetDimensions();
4564
0
                        const size_t nDims = dims.size();
4565
0
                        bool ok =
4566
0
                            m_poCachedArray->GetDataType() == GetDataType() &&
4567
0
                            cachedDims.size() == nDims;
4568
0
                        for (size_t i = 0; ok && i < nDims; ++i)
4569
0
                        {
4570
0
                            ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
4571
0
                        }
4572
0
                        if (ok)
4573
0
                        {
4574
0
                            CPLDebug("GDAL", "Cached array for %s found in %s",
4575
0
                                     osCachedArrayName.c_str(),
4576
0
                                     osCacheFilename.c_str());
4577
0
                        }
4578
0
                        else
4579
0
                        {
4580
0
                            CPLError(CE_Warning, CPLE_AppDefined,
4581
0
                                     "Cached array %s in %s has incompatible "
4582
0
                                     "characteristics with current array.",
4583
0
                                     osCachedArrayName.c_str(),
4584
0
                                     osCacheFilename.c_str());
4585
0
                            m_poCachedArray.reset();
4586
0
                        }
4587
0
                    }
4588
0
                }
4589
0
            }
4590
0
        }
4591
0
    }
4592
4593
0
    const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
4594
0
    if (!array->GetDataType().CanConvertTo(bufferDataType))
4595
0
    {
4596
0
        CPLError(CE_Failure, CPLE_AppDefined,
4597
0
                 "Array data type is not convertible to buffer data type");
4598
0
        return false;
4599
0
    }
4600
4601
0
    std::vector<GInt64> tmp_arrayStep;
4602
0
    std::vector<GPtrDiff_t> tmp_bufferStride;
4603
0
    if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
4604
0
                                     bufferStride, bufferDataType, pDstBuffer,
4605
0
                                     pDstBufferAllocStart, nDstBufferAllocSize,
4606
0
                                     tmp_arrayStep, tmp_bufferStride))
4607
0
    {
4608
0
        return false;
4609
0
    }
4610
4611
0
    return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
4612
0
                        bufferDataType, pDstBuffer);
4613
0
}
4614
4615
/************************************************************************/
4616
/*                            GetRootGroup()                            */
4617
/************************************************************************/
4618
4619
/** Return the root group to which this arrays belongs too.
4620
 *
4621
 * Note that arrays may be free standing and some drivers may not implement
4622
 * this method, hence nullptr may be returned.
4623
 *
4624
 * It is used internally by the GetResampled() method to detect if GLT
4625
 * orthorectification is available.
4626
 *
4627
 * @return the root group, or nullptr.
4628
 * @since GDAL 3.8
4629
 */
4630
std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
4631
0
{
4632
0
    return nullptr;
4633
0
}
4634
4635
//! @cond Doxygen_Suppress
4636
4637
/************************************************************************/
4638
/*                        IsTransposedRequest()                         */
4639
/************************************************************************/
4640
4641
bool GDALMDArray::IsTransposedRequest(
4642
    const size_t *count,
4643
    const GPtrDiff_t *bufferStride) const  // stride in elements
4644
0
{
4645
    /*
4646
    For example:
4647
    count = [2,3,4]
4648
    strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
4649
    stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
4650
    (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
4651
    (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
4652
    */
4653
0
    const size_t nDims(GetDimensionCount());
4654
0
    size_t nCurStrideForRowMajorStrides = 1;
4655
0
    bool bRowMajorStrides = true;
4656
0
    size_t nElts = 1;
4657
0
    size_t nLastIdx = 0;
4658
0
    for (size_t i = nDims; i > 0;)
4659
0
    {
4660
0
        --i;
4661
0
        if (bufferStride[i] < 0)
4662
0
            return false;
4663
0
        if (static_cast<size_t>(bufferStride[i]) !=
4664
0
            nCurStrideForRowMajorStrides)
4665
0
        {
4666
0
            bRowMajorStrides = false;
4667
0
        }
4668
        // Integer overflows have already been checked in CheckReadWriteParams()
4669
0
        nCurStrideForRowMajorStrides *= count[i];
4670
0
        nElts *= count[i];
4671
0
        nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
4672
0
    }
4673
0
    if (bRowMajorStrides)
4674
0
        return false;
4675
0
    return nLastIdx == nElts - 1;
4676
0
}
4677
4678
/************************************************************************/
4679
/*                   CopyToFinalBufferSameDataType()                    */
4680
/************************************************************************/
4681
4682
template <size_t N>
4683
void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
4684
                                   size_t nDims, const size_t *count,
4685
                                   const GPtrDiff_t *bufferStride)
4686
0
{
4687
0
    std::vector<size_t> anStackCount(nDims);
4688
0
    std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4689
0
    const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4690
0
#if defined(__GNUC__)
4691
0
#pragma GCC diagnostic push
4692
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
4693
0
#endif
4694
0
    pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4695
0
#if defined(__GNUC__)
4696
0
#pragma GCC diagnostic pop
4697
0
#endif
4698
0
    size_t iDim = 0;
4699
4700
0
lbl_next_depth:
4701
0
    if (iDim == nDims - 1)
4702
0
    {
4703
0
        size_t n = count[iDim];
4704
0
        GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
4705
0
        const auto bufferStrideLastDim = bufferStride[iDim] * N;
4706
0
        while (n > 0)
4707
0
        {
4708
0
            --n;
4709
0
            memcpy(pabyDstBuffer, pabySrcBuffer, N);
4710
0
            pabyDstBuffer += bufferStrideLastDim;
4711
0
            pabySrcBuffer += N;
4712
0
        }
4713
0
    }
4714
0
    else
4715
0
    {
4716
0
        anStackCount[iDim] = count[iDim];
4717
0
        while (true)
4718
0
        {
4719
0
            ++iDim;
4720
0
            pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4721
0
            goto lbl_next_depth;
4722
0
        lbl_return_to_caller_in_loop:
4723
0
            --iDim;
4724
0
            --anStackCount[iDim];
4725
0
            if (anStackCount[iDim] == 0)
4726
0
                break;
4727
0
            pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
4728
0
        }
4729
0
    }
4730
0
    if (iDim > 0)
4731
0
        goto lbl_return_to_caller_in_loop;
4732
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*)
4733
4734
/************************************************************************/
4735
/*                         CopyToFinalBuffer()                          */
4736
/************************************************************************/
4737
4738
static void CopyToFinalBuffer(const void *pSrcBuffer,
4739
                              const GDALExtendedDataType &eSrcDataType,
4740
                              void *pDstBuffer,
4741
                              const GDALExtendedDataType &eDstDataType,
4742
                              size_t nDims, const size_t *count,
4743
                              const GPtrDiff_t *bufferStride)
4744
0
{
4745
0
    const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
4746
    // Use specialized implementation for well-known data types when no
4747
    // type conversion is needed
4748
0
    if (eSrcDataType == eDstDataType)
4749
0
    {
4750
0
        if (nSrcDataTypeSize == 1)
4751
0
        {
4752
0
            CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
4753
0
                                             count, bufferStride);
4754
0
            return;
4755
0
        }
4756
0
        else if (nSrcDataTypeSize == 2)
4757
0
        {
4758
0
            CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
4759
0
                                             count, bufferStride);
4760
0
            return;
4761
0
        }
4762
0
        else if (nSrcDataTypeSize == 4)
4763
0
        {
4764
0
            CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
4765
0
                                             count, bufferStride);
4766
0
            return;
4767
0
        }
4768
0
        else if (nSrcDataTypeSize == 8)
4769
0
        {
4770
0
            CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
4771
0
                                             count, bufferStride);
4772
0
            return;
4773
0
        }
4774
0
    }
4775
4776
0
    const size_t nDstDataTypeSize(eDstDataType.GetSize());
4777
0
    std::vector<size_t> anStackCount(nDims);
4778
0
    std::vector<GByte *> pabyDstBufferStack(nDims + 1);
4779
0
    const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4780
0
    pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
4781
0
    size_t iDim = 0;
4782
4783
0
lbl_next_depth:
4784
0
    if (iDim == nDims - 1)
4785
0
    {
4786
0
        GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
4787
0
                                         pabyDstBufferStack[iDim], eDstDataType,
4788
0
                                         bufferStride[iDim], count[iDim]);
4789
0
        pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
4790
0
    }
4791
0
    else
4792
0
    {
4793
0
        anStackCount[iDim] = count[iDim];
4794
0
        while (true)
4795
0
        {
4796
0
            ++iDim;
4797
0
            pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
4798
0
            goto lbl_next_depth;
4799
0
        lbl_return_to_caller_in_loop:
4800
0
            --iDim;
4801
0
            --anStackCount[iDim];
4802
0
            if (anStackCount[iDim] == 0)
4803
0
                break;
4804
0
            pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
4805
0
        }
4806
0
    }
4807
0
    if (iDim > 0)
4808
0
        goto lbl_return_to_caller_in_loop;
4809
0
}
4810
4811
/************************************************************************/
4812
/*                         TransposeLast2Dims()                         */
4813
/************************************************************************/
4814
4815
static bool TransposeLast2Dims(void *pDstBuffer,
4816
                               const GDALExtendedDataType &eDT,
4817
                               const size_t nDims, const size_t *count,
4818
                               const size_t nEltsNonLast2Dims)
4819
0
{
4820
0
    const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4821
0
    const auto nDTSize = eDT.GetSize();
4822
0
    void *pTempBufferForLast2DimsTranspose =
4823
0
        VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
4824
0
    if (pTempBufferForLast2DimsTranspose == nullptr)
4825
0
        return false;
4826
4827
0
    GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4828
0
    for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
4829
0
    {
4830
0
        GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
4831
0
                        pTempBufferForLast2DimsTranspose,
4832
0
                        eDT.GetNumericDataType(), count[nDims - 1],
4833
0
                        count[nDims - 2]);
4834
0
        memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
4835
0
               nDTSize * nEltsLast2Dims);
4836
0
        pabyDstBuffer += nDTSize * nEltsLast2Dims;
4837
0
    }
4838
4839
0
    VSIFree(pTempBufferForLast2DimsTranspose);
4840
4841
0
    return true;
4842
0
}
4843
4844
/************************************************************************/
4845
/*                      ReadForTransposedRequest()                      */
4846
/************************************************************************/
4847
4848
// Using the netCDF/HDF5 APIs to read a slice with strides that express a
4849
// transposed view yield to extremely poor/unusable performance. This fixes
4850
// this by using temporary memory to read in a contiguous buffer in a
4851
// row-major order, and then do the transposition to the final buffer.
4852
4853
bool GDALMDArray::ReadForTransposedRequest(
4854
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4855
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4856
    void *pDstBuffer) const
4857
0
{
4858
0
    const size_t nDims(GetDimensionCount());
4859
0
    if (nDims == 0)
4860
0
    {
4861
0
        CPLAssert(false);
4862
0
        return false;  // shouldn't happen
4863
0
    }
4864
0
    size_t nElts = 1;
4865
0
    for (size_t i = 0; i < nDims; ++i)
4866
0
        nElts *= count[i];
4867
4868
0
    std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
4869
0
    tmpBufferStrides.back() = 1;
4870
0
    for (size_t i = nDims - 1; i > 0;)
4871
0
    {
4872
0
        --i;
4873
0
        tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
4874
0
    }
4875
4876
0
    const auto &eDT = GetDataType();
4877
0
    const auto nDTSize = eDT.GetSize();
4878
0
    if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
4879
0
        static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
4880
0
        (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
4881
0
    {
4882
        // Optimization of the optimization if only the last 2 dims are
4883
        // transposed that saves on temporary buffer allocation
4884
0
        const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
4885
0
        size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
4886
0
        bool bRowMajorStridesForNonLast2Dims = true;
4887
0
        size_t nEltsNonLast2Dims = 1;
4888
0
        for (size_t i = nDims - 2; i > 0;)
4889
0
        {
4890
0
            --i;
4891
0
            if (static_cast<size_t>(bufferStride[i]) !=
4892
0
                nCurStrideForRowMajorStrides)
4893
0
            {
4894
0
                bRowMajorStridesForNonLast2Dims = false;
4895
0
            }
4896
            // Integer overflows have already been checked in
4897
            // CheckReadWriteParams()
4898
0
            nCurStrideForRowMajorStrides *= count[i];
4899
0
            nEltsNonLast2Dims *= count[i];
4900
0
        }
4901
0
        if (bRowMajorStridesForNonLast2Dims)
4902
0
        {
4903
            // We read in the final buffer!
4904
0
            if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
4905
0
                       eDT, pDstBuffer))
4906
0
            {
4907
0
                return false;
4908
0
            }
4909
4910
0
            return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
4911
0
                                      nEltsNonLast2Dims);
4912
0
        }
4913
0
    }
4914
4915
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
4916
0
    if (pTempBuffer == nullptr)
4917
0
        return false;
4918
4919
0
    if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
4920
0
               pTempBuffer))
4921
0
    {
4922
0
        VSIFree(pTempBuffer);
4923
0
        return false;
4924
0
    }
4925
0
    CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
4926
0
                      count, bufferStride);
4927
4928
0
    if (eDT.NeedsFreeDynamicMemory())
4929
0
    {
4930
0
        GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
4931
0
        for (size_t i = 0; i < nElts; ++i)
4932
0
        {
4933
0
            eDT.FreeDynamicMemory(pabyPtr);
4934
0
            pabyPtr += nDTSize;
4935
0
        }
4936
0
    }
4937
4938
0
    VSIFree(pTempBuffer);
4939
0
    return true;
4940
0
}
4941
4942
/************************************************************************/
4943
/*           IsStepOneContiguousRowMajorOrderedSameDataType()           */
4944
/************************************************************************/
4945
4946
// Returns true if at all following conditions are met:
4947
// arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
4948
// defines a row-major ordered contiguous buffer.
4949
bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
4950
    const size_t *count, const GInt64 *arrayStep,
4951
    const GPtrDiff_t *bufferStride,
4952
    const GDALExtendedDataType &bufferDataType) const
4953
0
{
4954
0
    if (bufferDataType != GetDataType())
4955
0
        return false;
4956
0
    size_t nExpectedStride = 1;
4957
0
    for (size_t i = GetDimensionCount(); i > 0;)
4958
0
    {
4959
0
        --i;
4960
0
        if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
4961
0
            static_cast<size_t>(bufferStride[i]) != nExpectedStride)
4962
0
        {
4963
0
            return false;
4964
0
        }
4965
0
        nExpectedStride *= count[i];
4966
0
    }
4967
0
    return true;
4968
0
}
4969
4970
/************************************************************************/
4971
/*                      ReadUsingContiguousIRead()                      */
4972
/************************************************************************/
4973
4974
// Used for example by the TileDB driver when requesting it with
4975
// arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
4976
// not defining a row-major ordered contiguous buffer.
4977
// Should only be called when at least one of the above conditions are true,
4978
// which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
4979
// returning none.
4980
// This method will call IRead() again with arrayStep[] == 1,
4981
// bufferDataType == GetDataType() and bufferStride[] defining a row-major
4982
// ordered contiguous buffer, on a temporary buffer. And it will rearrange the
4983
// content of that temporary buffer onto pDstBuffer.
4984
bool GDALMDArray::ReadUsingContiguousIRead(
4985
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
4986
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
4987
    void *pDstBuffer) const
4988
0
{
4989
0
    const size_t nDims(GetDimensionCount());
4990
0
    std::vector<GUInt64> anTmpStartIdx(nDims);
4991
0
    std::vector<size_t> anTmpCount(nDims);
4992
0
    const auto &oType = GetDataType();
4993
0
    size_t nMemArraySize = oType.GetSize();
4994
0
    std::vector<GPtrDiff_t> anTmpStride(nDims);
4995
0
    GPtrDiff_t nStride = 1;
4996
0
    for (size_t i = nDims; i > 0;)
4997
0
    {
4998
0
        --i;
4999
0
        if (arrayStep[i] > 0)
5000
0
            anTmpStartIdx[i] = arrayStartIdx[i];
5001
0
        else
5002
0
            anTmpStartIdx[i] =
5003
0
                arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
5004
0
        const uint64_t nCount =
5005
0
            (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
5006
0
        if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
5007
0
        {
5008
0
            CPLError(CE_Failure, CPLE_AppDefined,
5009
0
                     "Read() failed due to too large memory requirement");
5010
0
            return false;
5011
0
        }
5012
0
        anTmpCount[i] = static_cast<size_t>(nCount);
5013
0
        nMemArraySize *= anTmpCount[i];
5014
0
        anTmpStride[i] = nStride;
5015
0
        nStride *= anTmpCount[i];
5016
0
    }
5017
0
    std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
5018
0
        VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
5019
0
    if (!pTmpBuffer)
5020
0
        return false;
5021
0
    if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
5022
0
               std::vector<GInt64>(nDims, 1).data(),  // steps
5023
0
               anTmpStride.data(), oType, pTmpBuffer.get()))
5024
0
    {
5025
0
        return false;
5026
0
    }
5027
0
    std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
5028
0
    for (size_t i = 0; i < nDims; ++i)
5029
0
    {
5030
0
        if (arrayStep[i] > 0)
5031
0
            anTmpStartIdx[i] = 0;
5032
0
        else
5033
0
            anTmpStartIdx[i] = anTmpCount[i] - 1;
5034
0
        apoTmpDims[i] = std::make_shared<GDALDimension>(
5035
0
            std::string(), std::string(), std::string(), std::string(),
5036
0
            anTmpCount[i]);
5037
0
    }
5038
0
    auto poMEMArray =
5039
0
        MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
5040
0
    return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
5041
0
           poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
5042
0
                            bufferStride, bufferDataType, pDstBuffer);
5043
0
}
5044
5045
//! @endcond
5046
5047
/************************************************************************/
5048
/*                          GDALSlicedMDArray                           */
5049
/************************************************************************/
5050
5051
class GDALSlicedMDArray final : public GDALPamMDArray
5052
{
5053
  private:
5054
    std::shared_ptr<GDALMDArray> m_poParent{};
5055
    std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5056
    std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
5057
    std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
5058
    std::vector<Range>
5059
        m_parentRanges{};  // of size m_poParent->GetDimensionCount()
5060
5061
    mutable std::vector<GUInt64> m_parentStart;
5062
    mutable std::vector<size_t> m_parentCount;
5063
    mutable std::vector<GInt64> m_parentStep;
5064
    mutable std::vector<GPtrDiff_t> m_parentStride;
5065
5066
    void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5067
                             const GInt64 *arrayStep,
5068
                             const GPtrDiff_t *bufferStride) const;
5069
5070
  protected:
5071
    explicit GDALSlicedMDArray(
5072
        const std::shared_ptr<GDALMDArray> &poParent,
5073
        const std::string &viewExpr,
5074
        std::vector<std::shared_ptr<GDALDimension>> &&dims,
5075
        std::vector<size_t> &&mapDimIdxToParentDimIdx,
5076
        std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5077
        std::vector<Range> &&parentRanges)
5078
0
        : GDALAbstractMDArray(std::string(), "Sliced view of " +
5079
0
                                                 poParent->GetFullName() +
5080
0
                                                 " (" + viewExpr + ")"),
5081
0
          GDALPamMDArray(std::string(),
5082
0
                         "Sliced view of " + poParent->GetFullName() + " (" +
5083
0
                             viewExpr + ")",
5084
0
                         GDALPamMultiDim::GetPAM(poParent),
5085
0
                         poParent->GetContext()),
5086
0
          m_poParent(std::move(poParent)), m_dims(std::move(dims)),
5087
0
          m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
5088
0
          m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
5089
0
          m_parentRanges(std::move(parentRanges)),
5090
0
          m_parentStart(m_poParent->GetDimensionCount()),
5091
0
          m_parentCount(m_poParent->GetDimensionCount(), 1),
5092
0
          m_parentStep(m_poParent->GetDimensionCount()),
5093
0
          m_parentStride(m_poParent->GetDimensionCount())
5094
0
    {
5095
0
    }
5096
5097
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5098
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5099
               const GDALExtendedDataType &bufferDataType,
5100
               void *pDstBuffer) const override;
5101
5102
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
5103
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5104
                const GDALExtendedDataType &bufferDataType,
5105
                const void *pSrcBuffer) override;
5106
5107
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5108
                     CSLConstList papszOptions) const override;
5109
5110
  public:
5111
    static std::shared_ptr<GDALSlicedMDArray>
5112
    Create(const std::shared_ptr<GDALMDArray> &poParent,
5113
           const std::string &viewExpr,
5114
           std::vector<std::shared_ptr<GDALDimension>> &&dims,
5115
           std::vector<size_t> &&mapDimIdxToParentDimIdx,
5116
           std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
5117
           std::vector<Range> &&parentRanges)
5118
0
    {
5119
0
        CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
5120
0
        CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
5121
5122
0
        auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
5123
0
            poParent, viewExpr, std::move(dims),
5124
0
            std::move(mapDimIdxToParentDimIdx),
5125
0
            std::move(apoNewIndexingVariables), std::move(parentRanges))));
5126
0
        newAr->SetSelf(newAr);
5127
0
        return newAr;
5128
0
    }
5129
5130
    bool IsWritable() const override
5131
0
    {
5132
0
        return m_poParent->IsWritable();
5133
0
    }
5134
5135
    const std::string &GetFilename() const override
5136
0
    {
5137
0
        return m_poParent->GetFilename();
5138
0
    }
5139
5140
    const std::vector<std::shared_ptr<GDALDimension>> &
5141
    GetDimensions() const override
5142
0
    {
5143
0
        return m_dims;
5144
0
    }
5145
5146
    const GDALExtendedDataType &GetDataType() const override
5147
0
    {
5148
0
        return m_poParent->GetDataType();
5149
0
    }
5150
5151
    const std::string &GetUnit() const override
5152
0
    {
5153
0
        return m_poParent->GetUnit();
5154
0
    }
5155
5156
    // bool SetUnit(const std::string& osUnit) override  { return
5157
    // m_poParent->SetUnit(osUnit); }
5158
5159
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5160
0
    {
5161
0
        auto poSrcSRS = m_poParent->GetSpatialRef();
5162
0
        if (!poSrcSRS)
5163
0
            return nullptr;
5164
0
        auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
5165
0
        std::vector<int> dstMapping;
5166
0
        for (int srcAxis : srcMapping)
5167
0
        {
5168
0
            bool bFound = false;
5169
0
            for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
5170
0
            {
5171
0
                if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
5172
0
                    srcAxis - 1)
5173
0
                {
5174
0
                    dstMapping.push_back(static_cast<int>(i) + 1);
5175
0
                    bFound = true;
5176
0
                    break;
5177
0
                }
5178
0
            }
5179
0
            if (!bFound)
5180
0
            {
5181
0
                dstMapping.push_back(0);
5182
0
            }
5183
0
        }
5184
0
        auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
5185
0
        poClone->SetDataAxisToSRSAxisMapping(dstMapping);
5186
0
        return poClone;
5187
0
    }
5188
5189
    const void *GetRawNoDataValue() const override
5190
0
    {
5191
0
        return m_poParent->GetRawNoDataValue();
5192
0
    }
5193
5194
    // bool SetRawNoDataValue(const void* pRawNoData) override { return
5195
    // m_poParent->SetRawNoDataValue(pRawNoData); }
5196
5197
    double GetOffset(bool *pbHasOffset,
5198
                     GDALDataType *peStorageType) const override
5199
0
    {
5200
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
5201
0
    }
5202
5203
    double GetScale(bool *pbHasScale,
5204
                    GDALDataType *peStorageType) const override
5205
0
    {
5206
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
5207
0
    }
5208
5209
    // bool SetOffset(double dfOffset) override { return
5210
    // m_poParent->SetOffset(dfOffset); }
5211
5212
    // bool SetScale(double dfScale) override { return
5213
    // m_poParent->SetScale(dfScale); }
5214
5215
    std::vector<GUInt64> GetBlockSize() const override
5216
0
    {
5217
0
        std::vector<GUInt64> ret(GetDimensionCount());
5218
0
        const auto parentBlockSize(m_poParent->GetBlockSize());
5219
0
        for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
5220
0
        {
5221
0
            const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
5222
0
            if (iOldAxis != static_cast<size_t>(-1))
5223
0
            {
5224
0
                ret[i] = parentBlockSize[iOldAxis];
5225
0
            }
5226
0
        }
5227
0
        return ret;
5228
0
    }
5229
5230
    std::shared_ptr<GDALAttribute>
5231
    GetAttribute(const std::string &osName) const override
5232
0
    {
5233
0
        return m_poParent->GetAttribute(osName);
5234
0
    }
5235
5236
    std::vector<std::shared_ptr<GDALAttribute>>
5237
    GetAttributes(CSLConstList papszOptions = nullptr) const override
5238
0
    {
5239
0
        return m_poParent->GetAttributes(papszOptions);
5240
0
    }
5241
};
5242
5243
/************************************************************************/
5244
/*                        PrepareParentArrays()                         */
5245
/************************************************************************/
5246
5247
void GDALSlicedMDArray::PrepareParentArrays(
5248
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
5249
    const GPtrDiff_t *bufferStride) const
5250
0
{
5251
0
    const size_t nParentDimCount = m_parentRanges.size();
5252
0
    for (size_t i = 0; i < nParentDimCount; i++)
5253
0
    {
5254
        // For dimensions in parent that have no existence in sliced array
5255
0
        m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
5256
0
    }
5257
5258
0
    for (size_t i = 0; i < m_dims.size(); i++)
5259
0
    {
5260
0
        const auto iParent = m_mapDimIdxToParentDimIdx[i];
5261
0
        if (iParent != static_cast<size_t>(-1))
5262
0
        {
5263
0
            m_parentStart[iParent] =
5264
0
                m_parentRanges[iParent].m_nIncr >= 0
5265
0
                    ? m_parentRanges[iParent].m_nStartIdx +
5266
0
                          arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
5267
0
                    : m_parentRanges[iParent].m_nStartIdx -
5268
0
                          arrayStartIdx[i] *
5269
0
                              static_cast<GUInt64>(
5270
0
                                  -m_parentRanges[iParent].m_nIncr);
5271
0
            m_parentCount[iParent] = count[i];
5272
0
            if (arrayStep)
5273
0
            {
5274
0
                m_parentStep[iParent] =
5275
0
                    count[i] == 1 ? 1 :
5276
                                  // other checks should have ensured this does
5277
                        // not overflow
5278
0
                        arrayStep[i] * m_parentRanges[iParent].m_nIncr;
5279
0
            }
5280
0
            if (bufferStride)
5281
0
            {
5282
0
                m_parentStride[iParent] = bufferStride[i];
5283
0
            }
5284
0
        }
5285
0
    }
5286
0
}
5287
5288
/************************************************************************/
5289
/*                               IRead()                                */
5290
/************************************************************************/
5291
5292
bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5293
                              const GInt64 *arrayStep,
5294
                              const GPtrDiff_t *bufferStride,
5295
                              const GDALExtendedDataType &bufferDataType,
5296
                              void *pDstBuffer) const
5297
0
{
5298
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5299
0
    return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
5300
0
                            m_parentStep.data(), m_parentStride.data(),
5301
0
                            bufferDataType, pDstBuffer);
5302
0
}
5303
5304
/************************************************************************/
5305
/*                               IWrite()                               */
5306
/************************************************************************/
5307
5308
bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
5309
                               const size_t *count, const GInt64 *arrayStep,
5310
                               const GPtrDiff_t *bufferStride,
5311
                               const GDALExtendedDataType &bufferDataType,
5312
                               const void *pSrcBuffer)
5313
0
{
5314
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
5315
0
    return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
5316
0
                             m_parentStep.data(), m_parentStride.data(),
5317
0
                             bufferDataType, pSrcBuffer);
5318
0
}
5319
5320
/************************************************************************/
5321
/*                            IAdviseRead()                             */
5322
/************************************************************************/
5323
5324
bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
5325
                                    const size_t *count,
5326
                                    CSLConstList papszOptions) const
5327
0
{
5328
0
    PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
5329
0
    return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
5330
0
                                  papszOptions);
5331
0
}
5332
5333
/************************************************************************/
5334
/*                         CreateSlicedArray()                          */
5335
/************************************************************************/
5336
5337
static std::shared_ptr<GDALMDArray>
5338
CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
5339
                  const std::string &viewExpr, const std::string &activeSlice,
5340
                  bool bRenameDimensions,
5341
                  std::vector<GDALMDArray::ViewSpec> &viewSpecs)
5342
0
{
5343
0
    const auto &srcDims(self->GetDimensions());
5344
0
    if (srcDims.empty())
5345
0
    {
5346
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
5347
0
        return nullptr;
5348
0
    }
5349
5350
0
    CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
5351
0
    const auto nTokens = static_cast<size_t>(aosTokens.size());
5352
5353
0
    std::vector<std::shared_ptr<GDALDimension>> newDims;
5354
0
    std::vector<size_t> mapDimIdxToParentDimIdx;
5355
0
    std::vector<GDALSlicedMDArray::Range> parentRanges;
5356
0
    newDims.reserve(nTokens);
5357
0
    mapDimIdxToParentDimIdx.reserve(nTokens);
5358
0
    parentRanges.reserve(nTokens);
5359
5360
0
    bool bGotEllipsis = false;
5361
0
    size_t nCurSrcDim = 0;
5362
0
    std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
5363
0
    for (size_t i = 0; i < nTokens; i++)
5364
0
    {
5365
0
        const char *pszIdxSpec = aosTokens[i];
5366
0
        if (EQUAL(pszIdxSpec, "..."))
5367
0
        {
5368
0
            if (bGotEllipsis)
5369
0
            {
5370
0
                CPLError(CE_Failure, CPLE_AppDefined,
5371
0
                         "Only one single ellipsis is supported");
5372
0
                return nullptr;
5373
0
            }
5374
0
            bGotEllipsis = true;
5375
0
            const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
5376
0
            for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
5377
0
            {
5378
0
                parentRanges.emplace_back(0, 1);
5379
0
                newDims.push_back(srcDims[nCurSrcDim]);
5380
0
                mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5381
0
            }
5382
0
            continue;
5383
0
        }
5384
0
        else if (EQUAL(pszIdxSpec, "newaxis") ||
5385
0
                 EQUAL(pszIdxSpec, "np.newaxis"))
5386
0
        {
5387
0
            newDims.push_back(std::make_shared<GDALDimension>(
5388
0
                std::string(), "newaxis", std::string(), std::string(), 1));
5389
0
            mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
5390
0
            continue;
5391
0
        }
5392
0
        else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
5393
0
        {
5394
0
            if (nCurSrcDim >= srcDims.size())
5395
0
            {
5396
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5397
0
                         activeSlice.c_str());
5398
0
                return nullptr;
5399
0
            }
5400
5401
0
            auto nVal = CPLAtoGIntBig(pszIdxSpec);
5402
0
            GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
5403
0
            if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
5404
0
                (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
5405
0
            {
5406
0
                CPLError(CE_Failure, CPLE_AppDefined,
5407
0
                         "Index " CPL_FRMT_GIB " is out of bounds", nVal);
5408
0
                return nullptr;
5409
0
            }
5410
0
            if (nVal < 0)
5411
0
                nVal += nDimSize;
5412
0
            parentRanges.emplace_back(nVal, 0);
5413
0
        }
5414
0
        else
5415
0
        {
5416
0
            if (nCurSrcDim >= srcDims.size())
5417
0
            {
5418
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
5419
0
                         activeSlice.c_str());
5420
0
                return nullptr;
5421
0
            }
5422
5423
0
            CPLStringList aosRangeTokens(
5424
0
                CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
5425
0
            int nRangeTokens = aosRangeTokens.size();
5426
0
            if (nRangeTokens > 3)
5427
0
            {
5428
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
5429
0
                         pszIdxSpec);
5430
0
                return nullptr;
5431
0
            }
5432
0
            if (nRangeTokens <= 1)
5433
0
            {
5434
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
5435
0
                         pszIdxSpec);
5436
0
                return nullptr;
5437
0
            }
5438
0
            const char *pszStart = aosRangeTokens[0];
5439
0
            const char *pszEnd = aosRangeTokens[1];
5440
0
            const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
5441
0
            GDALSlicedMDArray::Range range;
5442
0
            const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
5443
0
            range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
5444
0
            if (range.m_nIncr == 0 ||
5445
0
                range.m_nIncr == std::numeric_limits<GInt64>::min())
5446
0
            {
5447
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
5448
0
                return nullptr;
5449
0
            }
5450
0
            auto startIdx(CPLAtoGIntBig(pszStart));
5451
0
            if (startIdx < 0)
5452
0
            {
5453
0
                if (nDimSize < static_cast<GUInt64>(-startIdx))
5454
0
                    startIdx = 0;
5455
0
                else
5456
0
                    startIdx = nDimSize + startIdx;
5457
0
            }
5458
0
            const bool bPosIncr = range.m_nIncr > 0;
5459
0
            range.m_nStartIdx = startIdx;
5460
0
            range.m_nStartIdx = EQUAL(pszStart, "")
5461
0
                                    ? (bPosIncr ? 0 : nDimSize - 1)
5462
0
                                    : range.m_nStartIdx;
5463
0
            if (range.m_nStartIdx >= nDimSize - 1)
5464
0
                range.m_nStartIdx = nDimSize - 1;
5465
0
            auto endIdx(CPLAtoGIntBig(pszEnd));
5466
0
            if (endIdx < 0)
5467
0
            {
5468
0
                const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
5469
0
                if (nDimSize < positiveEndIdx)
5470
0
                    endIdx = 0;
5471
0
                else
5472
0
                    endIdx = nDimSize - positiveEndIdx;
5473
0
            }
5474
0
            GUInt64 nEndIdx = endIdx;
5475
0
            nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
5476
0
            if (pszStart[0] || pszEnd[0])
5477
0
            {
5478
0
                if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
5479
0
                    (!bPosIncr && range.m_nStartIdx <= nEndIdx))
5480
0
                {
5481
0
                    CPLError(CE_Failure, CPLE_AppDefined,
5482
0
                             "Output dimension of size 0 is not allowed");
5483
0
                    return nullptr;
5484
0
                }
5485
0
            }
5486
0
            int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
5487
0
            const auto nAbsIncr = std::abs(range.m_nIncr);
5488
0
            const GUInt64 newSize =
5489
0
                (pszStart[0] == 0 && pszEnd[0] == 0 &&
5490
0
                 range.m_nStartIdx == nEndIdx)
5491
0
                    ? 1
5492
0
                : bPosIncr
5493
0
                    ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
5494
0
                    : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
5495
0
            const auto &poSrcDim = srcDims[nCurSrcDim];
5496
0
            if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
5497
0
                newSize == poSrcDim->GetSize())
5498
0
            {
5499
0
                newDims.push_back(poSrcDim);
5500
0
            }
5501
0
            else
5502
0
            {
5503
0
                std::string osNewDimName(poSrcDim->GetName());
5504
0
                if (bRenameDimensions)
5505
0
                {
5506
0
                    osNewDimName =
5507
0
                        "subset_" + poSrcDim->GetName() +
5508
0
                        CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
5509
0
                                   "_" CPL_FRMT_GUIB,
5510
0
                                   static_cast<GUIntBig>(range.m_nStartIdx),
5511
0
                                   static_cast<GIntBig>(range.m_nIncr),
5512
0
                                   static_cast<GUIntBig>(newSize));
5513
0
                }
5514
0
                auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
5515
0
                    std::string(), osNewDimName, poSrcDim->GetType(),
5516
0
                    range.m_nIncr > 0 ? poSrcDim->GetDirection()
5517
0
                                      : std::string(),
5518
0
                    newSize);
5519
0
                auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
5520
0
                if (poSrcIndexingVar &&
5521
0
                    poSrcIndexingVar->GetDimensionCount() == 1 &&
5522
0
                    poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
5523
0
                {
5524
0
                    std::vector<std::shared_ptr<GDALDimension>>
5525
0
                        indexingVarNewDims{poNewDim};
5526
0
                    std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
5527
0
                    std::vector<std::shared_ptr<GDALMDArray>>
5528
0
                        indexingVarNewIndexingVar;
5529
0
                    std::vector<GDALSlicedMDArray::Range>
5530
0
                        indexingVarParentRanges{range};
5531
0
                    auto poNewIndexingVar = GDALSlicedMDArray::Create(
5532
0
                        poSrcIndexingVar, pszIdxSpec,
5533
0
                        std::move(indexingVarNewDims),
5534
0
                        std::move(indexingVarMapDimIdxToParentDimIdx),
5535
0
                        std::move(indexingVarNewIndexingVar),
5536
0
                        std::move(indexingVarParentRanges));
5537
0
                    poNewDim->SetIndexingVariable(poNewIndexingVar);
5538
0
                    apoNewIndexingVariables.push_back(
5539
0
                        std::move(poNewIndexingVar));
5540
0
                }
5541
0
                newDims.push_back(std::move(poNewDim));
5542
0
            }
5543
0
            mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5544
0
            parentRanges.emplace_back(range);
5545
0
        }
5546
5547
0
        nCurSrcDim++;
5548
0
    }
5549
0
    for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
5550
0
    {
5551
0
        parentRanges.emplace_back(0, 1);
5552
0
        newDims.push_back(srcDims[nCurSrcDim]);
5553
0
        mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
5554
0
    }
5555
5556
0
    GDALMDArray::ViewSpec viewSpec;
5557
0
    viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
5558
0
    viewSpec.m_parentRanges = parentRanges;
5559
0
    viewSpecs.emplace_back(std::move(viewSpec));
5560
5561
0
    return GDALSlicedMDArray::Create(
5562
0
        self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
5563
0
        std::move(apoNewIndexingVariables), std::move(parentRanges));
5564
0
}
5565
5566
/************************************************************************/
5567
/*                       GDALExtractFieldMDArray                        */
5568
/************************************************************************/
5569
5570
class GDALExtractFieldMDArray final : public GDALPamMDArray
5571
{
5572
  private:
5573
    std::shared_ptr<GDALMDArray> m_poParent{};
5574
    GDALExtendedDataType m_dt;
5575
    std::string m_srcCompName;
5576
    mutable std::vector<GByte> m_pabyNoData{};
5577
5578
  protected:
5579
    GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
5580
                            const std::string &fieldName,
5581
                            const std::unique_ptr<GDALEDTComponent> &srcComp)
5582
0
        : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
5583
0
                                                 " of " +
5584
0
                                                 poParent->GetFullName()),
5585
0
          GDALPamMDArray(
5586
0
              std::string(),
5587
0
              "Extract field " + fieldName + " of " + poParent->GetFullName(),
5588
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
5589
0
          m_poParent(poParent), m_dt(srcComp->GetType()),
5590
0
          m_srcCompName(srcComp->GetName())
5591
0
    {
5592
0
        m_pabyNoData.resize(m_dt.GetSize());
5593
0
    }
5594
5595
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
5596
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
5597
               const GDALExtendedDataType &bufferDataType,
5598
               void *pDstBuffer) const override;
5599
5600
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
5601
                     CSLConstList papszOptions) const override
5602
0
    {
5603
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
5604
0
    }
5605
5606
  public:
5607
    static std::shared_ptr<GDALExtractFieldMDArray>
5608
    Create(const std::shared_ptr<GDALMDArray> &poParent,
5609
           const std::string &fieldName,
5610
           const std::unique_ptr<GDALEDTComponent> &srcComp)
5611
0
    {
5612
0
        auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
5613
0
            new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
5614
0
        newAr->SetSelf(newAr);
5615
0
        return newAr;
5616
0
    }
5617
5618
    ~GDALExtractFieldMDArray() override
5619
0
    {
5620
0
        m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5621
0
    }
5622
5623
    bool IsWritable() const override
5624
0
    {
5625
0
        return m_poParent->IsWritable();
5626
0
    }
5627
5628
    const std::string &GetFilename() const override
5629
0
    {
5630
0
        return m_poParent->GetFilename();
5631
0
    }
5632
5633
    const std::vector<std::shared_ptr<GDALDimension>> &
5634
    GetDimensions() const override
5635
0
    {
5636
0
        return m_poParent->GetDimensions();
5637
0
    }
5638
5639
    const GDALExtendedDataType &GetDataType() const override
5640
0
    {
5641
0
        return m_dt;
5642
0
    }
5643
5644
    const std::string &GetUnit() const override
5645
0
    {
5646
0
        return m_poParent->GetUnit();
5647
0
    }
5648
5649
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
5650
0
    {
5651
0
        return m_poParent->GetSpatialRef();
5652
0
    }
5653
5654
    const void *GetRawNoDataValue() const override
5655
0
    {
5656
0
        const void *parentNoData = m_poParent->GetRawNoDataValue();
5657
0
        if (parentNoData == nullptr)
5658
0
            return nullptr;
5659
5660
0
        m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
5661
0
        memset(&m_pabyNoData[0], 0, m_dt.GetSize());
5662
5663
0
        std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5664
0
        comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5665
0
            new GDALEDTComponent(m_srcCompName, 0, m_dt)));
5666
0
        auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
5667
0
                                                std::move(comps)));
5668
5669
0
        GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
5670
0
                                        &m_pabyNoData[0], tmpDT);
5671
5672
0
        return &m_pabyNoData[0];
5673
0
    }
5674
5675
    double GetOffset(bool *pbHasOffset,
5676
                     GDALDataType *peStorageType) const override
5677
0
    {
5678
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
5679
0
    }
5680
5681
    double GetScale(bool *pbHasScale,
5682
                    GDALDataType *peStorageType) const override
5683
0
    {
5684
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
5685
0
    }
5686
5687
    std::vector<GUInt64> GetBlockSize() const override
5688
0
    {
5689
0
        return m_poParent->GetBlockSize();
5690
0
    }
5691
};
5692
5693
/************************************************************************/
5694
/*                               IRead()                                */
5695
/************************************************************************/
5696
5697
bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
5698
                                    const size_t *count,
5699
                                    const GInt64 *arrayStep,
5700
                                    const GPtrDiff_t *bufferStride,
5701
                                    const GDALExtendedDataType &bufferDataType,
5702
                                    void *pDstBuffer) const
5703
0
{
5704
0
    std::vector<std::unique_ptr<GDALEDTComponent>> comps;
5705
0
    comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
5706
0
        new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
5707
0
    auto tmpDT(GDALExtendedDataType::Create(
5708
0
        std::string(), bufferDataType.GetSize(), std::move(comps)));
5709
5710
0
    return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
5711
0
                            tmpDT, pDstBuffer);
5712
0
}
5713
5714
/************************************************************************/
5715
/*                    CreateFieldNameExtractArray()                     */
5716
/************************************************************************/
5717
5718
static std::shared_ptr<GDALMDArray>
5719
CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
5720
                            const std::string &fieldName)
5721
0
{
5722
0
    CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
5723
0
    const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
5724
0
    for (const auto &comp : self->GetDataType().GetComponents())
5725
0
    {
5726
0
        if (comp->GetName() == fieldName)
5727
0
        {
5728
0
            srcComp = &comp;
5729
0
            break;
5730
0
        }
5731
0
    }
5732
0
    if (srcComp == nullptr)
5733
0
    {
5734
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
5735
0
                 fieldName.c_str());
5736
0
        return nullptr;
5737
0
    }
5738
0
    return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
5739
0
}
5740
5741
/************************************************************************/
5742
/*                              GetView()                               */
5743
/************************************************************************/
5744
5745
// clang-format off
5746
/** Return a view of the array using slicing or field access.
5747
 *
5748
 * The slice expression uses the same syntax as NumPy basic slicing and
5749
 * indexing. See
5750
 * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
5751
 * Or it can use field access by name. See
5752
 * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
5753
 *
5754
 * Multiple [] bracket elements can be concatenated, with a slice expression
5755
 * or field name inside each.
5756
 *
5757
 * For basic slicing and indexing, inside each [] bracket element, a list of
5758
 * indexes that apply to successive source dimensions, can be specified, using
5759
 * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
5760
 * or newaxis, using a comma separator.
5761
 *
5762
 * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
5763
 * <ul>
5764
 * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
5765
 *     at index 1 in the first dimension, and index 2 in the second dimension
5766
 *     from the source array. That is 5</li>
5767
 * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
5768
 * implemented internally doing this intermediate slicing approach.</li>
5769
 * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
5770
 * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
5771
 *     first dimension. That is [4,5,6,7].</li>
5772
 * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
5773
 *     second dimension. That is [2,6].</li>
5774
 * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
5775
 * the second dimension. That is [[2],[6]].</li>
5776
 * <li>GetView("[::,2]"): Same as
5777
 * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
5778
 * ellipsis only expands to one dimension here.</li>
5779
 * <li>GetView("[:,::2]"):
5780
 * returns a 2-dimensional array, with even-indexed elements of the second
5781
 * dimension. That is [[0,2],[4,6]].</li>
5782
 * <li>GetView("[:,1::2]"): returns a
5783
 * 2-dimensional array, with odd-indexed elements of the second dimension. That
5784
 * is [[1,3],[5,7]].</li>
5785
 * <li>GetView("[:,1:3:]"): returns a 2-dimensional
5786
 * array, with elements of the second dimension with index in the range [1,3[.
5787
 * That is [[1,2],[5,6]].</li>
5788
 * <li>GetView("[::-1,:]"): returns a 2-dimensional
5789
 * array, with the values in first dimension reversed. That is
5790
 * [[4,5,6,7],[0,1,2,3]].</li>
5791
 * <li>GetView("[newaxis,...]"): returns a
5792
 * 3-dimensional array, with an additional dimension of size 1 put at the
5793
 * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
5794
 * </ul>
5795
 *
5796
 * One difference with NumPy behavior is that ranges that would result in
5797
 * zero elements are not allowed (dimensions of size 0 not being allowed in the
5798
 * GDAL multidimensional model).
5799
 *
5800
 * For field access, the syntax to use is ["field_name"] or ['field_name'].
5801
 * Multiple field specification is not supported currently.
5802
 *
5803
 * Both type of access can be combined, e.g. GetView("[1]['field_name']")
5804
 *
5805
 * \note When using the GDAL Python bindings, natural Python syntax can be
5806
 * used. That is ar[0,::,1]["foo"] will be internally translated to
5807
 * ar.GetView("[0,::,1]['foo']")
5808
 * \note When using the C++ API and integer indexing only, you may use the
5809
 * at(idx0, idx1, ...) method.
5810
 *
5811
 * The returned array holds a reference to the original one, and thus is
5812
 * a view of it (not a copy). If the content of the original array changes,
5813
 * the content of the view array too. When using basic slicing and indexing,
5814
 * the view can be written if the underlying array is writable.
5815
 *
5816
 * This is the same as the C function GDALMDArrayGetView()
5817
 *
5818
 * @param viewExpr Expression expressing basic slicing and indexing, or field
5819
 * access.
5820
 * @return a new array, that holds a reference to the original one, and thus is
5821
 * a view of it (not a copy), or nullptr in case of error.
5822
 */
5823
// clang-format on
5824
5825
std::shared_ptr<GDALMDArray>
5826
GDALMDArray::GetView(const std::string &viewExpr) const
5827
0
{
5828
0
    std::vector<ViewSpec> viewSpecs;
5829
0
    return GetView(viewExpr, true, viewSpecs);
5830
0
}
5831
5832
//! @cond Doxygen_Suppress
5833
std::shared_ptr<GDALMDArray>
5834
GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
5835
                     std::vector<ViewSpec> &viewSpecs) const
5836
0
{
5837
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5838
0
    if (!self)
5839
0
    {
5840
0
        CPLError(CE_Failure, CPLE_AppDefined,
5841
0
                 "Driver implementation issue: m_pSelf not set !");
5842
0
        return nullptr;
5843
0
    }
5844
0
    std::string curExpr(viewExpr);
5845
0
    while (true)
5846
0
    {
5847
0
        if (curExpr.empty() || curExpr[0] != '[')
5848
0
        {
5849
0
            CPLError(CE_Failure, CPLE_AppDefined,
5850
0
                     "Slice string should start with ['");
5851
0
            return nullptr;
5852
0
        }
5853
5854
0
        std::string fieldName;
5855
0
        size_t endExpr;
5856
0
        if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
5857
0
        {
5858
0
            if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
5859
0
            {
5860
0
                CPLError(CE_Failure, CPLE_AppDefined,
5861
0
                         "Field access not allowed on non-compound data type");
5862
0
                return nullptr;
5863
0
            }
5864
0
            size_t idx = 2;
5865
0
            for (; idx < curExpr.size(); idx++)
5866
0
            {
5867
0
                const char ch = curExpr[idx];
5868
0
                if (ch == curExpr[1])
5869
0
                    break;
5870
0
                if (ch == '\\' && idx + 1 < curExpr.size())
5871
0
                {
5872
0
                    fieldName += curExpr[idx + 1];
5873
0
                    idx++;
5874
0
                }
5875
0
                else
5876
0
                {
5877
0
                    fieldName += ch;
5878
0
                }
5879
0
            }
5880
0
            if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
5881
0
            {
5882
0
                CPLError(CE_Failure, CPLE_AppDefined,
5883
0
                         "Invalid field access specification");
5884
0
                return nullptr;
5885
0
            }
5886
0
            endExpr = idx + 1;
5887
0
        }
5888
0
        else
5889
0
        {
5890
0
            endExpr = curExpr.find(']');
5891
0
        }
5892
0
        if (endExpr == std::string::npos)
5893
0
        {
5894
0
            CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
5895
0
            return nullptr;
5896
0
        }
5897
0
        if (endExpr == 1)
5898
0
        {
5899
0
            CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
5900
0
            return nullptr;
5901
0
        }
5902
0
        std::string activeSlice(curExpr.substr(1, endExpr - 1));
5903
5904
0
        if (!fieldName.empty())
5905
0
        {
5906
0
            ViewSpec viewSpec;
5907
0
            viewSpec.m_osFieldName = fieldName;
5908
0
            viewSpecs.emplace_back(std::move(viewSpec));
5909
0
        }
5910
5911
0
        auto newArray = !fieldName.empty()
5912
0
                            ? CreateFieldNameExtractArray(self, fieldName)
5913
0
                            : CreateSlicedArray(self, viewExpr, activeSlice,
5914
0
                                                bRenameDimensions, viewSpecs);
5915
5916
0
        if (endExpr == curExpr.size() - 1)
5917
0
        {
5918
0
            return newArray;
5919
0
        }
5920
0
        self = std::move(newArray);
5921
0
        curExpr = curExpr.substr(endExpr + 1);
5922
0
    }
5923
0
}
5924
5925
//! @endcond
5926
5927
std::shared_ptr<GDALMDArray>
5928
GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
5929
0
{
5930
0
    std::string osExpr("[");
5931
0
    bool bFirst = true;
5932
0
    for (const auto &idx : indices)
5933
0
    {
5934
0
        if (!bFirst)
5935
0
            osExpr += ',';
5936
0
        bFirst = false;
5937
0
        osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
5938
0
    }
5939
0
    return GetView(osExpr + ']');
5940
0
}
5941
5942
/************************************************************************/
5943
/*                              operator[]                              */
5944
/************************************************************************/
5945
5946
/** Return a view of the array using field access
5947
 *
5948
 * Equivalent of GetView("['fieldName']")
5949
 *
5950
 * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
5951
 */
5952
std::shared_ptr<GDALMDArray>
5953
GDALMDArray::operator[](const std::string &fieldName) const
5954
0
{
5955
0
    return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
5956
0
                                            .replaceAll('\\', "\\\\")
5957
0
                                            .replaceAll('\'', "\\\'")
5958
0
                                            .c_str()));
5959
0
}
5960
5961
/************************************************************************/
5962
/*                        GDALMDArrayTransposed                         */
5963
/************************************************************************/
5964
5965
class GDALMDArrayTransposed final : public GDALPamMDArray
5966
{
5967
  private:
5968
    std::shared_ptr<GDALMDArray> m_poParent{};
5969
    std::vector<int> m_anMapNewAxisToOldAxis{};
5970
    std::vector<std::shared_ptr<GDALDimension>> m_dims{};
5971
5972
    mutable std::vector<GUInt64> m_parentStart;
5973
    mutable std::vector<size_t> m_parentCount;
5974
    mutable std::vector<GInt64> m_parentStep;
5975
    mutable std::vector<GPtrDiff_t> m_parentStride;
5976
5977
    void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
5978
                             const GInt64 *arrayStep,
5979
                             const GPtrDiff_t *bufferStride) const;
5980
5981
    static std::string
5982
    MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
5983
0
    {
5984
0
        std::string ret;
5985
0
        ret += '[';
5986
0
        for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
5987
0
        {
5988
0
            if (i > 0)
5989
0
                ret += ',';
5990
0
            ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
5991
0
        }
5992
0
        ret += ']';
5993
0
        return ret;
5994
0
    }
5995
5996
  protected:
5997
    GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
5998
                          const std::vector<int> &anMapNewAxisToOldAxis,
5999
                          std::vector<std::shared_ptr<GDALDimension>> &&dims)
6000
0
        : GDALAbstractMDArray(std::string(),
6001
0
                              "Transposed view of " + poParent->GetFullName() +
6002
0
                                  " along " +
6003
0
                                  MappingToStr(anMapNewAxisToOldAxis)),
6004
0
          GDALPamMDArray(std::string(),
6005
0
                         "Transposed view of " + poParent->GetFullName() +
6006
0
                             " along " + MappingToStr(anMapNewAxisToOldAxis),
6007
0
                         GDALPamMultiDim::GetPAM(poParent),
6008
0
                         poParent->GetContext()),
6009
0
          m_poParent(std::move(poParent)),
6010
0
          m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
6011
0
          m_dims(std::move(dims)),
6012
0
          m_parentStart(m_poParent->GetDimensionCount()),
6013
0
          m_parentCount(m_poParent->GetDimensionCount()),
6014
0
          m_parentStep(m_poParent->GetDimensionCount()),
6015
0
          m_parentStride(m_poParent->GetDimensionCount())
6016
0
    {
6017
0
    }
6018
6019
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6020
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6021
               const GDALExtendedDataType &bufferDataType,
6022
               void *pDstBuffer) const override;
6023
6024
    bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
6025
                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6026
                const GDALExtendedDataType &bufferDataType,
6027
                const void *pSrcBuffer) override;
6028
6029
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6030
                     CSLConstList papszOptions) const override;
6031
6032
  public:
6033
    static std::shared_ptr<GDALMDArrayTransposed>
6034
    Create(const std::shared_ptr<GDALMDArray> &poParent,
6035
           const std::vector<int> &anMapNewAxisToOldAxis)
6036
0
    {
6037
0
        const auto &parentDims(poParent->GetDimensions());
6038
0
        std::vector<std::shared_ptr<GDALDimension>> dims;
6039
0
        for (const auto iOldAxis : anMapNewAxisToOldAxis)
6040
0
        {
6041
0
            if (iOldAxis < 0)
6042
0
            {
6043
0
                dims.push_back(std::make_shared<GDALDimension>(
6044
0
                    std::string(), "newaxis", std::string(), std::string(), 1));
6045
0
            }
6046
0
            else
6047
0
            {
6048
0
                dims.emplace_back(parentDims[iOldAxis]);
6049
0
            }
6050
0
        }
6051
6052
0
        auto newAr(
6053
0
            std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
6054
0
                poParent, anMapNewAxisToOldAxis, std::move(dims))));
6055
0
        newAr->SetSelf(newAr);
6056
0
        return newAr;
6057
0
    }
6058
6059
    bool IsWritable() const override
6060
0
    {
6061
0
        return m_poParent->IsWritable();
6062
0
    }
6063
6064
    const std::string &GetFilename() const override
6065
0
    {
6066
0
        return m_poParent->GetFilename();
6067
0
    }
6068
6069
    const std::vector<std::shared_ptr<GDALDimension>> &
6070
    GetDimensions() const override
6071
0
    {
6072
0
        return m_dims;
6073
0
    }
6074
6075
    const GDALExtendedDataType &GetDataType() const override
6076
0
    {
6077
0
        return m_poParent->GetDataType();
6078
0
    }
6079
6080
    const std::string &GetUnit() const override
6081
0
    {
6082
0
        return m_poParent->GetUnit();
6083
0
    }
6084
6085
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6086
0
    {
6087
0
        auto poSrcSRS = m_poParent->GetSpatialRef();
6088
0
        if (!poSrcSRS)
6089
0
            return nullptr;
6090
0
        auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
6091
0
        std::vector<int> dstMapping;
6092
0
        for (int srcAxis : srcMapping)
6093
0
        {
6094
0
            bool bFound = false;
6095
0
            for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
6096
0
            {
6097
0
                if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
6098
0
                {
6099
0
                    dstMapping.push_back(static_cast<int>(i) + 1);
6100
0
                    bFound = true;
6101
0
                    break;
6102
0
                }
6103
0
            }
6104
0
            if (!bFound)
6105
0
            {
6106
0
                dstMapping.push_back(0);
6107
0
            }
6108
0
        }
6109
0
        auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
6110
0
        poClone->SetDataAxisToSRSAxisMapping(dstMapping);
6111
0
        return poClone;
6112
0
    }
6113
6114
    const void *GetRawNoDataValue() const override
6115
0
    {
6116
0
        return m_poParent->GetRawNoDataValue();
6117
0
    }
6118
6119
    // bool SetRawNoDataValue(const void* pRawNoData) override { return
6120
    // m_poParent->SetRawNoDataValue(pRawNoData); }
6121
6122
    double GetOffset(bool *pbHasOffset,
6123
                     GDALDataType *peStorageType) const override
6124
0
    {
6125
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
6126
0
    }
6127
6128
    double GetScale(bool *pbHasScale,
6129
                    GDALDataType *peStorageType) const override
6130
0
    {
6131
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
6132
0
    }
6133
6134
    // bool SetOffset(double dfOffset) override { return
6135
    // m_poParent->SetOffset(dfOffset); }
6136
6137
    // bool SetScale(double dfScale) override { return
6138
    // m_poParent->SetScale(dfScale); }
6139
6140
    std::vector<GUInt64> GetBlockSize() const override
6141
0
    {
6142
0
        std::vector<GUInt64> ret(GetDimensionCount());
6143
0
        const auto parentBlockSize(m_poParent->GetBlockSize());
6144
0
        for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6145
0
        {
6146
0
            const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6147
0
            if (iOldAxis >= 0)
6148
0
            {
6149
0
                ret[i] = parentBlockSize[iOldAxis];
6150
0
            }
6151
0
        }
6152
0
        return ret;
6153
0
    }
6154
6155
    std::shared_ptr<GDALAttribute>
6156
    GetAttribute(const std::string &osName) const override
6157
0
    {
6158
0
        return m_poParent->GetAttribute(osName);
6159
0
    }
6160
6161
    std::vector<std::shared_ptr<GDALAttribute>>
6162
    GetAttributes(CSLConstList papszOptions = nullptr) const override
6163
0
    {
6164
0
        return m_poParent->GetAttributes(papszOptions);
6165
0
    }
6166
};
6167
6168
/************************************************************************/
6169
/*                        PrepareParentArrays()                         */
6170
/************************************************************************/
6171
6172
void GDALMDArrayTransposed::PrepareParentArrays(
6173
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
6174
    const GPtrDiff_t *bufferStride) const
6175
0
{
6176
0
    for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
6177
0
    {
6178
0
        const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
6179
0
        if (iOldAxis >= 0)
6180
0
        {
6181
0
            m_parentStart[iOldAxis] = arrayStartIdx[i];
6182
0
            m_parentCount[iOldAxis] = count[i];
6183
0
            if (arrayStep)  // only null when called from IAdviseRead()
6184
0
            {
6185
0
                m_parentStep[iOldAxis] = arrayStep[i];
6186
0
            }
6187
0
            if (bufferStride)  // only null when called from IAdviseRead()
6188
0
            {
6189
0
                m_parentStride[iOldAxis] = bufferStride[i];
6190
0
            }
6191
0
        }
6192
0
    }
6193
0
}
6194
6195
/************************************************************************/
6196
/*                               IRead()                                */
6197
/************************************************************************/
6198
6199
bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
6200
                                  const size_t *count, const GInt64 *arrayStep,
6201
                                  const GPtrDiff_t *bufferStride,
6202
                                  const GDALExtendedDataType &bufferDataType,
6203
                                  void *pDstBuffer) const
6204
0
{
6205
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6206
0
    return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
6207
0
                            m_parentStep.data(), m_parentStride.data(),
6208
0
                            bufferDataType, pDstBuffer);
6209
0
}
6210
6211
/************************************************************************/
6212
/*                               IWrite()                               */
6213
/************************************************************************/
6214
6215
bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
6216
                                   const size_t *count, const GInt64 *arrayStep,
6217
                                   const GPtrDiff_t *bufferStride,
6218
                                   const GDALExtendedDataType &bufferDataType,
6219
                                   const void *pSrcBuffer)
6220
0
{
6221
0
    PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
6222
0
    return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
6223
0
                             m_parentStep.data(), m_parentStride.data(),
6224
0
                             bufferDataType, pSrcBuffer);
6225
0
}
6226
6227
/************************************************************************/
6228
/*                            IAdviseRead()                             */
6229
/************************************************************************/
6230
6231
bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
6232
                                        const size_t *count,
6233
                                        CSLConstList papszOptions) const
6234
0
{
6235
0
    PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
6236
0
    return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
6237
0
                                  papszOptions);
6238
0
}
6239
6240
/************************************************************************/
6241
/*                             Transpose()                              */
6242
/************************************************************************/
6243
6244
/** Return a view of the array whose axis have been reordered.
6245
 *
6246
 * The anMapNewAxisToOldAxis parameter should contain all the values between 0
6247
 * and GetDimensionCount() - 1, and each only once.
6248
 * -1 can be used as a special index value to ask for the insertion of a new
6249
 * axis of size 1.
6250
 * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
6251
 * index of one of its dimension, it corresponds to the axis of index
6252
 * anMapNewAxisToOldAxis[i] from the current array.
6253
 *
6254
 * This is similar to the numpy.transpose() method
6255
 *
6256
 * The returned array holds a reference to the original one, and thus is
6257
 * a view of it (not a copy). If the content of the original array changes,
6258
 * the content of the view array too. The view can be written if the underlying
6259
 * array is writable.
6260
 *
6261
 * Note that I/O performance in such a transposed view might be poor.
6262
 *
6263
 * This is the same as the C function GDALMDArrayTranspose().
6264
 *
6265
 * @return a new array, that holds a reference to the original one, and thus is
6266
 * a view of it (not a copy), or nullptr in case of error.
6267
 */
6268
std::shared_ptr<GDALMDArray>
6269
GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
6270
0
{
6271
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6272
0
    if (!self)
6273
0
    {
6274
0
        CPLError(CE_Failure, CPLE_AppDefined,
6275
0
                 "Driver implementation issue: m_pSelf not set !");
6276
0
        return nullptr;
6277
0
    }
6278
0
    const int nDims = static_cast<int>(GetDimensionCount());
6279
0
    std::vector<bool> alreadyUsedOldAxis(nDims, false);
6280
0
    int nCountOldAxis = 0;
6281
0
    for (const auto iOldAxis : anMapNewAxisToOldAxis)
6282
0
    {
6283
0
        if (iOldAxis < -1 || iOldAxis >= nDims)
6284
0
        {
6285
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
6286
0
            return nullptr;
6287
0
        }
6288
0
        if (iOldAxis >= 0)
6289
0
        {
6290
0
            if (alreadyUsedOldAxis[iOldAxis])
6291
0
            {
6292
0
                CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
6293
0
                         iOldAxis);
6294
0
                return nullptr;
6295
0
            }
6296
0
            alreadyUsedOldAxis[iOldAxis] = true;
6297
0
            nCountOldAxis++;
6298
0
        }
6299
0
    }
6300
0
    if (nCountOldAxis != nDims)
6301
0
    {
6302
0
        CPLError(CE_Failure, CPLE_AppDefined,
6303
0
                 "One or several original axis missing");
6304
0
        return nullptr;
6305
0
    }
6306
0
    return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
6307
0
}
6308
6309
/************************************************************************/
6310
/*                               IRead()                                */
6311
/************************************************************************/
6312
6313
bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
6314
                                const size_t *count, const GInt64 *arrayStep,
6315
                                const GPtrDiff_t *bufferStride,
6316
                                const GDALExtendedDataType &bufferDataType,
6317
                                void *pDstBuffer) const
6318
0
{
6319
0
    const double dfScale = m_dfScale;
6320
0
    const double dfOffset = m_dfOffset;
6321
0
    const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6322
0
    const auto dtDouble =
6323
0
        GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6324
0
    const size_t nDTSize = dtDouble.GetSize();
6325
0
    const bool bTempBufferNeeded = (dtDouble != bufferDataType);
6326
6327
0
    double adfSrcNoData[2] = {0, 0};
6328
0
    if (m_bHasNoData)
6329
0
    {
6330
0
        GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6331
0
                                        m_poParent->GetDataType(),
6332
0
                                        &adfSrcNoData[0], dtDouble);
6333
0
    }
6334
6335
0
    const auto nDims = GetDimensions().size();
6336
0
    if (nDims == 0)
6337
0
    {
6338
0
        double adfVal[2];
6339
0
        if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
6340
0
                              dtDouble, &adfVal[0]))
6341
0
        {
6342
0
            return false;
6343
0
        }
6344
0
        if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
6345
0
        {
6346
0
            adfVal[0] = adfVal[0] * dfScale + dfOffset;
6347
0
            if (bDTIsComplex)
6348
0
            {
6349
0
                adfVal[1] = adfVal[1] * dfScale + dfOffset;
6350
0
            }
6351
0
            GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
6352
0
                                            bufferDataType);
6353
0
        }
6354
0
        else
6355
0
        {
6356
0
            GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
6357
0
                                            pDstBuffer, bufferDataType);
6358
0
        }
6359
0
        return true;
6360
0
    }
6361
6362
0
    std::vector<GPtrDiff_t> actualBufferStrideVector;
6363
0
    const GPtrDiff_t *actualBufferStridePtr = bufferStride;
6364
0
    void *pTempBuffer = pDstBuffer;
6365
0
    if (bTempBufferNeeded)
6366
0
    {
6367
0
        size_t nElts = 1;
6368
0
        actualBufferStrideVector.resize(nDims);
6369
0
        for (size_t i = 0; i < nDims; i++)
6370
0
            nElts *= count[i];
6371
0
        actualBufferStrideVector.back() = 1;
6372
0
        for (size_t i = nDims - 1; i > 0;)
6373
0
        {
6374
0
            --i;
6375
0
            actualBufferStrideVector[i] =
6376
0
                actualBufferStrideVector[i + 1] * count[i + 1];
6377
0
        }
6378
0
        actualBufferStridePtr = actualBufferStrideVector.data();
6379
0
        pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6380
0
        if (!pTempBuffer)
6381
0
            return false;
6382
0
    }
6383
0
    if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
6384
0
                          actualBufferStridePtr, dtDouble, pTempBuffer))
6385
0
    {
6386
0
        if (bTempBufferNeeded)
6387
0
            VSIFree(pTempBuffer);
6388
0
        return false;
6389
0
    }
6390
6391
0
    struct Stack
6392
0
    {
6393
0
        size_t nIters = 0;
6394
0
        double *src_ptr = nullptr;
6395
0
        GByte *dst_ptr = nullptr;
6396
0
        GPtrDiff_t src_inc_offset = 0;
6397
0
        GPtrDiff_t dst_inc_offset = 0;
6398
0
    };
6399
6400
0
    std::vector<Stack> stack(nDims);
6401
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
6402
0
    for (size_t i = 0; i < nDims; i++)
6403
0
    {
6404
0
        stack[i].src_inc_offset =
6405
0
            actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6406
0
        stack[i].dst_inc_offset =
6407
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6408
0
    }
6409
0
    stack[0].src_ptr = static_cast<double *>(pTempBuffer);
6410
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
6411
6412
0
    size_t dimIdx = 0;
6413
0
    const size_t nDimsMinus1 = nDims - 1;
6414
0
    GByte abyDstNoData[16];
6415
0
    CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
6416
0
    GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
6417
0
                                    bufferDataType);
6418
6419
0
lbl_next_depth:
6420
0
    if (dimIdx == nDimsMinus1)
6421
0
    {
6422
0
        auto nIters = count[dimIdx];
6423
0
        double *padfVal = stack[dimIdx].src_ptr;
6424
0
        GByte *dst_ptr = stack[dimIdx].dst_ptr;
6425
0
        while (true)
6426
0
        {
6427
0
            if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
6428
0
            {
6429
0
                padfVal[0] = padfVal[0] * dfScale + dfOffset;
6430
0
                if (bDTIsComplex)
6431
0
                {
6432
0
                    padfVal[1] = padfVal[1] * dfScale + dfOffset;
6433
0
                }
6434
0
                if (bTempBufferNeeded)
6435
0
                {
6436
0
                    GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
6437
0
                                                    dst_ptr, bufferDataType);
6438
0
                }
6439
0
            }
6440
0
            else
6441
0
            {
6442
0
                memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
6443
0
            }
6444
6445
0
            if ((--nIters) == 0)
6446
0
                break;
6447
0
            padfVal += stack[dimIdx].src_inc_offset;
6448
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
6449
0
        }
6450
0
    }
6451
0
    else
6452
0
    {
6453
0
        stack[dimIdx].nIters = count[dimIdx];
6454
0
        while (true)
6455
0
        {
6456
0
            dimIdx++;
6457
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6458
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6459
0
            goto lbl_next_depth;
6460
0
        lbl_return_to_caller:
6461
0
            dimIdx--;
6462
0
            if ((--stack[dimIdx].nIters) == 0)
6463
0
                break;
6464
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6465
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6466
0
        }
6467
0
    }
6468
0
    if (dimIdx > 0)
6469
0
        goto lbl_return_to_caller;
6470
6471
0
    if (bTempBufferNeeded)
6472
0
        VSIFree(pTempBuffer);
6473
0
    return true;
6474
0
}
6475
6476
/************************************************************************/
6477
/*                               IWrite()                               */
6478
/************************************************************************/
6479
6480
bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
6481
                                 const size_t *count, const GInt64 *arrayStep,
6482
                                 const GPtrDiff_t *bufferStride,
6483
                                 const GDALExtendedDataType &bufferDataType,
6484
                                 const void *pSrcBuffer)
6485
0
{
6486
0
    const double dfScale = m_dfScale;
6487
0
    const double dfOffset = m_dfOffset;
6488
0
    const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
6489
0
    const auto dtDouble =
6490
0
        GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
6491
0
    const size_t nDTSize = dtDouble.GetSize();
6492
0
    const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
6493
0
    const bool bSelfAndParentHaveNoData =
6494
0
        m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
6495
0
    double dfNoData = 0;
6496
0
    if (m_bHasNoData)
6497
0
    {
6498
0
        GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
6499
0
                        &dfNoData, GDT_Float64, 0, 1);
6500
0
    }
6501
6502
0
    double adfSrcNoData[2] = {0, 0};
6503
0
    if (bSelfAndParentHaveNoData)
6504
0
    {
6505
0
        GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
6506
0
                                        m_poParent->GetDataType(),
6507
0
                                        &adfSrcNoData[0], dtDouble);
6508
0
    }
6509
6510
0
    const auto nDims = GetDimensions().size();
6511
0
    if (nDims == 0)
6512
0
    {
6513
0
        double adfVal[2];
6514
0
        GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
6515
0
                                        dtDouble);
6516
0
        if (bSelfAndParentHaveNoData &&
6517
0
            (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
6518
0
        {
6519
0
            return m_poParent->Write(arrayStartIdx, count, arrayStep,
6520
0
                                     bufferStride, m_poParent->GetDataType(),
6521
0
                                     m_poParent->GetRawNoDataValue());
6522
0
        }
6523
0
        else
6524
0
        {
6525
0
            adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
6526
0
            if (bDTIsComplex)
6527
0
            {
6528
0
                adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
6529
0
            }
6530
0
            return m_poParent->Write(arrayStartIdx, count, arrayStep,
6531
0
                                     bufferStride, dtDouble, &adfVal[0]);
6532
0
        }
6533
0
    }
6534
6535
0
    std::vector<GPtrDiff_t> tmpBufferStrideVector;
6536
0
    size_t nElts = 1;
6537
0
    tmpBufferStrideVector.resize(nDims);
6538
0
    for (size_t i = 0; i < nDims; i++)
6539
0
        nElts *= count[i];
6540
0
    tmpBufferStrideVector.back() = 1;
6541
0
    for (size_t i = nDims - 1; i > 0;)
6542
0
    {
6543
0
        --i;
6544
0
        tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
6545
0
    }
6546
0
    const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
6547
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
6548
0
    if (!pTempBuffer)
6549
0
        return false;
6550
6551
0
    struct Stack
6552
0
    {
6553
0
        size_t nIters = 0;
6554
0
        double *dst_ptr = nullptr;
6555
0
        const GByte *src_ptr = nullptr;
6556
0
        GPtrDiff_t src_inc_offset = 0;
6557
0
        GPtrDiff_t dst_inc_offset = 0;
6558
0
    };
6559
6560
0
    std::vector<Stack> stack(nDims);
6561
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
6562
0
    for (size_t i = 0; i < nDims; i++)
6563
0
    {
6564
0
        stack[i].dst_inc_offset =
6565
0
            tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
6566
0
        stack[i].src_inc_offset =
6567
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
6568
0
    }
6569
0
    stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
6570
0
    stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
6571
6572
0
    size_t dimIdx = 0;
6573
0
    const size_t nDimsMinus1 = nDims - 1;
6574
6575
0
lbl_next_depth:
6576
0
    if (dimIdx == nDimsMinus1)
6577
0
    {
6578
0
        auto nIters = count[dimIdx];
6579
0
        double *dst_ptr = stack[dimIdx].dst_ptr;
6580
0
        const GByte *src_ptr = stack[dimIdx].src_ptr;
6581
0
        while (true)
6582
0
        {
6583
0
            double adfVal[2];
6584
0
            const double *padfSrcVal;
6585
0
            if (bIsBufferDataTypeNativeDataType)
6586
0
            {
6587
0
                padfSrcVal = reinterpret_cast<const double *>(src_ptr);
6588
0
            }
6589
0
            else
6590
0
            {
6591
0
                GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
6592
0
                                                &adfVal[0], dtDouble);
6593
0
                padfSrcVal = adfVal;
6594
0
            }
6595
6596
0
            if (bSelfAndParentHaveNoData &&
6597
0
                (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
6598
0
            {
6599
0
                dst_ptr[0] = adfSrcNoData[0];
6600
0
                if (bDTIsComplex)
6601
0
                {
6602
0
                    dst_ptr[1] = adfSrcNoData[1];
6603
0
                }
6604
0
            }
6605
0
            else
6606
0
            {
6607
0
                dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
6608
0
                if (bDTIsComplex)
6609
0
                {
6610
0
                    dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
6611
0
                }
6612
0
            }
6613
6614
0
            if ((--nIters) == 0)
6615
0
                break;
6616
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
6617
0
            src_ptr += stack[dimIdx].src_inc_offset;
6618
0
        }
6619
0
    }
6620
0
    else
6621
0
    {
6622
0
        stack[dimIdx].nIters = count[dimIdx];
6623
0
        while (true)
6624
0
        {
6625
0
            dimIdx++;
6626
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
6627
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
6628
0
            goto lbl_next_depth;
6629
0
        lbl_return_to_caller:
6630
0
            dimIdx--;
6631
0
            if ((--stack[dimIdx].nIters) == 0)
6632
0
                break;
6633
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
6634
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
6635
0
        }
6636
0
    }
6637
0
    if (dimIdx > 0)
6638
0
        goto lbl_return_to_caller;
6639
6640
    // If the parent array is not double/complex-double, then convert the
6641
    // values to it, before calling Write(), as some implementations can be
6642
    // very slow when doing the type conversion.
6643
0
    const auto &eParentDT = m_poParent->GetDataType();
6644
0
    const size_t nParentDTSize = eParentDT.GetSize();
6645
0
    if (nParentDTSize <= nDTSize / 2)
6646
0
    {
6647
        // Copy in-place by making sure that source and target do not overlap
6648
0
        const auto eNumericDT = dtDouble.GetNumericDataType();
6649
0
        const auto eParentNumericDT = eParentDT.GetNumericDataType();
6650
6651
        // Copy first element
6652
0
        {
6653
0
            std::vector<GByte> abyTemp(nParentDTSize);
6654
0
            GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
6655
0
                            static_cast<int>(nDTSize), &abyTemp[0],
6656
0
                            eParentNumericDT, static_cast<int>(nParentDTSize),
6657
0
                            1);
6658
0
            memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
6659
0
        }
6660
        // Remaining elements
6661
0
        for (size_t i = 1; i < nElts; ++i)
6662
0
        {
6663
0
            GDALCopyWords64(
6664
0
                static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
6665
0
                static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
6666
0
                eParentNumericDT, 0, 1);
6667
0
        }
6668
0
    }
6669
6670
0
    const bool ret =
6671
0
        m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
6672
0
                          eParentDT, pTempBuffer);
6673
6674
0
    VSIFree(pTempBuffer);
6675
0
    return ret;
6676
0
}
6677
6678
/************************************************************************/
6679
/*                            GetUnscaled()                             */
6680
/************************************************************************/
6681
6682
/** Return an array that is the unscaled version of the current one.
6683
 *
6684
 * That is each value of the unscaled array will be
6685
 * unscaled_value = raw_value * GetScale() + GetOffset()
6686
 *
6687
 * Starting with GDAL 3.3, the Write() method is implemented and will convert
6688
 * from unscaled values to raw values.
6689
 *
6690
 * This is the same as the C function GDALMDArrayGetUnscaled().
6691
 *
6692
 * @param dfOverriddenScale Custom scale value instead of GetScale()
6693
 * @param dfOverriddenOffset Custom offset value instead of GetOffset()
6694
 * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
6695
 * @return a new array, that holds a reference to the original one, and thus is
6696
 * a view of it (not a copy), or nullptr in case of error.
6697
 */
6698
std::shared_ptr<GDALMDArray>
6699
GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
6700
                         double dfOverriddenDstNodata) const
6701
0
{
6702
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
6703
0
    if (!self)
6704
0
    {
6705
0
        CPLError(CE_Failure, CPLE_AppDefined,
6706
0
                 "Driver implementation issue: m_pSelf not set !");
6707
0
        return nullptr;
6708
0
    }
6709
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
6710
0
    {
6711
0
        CPLError(CE_Failure, CPLE_AppDefined,
6712
0
                 "GetUnscaled() only supports numeric data type");
6713
0
        return nullptr;
6714
0
    }
6715
0
    const double dfScale =
6716
0
        std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
6717
0
    const double dfOffset =
6718
0
        std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
6719
0
    if (dfScale == 1.0 && dfOffset == 0.0)
6720
0
        return self;
6721
6722
0
    GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
6723
0
                           ? GDT_CFloat64
6724
0
                           : GDT_Float64;
6725
0
    if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
6726
0
    {
6727
0
        if (GetDataType().GetNumericDataType() == GDT_Float16)
6728
0
            eDT = GDT_Float16;
6729
0
        if (GetDataType().GetNumericDataType() == GDT_Float32)
6730
0
            eDT = GDT_Float32;
6731
0
    }
6732
6733
0
    return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
6734
0
                                       dfOverriddenDstNodata, eDT);
6735
0
}
6736
6737
/************************************************************************/
6738
/*                           GDALMDArrayMask                            */
6739
/************************************************************************/
6740
6741
class GDALMDArrayMask final : public GDALPamMDArray
6742
{
6743
  private:
6744
    std::shared_ptr<GDALMDArray> m_poParent{};
6745
    GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
6746
    double m_dfMissingValue = 0.0;
6747
    bool m_bHasMissingValue = false;
6748
    double m_dfFillValue = 0.0;
6749
    bool m_bHasFillValue = false;
6750
    double m_dfValidMin = 0.0;
6751
    bool m_bHasValidMin = false;
6752
    double m_dfValidMax = 0.0;
6753
    bool m_bHasValidMax = false;
6754
    std::vector<uint32_t> m_anValidFlagMasks{};
6755
    std::vector<uint32_t> m_anValidFlagValues{};
6756
6757
    bool Init(CSLConstList papszOptions);
6758
6759
    template <typename Type>
6760
    void
6761
    ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
6762
                 const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
6763
                 const void *pTempBuffer,
6764
                 const GDALExtendedDataType &oTmpBufferDT,
6765
                 const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
6766
6767
  protected:
6768
    explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
6769
0
        : GDALAbstractMDArray(std::string(),
6770
0
                              "Mask of " + poParent->GetFullName()),
6771
0
          GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
6772
0
                         GDALPamMultiDim::GetPAM(poParent),
6773
0
                         poParent->GetContext()),
6774
0
          m_poParent(std::move(poParent))
6775
0
    {
6776
0
    }
6777
6778
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
6779
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
6780
               const GDALExtendedDataType &bufferDataType,
6781
               void *pDstBuffer) const override;
6782
6783
    bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
6784
                     CSLConstList papszOptions) const override
6785
0
    {
6786
0
        return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
6787
0
    }
6788
6789
  public:
6790
    static std::shared_ptr<GDALMDArrayMask>
6791
    Create(const std::shared_ptr<GDALMDArray> &poParent,
6792
           CSLConstList papszOptions);
6793
6794
    bool IsWritable() const override
6795
0
    {
6796
0
        return false;
6797
0
    }
6798
6799
    const std::string &GetFilename() const override
6800
0
    {
6801
0
        return m_poParent->GetFilename();
6802
0
    }
6803
6804
    const std::vector<std::shared_ptr<GDALDimension>> &
6805
    GetDimensions() const override
6806
0
    {
6807
0
        return m_poParent->GetDimensions();
6808
0
    }
6809
6810
    const GDALExtendedDataType &GetDataType() const override
6811
0
    {
6812
0
        return m_dt;
6813
0
    }
6814
6815
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
6816
0
    {
6817
0
        return m_poParent->GetSpatialRef();
6818
0
    }
6819
6820
    std::vector<GUInt64> GetBlockSize() const override
6821
0
    {
6822
0
        return m_poParent->GetBlockSize();
6823
0
    }
6824
};
6825
6826
/************************************************************************/
6827
/*                      GDALMDArrayMask::Create()                       */
6828
/************************************************************************/
6829
6830
/* static */ std::shared_ptr<GDALMDArrayMask>
6831
GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
6832
                        CSLConstList papszOptions)
6833
0
{
6834
0
    auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
6835
0
    newAr->SetSelf(newAr);
6836
0
    if (!newAr->Init(papszOptions))
6837
0
        return nullptr;
6838
0
    return newAr;
6839
0
}
6840
6841
/************************************************************************/
6842
/*                       GDALMDArrayMask::Init()                        */
6843
/************************************************************************/
6844
6845
bool GDALMDArrayMask::Init(CSLConstList papszOptions)
6846
0
{
6847
0
    const auto GetSingleValNumericAttr =
6848
0
        [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
6849
0
    {
6850
0
        auto poAttr = m_poParent->GetAttribute(pszAttrName);
6851
0
        if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
6852
0
        {
6853
0
            const auto anDimSizes = poAttr->GetDimensionsSize();
6854
0
            if (anDimSizes.empty() ||
6855
0
                (anDimSizes.size() == 1 && anDimSizes[0] == 1))
6856
0
            {
6857
0
                bHasVal = true;
6858
0
                dfVal = poAttr->ReadAsDouble();
6859
0
            }
6860
0
        }
6861
0
    };
6862
6863
0
    GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
6864
0
                            m_dfMissingValue);
6865
0
    GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
6866
0
    GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
6867
0
    GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
6868
6869
0
    {
6870
0
        auto poValidRange = m_poParent->GetAttribute("valid_range");
6871
0
        if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
6872
0
            poValidRange->GetDimensionsSize()[0] == 2 &&
6873
0
            poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
6874
0
        {
6875
0
            m_bHasValidMin = true;
6876
0
            m_bHasValidMax = true;
6877
0
            auto vals = poValidRange->ReadAsDoubleArray();
6878
0
            CPLAssert(vals.size() == 2);
6879
0
            m_dfValidMin = vals[0];
6880
0
            m_dfValidMax = vals[1];
6881
0
        }
6882
0
    }
6883
6884
    // Take into account
6885
    // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
6886
    // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
6887
0
    const char *pszUnmaskFlags =
6888
0
        CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
6889
0
    if (pszUnmaskFlags)
6890
0
    {
6891
0
        const auto IsScalarStringAttr =
6892
0
            [](const std::shared_ptr<GDALAttribute> &poAttr)
6893
0
        {
6894
0
            return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
6895
0
                   (poAttr->GetDimensionsSize().empty() ||
6896
0
                    (poAttr->GetDimensionsSize().size() == 1 &&
6897
0
                     poAttr->GetDimensionsSize()[0] == 1));
6898
0
        };
6899
6900
0
        auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
6901
0
        if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
6902
0
        {
6903
0
            CPLError(CE_Failure, CPLE_AppDefined,
6904
0
                     "UNMASK_FLAGS option specified but array has no "
6905
0
                     "flag_meanings attribute");
6906
0
            return false;
6907
0
        }
6908
0
        const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
6909
0
        if (!pszFlagMeanings)
6910
0
        {
6911
0
            CPLError(CE_Failure, CPLE_AppDefined,
6912
0
                     "Cannot read flag_meanings attribute");
6913
0
            return false;
6914
0
        }
6915
6916
0
        const auto IsSingleDimNumericAttr =
6917
0
            [](const std::shared_ptr<GDALAttribute> &poAttr)
6918
0
        {
6919
0
            return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
6920
0
                   poAttr->GetDimensionsSize().size() == 1;
6921
0
        };
6922
6923
0
        auto poFlagValues = m_poParent->GetAttribute("flag_values");
6924
0
        const bool bHasFlagValues =
6925
0
            poFlagValues && IsSingleDimNumericAttr(poFlagValues);
6926
6927
0
        auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
6928
0
        const bool bHasFlagMasks =
6929
0
            poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
6930
6931
0
        if (!bHasFlagValues && !bHasFlagMasks)
6932
0
        {
6933
0
            CPLError(CE_Failure, CPLE_AppDefined,
6934
0
                     "Cannot find flag_values and/or flag_masks attribute");
6935
0
            return false;
6936
0
        }
6937
6938
0
        const CPLStringList aosUnmaskFlags(
6939
0
            CSLTokenizeString2(pszUnmaskFlags, ",", 0));
6940
0
        const CPLStringList aosFlagMeanings(
6941
0
            CSLTokenizeString2(pszFlagMeanings, " ", 0));
6942
6943
0
        if (bHasFlagValues)
6944
0
        {
6945
0
            const auto eType = poFlagValues->GetDataType().GetNumericDataType();
6946
            // We could support Int64 or UInt64, but more work...
6947
0
            if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6948
0
                eType != GDT_UInt16 && eType != GDT_Int16 &&
6949
0
                eType != GDT_UInt32 && eType != GDT_Int32)
6950
0
            {
6951
0
                CPLError(CE_Failure, CPLE_NotSupported,
6952
0
                         "Unsupported data type for flag_values attribute: %s",
6953
0
                         GDALGetDataTypeName(eType));
6954
0
                return false;
6955
0
            }
6956
0
        }
6957
6958
0
        if (bHasFlagMasks)
6959
0
        {
6960
0
            const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
6961
            // We could support Int64 or UInt64, but more work...
6962
0
            if (eType != GDT_UInt8 && eType != GDT_Int8 &&
6963
0
                eType != GDT_UInt16 && eType != GDT_Int16 &&
6964
0
                eType != GDT_UInt32 && eType != GDT_Int32)
6965
0
            {
6966
0
                CPLError(CE_Failure, CPLE_NotSupported,
6967
0
                         "Unsupported data type for flag_masks attribute: %s",
6968
0
                         GDALGetDataTypeName(eType));
6969
0
                return false;
6970
0
            }
6971
0
        }
6972
6973
0
        const std::vector<double> adfValues(
6974
0
            bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
6975
0
                           : std::vector<double>());
6976
0
        const std::vector<double> adfMasks(
6977
0
            bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
6978
0
                          : std::vector<double>());
6979
6980
0
        if (bHasFlagValues &&
6981
0
            adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
6982
0
        {
6983
0
            CPLError(CE_Failure, CPLE_AppDefined,
6984
0
                     "Number of values in flag_values attribute is different "
6985
0
                     "from the one in flag_meanings");
6986
0
            return false;
6987
0
        }
6988
6989
0
        if (bHasFlagMasks &&
6990
0
            adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
6991
0
        {
6992
0
            CPLError(CE_Failure, CPLE_AppDefined,
6993
0
                     "Number of values in flag_masks attribute is different "
6994
0
                     "from the one in flag_meanings");
6995
0
            return false;
6996
0
        }
6997
6998
0
        for (int i = 0; i < aosUnmaskFlags.size(); ++i)
6999
0
        {
7000
0
            const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
7001
0
            if (nIdxFlag < 0)
7002
0
            {
7003
0
                CPLError(
7004
0
                    CE_Failure, CPLE_AppDefined,
7005
0
                    "Cannot fing flag %s in flag_meanings = '%s' attribute",
7006
0
                    aosUnmaskFlags[i], pszFlagMeanings);
7007
0
                return false;
7008
0
            }
7009
7010
0
            if (bHasFlagValues && adfValues[nIdxFlag] < 0)
7011
0
            {
7012
0
                CPLError(CE_Failure, CPLE_AppDefined,
7013
0
                         "Invalid value in flag_values[%d] = %f", nIdxFlag,
7014
0
                         adfValues[nIdxFlag]);
7015
0
                return false;
7016
0
            }
7017
7018
0
            if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
7019
0
            {
7020
0
                CPLError(CE_Failure, CPLE_AppDefined,
7021
0
                         "Invalid value in flag_masks[%d] = %f", nIdxFlag,
7022
0
                         adfMasks[nIdxFlag]);
7023
0
                return false;
7024
0
            }
7025
7026
0
            if (bHasFlagValues)
7027
0
            {
7028
0
                m_anValidFlagValues.push_back(
7029
0
                    static_cast<uint32_t>(adfValues[nIdxFlag]));
7030
0
            }
7031
7032
0
            if (bHasFlagMasks)
7033
0
            {
7034
0
                m_anValidFlagMasks.push_back(
7035
0
                    static_cast<uint32_t>(adfMasks[nIdxFlag]));
7036
0
            }
7037
0
        }
7038
0
    }
7039
7040
0
    return true;
7041
0
}
7042
7043
/************************************************************************/
7044
/*                               IRead()                                */
7045
/************************************************************************/
7046
7047
bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7048
                            const GInt64 *arrayStep,
7049
                            const GPtrDiff_t *bufferStride,
7050
                            const GDALExtendedDataType &bufferDataType,
7051
                            void *pDstBuffer) const
7052
0
{
7053
0
    if (bufferDataType.GetClass() != GEDTC_NUMERIC)
7054
0
    {
7055
0
        CPLError(CE_Failure, CPLE_AppDefined,
7056
0
                 "%s: only reading to a numeric data type is supported",
7057
0
                 __func__);
7058
0
        return false;
7059
0
    }
7060
0
    size_t nElts = 1;
7061
0
    const size_t nDims = GetDimensionCount();
7062
0
    std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
7063
0
    for (size_t i = 0; i < nDims; i++)
7064
0
        nElts *= count[i];
7065
0
    if (nDims > 0)
7066
0
    {
7067
0
        tmpBufferStrideVector.back() = 1;
7068
0
        for (size_t i = nDims - 1; i > 0;)
7069
0
        {
7070
0
            --i;
7071
0
            tmpBufferStrideVector[i] =
7072
0
                tmpBufferStrideVector[i + 1] * count[i + 1];
7073
0
        }
7074
0
    }
7075
7076
    /* Optimized case: if we are an integer data type and that there is no */
7077
    /* attribute that can be used to set mask = 0, then fill the mask buffer */
7078
    /* directly */
7079
0
    if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
7080
0
        !m_bHasValidMax && m_anValidFlagValues.empty() &&
7081
0
        m_anValidFlagMasks.empty() &&
7082
0
        m_poParent->GetRawNoDataValue() == nullptr &&
7083
0
        GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
7084
0
    {
7085
0
        const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7086
0
        if (bBufferDataTypeIsByte)  // Byte case
7087
0
        {
7088
0
            bool bContiguous = true;
7089
0
            for (size_t i = 0; i < nDims; i++)
7090
0
            {
7091
0
                if (bufferStride[i] != tmpBufferStrideVector[i])
7092
0
                {
7093
0
                    bContiguous = false;
7094
0
                    break;
7095
0
                }
7096
0
            }
7097
0
            if (bContiguous)
7098
0
            {
7099
                // CPLDebug("GDAL", "GetMask(): contiguous case");
7100
0
                memset(pDstBuffer, 1, nElts);
7101
0
                return true;
7102
0
            }
7103
0
        }
7104
7105
0
        struct Stack
7106
0
        {
7107
0
            size_t nIters = 0;
7108
0
            GByte *dst_ptr = nullptr;
7109
0
            GPtrDiff_t dst_inc_offset = 0;
7110
0
        };
7111
7112
0
        std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7113
0
        const size_t nBufferDTSize = bufferDataType.GetSize();
7114
0
        for (size_t i = 0; i < nDims; i++)
7115
0
        {
7116
0
            stack[i].dst_inc_offset =
7117
0
                static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7118
0
        }
7119
0
        stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7120
7121
0
        size_t dimIdx = 0;
7122
0
        const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7123
0
        GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
7124
0
        CPLAssert(nBufferDTSize <= 16);
7125
0
        const GByte flag = 1;
7126
0
        GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
7127
0
                        bufferDataType.GetNumericDataType(), 0, 1);
7128
7129
0
    lbl_next_depth:
7130
0
        if (dimIdx == nDimsMinus1)
7131
0
        {
7132
0
            auto nIters = nDims > 0 ? count[dimIdx] : 1;
7133
0
            GByte *dst_ptr = stack[dimIdx].dst_ptr;
7134
7135
0
            while (true)
7136
0
            {
7137
                // cppcheck-suppress knownConditionTrueFalse
7138
0
                if (bBufferDataTypeIsByte)
7139
0
                {
7140
0
                    *dst_ptr = flag;
7141
0
                }
7142
0
                else
7143
0
                {
7144
0
                    memcpy(dst_ptr, abyOne, nBufferDTSize);
7145
0
                }
7146
7147
0
                if ((--nIters) == 0)
7148
0
                    break;
7149
0
                dst_ptr += stack[dimIdx].dst_inc_offset;
7150
0
            }
7151
0
        }
7152
0
        else
7153
0
        {
7154
0
            stack[dimIdx].nIters = count[dimIdx];
7155
0
            while (true)
7156
0
            {
7157
0
                dimIdx++;
7158
0
                stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7159
0
                goto lbl_next_depth;
7160
0
            lbl_return_to_caller:
7161
0
                dimIdx--;
7162
0
                if ((--stack[dimIdx].nIters) == 0)
7163
0
                    break;
7164
0
                stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7165
0
            }
7166
0
        }
7167
0
        if (dimIdx > 0)
7168
0
            goto lbl_return_to_caller;
7169
7170
0
        return true;
7171
0
    }
7172
7173
0
    const auto oTmpBufferDT =
7174
0
        GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
7175
0
            ? GDALExtendedDataType::Create(GDT_Float64)
7176
0
            : m_poParent->GetDataType();
7177
0
    const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7178
0
    void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
7179
0
    if (!pTempBuffer)
7180
0
        return false;
7181
0
    if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
7182
0
                          tmpBufferStrideVector.data(), oTmpBufferDT,
7183
0
                          pTempBuffer))
7184
0
    {
7185
0
        VSIFree(pTempBuffer);
7186
0
        return false;
7187
0
    }
7188
7189
0
    switch (oTmpBufferDT.GetNumericDataType())
7190
0
    {
7191
0
        case GDT_UInt8:
7192
0
            ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
7193
0
                                pTempBuffer, oTmpBufferDT,
7194
0
                                tmpBufferStrideVector);
7195
0
            break;
7196
7197
0
        case GDT_Int8:
7198
0
            ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
7199
0
                                pTempBuffer, oTmpBufferDT,
7200
0
                                tmpBufferStrideVector);
7201
0
            break;
7202
7203
0
        case GDT_UInt16:
7204
0
            ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
7205
0
                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
7206
0
                                  tmpBufferStrideVector);
7207
0
            break;
7208
7209
0
        case GDT_Int16:
7210
0
            ReadInternal<GInt16>(count, bufferStride, bufferDataType,
7211
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7212
0
                                 tmpBufferStrideVector);
7213
0
            break;
7214
7215
0
        case GDT_UInt32:
7216
0
            ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
7217
0
                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
7218
0
                                  tmpBufferStrideVector);
7219
0
            break;
7220
7221
0
        case GDT_Int32:
7222
0
            ReadInternal<GInt32>(count, bufferStride, bufferDataType,
7223
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7224
0
                                 tmpBufferStrideVector);
7225
0
            break;
7226
7227
0
        case GDT_UInt64:
7228
0
            ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
7229
0
                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
7230
0
                                        tmpBufferStrideVector);
7231
0
            break;
7232
7233
0
        case GDT_Int64:
7234
0
            ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
7235
0
                                       pDstBuffer, pTempBuffer, oTmpBufferDT,
7236
0
                                       tmpBufferStrideVector);
7237
0
            break;
7238
7239
0
        case GDT_Float16:
7240
0
            ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
7241
0
                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
7242
0
                                   tmpBufferStrideVector);
7243
0
            break;
7244
7245
0
        case GDT_Float32:
7246
0
            ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
7247
0
                                pTempBuffer, oTmpBufferDT,
7248
0
                                tmpBufferStrideVector);
7249
0
            break;
7250
7251
0
        case GDT_Float64:
7252
0
            ReadInternal<double>(count, bufferStride, bufferDataType,
7253
0
                                 pDstBuffer, pTempBuffer, oTmpBufferDT,
7254
0
                                 tmpBufferStrideVector);
7255
0
            break;
7256
0
        case GDT_Unknown:
7257
0
        case GDT_CInt16:
7258
0
        case GDT_CInt32:
7259
0
        case GDT_CFloat16:
7260
0
        case GDT_CFloat32:
7261
0
        case GDT_CFloat64:
7262
0
        case GDT_TypeCount:
7263
0
            CPLAssert(false);
7264
0
            break;
7265
0
    }
7266
7267
0
    VSIFree(pTempBuffer);
7268
7269
0
    return true;
7270
0
}
7271
7272
/************************************************************************/
7273
/*                            IsValidForDT()                            */
7274
/************************************************************************/
7275
7276
template <typename Type> static bool IsValidForDT(double dfVal)
7277
0
{
7278
0
    if (std::isnan(dfVal))
7279
0
        return false;
7280
0
    if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
7281
0
        return false;
7282
0
    if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
7283
0
        return false;
7284
0
    return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
7285
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)
7286
7287
template <> bool IsValidForDT<double>(double)
7288
0
{
7289
0
    return true;
7290
0
}
7291
7292
/************************************************************************/
7293
/*                               IsNan()                                */
7294
/************************************************************************/
7295
7296
template <typename Type> inline bool IsNan(Type)
7297
0
{
7298
0
    return false;
7299
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)
7300
7301
template <> bool IsNan<double>(double val)
7302
0
{
7303
0
    return std::isnan(val);
7304
0
}
7305
7306
template <> bool IsNan<float>(float val)
7307
0
{
7308
0
    return std::isnan(val);
7309
0
}
7310
7311
/************************************************************************/
7312
/*                            ReadInternal()                            */
7313
/************************************************************************/
7314
7315
template <typename Type>
7316
void GDALMDArrayMask::ReadInternal(
7317
    const size_t *count, const GPtrDiff_t *bufferStride,
7318
    const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
7319
    const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
7320
    const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
7321
0
{
7322
0
    const size_t nDims = GetDimensionCount();
7323
7324
0
    const auto castValue = [](bool &bHasVal, double dfVal) -> Type
7325
0
    {
7326
0
        if (bHasVal)
7327
0
        {
7328
0
            if (IsValidForDT<Type>(dfVal))
7329
0
            {
7330
0
                return static_cast<Type>(dfVal);
7331
0
            }
7332
0
            else
7333
0
            {
7334
0
                bHasVal = false;
7335
0
            }
7336
0
        }
7337
0
        return 0;
7338
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
7339
7340
0
    const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
7341
0
    bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
7342
0
    const Type nNoDataValue =
7343
0
        castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
7344
0
    bool bHasMissingValue = m_bHasMissingValue;
7345
0
    const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
7346
0
    bool bHasFillValue = m_bHasFillValue;
7347
0
    const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
7348
0
    bool bHasValidMin = m_bHasValidMin;
7349
0
    const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
7350
0
    bool bHasValidMax = m_bHasValidMax;
7351
0
    const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
7352
0
    const bool bHasValidFlags =
7353
0
        !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
7354
7355
0
    const auto IsValidFlag = [this](Type v)
7356
0
    {
7357
0
        if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
7358
0
        {
7359
0
            for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7360
0
            {
7361
0
                if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
7362
0
                    m_anValidFlagValues[i])
7363
0
                {
7364
0
                    return true;
7365
0
                }
7366
0
            }
7367
0
        }
7368
0
        else if (!m_anValidFlagValues.empty())
7369
0
        {
7370
0
            for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
7371
0
            {
7372
0
                if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
7373
0
                {
7374
0
                    return true;
7375
0
                }
7376
0
            }
7377
0
        }
7378
0
        else /* if( !m_anValidFlagMasks.empty() ) */
7379
0
        {
7380
0
            for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
7381
0
            {
7382
0
                if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
7383
0
                {
7384
0
                    return true;
7385
0
                }
7386
0
            }
7387
0
        }
7388
0
        return false;
7389
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
7390
7391
0
#define GET_MASK_FOR_SAMPLE(v)                                                 \
7392
0
    static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
7393
0
                       !(bHasMissingValue && v == nMissingValue) &&            \
7394
0
                       !(bHasFillValue && v == nFillValue) &&                  \
7395
0
                       !(bHasValidMin && v < nValidMin) &&                     \
7396
0
                       !(bHasValidMax && v > nValidMax) &&                     \
7397
0
                       (!bHasValidFlags || IsValidFlag(v)));
7398
7399
0
    const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
7400
    /* Optimized case: Byte output and output buffer is contiguous */
7401
0
    if (bBufferDataTypeIsByte)
7402
0
    {
7403
0
        bool bContiguous = true;
7404
0
        for (size_t i = 0; i < nDims; i++)
7405
0
        {
7406
0
            if (bufferStride[i] != tmpBufferStrideVector[i])
7407
0
            {
7408
0
                bContiguous = false;
7409
0
                break;
7410
0
            }
7411
0
        }
7412
0
        if (bContiguous)
7413
0
        {
7414
0
            size_t nElts = 1;
7415
0
            for (size_t i = 0; i < nDims; i++)
7416
0
                nElts *= count[i];
7417
7418
0
            for (size_t i = 0; i < nElts; i++)
7419
0
            {
7420
0
                const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
7421
0
                static_cast<GByte *>(pDstBuffer)[i] =
7422
0
                    GET_MASK_FOR_SAMPLE(*pSrc);
7423
0
            }
7424
0
            return;
7425
0
        }
7426
0
    }
7427
7428
0
    const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
7429
7430
0
    struct Stack
7431
0
    {
7432
0
        size_t nIters = 0;
7433
0
        const GByte *src_ptr = nullptr;
7434
0
        GByte *dst_ptr = nullptr;
7435
0
        GPtrDiff_t src_inc_offset = 0;
7436
0
        GPtrDiff_t dst_inc_offset = 0;
7437
0
    };
7438
7439
0
    std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
7440
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
7441
0
    for (size_t i = 0; i < nDims; i++)
7442
0
    {
7443
0
        stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
7444
0
            tmpBufferStrideVector[i] * nTmpBufferDTSize);
7445
0
        stack[i].dst_inc_offset =
7446
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
7447
0
    }
7448
0
    stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
7449
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
7450
7451
0
    size_t dimIdx = 0;
7452
0
    const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
7453
0
    GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
7454
0
    CPLAssert(nBufferDTSize <= 16);
7455
0
    for (GByte flag = 0; flag <= 1; flag++)
7456
0
    {
7457
0
        GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
7458
0
                        bufferDataType.GetNumericDataType(), 0, 1);
7459
0
    }
7460
7461
0
lbl_next_depth:
7462
0
    if (dimIdx == nDimsMinus1)
7463
0
    {
7464
0
        auto nIters = nDims > 0 ? count[dimIdx] : 1;
7465
0
        const GByte *src_ptr = stack[dimIdx].src_ptr;
7466
0
        GByte *dst_ptr = stack[dimIdx].dst_ptr;
7467
7468
0
        while (true)
7469
0
        {
7470
0
            const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
7471
0
            const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
7472
7473
0
            if (bBufferDataTypeIsByte)
7474
0
            {
7475
0
                *dst_ptr = flag;
7476
0
            }
7477
0
            else
7478
0
            {
7479
0
                memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
7480
0
            }
7481
7482
0
            if ((--nIters) == 0)
7483
0
                break;
7484
0
            src_ptr += stack[dimIdx].src_inc_offset;
7485
0
            dst_ptr += stack[dimIdx].dst_inc_offset;
7486
0
        }
7487
0
    }
7488
0
    else
7489
0
    {
7490
0
        stack[dimIdx].nIters = count[dimIdx];
7491
0
        while (true)
7492
0
        {
7493
0
            dimIdx++;
7494
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
7495
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
7496
0
            goto lbl_next_depth;
7497
0
        lbl_return_to_caller:
7498
0
            dimIdx--;
7499
0
            if ((--stack[dimIdx].nIters) == 0)
7500
0
                break;
7501
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
7502
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
7503
0
        }
7504
0
    }
7505
0
    if (dimIdx > 0)
7506
0
        goto lbl_return_to_caller;
7507
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
7508
7509
/************************************************************************/
7510
/*                              GetMask()                               */
7511
/************************************************************************/
7512
7513
/** Return an array that is a mask for the current array
7514
7515
 This array will be of type Byte, with values set to 0 to indicate invalid
7516
 pixels of the current array, and values set to 1 to indicate valid pixels.
7517
7518
 The generic implementation honours the NoDataValue, as well as various
7519
 netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
7520
 and valid_range.
7521
7522
 Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
7523
 can be used to specify strings of the "flag_meanings" attribute
7524
 (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
7525
 for which pixels matching any of those flags will be set at 1 in the mask array,
7526
 and pixels matching none of those flags will be set at 0.
7527
 For example, let's consider the following netCDF variable defined with:
7528
 \verbatim
7529
 l2p_flags:valid_min = 0s ;
7530
 l2p_flags:valid_max = 256s ;
7531
 l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
7532
 l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
7533
 \endverbatim
7534
7535
 GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
7536
 - for pixel values *outside* valid_range [0,256], the mask value will be 0.
7537
 - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
7538
   will be 1.
7539
 - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
7540
   will be 0.
7541
7542
 This is the same as the C function GDALMDArrayGetMask().
7543
7544
 @param papszOptions NULL-terminated list of options, or NULL.
7545
7546
 @return a new array, that holds a reference to the original one, and thus is
7547
 a view of it (not a copy), or nullptr in case of error.
7548
*/
7549
std::shared_ptr<GDALMDArray>
7550
GDALMDArray::GetMask(CSLConstList papszOptions) const
7551
0
{
7552
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
7553
0
    if (!self)
7554
0
    {
7555
0
        CPLError(CE_Failure, CPLE_AppDefined,
7556
0
                 "Driver implementation issue: m_pSelf not set !");
7557
0
        return nullptr;
7558
0
    }
7559
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
7560
0
    {
7561
0
        CPLError(CE_Failure, CPLE_AppDefined,
7562
0
                 "GetMask() only supports numeric data type");
7563
0
        return nullptr;
7564
0
    }
7565
0
    return GDALMDArrayMask::Create(self, papszOptions);
7566
0
}
7567
7568
/************************************************************************/
7569
/*                         IsRegularlySpaced()                          */
7570
/************************************************************************/
7571
7572
/** Returns whether an array is a 1D regularly spaced array.
7573
 *
7574
 * @param[out] dfStart     First value in the array
7575
 * @param[out] dfIncrement Increment/spacing between consecutive values.
7576
 * @return true if the array is regularly spaced.
7577
 */
7578
bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
7579
0
{
7580
0
    dfStart = 0;
7581
0
    dfIncrement = 0;
7582
0
    if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
7583
0
        return false;
7584
0
    const auto nSize = GetDimensions()[0]->GetSize();
7585
0
    if (nSize <= 1 || nSize > 10 * 1000 * 1000)
7586
0
        return false;
7587
7588
0
    size_t nCount = static_cast<size_t>(nSize);
7589
0
    std::vector<double> adfTmp;
7590
0
    try
7591
0
    {
7592
0
        adfTmp.resize(nCount);
7593
0
    }
7594
0
    catch (const std::exception &)
7595
0
    {
7596
0
        return false;
7597
0
    }
7598
7599
0
    GUInt64 anStart[1] = {0};
7600
0
    size_t anCount[1] = {nCount};
7601
7602
0
    const auto IsRegularlySpacedInternal =
7603
0
        [&dfStart, &dfIncrement, &anCount, &adfTmp]()
7604
0
    {
7605
0
        dfStart = adfTmp[0];
7606
0
        dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
7607
0
        if (dfIncrement == 0)
7608
0
        {
7609
0
            return false;
7610
0
        }
7611
0
        for (size_t i = 1; i < anCount[0]; i++)
7612
0
        {
7613
0
            if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
7614
0
                1e-3 * fabs(dfIncrement))
7615
0
            {
7616
0
                return false;
7617
0
            }
7618
0
        }
7619
0
        return true;
7620
0
    };
7621
7622
    // First try with the first block(s). This can avoid excessive processing
7623
    // time, for example with Zarr datasets.
7624
    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
7625
    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
7626
0
    const auto nBlockSize = GetBlockSize()[0];
7627
0
    if (nCount >= 5 && nBlockSize <= nCount / 2)
7628
0
    {
7629
0
        size_t nReducedCount =
7630
0
            std::max<size_t>(3, static_cast<size_t>(nBlockSize));
7631
0
        while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
7632
0
            nReducedCount *= 2;
7633
0
        anCount[0] = nReducedCount;
7634
0
        if (!Read(anStart, anCount, nullptr, nullptr,
7635
0
                  GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
7636
0
        {
7637
0
            return false;
7638
0
        }
7639
0
        if (!IsRegularlySpacedInternal())
7640
0
        {
7641
0
            return false;
7642
0
        }
7643
7644
        // Get next values
7645
0
        anStart[0] = nReducedCount;
7646
0
        anCount[0] = nCount - nReducedCount;
7647
0
    }
7648
7649
0
    if (!Read(anStart, anCount, nullptr, nullptr,
7650
0
              GDALExtendedDataType::Create(GDT_Float64),
7651
0
              &adfTmp[static_cast<size_t>(anStart[0])]))
7652
0
    {
7653
0
        return false;
7654
0
    }
7655
7656
0
    return IsRegularlySpacedInternal();
7657
0
}
7658
7659
/************************************************************************/
7660
/*                         GuessGeoTransform()                          */
7661
/************************************************************************/
7662
7663
/** Returns whether 2 specified dimensions form a geotransform
7664
 *
7665
 * @param nDimX                Index of the X axis.
7666
 * @param nDimY                Index of the Y axis.
7667
 * @param bPixelIsPoint        Whether the geotransform should be returned
7668
 *                             with the pixel-is-point (pixel-center) convention
7669
 *                             (bPixelIsPoint = true), or with the pixel-is-area
7670
 *                             (top left corner convention)
7671
 *                             (bPixelIsPoint = false)
7672
 * @param[out] gt              Computed geotransform
7673
 * @return true if a geotransform could be computed.
7674
 */
7675
bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7676
                                    bool bPixelIsPoint,
7677
                                    GDALGeoTransform &gt) const
7678
0
{
7679
0
    const auto &dims(GetDimensions());
7680
0
    auto poVarX = dims[nDimX]->GetIndexingVariable();
7681
0
    auto poVarY = dims[nDimY]->GetIndexingVariable();
7682
0
    double dfXStart = 0.0;
7683
0
    double dfXSpacing = 0.0;
7684
0
    double dfYStart = 0.0;
7685
0
    double dfYSpacing = 0.0;
7686
0
    if (poVarX && poVarX->GetDimensionCount() == 1 &&
7687
0
        poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
7688
0
        poVarY && poVarY->GetDimensionCount() == 1 &&
7689
0
        poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
7690
0
        poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
7691
0
        poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
7692
0
    {
7693
0
        gt.xorig = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
7694
0
        gt.xscale = dfXSpacing;
7695
0
        gt.xrot = 0;
7696
0
        gt.yorig = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
7697
0
        gt.yrot = 0;
7698
0
        gt.yscale = dfYSpacing;
7699
0
        return true;
7700
0
    }
7701
0
    return false;
7702
0
}
7703
7704
/** Returns whether 2 specified dimensions form a geotransform
7705
 *
7706
 * @param nDimX                Index of the X axis.
7707
 * @param nDimY                Index of the Y axis.
7708
 * @param bPixelIsPoint        Whether the geotransform should be returned
7709
 *                             with the pixel-is-point (pixel-center) convention
7710
 *                             (bPixelIsPoint = true), or with the pixel-is-area
7711
 *                             (top left corner convention)
7712
 *                             (bPixelIsPoint = false)
7713
 * @param[out] adfGeoTransform Computed geotransform
7714
 * @return true if a geotransform could be computed.
7715
 */
7716
bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
7717
                                    bool bPixelIsPoint,
7718
                                    double adfGeoTransform[6]) const
7719
0
{
7720
0
    GDALGeoTransform *gt =
7721
0
        reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
7722
0
    return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
7723
0
}
7724
7725
/************************************************************************/
7726
/*                         GDALMDArrayResampled                         */
7727
/************************************************************************/
7728
7729
class GDALMDArrayResampledDataset;
7730
7731
class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
7732
{
7733
  protected:
7734
    CPLErr IReadBlock(int, int, void *) override;
7735
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
7736
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
7737
                     GDALDataType eBufType, GSpacing nPixelSpaceBuf,
7738
                     GSpacing nLineSpaceBuf,
7739
                     GDALRasterIOExtraArg *psExtraArg) override;
7740
7741
  public:
7742
    explicit GDALMDArrayResampledDatasetRasterBand(
7743
        GDALMDArrayResampledDataset *poDSIn);
7744
7745
    double GetNoDataValue(int *pbHasNoData) override;
7746
};
7747
7748
class GDALMDArrayResampledDataset final : public GDALPamDataset
7749
{
7750
    friend class GDALMDArrayResampled;
7751
    friend class GDALMDArrayResampledDatasetRasterBand;
7752
7753
    std::shared_ptr<GDALMDArray> m_poArray;
7754
    const size_t m_iXDim;
7755
    const size_t m_iYDim;
7756
    GDALGeoTransform m_gt{};
7757
    bool m_bHasGT = false;
7758
    mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
7759
7760
    std::vector<GUInt64> m_anOffset{};
7761
    std::vector<size_t> m_anCount{};
7762
    std::vector<GPtrDiff_t> m_anStride{};
7763
7764
    std::string m_osFilenameLong{};
7765
    std::string m_osFilenameLat{};
7766
7767
  public:
7768
    GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
7769
                                size_t iXDim, size_t iYDim)
7770
0
        : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
7771
0
          m_anOffset(m_poArray->GetDimensionCount(), 0),
7772
0
          m_anCount(m_poArray->GetDimensionCount(), 1),
7773
0
          m_anStride(m_poArray->GetDimensionCount(), 0)
7774
0
    {
7775
0
        const auto &dims(m_poArray->GetDimensions());
7776
7777
0
        nRasterYSize = static_cast<int>(
7778
0
            std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
7779
0
        nRasterXSize = static_cast<int>(
7780
0
            std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
7781
7782
0
        m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
7783
7784
0
        SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
7785
0
    }
7786
7787
    ~GDALMDArrayResampledDataset() override;
7788
7789
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
7790
0
    {
7791
0
        gt = m_gt;
7792
0
        return m_bHasGT ? CE_None : CE_Failure;
7793
0
    }
7794
7795
    const OGRSpatialReference *GetSpatialRef() const override
7796
0
    {
7797
0
        m_poSRS = m_poArray->GetSpatialRef();
7798
0
        if (m_poSRS)
7799
0
        {
7800
0
            m_poSRS.reset(m_poSRS->Clone());
7801
0
            auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
7802
0
            for (auto &m : axisMapping)
7803
0
            {
7804
0
                if (m == static_cast<int>(m_iXDim) + 1)
7805
0
                    m = 1;
7806
0
                else if (m == static_cast<int>(m_iYDim) + 1)
7807
0
                    m = 2;
7808
0
            }
7809
0
            m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
7810
0
        }
7811
0
        return m_poSRS.get();
7812
0
    }
7813
7814
    void SetGeolocationArray(const std::string &osFilenameLong,
7815
                             const std::string &osFilenameLat)
7816
0
    {
7817
0
        m_osFilenameLong = osFilenameLong;
7818
0
        m_osFilenameLat = osFilenameLat;
7819
0
        CPLStringList aosGeoLoc;
7820
0
        aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
7821
0
        aosGeoLoc.SetNameValue("LINE_STEP", "1");
7822
0
        aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
7823
0
        aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
7824
0
        aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
7825
0
        aosGeoLoc.SetNameValue("X_BAND", "1");
7826
0
        aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
7827
0
        aosGeoLoc.SetNameValue("Y_BAND", "1");
7828
0
        aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
7829
0
        aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
7830
0
        SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
7831
0
    }
7832
};
7833
7834
GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
7835
0
{
7836
0
    if (!m_osFilenameLong.empty())
7837
0
        VSIUnlink(m_osFilenameLong.c_str());
7838
0
    if (!m_osFilenameLat.empty())
7839
0
        VSIUnlink(m_osFilenameLat.c_str());
7840
0
}
7841
7842
/************************************************************************/
7843
/*               GDALMDArrayResampledDatasetRasterBand()                */
7844
/************************************************************************/
7845
7846
GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
7847
    GDALMDArrayResampledDataset *poDSIn)
7848
0
{
7849
0
    const auto &poArray(poDSIn->m_poArray);
7850
0
    const auto blockSize(poArray->GetBlockSize());
7851
0
    nBlockYSize = (blockSize[poDSIn->m_iYDim])
7852
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7853
0
                                                  blockSize[poDSIn->m_iYDim]))
7854
0
                      : 1;
7855
0
    nBlockXSize = blockSize[poDSIn->m_iXDim]
7856
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
7857
0
                                                  blockSize[poDSIn->m_iXDim]))
7858
0
                      : poDSIn->GetRasterXSize();
7859
0
    eDataType = poArray->GetDataType().GetNumericDataType();
7860
0
    eAccess = poDSIn->eAccess;
7861
0
}
7862
7863
/************************************************************************/
7864
/*                           GetNoDataValue()                           */
7865
/************************************************************************/
7866
7867
double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
7868
0
{
7869
0
    auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7870
0
    const auto &poArray(l_poDS->m_poArray);
7871
0
    bool bHasNodata = false;
7872
0
    double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
7873
0
    if (pbHasNoData)
7874
0
        *pbHasNoData = bHasNodata;
7875
0
    return dfRes;
7876
0
}
7877
7878
/************************************************************************/
7879
/*                             IReadBlock()                             */
7880
/************************************************************************/
7881
7882
CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
7883
                                                         int nBlockYOff,
7884
                                                         void *pImage)
7885
0
{
7886
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
7887
0
    const int nXOff = nBlockXOff * nBlockXSize;
7888
0
    const int nYOff = nBlockYOff * nBlockYSize;
7889
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
7890
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
7891
0
    GDALRasterIOExtraArg sExtraArg;
7892
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
7893
0
    return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
7894
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
7895
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
7896
0
}
7897
7898
/************************************************************************/
7899
/*                             IRasterIO()                              */
7900
/************************************************************************/
7901
7902
CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
7903
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
7904
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
7905
    GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
7906
    GDALRasterIOExtraArg *psExtraArg)
7907
0
{
7908
0
    auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
7909
0
    const auto &poArray(l_poDS->m_poArray);
7910
0
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
7911
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
7912
0
        nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
7913
0
        (nLineSpaceBuf % nBufferDTSize) == 0)
7914
0
    {
7915
0
        l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
7916
0
        l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
7917
0
        l_poDS->m_anStride[l_poDS->m_iXDim] =
7918
0
            static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
7919
7920
0
        l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
7921
0
        l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
7922
0
        l_poDS->m_anStride[l_poDS->m_iYDim] =
7923
0
            static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
7924
7925
0
        return poArray->Read(l_poDS->m_anOffset.data(),
7926
0
                             l_poDS->m_anCount.data(), nullptr,
7927
0
                             l_poDS->m_anStride.data(),
7928
0
                             GDALExtendedDataType::Create(eBufType), pData)
7929
0
                   ? CE_None
7930
0
                   : CE_Failure;
7931
0
    }
7932
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
7933
0
                                     pData, nBufXSize, nBufYSize, eBufType,
7934
0
                                     nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
7935
0
}
7936
7937
class GDALMDArrayResampled final : public GDALPamMDArray
7938
{
7939
  private:
7940
    std::shared_ptr<GDALMDArray> m_poParent{};
7941
    std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
7942
    std::vector<GUInt64> m_anBlockSize;
7943
    GDALExtendedDataType m_dt;
7944
    std::shared_ptr<OGRSpatialReference> m_poSRS{};
7945
    std::shared_ptr<GDALMDArray> m_poVarX{};
7946
    std::shared_ptr<GDALMDArray> m_poVarY{};
7947
    std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
7948
    std::unique_ptr<GDALDataset> m_poReprojectedDS{};
7949
7950
  protected:
7951
    GDALMDArrayResampled(
7952
        const std::shared_ptr<GDALMDArray> &poParent,
7953
        const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
7954
        const std::vector<GUInt64> &anBlockSize)
7955
0
        : GDALAbstractMDArray(std::string(),
7956
0
                              "Resampled view of " + poParent->GetFullName()),
7957
0
          GDALPamMDArray(
7958
0
              std::string(), "Resampled view of " + poParent->GetFullName(),
7959
0
              GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
7960
0
          m_poParent(std::move(poParent)), m_apoDims(apoDims),
7961
0
          m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
7962
0
    {
7963
0
        CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
7964
0
        CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
7965
0
    }
7966
7967
    bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
7968
               const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
7969
               const GDALExtendedDataType &bufferDataType,
7970
               void *pDstBuffer) const override;
7971
7972
  public:
7973
    static std::shared_ptr<GDALMDArray>
7974
    Create(const std::shared_ptr<GDALMDArray> &poParent,
7975
           const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
7976
           GDALRIOResampleAlg resampleAlg,
7977
           const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
7978
7979
    ~GDALMDArrayResampled() override
7980
0
    {
7981
        // First close the warped VRT
7982
0
        m_poReprojectedDS.reset();
7983
0
        m_poParentDS.reset();
7984
0
    }
7985
7986
    bool IsWritable() const override
7987
0
    {
7988
0
        return false;
7989
0
    }
7990
7991
    const std::string &GetFilename() const override
7992
0
    {
7993
0
        return m_poParent->GetFilename();
7994
0
    }
7995
7996
    const std::vector<std::shared_ptr<GDALDimension>> &
7997
    GetDimensions() const override
7998
0
    {
7999
0
        return m_apoDims;
8000
0
    }
8001
8002
    const GDALExtendedDataType &GetDataType() const override
8003
0
    {
8004
0
        return m_dt;
8005
0
    }
8006
8007
    std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
8008
0
    {
8009
0
        return m_poSRS;
8010
0
    }
8011
8012
    std::vector<GUInt64> GetBlockSize() const override
8013
0
    {
8014
0
        return m_anBlockSize;
8015
0
    }
8016
8017
    std::shared_ptr<GDALAttribute>
8018
    GetAttribute(const std::string &osName) const override
8019
0
    {
8020
0
        return m_poParent->GetAttribute(osName);
8021
0
    }
8022
8023
    std::vector<std::shared_ptr<GDALAttribute>>
8024
    GetAttributes(CSLConstList papszOptions = nullptr) const override
8025
0
    {
8026
0
        return m_poParent->GetAttributes(papszOptions);
8027
0
    }
8028
8029
    const std::string &GetUnit() const override
8030
0
    {
8031
0
        return m_poParent->GetUnit();
8032
0
    }
8033
8034
    const void *GetRawNoDataValue() const override
8035
0
    {
8036
0
        return m_poParent->GetRawNoDataValue();
8037
0
    }
8038
8039
    double GetOffset(bool *pbHasOffset,
8040
                     GDALDataType *peStorageType) const override
8041
0
    {
8042
0
        return m_poParent->GetOffset(pbHasOffset, peStorageType);
8043
0
    }
8044
8045
    double GetScale(bool *pbHasScale,
8046
                    GDALDataType *peStorageType) const override
8047
0
    {
8048
0
        return m_poParent->GetScale(pbHasScale, peStorageType);
8049
0
    }
8050
};
8051
8052
/************************************************************************/
8053
/*                    GDALMDArrayResampled::Create()                    */
8054
/************************************************************************/
8055
8056
std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
8057
    const std::shared_ptr<GDALMDArray> &poParent,
8058
    const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
8059
    GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8060
    CSLConstList /* papszOptions */)
8061
0
{
8062
0
    const char *pszResampleAlg = "nearest";
8063
0
    bool unsupported = false;
8064
0
    switch (resampleAlg)
8065
0
    {
8066
0
        case GRIORA_NearestNeighbour:
8067
0
            pszResampleAlg = "nearest";
8068
0
            break;
8069
0
        case GRIORA_Bilinear:
8070
0
            pszResampleAlg = "bilinear";
8071
0
            break;
8072
0
        case GRIORA_Cubic:
8073
0
            pszResampleAlg = "cubic";
8074
0
            break;
8075
0
        case GRIORA_CubicSpline:
8076
0
            pszResampleAlg = "cubicspline";
8077
0
            break;
8078
0
        case GRIORA_Lanczos:
8079
0
            pszResampleAlg = "lanczos";
8080
0
            break;
8081
0
        case GRIORA_Average:
8082
0
            pszResampleAlg = "average";
8083
0
            break;
8084
0
        case GRIORA_Mode:
8085
0
            pszResampleAlg = "mode";
8086
0
            break;
8087
0
        case GRIORA_Gauss:
8088
0
            unsupported = true;
8089
0
            break;
8090
0
        case GRIORA_RESERVED_START:
8091
0
            unsupported = true;
8092
0
            break;
8093
0
        case GRIORA_RESERVED_END:
8094
0
            unsupported = true;
8095
0
            break;
8096
0
        case GRIORA_RMS:
8097
0
            pszResampleAlg = "rms";
8098
0
            break;
8099
0
    }
8100
0
    if (unsupported)
8101
0
    {
8102
0
        CPLError(CE_Failure, CPLE_NotSupported,
8103
0
                 "Unsupported resample method for GetResampled()");
8104
0
        return nullptr;
8105
0
    }
8106
8107
0
    if (poParent->GetDimensionCount() < 2)
8108
0
    {
8109
0
        CPLError(CE_Failure, CPLE_NotSupported,
8110
0
                 "GetResampled() only supports 2 dimensions or more");
8111
0
        return nullptr;
8112
0
    }
8113
8114
0
    const auto &aoParentDims = poParent->GetDimensions();
8115
0
    if (apoNewDimsIn.size() != aoParentDims.size())
8116
0
    {
8117
0
        CPLError(CE_Failure, CPLE_AppDefined,
8118
0
                 "GetResampled(): apoNewDims size should be the same as "
8119
0
                 "GetDimensionCount()");
8120
0
        return nullptr;
8121
0
    }
8122
8123
0
    std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
8124
0
    apoNewDims.reserve(apoNewDimsIn.size());
8125
8126
0
    std::vector<GUInt64> anBlockSize;
8127
0
    anBlockSize.reserve(apoNewDimsIn.size());
8128
0
    const auto &anParentBlockSize = poParent->GetBlockSize();
8129
8130
0
    auto apoParentDims = poParent->GetDimensions();
8131
    // Special case for NASA EMIT datasets
8132
0
    const bool bYXBandOrder = (apoParentDims.size() == 3 &&
8133
0
                               apoParentDims[0]->GetName() == "downtrack" &&
8134
0
                               apoParentDims[1]->GetName() == "crosstrack" &&
8135
0
                               apoParentDims[2]->GetName() == "bands");
8136
8137
0
    const size_t iYDimParent =
8138
0
        bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
8139
0
    const size_t iXDimParent =
8140
0
        bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
8141
8142
0
    for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
8143
0
    {
8144
0
        if (i == iYDimParent || i == iXDimParent)
8145
0
            continue;
8146
0
        if (apoNewDimsIn[i] == nullptr)
8147
0
        {
8148
0
            apoNewDims.emplace_back(aoParentDims[i]);
8149
0
        }
8150
0
        else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
8151
0
                 apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
8152
0
        {
8153
0
            CPLError(CE_Failure, CPLE_AppDefined,
8154
0
                     "GetResampled(): apoNewDims[%u] should be the same "
8155
0
                     "as its parent",
8156
0
                     i);
8157
0
            return nullptr;
8158
0
        }
8159
0
        else
8160
0
        {
8161
0
            apoNewDims.emplace_back(aoParentDims[i]);
8162
0
        }
8163
0
        anBlockSize.emplace_back(anParentBlockSize[i]);
8164
0
    }
8165
8166
0
    std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
8167
0
        new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
8168
8169
0
    double dfXStart = 0.0;
8170
0
    double dfXSpacing = 0.0;
8171
0
    bool gotXSpacing = false;
8172
0
    auto poNewDimX = apoNewDimsIn[iXDimParent];
8173
0
    if (poNewDimX)
8174
0
    {
8175
0
        if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
8176
0
        {
8177
0
            CPLError(CE_Failure, CPLE_NotSupported,
8178
0
                     "Too big size for X dimension");
8179
0
            return nullptr;
8180
0
        }
8181
0
        auto var = poNewDimX->GetIndexingVariable();
8182
0
        if (var)
8183
0
        {
8184
0
            if (var->GetDimensionCount() != 1 ||
8185
0
                var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
8186
0
                !var->IsRegularlySpaced(dfXStart, dfXSpacing))
8187
0
            {
8188
0
                CPLError(CE_Failure, CPLE_NotSupported,
8189
0
                         "New X dimension should be indexed by a regularly "
8190
0
                         "spaced variable");
8191
0
                return nullptr;
8192
0
            }
8193
0
            gotXSpacing = true;
8194
0
        }
8195
0
    }
8196
8197
0
    double dfYStart = 0.0;
8198
0
    double dfYSpacing = 0.0;
8199
0
    auto poNewDimY = apoNewDimsIn[iYDimParent];
8200
0
    bool gotYSpacing = false;
8201
0
    if (poNewDimY)
8202
0
    {
8203
0
        if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
8204
0
        {
8205
0
            CPLError(CE_Failure, CPLE_NotSupported,
8206
0
                     "Too big size for Y dimension");
8207
0
            return nullptr;
8208
0
        }
8209
0
        auto var = poNewDimY->GetIndexingVariable();
8210
0
        if (var)
8211
0
        {
8212
0
            if (var->GetDimensionCount() != 1 ||
8213
0
                var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
8214
0
                !var->IsRegularlySpaced(dfYStart, dfYSpacing))
8215
0
            {
8216
0
                CPLError(CE_Failure, CPLE_NotSupported,
8217
0
                         "New Y dimension should be indexed by a regularly "
8218
0
                         "spaced variable");
8219
0
                return nullptr;
8220
0
            }
8221
0
            gotYSpacing = true;
8222
0
        }
8223
0
    }
8224
8225
    // This limitation could probably be removed
8226
0
    if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
8227
0
    {
8228
0
        CPLError(CE_Failure, CPLE_NotSupported,
8229
0
                 "Either none of new X or Y dimension should have an indexing "
8230
0
                 "variable, or both should both should have one.");
8231
0
        return nullptr;
8232
0
    }
8233
8234
0
    std::string osDstWKT;
8235
0
    if (poTargetSRS)
8236
0
    {
8237
0
        char *pszDstWKT = nullptr;
8238
0
        if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
8239
0
        {
8240
0
            CPLFree(pszDstWKT);
8241
0
            return nullptr;
8242
0
        }
8243
0
        osDstWKT = pszDstWKT;
8244
0
        CPLFree(pszDstWKT);
8245
0
    }
8246
8247
    // Use coordinate variables for geolocation array
8248
0
    const auto apoCoordinateVars = poParent->GetCoordinateVariables();
8249
0
    bool useGeolocationArray = false;
8250
0
    if (apoCoordinateVars.size() >= 2)
8251
0
    {
8252
0
        std::shared_ptr<GDALMDArray> poLongVar;
8253
0
        std::shared_ptr<GDALMDArray> poLatVar;
8254
0
        for (const auto &poCoordVar : apoCoordinateVars)
8255
0
        {
8256
0
            const auto &osName = poCoordVar->GetName();
8257
0
            const auto poAttr = poCoordVar->GetAttribute("standard_name");
8258
0
            std::string osStandardName;
8259
0
            if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
8260
0
                poAttr->GetDimensionCount() == 0)
8261
0
            {
8262
0
                const char *pszStandardName = poAttr->ReadAsString();
8263
0
                if (pszStandardName)
8264
0
                    osStandardName = pszStandardName;
8265
0
            }
8266
0
            if (osName == "lon" || osName == "longitude" ||
8267
0
                osName == "Longitude" || osStandardName == "longitude")
8268
0
            {
8269
0
                poLongVar = poCoordVar;
8270
0
            }
8271
0
            else if (osName == "lat" || osName == "latitude" ||
8272
0
                     osName == "Latitude" || osStandardName == "latitude")
8273
0
            {
8274
0
                poLatVar = poCoordVar;
8275
0
            }
8276
0
        }
8277
0
        if (poLatVar != nullptr && poLongVar != nullptr)
8278
0
        {
8279
0
            const auto longDimCount = poLongVar->GetDimensionCount();
8280
0
            const auto &longDims = poLongVar->GetDimensions();
8281
0
            const auto latDimCount = poLatVar->GetDimensionCount();
8282
0
            const auto &latDims = poLatVar->GetDimensions();
8283
0
            const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
8284
0
            const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
8285
0
            if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
8286
0
                latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
8287
0
            {
8288
                // Geolocation arrays are 1D, and of consistent size with
8289
                // the variable
8290
0
                useGeolocationArray = true;
8291
0
            }
8292
0
            else if ((longDimCount == 2 ||
8293
0
                      (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
8294
0
                     longDims[longDimCount - 2]->GetSize() == yDimSize &&
8295
0
                     longDims[longDimCount - 1]->GetSize() == xDimSize &&
8296
0
                     (latDimCount == 2 ||
8297
0
                      (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
8298
0
                     latDims[latDimCount - 2]->GetSize() == yDimSize &&
8299
0
                     latDims[latDimCount - 1]->GetSize() == xDimSize)
8300
8301
0
            {
8302
                // Geolocation arrays are 2D (or 3D with first dimension of
8303
                // size 1, as found in Sentinel 5P products), and of consistent
8304
                // size with the variable
8305
0
                useGeolocationArray = true;
8306
0
            }
8307
0
            else
8308
0
            {
8309
0
                CPLDebug(
8310
0
                    "GDAL",
8311
0
                    "Longitude and latitude coordinate variables found, "
8312
0
                    "but their characteristics are not compatible of using "
8313
0
                    "them as geolocation arrays");
8314
0
            }
8315
0
            if (useGeolocationArray)
8316
0
            {
8317
0
                CPLDebug("GDAL",
8318
0
                         "Setting geolocation array from variables %s and %s",
8319
0
                         poLongVar->GetName().c_str(),
8320
0
                         poLatVar->GetName().c_str());
8321
0
                const std::string osFilenameLong =
8322
0
                    VSIMemGenerateHiddenFilename("longitude.tif");
8323
0
                const std::string osFilenameLat =
8324
0
                    VSIMemGenerateHiddenFilename("latitude.tif");
8325
0
                std::unique_ptr<GDALDataset> poTmpLongDS(
8326
0
                    longDimCount == 1
8327
0
                        ? poLongVar->AsClassicDataset(0, 0)
8328
0
                        : poLongVar->AsClassicDataset(longDimCount - 1,
8329
0
                                                      longDimCount - 2));
8330
0
                auto hTIFFLongDS = GDALTranslate(
8331
0
                    osFilenameLong.c_str(),
8332
0
                    GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
8333
0
                std::unique_ptr<GDALDataset> poTmpLatDS(
8334
0
                    latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
8335
0
                                     : poLatVar->AsClassicDataset(
8336
0
                                           latDimCount - 1, latDimCount - 2));
8337
0
                auto hTIFFLatDS = GDALTranslate(
8338
0
                    osFilenameLat.c_str(),
8339
0
                    GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
8340
0
                const bool bError =
8341
0
                    (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
8342
0
                GDALClose(hTIFFLongDS);
8343
0
                GDALClose(hTIFFLatDS);
8344
0
                if (bError)
8345
0
                {
8346
0
                    VSIUnlink(osFilenameLong.c_str());
8347
0
                    VSIUnlink(osFilenameLat.c_str());
8348
0
                    return nullptr;
8349
0
                }
8350
8351
0
                poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
8352
0
            }
8353
0
        }
8354
0
        else
8355
0
        {
8356
0
            CPLDebug("GDAL",
8357
0
                     "Coordinate variables available for %s, but "
8358
0
                     "longitude and/or latitude variables were not identified",
8359
0
                     poParent->GetName().c_str());
8360
0
        }
8361
0
    }
8362
8363
    // Build gdalwarp arguments
8364
0
    CPLStringList aosArgv;
8365
8366
0
    aosArgv.AddString("-of");
8367
0
    aosArgv.AddString("VRT");
8368
8369
0
    aosArgv.AddString("-r");
8370
0
    aosArgv.AddString(pszResampleAlg);
8371
8372
0
    if (!osDstWKT.empty())
8373
0
    {
8374
0
        aosArgv.AddString("-t_srs");
8375
0
        aosArgv.AddString(osDstWKT.c_str());
8376
0
    }
8377
8378
0
    if (useGeolocationArray)
8379
0
        aosArgv.AddString("-geoloc");
8380
8381
0
    if (gotXSpacing && gotYSpacing)
8382
0
    {
8383
0
        const double dfXMin = dfXStart - dfXSpacing / 2;
8384
0
        const double dfXMax =
8385
0
            dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
8386
0
        const double dfYMax = dfYStart - dfYSpacing / 2;
8387
0
        const double dfYMin =
8388
0
            dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
8389
0
        aosArgv.AddString("-te");
8390
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
8391
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
8392
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
8393
0
        aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
8394
0
    }
8395
8396
0
    if (poNewDimX && poNewDimY)
8397
0
    {
8398
0
        aosArgv.AddString("-ts");
8399
0
        aosArgv.AddString(
8400
0
            CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8401
0
        aosArgv.AddString(
8402
0
            CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8403
0
    }
8404
0
    else if (poNewDimX)
8405
0
    {
8406
0
        aosArgv.AddString("-ts");
8407
0
        aosArgv.AddString(
8408
0
            CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
8409
0
        aosArgv.AddString("0");
8410
0
    }
8411
0
    else if (poNewDimY)
8412
0
    {
8413
0
        aosArgv.AddString("-ts");
8414
0
        aosArgv.AddString("0");
8415
0
        aosArgv.AddString(
8416
0
            CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
8417
0
    }
8418
8419
    // Create a warped VRT dataset
8420
0
    GDALWarpAppOptions *psOptions =
8421
0
        GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
8422
0
    GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
8423
0
    std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
8424
0
        GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
8425
0
    GDALWarpAppOptionsFree(psOptions);
8426
0
    if (poReprojectedDS == nullptr)
8427
0
        return nullptr;
8428
8429
0
    int nBlockXSize;
8430
0
    int nBlockYSize;
8431
0
    poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
8432
0
    anBlockSize.emplace_back(nBlockYSize);
8433
0
    anBlockSize.emplace_back(nBlockXSize);
8434
8435
0
    GDALGeoTransform gt;
8436
0
    CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
8437
0
    CPLAssert(eErr == CE_None);
8438
0
    CPL_IGNORE_RET_VAL(eErr);
8439
8440
0
    auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
8441
0
        std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
8442
0
        poReprojectedDS->GetRasterYSize());
8443
0
    auto varY = GDALMDArrayRegularlySpaced::Create(
8444
0
        std::string(), poDimY->GetName(), poDimY, gt.yorig + gt.yscale / 2,
8445
0
        gt.yscale, 0);
8446
0
    poDimY->SetIndexingVariable(varY);
8447
8448
0
    auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
8449
0
        std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
8450
0
        poReprojectedDS->GetRasterXSize());
8451
0
    auto varX = GDALMDArrayRegularlySpaced::Create(
8452
0
        std::string(), poDimX->GetName(), poDimX, gt.xorig + gt.xscale / 2,
8453
0
        gt.xscale, 0);
8454
0
    poDimX->SetIndexingVariable(varX);
8455
8456
0
    apoNewDims.emplace_back(poDimY);
8457
0
    apoNewDims.emplace_back(poDimX);
8458
0
    auto newAr(std::shared_ptr<GDALMDArrayResampled>(
8459
0
        new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
8460
0
    newAr->SetSelf(newAr);
8461
0
    if (poTargetSRS)
8462
0
    {
8463
0
        newAr->m_poSRS.reset(poTargetSRS->Clone());
8464
0
    }
8465
0
    else
8466
0
    {
8467
0
        newAr->m_poSRS = poParent->GetSpatialRef();
8468
0
    }
8469
0
    newAr->m_poVarX = varX;
8470
0
    newAr->m_poVarY = varY;
8471
0
    newAr->m_poReprojectedDS = std::move(poReprojectedDS);
8472
0
    newAr->m_poParentDS = std::move(poParentDS);
8473
8474
    // If the input array is y,x,band ordered, the above newAr is
8475
    // actually band,y,x ordered as it is more convenient for
8476
    // GDALMDArrayResampled::IRead() implementation. But transpose that
8477
    // array to the order of the input array
8478
0
    if (bYXBandOrder)
8479
0
        return newAr->Transpose(std::vector<int>{1, 2, 0});
8480
8481
0
    return newAr;
8482
0
}
8483
8484
/************************************************************************/
8485
/*                    GDALMDArrayResampled::IRead()                     */
8486
/************************************************************************/
8487
8488
bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
8489
                                 const size_t *count, const GInt64 *arrayStep,
8490
                                 const GPtrDiff_t *bufferStride,
8491
                                 const GDALExtendedDataType &bufferDataType,
8492
                                 void *pDstBuffer) const
8493
0
{
8494
0
    if (bufferDataType.GetClass() != GEDTC_NUMERIC)
8495
0
        return false;
8496
8497
0
    struct Stack
8498
0
    {
8499
0
        size_t nIters = 0;
8500
0
        GByte *dst_ptr = nullptr;
8501
0
        GPtrDiff_t dst_inc_offset = 0;
8502
0
    };
8503
8504
0
    const auto nDims = GetDimensionCount();
8505
0
    std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
8506
0
    const size_t nBufferDTSize = bufferDataType.GetSize();
8507
0
    for (size_t i = 0; i < nDims; i++)
8508
0
    {
8509
0
        stack[i].dst_inc_offset =
8510
0
            static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
8511
0
    }
8512
0
    stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
8513
8514
0
    size_t dimIdx = 0;
8515
0
    const size_t iDimY = nDims - 2;
8516
0
    const size_t iDimX = nDims - 1;
8517
    // Use an array to avoid a false positive warning from CLang Static
8518
    // Analyzer about flushCaches being never read
8519
0
    bool flushCaches[] = {false};
8520
0
    const bool bYXBandOrder =
8521
0
        m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
8522
8523
0
lbl_next_depth:
8524
0
    if (dimIdx == iDimY)
8525
0
    {
8526
0
        if (flushCaches[0])
8527
0
        {
8528
0
            flushCaches[0] = false;
8529
            // When changing of 2D slice, flush GDAL 2D buffers
8530
0
            m_poParentDS->FlushCache(false);
8531
0
            m_poReprojectedDS->FlushCache(false);
8532
0
        }
8533
8534
0
        if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
8535
0
                                    GF_Read, iDimX, iDimY, arrayStartIdx, count,
8536
0
                                    arrayStep, bufferStride, bufferDataType,
8537
0
                                    stack[dimIdx].dst_ptr))
8538
0
        {
8539
0
            return false;
8540
0
        }
8541
0
    }
8542
0
    else
8543
0
    {
8544
0
        stack[dimIdx].nIters = count[dimIdx];
8545
0
        if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
8546
0
            arrayStartIdx[dimIdx])
8547
0
        {
8548
0
            flushCaches[0] = true;
8549
0
        }
8550
0
        m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
8551
0
            arrayStartIdx[dimIdx];
8552
0
        while (true)
8553
0
        {
8554
0
            dimIdx++;
8555
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
8556
0
            goto lbl_next_depth;
8557
0
        lbl_return_to_caller:
8558
0
            dimIdx--;
8559
0
            if ((--stack[dimIdx].nIters) == 0)
8560
0
                break;
8561
0
            flushCaches[0] = true;
8562
0
            ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
8563
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
8564
0
        }
8565
0
    }
8566
0
    if (dimIdx > 0)
8567
0
        goto lbl_return_to_caller;
8568
8569
0
    return true;
8570
0
}
8571
8572
/************************************************************************/
8573
/*                            GetResampled()                            */
8574
/************************************************************************/
8575
8576
/** Return an array that is a resampled / reprojected view of the current array
8577
 *
8578
 * This is the same as the C function GDALMDArrayGetResampled().
8579
 *
8580
 * Currently this method can only resample along the last 2 dimensions, unless
8581
 * orthorectifying a NASA EMIT dataset.
8582
 *
8583
 * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
8584
 * geometry lookup table (GLT) is used by default for fast orthorectification.
8585
 *
8586
 * Options available are:
8587
 * <ul>
8588
 * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
8589
 * Can be set to NO to use generic reprojection method.
8590
 * </li>
8591
 * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
8592
 * orthorectification to take into account the value of the
8593
 * /sensor_band_parameters/good_wavelengths array to decide if slices of the
8594
 * current array along the band dimension are valid.</li>
8595
 * </ul>
8596
 *
8597
 * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
8598
 *                   apoNewDims[i] can be NULL to let the method automatically
8599
 *                   determine it.
8600
 * @param resampleAlg Resampling algorithm
8601
 * @param poTargetSRS Target SRS, or nullptr
8602
 * @param papszOptions NULL-terminated list of options, or NULL.
8603
 *
8604
 * @return a new array, that holds a reference to the original one, and thus is
8605
 * a view of it (not a copy), or nullptr in case of error.
8606
 *
8607
 * @since 3.4
8608
 */
8609
std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
8610
    const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
8611
    GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
8612
    CSLConstList papszOptions) const
8613
0
{
8614
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
8615
0
    if (!self)
8616
0
    {
8617
0
        CPLError(CE_Failure, CPLE_AppDefined,
8618
0
                 "Driver implementation issue: m_pSelf not set !");
8619
0
        return nullptr;
8620
0
    }
8621
0
    if (GetDataType().GetClass() != GEDTC_NUMERIC)
8622
0
    {
8623
0
        CPLError(CE_Failure, CPLE_AppDefined,
8624
0
                 "GetResampled() only supports numeric data type");
8625
0
        return nullptr;
8626
0
    }
8627
8628
    // Special case for NASA EMIT datasets
8629
0
    auto apoDims = GetDimensions();
8630
0
    if (poTargetSRS == nullptr &&
8631
0
        ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
8632
0
          apoDims[1]->GetName() == "crosstrack" &&
8633
0
          apoDims[2]->GetName() == "bands" &&
8634
0
          (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
8635
0
           apoNewDims ==
8636
0
               std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
8637
0
                                                           apoDims[2]})) ||
8638
0
         (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
8639
0
          apoDims[1]->GetName() == "crosstrack" &&
8640
0
          apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
8641
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions,
8642
0
                                         "EMIT_ORTHORECTIFICATION", "YES")))
8643
0
    {
8644
0
        auto poRootGroup = GetRootGroup();
8645
0
        if (poRootGroup)
8646
0
        {
8647
0
            auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
8648
0
            auto poLocationGroup = poRootGroup->OpenGroup("location");
8649
0
            if (poAttrGeotransform &&
8650
0
                poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
8651
0
                poAttrGeotransform->GetDimensionCount() == 1 &&
8652
0
                poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
8653
0
                poLocationGroup)
8654
0
            {
8655
0
                auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
8656
0
                auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
8657
0
                if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
8658
0
                    poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
8659
0
                    poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
8660
0
                    poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
8661
0
                    poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
8662
0
                    poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
8663
0
                {
8664
0
                    return CreateGLTOrthorectified(
8665
0
                        self, poRootGroup, poGLT_X, poGLT_Y,
8666
0
                        /* nGLTIndexOffset = */ -1,
8667
0
                        poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
8668
0
                }
8669
0
            }
8670
0
        }
8671
0
    }
8672
8673
0
    if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
8674
0
                                         "EMIT_ORTHORECTIFICATION", "NO")))
8675
0
    {
8676
0
        CPLError(CE_Failure, CPLE_AppDefined,
8677
0
                 "EMIT_ORTHORECTIFICATION required, but dataset and/or "
8678
0
                 "parameters are not compatible with it");
8679
0
        return nullptr;
8680
0
    }
8681
8682
0
    return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
8683
0
                                        poTargetSRS, papszOptions);
8684
0
}
8685
8686
/************************************************************************/
8687
/*                        GDALDatasetFromArray()                        */
8688
/************************************************************************/
8689
8690
class GDALDatasetFromArray;
8691
8692
namespace
8693
{
8694
struct MetadataItem
8695
{
8696
    std::shared_ptr<GDALAbstractMDArray> poArray{};
8697
    std::string osName{};
8698
    std::string osDefinition{};
8699
    bool bDefinitionUsesPctForG = false;
8700
};
8701
8702
struct BandImageryMetadata
8703
{
8704
    std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
8705
    double dfCentralWavelengthToMicrometer = 1.0;
8706
    std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
8707
    double dfFWHMToMicrometer = 1.0;
8708
};
8709
8710
}  // namespace
8711
8712
class GDALRasterBandFromArray final : public GDALPamRasterBand
8713
{
8714
    std::vector<GUInt64> m_anOffset{};
8715
    std::vector<size_t> m_anCount{};
8716
    std::vector<GPtrDiff_t> m_anStride{};
8717
8718
  protected:
8719
    CPLErr IReadBlock(int, int, void *) override;
8720
    CPLErr IWriteBlock(int, int, void *) override;
8721
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
8722
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
8723
                     GDALDataType eBufType, GSpacing nPixelSpaceBuf,
8724
                     GSpacing nLineSpaceBuf,
8725
                     GDALRasterIOExtraArg *psExtraArg) override;
8726
8727
  public:
8728
    explicit GDALRasterBandFromArray(
8729
        GDALDatasetFromArray *poDSIn,
8730
        const std::vector<GUInt64> &anOtherDimCoord,
8731
        const std::vector<std::vector<MetadataItem>>
8732
            &aoBandParameterMetadataItems,
8733
        const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8734
        double dfDelay, time_t nStartTime, bool &bHasWarned);
8735
8736
    double GetNoDataValue(int *pbHasNoData) override;
8737
    int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
8738
    uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
8739
    double GetOffset(int *pbHasOffset) override;
8740
    double GetScale(int *pbHasScale) override;
8741
    const char *GetUnitType() override;
8742
    GDALColorInterp GetColorInterpretation() override;
8743
    int GetOverviewCount() override;
8744
    GDALRasterBand *GetOverview(int idx) override;
8745
};
8746
8747
class GDALDatasetFromArray final : public GDALPamDataset
8748
{
8749
    friend class GDALRasterBandFromArray;
8750
8751
    std::shared_ptr<GDALMDArray> m_poArray;
8752
    const size_t m_iXDim;
8753
    const size_t m_iYDim;
8754
    const CPLStringList m_aosOptions;
8755
    GDALGeoTransform m_gt{};
8756
    bool m_bHasGT = false;
8757
    mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
8758
    GDALMultiDomainMetadata m_oMDD{};
8759
    std::string m_osOvrFilename{};
8760
    bool m_bOverviewsDiscovered = false;
8761
    std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
8762
        m_apoOverviews{};
8763
8764
  public:
8765
    GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
8766
                         size_t iXDim, size_t iYDim,
8767
                         const CPLStringList &aosOptions)
8768
0
        : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
8769
0
          m_aosOptions(aosOptions)
8770
0
    {
8771
        // Initialize an overview filename from the filename of the array
8772
        // and its name.
8773
0
        const std::string &osFilename = m_poArray->GetFilename();
8774
0
        if (!osFilename.empty())
8775
0
        {
8776
0
            m_osOvrFilename = osFilename;
8777
0
            m_osOvrFilename += '.';
8778
0
            for (char ch : m_poArray->GetName())
8779
0
            {
8780
0
                if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
8781
0
                    (ch >= 'a' && ch <= 'z') || ch == '_')
8782
0
                {
8783
0
                    m_osOvrFilename += ch;
8784
0
                }
8785
0
                else
8786
0
                {
8787
0
                    m_osOvrFilename += '_';
8788
0
                }
8789
0
            }
8790
0
            m_osOvrFilename += ".ovr";
8791
0
            oOvManager.Initialize(this);
8792
0
        }
8793
0
    }
8794
8795
    static std::unique_ptr<GDALDatasetFromArray>
8796
    Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
8797
           size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
8798
           CSLConstList papszOptions);
8799
8800
    ~GDALDatasetFromArray() override;
8801
8802
    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override
8803
0
    {
8804
0
        CPLErr eErr = CE_None;
8805
0
        if (nOpenFlags != OPEN_FLAGS_CLOSED)
8806
0
        {
8807
0
            if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
8808
0
                CE_None)
8809
0
                eErr = CE_Failure;
8810
0
            m_poArray.reset();
8811
0
        }
8812
0
        return eErr;
8813
0
    }
8814
8815
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
8816
0
    {
8817
0
        gt = m_gt;
8818
0
        return m_bHasGT ? CE_None : CE_Failure;
8819
0
    }
8820
8821
    const OGRSpatialReference *GetSpatialRef() const override
8822
0
    {
8823
0
        if (m_poArray->GetDimensionCount() < 2)
8824
0
            return nullptr;
8825
0
        m_poSRS = m_poArray->GetSpatialRef();
8826
0
        if (m_poSRS)
8827
0
        {
8828
0
            m_poSRS.reset(m_poSRS->Clone());
8829
0
            auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
8830
0
            for (auto &m : axisMapping)
8831
0
            {
8832
0
                if (m == static_cast<int>(m_iXDim) + 1)
8833
0
                    m = 1;
8834
0
                else if (m == static_cast<int>(m_iYDim) + 1)
8835
0
                    m = 2;
8836
0
            }
8837
0
            m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
8838
0
        }
8839
0
        return m_poSRS.get();
8840
0
    }
8841
8842
    CPLErr SetMetadata(CSLConstList papszMetadata,
8843
                       const char *pszDomain) override
8844
0
    {
8845
0
        return m_oMDD.SetMetadata(papszMetadata, pszDomain);
8846
0
    }
8847
8848
    CSLConstList GetMetadata(const char *pszDomain) override
8849
0
    {
8850
0
        return m_oMDD.GetMetadata(pszDomain);
8851
0
    }
8852
8853
    const char *GetMetadataItem(const char *pszName,
8854
                                const char *pszDomain) override
8855
0
    {
8856
0
        if (!m_osOvrFilename.empty() && pszName &&
8857
0
            EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
8858
0
            EQUAL(pszDomain, "OVERVIEWS"))
8859
0
        {
8860
0
            return m_osOvrFilename.c_str();
8861
0
        }
8862
0
        return m_oMDD.GetMetadataItem(pszName, pszDomain);
8863
0
    }
8864
8865
    void DiscoverOverviews()
8866
0
    {
8867
0
        if (!m_bOverviewsDiscovered)
8868
0
        {
8869
0
            m_bOverviewsDiscovered = true;
8870
0
            if (const int nOverviews = m_poArray->GetOverviewCount())
8871
0
            {
8872
0
                if (auto poRootGroup = m_poArray->GetRootGroup())
8873
0
                {
8874
0
                    const size_t nDims = m_poArray->GetDimensionCount();
8875
0
                    CPLStringList aosOptions(m_aosOptions);
8876
0
                    aosOptions.SetNameValue("LOAD_PAM", "NO");
8877
0
                    for (int iOvr = 0; iOvr < nOverviews; ++iOvr)
8878
0
                    {
8879
0
                        if (auto poOvrArray = m_poArray->GetOverview(iOvr))
8880
0
                        {
8881
0
                            if (poOvrArray->GetDimensionCount() == nDims &&
8882
0
                                poOvrArray->GetDataType() ==
8883
0
                                    m_poArray->GetDataType())
8884
0
                            {
8885
0
                                auto poOvrDS =
8886
0
                                    Create(poOvrArray, m_iXDim, m_iYDim,
8887
0
                                           poRootGroup, aosOptions);
8888
0
                                if (poOvrDS)
8889
0
                                {
8890
0
                                    m_apoOverviews.push_back(
8891
0
                                        std::unique_ptr<
8892
0
                                            GDALDataset,
8893
0
                                            GDALDatasetUniquePtrReleaser>(
8894
0
                                            poOvrDS.release()));
8895
0
                                }
8896
0
                            }
8897
0
                        }
8898
0
                    }
8899
0
                }
8900
0
            }
8901
0
        }
8902
0
    }
8903
};
8904
8905
GDALDatasetFromArray::~GDALDatasetFromArray()
8906
0
{
8907
0
    GDALDatasetFromArray::Close();
8908
0
}
8909
8910
/************************************************************************/
8911
/*                      GDALRasterBandFromArray()                       */
8912
/************************************************************************/
8913
8914
GDALRasterBandFromArray::GDALRasterBandFromArray(
8915
    GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
8916
    const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
8917
    const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
8918
    double dfDelay, time_t nStartTime, bool &bHasWarned)
8919
0
{
8920
0
    const auto &poArray(poDSIn->m_poArray);
8921
0
    const auto &dims(poArray->GetDimensions());
8922
0
    const auto nDimCount(dims.size());
8923
0
    const auto blockSize(poArray->GetBlockSize());
8924
0
    nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
8925
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8926
0
                                                  blockSize[poDSIn->m_iYDim]))
8927
0
                      : 1;
8928
0
    nBlockXSize = blockSize[poDSIn->m_iXDim]
8929
0
                      ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
8930
0
                                                  blockSize[poDSIn->m_iXDim]))
8931
0
                      : poDSIn->GetRasterXSize();
8932
0
    eDataType = poArray->GetDataType().GetNumericDataType();
8933
0
    eAccess = poDSIn->eAccess;
8934
0
    m_anOffset.resize(nDimCount);
8935
0
    m_anCount.resize(nDimCount, 1);
8936
0
    m_anStride.resize(nDimCount);
8937
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
8938
0
    {
8939
0
        if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
8940
0
        {
8941
0
            std::string dimName(dims[i]->GetName());
8942
0
            GUInt64 nIndex = anOtherDimCoord[j];
8943
            // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
8944
            // subsetted dimensions as generated by GetView()
8945
0
            if (STARTS_WITH(dimName.c_str(), "subset_"))
8946
0
            {
8947
0
                CPLStringList aosTokens(
8948
0
                    CSLTokenizeString2(dimName.c_str(), "_", 0));
8949
0
                if (aosTokens.size() == 5)
8950
0
                {
8951
0
                    dimName = aosTokens[1];
8952
0
                    const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
8953
0
                        aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
8954
0
                    const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
8955
0
                    nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
8956
0
                                          : nStartDim - (nIndex * -nIncrDim);
8957
0
                }
8958
0
            }
8959
0
            if (nDimCount != 3 || dimName != "Band")
8960
0
            {
8961
0
                SetMetadataItem(
8962
0
                    CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
8963
0
                    CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
8964
0
            }
8965
8966
0
            auto indexingVar = dims[i]->GetIndexingVariable();
8967
8968
            // If the indexing variable is also listed in band parameter arrays,
8969
            // then don't use our default formatting
8970
0
            if (indexingVar)
8971
0
            {
8972
0
                for (const auto &oItem : aoBandParameterMetadataItems[j])
8973
0
                {
8974
0
                    if (oItem.poArray->GetFullName() ==
8975
0
                        indexingVar->GetFullName())
8976
0
                    {
8977
0
                        indexingVar.reset();
8978
0
                        break;
8979
0
                    }
8980
0
                }
8981
0
            }
8982
8983
0
            if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
8984
0
                indexingVar->GetDimensions()[0]->GetSize() ==
8985
0
                    dims[i]->GetSize())
8986
0
            {
8987
0
                if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
8988
0
                {
8989
0
                    if (!bHasWarned)
8990
0
                    {
8991
0
                        CPLError(
8992
0
                            CE_Warning, CPLE_AppDefined,
8993
0
                            "Maximum delay to load band metadata from "
8994
0
                            "dimension indexing variables has expired. "
8995
0
                            "Increase the value of the "
8996
0
                            "LOAD_EXTRA_DIM_METADATA_DELAY "
8997
0
                            "option of GDALMDArray::AsClassicDataset() "
8998
0
                            "(also accessible as the "
8999
0
                            "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
9000
0
                            "configuration option), "
9001
0
                            "or set it to 'unlimited' for unlimited delay. ");
9002
0
                        bHasWarned = true;
9003
0
                    }
9004
0
                }
9005
0
                else
9006
0
                {
9007
0
                    size_t nCount = 1;
9008
0
                    const auto &dt(indexingVar->GetDataType());
9009
0
                    std::vector<GByte> abyTmp(dt.GetSize());
9010
0
                    if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
9011
0
                                          nullptr, nullptr, dt, &abyTmp[0]))
9012
0
                    {
9013
0
                        char *pszTmp = nullptr;
9014
0
                        GDALExtendedDataType::CopyValue(
9015
0
                            &abyTmp[0], dt, &pszTmp,
9016
0
                            GDALExtendedDataType::CreateString());
9017
0
                        dt.FreeDynamicMemory(abyTmp.data());
9018
0
                        if (pszTmp)
9019
0
                        {
9020
0
                            SetMetadataItem(
9021
0
                                CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
9022
0
                                pszTmp);
9023
0
                            CPLFree(pszTmp);
9024
0
                        }
9025
9026
0
                        const auto &unit(indexingVar->GetUnit());
9027
0
                        if (!unit.empty())
9028
0
                        {
9029
0
                            SetMetadataItem(
9030
0
                                CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
9031
0
                                unit.c_str());
9032
0
                        }
9033
0
                    }
9034
0
                }
9035
0
            }
9036
9037
0
            for (const auto &oItem : aoBandParameterMetadataItems[j])
9038
0
            {
9039
0
                CPLString osVal;
9040
9041
0
                size_t nCount = 1;
9042
0
                const auto &dt(oItem.poArray->GetDataType());
9043
0
                if (oItem.bDefinitionUsesPctForG)
9044
0
                {
9045
                    // There is one and only one %[x][.y]f|g in osDefinition
9046
0
                    std::vector<GByte> abyTmp(dt.GetSize());
9047
0
                    if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9048
0
                                            nullptr, nullptr, dt, &abyTmp[0]))
9049
0
                    {
9050
0
                        double dfVal = 0;
9051
0
                        GDALExtendedDataType::CopyValue(
9052
0
                            &abyTmp[0], dt, &dfVal,
9053
0
                            GDALExtendedDataType::Create(GDT_Float64));
9054
0
                        osVal.Printf(oItem.osDefinition.c_str(), dfVal);
9055
0
                        dt.FreeDynamicMemory(abyTmp.data());
9056
0
                    }
9057
0
                }
9058
0
                else
9059
0
                {
9060
                    // There should be zero or one %s in osDefinition
9061
0
                    char *pszValue = nullptr;
9062
0
                    if (dt.GetClass() == GEDTC_STRING)
9063
0
                    {
9064
0
                        CPL_IGNORE_RET_VAL(oItem.poArray->Read(
9065
0
                            &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
9066
0
                            dt, &pszValue));
9067
0
                    }
9068
0
                    else
9069
0
                    {
9070
0
                        std::vector<GByte> abyTmp(dt.GetSize());
9071
0
                        if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
9072
0
                                                nullptr, nullptr, dt,
9073
0
                                                &abyTmp[0]))
9074
0
                        {
9075
0
                            GDALExtendedDataType::CopyValue(
9076
0
                                &abyTmp[0], dt, &pszValue,
9077
0
                                GDALExtendedDataType::CreateString());
9078
0
                        }
9079
0
                    }
9080
9081
0
                    if (pszValue)
9082
0
                    {
9083
0
                        osVal.Printf(oItem.osDefinition.c_str(), pszValue);
9084
0
                        CPLFree(pszValue);
9085
0
                    }
9086
0
                }
9087
0
                if (!osVal.empty())
9088
0
                    SetMetadataItem(oItem.osName.c_str(), osVal);
9089
0
            }
9090
9091
0
            if (aoBandImageryMetadata[j].poCentralWavelengthArray)
9092
0
            {
9093
0
                auto &poCentralWavelengthArray =
9094
0
                    aoBandImageryMetadata[j].poCentralWavelengthArray;
9095
0
                size_t nCount = 1;
9096
0
                const auto &dt(poCentralWavelengthArray->GetDataType());
9097
0
                std::vector<GByte> abyTmp(dt.GetSize());
9098
0
                if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
9099
0
                                                   &nCount, nullptr, nullptr,
9100
0
                                                   dt, &abyTmp[0]))
9101
0
                {
9102
0
                    double dfVal = 0;
9103
0
                    GDALExtendedDataType::CopyValue(
9104
0
                        &abyTmp[0], dt, &dfVal,
9105
0
                        GDALExtendedDataType::Create(GDT_Float64));
9106
0
                    dt.FreeDynamicMemory(abyTmp.data());
9107
0
                    SetMetadataItem(
9108
0
                        "CENTRAL_WAVELENGTH_UM",
9109
0
                        CPLSPrintf(
9110
0
                            "%g", dfVal * aoBandImageryMetadata[j]
9111
0
                                              .dfCentralWavelengthToMicrometer),
9112
0
                        "IMAGERY");
9113
0
                }
9114
0
            }
9115
9116
0
            if (aoBandImageryMetadata[j].poFWHMArray)
9117
0
            {
9118
0
                auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
9119
0
                size_t nCount = 1;
9120
0
                const auto &dt(poFWHMArray->GetDataType());
9121
0
                std::vector<GByte> abyTmp(dt.GetSize());
9122
0
                if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
9123
0
                                      nullptr, dt, &abyTmp[0]))
9124
0
                {
9125
0
                    double dfVal = 0;
9126
0
                    GDALExtendedDataType::CopyValue(
9127
0
                        &abyTmp[0], dt, &dfVal,
9128
0
                        GDALExtendedDataType::Create(GDT_Float64));
9129
0
                    dt.FreeDynamicMemory(abyTmp.data());
9130
0
                    SetMetadataItem(
9131
0
                        "FWHM_UM",
9132
0
                        CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
9133
0
                                                     .dfFWHMToMicrometer),
9134
0
                        "IMAGERY");
9135
0
                }
9136
0
            }
9137
9138
0
            m_anOffset[i] = anOtherDimCoord[j];
9139
0
            j++;
9140
0
        }
9141
0
    }
9142
0
}
9143
9144
/************************************************************************/
9145
/*                           GetNoDataValue()                           */
9146
/************************************************************************/
9147
9148
double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
9149
0
{
9150
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9151
0
    const auto &poArray(l_poDS->m_poArray);
9152
0
    bool bHasNodata = false;
9153
0
    const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
9154
0
    if (pbHasNoData)
9155
0
        *pbHasNoData = bHasNodata;
9156
0
    return res;
9157
0
}
9158
9159
/************************************************************************/
9160
/*                       GetNoDataValueAsInt64()                        */
9161
/************************************************************************/
9162
9163
int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
9164
0
{
9165
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9166
0
    const auto &poArray(l_poDS->m_poArray);
9167
0
    bool bHasNodata = false;
9168
0
    const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
9169
0
    if (pbHasNoData)
9170
0
        *pbHasNoData = bHasNodata;
9171
0
    return nodata;
9172
0
}
9173
9174
/************************************************************************/
9175
/*                       GetNoDataValueAsUInt64()                       */
9176
/************************************************************************/
9177
9178
uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
9179
0
{
9180
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9181
0
    const auto &poArray(l_poDS->m_poArray);
9182
0
    bool bHasNodata = false;
9183
0
    const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
9184
0
    if (pbHasNoData)
9185
0
        *pbHasNoData = bHasNodata;
9186
0
    return nodata;
9187
0
}
9188
9189
/************************************************************************/
9190
/*                             GetOffset()                              */
9191
/************************************************************************/
9192
9193
double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
9194
0
{
9195
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9196
0
    const auto &poArray(l_poDS->m_poArray);
9197
0
    bool bHasValue = false;
9198
0
    double dfRes = poArray->GetOffset(&bHasValue);
9199
0
    if (pbHasOffset)
9200
0
        *pbHasOffset = bHasValue;
9201
0
    return dfRes;
9202
0
}
9203
9204
/************************************************************************/
9205
/*                            GetUnitType()                             */
9206
/************************************************************************/
9207
9208
const char *GDALRasterBandFromArray::GetUnitType()
9209
0
{
9210
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9211
0
    const auto &poArray(l_poDS->m_poArray);
9212
0
    return poArray->GetUnit().c_str();
9213
0
}
9214
9215
/************************************************************************/
9216
/*                              GetScale()                              */
9217
/************************************************************************/
9218
9219
double GDALRasterBandFromArray::GetScale(int *pbHasScale)
9220
0
{
9221
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9222
0
    const auto &poArray(l_poDS->m_poArray);
9223
0
    bool bHasValue = false;
9224
0
    double dfRes = poArray->GetScale(&bHasValue);
9225
0
    if (pbHasScale)
9226
0
        *pbHasScale = bHasValue;
9227
0
    return dfRes;
9228
0
}
9229
9230
/************************************************************************/
9231
/*                             IReadBlock()                             */
9232
/************************************************************************/
9233
9234
CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
9235
                                           void *pImage)
9236
0
{
9237
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9238
0
    const int nXOff = nBlockXOff * nBlockXSize;
9239
0
    const int nYOff = nBlockYOff * nBlockYSize;
9240
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9241
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9242
0
    GDALRasterIOExtraArg sExtraArg;
9243
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9244
0
    return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9245
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
9246
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9247
0
}
9248
9249
/************************************************************************/
9250
/*                            IWriteBlock()                             */
9251
/************************************************************************/
9252
9253
CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
9254
                                            void *pImage)
9255
0
{
9256
0
    const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
9257
0
    const int nXOff = nBlockXOff * nBlockXSize;
9258
0
    const int nYOff = nBlockYOff * nBlockYSize;
9259
0
    const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
9260
0
    const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
9261
0
    GDALRasterIOExtraArg sExtraArg;
9262
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
9263
0
    return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
9264
0
                     nReqXSize, nReqYSize, eDataType, nDTSize,
9265
0
                     static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
9266
0
}
9267
9268
/************************************************************************/
9269
/*                             IRasterIO()                              */
9270
/************************************************************************/
9271
9272
CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
9273
                                          int nYOff, int nXSize, int nYSize,
9274
                                          void *pData, int nBufXSize,
9275
                                          int nBufYSize, GDALDataType eBufType,
9276
                                          GSpacing nPixelSpaceBuf,
9277
                                          GSpacing nLineSpaceBuf,
9278
                                          GDALRasterIOExtraArg *psExtraArg)
9279
0
{
9280
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9281
0
    const auto &poArray(l_poDS->m_poArray);
9282
0
    const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
9283
0
    if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
9284
0
        (nPixelSpaceBuf % nBufferDTSize) == 0 &&
9285
0
        (nLineSpaceBuf % nBufferDTSize) == 0)
9286
0
    {
9287
0
        m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
9288
0
        m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
9289
0
        m_anStride[l_poDS->m_iXDim] =
9290
0
            static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
9291
0
        if (poArray->GetDimensionCount() >= 2)
9292
0
        {
9293
0
            m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
9294
0
            m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
9295
0
            m_anStride[l_poDS->m_iYDim] =
9296
0
                static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
9297
0
        }
9298
0
        if (eRWFlag == GF_Read)
9299
0
        {
9300
0
            return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
9301
0
                                 m_anStride.data(),
9302
0
                                 GDALExtendedDataType::Create(eBufType), pData)
9303
0
                       ? CE_None
9304
0
                       : CE_Failure;
9305
0
        }
9306
0
        else
9307
0
        {
9308
0
            return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
9309
0
                                  m_anStride.data(),
9310
0
                                  GDALExtendedDataType::Create(eBufType), pData)
9311
0
                       ? CE_None
9312
0
                       : CE_Failure;
9313
0
        }
9314
0
    }
9315
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
9316
0
                                     pData, nBufXSize, nBufYSize, eBufType,
9317
0
                                     nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
9318
0
}
9319
9320
/************************************************************************/
9321
/*                       GetColorInterpretation()                       */
9322
/************************************************************************/
9323
9324
GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
9325
0
{
9326
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9327
0
    const auto &poArray(l_poDS->m_poArray);
9328
0
    auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
9329
0
    if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
9330
0
    {
9331
0
        bool bOK = false;
9332
0
        GUInt64 nStartIndex = 0;
9333
0
        if (poArray->GetDimensionCount() == 2 &&
9334
0
            poAttr->GetDimensionCount() == 0)
9335
0
        {
9336
0
            bOK = true;
9337
0
        }
9338
0
        else if (poArray->GetDimensionCount() == 3)
9339
0
        {
9340
0
            uint64_t nExtraDimSamples = 1;
9341
0
            const auto &apoDims = poArray->GetDimensions();
9342
0
            for (size_t i = 0; i < apoDims.size(); ++i)
9343
0
            {
9344
0
                if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
9345
0
                    nExtraDimSamples *= apoDims[i]->GetSize();
9346
0
            }
9347
0
            if (poAttr->GetDimensionsSize() ==
9348
0
                std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
9349
0
            {
9350
0
                bOK = true;
9351
0
            }
9352
0
            nStartIndex = nBand - 1;
9353
0
        }
9354
0
        if (bOK)
9355
0
        {
9356
0
            const auto oStringDT = GDALExtendedDataType::CreateString();
9357
0
            const size_t nCount = 1;
9358
0
            const GInt64 arrayStep = 1;
9359
0
            const GPtrDiff_t bufferStride = 1;
9360
0
            char *pszValue = nullptr;
9361
0
            poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
9362
0
                         oStringDT, &pszValue);
9363
0
            if (pszValue)
9364
0
            {
9365
0
                const auto eColorInterp =
9366
0
                    GDALGetColorInterpretationByName(pszValue);
9367
0
                CPLFree(pszValue);
9368
0
                return eColorInterp;
9369
0
            }
9370
0
        }
9371
0
    }
9372
0
    return GCI_Undefined;
9373
0
}
9374
9375
/************************************************************************/
9376
/*             GDALRasterBandFromArray::GetOverviewCount()              */
9377
/************************************************************************/
9378
9379
int GDALRasterBandFromArray::GetOverviewCount()
9380
0
{
9381
0
    const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9382
0
    if (nPAMCount)
9383
0
        return nPAMCount;
9384
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9385
0
    l_poDS->DiscoverOverviews();
9386
0
    return static_cast<int>(l_poDS->m_apoOverviews.size());
9387
0
}
9388
9389
/************************************************************************/
9390
/*                GDALRasterBandFromArray::GetOverview()                */
9391
/************************************************************************/
9392
9393
GDALRasterBand *GDALRasterBandFromArray::GetOverview(int idx)
9394
0
{
9395
0
    const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
9396
0
    if (nPAMCount)
9397
0
        return GDALPamRasterBand::GetOverview(idx);
9398
0
    auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
9399
0
    l_poDS->DiscoverOverviews();
9400
0
    if (idx < 0 || static_cast<size_t>(idx) >= l_poDS->m_apoOverviews.size())
9401
0
    {
9402
0
        return nullptr;
9403
0
    }
9404
0
    return l_poDS->m_apoOverviews[idx]->GetRasterBand(nBand);
9405
0
}
9406
9407
/************************************************************************/
9408
/*                    GDALDatasetFromArray::Create()                    */
9409
/************************************************************************/
9410
9411
std::unique_ptr<GDALDatasetFromArray> GDALDatasetFromArray::Create(
9412
    const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
9413
    const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
9414
9415
0
{
9416
0
    const auto nDimCount(array->GetDimensionCount());
9417
0
    if (nDimCount == 0)
9418
0
    {
9419
0
        CPLError(CE_Failure, CPLE_NotSupported,
9420
0
                 "Unsupported number of dimensions");
9421
0
        return nullptr;
9422
0
    }
9423
0
    if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
9424
0
        array->GetDataType().GetNumericDataType() == GDT_Unknown)
9425
0
    {
9426
0
        CPLError(CE_Failure, CPLE_NotSupported,
9427
0
                 "Only arrays with numeric data types "
9428
0
                 "can be exposed as classic GDALDataset");
9429
0
        return nullptr;
9430
0
    }
9431
0
    if (iXDim >= nDimCount || iYDim >= nDimCount ||
9432
0
        (nDimCount >= 2 && iXDim == iYDim))
9433
0
    {
9434
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
9435
0
        return nullptr;
9436
0
    }
9437
0
    GUInt64 nTotalBands = 1;
9438
0
    const auto &dims(array->GetDimensions());
9439
0
    for (size_t i = 0; i < nDimCount; ++i)
9440
0
    {
9441
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9442
0
        {
9443
0
            if (dims[i]->GetSize() > 65536 / nTotalBands)
9444
0
            {
9445
0
                CPLError(CE_Failure, CPLE_AppDefined,
9446
0
                         "Too many bands. Operate on a sliced view");
9447
0
                return nullptr;
9448
0
            }
9449
0
            nTotalBands *= dims[i]->GetSize();
9450
0
        }
9451
0
    }
9452
9453
0
    std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
9454
0
    std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
9455
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
9456
0
    {
9457
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
9458
0
        {
9459
0
            oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
9460
0
            oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
9461
0
            ++j;
9462
0
        }
9463
0
    }
9464
9465
0
    const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
9466
9467
0
    const char *pszBandMetadata =
9468
0
        CSLFetchNameValue(papszOptions, "BAND_METADATA");
9469
0
    std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
9470
0
        nNewDimCount);
9471
0
    if (pszBandMetadata)
9472
0
    {
9473
0
        if (!poRootGroup)
9474
0
        {
9475
0
            CPLError(CE_Failure, CPLE_AppDefined,
9476
0
                     "Root group should be provided when BAND_METADATA is set");
9477
0
            return nullptr;
9478
0
        }
9479
0
        CPLJSONDocument oDoc;
9480
0
        if (!oDoc.LoadMemory(pszBandMetadata))
9481
0
        {
9482
0
            CPLError(CE_Failure, CPLE_AppDefined,
9483
0
                     "Invalid JSON content for BAND_METADATA");
9484
0
            return nullptr;
9485
0
        }
9486
0
        auto oRoot = oDoc.GetRoot();
9487
0
        if (oRoot.GetType() != CPLJSONObject::Type::Array)
9488
0
        {
9489
0
            CPLError(CE_Failure, CPLE_AppDefined,
9490
0
                     "Value of BAND_METADATA should be an array");
9491
0
            return nullptr;
9492
0
        }
9493
9494
0
        auto oArray = oRoot.ToArray();
9495
0
        for (int j = 0; j < oArray.Size(); ++j)
9496
0
        {
9497
0
            const auto oJsonItem = oArray[j];
9498
0
            MetadataItem oItem;
9499
0
            size_t iExtraDimIdx = 0;
9500
9501
0
            const auto osBandArrayFullname = oJsonItem.GetString("array");
9502
0
            const auto osBandAttributeName = oJsonItem.GetString("attribute");
9503
0
            std::shared_ptr<GDALMDArray> poArray;
9504
0
            std::shared_ptr<GDALAttribute> poAttribute;
9505
0
            if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9506
0
            {
9507
0
                CPLError(CE_Failure, CPLE_AppDefined,
9508
0
                         "BAND_METADATA[%d][\"array\"] or "
9509
0
                         "BAND_METADATA[%d][\"attribute\"] is missing",
9510
0
                         j, j);
9511
0
                return nullptr;
9512
0
            }
9513
0
            else if (!osBandArrayFullname.empty() &&
9514
0
                     !osBandAttributeName.empty())
9515
0
            {
9516
0
                CPLError(
9517
0
                    CE_Failure, CPLE_AppDefined,
9518
0
                    "BAND_METADATA[%d][\"array\"] and "
9519
0
                    "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
9520
0
                    j, j);
9521
0
                return nullptr;
9522
0
            }
9523
0
            else if (!osBandArrayFullname.empty())
9524
0
            {
9525
0
                poArray =
9526
0
                    poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
9527
0
                if (!poArray)
9528
0
                {
9529
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9530
0
                             "Array %s cannot be found",
9531
0
                             osBandArrayFullname.c_str());
9532
0
                    return nullptr;
9533
0
                }
9534
0
                if (poArray->GetDimensionCount() != 1)
9535
0
                {
9536
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9537
0
                             "Array %s is not a 1D array",
9538
0
                             osBandArrayFullname.c_str());
9539
0
                    return nullptr;
9540
0
                }
9541
0
                const auto &osAuxArrayDimName =
9542
0
                    poArray->GetDimensions()[0]->GetName();
9543
0
                auto oIter =
9544
0
                    oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9545
0
                if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9546
0
                {
9547
0
                    CPLError(
9548
0
                        CE_Failure, CPLE_AppDefined,
9549
0
                        "Dimension %s of array %s is not a non-X/Y dimension "
9550
0
                        "of array %s",
9551
0
                        osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
9552
0
                        array->GetName().c_str());
9553
0
                    return nullptr;
9554
0
                }
9555
0
                iExtraDimIdx = oIter->second;
9556
0
                CPLAssert(iExtraDimIdx < nNewDimCount);
9557
0
            }
9558
0
            else
9559
0
            {
9560
0
                CPLAssert(!osBandAttributeName.empty());
9561
0
                poAttribute = !osBandAttributeName.empty() &&
9562
0
                                      osBandAttributeName[0] == '/'
9563
0
                                  ? poRootGroup->OpenAttributeFromFullname(
9564
0
                                        osBandAttributeName)
9565
0
                                  : array->GetAttribute(osBandAttributeName);
9566
0
                if (!poAttribute)
9567
0
                {
9568
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9569
0
                             "Attribute %s cannot be found",
9570
0
                             osBandAttributeName.c_str());
9571
0
                    return nullptr;
9572
0
                }
9573
0
                const auto aoAttrDims = poAttribute->GetDimensionsSize();
9574
0
                if (aoAttrDims.size() != 1)
9575
0
                {
9576
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9577
0
                             "Attribute %s is not a 1D array",
9578
0
                             osBandAttributeName.c_str());
9579
0
                    return nullptr;
9580
0
                }
9581
0
                bool found = false;
9582
0
                for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9583
0
                {
9584
0
                    if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9585
0
                            ->GetSize() == aoAttrDims[0])
9586
0
                    {
9587
0
                        if (found)
9588
0
                        {
9589
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9590
0
                                     "Several dimensions of %s have the same "
9591
0
                                     "size as attribute %s. Cannot infer which "
9592
0
                                     "one to bind to!",
9593
0
                                     array->GetName().c_str(),
9594
0
                                     osBandAttributeName.c_str());
9595
0
                            return nullptr;
9596
0
                        }
9597
0
                        found = true;
9598
0
                        iExtraDimIdx = iter.second;
9599
0
                    }
9600
0
                }
9601
0
                if (!found)
9602
0
                {
9603
0
                    CPLError(
9604
0
                        CE_Failure, CPLE_AppDefined,
9605
0
                        "No dimension of %s has the same size as attribute %s",
9606
0
                        array->GetName().c_str(), osBandAttributeName.c_str());
9607
0
                    return nullptr;
9608
0
                }
9609
0
            }
9610
9611
0
            oItem.osName = oJsonItem.GetString("item_name");
9612
0
            if (oItem.osName.empty())
9613
0
            {
9614
0
                CPLError(CE_Failure, CPLE_AppDefined,
9615
0
                         "BAND_METADATA[%d][\"item_name\"] is missing", j);
9616
0
                return nullptr;
9617
0
            }
9618
9619
0
            const auto osDefinition = oJsonItem.GetString("item_value", "%s");
9620
9621
            // Check correctness of definition
9622
0
            bool bFirstNumericFormatter = true;
9623
0
            std::string osModDefinition;
9624
0
            bool bDefinitionUsesPctForG = false;
9625
0
            for (size_t k = 0; k < osDefinition.size(); ++k)
9626
0
            {
9627
0
                if (osDefinition[k] == '%')
9628
0
                {
9629
0
                    osModDefinition += osDefinition[k];
9630
0
                    if (k + 1 == osDefinition.size())
9631
0
                    {
9632
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9633
0
                                 "Value of "
9634
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9635
0
                                 "%s is invalid at offset %d",
9636
0
                                 j, osDefinition.c_str(), int(k));
9637
0
                        return nullptr;
9638
0
                    }
9639
0
                    ++k;
9640
0
                    if (osDefinition[k] == '%')
9641
0
                    {
9642
0
                        osModDefinition += osDefinition[k];
9643
0
                        continue;
9644
0
                    }
9645
0
                    if (!bFirstNumericFormatter)
9646
0
                    {
9647
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9648
0
                                 "Value of "
9649
0
                                 "BAND_METADATA[%d][\"item_value\"] = %s is "
9650
0
                                 "invalid at offset %d: %%[x][.y]f|g or %%s "
9651
0
                                 "formatters should be specified at most once",
9652
0
                                 j, osDefinition.c_str(), int(k));
9653
0
                        return nullptr;
9654
0
                    }
9655
0
                    bFirstNumericFormatter = false;
9656
0
                    for (; k < osDefinition.size(); ++k)
9657
0
                    {
9658
0
                        osModDefinition += osDefinition[k];
9659
0
                        if (!((osDefinition[k] >= '0' &&
9660
0
                               osDefinition[k] <= '9') ||
9661
0
                              osDefinition[k] == '.'))
9662
0
                            break;
9663
0
                    }
9664
0
                    if (k == osDefinition.size() ||
9665
0
                        (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
9666
0
                         osDefinition[k] != 's'))
9667
0
                    {
9668
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9669
0
                                 "Value of "
9670
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9671
0
                                 "%s is invalid at offset %d: only "
9672
0
                                 "%%[x][.y]f|g or %%s formatters are accepted",
9673
0
                                 j, osDefinition.c_str(), int(k));
9674
0
                        return nullptr;
9675
0
                    }
9676
0
                    bDefinitionUsesPctForG =
9677
0
                        (osDefinition[k] == 'f' || osDefinition[k] == 'g');
9678
0
                    if (bDefinitionUsesPctForG)
9679
0
                    {
9680
0
                        if (poArray &&
9681
0
                            poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
9682
0
                        {
9683
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9684
0
                                     "Data type of %s array is not numeric",
9685
0
                                     poArray->GetName().c_str());
9686
0
                            return nullptr;
9687
0
                        }
9688
0
                        else if (poAttribute &&
9689
0
                                 poAttribute->GetDataType().GetClass() !=
9690
0
                                     GEDTC_NUMERIC)
9691
0
                        {
9692
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9693
0
                                     "Data type of %s attribute is not numeric",
9694
0
                                     poAttribute->GetFullName().c_str());
9695
0
                            return nullptr;
9696
0
                        }
9697
0
                    }
9698
0
                }
9699
0
                else if (osDefinition[k] == '$' &&
9700
0
                         k + 1 < osDefinition.size() &&
9701
0
                         osDefinition[k + 1] == '{')
9702
0
                {
9703
0
                    const auto nPos = osDefinition.find('}', k);
9704
0
                    if (nPos == std::string::npos)
9705
0
                    {
9706
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9707
0
                                 "Value of "
9708
0
                                 "BAND_METADATA[%d][\"item_value\"] = "
9709
0
                                 "%s is invalid at offset %d",
9710
0
                                 j, osDefinition.c_str(), int(k));
9711
0
                        return nullptr;
9712
0
                    }
9713
0
                    const auto osAttrName =
9714
0
                        osDefinition.substr(k + 2, nPos - (k + 2));
9715
0
                    std::shared_ptr<GDALAttribute> poAttr;
9716
0
                    if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9717
0
                    {
9718
0
                        poAttr = poArray->GetAttribute(osAttrName);
9719
0
                        if (!poAttr)
9720
0
                        {
9721
0
                            CPLError(
9722
0
                                CE_Failure, CPLE_AppDefined,
9723
0
                                "Value of "
9724
0
                                "BAND_METADATA[%d][\"item_value\"] = "
9725
0
                                "%s is invalid: %s is not an attribute of %s",
9726
0
                                j, osDefinition.c_str(), osAttrName.c_str(),
9727
0
                                poArray->GetName().c_str());
9728
0
                            return nullptr;
9729
0
                        }
9730
0
                    }
9731
0
                    else
9732
0
                    {
9733
0
                        poAttr =
9734
0
                            poRootGroup->OpenAttributeFromFullname(osAttrName);
9735
0
                        if (!poAttr)
9736
0
                        {
9737
0
                            CPLError(CE_Failure, CPLE_AppDefined,
9738
0
                                     "Value of "
9739
0
                                     "BAND_METADATA[%d][\"item_value\"] = "
9740
0
                                     "%s is invalid: %s is not an attribute",
9741
0
                                     j, osDefinition.c_str(),
9742
0
                                     osAttrName.c_str());
9743
0
                            return nullptr;
9744
0
                        }
9745
0
                    }
9746
0
                    k = nPos;
9747
0
                    const char *pszValue = poAttr->ReadAsString();
9748
0
                    if (!pszValue)
9749
0
                    {
9750
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9751
0
                                 "Cannot get value of attribute %s as a "
9752
0
                                 "string",
9753
0
                                 osAttrName.c_str());
9754
0
                        return nullptr;
9755
0
                    }
9756
0
                    osModDefinition += pszValue;
9757
0
                }
9758
0
                else
9759
0
                {
9760
0
                    osModDefinition += osDefinition[k];
9761
0
                }
9762
0
            }
9763
9764
0
            if (poArray)
9765
0
                oItem.poArray = std::move(poArray);
9766
0
            else
9767
0
                oItem.poArray = std::move(poAttribute);
9768
0
            oItem.osDefinition = std::move(osModDefinition);
9769
0
            oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
9770
9771
0
            aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
9772
0
                std::move(oItem));
9773
0
        }
9774
0
    }
9775
9776
0
    std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
9777
0
    const char *pszBandImageryMetadata =
9778
0
        CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
9779
0
    if (pszBandImageryMetadata)
9780
0
    {
9781
0
        if (!poRootGroup)
9782
0
        {
9783
0
            CPLError(CE_Failure, CPLE_AppDefined,
9784
0
                     "Root group should be provided when BAND_IMAGERY_METADATA "
9785
0
                     "is set");
9786
0
            return nullptr;
9787
0
        }
9788
0
        CPLJSONDocument oDoc;
9789
0
        if (!oDoc.LoadMemory(pszBandImageryMetadata))
9790
0
        {
9791
0
            CPLError(CE_Failure, CPLE_AppDefined,
9792
0
                     "Invalid JSON content for BAND_IMAGERY_METADATA");
9793
0
            return nullptr;
9794
0
        }
9795
0
        auto oRoot = oDoc.GetRoot();
9796
0
        if (oRoot.GetType() != CPLJSONObject::Type::Object)
9797
0
        {
9798
0
            CPLError(CE_Failure, CPLE_AppDefined,
9799
0
                     "Value of BAND_IMAGERY_METADATA should be an object");
9800
0
            return nullptr;
9801
0
        }
9802
0
        for (const auto &oJsonItem : oRoot.GetChildren())
9803
0
        {
9804
0
            if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
9805
0
                oJsonItem.GetName() == "FWHM_UM")
9806
0
            {
9807
0
                const auto osBandArrayFullname = oJsonItem.GetString("array");
9808
0
                const auto osBandAttributeName =
9809
0
                    oJsonItem.GetString("attribute");
9810
0
                std::shared_ptr<GDALMDArray> poArray;
9811
0
                std::shared_ptr<GDALAttribute> poAttribute;
9812
0
                size_t iExtraDimIdx = 0;
9813
0
                if (osBandArrayFullname.empty() && osBandAttributeName.empty())
9814
0
                {
9815
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9816
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
9817
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
9818
0
                             "missing",
9819
0
                             oJsonItem.GetName().c_str(),
9820
0
                             oJsonItem.GetName().c_str());
9821
0
                    return nullptr;
9822
0
                }
9823
0
                else if (!osBandArrayFullname.empty() &&
9824
0
                         !osBandAttributeName.empty())
9825
0
                {
9826
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9827
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
9828
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
9829
0
                             "mutually exclusive",
9830
0
                             oJsonItem.GetName().c_str(),
9831
0
                             oJsonItem.GetName().c_str());
9832
0
                    return nullptr;
9833
0
                }
9834
0
                else if (!osBandArrayFullname.empty())
9835
0
                {
9836
0
                    poArray = poRootGroup->OpenMDArrayFromFullname(
9837
0
                        osBandArrayFullname);
9838
0
                    if (!poArray)
9839
0
                    {
9840
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9841
0
                                 "Array %s cannot be found",
9842
0
                                 osBandArrayFullname.c_str());
9843
0
                        return nullptr;
9844
0
                    }
9845
0
                    if (poArray->GetDimensionCount() != 1)
9846
0
                    {
9847
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9848
0
                                 "Array %s is not a 1D array",
9849
0
                                 osBandArrayFullname.c_str());
9850
0
                        return nullptr;
9851
0
                    }
9852
0
                    const auto &osAuxArrayDimName =
9853
0
                        poArray->GetDimensions()[0]->GetName();
9854
0
                    auto oIter =
9855
0
                        oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
9856
0
                    if (oIter == oMapArrayDimNameToExtraDimIdx.end())
9857
0
                    {
9858
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9859
0
                                 "Dimension \"%s\" of array \"%s\" is not a "
9860
0
                                 "non-X/Y dimension of array \"%s\"",
9861
0
                                 osAuxArrayDimName.c_str(),
9862
0
                                 osBandArrayFullname.c_str(),
9863
0
                                 array->GetName().c_str());
9864
0
                        return nullptr;
9865
0
                    }
9866
0
                    iExtraDimIdx = oIter->second;
9867
0
                    CPLAssert(iExtraDimIdx < nNewDimCount);
9868
0
                }
9869
0
                else
9870
0
                {
9871
0
                    poAttribute =
9872
0
                        !osBandAttributeName.empty() &&
9873
0
                                osBandAttributeName[0] == '/'
9874
0
                            ? poRootGroup->OpenAttributeFromFullname(
9875
0
                                  osBandAttributeName)
9876
0
                            : array->GetAttribute(osBandAttributeName);
9877
0
                    if (!poAttribute)
9878
0
                    {
9879
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9880
0
                                 "Attribute %s cannot be found",
9881
0
                                 osBandAttributeName.c_str());
9882
0
                        return nullptr;
9883
0
                    }
9884
0
                    const auto aoAttrDims = poAttribute->GetDimensionsSize();
9885
0
                    if (aoAttrDims.size() != 1)
9886
0
                    {
9887
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9888
0
                                 "Attribute %s is not a 1D array",
9889
0
                                 osBandAttributeName.c_str());
9890
0
                        return nullptr;
9891
0
                    }
9892
0
                    bool found = false;
9893
0
                    for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
9894
0
                    {
9895
0
                        if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
9896
0
                                ->GetSize() == aoAttrDims[0])
9897
0
                        {
9898
0
                            if (found)
9899
0
                            {
9900
0
                                CPLError(CE_Failure, CPLE_AppDefined,
9901
0
                                         "Several dimensions of %s have the "
9902
0
                                         "same size as attribute %s. Cannot "
9903
0
                                         "infer which one to bind to!",
9904
0
                                         array->GetName().c_str(),
9905
0
                                         osBandAttributeName.c_str());
9906
0
                                return nullptr;
9907
0
                            }
9908
0
                            found = true;
9909
0
                            iExtraDimIdx = iter.second;
9910
0
                        }
9911
0
                    }
9912
0
                    if (!found)
9913
0
                    {
9914
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9915
0
                                 "No dimension of %s has the same size as "
9916
0
                                 "attribute %s",
9917
0
                                 array->GetName().c_str(),
9918
0
                                 osBandAttributeName.c_str());
9919
0
                        return nullptr;
9920
0
                    }
9921
0
                }
9922
9923
0
                std::string osUnit = oJsonItem.GetString("unit", "um");
9924
0
                if (STARTS_WITH(osUnit.c_str(), "${"))
9925
0
                {
9926
0
                    if (osUnit.back() != '}')
9927
0
                    {
9928
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9929
0
                                 "Value of "
9930
0
                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9931
0
                                 "%s is invalid",
9932
0
                                 oJsonItem.GetName().c_str(), osUnit.c_str());
9933
0
                        return nullptr;
9934
0
                    }
9935
0
                    const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
9936
0
                    std::shared_ptr<GDALAttribute> poAttr;
9937
0
                    if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
9938
0
                    {
9939
0
                        poAttr = poArray->GetAttribute(osAttrName);
9940
0
                        if (!poAttr)
9941
0
                        {
9942
0
                            CPLError(
9943
0
                                CE_Failure, CPLE_AppDefined,
9944
0
                                "Value of "
9945
0
                                "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9946
0
                                "%s is invalid: %s is not an attribute of %s",
9947
0
                                oJsonItem.GetName().c_str(), osUnit.c_str(),
9948
0
                                osAttrName.c_str(),
9949
0
                                osBandArrayFullname.c_str());
9950
0
                            return nullptr;
9951
0
                        }
9952
0
                    }
9953
0
                    else
9954
0
                    {
9955
0
                        poAttr =
9956
0
                            poRootGroup->OpenAttributeFromFullname(osAttrName);
9957
0
                        if (!poAttr)
9958
0
                        {
9959
0
                            CPLError(
9960
0
                                CE_Failure, CPLE_AppDefined,
9961
0
                                "Value of "
9962
0
                                "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
9963
0
                                "%s is invalid: %s is not an attribute",
9964
0
                                oJsonItem.GetName().c_str(), osUnit.c_str(),
9965
0
                                osAttrName.c_str());
9966
0
                            return nullptr;
9967
0
                        }
9968
0
                    }
9969
9970
0
                    const char *pszValue = poAttr->ReadAsString();
9971
0
                    if (!pszValue)
9972
0
                    {
9973
0
                        CPLError(CE_Failure, CPLE_AppDefined,
9974
0
                                 "Cannot get value of attribute %s of %s as a "
9975
0
                                 "string",
9976
0
                                 osAttrName.c_str(),
9977
0
                                 osBandArrayFullname.c_str());
9978
0
                        return nullptr;
9979
0
                    }
9980
0
                    osUnit = pszValue;
9981
0
                }
9982
0
                double dfConvToUM = 1.0;
9983
0
                if (osUnit == "nm" || osUnit == "nanometre" ||
9984
0
                    osUnit == "nanometres" || osUnit == "nanometer" ||
9985
0
                    osUnit == "nanometers")
9986
0
                {
9987
0
                    dfConvToUM = 1e-3;
9988
0
                }
9989
0
                else if (!(osUnit == "um" || osUnit == "micrometre" ||
9990
0
                           osUnit == "micrometres" || osUnit == "micrometer" ||
9991
0
                           osUnit == "micrometers"))
9992
0
                {
9993
0
                    CPLError(CE_Failure, CPLE_AppDefined,
9994
0
                             "Unhandled value for "
9995
0
                             "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
9996
0
                             oJsonItem.GetName().c_str(), osUnit.c_str());
9997
0
                    return nullptr;
9998
0
                }
9999
10000
0
                BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
10001
10002
0
                std::shared_ptr<GDALAbstractMDArray> abstractArray;
10003
0
                if (poArray)
10004
0
                    abstractArray = std::move(poArray);
10005
0
                else
10006
0
                    abstractArray = std::move(poAttribute);
10007
0
                if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
10008
0
                {
10009
0
                    item.poCentralWavelengthArray = std::move(abstractArray);
10010
0
                    item.dfCentralWavelengthToMicrometer = dfConvToUM;
10011
0
                }
10012
0
                else
10013
0
                {
10014
0
                    item.poFWHMArray = std::move(abstractArray);
10015
0
                    item.dfFWHMToMicrometer = dfConvToUM;
10016
0
                }
10017
0
            }
10018
0
            else
10019
0
            {
10020
0
                CPLError(CE_Warning, CPLE_AppDefined,
10021
0
                         "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
10022
0
                         oJsonItem.GetName().c_str());
10023
0
            }
10024
0
        }
10025
0
    }
10026
10027
0
    auto poDS = std::make_unique<GDALDatasetFromArray>(
10028
0
        array, iXDim, iYDim, CPLStringList(papszOptions));
10029
10030
0
    poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
10031
10032
0
    poDS->nRasterYSize =
10033
0
        nDimCount < 2 ? 1
10034
0
                      : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
10035
0
                                                  dims[iYDim]->GetSize()));
10036
0
    poDS->nRasterXSize = static_cast<int>(
10037
0
        std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
10038
10039
0
    std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
10040
0
    std::vector<GUInt64> anStackIters(nDimCount);
10041
0
    std::vector<size_t> anMapNewToOld(nNewDimCount);
10042
0
    for (size_t i = 0, j = 0; i < nDimCount; ++i)
10043
0
    {
10044
0
        if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
10045
0
        {
10046
0
            anMapNewToOld[j] = i;
10047
0
            j++;
10048
0
        }
10049
0
    }
10050
10051
0
    poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
10052
10053
0
    const auto attrs(array->GetAttributes());
10054
0
    for (const auto &attr : attrs)
10055
0
    {
10056
0
        if (attr->GetName() == "spatial:registration")
10057
0
        {
10058
            // From https://github.com/zarr-conventions/spatial
10059
0
            const char *pszValue = attr->ReadAsString();
10060
0
            if (pszValue && strcmp(pszValue, "pixel") == 0)
10061
0
                poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10062
0
                                             GDALMD_AOP_AREA);
10063
0
            else if (pszValue && strcmp(pszValue, "node") == 0)
10064
0
                poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
10065
0
                                             GDALMD_AOP_POINT);
10066
0
            else if (pszValue)
10067
0
                poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), pszValue);
10068
0
        }
10069
0
        else if (attr->GetName() == "gdal:geotransform")
10070
0
        {
10071
            // From Zarr driver
10072
0
            const auto doubleArray = attr->ReadAsDoubleArray();
10073
0
            if (doubleArray.size() == 6)
10074
0
            {
10075
0
                poDS->m_bHasGT = true;
10076
0
                poDS->m_gt = GDALGeoTransform(doubleArray.data());
10077
0
            }
10078
0
        }
10079
0
        else if (attr->GetName() != "COLOR_INTERPRETATION")
10080
0
        {
10081
0
            auto stringArray = attr->ReadAsStringArray();
10082
0
            std::string val;
10083
0
            if (stringArray.size() > 1)
10084
0
            {
10085
0
                val += '{';
10086
0
            }
10087
0
            for (int i = 0; i < stringArray.size(); ++i)
10088
0
            {
10089
0
                if (i > 0)
10090
0
                    val += ',';
10091
0
                val += stringArray[i];
10092
0
            }
10093
0
            if (stringArray.size() > 1)
10094
0
            {
10095
0
                val += '}';
10096
0
            }
10097
0
            poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
10098
0
        }
10099
0
    }
10100
10101
0
    const char *pszDelay = CSLFetchNameValueDef(
10102
0
        papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
10103
0
        CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
10104
0
    const double dfDelay =
10105
0
        EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
10106
0
    const auto nStartTime = time(nullptr);
10107
0
    bool bHasWarned = false;
10108
    // Instantiate bands by iterating over non-XY variables
10109
0
    size_t iDim = 0;
10110
0
    int nCurBand = 1;
10111
0
lbl_next_depth:
10112
0
    if (iDim < nNewDimCount)
10113
0
    {
10114
0
        anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
10115
0
        anOtherDimCoord[iDim] = 0;
10116
0
        while (true)
10117
0
        {
10118
0
            ++iDim;
10119
0
            goto lbl_next_depth;
10120
0
        lbl_return_to_caller:
10121
0
            --iDim;
10122
0
            --anStackIters[iDim];
10123
0
            if (anStackIters[iDim] == 0)
10124
0
                break;
10125
0
            ++anOtherDimCoord[iDim];
10126
0
        }
10127
0
    }
10128
0
    else
10129
0
    {
10130
0
        poDS->SetBand(nCurBand,
10131
0
                      new GDALRasterBandFromArray(
10132
0
                          poDS.get(), anOtherDimCoord,
10133
0
                          aoBandParameterMetadataItems, aoBandImageryMetadata,
10134
0
                          dfDelay, nStartTime, bHasWarned));
10135
0
        ++nCurBand;
10136
0
    }
10137
0
    if (iDim > 0)
10138
0
        goto lbl_return_to_caller;
10139
10140
0
    if (!array->GetFilename().empty() &&
10141
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "LOAD_PAM", "YES")))
10142
0
    {
10143
0
        poDS->SetPhysicalFilename(array->GetFilename().c_str());
10144
0
        std::string osDerivedDatasetName(
10145
0
            CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
10146
0
                       int(iYDim), array->GetFullName().c_str()));
10147
0
        if (!array->GetContext().empty())
10148
0
        {
10149
0
            osDerivedDatasetName += " with context ";
10150
0
            osDerivedDatasetName += array->GetContext();
10151
0
        }
10152
0
        poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
10153
0
        poDS->TryLoadXML();
10154
10155
0
        for (const auto &[pszKey, pszValue] :
10156
0
             cpl::IterateNameValue(static_cast<CSLConstList>(
10157
0
                 poDS->GDALPamDataset::GetMetadata())))
10158
0
        {
10159
0
            poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
10160
0
        }
10161
0
    }
10162
10163
0
    return poDS;
10164
0
}
10165
10166
/************************************************************************/
10167
/*                          AsClassicDataset()                          */
10168
/************************************************************************/
10169
10170
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
10171
 *
10172
 * In the case of > 2D arrays, additional dimensions will be represented as
10173
 * raster bands.
10174
 *
10175
 * The "reverse" method is GDALRasterBand::AsMDArray().
10176
 *
10177
 * This is the same as the C function GDALMDArrayAsClassicDataset().
10178
 *
10179
 * @param iXDim Index of the dimension that will be used as the X/width axis.
10180
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
10181
 *              Ignored if the dimension count is 1.
10182
 * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
10183
 *                    and BAND_IMAGERY_METADATA option.
10184
 * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
10185
 *                     nullptr. Current supported options are:
10186
 *                     <ul>
10187
 *                     <li>BAND_METADATA: JSON serialized array defining which
10188
 *                         arrays of the poRootGroup, indexed by non-X and Y
10189
 *                         dimensions, should be mapped as band metadata items.
10190
 *                         Each array item should be an object with the
10191
 *                         following members:
10192
 *                         - "array": full name of a band parameter array.
10193
 *                           Such array must be a one
10194
 *                           dimensional array, and its dimension must be one of
10195
 *                           the dimensions of the array on which the method is
10196
 *                           called (excluding the X and Y dimensions).
10197
 *                         - "attribute": name relative to *this array or full
10198
 *                           name of a single dimension numeric array whose size
10199
 *                           must be one of the dimensions of *this array
10200
 *                           (excluding the X and Y dimensions).
10201
 *                           "array" and "attribute" are mutually exclusive.
10202
 *                         - "item_name": band metadata item name
10203
 *                         - "item_value": (optional) String, where "%[x][.y]f",
10204
 *                           "%[x][.y]g" or "%s" printf-like formatting can be
10205
 *                           used to format the corresponding value of the
10206
 *                           parameter array. The percentage character should be
10207
 *                           repeated: "%%"
10208
 *                           "${attribute_name}" can also be used to include the
10209
 *                           value of an attribute for "array" when set and if
10210
 *                           not starting with '/'. Otherwise if starting with
10211
 *                           '/', it is the full path to the attribute.
10212
 *
10213
 *                           If "item_value" is not provided, a default formatting
10214
 *                           of the value will be applied.
10215
 *
10216
 *                         Example:
10217
 *                         [
10218
 *                            {
10219
 *                              "array": "/sensor_band_parameters/wavelengths",
10220
 *                              "item_name": "WAVELENGTH",
10221
 *                              "item_value": "%.1f ${units}"
10222
 *                            },
10223
 *                            {
10224
 *                              "array": "/sensor_band_parameters/fwhm",
10225
 *                              "item_name": "FWHM"
10226
 *                            },
10227
 *                            {
10228
 *                              "array": "/sensor_band_parameters/fwhm",
10229
 *                              "item_name": "FWHM_UNIT",
10230
 *                              "item_value": "${units}"
10231
 *                            }
10232
 *                         ]
10233
 *
10234
 *                         Example for Planet Labs Tanager radiance products:
10235
 *                         [
10236
 *                            {
10237
 *                              "attribute": "center_wavelengths",
10238
 *                              "item_name": "WAVELENGTH",
10239
 *                              "item_value": "%.1f ${center_wavelengths_units}"
10240
 *                            }
10241
 *                         ]
10242
 *
10243
 *                     </li>
10244
 *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
10245
 *                         JSON serialized object defining which arrays of the
10246
 *                         poRootGroup, indexed by non-X and Y dimensions,
10247
 *                         should be mapped as band metadata items in the
10248
 *                         band IMAGERY domain.
10249
 *                         The object currently accepts 2 members:
10250
 *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
10251
 *                           micrometers.
10252
 *                         - "FWHM_UM": Full-width half-maximum
10253
 *                           in micrometers.
10254
 *                         The value of each member should be an object with the
10255
 *                         following members:
10256
 *                         - "array": full name of a band parameter array.
10257
 *                           Such array must be a one dimensional array, and its
10258
 *                           dimension must be one of the dimensions of the
10259
 *                           array on which the method is called
10260
 *                           (excluding the X and Y dimensions).
10261
 *                         - "attribute": name relative to *this array or full
10262
 *                           name of a single dimension numeric array whose size
10263
 *                           must be one of the dimensions of *this array
10264
 *                           (excluding the X and Y dimensions).
10265
 *                           "array" and "attribute" are mutually exclusive,
10266
 *                           and one of them is required.
10267
 *                         - "unit": (optional) unit of the values pointed in
10268
 *                           the above array.
10269
 *                           Can be a literal string or a string of the form
10270
 *                           "${attribute_name}" to point to an attribute for
10271
 *                           "array" when set and if no starting
10272
 *                           with '/'. Otherwise if starting with '/', it is
10273
 *                           the full path to the attribute.
10274
 *                           Accepted values are "um", "micrometer"
10275
 *                           (with UK vs US spelling, singular or plural), "nm",
10276
 *                           "nanometer" (with UK vs US spelling, singular or
10277
 *                           plural)
10278
 *                           If not provided, micrometer is assumed.
10279
 *
10280
 *                         Example for EMIT datasets:
10281
 *                         {
10282
 *                            "CENTRAL_WAVELENGTH_UM": {
10283
 *                                "array": "/sensor_band_parameters/wavelengths",
10284
 *                                "unit": "${units}"
10285
 *                            },
10286
 *                            "FWHM_UM": {
10287
 *                                "array": "/sensor_band_parameters/fwhm",
10288
 *                                "unit": "${units}"
10289
 *                            }
10290
 *                         }
10291
 *
10292
 *                         Example for Planet Labs Tanager radiance products:
10293
 *                         {
10294
 *                            "CENTRAL_WAVELENGTH_UM": {
10295
 *                              "attribute": "center_wavelengths",
10296
 *                              "unit": "${center_wavelengths_units}"
10297
 *                            },
10298
 *                            "FWHM_UM": {
10299
 *                              "attribute": "fwhm",
10300
 *                              "unit": "${fwhm_units}"
10301
 *                            }
10302
 *                         }
10303
 *
10304
 *                     </li>
10305
 *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
10306
 *                         seconds allowed to set the DIM_{dimname}_VALUE band
10307
 *                         metadata items from the indexing variable of the
10308
 *                         dimensions.
10309
 *                         Default value is 5. 'unlimited' can be used to mean
10310
 *                         unlimited delay. Can also be defined globally with
10311
 *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
10312
 *                         option.</li>
10313
 *                     </ul>
10314
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
10315
 */
10316
GDALDataset *
10317
GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
10318
                              const std::shared_ptr<GDALGroup> &poRootGroup,
10319
                              CSLConstList papszOptions) const
10320
0
{
10321
0
    auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
10322
0
    if (!self)
10323
0
    {
10324
0
        CPLError(CE_Failure, CPLE_AppDefined,
10325
0
                 "Driver implementation issue: m_pSelf not set !");
10326
0
        return nullptr;
10327
0
    }
10328
0
    return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
10329
0
                                        papszOptions)
10330
0
        .release();
10331
0
}
10332
10333
/************************************************************************/
10334
/*                           GetStatistics()                            */
10335
/************************************************************************/
10336
10337
/**
10338
 * \brief Fetch statistics.
10339
 *
10340
 * Returns the minimum, maximum, mean and standard deviation of all
10341
 * pixel values in this array.
10342
 *
10343
 * If bForce is FALSE results will only be returned if it can be done
10344
 * quickly (i.e. without scanning the data).  If bForce is FALSE and
10345
 * results cannot be returned efficiently, the method will return CE_Warning
10346
 * but no warning will have been issued.   This is a non-standard use of
10347
 * the CE_Warning return value to indicate "nothing done".
10348
 *
10349
 * When cached statistics are not available, and bForce is TRUE,
10350
 * ComputeStatistics() is called.
10351
 *
10352
 * Note that file formats using PAM (Persistent Auxiliary Metadata) services
10353
 * will generally cache statistics in the .aux.xml file allowing fast fetch
10354
 * after the first request.
10355
 *
10356
 * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10357
 *
10358
 * This method is the same as the C function GDALMDArrayGetStatistics().
10359
 *
10360
 * @param bApproxOK Currently ignored. In the future, should be set to true
10361
 * if statistics on the whole array are wished, or to false if a subset of it
10362
 * may be used.
10363
 *
10364
 * @param bForce If false statistics will only be returned if it can
10365
 * be done without rescanning the image.
10366
 *
10367
 * @param pdfMin Location into which to load image minimum (may be NULL).
10368
 *
10369
 * @param pdfMax Location into which to load image maximum (may be NULL).-
10370
 *
10371
 * @param pdfMean Location into which to load image mean (may be NULL).
10372
 *
10373
 * @param pdfStdDev Location into which to load image standard deviation
10374
 * (may be NULL).
10375
 *
10376
 * @param pnValidCount Number of samples whose value is different from the
10377
 * nodata value. (may be NULL)
10378
 *
10379
 * @param pfnProgress a function to call to report progress, or NULL.
10380
 *
10381
 * @param pProgressData application data to pass to the progress function.
10382
 *
10383
 * @return CE_None on success, CE_Warning if no values returned,
10384
 * CE_Failure if an error occurs.
10385
 *
10386
 * @since GDAL 3.2
10387
 */
10388
10389
CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
10390
                                  double *pdfMax, double *pdfMean,
10391
                                  double *pdfStdDev, GUInt64 *pnValidCount,
10392
                                  GDALProgressFunc pfnProgress,
10393
                                  void *pProgressData)
10394
0
{
10395
0
    if (!bForce)
10396
0
        return CE_Warning;
10397
10398
0
    return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
10399
0
                             pnValidCount, pfnProgress, pProgressData, nullptr)
10400
0
               ? CE_None
10401
0
               : CE_Failure;
10402
0
}
10403
10404
/************************************************************************/
10405
/*                         ComputeStatistics()                          */
10406
/************************************************************************/
10407
10408
/**
10409
 * \brief Compute statistics.
10410
 *
10411
 * Returns the minimum, maximum, mean and standard deviation of all
10412
 * pixel values in this array.
10413
 *
10414
 * Pixels taken into account in statistics are those whose mask value
10415
 * (as determined by GetMask()) is non-zero.
10416
 *
10417
 * Once computed, the statistics will generally be "set" back on the
10418
 * owing dataset.
10419
 *
10420
 * Cached statistics can be cleared with GDALDataset::ClearStatistics().
10421
 *
10422
 * This method is the same as the C functions GDALMDArrayComputeStatistics().
10423
 * and GDALMDArrayComputeStatisticsEx().
10424
 *
10425
 * @param bApproxOK Currently ignored. In the future, should be set to true
10426
 * if statistics on the whole array are wished, or to false if a subset of it
10427
 * may be used.
10428
 *
10429
 * @param pdfMin Location into which to load image minimum (may be NULL).
10430
 *
10431
 * @param pdfMax Location into which to load image maximum (may be NULL).-
10432
 *
10433
 * @param pdfMean Location into which to load image mean (may be NULL).
10434
 *
10435
 * @param pdfStdDev Location into which to load image standard deviation
10436
 * (may be NULL).
10437
 *
10438
 * @param pnValidCount Number of samples whose value is different from the
10439
 * nodata value. (may be NULL)
10440
 *
10441
 * @param pfnProgress a function to call to report progress, or NULL.
10442
 *
10443
 * @param pProgressData application data to pass to the progress function.
10444
 *
10445
 * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
10446
 *                     Options are driver specific. For now the netCDF and Zarr
10447
 *                     drivers recognize UPDATE_METADATA=YES, whose effect is
10448
 *                     to add or update the actual_range attribute with the
10449
 *                     computed min/max, only if done on the full array, in non
10450
 *                     approximate mode, and the dataset is opened in update
10451
 *                     mode.
10452
 *
10453
 * @return true on success
10454
 *
10455
 * @since GDAL 3.2
10456
 */
10457
10458
bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
10459
                                    double *pdfMax, double *pdfMean,
10460
                                    double *pdfStdDev, GUInt64 *pnValidCount,
10461
                                    GDALProgressFunc pfnProgress,
10462
                                    void *pProgressData,
10463
                                    CSLConstList papszOptions)
10464
0
{
10465
0
    struct StatsPerChunkType
10466
0
    {
10467
0
        const GDALMDArray *array = nullptr;
10468
0
        std::shared_ptr<GDALMDArray> poMask{};
10469
0
        double dfMin = cpl::NumericLimits<double>::max();
10470
0
        double dfMax = -cpl::NumericLimits<double>::max();
10471
0
        double dfMean = 0.0;
10472
0
        double dfM2 = 0.0;
10473
0
        GUInt64 nValidCount = 0;
10474
0
        std::vector<GByte> abyData{};
10475
0
        std::vector<double> adfData{};
10476
0
        std::vector<GByte> abyMaskData{};
10477
0
        GDALProgressFunc pfnProgress = nullptr;
10478
0
        void *pProgressData = nullptr;
10479
0
    };
10480
10481
0
    const auto PerChunkFunc = [](GDALAbstractMDArray *,
10482
0
                                 const GUInt64 *chunkArrayStartIdx,
10483
0
                                 const size_t *chunkCount, GUInt64 iCurChunk,
10484
0
                                 GUInt64 nChunkCount, void *pUserData)
10485
0
    {
10486
0
        StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
10487
0
        const GDALMDArray *array = data->array;
10488
0
        const GDALMDArray *poMask = data->poMask.get();
10489
0
        const size_t nDims = array->GetDimensionCount();
10490
0
        size_t nVals = 1;
10491
0
        for (size_t i = 0; i < nDims; i++)
10492
0
            nVals *= chunkCount[i];
10493
10494
        // Get mask
10495
0
        data->abyMaskData.resize(nVals);
10496
0
        if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10497
0
                           poMask->GetDataType(), &data->abyMaskData[0])))
10498
0
        {
10499
0
            return false;
10500
0
        }
10501
10502
        // Get data
10503
0
        const auto &oType = array->GetDataType();
10504
0
        if (oType.GetNumericDataType() == GDT_Float64)
10505
0
        {
10506
0
            data->adfData.resize(nVals);
10507
0
            if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10508
0
                             oType, &data->adfData[0]))
10509
0
            {
10510
0
                return false;
10511
0
            }
10512
0
        }
10513
0
        else
10514
0
        {
10515
0
            data->abyData.resize(nVals * oType.GetSize());
10516
0
            if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
10517
0
                             oType, &data->abyData[0]))
10518
0
            {
10519
0
                return false;
10520
0
            }
10521
0
            data->adfData.resize(nVals);
10522
0
            GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
10523
0
                            static_cast<int>(oType.GetSize()),
10524
0
                            &data->adfData[0], GDT_Float64,
10525
0
                            static_cast<int>(sizeof(double)),
10526
0
                            static_cast<GPtrDiff_t>(nVals));
10527
0
        }
10528
0
        for (size_t i = 0; i < nVals; i++)
10529
0
        {
10530
0
            if (data->abyMaskData[i])
10531
0
            {
10532
0
                const double dfValue = data->adfData[i];
10533
0
                data->dfMin = std::min(data->dfMin, dfValue);
10534
0
                data->dfMax = std::max(data->dfMax, dfValue);
10535
0
                data->nValidCount++;
10536
0
                const double dfDelta = dfValue - data->dfMean;
10537
0
                data->dfMean += dfDelta / data->nValidCount;
10538
0
                data->dfM2 += dfDelta * (dfValue - data->dfMean);
10539
0
            }
10540
0
        }
10541
0
        if (data->pfnProgress &&
10542
0
            !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
10543
0
                               "", data->pProgressData))
10544
0
        {
10545
0
            return false;
10546
0
        }
10547
0
        return true;
10548
0
    };
10549
10550
0
    const auto &oType = GetDataType();
10551
0
    if (oType.GetClass() != GEDTC_NUMERIC ||
10552
0
        GDALDataTypeIsComplex(oType.GetNumericDataType()))
10553
0
    {
10554
0
        CPLError(
10555
0
            CE_Failure, CPLE_NotSupported,
10556
0
            "Statistics can only be computed on non-complex numeric data type");
10557
0
        return false;
10558
0
    }
10559
10560
0
    const size_t nDims = GetDimensionCount();
10561
0
    std::vector<GUInt64> arrayStartIdx(nDims);
10562
0
    std::vector<GUInt64> count(nDims);
10563
0
    const auto &poDims = GetDimensions();
10564
0
    for (size_t i = 0; i < nDims; i++)
10565
0
    {
10566
0
        count[i] = poDims[i]->GetSize();
10567
0
    }
10568
0
    const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
10569
0
    const size_t nMaxChunkSize =
10570
0
        pszSwathSize
10571
0
            ? static_cast<size_t>(
10572
0
                  std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10573
0
                           CPLAtoGIntBig(pszSwathSize)))
10574
0
            : static_cast<size_t>(
10575
0
                  std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
10576
0
                           GDALGetCacheMax64() / 4));
10577
0
    StatsPerChunkType sData;
10578
0
    sData.array = this;
10579
0
    sData.poMask = GetMask(nullptr);
10580
0
    if (sData.poMask == nullptr)
10581
0
    {
10582
0
        return false;
10583
0
    }
10584
0
    sData.pfnProgress = pfnProgress;
10585
0
    sData.pProgressData = pProgressData;
10586
0
    if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
10587
0
                         GetProcessingChunkSize(nMaxChunkSize).data(),
10588
0
                         PerChunkFunc, &sData))
10589
0
    {
10590
0
        return false;
10591
0
    }
10592
10593
0
    if (pdfMin)
10594
0
        *pdfMin = sData.dfMin;
10595
10596
0
    if (pdfMax)
10597
0
        *pdfMax = sData.dfMax;
10598
10599
0
    if (pdfMean)
10600
0
        *pdfMean = sData.dfMean;
10601
10602
0
    const double dfStdDev =
10603
0
        sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
10604
0
    if (pdfStdDev)
10605
0
        *pdfStdDev = dfStdDev;
10606
10607
0
    if (pnValidCount)
10608
0
        *pnValidCount = sData.nValidCount;
10609
10610
0
    SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
10611
0
                  sData.nValidCount, papszOptions);
10612
10613
0
    return true;
10614
0
}
10615
10616
/************************************************************************/
10617
/*                           SetStatistics()                            */
10618
/************************************************************************/
10619
//! @cond Doxygen_Suppress
10620
bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
10621
                                double /* dfMax */, double /* dfMean */,
10622
                                double /* dfStdDev */,
10623
                                GUInt64 /* nValidCount */,
10624
                                CSLConstList /* papszOptions */)
10625
0
{
10626
0
    CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
10627
0
    return false;
10628
0
}
10629
10630
//! @endcond
10631
10632
/************************************************************************/
10633
/*                          ClearStatistics()                           */
10634
/************************************************************************/
10635
10636
/**
10637
 * \brief Clear statistics.
10638
 *
10639
 * @since GDAL 3.4
10640
 */
10641
void GDALMDArray::ClearStatistics()
10642
0
{
10643
0
}
10644
10645
/************************************************************************/
10646
/*                       GetCoordinateVariables()                       */
10647
/************************************************************************/
10648
10649
/**
10650
 * \brief Return coordinate variables.
10651
 *
10652
 * Coordinate variables are an alternate way of indexing an array that can
10653
 * be sometimes used. For example, an array collected through remote sensing
10654
 * might be indexed by (scanline, pixel). But there can be
10655
 * a longitude and latitude arrays alongside that are also both indexed by
10656
 * (scanline, pixel), and are referenced from operational arrays for
10657
 * reprojection purposes.
10658
 *
10659
 * For netCDF, this will return the arrays referenced by the "coordinates"
10660
 * attribute.
10661
 *
10662
 * This method is the same as the C function
10663
 * GDALMDArrayGetCoordinateVariables().
10664
 *
10665
 * @return a vector of arrays
10666
 *
10667
 * @since GDAL 3.4
10668
 */
10669
10670
std::vector<std::shared_ptr<GDALMDArray>>
10671
GDALMDArray::GetCoordinateVariables() const
10672
0
{
10673
0
    return {};
10674
0
}
10675
10676
/************************************************************************/
10677
/*                       ~GDALExtendedDataType()                        */
10678
/************************************************************************/
10679
10680
0
GDALExtendedDataType::~GDALExtendedDataType() = default;
10681
10682
/************************************************************************/
10683
/*                        GDALExtendedDataType()                        */
10684
/************************************************************************/
10685
10686
GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
10687
                                           GDALExtendedDataTypeSubType eSubType)
10688
0
    : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
10689
0
      m_nMaxStringLength(nMaxStringLength)
10690
0
{
10691
0
}
10692
10693
/************************************************************************/
10694
/*                        GDALExtendedDataType()                        */
10695
/************************************************************************/
10696
10697
GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
10698
0
    : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
10699
0
      m_nSize(GDALGetDataTypeSizeBytes(eType))
10700
0
{
10701
0
}
10702
10703
/************************************************************************/
10704
/*                        GDALExtendedDataType()                        */
10705
/************************************************************************/
10706
10707
GDALExtendedDataType::GDALExtendedDataType(
10708
    const std::string &osName, GDALDataType eBaseType,
10709
    std::unique_ptr<GDALRasterAttributeTable> poRAT)
10710
0
    : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
10711
0
      m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
10712
0
{
10713
0
}
10714
10715
/************************************************************************/
10716
/*                        GDALExtendedDataType()                        */
10717
/************************************************************************/
10718
10719
GDALExtendedDataType::GDALExtendedDataType(
10720
    const std::string &osName, size_t nTotalSize,
10721
    std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10722
0
    : m_osName(osName), m_eClass(GEDTC_COMPOUND),
10723
0
      m_aoComponents(std::move(components)), m_nSize(nTotalSize)
10724
0
{
10725
0
}
10726
10727
/************************************************************************/
10728
/*                        GDALExtendedDataType()                        */
10729
/************************************************************************/
10730
10731
/** Move constructor. */
10732
0
GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
10733
10734
/************************************************************************/
10735
/*                        GDALExtendedDataType()                        */
10736
/************************************************************************/
10737
10738
/** Copy constructor. */
10739
GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
10740
0
    : m_osName(other.m_osName), m_eClass(other.m_eClass),
10741
0
      m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
10742
0
      m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
10743
0
      m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
10744
0
{
10745
0
    if (m_eClass == GEDTC_COMPOUND)
10746
0
    {
10747
0
        for (const auto &elt : other.m_aoComponents)
10748
0
        {
10749
0
            m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10750
0
        }
10751
0
    }
10752
0
}
10753
10754
/************************************************************************/
10755
/*                             operator= ()                             */
10756
/************************************************************************/
10757
10758
/** Copy assignment. */
10759
GDALExtendedDataType &
10760
GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
10761
0
{
10762
0
    if (this != &other)
10763
0
    {
10764
0
        m_osName = other.m_osName;
10765
0
        m_eClass = other.m_eClass;
10766
0
        m_eSubType = other.m_eSubType;
10767
0
        m_eNumericDT = other.m_eNumericDT;
10768
0
        m_nSize = other.m_nSize;
10769
0
        m_nMaxStringLength = other.m_nMaxStringLength;
10770
0
        m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
10771
0
        m_aoComponents.clear();
10772
0
        if (m_eClass == GEDTC_COMPOUND)
10773
0
        {
10774
0
            for (const auto &elt : other.m_aoComponents)
10775
0
            {
10776
0
                m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
10777
0
            }
10778
0
        }
10779
0
    }
10780
0
    return *this;
10781
0
}
10782
10783
/************************************************************************/
10784
/*                             operator= ()                             */
10785
/************************************************************************/
10786
10787
/** Move assignment. */
10788
GDALExtendedDataType &
10789
0
GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
10790
10791
/************************************************************************/
10792
/*                               Create()                               */
10793
/************************************************************************/
10794
10795
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
10796
 *
10797
 * This is the same as the C function GDALExtendedDataTypeCreate()
10798
 *
10799
 * @param eType Numeric data type. Must be different from GDT_Unknown and
10800
 * GDT_TypeCount
10801
 */
10802
GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
10803
0
{
10804
0
    return GDALExtendedDataType(eType);
10805
0
}
10806
10807
/************************************************************************/
10808
/*                               Create()                               */
10809
/************************************************************************/
10810
10811
/** Return a new GDALExtendedDataType from a raster attribute table.
10812
 *
10813
 * @param osName Type name
10814
 * @param eBaseType Base integer data type.
10815
 * @param poRAT Raster attribute table. Must not be NULL.
10816
 * @since 3.12
10817
 */
10818
GDALExtendedDataType
10819
GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
10820
                             std::unique_ptr<GDALRasterAttributeTable> poRAT)
10821
0
{
10822
0
    return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
10823
0
}
10824
10825
/************************************************************************/
10826
/*                               Create()                               */
10827
/************************************************************************/
10828
10829
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
10830
 *
10831
 * This is the same as the C function GDALExtendedDataTypeCreateCompound()
10832
 *
10833
 * @param osName Type name.
10834
 * @param nTotalSize Total size of the type in bytes.
10835
 *                   Should be large enough to store all components.
10836
 * @param components Components of the compound type.
10837
 */
10838
GDALExtendedDataType GDALExtendedDataType::Create(
10839
    const std::string &osName, size_t nTotalSize,
10840
    std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
10841
0
{
10842
0
    size_t nLastOffset = 0;
10843
    // Some arbitrary threshold to avoid potential integer overflows
10844
0
    if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
10845
0
    {
10846
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10847
0
        return GDALExtendedDataType(GDT_Unknown);
10848
0
    }
10849
0
    for (const auto &comp : components)
10850
0
    {
10851
        // Check alignment too ?
10852
0
        if (comp->GetOffset() < nLastOffset)
10853
0
        {
10854
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10855
0
            return GDALExtendedDataType(GDT_Unknown);
10856
0
        }
10857
0
        nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
10858
0
    }
10859
0
    if (nTotalSize < nLastOffset)
10860
0
    {
10861
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
10862
0
        return GDALExtendedDataType(GDT_Unknown);
10863
0
    }
10864
0
    if (nTotalSize == 0 || components.empty())
10865
0
    {
10866
0
        CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
10867
0
        return GDALExtendedDataType(GDT_Unknown);
10868
0
    }
10869
0
    return GDALExtendedDataType(osName, nTotalSize, std::move(components));
10870
0
}
10871
10872
/************************************************************************/
10873
/*                               Create()                               */
10874
/************************************************************************/
10875
10876
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
10877
 *
10878
 * This is the same as the C function GDALExtendedDataTypeCreateString().
10879
 *
10880
 * @param nMaxStringLength maximum length of a string in bytes. 0 if
10881
 * unknown/unlimited
10882
 * @param eSubType Subtype.
10883
 */
10884
GDALExtendedDataType
10885
GDALExtendedDataType::CreateString(size_t nMaxStringLength,
10886
                                   GDALExtendedDataTypeSubType eSubType)
10887
0
{
10888
0
    return GDALExtendedDataType(nMaxStringLength, eSubType);
10889
0
}
10890
10891
/************************************************************************/
10892
/*                             operator==()                             */
10893
/************************************************************************/
10894
10895
/** Equality operator.
10896
 *
10897
 * This is the same as the C function GDALExtendedDataTypeEquals().
10898
 */
10899
bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
10900
0
{
10901
0
    if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
10902
0
        m_nSize != other.m_nSize || m_osName != other.m_osName)
10903
0
    {
10904
0
        return false;
10905
0
    }
10906
0
    if (m_eClass == GEDTC_NUMERIC)
10907
0
    {
10908
0
        return m_eNumericDT == other.m_eNumericDT;
10909
0
    }
10910
0
    if (m_eClass == GEDTC_STRING)
10911
0
    {
10912
0
        return true;
10913
0
    }
10914
0
    CPLAssert(m_eClass == GEDTC_COMPOUND);
10915
0
    if (m_aoComponents.size() != other.m_aoComponents.size())
10916
0
    {
10917
0
        return false;
10918
0
    }
10919
0
    for (size_t i = 0; i < m_aoComponents.size(); i++)
10920
0
    {
10921
0
        if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
10922
0
        {
10923
0
            return false;
10924
0
        }
10925
0
    }
10926
0
    return true;
10927
0
}
10928
10929
/************************************************************************/
10930
/*                            CanConvertTo()                            */
10931
/************************************************************************/
10932
10933
/** Return whether this data type can be converted to the other one.
10934
 *
10935
 * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
10936
 *
10937
 * @param other Target data type for the conversion being considered.
10938
 */
10939
bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
10940
0
{
10941
0
    if (m_eClass == GEDTC_NUMERIC)
10942
0
    {
10943
0
        if (m_eNumericDT == GDT_Unknown)
10944
0
            return false;
10945
0
        if (other.m_eClass == GEDTC_NUMERIC &&
10946
0
            other.m_eNumericDT == GDT_Unknown)
10947
0
            return false;
10948
0
        return other.m_eClass == GEDTC_NUMERIC ||
10949
0
               other.m_eClass == GEDTC_STRING;
10950
0
    }
10951
0
    if (m_eClass == GEDTC_STRING)
10952
0
    {
10953
0
        return other.m_eClass == m_eClass;
10954
0
    }
10955
0
    CPLAssert(m_eClass == GEDTC_COMPOUND);
10956
0
    if (other.m_eClass != GEDTC_COMPOUND)
10957
0
        return false;
10958
0
    std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
10959
0
        srcComponents;
10960
0
    for (const auto &srcComp : m_aoComponents)
10961
0
    {
10962
0
        srcComponents[srcComp->GetName()] = &srcComp;
10963
0
    }
10964
0
    for (const auto &dstComp : other.m_aoComponents)
10965
0
    {
10966
0
        auto oIter = srcComponents.find(dstComp->GetName());
10967
0
        if (oIter == srcComponents.end())
10968
0
            return false;
10969
0
        if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
10970
0
            return false;
10971
0
    }
10972
0
    return true;
10973
0
}
10974
10975
/************************************************************************/
10976
/*                       NeedsFreeDynamicMemory()                       */
10977
/************************************************************************/
10978
10979
/** Return whether the data type holds dynamically allocated memory, that
10980
 * needs to be freed with FreeDynamicMemory().
10981
 *
10982
 */
10983
bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
10984
0
{
10985
0
    switch (m_eClass)
10986
0
    {
10987
0
        case GEDTC_STRING:
10988
0
            return true;
10989
10990
0
        case GEDTC_NUMERIC:
10991
0
            return false;
10992
10993
0
        case GEDTC_COMPOUND:
10994
0
        {
10995
0
            for (const auto &comp : m_aoComponents)
10996
0
            {
10997
0
                if (comp->GetType().NeedsFreeDynamicMemory())
10998
0
                    return true;
10999
0
            }
11000
0
        }
11001
0
    }
11002
0
    return false;
11003
0
}
11004
11005
/************************************************************************/
11006
/*                         FreeDynamicMemory()                          */
11007
/************************************************************************/
11008
11009
/** Release the dynamic memory (strings typically) from a raw value.
11010
 *
11011
 * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
11012
 *
11013
 * @param pBuffer Raw buffer of a single element of an attribute or array value.
11014
 */
11015
void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
11016
0
{
11017
0
    switch (m_eClass)
11018
0
    {
11019
0
        case GEDTC_STRING:
11020
0
        {
11021
0
            char *pszStr;
11022
0
            memcpy(&pszStr, pBuffer, sizeof(char *));
11023
0
            if (pszStr)
11024
0
            {
11025
0
                VSIFree(pszStr);
11026
0
            }
11027
0
            break;
11028
0
        }
11029
11030
0
        case GEDTC_NUMERIC:
11031
0
        {
11032
0
            break;
11033
0
        }
11034
11035
0
        case GEDTC_COMPOUND:
11036
0
        {
11037
0
            GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
11038
0
            for (const auto &comp : m_aoComponents)
11039
0
            {
11040
0
                comp->GetType().FreeDynamicMemory(pabyBuffer +
11041
0
                                                  comp->GetOffset());
11042
0
            }
11043
0
            break;
11044
0
        }
11045
0
    }
11046
0
}
11047
11048
/************************************************************************/
11049
/*                         ~GDALEDTComponent()                          */
11050
/************************************************************************/
11051
11052
0
GDALEDTComponent::~GDALEDTComponent() = default;
11053
11054
/************************************************************************/
11055
/*                          GDALEDTComponent()                          */
11056
/************************************************************************/
11057
11058
/** constructor of a GDALEDTComponent
11059
 *
11060
 * This is the same as the C function GDALEDTComponendCreate()
11061
 *
11062
 * @param name Component name
11063
 * @param offset Offset in byte of the component in the compound data type.
11064
 *               In case of nesting of compound data type, this should be
11065
 *               the offset to the immediate belonging data type, not to the
11066
 *               higher level one.
11067
 * @param type   Component data type.
11068
 */
11069
GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
11070
                                   const GDALExtendedDataType &type)
11071
0
    : m_osName(name), m_nOffset(offset), m_oType(type)
11072
0
{
11073
0
}
11074
11075
/************************************************************************/
11076
/*                          GDALEDTComponent()                          */
11077
/************************************************************************/
11078
11079
/** Copy constructor. */
11080
0
GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
11081
11082
/************************************************************************/
11083
/*                             operator==()                             */
11084
/************************************************************************/
11085
11086
/** Equality operator.
11087
 */
11088
bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
11089
0
{
11090
0
    return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
11091
0
           m_oType == other.m_oType;
11092
0
}
11093
11094
/************************************************************************/
11095
/*                           ~GDALDimension()                           */
11096
/************************************************************************/
11097
11098
0
GDALDimension::~GDALDimension() = default;
11099
11100
/************************************************************************/
11101
/*                           GDALDimension()                            */
11102
/************************************************************************/
11103
11104
//! @cond Doxygen_Suppress
11105
/** Constructor.
11106
 *
11107
 * @param osParentName Parent name
11108
 * @param osName name
11109
 * @param osType type. See GetType().
11110
 * @param osDirection direction. See GetDirection().
11111
 * @param nSize size.
11112
 */
11113
GDALDimension::GDALDimension(const std::string &osParentName,
11114
                             const std::string &osName,
11115
                             const std::string &osType,
11116
                             const std::string &osDirection, GUInt64 nSize)
11117
0
    : m_osName(osName),
11118
      m_osFullName(
11119
0
          !osParentName.empty()
11120
0
              ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
11121
0
              : osName),
11122
0
      m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
11123
0
{
11124
0
}
11125
11126
//! @endcond
11127
11128
/************************************************************************/
11129
/*                        GetIndexingVariable()                         */
11130
/************************************************************************/
11131
11132
/** Return the variable that is used to index the dimension (if there is one).
11133
 *
11134
 * This is the array, typically one-dimensional, describing the values taken
11135
 * by the dimension.
11136
 */
11137
std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
11138
0
{
11139
0
    return nullptr;
11140
0
}
11141
11142
/************************************************************************/
11143
/*                        SetIndexingVariable()                         */
11144
/************************************************************************/
11145
11146
/** Set the variable that is used to index the dimension.
11147
 *
11148
 * This is the array, typically one-dimensional, describing the values taken
11149
 * by the dimension.
11150
 *
11151
 * Optionally implemented by drivers.
11152
 *
11153
 * Drivers known to implement it: MEM.
11154
 *
11155
 * @param poArray Variable to use to index the dimension.
11156
 * @return true in case of success.
11157
 */
11158
bool GDALDimension::SetIndexingVariable(
11159
    CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
11160
0
{
11161
0
    CPLError(CE_Failure, CPLE_NotSupported,
11162
0
             "SetIndexingVariable() not implemented");
11163
0
    return false;
11164
0
}
11165
11166
/************************************************************************/
11167
/*                               Rename()                               */
11168
/************************************************************************/
11169
11170
/** Rename the dimension.
11171
 *
11172
 * This is not implemented by all drivers.
11173
 *
11174
 * Drivers known to implement it: MEM, netCDF, ZARR.
11175
 *
11176
 * This is the same as the C function GDALDimensionRename().
11177
 *
11178
 * @param osNewName New name.
11179
 *
11180
 * @return true in case of success
11181
 * @since GDAL 3.8
11182
 */
11183
bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
11184
0
{
11185
0
    CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
11186
0
    return false;
11187
0
}
11188
11189
/************************************************************************/
11190
/*                             BaseRename()                             */
11191
/************************************************************************/
11192
11193
//! @cond Doxygen_Suppress
11194
void GDALDimension::BaseRename(const std::string &osNewName)
11195
0
{
11196
0
    m_osFullName.resize(m_osFullName.size() - m_osName.size());
11197
0
    m_osFullName += osNewName;
11198
0
    m_osName = osNewName;
11199
0
}
11200
11201
//! @endcond
11202
11203
//! @cond Doxygen_Suppress
11204
/************************************************************************/
11205
/*                           ParentRenamed()                            */
11206
/************************************************************************/
11207
11208
void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
11209
0
{
11210
0
    m_osFullName = osNewParentFullName;
11211
0
    m_osFullName += "/";
11212
0
    m_osFullName += m_osName;
11213
0
}
11214
11215
//! @endcond
11216
11217
//! @cond Doxygen_Suppress
11218
/************************************************************************/
11219
/*                           ParentDeleted()                            */
11220
/************************************************************************/
11221
11222
void GDALDimension::ParentDeleted()
11223
0
{
11224
0
}
11225
11226
//! @endcond
11227
11228
/************************************************************************/
11229
/************************************************************************/
11230
/************************************************************************/
11231
/*                              C API                                   */
11232
/************************************************************************/
11233
/************************************************************************/
11234
/************************************************************************/
11235
11236
/************************************************************************/
11237
/*                     GDALExtendedDataTypeCreate()                     */
11238
/************************************************************************/
11239
11240
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
11241
 *
11242
 * This is the same as the C++ method GDALExtendedDataType::Create()
11243
 *
11244
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11245
 *
11246
 * @param eType Numeric data type. Must be different from GDT_Unknown and
11247
 * GDT_TypeCount
11248
 *
11249
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11250
 */
11251
GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
11252
0
{
11253
0
    if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
11254
0
    {
11255
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11256
0
                 "Illegal GDT_Unknown/GDT_TypeCount argument");
11257
0
        return nullptr;
11258
0
    }
11259
0
    return new GDALExtendedDataTypeHS(
11260
0
        new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
11261
0
}
11262
11263
/************************************************************************/
11264
/*                  GDALExtendedDataTypeCreateString()                  */
11265
/************************************************************************/
11266
11267
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
11268
 *
11269
 * This is the same as the C++ method GDALExtendedDataType::CreateString()
11270
 *
11271
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11272
 *
11273
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11274
 */
11275
GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
11276
0
{
11277
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11278
0
        GDALExtendedDataType::CreateString(nMaxStringLength)));
11279
0
}
11280
11281
/************************************************************************/
11282
/*                 GDALExtendedDataTypeCreateStringEx()                 */
11283
/************************************************************************/
11284
11285
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
11286
 *
11287
 * This is the same as the C++ method GDALExtendedDataType::CreateString()
11288
 *
11289
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11290
 *
11291
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11292
 * @since GDAL 3.4
11293
 */
11294
GDALExtendedDataTypeH
11295
GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
11296
                                   GDALExtendedDataTypeSubType eSubType)
11297
0
{
11298
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
11299
0
        GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
11300
0
}
11301
11302
/************************************************************************/
11303
/*                 GDALExtendedDataTypeCreateCompound()                 */
11304
/************************************************************************/
11305
11306
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
11307
 *
11308
 * This is the same as the C++ method GDALExtendedDataType::Create(const
11309
 * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
11310
 *
11311
 * The returned handle should be freed with GDALExtendedDataTypeRelease().
11312
 *
11313
 * @param pszName Type name.
11314
 * @param nTotalSize Total size of the type in bytes.
11315
 *                   Should be large enough to store all components.
11316
 * @param nComponents Number of components in comps array.
11317
 * @param comps Components.
11318
 * @return a new GDALExtendedDataTypeH handle, or nullptr.
11319
 */
11320
GDALExtendedDataTypeH
11321
GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
11322
                                   size_t nComponents,
11323
                                   const GDALEDTComponentH *comps)
11324
0
{
11325
0
    std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
11326
0
    for (size_t i = 0; i < nComponents; i++)
11327
0
    {
11328
0
        compsCpp.emplace_back(
11329
0
            std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
11330
0
    }
11331
0
    auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
11332
0
                                           std::move(compsCpp));
11333
0
    if (dt.GetClass() != GEDTC_COMPOUND)
11334
0
        return nullptr;
11335
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
11336
0
}
11337
11338
/************************************************************************/
11339
/*                    GDALExtendedDataTypeRelease()                     */
11340
/************************************************************************/
11341
11342
/** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
11343
 *
11344
 * Note: when applied on a object coming from a driver, this does not
11345
 * destroy the object in the file, database, etc...
11346
 */
11347
void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
11348
0
{
11349
0
    delete hEDT;
11350
0
}
11351
11352
/************************************************************************/
11353
/*                    GDALExtendedDataTypeGetName()                     */
11354
/************************************************************************/
11355
11356
/** Return type name.
11357
 *
11358
 * This is the same as the C++ method GDALExtendedDataType::GetName()
11359
 */
11360
const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
11361
0
{
11362
0
    VALIDATE_POINTER1(hEDT, __func__, "");
11363
0
    return hEDT->m_poImpl->GetName().c_str();
11364
0
}
11365
11366
/************************************************************************/
11367
/*                    GDALExtendedDataTypeGetClass()                    */
11368
/************************************************************************/
11369
11370
/** Return type class.
11371
 *
11372
 * This is the same as the C++ method GDALExtendedDataType::GetClass()
11373
 */
11374
GDALExtendedDataTypeClass
11375
GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
11376
0
{
11377
0
    VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
11378
0
    return hEDT->m_poImpl->GetClass();
11379
0
}
11380
11381
/************************************************************************/
11382
/*               GDALExtendedDataTypeGetNumericDataType()               */
11383
/************************************************************************/
11384
11385
/** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
11386
 *
11387
 * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
11388
 */
11389
GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
11390
0
{
11391
0
    VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
11392
0
    return hEDT->m_poImpl->GetNumericDataType();
11393
0
}
11394
11395
/************************************************************************/
11396
/*                    GDALExtendedDataTypeGetSize()                     */
11397
/************************************************************************/
11398
11399
/** Return data type size in bytes.
11400
 *
11401
 * This is the same as the C++ method GDALExtendedDataType::GetSize()
11402
 */
11403
size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
11404
0
{
11405
0
    VALIDATE_POINTER1(hEDT, __func__, 0);
11406
0
    return hEDT->m_poImpl->GetSize();
11407
0
}
11408
11409
/************************************************************************/
11410
/*               GDALExtendedDataTypeGetMaxStringLength()               */
11411
/************************************************************************/
11412
11413
/** Return the maximum length of a string in bytes.
11414
 *
11415
 * 0 indicates unknown/unlimited string.
11416
 *
11417
 * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
11418
 */
11419
size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
11420
0
{
11421
0
    VALIDATE_POINTER1(hEDT, __func__, 0);
11422
0
    return hEDT->m_poImpl->GetMaxStringLength();
11423
0
}
11424
11425
/************************************************************************/
11426
/*                  GDALExtendedDataTypeCanConvertTo()                  */
11427
/************************************************************************/
11428
11429
/** Return whether this data type can be converted to the other one.
11430
 *
11431
 * This is the same as the C function GDALExtendedDataType::CanConvertTo()
11432
 *
11433
 * @param hSourceEDT Source data type for the conversion being considered.
11434
 * @param hTargetEDT Target data type for the conversion being considered.
11435
 * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
11436
 */
11437
int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
11438
                                     GDALExtendedDataTypeH hTargetEDT)
11439
0
{
11440
0
    VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
11441
0
    VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
11442
0
    return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
11443
0
}
11444
11445
/************************************************************************/
11446
/*                     GDALExtendedDataTypeEquals()                     */
11447
/************************************************************************/
11448
11449
/** Return whether this data type is equal to another one.
11450
 *
11451
 * This is the same as the C++ method GDALExtendedDataType::operator==()
11452
 *
11453
 * @param hFirstEDT First data type.
11454
 * @param hSecondEDT Second data type.
11455
 * @return TRUE if they are equal. FALSE otherwise.
11456
 */
11457
int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
11458
                               GDALExtendedDataTypeH hSecondEDT)
11459
0
{
11460
0
    VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
11461
0
    VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
11462
0
    return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
11463
0
}
11464
11465
/************************************************************************/
11466
/*                   GDALExtendedDataTypeGetSubType()                   */
11467
/************************************************************************/
11468
11469
/** Return the subtype of a type.
11470
 *
11471
 * This is the same as the C++ method GDALExtendedDataType::GetSubType()
11472
 *
11473
 * @param hEDT Data type.
11474
 * @return subtype.
11475
 * @since 3.4
11476
 */
11477
GDALExtendedDataTypeSubType
11478
GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
11479
0
{
11480
0
    VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
11481
0
    return hEDT->m_poImpl->GetSubType();
11482
0
}
11483
11484
/************************************************************************/
11485
/*                     GDALExtendedDataTypeGetRAT()                     */
11486
/************************************************************************/
11487
11488
/** Return associated raster attribute table, when there is one.
11489
 *
11490
 * * For the netCDF driver, the RAT will capture enumerated types, with
11491
 * a "value" column with an integer value and a "name" column with the
11492
 * associated name.
11493
 * This is the same as the C++ method GDALExtendedDataType::GetRAT()
11494
 *
11495
 * @param hEDT Data type.
11496
 * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
11497
 * @since 3.12
11498
 */
11499
GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
11500
0
{
11501
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
11502
0
    return GDALRasterAttributeTable::ToHandle(
11503
0
        const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
11504
0
}
11505
11506
/************************************************************************/
11507
/*                 GDALExtendedDataTypeGetComponents()                  */
11508
/************************************************************************/
11509
11510
/** Return the components of the data type (only valid when GetClass() ==
11511
 * GEDTC_COMPOUND)
11512
 *
11513
 * The returned array and its content must be freed with
11514
 * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
11515
 * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
11516
 * individual array members).
11517
 *
11518
 * This is the same as the C++ method GDALExtendedDataType::GetComponents()
11519
 *
11520
 * @param hEDT Data type
11521
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11522
 * @return an array of *pnCount components.
11523
 */
11524
GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
11525
                                                     size_t *pnCount)
11526
0
{
11527
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
11528
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
11529
0
    const auto &components = hEDT->m_poImpl->GetComponents();
11530
0
    auto ret = static_cast<GDALEDTComponentH *>(
11531
0
        CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
11532
0
    for (size_t i = 0; i < components.size(); i++)
11533
0
    {
11534
0
        ret[i] = new GDALEDTComponentHS(*components[i].get());
11535
0
    }
11536
0
    *pnCount = components.size();
11537
0
    return ret;
11538
0
}
11539
11540
/************************************************************************/
11541
/*                 GDALExtendedDataTypeFreeComponents()                 */
11542
/************************************************************************/
11543
11544
/** Free the return of GDALExtendedDataTypeGetComponents().
11545
 *
11546
 * @param components return value of GDALExtendedDataTypeGetComponents()
11547
 * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
11548
 */
11549
void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
11550
                                        size_t nCount)
11551
0
{
11552
0
    for (size_t i = 0; i < nCount; i++)
11553
0
    {
11554
0
        delete components[i];
11555
0
    }
11556
0
    CPLFree(components);
11557
0
}
11558
11559
/************************************************************************/
11560
/*                       GDALEDTComponentCreate()                       */
11561
/************************************************************************/
11562
11563
/** Create a new GDALEDTComponent.
11564
 *
11565
 * The returned value must be freed with GDALEDTComponentRelease().
11566
 *
11567
 * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
11568
 */
11569
GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
11570
                                         GDALExtendedDataTypeH hType)
11571
0
{
11572
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11573
0
    VALIDATE_POINTER1(hType, __func__, nullptr);
11574
0
    return new GDALEDTComponentHS(
11575
0
        GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
11576
0
}
11577
11578
/************************************************************************/
11579
/*                      GDALEDTComponentRelease()                       */
11580
/************************************************************************/
11581
11582
/** Release the GDAL in-memory object associated with a GDALEDTComponentH.
11583
 *
11584
 * Note: when applied on a object coming from a driver, this does not
11585
 * destroy the object in the file, database, etc...
11586
 */
11587
void GDALEDTComponentRelease(GDALEDTComponentH hComp)
11588
0
{
11589
0
    delete hComp;
11590
0
}
11591
11592
/************************************************************************/
11593
/*                      GDALEDTComponentGetName()                       */
11594
/************************************************************************/
11595
11596
/** Return the name.
11597
 *
11598
 * The returned pointer is valid until hComp is released.
11599
 *
11600
 * This is the same as the C++ method GDALEDTComponent::GetName().
11601
 */
11602
const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
11603
0
{
11604
0
    VALIDATE_POINTER1(hComp, __func__, nullptr);
11605
0
    return hComp->m_poImpl->GetName().c_str();
11606
0
}
11607
11608
/************************************************************************/
11609
/*                     GDALEDTComponentGetOffset()                      */
11610
/************************************************************************/
11611
11612
/** Return the offset (in bytes) of the component in the compound data type.
11613
 *
11614
 * This is the same as the C++ method GDALEDTComponent::GetOffset().
11615
 */
11616
size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
11617
0
{
11618
0
    VALIDATE_POINTER1(hComp, __func__, 0);
11619
0
    return hComp->m_poImpl->GetOffset();
11620
0
}
11621
11622
/************************************************************************/
11623
/*                      GDALEDTComponentGetType()                       */
11624
/************************************************************************/
11625
11626
/** Return the data type of the component.
11627
 *
11628
 * This is the same as the C++ method GDALEDTComponent::GetType().
11629
 */
11630
GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
11631
0
{
11632
0
    VALIDATE_POINTER1(hComp, __func__, nullptr);
11633
0
    return new GDALExtendedDataTypeHS(
11634
0
        new GDALExtendedDataType(hComp->m_poImpl->GetType()));
11635
0
}
11636
11637
/************************************************************************/
11638
/*                          GDALGroupRelease()                          */
11639
/************************************************************************/
11640
11641
/** Release the GDAL in-memory object associated with a GDALGroupH.
11642
 *
11643
 * Note: when applied on a object coming from a driver, this does not
11644
 * destroy the object in the file, database, etc...
11645
 */
11646
void GDALGroupRelease(GDALGroupH hGroup)
11647
0
{
11648
0
    delete hGroup;
11649
0
}
11650
11651
/************************************************************************/
11652
/*                          GDALGroupGetName()                          */
11653
/************************************************************************/
11654
11655
/** Return the name of the group.
11656
 *
11657
 * The returned pointer is valid until hGroup is released.
11658
 *
11659
 * This is the same as the C++ method GDALGroup::GetName().
11660
 */
11661
const char *GDALGroupGetName(GDALGroupH hGroup)
11662
0
{
11663
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11664
0
    return hGroup->m_poImpl->GetName().c_str();
11665
0
}
11666
11667
/************************************************************************/
11668
/*                        GDALGroupGetFullName()                        */
11669
/************************************************************************/
11670
11671
/** Return the full name of the group.
11672
 *
11673
 * The returned pointer is valid until hGroup is released.
11674
 *
11675
 * This is the same as the C++ method GDALGroup::GetFullName().
11676
 */
11677
const char *GDALGroupGetFullName(GDALGroupH hGroup)
11678
0
{
11679
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11680
0
    return hGroup->m_poImpl->GetFullName().c_str();
11681
0
}
11682
11683
/************************************************************************/
11684
/*                      GDALGroupGetMDArrayNames()                      */
11685
/************************************************************************/
11686
11687
/** Return the list of multidimensional array names contained in this group.
11688
 *
11689
 * This is the same as the C++ method GDALGroup::GetGroupNames().
11690
 *
11691
 * @return the array names, to be freed with CSLDestroy()
11692
 */
11693
char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
11694
0
{
11695
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11696
0
    auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
11697
0
    CPLStringList res;
11698
0
    for (const auto &name : names)
11699
0
    {
11700
0
        res.AddString(name.c_str());
11701
0
    }
11702
0
    return res.StealList();
11703
0
}
11704
11705
/************************************************************************/
11706
/*               GDALGroupGetMDArrayFullNamesRecursive()                */
11707
/************************************************************************/
11708
11709
/** Return the list of multidimensional array full names contained in this
11710
 * group and its subgroups.
11711
 *
11712
 * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
11713
 *
11714
 * @return the array names, to be freed with CSLDestroy()
11715
 *
11716
 * @since 3.11
11717
 */
11718
char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
11719
                                             CSLConstList papszGroupOptions,
11720
                                             CSLConstList papszArrayOptions)
11721
0
{
11722
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11723
0
    auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
11724
0
        papszGroupOptions, papszArrayOptions);
11725
0
    CPLStringList res;
11726
0
    for (const auto &name : names)
11727
0
    {
11728
0
        res.AddString(name.c_str());
11729
0
    }
11730
0
    return res.StealList();
11731
0
}
11732
11733
/************************************************************************/
11734
/*                        GDALGroupOpenMDArray()                        */
11735
/************************************************************************/
11736
11737
/** Open and return a multidimensional array.
11738
 *
11739
 * This is the same as the C++ method GDALGroup::OpenMDArray().
11740
 *
11741
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11742
 */
11743
GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
11744
                                  CSLConstList papszOptions)
11745
0
{
11746
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11747
0
    VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
11748
0
    auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
11749
0
                                               papszOptions);
11750
0
    if (!array)
11751
0
        return nullptr;
11752
0
    return new GDALMDArrayHS(array);
11753
0
}
11754
11755
/************************************************************************/
11756
/*                  GDALGroupOpenMDArrayFromFullname()                  */
11757
/************************************************************************/
11758
11759
/** Open and return a multidimensional array from its fully qualified name.
11760
 *
11761
 * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
11762
 *
11763
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
11764
 *
11765
 * @since GDAL 3.2
11766
 */
11767
GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
11768
                                              const char *pszFullname,
11769
                                              CSLConstList papszOptions)
11770
0
{
11771
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11772
0
    VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11773
0
    auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
11774
0
        std::string(pszFullname), papszOptions);
11775
0
    if (!array)
11776
0
        return nullptr;
11777
0
    return new GDALMDArrayHS(array);
11778
0
}
11779
11780
/************************************************************************/
11781
/*                      GDALGroupResolveMDArray()                       */
11782
/************************************************************************/
11783
11784
/** Locate an array in a group and its subgroups by name.
11785
 *
11786
 * See GDALGroup::ResolveMDArray() for description of the behavior.
11787
 * @since GDAL 3.2
11788
 */
11789
GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
11790
                                     const char *pszStartingPoint,
11791
                                     CSLConstList papszOptions)
11792
0
{
11793
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11794
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11795
0
    VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
11796
0
    auto array = hGroup->m_poImpl->ResolveMDArray(
11797
0
        std::string(pszName), std::string(pszStartingPoint), papszOptions);
11798
0
    if (!array)
11799
0
        return nullptr;
11800
0
    return new GDALMDArrayHS(array);
11801
0
}
11802
11803
/************************************************************************/
11804
/*                       GDALGroupGetGroupNames()                       */
11805
/************************************************************************/
11806
11807
/** Return the list of sub-groups contained in this group.
11808
 *
11809
 * This is the same as the C++ method GDALGroup::GetGroupNames().
11810
 *
11811
 * @return the group names, to be freed with CSLDestroy()
11812
 */
11813
char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
11814
0
{
11815
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11816
0
    auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
11817
0
    CPLStringList res;
11818
0
    for (const auto &name : names)
11819
0
    {
11820
0
        res.AddString(name.c_str());
11821
0
    }
11822
0
    return res.StealList();
11823
0
}
11824
11825
/************************************************************************/
11826
/*                         GDALGroupOpenGroup()                         */
11827
/************************************************************************/
11828
11829
/** Open and return a sub-group.
11830
 *
11831
 * This is the same as the C++ method GDALGroup::OpenGroup().
11832
 *
11833
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11834
 */
11835
GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
11836
                              CSLConstList papszOptions)
11837
0
{
11838
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11839
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
11840
0
    auto subGroup =
11841
0
        hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
11842
0
    if (!subGroup)
11843
0
        return nullptr;
11844
0
    return new GDALGroupHS(subGroup);
11845
0
}
11846
11847
/************************************************************************/
11848
/*                    GDALGroupGetVectorLayerNames()                    */
11849
/************************************************************************/
11850
11851
/** Return the list of layer names contained in this group.
11852
 *
11853
 * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
11854
 *
11855
 * @return the group names, to be freed with CSLDestroy()
11856
 * @since 3.4
11857
 */
11858
char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
11859
                                    CSLConstList papszOptions)
11860
0
{
11861
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11862
0
    auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
11863
0
    CPLStringList res;
11864
0
    for (const auto &name : names)
11865
0
    {
11866
0
        res.AddString(name.c_str());
11867
0
    }
11868
0
    return res.StealList();
11869
0
}
11870
11871
/************************************************************************/
11872
/*                      GDALGroupOpenVectorLayer()                      */
11873
/************************************************************************/
11874
11875
/** Open and return a vector layer.
11876
 *
11877
 * This is the same as the C++ method GDALGroup::OpenVectorLayer().
11878
 *
11879
 * Note that the vector layer is owned by its parent GDALDatasetH, and thus
11880
 * the returned handled if only valid while the parent GDALDatasetH is kept
11881
 * opened.
11882
 *
11883
 * @return the vector layer, or nullptr.
11884
 * @since 3.4
11885
 */
11886
OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
11887
                                   const char *pszVectorLayerName,
11888
                                   CSLConstList papszOptions)
11889
0
{
11890
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11891
0
    VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
11892
0
    return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
11893
0
        std::string(pszVectorLayerName), papszOptions));
11894
0
}
11895
11896
/************************************************************************/
11897
/*                  GDALGroupOpenMDArrayFromFullname()                  */
11898
/************************************************************************/
11899
11900
/** Open and return a sub-group from its fully qualified name.
11901
 *
11902
 * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
11903
 *
11904
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
11905
 *
11906
 * @since GDAL 3.2
11907
 */
11908
GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
11909
                                          const char *pszFullname,
11910
                                          CSLConstList papszOptions)
11911
0
{
11912
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11913
0
    VALIDATE_POINTER1(pszFullname, __func__, nullptr);
11914
0
    auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
11915
0
        std::string(pszFullname), papszOptions);
11916
0
    if (!subGroup)
11917
0
        return nullptr;
11918
0
    return new GDALGroupHS(subGroup);
11919
0
}
11920
11921
/************************************************************************/
11922
/*                       GDALGroupGetDimensions()                       */
11923
/************************************************************************/
11924
11925
/** Return the list of dimensions contained in this group and used by its
11926
 * arrays.
11927
 *
11928
 * The returned array must be freed with GDALReleaseDimensions().  If only the
11929
 * array itself needs to be freed, CPLFree() should be called (and
11930
 * GDALDimensionRelease() on individual array members).
11931
 *
11932
 * This is the same as the C++ method GDALGroup::GetDimensions().
11933
 *
11934
 * @param hGroup Group.
11935
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11936
 * @param papszOptions Driver specific options determining how dimensions
11937
 * should be retrieved. Pass nullptr for default behavior.
11938
 *
11939
 * @return an array of *pnCount dimensions.
11940
 */
11941
GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
11942
                                       CSLConstList papszOptions)
11943
0
{
11944
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11945
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
11946
0
    auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
11947
0
    auto ret = static_cast<GDALDimensionH *>(
11948
0
        CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
11949
0
    for (size_t i = 0; i < dims.size(); i++)
11950
0
    {
11951
0
        ret[i] = new GDALDimensionHS(dims[i]);
11952
0
    }
11953
0
    *pnCount = dims.size();
11954
0
    return ret;
11955
0
}
11956
11957
/************************************************************************/
11958
/*                       GDALGroupGetAttribute()                        */
11959
/************************************************************************/
11960
11961
/** Return an attribute by its name.
11962
 *
11963
 * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
11964
 *
11965
 * The returned attribute must be freed with GDALAttributeRelease().
11966
 */
11967
GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
11968
0
{
11969
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
11970
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
11971
0
    auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
11972
0
    if (attr)
11973
0
        return new GDALAttributeHS(attr);
11974
0
    return nullptr;
11975
0
}
11976
11977
/************************************************************************/
11978
/*                       GDALGroupGetAttributes()                       */
11979
/************************************************************************/
11980
11981
/** Return the list of attributes contained in this group.
11982
 *
11983
 * The returned array must be freed with GDALReleaseAttributes(). If only the
11984
 * array itself needs to be freed, CPLFree() should be called (and
11985
 * GDALAttributeRelease() on individual array members).
11986
 *
11987
 * This is the same as the C++ method GDALGroup::GetAttributes().
11988
 *
11989
 * @param hGroup Group.
11990
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
11991
 * @param papszOptions Driver specific options determining how attributes
11992
 * should be retrieved. Pass nullptr for default behavior.
11993
 *
11994
 * @return an array of *pnCount attributes.
11995
 */
11996
GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
11997
                                       CSLConstList papszOptions)
11998
0
{
11999
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12000
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12001
0
    auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
12002
0
    auto ret = static_cast<GDALAttributeH *>(
12003
0
        CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12004
0
    for (size_t i = 0; i < attrs.size(); i++)
12005
0
    {
12006
0
        ret[i] = new GDALAttributeHS(attrs[i]);
12007
0
    }
12008
0
    *pnCount = attrs.size();
12009
0
    return ret;
12010
0
}
12011
12012
/************************************************************************/
12013
/*                     GDALGroupGetStructuralInfo()                     */
12014
/************************************************************************/
12015
12016
/** Return structural information on the group.
12017
 *
12018
 * This may be the compression, etc..
12019
 *
12020
 * The return value should not be freed and is valid until GDALGroup is
12021
 * released or this function called again.
12022
 *
12023
 * This is the same as the C++ method GDALGroup::GetStructuralInfo().
12024
 */
12025
CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
12026
0
{
12027
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12028
0
    return hGroup->m_poImpl->GetStructuralInfo();
12029
0
}
12030
12031
/************************************************************************/
12032
/*                     GDALGroupGetDataTypeCount()                      */
12033
/************************************************************************/
12034
12035
/** Return the number of data types associated with the group
12036
 * (typically enumerations).
12037
 *
12038
 * This is the same as the C++ method GDALGroup::GetDataTypes().size().
12039
 *
12040
 * @since 3.12
12041
 */
12042
size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
12043
0
{
12044
0
    VALIDATE_POINTER1(hGroup, __func__, 0);
12045
0
    return hGroup->m_poImpl->GetDataTypes().size();
12046
0
}
12047
12048
/************************************************************************/
12049
/*                        GDALGroupGetDataType()                        */
12050
/************************************************************************/
12051
12052
/** Return one of the data types associated with the group.
12053
 *
12054
 * This is the same as the C++ method GDALGroup::GetDataTypes()[].
12055
 *
12056
 * @return a type to release with GDALExtendedDataTypeRelease() once done,
12057
 * or nullptr in case of error.
12058
 * @since 3.12
12059
 */
12060
GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
12061
0
{
12062
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12063
0
    if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
12064
0
        return nullptr;
12065
0
    return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
12066
0
        *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
12067
0
}
12068
12069
/************************************************************************/
12070
/*                       GDALReleaseAttributes()                        */
12071
/************************************************************************/
12072
12073
/** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
12074
 *
12075
 * @param attributes return pointer of above methods
12076
 * @param nCount *pnCount value returned by above methods
12077
 */
12078
void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
12079
0
{
12080
0
    for (size_t i = 0; i < nCount; i++)
12081
0
    {
12082
0
        delete attributes[i];
12083
0
    }
12084
0
    CPLFree(attributes);
12085
0
}
12086
12087
/************************************************************************/
12088
/*                        GDALGroupCreateGroup()                        */
12089
/************************************************************************/
12090
12091
/** Create a sub-group within a group.
12092
 *
12093
 * This is the same as the C++ method GDALGroup::CreateGroup().
12094
 *
12095
 * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
12096
 */
12097
GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12098
                                CSLConstList papszOptions)
12099
0
{
12100
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12101
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
12102
0
    auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
12103
0
                                             papszOptions);
12104
0
    if (!ret)
12105
0
        return nullptr;
12106
0
    return new GDALGroupHS(ret);
12107
0
}
12108
12109
/************************************************************************/
12110
/*                        GDALGroupDeleteGroup()                        */
12111
/************************************************************************/
12112
12113
/** Delete a sub-group from a group.
12114
 *
12115
 * After this call, if a previously obtained instance of the deleted object
12116
 * is still alive, no method other than for freeing it should be invoked.
12117
 *
12118
 * This is the same as the C++ method GDALGroup::DeleteGroup().
12119
 *
12120
 * @return true in case of success.
12121
 * @since GDAL 3.8
12122
 */
12123
bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
12124
                          CSLConstList papszOptions)
12125
0
{
12126
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12127
0
    VALIDATE_POINTER1(pszSubGroupName, __func__, false);
12128
0
    return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
12129
0
                                         papszOptions);
12130
0
}
12131
12132
/************************************************************************/
12133
/*                      GDALGroupCreateDimension()                      */
12134
/************************************************************************/
12135
12136
/** Create a dimension within a group.
12137
 *
12138
 * This is the same as the C++ method GDALGroup::CreateDimension().
12139
 *
12140
 * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
12141
 */
12142
GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
12143
                                        const char *pszType,
12144
                                        const char *pszDirection, GUInt64 nSize,
12145
                                        CSLConstList papszOptions)
12146
0
{
12147
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12148
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12149
0
    auto ret = hGroup->m_poImpl->CreateDimension(
12150
0
        std::string(pszName), std::string(pszType ? pszType : ""),
12151
0
        std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
12152
0
    if (!ret)
12153
0
        return nullptr;
12154
0
    return new GDALDimensionHS(ret);
12155
0
}
12156
12157
/************************************************************************/
12158
/*                       GDALGroupCreateMDArray()                       */
12159
/************************************************************************/
12160
12161
/** Create a multidimensional array within a group.
12162
 *
12163
 * This is the same as the C++ method GDALGroup::CreateMDArray().
12164
 *
12165
 * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
12166
 */
12167
GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
12168
                                    size_t nDimensions,
12169
                                    GDALDimensionH *pahDimensions,
12170
                                    GDALExtendedDataTypeH hEDT,
12171
                                    CSLConstList papszOptions)
12172
0
{
12173
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12174
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12175
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
12176
0
    std::vector<std::shared_ptr<GDALDimension>> dims;
12177
0
    dims.reserve(nDimensions);
12178
0
    for (size_t i = 0; i < nDimensions; i++)
12179
0
        dims.push_back(pahDimensions[i]->m_poImpl);
12180
0
    auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
12181
0
                                               *(hEDT->m_poImpl), papszOptions);
12182
0
    if (!ret)
12183
0
        return nullptr;
12184
0
    return new GDALMDArrayHS(ret);
12185
0
}
12186
12187
/************************************************************************/
12188
/*                       GDALGroupDeleteMDArray()                       */
12189
/************************************************************************/
12190
12191
/** Delete an array from a group.
12192
 *
12193
 * After this call, if a previously obtained instance of the deleted object
12194
 * is still alive, no method other than for freeing it should be invoked.
12195
 *
12196
 * This is the same as the C++ method GDALGroup::DeleteMDArray().
12197
 *
12198
 * @return true in case of success.
12199
 * @since GDAL 3.8
12200
 */
12201
bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
12202
                            CSLConstList papszOptions)
12203
0
{
12204
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12205
0
    VALIDATE_POINTER1(pszName, __func__, false);
12206
0
    return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
12207
0
}
12208
12209
/************************************************************************/
12210
/*                      GDALGroupCreateAttribute()                      */
12211
/************************************************************************/
12212
12213
/** Create a attribute within a group.
12214
 *
12215
 * This is the same as the C++ method GDALGroup::CreateAttribute().
12216
 *
12217
 * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12218
 */
12219
GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
12220
                                        size_t nDimensions,
12221
                                        const GUInt64 *panDimensions,
12222
                                        GDALExtendedDataTypeH hEDT,
12223
                                        CSLConstList papszOptions)
12224
0
{
12225
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12226
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
12227
0
    std::vector<GUInt64> dims;
12228
0
    dims.reserve(nDimensions);
12229
0
    for (size_t i = 0; i < nDimensions; i++)
12230
0
        dims.push_back(panDimensions[i]);
12231
0
    auto ret = hGroup->m_poImpl->CreateAttribute(
12232
0
        std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12233
0
    if (!ret)
12234
0
        return nullptr;
12235
0
    return new GDALAttributeHS(ret);
12236
0
}
12237
12238
/************************************************************************/
12239
/*                      GDALGroupDeleteAttribute()                      */
12240
/************************************************************************/
12241
12242
/** Delete an attribute from a group.
12243
 *
12244
 * After this call, if a previously obtained instance of the deleted object
12245
 * is still alive, no method other than for freeing it should be invoked.
12246
 *
12247
 * This is the same as the C++ method GDALGroup::DeleteAttribute().
12248
 *
12249
 * @return true in case of success.
12250
 * @since GDAL 3.8
12251
 */
12252
bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
12253
                              CSLConstList papszOptions)
12254
0
{
12255
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12256
0
    VALIDATE_POINTER1(pszName, __func__, false);
12257
0
    return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
12258
0
                                             papszOptions);
12259
0
}
12260
12261
/************************************************************************/
12262
/*                          GDALGroupRename()                           */
12263
/************************************************************************/
12264
12265
/** Rename the group.
12266
 *
12267
 * This is not implemented by all drivers.
12268
 *
12269
 * Drivers known to implement it: MEM, netCDF.
12270
 *
12271
 * This is the same as the C++ method GDALGroup::Rename()
12272
 *
12273
 * @return true in case of success
12274
 * @since GDAL 3.8
12275
 */
12276
bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
12277
0
{
12278
0
    VALIDATE_POINTER1(hGroup, __func__, false);
12279
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
12280
0
    return hGroup->m_poImpl->Rename(pszNewName);
12281
0
}
12282
12283
/************************************************************************/
12284
/*               GDALGroupSubsetDimensionFromSelection()                */
12285
/************************************************************************/
12286
12287
/** Return a virtual group whose one dimension has been subset according to a
12288
 * selection.
12289
 *
12290
 * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
12291
 *
12292
 * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
12293
 */
12294
GDALGroupH
12295
GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
12296
                                      const char *pszSelection,
12297
                                      CPL_UNUSED CSLConstList papszOptions)
12298
0
{
12299
0
    VALIDATE_POINTER1(hGroup, __func__, nullptr);
12300
0
    VALIDATE_POINTER1(pszSelection, __func__, nullptr);
12301
0
    auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
12302
0
        std::string(pszSelection));
12303
0
    if (!hNewGroup)
12304
0
        return nullptr;
12305
0
    return new GDALGroupHS(hNewGroup);
12306
0
}
12307
12308
/************************************************************************/
12309
/*                         GDALMDArrayRelease()                         */
12310
/************************************************************************/
12311
12312
/** Release the GDAL in-memory object associated with a GDALMDArray.
12313
 *
12314
 * Note: when applied on a object coming from a driver, this does not
12315
 * destroy the object in the file, database, etc...
12316
 */
12317
void GDALMDArrayRelease(GDALMDArrayH hMDArray)
12318
0
{
12319
0
    delete hMDArray;
12320
0
}
12321
12322
/************************************************************************/
12323
/*                         GDALMDArrayGetName()                         */
12324
/************************************************************************/
12325
12326
/** Return array name.
12327
 *
12328
 * This is the same as the C++ method GDALMDArray::GetName()
12329
 */
12330
const char *GDALMDArrayGetName(GDALMDArrayH hArray)
12331
0
{
12332
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12333
0
    return hArray->m_poImpl->GetName().c_str();
12334
0
}
12335
12336
/************************************************************************/
12337
/*                       GDALMDArrayGetFullName()                       */
12338
/************************************************************************/
12339
12340
/** Return array full name.
12341
 *
12342
 * This is the same as the C++ method GDALMDArray::GetFullName()
12343
 */
12344
const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
12345
0
{
12346
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12347
0
    return hArray->m_poImpl->GetFullName().c_str();
12348
0
}
12349
12350
/************************************************************************/
12351
/*                         GDALMDArrayGetName()                         */
12352
/************************************************************************/
12353
12354
/** Return the total number of values in the array.
12355
 *
12356
 * This is the same as the C++ method
12357
 * GDALAbstractMDArray::GetTotalElementsCount()
12358
 */
12359
GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
12360
0
{
12361
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12362
0
    return hArray->m_poImpl->GetTotalElementsCount();
12363
0
}
12364
12365
/************************************************************************/
12366
/*                    GDALMDArrayGetDimensionCount()                    */
12367
/************************************************************************/
12368
12369
/** Return the number of dimensions.
12370
 *
12371
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
12372
 */
12373
size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
12374
0
{
12375
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12376
0
    return hArray->m_poImpl->GetDimensionCount();
12377
0
}
12378
12379
/************************************************************************/
12380
/*                      GDALMDArrayGetDimensions()                      */
12381
/************************************************************************/
12382
12383
/** Return the dimensions of the array
12384
 *
12385
 * The returned array must be freed with GDALReleaseDimensions(). If only the
12386
 * array itself needs to be freed, CPLFree() should be called (and
12387
 * GDALDimensionRelease() on individual array members).
12388
 *
12389
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
12390
 *
12391
 * @param hArray Array.
12392
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12393
 *
12394
 * @return an array of *pnCount dimensions.
12395
 */
12396
GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
12397
0
{
12398
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12399
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12400
0
    const auto &dims(hArray->m_poImpl->GetDimensions());
12401
0
    auto ret = static_cast<GDALDimensionH *>(
12402
0
        CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
12403
0
    for (size_t i = 0; i < dims.size(); i++)
12404
0
    {
12405
0
        ret[i] = new GDALDimensionHS(dims[i]);
12406
0
    }
12407
0
    *pnCount = dims.size();
12408
0
    return ret;
12409
0
}
12410
12411
/************************************************************************/
12412
/*                       GDALReleaseDimensions()                        */
12413
/************************************************************************/
12414
12415
/** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
12416
 *
12417
 * @param dims return pointer of above methods
12418
 * @param nCount *pnCount value returned by above methods
12419
 */
12420
void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
12421
0
{
12422
0
    for (size_t i = 0; i < nCount; i++)
12423
0
    {
12424
0
        delete dims[i];
12425
0
    }
12426
0
    CPLFree(dims);
12427
0
}
12428
12429
/************************************************************************/
12430
/*                       GDALMDArrayGetDataType()                       */
12431
/************************************************************************/
12432
12433
/** Return the data type
12434
 *
12435
 * The return must be freed with GDALExtendedDataTypeRelease().
12436
 */
12437
GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
12438
0
{
12439
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12440
0
    return new GDALExtendedDataTypeHS(
12441
0
        new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
12442
0
}
12443
12444
/************************************************************************/
12445
/*                          GDALMDArrayRead()                           */
12446
/************************************************************************/
12447
12448
/** Read part or totality of a multidimensional array.
12449
 *
12450
 * This is the same as the C++ method GDALAbstractMDArray::Read()
12451
 *
12452
 * @return TRUE in case of success.
12453
 */
12454
int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12455
                    const size_t *count, const GInt64 *arrayStep,
12456
                    const GPtrDiff_t *bufferStride,
12457
                    GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
12458
                    const void *pDstBufferAllocStart,
12459
                    size_t nDstBufferAllocSize)
12460
0
{
12461
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12462
0
    if ((arrayStartIdx == nullptr || count == nullptr) &&
12463
0
        hArray->m_poImpl->GetDimensionCount() > 0)
12464
0
    {
12465
0
        VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12466
0
        VALIDATE_POINTER1(count, __func__, FALSE);
12467
0
    }
12468
0
    VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12469
0
    VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
12470
0
    return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
12471
0
                                  *(bufferDataType->m_poImpl), pDstBuffer,
12472
0
                                  pDstBufferAllocStart, nDstBufferAllocSize);
12473
0
}
12474
12475
/************************************************************************/
12476
/*                          GDALMDArrayWrite()                          */
12477
/************************************************************************/
12478
12479
/** Write part or totality of a multidimensional array.
12480
 *
12481
 * This is the same as the C++ method GDALAbstractMDArray::Write()
12482
 *
12483
 * @return TRUE in case of success.
12484
 */
12485
int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12486
                     const size_t *count, const GInt64 *arrayStep,
12487
                     const GPtrDiff_t *bufferStride,
12488
                     GDALExtendedDataTypeH bufferDataType,
12489
                     const void *pSrcBuffer, const void *pSrcBufferAllocStart,
12490
                     size_t nSrcBufferAllocSize)
12491
0
{
12492
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12493
0
    if ((arrayStartIdx == nullptr || count == nullptr) &&
12494
0
        hArray->m_poImpl->GetDimensionCount() > 0)
12495
0
    {
12496
0
        VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
12497
0
        VALIDATE_POINTER1(count, __func__, FALSE);
12498
0
    }
12499
0
    VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
12500
0
    VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
12501
0
    return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
12502
0
                                   bufferStride, *(bufferDataType->m_poImpl),
12503
0
                                   pSrcBuffer, pSrcBufferAllocStart,
12504
0
                                   nSrcBufferAllocSize);
12505
0
}
12506
12507
/************************************************************************/
12508
/*                       GDALMDArrayAdviseRead()                        */
12509
/************************************************************************/
12510
12511
/** Advise driver of upcoming read requests.
12512
 *
12513
 * This is the same as the C++ method GDALMDArray::AdviseRead()
12514
 *
12515
 * @return TRUE in case of success.
12516
 *
12517
 * @since GDAL 3.2
12518
 */
12519
int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12520
                          const size_t *count)
12521
0
{
12522
0
    return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
12523
0
}
12524
12525
/************************************************************************/
12526
/*                      GDALMDArrayAdviseReadEx()                       */
12527
/************************************************************************/
12528
12529
/** Advise driver of upcoming read requests.
12530
 *
12531
 * This is the same as the C++ method GDALMDArray::AdviseRead()
12532
 *
12533
 * @return TRUE in case of success.
12534
 *
12535
 * @since GDAL 3.4
12536
 */
12537
int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
12538
                            const size_t *count, CSLConstList papszOptions)
12539
0
{
12540
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12541
0
    return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
12542
0
}
12543
12544
/************************************************************************/
12545
/*                      GDALMDArrayGetAttribute()                       */
12546
/************************************************************************/
12547
12548
/** Return an attribute by its name.
12549
 *
12550
 * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
12551
 *
12552
 * The returned attribute must be freed with GDALAttributeRelease().
12553
 */
12554
GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
12555
0
{
12556
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12557
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12558
0
    auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
12559
0
    if (attr)
12560
0
        return new GDALAttributeHS(attr);
12561
0
    return nullptr;
12562
0
}
12563
12564
/************************************************************************/
12565
/*                      GDALMDArrayGetAttributes()                      */
12566
/************************************************************************/
12567
12568
/** Return the list of attributes contained in this array.
12569
 *
12570
 * The returned array must be freed with GDALReleaseAttributes(). If only the
12571
 * array itself needs to be freed, CPLFree() should be called (and
12572
 * GDALAttributeRelease() on individual array members).
12573
 *
12574
 * This is the same as the C++ method GDALMDArray::GetAttributes().
12575
 *
12576
 * @param hArray Array.
12577
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
12578
 * @param papszOptions Driver specific options determining how attributes
12579
 * should be retrieved. Pass nullptr for default behavior.
12580
 *
12581
 * @return an array of *pnCount attributes.
12582
 */
12583
GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
12584
                                         CSLConstList papszOptions)
12585
0
{
12586
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12587
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
12588
0
    auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
12589
0
    auto ret = static_cast<GDALAttributeH *>(
12590
0
        CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
12591
0
    for (size_t i = 0; i < attrs.size(); i++)
12592
0
    {
12593
0
        ret[i] = new GDALAttributeHS(attrs[i]);
12594
0
    }
12595
0
    *pnCount = attrs.size();
12596
0
    return ret;
12597
0
}
12598
12599
/************************************************************************/
12600
/*                     GDALMDArrayCreateAttribute()                     */
12601
/************************************************************************/
12602
12603
/** Create a attribute within an array.
12604
 *
12605
 * This is the same as the C++ method GDALMDArray::CreateAttribute().
12606
 *
12607
 * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
12608
 */
12609
GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
12610
                                          const char *pszName,
12611
                                          size_t nDimensions,
12612
                                          const GUInt64 *panDimensions,
12613
                                          GDALExtendedDataTypeH hEDT,
12614
                                          CSLConstList papszOptions)
12615
0
{
12616
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12617
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
12618
0
    VALIDATE_POINTER1(hEDT, __func__, nullptr);
12619
0
    std::vector<GUInt64> dims;
12620
0
    dims.reserve(nDimensions);
12621
0
    for (size_t i = 0; i < nDimensions; i++)
12622
0
        dims.push_back(panDimensions[i]);
12623
0
    auto ret = hArray->m_poImpl->CreateAttribute(
12624
0
        std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
12625
0
    if (!ret)
12626
0
        return nullptr;
12627
0
    return new GDALAttributeHS(ret);
12628
0
}
12629
12630
/************************************************************************/
12631
/*                     GDALMDArrayDeleteAttribute()                     */
12632
/************************************************************************/
12633
12634
/** Delete an attribute from an array.
12635
 *
12636
 * After this call, if a previously obtained instance of the deleted object
12637
 * is still alive, no method other than for freeing it should be invoked.
12638
 *
12639
 * This is the same as the C++ method GDALMDArray::DeleteAttribute().
12640
 *
12641
 * @return true in case of success.
12642
 * @since GDAL 3.8
12643
 */
12644
bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
12645
                                CSLConstList papszOptions)
12646
0
{
12647
0
    VALIDATE_POINTER1(hArray, __func__, false);
12648
0
    VALIDATE_POINTER1(pszName, __func__, false);
12649
0
    return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
12650
0
                                             papszOptions);
12651
0
}
12652
12653
/************************************************************************/
12654
/*                    GDALMDArrayGetRawNoDataValue()                    */
12655
/************************************************************************/
12656
12657
/** Return the nodata value as a "raw" value.
12658
 *
12659
 * The value returned might be nullptr in case of no nodata value. When
12660
 * a nodata value is registered, a non-nullptr will be returned whose size in
12661
 * bytes is GetDataType().GetSize().
12662
 *
12663
 * The returned value should not be modified or freed.
12664
 *
12665
 * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
12666
 *
12667
 * @return nullptr or a pointer to GetDataType().GetSize() bytes.
12668
 */
12669
const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
12670
0
{
12671
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
12672
0
    return hArray->m_poImpl->GetRawNoDataValue();
12673
0
}
12674
12675
/************************************************************************/
12676
/*                 GDALMDArrayGetNoDataValueAsDouble()                  */
12677
/************************************************************************/
12678
12679
/** Return the nodata value as a double.
12680
 *
12681
 * The value returned might be nullptr in case of no nodata value. When
12682
 * a nodata value is registered, a non-nullptr will be returned whose size in
12683
 * bytes is GetDataType().GetSize().
12684
 *
12685
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
12686
 *
12687
 * @param hArray Array handle.
12688
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12689
 * if a nodata value exists and can be converted to double. Might be nullptr.
12690
 *
12691
 * @return the nodata value as a double. A 0.0 value might also indicate the
12692
 * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
12693
 * will be set to false then).
12694
 */
12695
double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
12696
                                         int *pbHasNoDataValue)
12697
0
{
12698
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12699
0
    bool bHasNodataValue = false;
12700
0
    double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
12701
0
    if (pbHasNoDataValue)
12702
0
        *pbHasNoDataValue = bHasNodataValue;
12703
0
    return ret;
12704
0
}
12705
12706
/************************************************************************/
12707
/*                  GDALMDArrayGetNoDataValueAsInt64()                  */
12708
/************************************************************************/
12709
12710
/** Return the nodata value as a Int64.
12711
 *
12712
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12713
 *
12714
 * @param hArray Array handle.
12715
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12716
 * if a nodata value exists and can be converted to Int64. Might be nullptr.
12717
 *
12718
 * @return the nodata value as a Int64.
12719
 * @since GDAL 3.5
12720
 */
12721
int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
12722
                                         int *pbHasNoDataValue)
12723
0
{
12724
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12725
0
    bool bHasNodataValue = false;
12726
0
    const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
12727
0
    if (pbHasNoDataValue)
12728
0
        *pbHasNoDataValue = bHasNodataValue;
12729
0
    return ret;
12730
0
}
12731
12732
/************************************************************************/
12733
/*                 GDALMDArrayGetNoDataValueAsUInt64()                  */
12734
/************************************************************************/
12735
12736
/** Return the nodata value as a UInt64.
12737
 *
12738
 * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
12739
 *
12740
 * @param hArray Array handle.
12741
 * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
12742
 * if a nodata value exists and can be converted to UInt64. Might be nullptr.
12743
 *
12744
 * @return the nodata value as a UInt64.
12745
 * @since GDAL 3.5
12746
 */
12747
uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
12748
                                           int *pbHasNoDataValue)
12749
0
{
12750
0
    VALIDATE_POINTER1(hArray, __func__, 0);
12751
0
    bool bHasNodataValue = false;
12752
0
    const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
12753
0
    if (pbHasNoDataValue)
12754
0
        *pbHasNoDataValue = bHasNodataValue;
12755
0
    return ret;
12756
0
}
12757
12758
/************************************************************************/
12759
/*                    GDALMDArraySetRawNoDataValue()                    */
12760
/************************************************************************/
12761
12762
/** Set the nodata value as a "raw" value.
12763
 *
12764
 * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
12765
 * void*).
12766
 *
12767
 * @return TRUE in case of success.
12768
 */
12769
int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
12770
0
{
12771
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12772
0
    return hArray->m_poImpl->SetRawNoDataValue(pNoData);
12773
0
}
12774
12775
/************************************************************************/
12776
/*                 GDALMDArraySetNoDataValueAsDouble()                  */
12777
/************************************************************************/
12778
12779
/** Set the nodata value as a double.
12780
 *
12781
 * If the natural data type of the attribute/array is not double, type
12782
 * conversion will occur to the type returned by GetDataType().
12783
 *
12784
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
12785
 *
12786
 * @return TRUE in case of success.
12787
 */
12788
int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
12789
0
{
12790
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12791
0
    return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
12792
0
}
12793
12794
/************************************************************************/
12795
/*                  GDALMDArraySetNoDataValueAsInt64()                  */
12796
/************************************************************************/
12797
12798
/** Set the nodata value as a Int64.
12799
 *
12800
 * If the natural data type of the attribute/array is not Int64, type conversion
12801
 * will occur to the type returned by GetDataType().
12802
 *
12803
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
12804
 *
12805
 * @return TRUE in case of success.
12806
 * @since GDAL 3.5
12807
 */
12808
int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
12809
0
{
12810
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12811
0
    return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12812
0
}
12813
12814
/************************************************************************/
12815
/*                 GDALMDArraySetNoDataValueAsUInt64()                  */
12816
/************************************************************************/
12817
12818
/** Set the nodata value as a UInt64.
12819
 *
12820
 * If the natural data type of the attribute/array is not UInt64, type
12821
 * conversion will occur to the type returned by GetDataType().
12822
 *
12823
 * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
12824
 *
12825
 * @return TRUE in case of success.
12826
 * @since GDAL 3.5
12827
 */
12828
int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
12829
                                      uint64_t nNoDataValue)
12830
0
{
12831
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12832
0
    return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
12833
0
}
12834
12835
/************************************************************************/
12836
/*                         GDALMDArrayResize()                          */
12837
/************************************************************************/
12838
12839
/** Resize an array to new dimensions.
12840
 *
12841
 * Not all drivers may allow this operation, and with restrictions (e.g.
12842
 * for netCDF, this is limited to growing of "unlimited" dimensions)
12843
 *
12844
 * Resizing a dimension used in other arrays will cause those other arrays
12845
 * to be resized.
12846
 *
12847
 * This is the same as the C++ method GDALMDArray::Resize().
12848
 *
12849
 * @param hArray Array.
12850
 * @param panNewDimSizes Array of GetDimensionCount() values containing the
12851
 *                       new size of each indexing dimension.
12852
 * @param papszOptions Options. (Driver specific)
12853
 * @return true in case of success.
12854
 * @since GDAL 3.7
12855
 */
12856
bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
12857
                       CSLConstList papszOptions)
12858
0
{
12859
0
    VALIDATE_POINTER1(hArray, __func__, false);
12860
0
    VALIDATE_POINTER1(panNewDimSizes, __func__, false);
12861
0
    std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
12862
0
    for (size_t i = 0; i < anNewDimSizes.size(); ++i)
12863
0
    {
12864
0
        anNewDimSizes[i] = panNewDimSizes[i];
12865
0
    }
12866
0
    return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
12867
0
}
12868
12869
/************************************************************************/
12870
/*                        GDALMDArraySetScale()                         */
12871
/************************************************************************/
12872
12873
/** Set the scale value to apply to raw values.
12874
 *
12875
 * unscaled_value = raw_value * GetScale() + GetOffset()
12876
 *
12877
 * This is the same as the C++ method GDALMDArray::SetScale().
12878
 *
12879
 * @return TRUE in case of success.
12880
 */
12881
int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
12882
0
{
12883
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12884
0
    return hArray->m_poImpl->SetScale(dfScale);
12885
0
}
12886
12887
/************************************************************************/
12888
/*                       GDALMDArraySetScaleEx()                        */
12889
/************************************************************************/
12890
12891
/** Set the scale value to apply to raw values.
12892
 *
12893
 * unscaled_value = raw_value * GetScale() + GetOffset()
12894
 *
12895
 * This is the same as the C++ method GDALMDArray::SetScale().
12896
 *
12897
 * @return TRUE in case of success.
12898
 * @since GDAL 3.3
12899
 */
12900
int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
12901
                          GDALDataType eStorageType)
12902
0
{
12903
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12904
0
    return hArray->m_poImpl->SetScale(dfScale, eStorageType);
12905
0
}
12906
12907
/************************************************************************/
12908
/*                        GDALMDArraySetOffset()                        */
12909
/************************************************************************/
12910
12911
/** Set the scale value to apply to raw values.
12912
 *
12913
 * unscaled_value = raw_value * GetScale() + GetOffset()
12914
 *
12915
 * This is the same as the C++ method GDALMDArray::SetOffset().
12916
 *
12917
 * @return TRUE in case of success.
12918
 */
12919
int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
12920
0
{
12921
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12922
0
    return hArray->m_poImpl->SetOffset(dfOffset);
12923
0
}
12924
12925
/************************************************************************/
12926
/*                       GDALMDArraySetOffsetEx()                       */
12927
/************************************************************************/
12928
12929
/** Set the scale value to apply to raw values.
12930
 *
12931
 * unscaled_value = raw_value * GetOffset() + GetOffset()
12932
 *
12933
 * This is the same as the C++ method GDALMDArray::SetOffset().
12934
 *
12935
 * @return TRUE in case of success.
12936
 * @since GDAL 3.3
12937
 */
12938
int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
12939
                           GDALDataType eStorageType)
12940
0
{
12941
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
12942
0
    return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
12943
0
}
12944
12945
/************************************************************************/
12946
/*                        GDALMDArrayGetScale()                         */
12947
/************************************************************************/
12948
12949
/** Get the scale value to apply to raw values.
12950
 *
12951
 * unscaled_value = raw_value * GetScale() + GetOffset()
12952
 *
12953
 * This is the same as the C++ method GDALMDArray::GetScale().
12954
 *
12955
 * @return the scale value
12956
 */
12957
double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
12958
0
{
12959
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12960
0
    bool bHasValue = false;
12961
0
    double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
12962
0
    if (pbHasValue)
12963
0
        *pbHasValue = bHasValue;
12964
0
    return dfRet;
12965
0
}
12966
12967
/************************************************************************/
12968
/*                       GDALMDArrayGetScaleEx()                        */
12969
/************************************************************************/
12970
12971
/** Get the scale value to apply to raw values.
12972
 *
12973
 * unscaled_value = raw_value * GetScale() + GetScale()
12974
 *
12975
 * This is the same as the C++ method GDALMDArray::GetScale().
12976
 *
12977
 * @return the scale value
12978
 * @since GDAL 3.3
12979
 */
12980
double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
12981
                             GDALDataType *peStorageType)
12982
0
{
12983
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
12984
0
    bool bHasValue = false;
12985
0
    double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
12986
0
    if (pbHasValue)
12987
0
        *pbHasValue = bHasValue;
12988
0
    return dfRet;
12989
0
}
12990
12991
/************************************************************************/
12992
/*                        GDALMDArrayGetOffset()                        */
12993
/************************************************************************/
12994
12995
/** Get the scale value to apply to raw values.
12996
 *
12997
 * unscaled_value = raw_value * GetScale() + GetOffset()
12998
 *
12999
 * This is the same as the C++ method GDALMDArray::GetOffset().
13000
 *
13001
 * @return the scale value
13002
 */
13003
double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
13004
0
{
13005
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
13006
0
    bool bHasValue = false;
13007
0
    double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
13008
0
    if (pbHasValue)
13009
0
        *pbHasValue = bHasValue;
13010
0
    return dfRet;
13011
0
}
13012
13013
/************************************************************************/
13014
/*                       GDALMDArrayGetOffsetEx()                       */
13015
/************************************************************************/
13016
13017
/** Get the scale value to apply to raw values.
13018
 *
13019
 * unscaled_value = raw_value * GetScale() + GetOffset()
13020
 *
13021
 * This is the same as the C++ method GDALMDArray::GetOffset().
13022
 *
13023
 * @return the scale value
13024
 * @since GDAL 3.3
13025
 */
13026
double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
13027
                              GDALDataType *peStorageType)
13028
0
{
13029
0
    VALIDATE_POINTER1(hArray, __func__, 0.0);
13030
0
    bool bHasValue = false;
13031
0
    double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
13032
0
    if (pbHasValue)
13033
0
        *pbHasValue = bHasValue;
13034
0
    return dfRet;
13035
0
}
13036
13037
/************************************************************************/
13038
/*                      GDALMDArrayGetBlockSize()                       */
13039
/************************************************************************/
13040
13041
/** Return the "natural" block size of the array along all dimensions.
13042
 *
13043
 * Some drivers might organize the array in tiles/blocks and reading/writing
13044
 * aligned on those tile/block boundaries will be more efficient.
13045
 *
13046
 * The returned number of elements in the vector is the same as
13047
 * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
13048
 * the natural block size along the considered dimension.
13049
 * "Flat" arrays will typically return a vector of values set to 0.
13050
 *
13051
 * The default implementation will return a vector of values set to 0.
13052
 *
13053
 * This method is used by GetProcessingChunkSize().
13054
 *
13055
 * Pedantic note: the returned type is GUInt64, so in the highly unlikely
13056
 * theoretical case of a 32-bit platform, this might exceed its size_t
13057
 * allocation capabilities.
13058
 *
13059
 * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
13060
 *
13061
 * @return the block size, in number of elements along each dimension.
13062
 */
13063
GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
13064
0
{
13065
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13066
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13067
0
    auto res = hArray->m_poImpl->GetBlockSize();
13068
0
    auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
13069
0
    for (size_t i = 0; i < res.size(); i++)
13070
0
    {
13071
0
        ret[i] = res[i];
13072
0
    }
13073
0
    *pnCount = res.size();
13074
0
    return ret;
13075
0
}
13076
13077
/************************************************************************/
13078
/*                 GDALMDArrayGetProcessingChunkSize()                  */
13079
/************************************************************************/
13080
13081
/** \brief Return an optimal chunk size for read/write operations, given the
13082
 * natural block size and memory constraints specified.
13083
 *
13084
 * This method will use GetBlockSize() to define a chunk whose dimensions are
13085
 * multiple of those returned by GetBlockSize() (unless the block define by
13086
 * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
13087
 * returned by this method).
13088
 *
13089
 * This is the same as the C++ method
13090
 * GDALAbstractMDArray::GetProcessingChunkSize().
13091
 *
13092
 * @param hArray Array.
13093
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13094
 * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
13095
 * chunk.
13096
 *
13097
 * @return the chunk size, in number of elements along each dimension.
13098
 */
13099
13100
size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
13101
                                          size_t nMaxChunkMemory)
13102
0
{
13103
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13104
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13105
0
    auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
13106
0
    auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
13107
0
    for (size_t i = 0; i < res.size(); i++)
13108
0
    {
13109
0
        ret[i] = res[i];
13110
0
    }
13111
0
    *pnCount = res.size();
13112
0
    return ret;
13113
0
}
13114
13115
/************************************************************************/
13116
/*                    GDALMDArrayGetStructuralInfo()                    */
13117
/************************************************************************/
13118
13119
/** Return structural information on the array.
13120
 *
13121
 * This may be the compression, etc..
13122
 *
13123
 * The return value should not be freed and is valid until GDALMDArray is
13124
 * released or this function called again.
13125
 *
13126
 * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
13127
 */
13128
CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
13129
0
{
13130
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13131
0
    return hArray->m_poImpl->GetStructuralInfo();
13132
0
}
13133
13134
/************************************************************************/
13135
/*                         GDALMDArrayGetView()                         */
13136
/************************************************************************/
13137
13138
/** Return a view of the array using slicing or field access.
13139
 *
13140
 * The returned object should be released with GDALMDArrayRelease().
13141
 *
13142
 * This is the same as the C++ method GDALMDArray::GetView().
13143
 */
13144
GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
13145
0
{
13146
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13147
0
    VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
13148
0
    auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
13149
0
    if (!sliced)
13150
0
        return nullptr;
13151
0
    return new GDALMDArrayHS(sliced);
13152
0
}
13153
13154
/************************************************************************/
13155
/*                        GDALMDArrayTranspose()                        */
13156
/************************************************************************/
13157
13158
/** Return a view of the array whose axis have been reordered.
13159
 *
13160
 * The returned object should be released with GDALMDArrayRelease().
13161
 *
13162
 * This is the same as the C++ method GDALMDArray::Transpose().
13163
 */
13164
GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
13165
                                  const int *panMapNewAxisToOldAxis)
13166
0
{
13167
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13168
0
    std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
13169
0
    if (nNewAxisCount)
13170
0
    {
13171
0
        memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
13172
0
               nNewAxisCount * sizeof(int));
13173
0
    }
13174
0
    auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
13175
0
    if (!reordered)
13176
0
        return nullptr;
13177
0
    return new GDALMDArrayHS(reordered);
13178
0
}
13179
13180
/************************************************************************/
13181
/*                       GDALMDArrayGetUnscaled()                       */
13182
/************************************************************************/
13183
13184
/** Return an array that is the unscaled version of the current one.
13185
 *
13186
 * That is each value of the unscaled array will be
13187
 * unscaled_value = raw_value * GetScale() + GetOffset()
13188
 *
13189
 * Starting with GDAL 3.3, the Write() method is implemented and will convert
13190
 * from unscaled values to raw values.
13191
 *
13192
 * The returned object should be released with GDALMDArrayRelease().
13193
 *
13194
 * This is the same as the C++ method GDALMDArray::GetUnscaled().
13195
 */
13196
GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
13197
0
{
13198
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13199
0
    auto unscaled = hArray->m_poImpl->GetUnscaled();
13200
0
    if (!unscaled)
13201
0
        return nullptr;
13202
0
    return new GDALMDArrayHS(unscaled);
13203
0
}
13204
13205
/************************************************************************/
13206
/*                         GDALMDArrayGetMask()                         */
13207
/************************************************************************/
13208
13209
/** Return an array that is a mask for the current array
13210
 *
13211
 * This array will be of type Byte, with values set to 0 to indicate invalid
13212
 * pixels of the current array, and values set to 1 to indicate valid pixels.
13213
 *
13214
 * The returned object should be released with GDALMDArrayRelease().
13215
 *
13216
 * This is the same as the C++ method GDALMDArray::GetMask().
13217
 */
13218
GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
13219
0
{
13220
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13221
0
    auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
13222
0
    if (!unscaled)
13223
0
        return nullptr;
13224
0
    return new GDALMDArrayHS(unscaled);
13225
0
}
13226
13227
/************************************************************************/
13228
/*                      GDALMDArrayGetResampled()                       */
13229
/************************************************************************/
13230
13231
/** Return an array that is a resampled / reprojected view of the current array
13232
 *
13233
 * This is the same as the C++ method GDALMDArray::GetResampled().
13234
 *
13235
 * Currently this method can only resample along the last 2 dimensions, unless
13236
 * orthorectifying a NASA EMIT dataset.
13237
 *
13238
 * The returned object should be released with GDALMDArrayRelease().
13239
 *
13240
 * @since 3.4
13241
 */
13242
GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
13243
                                     const GDALDimensionH *pahNewDims,
13244
                                     GDALRIOResampleAlg resampleAlg,
13245
                                     OGRSpatialReferenceH hTargetSRS,
13246
                                     CSLConstList papszOptions)
13247
0
{
13248
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13249
0
    VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
13250
0
    std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
13251
0
    for (size_t i = 0; i < nNewDimCount; ++i)
13252
0
    {
13253
0
        if (pahNewDims[i])
13254
0
            apoNewDims[i] = pahNewDims[i]->m_poImpl;
13255
0
    }
13256
0
    auto poNewArray = hArray->m_poImpl->GetResampled(
13257
0
        apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
13258
0
        papszOptions);
13259
0
    if (!poNewArray)
13260
0
        return nullptr;
13261
0
    return new GDALMDArrayHS(poNewArray);
13262
0
}
13263
13264
/************************************************************************/
13265
/*                         GDALMDArraySetUnit()                         */
13266
/************************************************************************/
13267
13268
/** Set the variable unit.
13269
 *
13270
 * Values should conform as much as possible with those allowed by
13271
 * the NetCDF CF conventions:
13272
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13273
 * but others might be returned.
13274
 *
13275
 * Few examples are "meter", "degrees", "second", ...
13276
 * Empty value means unknown.
13277
 *
13278
 * This is the same as the C function GDALMDArraySetUnit()
13279
 *
13280
 * @param hArray array.
13281
 * @param pszUnit unit name.
13282
 * @return TRUE in case of success.
13283
 */
13284
int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
13285
0
{
13286
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13287
0
    return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
13288
0
}
13289
13290
/************************************************************************/
13291
/*                         GDALMDArrayGetUnit()                         */
13292
/************************************************************************/
13293
13294
/** Return the array unit.
13295
 *
13296
 * Values should conform as much as possible with those allowed by
13297
 * the NetCDF CF conventions:
13298
 * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
13299
 * but others might be returned.
13300
 *
13301
 * Few examples are "meter", "degrees", "second", ...
13302
 * Empty value means unknown.
13303
 *
13304
 * The return value should not be freed and is valid until GDALMDArray is
13305
 * released or this function called again.
13306
 *
13307
 * This is the same as the C++ method GDALMDArray::GetUnit().
13308
 */
13309
const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
13310
0
{
13311
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13312
0
    return hArray->m_poImpl->GetUnit().c_str();
13313
0
}
13314
13315
/************************************************************************/
13316
/*                      GDALMDArrayGetSpatialRef()                      */
13317
/************************************************************************/
13318
13319
/** Assign a spatial reference system object to the array.
13320
 *
13321
 * This is the same as the C++ method GDALMDArray::SetSpatialRef().
13322
 * @return TRUE in case of success.
13323
 */
13324
int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
13325
0
{
13326
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13327
0
    return hArray->m_poImpl->SetSpatialRef(
13328
0
        OGRSpatialReference::FromHandle(hSRS));
13329
0
}
13330
13331
/************************************************************************/
13332
/*                      GDALMDArrayGetSpatialRef()                      */
13333
/************************************************************************/
13334
13335
/** Return the spatial reference system object associated with the array.
13336
 *
13337
 * This is the same as the C++ method GDALMDArray::GetSpatialRef().
13338
 *
13339
 * The returned object must be freed with OSRDestroySpatialReference().
13340
 */
13341
OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
13342
0
{
13343
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13344
0
    auto poSRS = hArray->m_poImpl->GetSpatialRef();
13345
0
    return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
13346
0
}
13347
13348
/************************************************************************/
13349
/*                      GDALMDArrayGetStatistics()                      */
13350
/************************************************************************/
13351
13352
/**
13353
 * \brief Fetch statistics.
13354
 *
13355
 * This is the same as the C++ method GDALMDArray::GetStatistics().
13356
 *
13357
 * @since GDAL 3.2
13358
 */
13359
13360
CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
13361
                                int bApproxOK, int bForce, double *pdfMin,
13362
                                double *pdfMax, double *pdfMean,
13363
                                double *pdfStdDev, GUInt64 *pnValidCount,
13364
                                GDALProgressFunc pfnProgress,
13365
                                void *pProgressData)
13366
0
{
13367
0
    VALIDATE_POINTER1(hArray, __func__, CE_Failure);
13368
0
    return hArray->m_poImpl->GetStatistics(
13369
0
        CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
13370
0
        pdfStdDev, pnValidCount, pfnProgress, pProgressData);
13371
0
}
13372
13373
/************************************************************************/
13374
/*                    GDALMDArrayComputeStatistics()                    */
13375
/************************************************************************/
13376
13377
/**
13378
 * \brief Compute statistics.
13379
 *
13380
 * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13381
 *
13382
 * @since GDAL 3.2
13383
 * @see GDALMDArrayComputeStatisticsEx()
13384
 */
13385
13386
int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13387
                                 int bApproxOK, double *pdfMin, double *pdfMax,
13388
                                 double *pdfMean, double *pdfStdDev,
13389
                                 GUInt64 *pnValidCount,
13390
                                 GDALProgressFunc pfnProgress,
13391
                                 void *pProgressData)
13392
0
{
13393
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13394
0
    return hArray->m_poImpl->ComputeStatistics(
13395
0
        CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13396
0
        pnValidCount, pfnProgress, pProgressData, nullptr);
13397
0
}
13398
13399
/************************************************************************/
13400
/*                   GDALMDArrayComputeStatisticsEx()                   */
13401
/************************************************************************/
13402
13403
/**
13404
 * \brief Compute statistics.
13405
 *
13406
 * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
13407
 *
13408
 * This is the same as the C++ method GDALMDArray::ComputeStatistics().
13409
 *
13410
 * @since GDAL 3.8
13411
 */
13412
13413
int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
13414
                                   int bApproxOK, double *pdfMin,
13415
                                   double *pdfMax, double *pdfMean,
13416
                                   double *pdfStdDev, GUInt64 *pnValidCount,
13417
                                   GDALProgressFunc pfnProgress,
13418
                                   void *pProgressData,
13419
                                   CSLConstList papszOptions)
13420
0
{
13421
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13422
0
    return hArray->m_poImpl->ComputeStatistics(
13423
0
        CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
13424
0
        pnValidCount, pfnProgress, pProgressData, papszOptions);
13425
0
}
13426
13427
/************************************************************************/
13428
/*                 GDALMDArrayGetCoordinateVariables()                  */
13429
/************************************************************************/
13430
13431
/** Return coordinate variables.
13432
 *
13433
 * The returned array must be freed with GDALReleaseArrays(). If only the array
13434
 * itself needs to be freed, CPLFree() should be called (and
13435
 * GDALMDArrayRelease() on individual array members).
13436
 *
13437
 * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
13438
 *
13439
 * @param hArray Array.
13440
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13441
 *
13442
 * @return an array of *pnCount arrays.
13443
 * @since 3.4
13444
 */
13445
GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
13446
                                                size_t *pnCount)
13447
0
{
13448
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13449
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13450
0
    const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
13451
0
    auto ret = static_cast<GDALMDArrayH *>(
13452
0
        CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
13453
0
    for (size_t i = 0; i < coordinates.size(); i++)
13454
0
    {
13455
0
        ret[i] = new GDALMDArrayHS(coordinates[i]);
13456
0
    }
13457
0
    *pnCount = coordinates.size();
13458
0
    return ret;
13459
0
}
13460
13461
/************************************************************************/
13462
/*                       GDALMDArrayGetGridded()                        */
13463
/************************************************************************/
13464
13465
/** Return a gridded array from scattered point data, that is from an array
13466
 * whose last dimension is the indexing variable of X and Y arrays.
13467
 *
13468
 * The returned object should be released with GDALMDArrayRelease().
13469
 *
13470
 * This is the same as the C++ method GDALMDArray::GetGridded().
13471
 *
13472
 * @since GDAL 3.7
13473
 */
13474
GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
13475
                                   const char *pszGridOptions,
13476
                                   GDALMDArrayH hXArray, GDALMDArrayH hYArray,
13477
                                   CSLConstList papszOptions)
13478
0
{
13479
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
13480
0
    VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
13481
0
    auto gridded = hArray->m_poImpl->GetGridded(
13482
0
        pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
13483
0
        hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
13484
0
    if (!gridded)
13485
0
        return nullptr;
13486
0
    return new GDALMDArrayHS(gridded);
13487
0
}
13488
13489
/************************************************************************/
13490
/*                       GDALMDArrayGetMeshGrid()                       */
13491
/************************************************************************/
13492
13493
/** Return a list of multidimensional arrays from a list of one-dimensional
13494
 * arrays.
13495
 *
13496
 * This is typically used to transform one-dimensional longitude, latitude
13497
 * arrays into 2D ones.
13498
 *
13499
 * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
13500
 * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
13501
 * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
13502
 * repeated to fill the matrix along the first dimension for x1, the second
13503
 * for x2 and so on.
13504
 *
13505
 * For example, if x = [1, 2], and y = [3, 4, 5],
13506
 * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
13507
 * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
13508
 * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
13509
 *
13510
 * and
13511
 * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
13512
 * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
13513
 * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
13514
 *
13515
 * The currently supported options are:
13516
 * <ul>
13517
 * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
13518
 * output.
13519
 * </li>
13520
 * </ul>
13521
 *
13522
 * This is the same as
13523
 * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
13524
 * function.
13525
 *
13526
 * The returned array (of arrays) must be freed with GDALReleaseArrays().
13527
 * If only the array itself needs to be freed, CPLFree() should be called
13528
 * (and GDALMDArrayRelease() on individual array members).
13529
 *
13530
 * This is the same as the C++ method GDALMDArray::GetMeshGrid()
13531
 *
13532
 * @param pahInputArrays Input arrays
13533
 * @param nCountInputArrays Number of input arrays
13534
 * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
13535
 * @param papszOptions NULL, or NULL terminated list of options.
13536
 *
13537
 * @return an array of *pnCountOutputArrays arrays.
13538
 * @since 3.10
13539
 */
13540
GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
13541
                                     size_t nCountInputArrays,
13542
                                     size_t *pnCountOutputArrays,
13543
                                     CSLConstList papszOptions)
13544
0
{
13545
0
    VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
13546
0
    VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
13547
13548
0
    std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
13549
0
    for (size_t i = 0; i < nCountInputArrays; ++i)
13550
0
        apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
13551
13552
0
    const auto apoOutputArrays =
13553
0
        GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
13554
0
    auto ret = static_cast<GDALMDArrayH *>(
13555
0
        CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
13556
0
    for (size_t i = 0; i < apoOutputArrays.size(); i++)
13557
0
    {
13558
0
        ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
13559
0
    }
13560
0
    *pnCountOutputArrays = apoOutputArrays.size();
13561
0
    return ret;
13562
0
}
13563
13564
/************************************************************************/
13565
/*                         GDALReleaseArrays()                          */
13566
/************************************************************************/
13567
13568
/** Free the return of GDALMDArrayGetCoordinateVariables()
13569
 *
13570
 * @param arrays return pointer of above methods
13571
 * @param nCount *pnCount value returned by above methods
13572
 */
13573
void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
13574
0
{
13575
0
    for (size_t i = 0; i < nCount; i++)
13576
0
    {
13577
0
        delete arrays[i];
13578
0
    }
13579
0
    CPLFree(arrays);
13580
0
}
13581
13582
/************************************************************************/
13583
/*                          GDALMDArrayCache()                          */
13584
/************************************************************************/
13585
13586
/**
13587
 * \brief Cache the content of the array into an auxiliary filename.
13588
 *
13589
 * This is the same as the C++ method GDALMDArray::Cache().
13590
 *
13591
 * @since GDAL 3.4
13592
 */
13593
13594
int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
13595
0
{
13596
0
    VALIDATE_POINTER1(hArray, __func__, FALSE);
13597
0
    return hArray->m_poImpl->Cache(papszOptions);
13598
0
}
13599
13600
/************************************************************************/
13601
/*                         GDALMDArrayRename()                          */
13602
/************************************************************************/
13603
13604
/** Rename the array.
13605
 *
13606
 * This is not implemented by all drivers.
13607
 *
13608
 * Drivers known to implement it: MEM, netCDF, Zarr.
13609
 *
13610
 * This is the same as the C++ method GDALAbstractMDArray::Rename()
13611
 *
13612
 * @return true in case of success
13613
 * @since GDAL 3.8
13614
 */
13615
bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
13616
0
{
13617
0
    VALIDATE_POINTER1(hArray, __func__, false);
13618
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
13619
0
    return hArray->m_poImpl->Rename(pszNewName);
13620
0
}
13621
13622
/************************************************************************/
13623
/*                        GDALAttributeRelease()                        */
13624
/************************************************************************/
13625
13626
/** Release the GDAL in-memory object associated with a GDALAttribute.
13627
 *
13628
 * Note: when applied on a object coming from a driver, this does not
13629
 * destroy the object in the file, database, etc...
13630
 */
13631
void GDALAttributeRelease(GDALAttributeH hAttr)
13632
0
{
13633
0
    delete hAttr;
13634
0
}
13635
13636
/************************************************************************/
13637
/*                        GDALAttributeGetName()                        */
13638
/************************************************************************/
13639
13640
/** Return the name of the attribute.
13641
 *
13642
 * The returned pointer is valid until hAttr is released.
13643
 *
13644
 * This is the same as the C++ method GDALAttribute::GetName().
13645
 */
13646
const char *GDALAttributeGetName(GDALAttributeH hAttr)
13647
0
{
13648
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13649
0
    return hAttr->m_poImpl->GetName().c_str();
13650
0
}
13651
13652
/************************************************************************/
13653
/*                      GDALAttributeGetFullName()                      */
13654
/************************************************************************/
13655
13656
/** Return the full name of the attribute.
13657
 *
13658
 * The returned pointer is valid until hAttr is released.
13659
 *
13660
 * This is the same as the C++ method GDALAttribute::GetFullName().
13661
 */
13662
const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
13663
0
{
13664
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13665
0
    return hAttr->m_poImpl->GetFullName().c_str();
13666
0
}
13667
13668
/************************************************************************/
13669
/*                 GDALAttributeGetTotalElementsCount()                 */
13670
/************************************************************************/
13671
13672
/** Return the total number of values in the attribute.
13673
 *
13674
 * This is the same as the C++ method
13675
 * GDALAbstractMDArray::GetTotalElementsCount()
13676
 */
13677
GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
13678
0
{
13679
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13680
0
    return hAttr->m_poImpl->GetTotalElementsCount();
13681
0
}
13682
13683
/************************************************************************/
13684
/*                   GDALAttributeGetDimensionCount()                   */
13685
/************************************************************************/
13686
13687
/** Return the number of dimensions.
13688
 *
13689
 * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
13690
 */
13691
size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
13692
0
{
13693
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13694
0
    return hAttr->m_poImpl->GetDimensionCount();
13695
0
}
13696
13697
/************************************************************************/
13698
/*                   GDALAttributeGetDimensionsSize()                   */
13699
/************************************************************************/
13700
13701
/** Return the dimension sizes of the attribute.
13702
 *
13703
 * The returned array must be freed with CPLFree()
13704
 *
13705
 * @param hAttr Attribute.
13706
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13707
 *
13708
 * @return an array of *pnCount values.
13709
 */
13710
GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
13711
0
{
13712
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13713
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13714
0
    const auto &dims = hAttr->m_poImpl->GetDimensions();
13715
0
    auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
13716
0
    for (size_t i = 0; i < dims.size(); i++)
13717
0
    {
13718
0
        ret[i] = dims[i]->GetSize();
13719
0
    }
13720
0
    *pnCount = dims.size();
13721
0
    return ret;
13722
0
}
13723
13724
/************************************************************************/
13725
/*                      GDALAttributeGetDataType()                      */
13726
/************************************************************************/
13727
13728
/** Return the data type
13729
 *
13730
 * The return must be freed with GDALExtendedDataTypeRelease().
13731
 */
13732
GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
13733
0
{
13734
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13735
0
    return new GDALExtendedDataTypeHS(
13736
0
        new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
13737
0
}
13738
13739
/************************************************************************/
13740
/*                       GDALAttributeReadAsRaw()                       */
13741
/************************************************************************/
13742
13743
/** Return the raw value of an attribute.
13744
 *
13745
 * This is the same as the C++ method GDALAttribute::ReadAsRaw().
13746
 *
13747
 * The returned buffer must be freed with GDALAttributeFreeRawResult()
13748
 *
13749
 * @param hAttr Attribute.
13750
 * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
13751
 *
13752
 * @return a buffer of *pnSize bytes.
13753
 */
13754
GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
13755
0
{
13756
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13757
0
    VALIDATE_POINTER1(pnSize, __func__, nullptr);
13758
0
    auto res(hAttr->m_poImpl->ReadAsRaw());
13759
0
    *pnSize = res.size();
13760
0
    auto ret = res.StealData();
13761
0
    if (!ret)
13762
0
    {
13763
0
        *pnSize = 0;
13764
0
        return nullptr;
13765
0
    }
13766
0
    return ret;
13767
0
}
13768
13769
/************************************************************************/
13770
/*                     GDALAttributeFreeRawResult()                     */
13771
/************************************************************************/
13772
13773
/** Free the return of GDALAttributeAsRaw()
13774
 */
13775
void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
13776
                                CPL_UNUSED size_t nSize)
13777
0
{
13778
0
    VALIDATE_POINTER0(hAttr, __func__);
13779
0
    if (raw)
13780
0
    {
13781
0
        const auto &dt(hAttr->m_poImpl->GetDataType());
13782
0
        const auto nDTSize(dt.GetSize());
13783
0
        GByte *pabyPtr = raw;
13784
0
        const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
13785
0
        CPLAssert(nSize == nDTSize * nEltCount);
13786
0
        for (size_t i = 0; i < nEltCount; ++i)
13787
0
        {
13788
0
            dt.FreeDynamicMemory(pabyPtr);
13789
0
            pabyPtr += nDTSize;
13790
0
        }
13791
0
        CPLFree(raw);
13792
0
    }
13793
0
}
13794
13795
/************************************************************************/
13796
/*                     GDALAttributeReadAsString()                      */
13797
/************************************************************************/
13798
13799
/** Return the value of an attribute as a string.
13800
 *
13801
 * The returned string should not be freed, and its lifetime does not
13802
 * excess a next call to ReadAsString() on the same object, or the deletion
13803
 * of the object itself.
13804
 *
13805
 * This function will only return the first element if there are several.
13806
 *
13807
 * This is the same as the C++ method GDALAttribute::ReadAsString()
13808
 *
13809
 * @return a string, or nullptr.
13810
 */
13811
const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
13812
0
{
13813
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13814
0
    return hAttr->m_poImpl->ReadAsString();
13815
0
}
13816
13817
/************************************************************************/
13818
/*                       GDALAttributeReadAsInt()                       */
13819
/************************************************************************/
13820
13821
/** Return the value of an attribute as a integer.
13822
 *
13823
 * This function will only return the first element if there are several.
13824
 *
13825
 * It can fail if its value can not be converted to integer.
13826
 *
13827
 * This is the same as the C++ method GDALAttribute::ReadAsInt()
13828
 *
13829
 * @return a integer, or INT_MIN in case of error.
13830
 */
13831
int GDALAttributeReadAsInt(GDALAttributeH hAttr)
13832
0
{
13833
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13834
0
    return hAttr->m_poImpl->ReadAsInt();
13835
0
}
13836
13837
/************************************************************************/
13838
/*                      GDALAttributeReadAsInt64()                      */
13839
/************************************************************************/
13840
13841
/** Return the value of an attribute as a int64_t.
13842
 *
13843
 * This function will only return the first element if there are several.
13844
 *
13845
 * It can fail if its value can not be converted to integer.
13846
 *
13847
 * This is the same as the C++ method GDALAttribute::ReadAsInt64()
13848
 *
13849
 * @return an int64_t, or INT64_MIN in case of error.
13850
 */
13851
int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
13852
0
{
13853
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13854
0
    return hAttr->m_poImpl->ReadAsInt64();
13855
0
}
13856
13857
/************************************************************************/
13858
/*                     GDALAttributeReadAsDouble()                      */
13859
/************************************************************************/
13860
13861
/** Return the value of an attribute as a double.
13862
 *
13863
 * This function will only return the first element if there are several.
13864
 *
13865
 * It can fail if its value can not be converted to double.
13866
 *
13867
 * This is the same as the C++ method GDALAttribute::ReadAsDouble()
13868
 *
13869
 * @return a double value.
13870
 */
13871
double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
13872
0
{
13873
0
    VALIDATE_POINTER1(hAttr, __func__, 0);
13874
0
    return hAttr->m_poImpl->ReadAsDouble();
13875
0
}
13876
13877
/************************************************************************/
13878
/*                   GDALAttributeReadAsStringArray()                   */
13879
/************************************************************************/
13880
13881
/** Return the value of an attribute as an array of strings.
13882
 *
13883
 * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
13884
 *
13885
 * The return value must be freed with CSLDestroy().
13886
 */
13887
char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
13888
0
{
13889
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13890
0
    return hAttr->m_poImpl->ReadAsStringArray().StealList();
13891
0
}
13892
13893
/************************************************************************/
13894
/*                    GDALAttributeReadAsIntArray()                     */
13895
/************************************************************************/
13896
13897
/** Return the value of an attribute as an array of integers.
13898
 *
13899
 * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
13900
 *
13901
 * @param hAttr Attribute
13902
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13903
 * @return array to be freed with CPLFree(), or nullptr.
13904
 */
13905
int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
13906
0
{
13907
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13908
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13909
0
    *pnCount = 0;
13910
0
    auto tmp(hAttr->m_poImpl->ReadAsIntArray());
13911
0
    if (tmp.empty())
13912
0
        return nullptr;
13913
0
    auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
13914
0
    if (!ret)
13915
0
        return nullptr;
13916
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
13917
0
    *pnCount = tmp.size();
13918
0
    return ret;
13919
0
}
13920
13921
/************************************************************************/
13922
/*                   GDALAttributeReadAsInt64Array()                    */
13923
/************************************************************************/
13924
13925
/** Return the value of an attribute as an array of int64_t.
13926
 *
13927
 * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
13928
 *
13929
 * @param hAttr Attribute
13930
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13931
 * @return array to be freed with CPLFree(), or nullptr.
13932
 */
13933
int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
13934
0
{
13935
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13936
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13937
0
    *pnCount = 0;
13938
0
    auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
13939
0
    if (tmp.empty())
13940
0
        return nullptr;
13941
0
    auto ret = static_cast<int64_t *>(
13942
0
        VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
13943
0
    if (!ret)
13944
0
        return nullptr;
13945
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
13946
0
    *pnCount = tmp.size();
13947
0
    return ret;
13948
0
}
13949
13950
/************************************************************************/
13951
/*                   GDALAttributeReadAsDoubleArray()                   */
13952
/************************************************************************/
13953
13954
/** Return the value of an attribute as an array of doubles.
13955
 *
13956
 * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
13957
 *
13958
 * @param hAttr Attribute
13959
 * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
13960
 * @return array to be freed with CPLFree(), or nullptr.
13961
 */
13962
double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
13963
0
{
13964
0
    VALIDATE_POINTER1(hAttr, __func__, nullptr);
13965
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
13966
0
    *pnCount = 0;
13967
0
    auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
13968
0
    if (tmp.empty())
13969
0
        return nullptr;
13970
0
    auto ret =
13971
0
        static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
13972
0
    if (!ret)
13973
0
        return nullptr;
13974
0
    memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
13975
0
    *pnCount = tmp.size();
13976
0
    return ret;
13977
0
}
13978
13979
/************************************************************************/
13980
/*                       GDALAttributeWriteRaw()                        */
13981
/************************************************************************/
13982
13983
/** Write an attribute from raw values expressed in GetDataType()
13984
 *
13985
 * The values should be provided in the type of GetDataType() and there should
13986
 * be exactly GetTotalElementsCount() of them.
13987
 * If GetDataType() is a string, each value should be a char* pointer.
13988
 *
13989
 * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
13990
 *
13991
 * @param hAttr Attribute
13992
 * @param pabyValue Buffer of nLen bytes.
13993
 * @param nLength Size of pabyValue in bytes. Should be equal to
13994
 *             GetTotalElementsCount() * GetDataType().GetSize()
13995
 * @return TRUE in case of success.
13996
 */
13997
int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
13998
                          size_t nLength)
13999
0
{
14000
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14001
0
    return hAttr->m_poImpl->Write(pabyValue, nLength);
14002
0
}
14003
14004
/************************************************************************/
14005
/*                      GDALAttributeWriteString()                      */
14006
/************************************************************************/
14007
14008
/** Write an attribute from a string value.
14009
 *
14010
 * Type conversion will be performed if needed. If the attribute contains
14011
 * multiple values, only the first one will be updated.
14012
 *
14013
 * This is the same as the C++ method GDALAttribute::Write(const char*)
14014
 *
14015
 * @param hAttr Attribute
14016
 * @param pszVal Pointer to a string.
14017
 * @return TRUE in case of success.
14018
 */
14019
int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
14020
0
{
14021
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14022
0
    return hAttr->m_poImpl->Write(pszVal);
14023
0
}
14024
14025
/************************************************************************/
14026
/*                       GDALAttributeWriteInt()                        */
14027
/************************************************************************/
14028
14029
/** Write an attribute from a integer value.
14030
 *
14031
 * Type conversion will be performed if needed. If the attribute contains
14032
 * multiple values, only the first one will be updated.
14033
 *
14034
 * This is the same as the C++ method GDALAttribute::WriteInt()
14035
 *
14036
 * @param hAttr Attribute
14037
 * @param nVal Value.
14038
 * @return TRUE in case of success.
14039
 */
14040
int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
14041
0
{
14042
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14043
0
    return hAttr->m_poImpl->WriteInt(nVal);
14044
0
}
14045
14046
/************************************************************************/
14047
/*                      GDALAttributeWriteInt64()                       */
14048
/************************************************************************/
14049
14050
/** Write an attribute from an int64_t value.
14051
 *
14052
 * Type conversion will be performed if needed. If the attribute contains
14053
 * multiple values, only the first one will be updated.
14054
 *
14055
 * This is the same as the C++ method GDALAttribute::WriteLong()
14056
 *
14057
 * @param hAttr Attribute
14058
 * @param nVal Value.
14059
 * @return TRUE in case of success.
14060
 */
14061
int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
14062
0
{
14063
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14064
0
    return hAttr->m_poImpl->WriteInt64(nVal);
14065
0
}
14066
14067
/************************************************************************/
14068
/*                      GDALAttributeWriteDouble()                      */
14069
/************************************************************************/
14070
14071
/** Write an attribute from a double value.
14072
 *
14073
 * Type conversion will be performed if needed. If the attribute contains
14074
 * multiple values, only the first one will be updated.
14075
 *
14076
 * This is the same as the C++ method GDALAttribute::Write(double);
14077
 *
14078
 * @param hAttr Attribute
14079
 * @param dfVal Value.
14080
 *
14081
 * @return TRUE in case of success.
14082
 */
14083
int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
14084
0
{
14085
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14086
0
    return hAttr->m_poImpl->Write(dfVal);
14087
0
}
14088
14089
/************************************************************************/
14090
/*                   GDALAttributeWriteStringArray()                    */
14091
/************************************************************************/
14092
14093
/** Write an attribute from an array of strings.
14094
 *
14095
 * Type conversion will be performed if needed.
14096
 *
14097
 * Exactly GetTotalElementsCount() strings must be provided
14098
 *
14099
 * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
14100
 *
14101
 * @param hAttr Attribute
14102
 * @param papszValues Array of strings.
14103
 * @return TRUE in case of success.
14104
 */
14105
int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
14106
                                  CSLConstList papszValues)
14107
0
{
14108
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14109
0
    return hAttr->m_poImpl->Write(papszValues);
14110
0
}
14111
14112
/************************************************************************/
14113
/*                     GDALAttributeWriteIntArray()                     */
14114
/************************************************************************/
14115
14116
/** Write an attribute from an array of int.
14117
 *
14118
 * Type conversion will be performed if needed.
14119
 *
14120
 * Exactly GetTotalElementsCount() strings must be provided
14121
 *
14122
 * This is the same as the C++ method GDALAttribute::Write(const int *,
14123
 * size_t)
14124
 *
14125
 * @param hAttr Attribute
14126
 * @param panValues Array of int.
14127
 * @param nCount Should be equal to GetTotalElementsCount().
14128
 * @return TRUE in case of success.
14129
 */
14130
int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
14131
                               size_t nCount)
14132
0
{
14133
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14134
0
    return hAttr->m_poImpl->Write(panValues, nCount);
14135
0
}
14136
14137
/************************************************************************/
14138
/*                    GDALAttributeWriteInt64Array()                    */
14139
/************************************************************************/
14140
14141
/** Write an attribute from an array of int64_t.
14142
 *
14143
 * Type conversion will be performed if needed.
14144
 *
14145
 * Exactly GetTotalElementsCount() strings must be provided
14146
 *
14147
 * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
14148
 * size_t)
14149
 *
14150
 * @param hAttr Attribute
14151
 * @param panValues Array of int64_t.
14152
 * @param nCount Should be equal to GetTotalElementsCount().
14153
 * @return TRUE in case of success.
14154
 */
14155
int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
14156
                                 size_t nCount)
14157
0
{
14158
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14159
0
    return hAttr->m_poImpl->Write(panValues, nCount);
14160
0
}
14161
14162
/************************************************************************/
14163
/*                   GDALAttributeWriteDoubleArray()                    */
14164
/************************************************************************/
14165
14166
/** Write an attribute from an array of double.
14167
 *
14168
 * Type conversion will be performed if needed.
14169
 *
14170
 * Exactly GetTotalElementsCount() strings must be provided
14171
 *
14172
 * This is the same as the C++ method GDALAttribute::Write(const double *,
14173
 * size_t)
14174
 *
14175
 * @param hAttr Attribute
14176
 * @param padfValues Array of double.
14177
 * @param nCount Should be equal to GetTotalElementsCount().
14178
 * @return TRUE in case of success.
14179
 */
14180
int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
14181
                                  const double *padfValues, size_t nCount)
14182
0
{
14183
0
    VALIDATE_POINTER1(hAttr, __func__, FALSE);
14184
0
    return hAttr->m_poImpl->Write(padfValues, nCount);
14185
0
}
14186
14187
/************************************************************************/
14188
/*                        GDALAttributeRename()                         */
14189
/************************************************************************/
14190
14191
/** Rename the attribute.
14192
 *
14193
 * This is not implemented by all drivers.
14194
 *
14195
 * Drivers known to implement it: MEM, netCDF.
14196
 *
14197
 * This is the same as the C++ method GDALAbstractMDArray::Rename()
14198
 *
14199
 * @return true in case of success
14200
 * @since GDAL 3.8
14201
 */
14202
bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
14203
0
{
14204
0
    VALIDATE_POINTER1(hAttr, __func__, false);
14205
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
14206
0
    return hAttr->m_poImpl->Rename(pszNewName);
14207
0
}
14208
14209
/************************************************************************/
14210
/*                        GDALDimensionRelease()                        */
14211
/************************************************************************/
14212
14213
/** Release the GDAL in-memory object associated with a GDALDimension.
14214
 *
14215
 * Note: when applied on a object coming from a driver, this does not
14216
 * destroy the object in the file, database, etc...
14217
 */
14218
void GDALDimensionRelease(GDALDimensionH hDim)
14219
0
{
14220
0
    delete hDim;
14221
0
}
14222
14223
/************************************************************************/
14224
/*                        GDALDimensionGetName()                        */
14225
/************************************************************************/
14226
14227
/** Return dimension name.
14228
 *
14229
 * This is the same as the C++ method GDALDimension::GetName()
14230
 */
14231
const char *GDALDimensionGetName(GDALDimensionH hDim)
14232
0
{
14233
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14234
0
    return hDim->m_poImpl->GetName().c_str();
14235
0
}
14236
14237
/************************************************************************/
14238
/*                      GDALDimensionGetFullName()                      */
14239
/************************************************************************/
14240
14241
/** Return dimension full name.
14242
 *
14243
 * This is the same as the C++ method GDALDimension::GetFullName()
14244
 */
14245
const char *GDALDimensionGetFullName(GDALDimensionH hDim)
14246
0
{
14247
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14248
0
    return hDim->m_poImpl->GetFullName().c_str();
14249
0
}
14250
14251
/************************************************************************/
14252
/*                        GDALDimensionGetType()                        */
14253
/************************************************************************/
14254
14255
/** Return dimension type.
14256
 *
14257
 * This is the same as the C++ method GDALDimension::GetType()
14258
 */
14259
const char *GDALDimensionGetType(GDALDimensionH hDim)
14260
0
{
14261
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14262
0
    return hDim->m_poImpl->GetType().c_str();
14263
0
}
14264
14265
/************************************************************************/
14266
/*                     GDALDimensionGetDirection()                      */
14267
/************************************************************************/
14268
14269
/** Return dimension direction.
14270
 *
14271
 * This is the same as the C++ method GDALDimension::GetDirection()
14272
 */
14273
const char *GDALDimensionGetDirection(GDALDimensionH hDim)
14274
0
{
14275
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14276
0
    return hDim->m_poImpl->GetDirection().c_str();
14277
0
}
14278
14279
/************************************************************************/
14280
/*                        GDALDimensionGetSize()                        */
14281
/************************************************************************/
14282
14283
/** Return the size, that is the number of values along the dimension.
14284
 *
14285
 * This is the same as the C++ method GDALDimension::GetSize()
14286
 */
14287
GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
14288
0
{
14289
0
    VALIDATE_POINTER1(hDim, __func__, 0);
14290
0
    return hDim->m_poImpl->GetSize();
14291
0
}
14292
14293
/************************************************************************/
14294
/*                  GDALDimensionGetIndexingVariable()                  */
14295
/************************************************************************/
14296
14297
/** Return the variable that is used to index the dimension (if there is one).
14298
 *
14299
 * This is the array, typically one-dimensional, describing the values taken
14300
 * by the dimension.
14301
 *
14302
 * The returned value should be freed with GDALMDArrayRelease().
14303
 *
14304
 * This is the same as the C++ method GDALDimension::GetIndexingVariable()
14305
 */
14306
GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
14307
0
{
14308
0
    VALIDATE_POINTER1(hDim, __func__, nullptr);
14309
0
    auto var(hDim->m_poImpl->GetIndexingVariable());
14310
0
    if (!var)
14311
0
        return nullptr;
14312
0
    return new GDALMDArrayHS(var);
14313
0
}
14314
14315
/************************************************************************/
14316
/*                  GDALDimensionSetIndexingVariable()                  */
14317
/************************************************************************/
14318
14319
/** Set the variable that is used to index the dimension.
14320
 *
14321
 * This is the array, typically one-dimensional, describing the values taken
14322
 * by the dimension.
14323
 *
14324
 * This is the same as the C++ method GDALDimension::SetIndexingVariable()
14325
 *
14326
 * @return TRUE in case of success.
14327
 */
14328
int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
14329
0
{
14330
0
    VALIDATE_POINTER1(hDim, __func__, FALSE);
14331
0
    return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
14332
0
                                                      : nullptr);
14333
0
}
14334
14335
/************************************************************************/
14336
/*                        GDALDimensionRename()                         */
14337
/************************************************************************/
14338
14339
/** Rename the dimension.
14340
 *
14341
 * This is not implemented by all drivers.
14342
 *
14343
 * Drivers known to implement it: MEM, netCDF.
14344
 *
14345
 * This is the same as the C++ method GDALDimension::Rename()
14346
 *
14347
 * @return true in case of success
14348
 * @since GDAL 3.8
14349
 */
14350
bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
14351
0
{
14352
0
    VALIDATE_POINTER1(hDim, __func__, false);
14353
0
    VALIDATE_POINTER1(pszNewName, __func__, false);
14354
0
    return hDim->m_poImpl->Rename(pszNewName);
14355
0
}
14356
14357
/************************************************************************/
14358
/*                      GDALDatasetGetRootGroup()                       */
14359
/************************************************************************/
14360
14361
/** Return the root GDALGroup of this dataset.
14362
 *
14363
 * Only valid for multidimensional datasets.
14364
 *
14365
 * The returned value must be freed with GDALGroupRelease().
14366
 *
14367
 * This is the same as the C++ method GDALDataset::GetRootGroup().
14368
 *
14369
 * @since GDAL 3.1
14370
 */
14371
GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
14372
0
{
14373
0
    VALIDATE_POINTER1(hDS, __func__, nullptr);
14374
0
    auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
14375
0
    return poGroup ? new GDALGroupHS(poGroup) : nullptr;
14376
0
}
14377
14378
/************************************************************************/
14379
/*                      GDALRasterBandAsMDArray()                       */
14380
/************************************************************************/
14381
14382
/** Return a view of this raster band as a 2D multidimensional GDALMDArray.
14383
 *
14384
 * The band must be linked to a GDALDataset. If this dataset is not already
14385
 * marked as shared, it will be, so that the returned array holds a reference
14386
 * to it.
14387
 *
14388
 * If the dataset has a geotransform attached, the X and Y dimensions of the
14389
 * returned array will have an associated indexing variable.
14390
 *
14391
 * The returned pointer must be released with GDALMDArrayRelease().
14392
 *
14393
 * This is the same as the C++ method GDALRasterBand::AsMDArray().
14394
 *
14395
 * @return a new array, or NULL.
14396
 *
14397
 * @since GDAL 3.1
14398
 */
14399
GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
14400
0
{
14401
0
    VALIDATE_POINTER1(hBand, __func__, nullptr);
14402
0
    auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
14403
0
    if (!poArray)
14404
0
        return nullptr;
14405
0
    return new GDALMDArrayHS(poArray);
14406
0
}
14407
14408
/************************************************************************/
14409
/*                        GDALDatasetAsMDArray()                        */
14410
/************************************************************************/
14411
14412
/** Return a view of this dataset as a 3D multidimensional GDALMDArray.
14413
 *
14414
 * If this dataset is not already marked as shared, it will be, so that the
14415
 * returned array holds a reference to it.
14416
 *
14417
 * If the dataset has a geotransform attached, the X and Y dimensions of the
14418
 * returned array will have an associated indexing variable.
14419
 *
14420
 * The currently supported list of options is:
14421
 * <ul>
14422
 * <li>DIM_ORDER=&lt;order&gt; where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
14423
 * "Band,Y,X" means that the first (slowest changing) dimension is Band
14424
 * and the last (fastest changing direction) is X
14425
 * "Y,X,Band" means that the first (slowest changing) dimension is Y
14426
 * and the last (fastest changing direction) is Band.
14427
 * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
14428
 * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
14429
 * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
14430
 * "Y,X,Band" is use.
14431
 * </li>
14432
 * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|&lt;BandMetadataItem&gt;:
14433
 * item from which to build the band indexing variable.
14434
 * <ul>
14435
 * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
14436
 * <li>"{None}" means that no band indexing variable must be created.</li>
14437
 * <li>"{Index}" means that the band index (starting at one) is used.</li>
14438
 * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
14439
 * <li>&lt;BandMetadataItem&gt; is the name of a band metadata item to use.</li>
14440
 * </ul>
14441
 * </li>
14442
 * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
14443
 * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
14444
 * Defaults to String.
14445
 * </li>
14446
 * <li>BAND_DIM_NAME=&lt;string&gt;: Name of the band dimension.
14447
 * Defaults to "Band".
14448
 * </li>
14449
 * <li>X_DIM_NAME=&lt;string&gt;: Name of the X dimension. Defaults to "X".
14450
 * </li>
14451
 * <li>Y_DIM_NAME=&lt;string&gt;: Name of the Y dimension. Defaults to "Y".
14452
 * </li>
14453
 * </ul>
14454
 *
14455
 * The returned pointer must be released with GDALMDArrayRelease().
14456
 *
14457
 * The "reverse" methods are GDALRasterBand::AsMDArray() and
14458
 * GDALDataset::AsMDArray()
14459
 *
14460
 * This is the same as the C++ method GDALDataset::AsMDArray().
14461
 *
14462
 * @param hDS Dataset handle.
14463
 * @param papszOptions Null-terminated list of strings, or nullptr.
14464
 * @return a new array, or NULL.
14465
 *
14466
 * @since GDAL 3.12
14467
 */
14468
GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
14469
0
{
14470
0
    VALIDATE_POINTER1(hDS, __func__, nullptr);
14471
0
    auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
14472
0
    if (!poArray)
14473
0
        return nullptr;
14474
0
    return new GDALMDArrayHS(poArray);
14475
0
}
14476
14477
/************************************************************************/
14478
/*                    GDALMDArrayAsClassicDataset()                     */
14479
/************************************************************************/
14480
14481
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
14482
 *
14483
 * Only 2D or more arrays are supported.
14484
 *
14485
 * In the case of > 2D arrays, additional dimensions will be represented as
14486
 * raster bands.
14487
 *
14488
 * The "reverse" methods are GDALRasterBand::AsMDArray() and
14489
 * GDALDataset::AsMDArray()
14490
 *
14491
 * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14492
 *
14493
 * @param hArray Array.
14494
 * @param iXDim Index of the dimension that will be used as the X/width axis.
14495
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
14496
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14497
 */
14498
GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
14499
                                         size_t iYDim)
14500
0
{
14501
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
14502
0
    return GDALDataset::ToHandle(
14503
0
        hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
14504
0
}
14505
14506
/************************************************************************/
14507
/*                   GDALMDArrayAsClassicDatasetEx()                    */
14508
/************************************************************************/
14509
14510
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
14511
 *
14512
 * Only 2D or more arrays are supported.
14513
 *
14514
 * In the case of > 2D arrays, additional dimensions will be represented as
14515
 * raster bands.
14516
 *
14517
 * The "reverse" method is GDALRasterBand::AsMDArray().
14518
 *
14519
 * This is the same as the C++ method GDALMDArray::AsClassicDataset().
14520
 * @param hArray Array.
14521
 * @param iXDim Index of the dimension that will be used as the X/width axis.
14522
 * @param iYDim Index of the dimension that will be used as the Y/height axis.
14523
 *              Ignored if the dimension count is 1.
14524
 * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
14525
 *                   BAND_IMAGERY_METADATA option.
14526
 * @param papszOptions Cf GDALMDArray::AsClassicDataset()
14527
 * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
14528
 * @since GDAL 3.8
14529
 */
14530
GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
14531
                                           size_t iYDim, GDALGroupH hRootGroup,
14532
                                           CSLConstList papszOptions)
14533
0
{
14534
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
14535
0
    return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
14536
0
        iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
14537
0
        papszOptions));
14538
0
}
14539
14540
//! @cond Doxygen_Suppress
14541
14542
GDALAttributeString::GDALAttributeString(const std::string &osParentName,
14543
                                         const std::string &osName,
14544
                                         const std::string &osValue,
14545
                                         GDALExtendedDataTypeSubType eSubType)
14546
0
    : GDALAbstractMDArray(osParentName, osName),
14547
0
      GDALAttribute(osParentName, osName),
14548
0
      m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
14549
0
{
14550
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)
14551
14552
const std::vector<std::shared_ptr<GDALDimension>> &
14553
GDALAttributeString::GetDimensions() const
14554
0
{
14555
0
    return m_dims;
14556
0
}
14557
14558
const GDALExtendedDataType &GDALAttributeString::GetDataType() const
14559
0
{
14560
0
    return m_dt;
14561
0
}
14562
14563
bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
14564
                                const GPtrDiff_t *,
14565
                                const GDALExtendedDataType &bufferDataType,
14566
                                void *pDstBuffer) const
14567
0
{
14568
0
    if (bufferDataType.GetClass() != GEDTC_STRING)
14569
0
        return false;
14570
0
    char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
14571
0
    if (!pszStr)
14572
0
        return false;
14573
0
    memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
14574
0
    *static_cast<char **>(pDstBuffer) = pszStr;
14575
0
    return true;
14576
0
}
14577
14578
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14579
                                           const std::string &osName,
14580
                                           double dfValue)
14581
0
    : GDALAbstractMDArray(osParentName, osName),
14582
0
      GDALAttribute(osParentName, osName),
14583
0
      m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
14584
0
{
14585
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)
14586
14587
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14588
                                           const std::string &osName,
14589
                                           int nValue)
14590
0
    : GDALAbstractMDArray(osParentName, osName),
14591
0
      GDALAttribute(osParentName, osName),
14592
0
      m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
14593
0
{
14594
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)
14595
14596
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
14597
                                           const std::string &osName,
14598
                                           const std::vector<GUInt32> &anValues)
14599
0
    : GDALAbstractMDArray(osParentName, osName),
14600
0
      GDALAttribute(osParentName, osName),
14601
0
      m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
14602
0
{
14603
0
    m_dims.push_back(std::make_shared<GDALDimension>(
14604
0
        std::string(), "dim0", std::string(), std::string(),
14605
0
        m_anValuesUInt32.size()));
14606
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&)
14607
14608
const std::vector<std::shared_ptr<GDALDimension>> &
14609
GDALAttributeNumeric::GetDimensions() const
14610
0
{
14611
0
    return m_dims;
14612
0
}
14613
14614
const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
14615
0
{
14616
0
    return m_dt;
14617
0
}
14618
14619
bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
14620
                                 const size_t *count, const GInt64 *arrayStep,
14621
                                 const GPtrDiff_t *bufferStride,
14622
                                 const GDALExtendedDataType &bufferDataType,
14623
                                 void *pDstBuffer) const
14624
0
{
14625
0
    if (m_dims.empty())
14626
0
    {
14627
0
        if (m_dt.GetNumericDataType() == GDT_Float64)
14628
0
            GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
14629
0
                                            bufferDataType);
14630
0
        else
14631
0
        {
14632
0
            CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
14633
0
            GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
14634
0
                                            bufferDataType);
14635
0
        }
14636
0
    }
14637
0
    else
14638
0
    {
14639
0
        CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
14640
0
        GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14641
0
        for (size_t i = 0; i < count[0]; ++i)
14642
0
        {
14643
0
            GDALExtendedDataType::CopyValue(
14644
0
                &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
14645
0
                                                      i * arrayStep[0])],
14646
0
                m_dt, pabyDstBuffer, bufferDataType);
14647
0
            pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
14648
0
        }
14649
0
    }
14650
0
    return true;
14651
0
}
14652
14653
GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
14654
    const std::string &osParentName, const std::string &osName,
14655
    const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14656
    double dfIncrement, double dfOffsetInIncrement)
14657
0
    : GDALAbstractMDArray(osParentName, osName),
14658
0
      GDALMDArray(osParentName, osName), m_dfStart(dfStart),
14659
0
      m_dfIncrement(dfIncrement), m_dfOffsetInIncrement(dfOffsetInIncrement),
14660
0
      m_dims{poDim}
14661
0
{
14662
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)
14663
14664
std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
14665
    const std::string &osParentName, const std::string &osName,
14666
    const std::shared_ptr<GDALDimension> &poDim, double dfStart,
14667
    double dfIncrement, double dfOffsetInIncrement)
14668
0
{
14669
0
    auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
14670
0
        osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
14671
0
    poArray->SetSelf(poArray);
14672
0
    return poArray;
14673
0
}
14674
14675
const std::vector<std::shared_ptr<GDALDimension>> &
14676
GDALMDArrayRegularlySpaced::GetDimensions() const
14677
0
{
14678
0
    return m_dims;
14679
0
}
14680
14681
const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
14682
0
{
14683
0
    return m_dt;
14684
0
}
14685
14686
std::vector<std::shared_ptr<GDALAttribute>>
14687
GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
14688
0
{
14689
0
    return m_attributes;
14690
0
}
14691
14692
void GDALMDArrayRegularlySpaced::AddAttribute(
14693
    const std::shared_ptr<GDALAttribute> &poAttr)
14694
0
{
14695
0
    m_attributes.emplace_back(poAttr);
14696
0
}
14697
14698
bool GDALMDArrayRegularlySpaced::IRead(
14699
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
14700
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
14701
    void *pDstBuffer) const
14702
0
{
14703
0
    GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
14704
0
    for (size_t i = 0; i < count[0]; i++)
14705
0
    {
14706
0
        const double dfVal =
14707
0
            m_dfStart +
14708
0
            (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
14709
0
             m_dfOffsetInIncrement) *
14710
0
                m_dfIncrement;
14711
0
        GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
14712
0
                                        bufferDataType);
14713
0
        pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
14714
0
    }
14715
0
    return true;
14716
0
}
14717
14718
bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
14719
                                                   double &dfIncrement) const
14720
0
{
14721
0
    dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
14722
0
    dfIncrement = m_dfIncrement;
14723
0
    return true;
14724
0
}
14725
14726
GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
14727
    const std::string &osParentName, const std::string &osName,
14728
    const std::string &osType, const std::string &osDirection, GUInt64 nSize)
14729
0
    : GDALDimension(osParentName, osName, osType, osDirection, nSize)
14730
0
{
14731
0
}
14732
14733
std::shared_ptr<GDALMDArray>
14734
GDALDimensionWeakIndexingVar::GetIndexingVariable() const
14735
0
{
14736
0
    return m_poIndexingVariable.lock();
14737
0
}
14738
14739
// cppcheck-suppress passedByValue
14740
bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
14741
    std::shared_ptr<GDALMDArray> poIndexingVariable)
14742
0
{
14743
0
    m_poIndexingVariable = poIndexingVariable;
14744
0
    return true;
14745
0
}
14746
14747
void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
14748
0
{
14749
0
    m_nSize = nNewSize;
14750
0
}
14751
14752
/************************************************************************/
14753
/*                       GDALPamMultiDim::Private                       */
14754
/************************************************************************/
14755
14756
struct GDALPamMultiDim::Private
14757
{
14758
    std::string m_osFilename{};
14759
    std::string m_osPamFilename{};
14760
14761
    struct Statistics
14762
    {
14763
        bool bHasStats = false;
14764
        bool bApproxStats = false;
14765
        double dfMin = 0;
14766
        double dfMax = 0;
14767
        double dfMean = 0;
14768
        double dfStdDev = 0;
14769
        GUInt64 nValidCount = 0;
14770
    };
14771
14772
    struct ArrayInfo
14773
    {
14774
        std::shared_ptr<OGRSpatialReference> poSRS{};
14775
        // cppcheck-suppress unusedStructMember
14776
        Statistics stats{};
14777
    };
14778
14779
    typedef std::pair<std::string, std::string> NameContext;
14780
    std::map<NameContext, ArrayInfo> m_oMapArray{};
14781
    std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
14782
    bool m_bDirty = false;
14783
    bool m_bLoaded = false;
14784
};
14785
14786
/************************************************************************/
14787
/*                           GDALPamMultiDim                            */
14788
/************************************************************************/
14789
14790
GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
14791
0
    : d(new Private())
14792
0
{
14793
0
    d->m_osFilename = osFilename;
14794
0
}
14795
14796
/************************************************************************/
14797
/*                 GDALPamMultiDim::~GDALPamMultiDim()                  */
14798
/************************************************************************/
14799
14800
GDALPamMultiDim::~GDALPamMultiDim()
14801
0
{
14802
0
    if (d->m_bDirty)
14803
0
        Save();
14804
0
}
14805
14806
/************************************************************************/
14807
/*                       GDALPamMultiDim::Load()                        */
14808
/************************************************************************/
14809
14810
void GDALPamMultiDim::Load()
14811
0
{
14812
0
    if (d->m_bLoaded)
14813
0
        return;
14814
0
    d->m_bLoaded = true;
14815
14816
0
    const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
14817
0
    d->m_osPamFilename =
14818
0
        pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
14819
0
    CPLXMLTreeCloser oTree(nullptr);
14820
0
    {
14821
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14822
0
        oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
14823
0
    }
14824
0
    if (!oTree)
14825
0
    {
14826
0
        return;
14827
0
    }
14828
0
    const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
14829
0
    if (!poPAMMultiDim)
14830
0
        return;
14831
0
    for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
14832
0
         psIter = psIter->psNext)
14833
0
    {
14834
0
        if (psIter->eType == CXT_Element &&
14835
0
            strcmp(psIter->pszValue, "Array") == 0)
14836
0
        {
14837
0
            const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
14838
0
            if (!pszName)
14839
0
                continue;
14840
0
            const char *pszContext = CPLGetXMLValue(psIter, "context", "");
14841
0
            const auto oKey =
14842
0
                std::pair<std::string, std::string>(pszName, pszContext);
14843
14844
            /* --------------------------------------------------------------------
14845
             */
14846
            /*      Check for an SRS node. */
14847
            /* --------------------------------------------------------------------
14848
             */
14849
0
            const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
14850
0
            if (psSRSNode)
14851
0
            {
14852
0
                std::shared_ptr<OGRSpatialReference> poSRS =
14853
0
                    std::make_shared<OGRSpatialReference>();
14854
0
                poSRS->SetFromUserInput(
14855
0
                    CPLGetXMLValue(psSRSNode, nullptr, ""),
14856
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
14857
0
                const char *pszMapping = CPLGetXMLValue(
14858
0
                    psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
14859
0
                if (pszMapping)
14860
0
                {
14861
0
                    char **papszTokens =
14862
0
                        CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14863
0
                    std::vector<int> anMapping;
14864
0
                    for (int i = 0; papszTokens && papszTokens[i]; i++)
14865
0
                    {
14866
0
                        anMapping.push_back(atoi(papszTokens[i]));
14867
0
                    }
14868
0
                    CSLDestroy(papszTokens);
14869
0
                    poSRS->SetDataAxisToSRSAxisMapping(anMapping);
14870
0
                }
14871
0
                else
14872
0
                {
14873
0
                    poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14874
0
                }
14875
14876
0
                const char *pszCoordinateEpoch =
14877
0
                    CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
14878
0
                if (pszCoordinateEpoch)
14879
0
                    poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
14880
14881
0
                d->m_oMapArray[oKey].poSRS = std::move(poSRS);
14882
0
            }
14883
14884
0
            const CPLXMLNode *psStatistics =
14885
0
                CPLGetXMLNode(psIter, "Statistics");
14886
0
            if (psStatistics)
14887
0
            {
14888
0
                Private::Statistics sStats;
14889
0
                sStats.bHasStats = true;
14890
0
                sStats.bApproxStats = CPLTestBool(
14891
0
                    CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
14892
0
                sStats.dfMin =
14893
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
14894
0
                sStats.dfMax =
14895
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
14896
0
                sStats.dfMean =
14897
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
14898
0
                sStats.dfStdDev =
14899
0
                    CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
14900
0
                sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
14901
0
                    CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
14902
0
                d->m_oMapArray[oKey].stats = sStats;
14903
0
            }
14904
0
        }
14905
0
        else
14906
0
        {
14907
0
            CPLXMLNode *psNextBackup = psIter->psNext;
14908
0
            psIter->psNext = nullptr;
14909
0
            d->m_apoOtherNodes.emplace_back(
14910
0
                CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
14911
0
            psIter->psNext = psNextBackup;
14912
0
        }
14913
0
    }
14914
0
}
14915
14916
/************************************************************************/
14917
/*                       GDALPamMultiDim::Save()                        */
14918
/************************************************************************/
14919
14920
void GDALPamMultiDim::Save()
14921
0
{
14922
0
    CPLXMLTreeCloser oTree(
14923
0
        CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
14924
0
    for (const auto &poOtherNode : d->m_apoOtherNodes)
14925
0
    {
14926
0
        CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
14927
0
    }
14928
0
    for (const auto &kv : d->m_oMapArray)
14929
0
    {
14930
0
        CPLXMLNode *psArrayNode =
14931
0
            CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
14932
0
        CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
14933
0
        if (!kv.first.second.empty())
14934
0
        {
14935
0
            CPLAddXMLAttributeAndValue(psArrayNode, "context",
14936
0
                                       kv.first.second.c_str());
14937
0
        }
14938
0
        if (kv.second.poSRS)
14939
0
        {
14940
0
            char *pszWKT = nullptr;
14941
0
            {
14942
0
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
14943
0
                const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
14944
0
                kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
14945
0
            }
14946
0
            CPLXMLNode *psSRSNode =
14947
0
                CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
14948
0
            CPLFree(pszWKT);
14949
0
            const auto &mapping =
14950
0
                kv.second.poSRS->GetDataAxisToSRSAxisMapping();
14951
0
            CPLString osMapping;
14952
0
            for (size_t i = 0; i < mapping.size(); ++i)
14953
0
            {
14954
0
                if (!osMapping.empty())
14955
0
                    osMapping += ",";
14956
0
                osMapping += CPLSPrintf("%d", mapping[i]);
14957
0
            }
14958
0
            CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
14959
0
                                       osMapping.c_str());
14960
14961
0
            const double dfCoordinateEpoch =
14962
0
                kv.second.poSRS->GetCoordinateEpoch();
14963
0
            if (dfCoordinateEpoch > 0)
14964
0
            {
14965
0
                std::string osCoordinateEpoch =
14966
0
                    CPLSPrintf("%f", dfCoordinateEpoch);
14967
0
                if (osCoordinateEpoch.find('.') != std::string::npos)
14968
0
                {
14969
0
                    while (osCoordinateEpoch.back() == '0')
14970
0
                        osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
14971
0
                }
14972
0
                CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
14973
0
                                           osCoordinateEpoch.c_str());
14974
0
            }
14975
0
        }
14976
14977
0
        if (kv.second.stats.bHasStats)
14978
0
        {
14979
0
            CPLXMLNode *psMDArray =
14980
0
                CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
14981
0
            CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
14982
0
                                        kv.second.stats.bApproxStats ? "1"
14983
0
                                                                     : "0");
14984
0
            CPLCreateXMLElementAndValue(
14985
0
                psMDArray, "Minimum",
14986
0
                CPLSPrintf("%.17g", kv.second.stats.dfMin));
14987
0
            CPLCreateXMLElementAndValue(
14988
0
                psMDArray, "Maximum",
14989
0
                CPLSPrintf("%.17g", kv.second.stats.dfMax));
14990
0
            CPLCreateXMLElementAndValue(
14991
0
                psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
14992
0
            CPLCreateXMLElementAndValue(
14993
0
                psMDArray, "StdDev",
14994
0
                CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
14995
0
            CPLCreateXMLElementAndValue(
14996
0
                psMDArray, "ValidSampleCount",
14997
0
                CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
14998
0
        }
14999
0
    }
15000
15001
0
    int bSaved;
15002
0
    CPLErrorAccumulator oErrorAccumulator;
15003
0
    {
15004
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
15005
0
        CPL_IGNORE_RET_VAL(oAccumulator);
15006
0
        bSaved =
15007
0
            CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
15008
0
    }
15009
15010
0
    const char *pszNewPam = nullptr;
15011
0
    if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
15012
0
        ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
15013
0
    {
15014
0
        CPLErrorReset();
15015
0
        CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
15016
0
    }
15017
0
    else
15018
0
    {
15019
0
        oErrorAccumulator.ReplayErrors();
15020
0
    }
15021
0
}
15022
15023
/************************************************************************/
15024
/*                   GDALPamMultiDim::GetSpatialRef()                   */
15025
/************************************************************************/
15026
15027
std::shared_ptr<OGRSpatialReference>
15028
GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
15029
                               const std::string &osContext)
15030
0
{
15031
0
    Load();
15032
0
    auto oIter =
15033
0
        d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15034
0
    if (oIter != d->m_oMapArray.end())
15035
0
        return oIter->second.poSRS;
15036
0
    return nullptr;
15037
0
}
15038
15039
/************************************************************************/
15040
/*                   GDALPamMultiDim::SetSpatialRef()                   */
15041
/************************************************************************/
15042
15043
void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
15044
                                    const std::string &osContext,
15045
                                    const OGRSpatialReference *poSRS)
15046
0
{
15047
0
    Load();
15048
0
    d->m_bDirty = true;
15049
0
    if (poSRS && !poSRS->IsEmpty())
15050
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
15051
0
            poSRS->Clone());
15052
0
    else
15053
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
15054
0
            .poSRS.reset();
15055
0
}
15056
15057
/************************************************************************/
15058
/*                           GetStatistics()                            */
15059
/************************************************************************/
15060
15061
CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
15062
                                      const std::string &osContext,
15063
                                      bool bApproxOK, double *pdfMin,
15064
                                      double *pdfMax, double *pdfMean,
15065
                                      double *pdfStdDev, GUInt64 *pnValidCount)
15066
0
{
15067
0
    Load();
15068
0
    auto oIter =
15069
0
        d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
15070
0
    if (oIter == d->m_oMapArray.end())
15071
0
        return CE_Failure;
15072
0
    const auto &stats = oIter->second.stats;
15073
0
    if (!stats.bHasStats)
15074
0
        return CE_Failure;
15075
0
    if (!bApproxOK && stats.bApproxStats)
15076
0
        return CE_Failure;
15077
0
    if (pdfMin)
15078
0
        *pdfMin = stats.dfMin;
15079
0
    if (pdfMax)
15080
0
        *pdfMax = stats.dfMax;
15081
0
    if (pdfMean)
15082
0
        *pdfMean = stats.dfMean;
15083
0
    if (pdfStdDev)
15084
0
        *pdfStdDev = stats.dfStdDev;
15085
0
    if (pnValidCount)
15086
0
        *pnValidCount = stats.nValidCount;
15087
0
    return CE_None;
15088
0
}
15089
15090
/************************************************************************/
15091
/*                           SetStatistics()                            */
15092
/************************************************************************/
15093
15094
void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
15095
                                    const std::string &osContext,
15096
                                    bool bApproxStats, double dfMin,
15097
                                    double dfMax, double dfMean,
15098
                                    double dfStdDev, GUInt64 nValidCount)
15099
0
{
15100
0
    Load();
15101
0
    d->m_bDirty = true;
15102
0
    auto &stats =
15103
0
        d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
15104
0
    stats.bHasStats = true;
15105
0
    stats.bApproxStats = bApproxStats;
15106
0
    stats.dfMin = dfMin;
15107
0
    stats.dfMax = dfMax;
15108
0
    stats.dfMean = dfMean;
15109
0
    stats.dfStdDev = dfStdDev;
15110
0
    stats.nValidCount = nValidCount;
15111
0
}
15112
15113
/************************************************************************/
15114
/*                          ClearStatistics()                           */
15115
/************************************************************************/
15116
15117
void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
15118
                                      const std::string &osContext)
15119
0
{
15120
0
    Load();
15121
0
    d->m_bDirty = true;
15122
0
    d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
15123
0
        false;
15124
0
}
15125
15126
/************************************************************************/
15127
/*                          ClearStatistics()                           */
15128
/************************************************************************/
15129
15130
void GDALPamMultiDim::ClearStatistics()
15131
0
{
15132
0
    Load();
15133
0
    d->m_bDirty = true;
15134
0
    for (auto &kv : d->m_oMapArray)
15135
0
        kv.second.stats.bHasStats = false;
15136
0
}
15137
15138
/************************************************************************/
15139
/*                               GetPAM()                               */
15140
/************************************************************************/
15141
15142
/*static*/ std::shared_ptr<GDALPamMultiDim>
15143
GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
15144
0
{
15145
0
    auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
15146
0
    if (poPamArray)
15147
0
        return poPamArray->GetPAM();
15148
0
    return nullptr;
15149
0
}
15150
15151
/************************************************************************/
15152
/*                            GDALPamMDArray                            */
15153
/************************************************************************/
15154
15155
GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
15156
                               const std::string &osName,
15157
                               const std::shared_ptr<GDALPamMultiDim> &poPam,
15158
                               const std::string &osContext)
15159
    :
15160
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
15161
      GDALAbstractMDArray(osParentName, osName),
15162
#endif
15163
0
      GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
15164
0
{
15165
0
}
15166
15167
/************************************************************************/
15168
/*                   GDALPamMDArray::SetSpatialRef()                    */
15169
/************************************************************************/
15170
15171
bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
15172
0
{
15173
0
    if (!m_poPam)
15174
0
        return false;
15175
0
    m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
15176
0
    return true;
15177
0
}
15178
15179
/************************************************************************/
15180
/*                   GDALPamMDArray::GetSpatialRef()                    */
15181
/************************************************************************/
15182
15183
std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
15184
0
{
15185
0
    if (!m_poPam)
15186
0
        return nullptr;
15187
0
    return m_poPam->GetSpatialRef(GetFullName(), GetContext());
15188
0
}
15189
15190
/************************************************************************/
15191
/*                           GetStatistics()                            */
15192
/************************************************************************/
15193
15194
CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
15195
                                     double *pdfMin, double *pdfMax,
15196
                                     double *pdfMean, double *pdfStdDev,
15197
                                     GUInt64 *pnValidCount,
15198
                                     GDALProgressFunc pfnProgress,
15199
                                     void *pProgressData)
15200
0
{
15201
0
    if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
15202
0
                                          bApproxOK, pdfMin, pdfMax, pdfMean,
15203
0
                                          pdfStdDev, pnValidCount) == CE_None)
15204
0
    {
15205
0
        return CE_None;
15206
0
    }
15207
0
    if (!bForce)
15208
0
        return CE_Warning;
15209
15210
0
    return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
15211
0
                                      pdfMean, pdfStdDev, pnValidCount,
15212
0
                                      pfnProgress, pProgressData);
15213
0
}
15214
15215
/************************************************************************/
15216
/*                           SetStatistics()                            */
15217
/************************************************************************/
15218
15219
bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
15220
                                   double dfMax, double dfMean, double dfStdDev,
15221
                                   GUInt64 nValidCount,
15222
                                   CSLConstList /* papszOptions */)
15223
0
{
15224
0
    if (!m_poPam)
15225
0
        return false;
15226
0
    m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
15227
0
                           dfMax, dfMean, dfStdDev, nValidCount);
15228
0
    return true;
15229
0
}
15230
15231
/************************************************************************/
15232
/*                          ClearStatistics()                           */
15233
/************************************************************************/
15234
15235
void GDALPamMDArray::ClearStatistics()
15236
0
{
15237
0
    if (!m_poPam)
15238
0
        return;
15239
0
    m_poPam->ClearStatistics(GetFullName(), GetContext());
15240
0
}
15241
15242
/************************************************************************/
15243
/*                 GDALMDIAsAttribute::GetDimensions()                  */
15244
/************************************************************************/
15245
15246
const std::vector<std::shared_ptr<GDALDimension>> &
15247
GDALMDIAsAttribute::GetDimensions() const
15248
0
{
15249
0
    return m_dims;
15250
0
}
15251
15252
/************************************************************************/
15253
/*         GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()          */
15254
/************************************************************************/
15255
15256
GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
15257
0
{
15258
0
    clear();
15259
0
}
15260
15261
/************************************************************************/
15262
/*                   GDALMDArrayRawBlockInfo::clear()                   */
15263
/************************************************************************/
15264
15265
void GDALMDArrayRawBlockInfo::clear()
15266
0
{
15267
0
    CPLFree(pszFilename);
15268
0
    pszFilename = nullptr;
15269
0
    CSLDestroy(papszInfo);
15270
0
    papszInfo = nullptr;
15271
0
    nOffset = 0;
15272
0
    nSize = 0;
15273
0
    CPLFree(pabyInlineData);
15274
0
    pabyInlineData = nullptr;
15275
0
}
15276
15277
/************************************************************************/
15278
/*          GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()          */
15279
/************************************************************************/
15280
15281
GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15282
    const GDALMDArrayRawBlockInfo &other)
15283
0
    : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
15284
0
      nOffset(other.nOffset), nSize(other.nSize),
15285
0
      papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
15286
0
{
15287
0
    if (other.pabyInlineData)
15288
0
    {
15289
0
        pabyInlineData = static_cast<GByte *>(
15290
0
            VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15291
0
        if (pabyInlineData)
15292
0
            memcpy(pabyInlineData, other.pabyInlineData,
15293
0
                   static_cast<size_t>(other.nSize));
15294
0
    }
15295
0
}
15296
15297
/************************************************************************/
15298
/*                 GDALMDArrayRawBlockInfo::operator=()                 */
15299
/************************************************************************/
15300
15301
GDALMDArrayRawBlockInfo &
15302
GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
15303
0
{
15304
0
    if (this != &other)
15305
0
    {
15306
0
        CPLFree(pszFilename);
15307
0
        pszFilename =
15308
0
            other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
15309
0
        nOffset = other.nOffset;
15310
0
        nSize = other.nSize;
15311
0
        CSLDestroy(papszInfo);
15312
0
        papszInfo = CSLDuplicate(other.papszInfo);
15313
0
        CPLFree(pabyInlineData);
15314
0
        pabyInlineData = nullptr;
15315
0
        if (other.pabyInlineData)
15316
0
        {
15317
0
            pabyInlineData = static_cast<GByte *>(
15318
0
                VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
15319
0
            if (pabyInlineData)
15320
0
                memcpy(pabyInlineData, other.pabyInlineData,
15321
0
                       static_cast<size_t>(other.nSize));
15322
0
        }
15323
0
    }
15324
0
    return *this;
15325
0
}
15326
15327
/************************************************************************/
15328
/*          GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()          */
15329
/************************************************************************/
15330
15331
GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
15332
    GDALMDArrayRawBlockInfo &&other)
15333
0
    : pszFilename(other.pszFilename), nOffset(other.nOffset),
15334
0
      nSize(other.nSize), papszInfo(other.papszInfo),
15335
0
      pabyInlineData(other.pabyInlineData)
15336
0
{
15337
0
    other.pszFilename = nullptr;
15338
0
    other.papszInfo = nullptr;
15339
0
    other.pabyInlineData = nullptr;
15340
0
}
15341
15342
/************************************************************************/
15343
/*                 GDALMDArrayRawBlockInfo::operator=()                 */
15344
/************************************************************************/
15345
15346
GDALMDArrayRawBlockInfo &
15347
GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
15348
0
{
15349
0
    if (this != &other)
15350
0
    {
15351
0
        std::swap(pszFilename, other.pszFilename);
15352
0
        nOffset = other.nOffset;
15353
0
        nSize = other.nSize;
15354
0
        std::swap(papszInfo, other.papszInfo);
15355
0
        std::swap(pabyInlineData, other.pabyInlineData);
15356
0
    }
15357
0
    return *this;
15358
0
}
15359
15360
//! @endcond
15361
15362
/************************************************************************/
15363
/*                    GDALMDArray::GetRawBlockInfo()                    */
15364
/************************************************************************/
15365
15366
/** Return information on a raw block.
15367
 *
15368
 * The block coordinates must be between 0 and
15369
 * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15370
 * 0 and GetDimensionCount()-1.
15371
 *
15372
 * If the queried block has valid coordinates but is missing in the dataset,
15373
 * all fields of info will be set to 0/nullptr, but the function will return
15374
 * true.
15375
 *
15376
 * This method is only implemented by a subset of drivers. The base
15377
 * implementation just returns false and empty info.
15378
 *
15379
 * The values returned in psBlockInfo->papszInfo are driver dependent.
15380
 *
15381
 * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15382
 * value is "LITTLE" or "BIG".
15383
 *
15384
 * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15385
 * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15386
 * comma-separated)
15387
 *
15388
 * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15389
 * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15390
 * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15391
 * "[idx0,...,idxN]" with the permutation).
15392
 *
15393
 * For VRT, the potential keys are the ones of the underlying source(s). Note
15394
 * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15395
 * that for each queried VRT block, there is one and only one source that
15396
 * is used to fill the VRT block and that the block size of this source is
15397
 * exactly the one of the VRT block.
15398
 *
15399
 * This is the same as C function GDALMDArrayGetRawBlockInfo().
15400
 *
15401
 * @param panBlockCoordinates array of GetDimensionCount() values with the block
15402
 *                            coordinates.
15403
 * @param[out] info structure to fill with block information.
15404
 * @return true in case of success, or false if an error occurs.
15405
 * @since 3.12
15406
 */
15407
bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
15408
                                  GDALMDArrayRawBlockInfo &info) const
15409
0
{
15410
0
    (void)panBlockCoordinates;
15411
0
    info.clear();
15412
0
    return false;
15413
0
}
15414
15415
/************************************************************************/
15416
/*                     GDALMDArrayGetRawBlockInfo()                     */
15417
/************************************************************************/
15418
15419
/** Return information on a raw block.
15420
 *
15421
 * The block coordinates must be between 0 and
15422
 * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
15423
 * 0 and GetDimensionCount()-1.
15424
 *
15425
 * If the queried block has valid coordinates but is missing in the dataset,
15426
 * all fields of info will be set to 0/nullptr, but the function will return
15427
 * true.
15428
 *
15429
 * This method is only implemented by a subset of drivers. The base
15430
 * implementation just returns false and empty info.
15431
 *
15432
 * The values returned in psBlockInfo->papszInfo are driver dependent.
15433
 *
15434
 * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
15435
 * value is "LITTLE" or "BIG".
15436
 *
15437
 * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
15438
 * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
15439
 * comma-separated)
15440
 *
15441
 * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
15442
 * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
15443
 * encoded content) and "TRANSPOSE_ORDER" (value is a string like
15444
 * "[idx0,...,idxN]" with the permutation).
15445
 *
15446
 * For VRT, the potential keys are the ones of the underlying source(s). Note
15447
 * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
15448
 * that for each queried VRT block, there is one and only one source that
15449
 * is used to fill the VRT block and that the block size of this source is
15450
 * exactly the one of the VRT block.
15451
 *
15452
 * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
15453
 *
15454
 * @param hArray handle to array.
15455
 * @param panBlockCoordinates array of GetDimensionCount() values with the block
15456
 *                            coordinates.
15457
 * @param[out] psBlockInfo structure to fill with block information.
15458
 *                         Must be allocated with GDALMDArrayRawBlockInfoCreate(),
15459
 *                         and freed with GDALMDArrayRawBlockInfoRelease().
15460
 * @return true in case of success, or false if an error occurs.
15461
 * @since 3.12
15462
 */
15463
bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
15464
                                const uint64_t *panBlockCoordinates,
15465
                                GDALMDArrayRawBlockInfo *psBlockInfo)
15466
0
{
15467
0
    VALIDATE_POINTER1(hArray, __func__, false);
15468
0
    VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
15469
0
    VALIDATE_POINTER1(psBlockInfo, __func__, false);
15470
0
    return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
15471
0
}
15472
15473
/************************************************************************/
15474
/*                   GDALMDArrayRawBlockInfoCreate()                    */
15475
/************************************************************************/
15476
15477
/** Allocate a new instance of GDALMDArrayRawBlockInfo.
15478
 *
15479
 * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
15480
 *
15481
 * @since 3.12
15482
 */
15483
GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
15484
0
{
15485
0
    return new GDALMDArrayRawBlockInfo();
15486
0
}
15487
15488
/************************************************************************/
15489
/*                   GDALMDArrayRawBlockInfoRelease()                   */
15490
/************************************************************************/
15491
15492
/** Free an instance of GDALMDArrayRawBlockInfo.
15493
 *
15494
 * @since 3.12
15495
 */
15496
void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
15497
0
{
15498
0
    delete psBlockInfo;
15499
0
}
15500
15501
/************************************************************************/
15502
/*                   GDALMDArray::GetOverviewCount()                    */
15503
/************************************************************************/
15504
15505
/**
15506
 * \brief Return the number of overview arrays available.
15507
 *
15508
 * This method is the same as the C function GDALMDArrayGetOverviewCount().
15509
 *
15510
 * @return overview count, zero if none.
15511
 *
15512
 * @since 3.13
15513
 */
15514
15515
int GDALMDArray::GetOverviewCount() const
15516
0
{
15517
0
    return 0;
15518
0
}
15519
15520
/************************************************************************/
15521
/*                    GDALMDArrayGetOverviewCount()                     */
15522
/************************************************************************/
15523
/**
15524
 * \brief Return the number of overview arrays available.
15525
 *
15526
 * This method is the same as the C++ method GDALMDArray::GetOverviewCount().
15527
 *
15528
 * @param hArray Array.
15529
 * @return overview count, zero if none.
15530
 *
15531
 * @since 3.13
15532
 */
15533
15534
int GDALMDArrayGetOverviewCount(GDALMDArrayH hArray)
15535
0
{
15536
0
    VALIDATE_POINTER1(hArray, __func__, 0);
15537
0
    return hArray->m_poImpl->GetOverviewCount();
15538
0
}
15539
15540
/************************************************************************/
15541
/*                      GDALMDArray::GetOverview()                      */
15542
/************************************************************************/
15543
15544
/**
15545
 * \brief Get overview array object.
15546
 *
15547
 * This method is the same as the C function GDALMDArrayGetOverview().
15548
 *
15549
 * @param nIdx overview index between 0 and GetOverviewCount()-1.
15550
 *
15551
 * @return overview GDALMDArray, or nullptr
15552
 *
15553
 * @since 3.13
15554
 */
15555
15556
std::shared_ptr<GDALMDArray> GDALMDArray::GetOverview(int nIdx) const
15557
0
{
15558
0
    (void)nIdx;
15559
0
    return nullptr;
15560
0
}
15561
15562
/************************************************************************/
15563
/*                       GDALMDArrayGetOverview()                       */
15564
/************************************************************************/
15565
15566
/**
15567
 * \brief Get overview array object.
15568
 *
15569
 * This method is the same as the C++ method GDALMDArray::GetOverview().
15570
 *
15571
 * @param hArray Array.
15572
 * @param nIdx overview index between 0 and GDALMDArrayGetOverviewCount()-1.
15573
 *
15574
 * @return overview GDALMDArray, or nullptr.
15575
 * Must be released with GDALMDArrayRelease()
15576
 *
15577
 * @since 3.13
15578
 */
15579
15580
GDALMDArrayH GDALMDArrayGetOverview(GDALMDArrayH hArray, int nIdx)
15581
0
{
15582
0
    VALIDATE_POINTER1(hArray, __func__, nullptr);
15583
0
    auto poOverview = hArray->m_poImpl->GetOverview(nIdx);
15584
0
    if (!poOverview)
15585
0
        return nullptr;
15586
0
    return new GDALMDArrayHS(poOverview);
15587
0
}