Coverage Report

Created: 2025-06-13 06:18

/src/gdal/frmts/vrt/vrtmultidim.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Name:     vrtmultidim.cpp
4
 * Purpose:  Implementation of VRTDriver
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
/*! @cond Doxygen_Suppress */
14
15
#include <algorithm>
16
#include <limits>
17
#include <mutex>
18
#include <unordered_set>
19
#include <utility>
20
21
#include "cpl_mem_cache.h"
22
#include "cpl_minixml.h"
23
#include "cpl_multiproc.h"
24
#include "vrtdataset.h"
25
26
0
VRTMDArraySource::~VRTMDArraySource() = default;
27
28
static std::shared_ptr<GDALMDArray> ParseArray(const CPLXMLNode *psTree,
29
                                               const char *pszVRTPath,
30
                                               const char *pszParentXMLNode);
31
32
struct VRTArrayDatasetWrapper
33
{
34
    VRTArrayDatasetWrapper(const VRTArrayDatasetWrapper &) = delete;
35
    VRTArrayDatasetWrapper &operator=(const VRTArrayDatasetWrapper &) = delete;
36
37
    GDALDataset *m_poDS;
38
39
0
    explicit VRTArrayDatasetWrapper(GDALDataset *poDS) : m_poDS(poDS)
40
0
    {
41
0
        CPLDebug("VRT", "Open %s", poDS->GetDescription());
42
0
    }
43
44
    ~VRTArrayDatasetWrapper()
45
0
    {
46
0
        CPLDebug("VRT", "Close %s", m_poDS->GetDescription());
47
0
        delete m_poDS;
48
0
    }
49
50
    GDALDataset *get() const
51
0
    {
52
0
        return m_poDS;
53
0
    }
54
};
55
56
typedef std::pair<std::shared_ptr<VRTArrayDatasetWrapper>,
57
                  std::unordered_set<const void *>>
58
    CacheEntry;
59
static std::mutex g_cacheLock;
60
static lru11::Cache<std::string, CacheEntry> g_cacheSources(100);
61
62
/************************************************************************/
63
/*                            GetRootGroup()                            */
64
/************************************************************************/
65
66
std::shared_ptr<GDALGroup> VRTDataset::GetRootGroup() const
67
0
{
68
0
    return m_poRootGroup;
69
0
}
70
71
/************************************************************************/
72
/*                              VRTGroup()                              */
73
/************************************************************************/
74
75
VRTGroup::VRTGroup(const char *pszVRTPath)
76
0
    : GDALGroup(std::string(), std::string()),
77
0
      m_poRefSelf(std::make_shared<Ref>(this)), m_osVRTPath(pszVRTPath)
78
0
{
79
0
}
80
81
/************************************************************************/
82
/*                              VRTGroup()                              */
83
/************************************************************************/
84
85
VRTGroup::VRTGroup(const std::string &osParentName, const std::string &osName)
86
0
    : GDALGroup(osParentName, osName), m_poRefSelf(std::make_shared<Ref>(this))
87
0
{
88
0
}
89
90
/************************************************************************/
91
/*                             ~VRTGroup()                              */
92
/************************************************************************/
93
94
VRTGroup::~VRTGroup()
95
0
{
96
0
    if (m_poSharedRefRootGroup)
97
0
    {
98
0
        VRTGroup::Serialize();
99
0
    }
100
0
}
101
102
/************************************************************************/
103
/*                         SetIsRootGroup()                             */
104
/************************************************************************/
105
106
void VRTGroup::SetIsRootGroup()
107
0
{
108
0
    m_poSharedRefRootGroup = std::make_shared<Ref>(this);
109
0
}
110
111
/************************************************************************/
112
/*                         SetRootGroupRef()                            */
113
/************************************************************************/
114
115
void VRTGroup::SetRootGroupRef(const std::weak_ptr<Ref> &rgRef)
116
0
{
117
0
    m_poWeakRefRootGroup = rgRef;
118
0
}
119
120
/************************************************************************/
121
/*                          GetRootGroupRef()                           */
122
/************************************************************************/
123
124
std::weak_ptr<VRTGroup::Ref> VRTGroup::GetRootGroupRef() const
125
0
{
126
0
    return m_poSharedRefRootGroup ? m_poSharedRefRootGroup
127
0
                                  : m_poWeakRefRootGroup;
128
0
}
129
130
/************************************************************************/
131
/*                           GetRootGroup()                             */
132
/************************************************************************/
133
134
VRTGroup *VRTGroup::GetRootGroup() const
135
0
{
136
0
    if (m_poSharedRefRootGroup)
137
0
        return m_poSharedRefRootGroup->m_ptr;
138
0
    auto ref(m_poWeakRefRootGroup.lock());
139
0
    return ref ? ref->m_ptr : nullptr;
140
0
}
141
142
/************************************************************************/
143
/*                       GetRootGroupSharedPtr()                        */
144
/************************************************************************/
145
146
std::shared_ptr<GDALGroup> VRTGroup::GetRootGroupSharedPtr() const
147
0
{
148
0
    auto group = GetRootGroup();
149
0
    if (group)
150
0
        return group->m_pSelf.lock();
151
0
    return nullptr;
152
0
}
153
154
/************************************************************************/
155
/*                               XMLInit()                              */
156
/************************************************************************/
157
158
bool VRTGroup::XMLInit(const std::shared_ptr<VRTGroup> &poRoot,
159
                       const std::shared_ptr<VRTGroup> &poThisGroup,
160
                       const CPLXMLNode *psNode, const char *pszVRTPath)
161
0
{
162
0
    if (pszVRTPath != nullptr)
163
0
        m_osVRTPath = pszVRTPath;
164
165
0
    for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
166
0
    {
167
0
        if (psIter->eType == CXT_Element &&
168
0
            strcmp(psIter->pszValue, "Group") == 0)
169
0
        {
170
0
            const char *pszSubGroupName =
171
0
                CPLGetXMLValue(psIter, "name", nullptr);
172
0
            if (pszSubGroupName == nullptr)
173
0
            {
174
0
                CPLError(CE_Failure, CPLE_AppDefined,
175
0
                         "Missing name attribute on Group");
176
0
                m_bDirty = false;
177
0
                return false;
178
0
            }
179
0
            auto poSubGroup(std::dynamic_pointer_cast<VRTGroup>(
180
0
                CreateGroup(pszSubGroupName)));
181
0
            if (poSubGroup == nullptr ||
182
0
                !poSubGroup->XMLInit(poRoot, poSubGroup, psIter,
183
0
                                     m_osVRTPath.c_str()))
184
0
            {
185
0
                m_bDirty = false;
186
0
                return false;
187
0
            }
188
0
        }
189
0
        else if (psIter->eType == CXT_Element &&
190
0
                 strcmp(psIter->pszValue, "Dimension") == 0)
191
0
        {
192
0
            auto poDim = VRTDimension::Create(
193
0
                poThisGroup, poThisGroup->GetFullName(), psIter);
194
0
            if (!poDim)
195
0
            {
196
0
                m_bDirty = false;
197
0
                return false;
198
0
            }
199
0
            m_oMapDimensions[poDim->GetName()] = poDim;
200
0
        }
201
0
        else if (psIter->eType == CXT_Element &&
202
0
                 strcmp(psIter->pszValue, "Attribute") == 0)
203
0
        {
204
0
            auto poAttr =
205
0
                VRTAttribute::Create(poThisGroup->GetFullName(), psIter);
206
0
            if (!poAttr)
207
0
            {
208
0
                m_bDirty = false;
209
0
                return false;
210
0
            }
211
0
            m_oMapAttributes[poAttr->GetName()] = poAttr;
212
0
        }
213
0
        else if (psIter->eType == CXT_Element &&
214
0
                 strcmp(psIter->pszValue, "Array") == 0)
215
0
        {
216
0
            auto poArray = VRTMDArray::Create(
217
0
                poThisGroup, poThisGroup->GetFullName(), psIter);
218
0
            if (!poArray)
219
0
            {
220
0
                m_bDirty = false;
221
0
                return false;
222
0
            }
223
0
            m_oMapMDArrays[poArray->GetName()] = poArray;
224
0
        }
225
0
    }
226
227
0
    m_bDirty = false;
228
0
    return true;
229
0
}
230
231
/************************************************************************/
232
/*                             Serialize()                              */
233
/************************************************************************/
234
235
bool VRTGroup::Serialize() const
236
0
{
237
0
    if (!m_bDirty || m_osFilename.empty())
238
0
        return true;
239
0
    m_bDirty = false;
240
241
    /* -------------------------------------------------------------------- */
242
    /*      Create the output file.                                         */
243
    /* -------------------------------------------------------------------- */
244
0
    VSILFILE *fpVRT = VSIFOpenL(m_osFilename.c_str(), "w");
245
0
    if (fpVRT == nullptr)
246
0
    {
247
0
        CPLError(CE_Failure, CPLE_AppDefined,
248
0
                 "Failed to write .vrt file in Serialize().");
249
0
        return false;
250
0
    }
251
252
0
    CPLXMLNode *psDSTree = SerializeToXML(m_osVRTPath.c_str());
253
0
    char *pszXML = CPLSerializeXMLTree(psDSTree);
254
255
0
    CPLDestroyXMLNode(psDSTree);
256
257
0
    bool bOK = true;
258
0
    if (pszXML)
259
0
    {
260
        /* ------------------------------------------------------------------ */
261
        /*      Write to disk.                                                */
262
        /* ------------------------------------------------------------------ */
263
0
        bOK &= VSIFWriteL(pszXML, 1, strlen(pszXML), fpVRT) == strlen(pszXML);
264
0
        CPLFree(pszXML);
265
0
    }
266
0
    if (VSIFCloseL(fpVRT) != 0)
267
0
        bOK = false;
268
0
    if (!bOK)
269
0
    {
270
0
        CPLError(CE_Failure, CPLE_AppDefined,
271
0
                 "Failed to write .vrt file in Serialize().");
272
0
    }
273
0
    return bOK;
274
0
}
275
276
/************************************************************************/
277
/*                           SerializeToXML()                           */
278
/************************************************************************/
279
280
CPLXMLNode *VRTGroup::SerializeToXML(const char *pszVRTPath) const
281
0
{
282
0
    CPLXMLNode *psDSTree = CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset");
283
0
    Serialize(psDSTree, pszVRTPath);
284
0
    return psDSTree;
285
0
}
286
287
/************************************************************************/
288
/*                             Serialize()                              */
289
/************************************************************************/
290
291
void VRTGroup::Serialize(CPLXMLNode *psParent, const char *pszVRTPath) const
292
0
{
293
0
    CPLXMLNode *psGroup = CPLCreateXMLNode(psParent, CXT_Element, "Group");
294
0
    CPLAddXMLAttributeAndValue(psGroup, "name", GetName().c_str());
295
0
    for (const auto &iter : m_oMapDimensions)
296
0
    {
297
0
        iter.second->Serialize(psGroup);
298
0
    }
299
0
    for (const auto &iter : m_oMapAttributes)
300
0
    {
301
0
        iter.second->Serialize(psGroup);
302
0
    }
303
0
    for (const auto &iter : m_oMapMDArrays)
304
0
    {
305
0
        iter.second->Serialize(psGroup, pszVRTPath);
306
0
    }
307
0
    for (const auto &iter : m_oMapGroups)
308
0
    {
309
0
        iter.second->Serialize(psGroup, pszVRTPath);
310
0
    }
311
0
}
312
313
/************************************************************************/
314
/*                            GetGroupNames()                           */
315
/************************************************************************/
316
317
std::vector<std::string> VRTGroup::GetGroupNames(CSLConstList) const
318
0
{
319
0
    std::vector<std::string> names;
320
0
    for (const auto &iter : m_oMapGroups)
321
0
        names.push_back(iter.first);
322
0
    return names;
323
0
}
324
325
/************************************************************************/
326
/*                         OpenGroupInternal()                          */
327
/************************************************************************/
328
329
std::shared_ptr<VRTGroup>
330
VRTGroup::OpenGroupInternal(const std::string &osName) const
331
0
{
332
0
    auto oIter = m_oMapGroups.find(osName);
333
0
    if (oIter != m_oMapGroups.end())
334
0
        return oIter->second;
335
0
    return nullptr;
336
0
}
337
338
/************************************************************************/
339
/*                            GetDimensions()                           */
340
/************************************************************************/
341
342
std::vector<std::shared_ptr<GDALDimension>>
343
VRTGroup::GetDimensions(CSLConstList) const
344
0
{
345
0
    std::vector<std::shared_ptr<GDALDimension>> oRes;
346
0
    for (const auto &oIter : m_oMapDimensions)
347
0
    {
348
0
        oRes.push_back(oIter.second);
349
0
    }
350
0
    return oRes;
351
0
}
352
353
/************************************************************************/
354
/*                    GetDimensionFromFullName()                   */
355
/************************************************************************/
356
357
std::shared_ptr<VRTDimension>
358
VRTGroup::GetDimensionFromFullName(const std::string &name,
359
                                   bool bEmitError) const
360
0
{
361
0
    if (name[0] != '/')
362
0
    {
363
0
        auto poDim(GetDimension(name));
364
0
        if (!poDim)
365
0
        {
366
0
            if (bEmitError)
367
0
            {
368
0
                CPLError(CE_Failure, CPLE_AppDefined,
369
0
                         "Cannot find dimension %s in this group",
370
0
                         name.c_str());
371
0
            }
372
0
            return nullptr;
373
0
        }
374
0
        return poDim;
375
0
    }
376
0
    else
377
0
    {
378
0
        auto curGroup(GetRootGroup());
379
0
        if (curGroup == nullptr)
380
0
        {
381
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
382
0
            return nullptr;
383
0
        }
384
0
        CPLStringList aosTokens(CSLTokenizeString2(name.c_str(), "/", 0));
385
0
        for (int i = 0; i < aosTokens.size() - 1; i++)
386
0
        {
387
0
            curGroup = curGroup->OpenGroupInternal(aosTokens[i]).get();
388
0
            if (!curGroup)
389
0
            {
390
0
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
391
0
                         aosTokens[i]);
392
0
                return nullptr;
393
0
            }
394
0
        }
395
0
        auto poDim(curGroup->GetDimension(aosTokens.back()));
396
0
        if (!poDim)
397
0
        {
398
0
            if (bEmitError)
399
0
            {
400
0
                CPLError(CE_Failure, CPLE_AppDefined,
401
0
                         "Cannot find dimension %s", name.c_str());
402
0
            }
403
0
            return nullptr;
404
0
        }
405
0
        return poDim;
406
0
    }
407
0
}
408
409
/************************************************************************/
410
/*                            GetAttributes()                           */
411
/************************************************************************/
412
413
std::vector<std::shared_ptr<GDALAttribute>>
414
VRTGroup::GetAttributes(CSLConstList) const
415
0
{
416
0
    std::vector<std::shared_ptr<GDALAttribute>> oRes;
417
0
    for (const auto &oIter : m_oMapAttributes)
418
0
    {
419
0
        oRes.push_back(oIter.second);
420
0
    }
421
0
    return oRes;
422
0
}
423
424
/************************************************************************/
425
/*                           GetMDArrayNames()                          */
426
/************************************************************************/
427
428
std::vector<std::string> VRTGroup::GetMDArrayNames(CSLConstList) const
429
0
{
430
0
    std::vector<std::string> names;
431
0
    for (const auto &iter : m_oMapMDArrays)
432
0
        names.push_back(iter.first);
433
0
    return names;
434
0
}
435
436
/************************************************************************/
437
/*                             OpenMDArray()                            */
438
/************************************************************************/
439
440
std::shared_ptr<GDALMDArray> VRTGroup::OpenMDArray(const std::string &osName,
441
                                                   CSLConstList) const
442
0
{
443
0
    auto oIter = m_oMapMDArrays.find(osName);
444
0
    if (oIter != m_oMapMDArrays.end())
445
0
        return oIter->second;
446
0
    return nullptr;
447
0
}
448
449
/************************************************************************/
450
/*                             SetDirty()                               */
451
/************************************************************************/
452
453
void VRTGroup::SetDirty()
454
0
{
455
0
    auto poRootGroup(GetRootGroup());
456
0
    if (poRootGroup)
457
0
        poRootGroup->m_bDirty = true;
458
0
}
459
460
/************************************************************************/
461
/*                             CreateGroup()                            */
462
/************************************************************************/
463
464
std::shared_ptr<GDALGroup> VRTGroup::CreateGroup(const std::string &osName,
465
                                                 CSLConstList /*papszOptions*/)
466
0
{
467
0
    if (osName.empty())
468
0
    {
469
0
        CPLError(CE_Failure, CPLE_NotSupported,
470
0
                 "Empty group name not supported");
471
0
        return nullptr;
472
0
    }
473
0
    if (m_oMapGroups.find(osName) != m_oMapGroups.end())
474
0
    {
475
0
        CPLError(CE_Failure, CPLE_AppDefined,
476
0
                 "A group with same name (%s) already exists", osName.c_str());
477
0
        return nullptr;
478
0
    }
479
0
    SetDirty();
480
0
    auto newGroup(VRTGroup::Create(GetFullName(), osName.c_str()));
481
0
    newGroup->SetRootGroupRef(GetRootGroupRef());
482
0
    m_oMapGroups[osName] = newGroup;
483
0
    return newGroup;
484
0
}
485
486
/************************************************************************/
487
/*                             CreateDimension()                        */
488
/************************************************************************/
489
490
std::shared_ptr<GDALDimension>
491
VRTGroup::CreateDimension(const std::string &osName, const std::string &osType,
492
                          const std::string &osDirection, GUInt64 nSize,
493
                          CSLConstList)
494
0
{
495
0
    if (osName.empty())
496
0
    {
497
0
        CPLError(CE_Failure, CPLE_NotSupported,
498
0
                 "Empty dimension name not supported");
499
0
        return nullptr;
500
0
    }
501
0
    if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
502
0
    {
503
0
        CPLError(CE_Failure, CPLE_AppDefined,
504
0
                 "A dimension with same name (%s) already exists",
505
0
                 osName.c_str());
506
0
        return nullptr;
507
0
    }
508
0
    SetDirty();
509
0
    auto newDim(std::make_shared<VRTDimension>(GetRef(), GetFullName(), osName,
510
0
                                               osType, osDirection, nSize,
511
0
                                               std::string()));
512
0
    m_oMapDimensions[osName] = newDim;
513
0
    return newDim;
514
0
}
515
516
/************************************************************************/
517
/*                           CreateAttribute()                          */
518
/************************************************************************/
519
520
std::shared_ptr<GDALAttribute>
521
VRTGroup::CreateAttribute(const std::string &osName,
522
                          const std::vector<GUInt64> &anDimensions,
523
                          const GDALExtendedDataType &oDataType, CSLConstList)
524
0
{
525
0
    if (!VRTAttribute::CreationCommonChecks(osName, anDimensions,
526
0
                                            m_oMapAttributes))
527
0
    {
528
0
        return nullptr;
529
0
    }
530
0
    SetDirty();
531
0
    auto newAttr(std::make_shared<VRTAttribute>(
532
0
        (GetFullName() == "/" ? "/" : GetFullName() + "/") + "_GLOBAL_", osName,
533
0
        anDimensions.empty() ? 0 : anDimensions[0], oDataType));
534
0
    m_oMapAttributes[osName] = newAttr;
535
0
    return newAttr;
536
0
}
537
538
/************************************************************************/
539
/*                            CreateMDArray()                           */
540
/************************************************************************/
541
542
std::shared_ptr<GDALMDArray> VRTGroup::CreateMDArray(
543
    const std::string &osName,
544
    const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
545
    const GDALExtendedDataType &oType, CSLConstList)
546
0
{
547
0
    if (osName.empty())
548
0
    {
549
0
        CPLError(CE_Failure, CPLE_NotSupported,
550
0
                 "Empty array name not supported");
551
0
        return nullptr;
552
0
    }
553
0
    if (m_oMapMDArrays.find(osName) != m_oMapMDArrays.end())
554
0
    {
555
0
        CPLError(CE_Failure, CPLE_AppDefined,
556
0
                 "An array with same name (%s) already exists", osName.c_str());
557
0
        return nullptr;
558
0
    }
559
0
    for (auto &poDim : aoDimensions)
560
0
    {
561
0
        auto poFoundDim(
562
0
            dynamic_cast<const VRTDimension *>(poDim.get())
563
0
                ? GetDimensionFromFullName(poDim->GetFullName(), false)
564
0
                : nullptr);
565
0
        if (poFoundDim == nullptr || poFoundDim->GetSize() != poDim->GetSize())
566
0
        {
567
0
            CPLError(CE_Failure, CPLE_AppDefined,
568
0
                     "One input dimension is not a VRTDimension "
569
0
                     "or a VRTDimension of this dataset");
570
0
            return nullptr;
571
0
        }
572
0
    }
573
0
    auto newArray(std::make_shared<VRTMDArray>(GetRef(), GetFullName(), osName,
574
0
                                               aoDimensions, oType));
575
0
    newArray->SetSelf(newArray);
576
0
    m_oMapMDArrays[osName] = newArray;
577
0
    return newArray;
578
0
}
579
580
/************************************************************************/
581
/*                          ParseDataType()                             */
582
/************************************************************************/
583
584
static GDALExtendedDataType ParseDataType(const CPLXMLNode *psNode)
585
0
{
586
0
    const auto *psType = CPLGetXMLNode(psNode, "DataType");
587
0
    if (psType == nullptr || psType->psChild == nullptr ||
588
0
        psType->psChild->eType != CXT_Text)
589
0
    {
590
0
        CPLError(CE_Failure, CPLE_AppDefined,
591
0
                 "Unhandled content for DataType or Missing");
592
0
        return GDALExtendedDataType::Create(GDT_Unknown);
593
0
    }
594
0
    GDALExtendedDataType dt(GDALExtendedDataType::CreateString());
595
0
    if (EQUAL(psType->psChild->pszValue, "String"))
596
0
    {
597
        // done
598
0
    }
599
0
    else
600
0
    {
601
0
        const auto eDT = GDALGetDataTypeByName(psType->psChild->pszValue);
602
0
        dt = GDALExtendedDataType::Create(eDT);
603
0
    }
604
0
    return dt;
605
0
}
606
607
/************************************************************************/
608
/*                              Create()                                */
609
/************************************************************************/
610
611
std::shared_ptr<VRTDimension>
612
VRTDimension::Create(const std::shared_ptr<VRTGroup> &poThisGroup,
613
                     const std::string &osParentName, const CPLXMLNode *psNode)
614
0
{
615
0
    const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
616
0
    if (pszName == nullptr)
617
0
    {
618
0
        CPLError(CE_Failure, CPLE_AppDefined,
619
0
                 "Missing name attribute on Dimension");
620
0
        return nullptr;
621
0
    }
622
0
    const char *pszType = CPLGetXMLValue(psNode, "type", "");
623
0
    const char *pszDirection = CPLGetXMLValue(psNode, "direction", "");
624
0
    const char *pszSize = CPLGetXMLValue(psNode, "size", "");
625
0
    GUInt64 nSize = static_cast<GUInt64>(
626
0
        CPLScanUIntBig(pszSize, static_cast<int>(strlen(pszSize))));
627
0
    if (nSize == 0)
628
0
    {
629
0
        CPLError(CE_Failure, CPLE_AppDefined,
630
0
                 "Invalid value for size attribute on Dimension");
631
0
        return nullptr;
632
0
    }
633
0
    const char *pszIndexingVariable =
634
0
        CPLGetXMLValue(psNode, "indexingVariable", "");
635
0
    return std::make_shared<VRTDimension>(poThisGroup->GetRef(), osParentName,
636
0
                                          pszName, pszType, pszDirection, nSize,
637
0
                                          pszIndexingVariable);
638
0
}
639
640
/************************************************************************/
641
/*                             Serialize()                              */
642
/************************************************************************/
643
644
void VRTDimension::Serialize(CPLXMLNode *psParent) const
645
0
{
646
0
    CPLXMLNode *psDimension =
647
0
        CPLCreateXMLNode(psParent, CXT_Element, "Dimension");
648
0
    CPLAddXMLAttributeAndValue(psDimension, "name", GetName().c_str());
649
0
    if (!m_osType.empty())
650
0
    {
651
0
        CPLAddXMLAttributeAndValue(psDimension, "type", m_osType.c_str());
652
0
    }
653
0
    if (!m_osDirection.empty())
654
0
    {
655
0
        CPLAddXMLAttributeAndValue(psDimension, "direction",
656
0
                                   m_osDirection.c_str());
657
0
    }
658
0
    CPLAddXMLAttributeAndValue(
659
0
        psDimension, "size",
660
0
        CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(m_nSize)));
661
0
    if (!m_osIndexingVariableName.empty())
662
0
    {
663
0
        CPLAddXMLAttributeAndValue(psDimension, "indexingVariable",
664
0
                                   m_osIndexingVariableName.c_str());
665
0
    }
666
0
}
667
668
/************************************************************************/
669
/*                                GetGroup()                            */
670
/************************************************************************/
671
672
VRTGroup *VRTDimension::GetGroup() const
673
0
{
674
0
    auto ref = m_poGroupRef.lock();
675
0
    return ref ? ref->m_ptr : nullptr;
676
0
}
677
678
/************************************************************************/
679
/*                         GetIndexingVariable()                        */
680
/************************************************************************/
681
682
std::shared_ptr<GDALMDArray> VRTDimension::GetIndexingVariable() const
683
0
{
684
0
    if (m_osIndexingVariableName.empty())
685
0
        return nullptr;
686
0
    auto poGroup = GetGroup();
687
0
    if (poGroup == nullptr)
688
0
    {
689
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot access group");
690
0
        return nullptr;
691
0
    }
692
0
    std::shared_ptr<GDALMDArray> poVar;
693
0
    if (m_osIndexingVariableName[0] != '/')
694
0
    {
695
0
        poVar = poGroup->OpenMDArray(m_osIndexingVariableName);
696
0
    }
697
0
    else
698
0
    {
699
0
        poGroup = poGroup->GetRootGroup();
700
0
        if (poGroup == nullptr)
701
0
        {
702
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
703
0
            return nullptr;
704
0
        }
705
0
        poVar = poGroup->OpenMDArrayFromFullname(m_osIndexingVariableName);
706
0
    }
707
0
    if (!poVar)
708
0
    {
709
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find variable %s",
710
0
                 m_osIndexingVariableName.c_str());
711
0
    }
712
0
    return poVar;
713
0
}
714
715
/************************************************************************/
716
/*                         SetIndexingVariable()                        */
717
/************************************************************************/
718
719
bool VRTDimension::SetIndexingVariable(
720
    std::shared_ptr<GDALMDArray> poIndexingVariable)
721
0
{
722
0
    if (poIndexingVariable == nullptr)
723
0
    {
724
0
        m_osIndexingVariableName.clear();
725
0
        return true;
726
0
    }
727
728
0
    auto poGroup = GetGroup();
729
0
    if (poGroup == nullptr)
730
0
    {
731
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot access group");
732
0
        return false;
733
0
    }
734
0
    poGroup = poGroup->GetRootGroup();
735
0
    if (poGroup == nullptr)
736
0
    {
737
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot access root group");
738
0
        return false;
739
0
    }
740
0
    auto poVar(std::dynamic_pointer_cast<VRTMDArray>(
741
0
        poGroup->OpenMDArrayFromFullname(poIndexingVariable->GetFullName())));
742
0
    if (!poVar)
743
0
    {
744
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find variable %s",
745
0
                 poIndexingVariable->GetFullName().c_str());
746
0
        return false;
747
0
    }
748
0
    if (poVar->GetGroup() == GetGroup())
749
0
    {
750
0
        m_osIndexingVariableName = poIndexingVariable->GetName();
751
0
    }
752
0
    else
753
0
    {
754
0
        m_osIndexingVariableName = poIndexingVariable->GetFullName();
755
0
    }
756
0
    return true;
757
0
}
758
759
/************************************************************************/
760
/*                       CreationCommonChecks()                         */
761
/************************************************************************/
762
763
bool VRTAttribute::CreationCommonChecks(
764
    const std::string &osName, const std::vector<GUInt64> &anDimensions,
765
    const std::map<std::string, std::shared_ptr<VRTAttribute>> &oMapAttributes)
766
0
{
767
0
    if (osName.empty())
768
0
    {
769
0
        CPLError(CE_Failure, CPLE_NotSupported,
770
0
                 "Empty attribute name not supported");
771
0
        return false;
772
0
    }
773
0
    if (oMapAttributes.find(osName) != oMapAttributes.end())
774
0
    {
775
0
        CPLError(CE_Failure, CPLE_AppDefined,
776
0
                 "An attribute with same name (%s) already exists",
777
0
                 osName.c_str());
778
0
        return false;
779
0
    }
780
0
    if (anDimensions.size() >= 2)
781
0
    {
782
0
        CPLError(CE_Failure, CPLE_AppDefined,
783
0
                 "Only single dimensional attribute handled");
784
0
        return false;
785
0
    }
786
0
    if (anDimensions.size() == 1 &&
787
0
        anDimensions[0] > static_cast<GUInt64>(INT_MAX))
788
0
    {
789
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too large attribute");
790
0
        return false;
791
0
    }
792
0
    return true;
793
0
}
794
795
/************************************************************************/
796
/*                              Create()                                */
797
/************************************************************************/
798
799
std::shared_ptr<VRTAttribute>
800
VRTAttribute::Create(const std::string &osParentName, const CPLXMLNode *psNode)
801
0
{
802
0
    const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
803
0
    if (pszName == nullptr)
804
0
    {
805
0
        CPLError(CE_Failure, CPLE_AppDefined,
806
0
                 "Missing name attribute on Attribute");
807
0
        return nullptr;
808
0
    }
809
0
    GDALExtendedDataType dt(ParseDataType(psNode));
810
0
    if (dt.GetClass() == GEDTC_NUMERIC &&
811
0
        dt.GetNumericDataType() == GDT_Unknown)
812
0
    {
813
0
        return nullptr;
814
0
    }
815
0
    std::vector<std::string> aosValues;
816
0
    for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
817
0
    {
818
0
        if (psIter->eType == CXT_Element &&
819
0
            strcmp(psIter->pszValue, "Value") == 0)
820
0
        {
821
0
            aosValues.push_back(CPLGetXMLValue(psIter, nullptr, ""));
822
0
        }
823
0
    }
824
0
    return std::make_shared<VRTAttribute>(osParentName, pszName, dt,
825
0
                                          std::move(aosValues));
826
0
}
827
828
/************************************************************************/
829
/*                                   IRead()                            */
830
/************************************************************************/
831
832
bool VRTAttribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
833
                         const GInt64 *arrayStep,
834
                         const GPtrDiff_t *bufferStride,
835
                         const GDALExtendedDataType &bufferDataType,
836
                         void *pDstBuffer) const
837
0
{
838
0
    const auto stringDT(GDALExtendedDataType::CreateString());
839
0
    if (m_aosList.empty())
840
0
    {
841
0
        const char *pszStr = nullptr;
842
0
        GDALExtendedDataType::CopyValue(&pszStr, stringDT, pDstBuffer,
843
0
                                        bufferDataType);
844
0
    }
845
0
    else
846
0
    {
847
0
        GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
848
0
        for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
849
0
        {
850
0
            const int idx =
851
0
                m_dims.empty()
852
0
                    ? 0
853
0
                    : static_cast<int>(arrayStartIdx[0] + i * arrayStep[0]);
854
0
            const char *pszStr = m_aosList[idx].data();
855
0
            GDALExtendedDataType::CopyValue(&pszStr, stringDT, pabyDstBuffer,
856
0
                                            bufferDataType);
857
0
            if (!m_dims.empty())
858
0
            {
859
0
                pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
860
0
            }
861
0
        }
862
0
    }
863
0
    return true;
864
0
}
865
866
/************************************************************************/
867
/*                                  IWrite()                            */
868
/************************************************************************/
869
870
bool VRTAttribute::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
871
                          const GInt64 *arrayStep,
872
                          const GPtrDiff_t *bufferStride,
873
                          const GDALExtendedDataType &bufferDataType,
874
                          const void *pSrcBuffer)
875
0
{
876
0
    m_aosList.resize(m_dims.empty() ? 1
877
0
                                    : static_cast<int>(m_dims[0]->GetSize()));
878
0
    const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
879
0
    const auto stringDT(GDALExtendedDataType::CreateString());
880
0
    for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
881
0
    {
882
0
        const int idx =
883
0
            m_dims.empty()
884
0
                ? 0
885
0
                : static_cast<int>(arrayStartIdx[0] + i * arrayStep[0]);
886
0
        char *pszStr = nullptr;
887
0
        GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType, &pszStr,
888
0
                                        stringDT);
889
0
        m_aosList[idx] = pszStr ? pszStr : "";
890
0
        CPLFree(pszStr);
891
0
        if (!m_dims.empty())
892
0
        {
893
0
            pabySrcBuffer += bufferStride[0] * bufferDataType.GetSize();
894
0
        }
895
0
    }
896
0
    return true;
897
0
}
898
899
/************************************************************************/
900
/*                             Serialize()                              */
901
/************************************************************************/
902
903
void VRTAttribute::Serialize(CPLXMLNode *psParent) const
904
0
{
905
0
    CPLXMLNode *psAttr = CPLCreateXMLNode(psParent, CXT_Element, "Attribute");
906
0
    CPLAddXMLAttributeAndValue(psAttr, "name", GetName().c_str());
907
0
    CPLXMLNode *psDataType = CPLCreateXMLNode(psAttr, CXT_Element, "DataType");
908
0
    if (m_dt.GetClass() == GEDTC_STRING)
909
0
        CPLCreateXMLNode(psDataType, CXT_Text, "String");
910
0
    else
911
0
        CPLCreateXMLNode(psDataType, CXT_Text,
912
0
                         GDALGetDataTypeName(m_dt.GetNumericDataType()));
913
0
    CPLXMLNode *psLast = psDataType;
914
0
    for (const auto &str : m_aosList)
915
0
    {
916
0
        CPLXMLNode *psValue = CPLCreateXMLNode(nullptr, CXT_Element, "Value");
917
0
        CPLCreateXMLNode(psValue, CXT_Text, str.c_str());
918
0
        psLast->psNext = psValue;
919
0
        psLast = psValue;
920
0
    }
921
0
}
922
923
/************************************************************************/
924
/*                              Create()                                */
925
/************************************************************************/
926
927
std::shared_ptr<VRTMDArray>
928
VRTMDArray::Create(const std::shared_ptr<VRTGroup> &poThisGroup,
929
                   const std::string &osParentName, const CPLXMLNode *psNode)
930
0
{
931
0
    const char *pszName = CPLGetXMLValue(psNode, "name", nullptr);
932
0
    if (pszName == nullptr)
933
0
    {
934
0
        CPLError(CE_Failure, CPLE_AppDefined,
935
0
                 "Missing name attribute on Array");
936
0
        return nullptr;
937
0
    }
938
939
    /* -------------------------------------------------------------------- */
940
    /*      Check for an SRS node.                                          */
941
    /* -------------------------------------------------------------------- */
942
0
    const CPLXMLNode *psSRSNode = CPLGetXMLNode(psNode, "SRS");
943
0
    std::unique_ptr<OGRSpatialReference> poSRS;
944
0
    if (psSRSNode)
945
0
    {
946
0
        poSRS = std::make_unique<OGRSpatialReference>();
947
0
        poSRS->SetFromUserInput(
948
0
            CPLGetXMLValue(psSRSNode, nullptr, ""),
949
0
            OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
950
0
        const char *pszMapping =
951
0
            CPLGetXMLValue(psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
952
0
        if (pszMapping)
953
0
        {
954
0
            char **papszTokens =
955
0
                CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
956
0
            std::vector<int> anMapping;
957
0
            for (int i = 0; papszTokens && papszTokens[i]; i++)
958
0
            {
959
0
                anMapping.push_back(atoi(papszTokens[i]));
960
0
            }
961
0
            CSLDestroy(papszTokens);
962
0
            poSRS->SetDataAxisToSRSAxisMapping(anMapping);
963
0
        }
964
0
    }
965
966
0
    GDALExtendedDataType dt(ParseDataType(psNode));
967
0
    if (dt.GetClass() == GEDTC_NUMERIC &&
968
0
        dt.GetNumericDataType() == GDT_Unknown)
969
0
    {
970
0
        return nullptr;
971
0
    }
972
0
    std::vector<std::shared_ptr<GDALDimension>> dims;
973
0
    std::map<std::string, std::shared_ptr<VRTAttribute>> oMapAttributes;
974
0
    for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
975
0
    {
976
0
        if (psIter->eType == CXT_Element &&
977
0
            strcmp(psIter->pszValue, "Dimension") == 0)
978
0
        {
979
0
            auto poDim =
980
0
                VRTDimension::Create(poThisGroup, std::string(), psIter);
981
0
            if (!poDim)
982
0
                return nullptr;
983
0
            dims.emplace_back(poDim);
984
0
        }
985
0
        else if (psIter->eType == CXT_Element &&
986
0
                 strcmp(psIter->pszValue, "DimensionRef") == 0)
987
0
        {
988
0
            const char *pszRef = CPLGetXMLValue(psIter, "ref", nullptr);
989
0
            if (pszRef == nullptr || pszRef[0] == '\0')
990
0
            {
991
0
                CPLError(CE_Failure, CPLE_AppDefined,
992
0
                         "Missing ref attribute on DimensionRef");
993
0
                return nullptr;
994
0
            }
995
0
            auto poDim(poThisGroup->GetDimensionFromFullName(pszRef, true));
996
0
            if (!poDim)
997
0
                return nullptr;
998
0
            dims.emplace_back(poDim);
999
0
        }
1000
0
        else if (psIter->eType == CXT_Element &&
1001
0
                 strcmp(psIter->pszValue, "Attribute") == 0)
1002
0
        {
1003
0
            auto poAttr =
1004
0
                VRTAttribute::Create(osParentName + "/" + pszName, psIter);
1005
0
            if (!poAttr)
1006
0
                return nullptr;
1007
0
            oMapAttributes[poAttr->GetName()] = poAttr;
1008
0
        }
1009
0
    }
1010
1011
0
    auto array(std::make_shared<VRTMDArray>(poThisGroup->GetRef(), osParentName,
1012
0
                                            pszName, dt, std::move(dims),
1013
0
                                            std::move(oMapAttributes)));
1014
0
    array->SetSelf(array);
1015
0
    array->SetSpatialRef(poSRS.get());
1016
1017
0
    const char *pszNoDataValue = CPLGetXMLValue(psNode, "NoDataValue", nullptr);
1018
0
    if (pszNoDataValue)
1019
0
        array->SetNoDataValue(CPLAtof(pszNoDataValue));
1020
1021
0
    const char *pszUnit = CPLGetXMLValue(psNode, "Unit", nullptr);
1022
0
    if (pszUnit)
1023
0
        array->SetUnit(pszUnit);
1024
1025
0
    const char *pszOffset = CPLGetXMLValue(psNode, "Offset", nullptr);
1026
0
    if (pszOffset)
1027
0
        array->SetOffset(CPLAtof(pszOffset));
1028
1029
0
    const char *pszScale = CPLGetXMLValue(psNode, "Scale", nullptr);
1030
0
    if (pszScale)
1031
0
        array->SetScale(CPLAtof(pszScale));
1032
1033
0
    for (const auto *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
1034
0
    {
1035
0
        if (psIter->eType == CXT_Element &&
1036
0
            strcmp(psIter->pszValue, "RegularlySpacedValues") == 0)
1037
0
        {
1038
0
            if (dt.GetClass() != GEDTC_NUMERIC)
1039
0
            {
1040
0
                CPLError(CE_Failure, CPLE_AppDefined,
1041
0
                         "RegularlySpacedValues only supported for numeric "
1042
0
                         "data types");
1043
0
                return nullptr;
1044
0
            }
1045
0
            if (array->GetDimensionCount() != 1)
1046
0
            {
1047
0
                CPLError(CE_Failure, CPLE_AppDefined,
1048
0
                         "RegularlySpacedValues only supported with single "
1049
0
                         "dimension array");
1050
0
                return nullptr;
1051
0
            }
1052
0
            const char *pszStart = CPLGetXMLValue(psIter, "start", nullptr);
1053
0
            if (pszStart == nullptr)
1054
0
            {
1055
0
                CPLError(CE_Failure, CPLE_AppDefined,
1056
0
                         "start attribute missing");
1057
0
                return nullptr;
1058
0
            }
1059
0
            const char *pszIncrement =
1060
0
                CPLGetXMLValue(psIter, "increment", nullptr);
1061
0
            if (pszIncrement == nullptr)
1062
0
            {
1063
0
                CPLError(CE_Failure, CPLE_AppDefined,
1064
0
                         "increment attribute missing");
1065
0
                return nullptr;
1066
0
            }
1067
0
            std::unique_ptr<VRTMDArraySourceRegularlySpaced> poSource(
1068
0
                new VRTMDArraySourceRegularlySpaced(CPLAtof(pszStart),
1069
0
                                                    CPLAtof(pszIncrement)));
1070
0
            array->AddSource(std::move(poSource));
1071
0
        }
1072
0
        else if (psIter->eType == CXT_Element &&
1073
0
                 (strcmp(psIter->pszValue, "InlineValues") == 0 ||
1074
0
                  strcmp(psIter->pszValue, "InlineValuesWithValueElement") ==
1075
0
                      0 ||
1076
0
                  strcmp(psIter->pszValue, "ConstantValue") == 0))
1077
0
        {
1078
0
            auto poSource(
1079
0
                VRTMDArraySourceInlinedValues::Create(array.get(), psIter));
1080
0
            if (!poSource)
1081
0
                return nullptr;
1082
0
            array->AddSource(std::move(poSource));
1083
0
        }
1084
0
        else if (psIter->eType == CXT_Element &&
1085
0
                 strcmp(psIter->pszValue, "Source") == 0)
1086
0
        {
1087
0
            auto poSource(
1088
0
                VRTMDArraySourceFromArray::Create(array.get(), psIter));
1089
0
            if (!poSource)
1090
0
                return nullptr;
1091
0
            array->AddSource(std::move(poSource));
1092
0
        }
1093
0
    }
1094
1095
0
    return array;
1096
0
}
1097
1098
/************************************************************************/
1099
/*                              Create()                                */
1100
/************************************************************************/
1101
1102
std::shared_ptr<VRTMDArray> VRTMDArray::Create(const char *pszVRTPath,
1103
                                               const CPLXMLNode *psNode)
1104
0
{
1105
0
    auto poDummyGroup =
1106
0
        std::shared_ptr<VRTGroup>(new VRTGroup(pszVRTPath ? pszVRTPath : ""));
1107
0
    auto poArray = Create(poDummyGroup, std::string(), psNode);
1108
0
    if (poArray)
1109
0
        poArray->m_poDummyOwningGroup = std::move(poDummyGroup);
1110
0
    return poArray;
1111
0
}
1112
1113
/************************************************************************/
1114
/*                            GetAttributes()                           */
1115
/************************************************************************/
1116
1117
std::vector<std::shared_ptr<GDALAttribute>>
1118
VRTMDArray::GetAttributes(CSLConstList) const
1119
0
{
1120
0
    std::vector<std::shared_ptr<GDALAttribute>> oRes;
1121
0
    for (const auto &oIter : m_oMapAttributes)
1122
0
    {
1123
0
        oRes.push_back(oIter.second);
1124
0
    }
1125
0
    return oRes;
1126
0
}
1127
1128
/************************************************************************/
1129
/*                                  Read()                              */
1130
/************************************************************************/
1131
1132
bool VRTMDArraySourceRegularlySpaced::Read(
1133
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
1134
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
1135
    void *pDstBuffer) const
1136
0
{
1137
0
    GDALExtendedDataType dtFloat64(GDALExtendedDataType::Create(GDT_Float64));
1138
0
    GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
1139
0
    for (size_t i = 0; i < count[0]; i++)
1140
0
    {
1141
0
        const double dfVal =
1142
0
            m_dfStart + (arrayStartIdx[0] + i * arrayStep[0]) * m_dfIncrement;
1143
0
        GDALExtendedDataType::CopyValue(&dfVal, dtFloat64, pabyDstBuffer,
1144
0
                                        bufferDataType);
1145
0
        pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
1146
0
    }
1147
0
    return true;
1148
0
}
1149
1150
/************************************************************************/
1151
/*                             Serialize()                              */
1152
/************************************************************************/
1153
1154
void VRTMDArraySourceRegularlySpaced::Serialize(CPLXMLNode *psParent,
1155
                                                const char *) const
1156
0
{
1157
0
    CPLXMLNode *psSource =
1158
0
        CPLCreateXMLNode(psParent, CXT_Element, "RegularlySpacedValues");
1159
0
    CPLAddXMLAttributeAndValue(psSource, "start",
1160
0
                               CPLSPrintf("%.17g", m_dfStart));
1161
0
    CPLAddXMLAttributeAndValue(psSource, "increment",
1162
0
                               CPLSPrintf("%.17g", m_dfIncrement));
1163
0
}
1164
1165
/************************************************************************/
1166
/*                              Create()                                */
1167
/************************************************************************/
1168
1169
std::unique_ptr<VRTMDArraySourceInlinedValues>
1170
VRTMDArraySourceInlinedValues::Create(const VRTMDArray *array,
1171
                                      const CPLXMLNode *psNode)
1172
0
{
1173
0
    const bool bIsConstantValue =
1174
0
        strcmp(psNode->pszValue, "ConstantValue") == 0;
1175
0
    const auto &dt(array->GetDataType());
1176
0
    const size_t nDTSize = dt.GetSize();
1177
0
    if (nDTSize == 0)
1178
0
        return nullptr;
1179
0
    if (strcmp(psNode->pszValue, "InlineValuesWithValueElement") == 0)
1180
0
    {
1181
0
        if (dt.GetClass() != GEDTC_NUMERIC && dt.GetClass() != GEDTC_STRING)
1182
0
        {
1183
0
            CPLError(CE_Failure, CPLE_AppDefined,
1184
0
                     "Only numeric or string data type handled for "
1185
0
                     "InlineValuesWithValueElement");
1186
0
            return nullptr;
1187
0
        }
1188
0
    }
1189
0
    else if (dt.GetClass() != GEDTC_NUMERIC)
1190
0
    {
1191
0
        CPLError(CE_Failure, CPLE_AppDefined,
1192
0
                 "Only numeric data type handled for InlineValues");
1193
0
        return nullptr;
1194
0
    }
1195
1196
0
    const int nDimCount = static_cast<int>(array->GetDimensionCount());
1197
0
    std::vector<GUInt64> anOffset(nDimCount);
1198
0
    std::vector<size_t> anCount(nDimCount);
1199
0
    size_t nArrayByteSize = nDTSize;
1200
0
    if (nDimCount > 0)
1201
0
    {
1202
0
        const auto &dims(array->GetDimensions());
1203
1204
0
        const char *pszOffset = CPLGetXMLValue(psNode, "offset", nullptr);
1205
0
        if (pszOffset != nullptr)
1206
0
        {
1207
0
            CPLStringList aosTokensOffset(
1208
0
                CSLTokenizeString2(pszOffset, ", ", 0));
1209
0
            if (aosTokensOffset.size() != nDimCount)
1210
0
            {
1211
0
                CPLError(CE_Failure, CPLE_AppDefined,
1212
0
                         "Wrong number of values in offset");
1213
0
                return nullptr;
1214
0
            }
1215
0
            for (int i = 0; i < nDimCount; ++i)
1216
0
            {
1217
0
                anOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1218
0
                    aosTokensOffset[i],
1219
0
                    static_cast<int>(strlen(aosTokensOffset[i]))));
1220
0
                if (aosTokensOffset[i][0] == '-' ||
1221
0
                    anOffset[i] >= dims[i]->GetSize())
1222
0
                {
1223
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1224
0
                             "Wrong value in offset");
1225
0
                    return nullptr;
1226
0
                }
1227
0
            }
1228
0
        }
1229
1230
0
        const char *pszCount = CPLGetXMLValue(psNode, "count", nullptr);
1231
0
        if (pszCount != nullptr)
1232
0
        {
1233
0
            CPLStringList aosTokensCount(CSLTokenizeString2(pszCount, ", ", 0));
1234
0
            if (aosTokensCount.size() != nDimCount)
1235
0
            {
1236
0
                CPLError(CE_Failure, CPLE_AppDefined,
1237
0
                         "Wrong number of values in count");
1238
0
                return nullptr;
1239
0
            }
1240
0
            for (int i = 0; i < nDimCount; ++i)
1241
0
            {
1242
0
                anCount[i] = static_cast<size_t>(CPLScanUIntBig(
1243
0
                    aosTokensCount[i],
1244
0
                    static_cast<int>(strlen(aosTokensCount[i]))));
1245
0
                if (aosTokensCount[i][0] == '-' || anCount[i] == 0 ||
1246
0
                    anOffset[i] + anCount[i] > dims[i]->GetSize())
1247
0
                {
1248
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1249
0
                             "Wrong value in count");
1250
0
                    return nullptr;
1251
0
                }
1252
0
            }
1253
0
        }
1254
0
        else
1255
0
        {
1256
0
            for (int i = 0; i < nDimCount; ++i)
1257
0
            {
1258
0
                anCount[i] =
1259
0
                    static_cast<size_t>(dims[i]->GetSize() - anOffset[i]);
1260
0
            }
1261
0
        }
1262
0
        if (!bIsConstantValue)
1263
0
        {
1264
0
            for (int i = 0; i < nDimCount; ++i)
1265
0
            {
1266
0
                if (anCount[i] >
1267
0
                    std::numeric_limits<size_t>::max() / nArrayByteSize)
1268
0
                {
1269
0
                    CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
1270
0
                    return nullptr;
1271
0
                }
1272
0
                nArrayByteSize *= anCount[i];
1273
0
            }
1274
0
        }
1275
0
    }
1276
1277
0
    const size_t nExpectedVals = nArrayByteSize / nDTSize;
1278
0
    CPLStringList aosValues;
1279
1280
0
    if (strcmp(psNode->pszValue, "InlineValuesWithValueElement") == 0)
1281
0
    {
1282
0
        for (auto psIter = psNode->psChild; psIter; psIter = psIter->psNext)
1283
0
        {
1284
0
            if (psIter->eType == CXT_Element &&
1285
0
                strcmp(psIter->pszValue, "Value") == 0)
1286
0
            {
1287
0
                aosValues.AddString(CPLGetXMLValue(psIter, nullptr, ""));
1288
0
            }
1289
0
        }
1290
0
    }
1291
0
    else
1292
0
    {
1293
0
        const char *pszValue = CPLGetXMLValue(psNode, nullptr, nullptr);
1294
0
        if (pszValue == nullptr ||
1295
0
            (!bIsConstantValue && nExpectedVals > strlen(pszValue)))
1296
0
        {
1297
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid content");
1298
0
            return nullptr;
1299
0
        }
1300
0
        aosValues.Assign(CSLTokenizeString2(pszValue, ", \r\n", 0), true);
1301
0
    }
1302
1303
0
    if (static_cast<size_t>(aosValues.size()) != nExpectedVals)
1304
0
    {
1305
0
        CPLError(CE_Failure, CPLE_AppDefined,
1306
0
                 "Invalid number of values. Got %u, expected %u",
1307
0
                 static_cast<unsigned>(aosValues.size()),
1308
0
                 static_cast<unsigned>(nExpectedVals));
1309
0
        return nullptr;
1310
0
    }
1311
0
    std::vector<GByte> abyValues;
1312
0
    try
1313
0
    {
1314
0
        abyValues.resize(nArrayByteSize);
1315
0
    }
1316
0
    catch (const std::exception &ex)
1317
0
    {
1318
0
        CPLError(CE_Failure, CPLE_OutOfMemory, "%s", ex.what());
1319
0
        return nullptr;
1320
0
    }
1321
1322
0
    const auto dtString(GDALExtendedDataType::CreateString());
1323
0
    GByte *pabyPtr = &abyValues[0];
1324
0
    for (int i = 0; i < aosValues.size(); ++i)
1325
0
    {
1326
0
        const char *pszVal = &aosValues[i][0];
1327
0
        GDALExtendedDataType::CopyValue(&pszVal, dtString, pabyPtr, dt);
1328
0
        pabyPtr += nDTSize;
1329
0
    }
1330
1331
0
    return std::make_unique<VRTMDArraySourceInlinedValues>(
1332
0
        array, bIsConstantValue, std::move(anOffset), std::move(anCount),
1333
0
        std::move(abyValues));
1334
0
}
1335
1336
/************************************************************************/
1337
/*                  ~VRTMDArraySourceInlinedValues()                    */
1338
/************************************************************************/
1339
1340
VRTMDArraySourceInlinedValues::~VRTMDArraySourceInlinedValues()
1341
0
{
1342
0
    if (m_dt.NeedsFreeDynamicMemory())
1343
0
    {
1344
0
        const size_t nDTSize = m_dt.GetSize();
1345
0
        const size_t nValueCount = m_abyValues.size() / nDTSize;
1346
0
        GByte *pabyPtr = &m_abyValues[0];
1347
0
        for (size_t i = 0; i < nValueCount; ++i)
1348
0
        {
1349
0
            m_dt.FreeDynamicMemory(pabyPtr);
1350
0
            pabyPtr += nDTSize;
1351
0
        }
1352
0
    }
1353
0
}
1354
1355
/************************************************************************/
1356
/*                                   Read()                             */
1357
/************************************************************************/
1358
static inline void IncrPointer(const GByte *&ptr, GInt64 nInc, size_t nIncSize)
1359
0
{
1360
0
    if (nInc < 0)
1361
0
        ptr -= (-nInc) * nIncSize;
1362
0
    else
1363
0
        ptr += nInc * nIncSize;
1364
0
}
1365
1366
static inline void IncrPointer(GByte *&ptr, GPtrDiff_t nInc, size_t nIncSize)
1367
0
{
1368
0
    if (nInc < 0)
1369
0
        ptr -= (-nInc) * nIncSize;
1370
0
    else
1371
0
        ptr += nInc * nIncSize;
1372
0
}
1373
1374
bool VRTMDArraySourceInlinedValues::Read(
1375
    const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
1376
    const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
1377
    void *pDstBuffer) const
1378
0
{
1379
0
    const auto nDims(m_poDstArray->GetDimensionCount());
1380
0
    std::vector<GUInt64> anReqStart(nDims);
1381
0
    std::vector<size_t> anReqCount(nDims);
1382
    // Compute the intersection between the inline value slab and the
1383
    // request slab.
1384
0
    for (size_t i = 0; i < nDims; i++)
1385
0
    {
1386
0
        auto start_i = arrayStartIdx[i];
1387
0
        auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1388
0
        if (arrayStep[i] < 0)
1389
0
        {
1390
            // For negative step request, temporarily simulate a positive step
1391
            // and fix up the start at the end of the loop.
1392
            // Use double negation so that operations occur only on
1393
            // positive quantities to avoid an artificial negative signed
1394
            // integer to unsigned conversion.
1395
0
            start_i = start_i - ((count[i] - 1) * (-step_i));
1396
0
            step_i = -step_i;
1397
0
        }
1398
1399
0
        const auto nRightDstOffsetFromConfig = m_anOffset[i] + m_anCount[i];
1400
0
        if (start_i >= nRightDstOffsetFromConfig ||
1401
0
            start_i + (count[i] - 1) * step_i < m_anOffset[i])
1402
0
        {
1403
0
            return true;
1404
0
        }
1405
0
        if (start_i < m_anOffset[i])
1406
0
        {
1407
0
            anReqStart[i] =
1408
0
                m_anOffset[i] +
1409
0
                (step_i - ((m_anOffset[i] - start_i) % step_i)) % step_i;
1410
0
        }
1411
0
        else
1412
0
        {
1413
0
            anReqStart[i] = start_i;
1414
0
        }
1415
0
        anReqCount[i] = 1 + static_cast<size_t>(
1416
0
                                (std::min(nRightDstOffsetFromConfig - 1,
1417
0
                                          start_i + (count[i] - 1) * step_i) -
1418
0
                                 anReqStart[i]) /
1419
0
                                step_i);
1420
0
        if (arrayStep[i] < 0)
1421
0
        {
1422
0
            anReqStart[i] = anReqStart[i] + (anReqCount[i] - 1) * step_i;
1423
0
        }
1424
0
    }
1425
1426
0
    size_t nSrcOffset = 0;
1427
0
    GPtrDiff_t nDstOffset = 0;
1428
0
    const auto nBufferDataTypeSize(bufferDataType.GetSize());
1429
0
    for (size_t i = 0; i < nDims; i++)
1430
0
    {
1431
0
        const size_t nRelStartSrc =
1432
0
            static_cast<size_t>(anReqStart[i] - m_anOffset[i]);
1433
0
        nSrcOffset += nRelStartSrc * m_anInlinedArrayStrideInBytes[i];
1434
0
        const size_t nRelStartDst =
1435
0
            static_cast<size_t>(anReqStart[i] - arrayStartIdx[i]);
1436
0
        nDstOffset += nRelStartDst * bufferStride[i] * nBufferDataTypeSize;
1437
0
    }
1438
0
    std::vector<const GByte *> abyStackSrcPtr(nDims + 1);
1439
0
    abyStackSrcPtr[0] = m_abyValues.data() + nSrcOffset;
1440
0
    std::vector<GByte *> abyStackDstPtr(nDims + 1);
1441
0
    abyStackDstPtr[0] = static_cast<GByte *>(pDstBuffer) + nDstOffset;
1442
1443
0
    const auto &dt(m_poDstArray->GetDataType());
1444
0
    std::vector<size_t> anStackCount(nDims);
1445
0
    size_t iDim = 0;
1446
1447
0
lbl_next_depth:
1448
0
    if (iDim == nDims)
1449
0
    {
1450
0
        GDALExtendedDataType::CopyValue(abyStackSrcPtr[nDims], dt,
1451
0
                                        abyStackDstPtr[nDims], bufferDataType);
1452
0
    }
1453
0
    else
1454
0
    {
1455
0
        anStackCount[iDim] = anReqCount[iDim];
1456
0
        while (true)
1457
0
        {
1458
0
            ++iDim;
1459
0
            abyStackSrcPtr[iDim] = abyStackSrcPtr[iDim - 1];
1460
0
            abyStackDstPtr[iDim] = abyStackDstPtr[iDim - 1];
1461
0
            goto lbl_next_depth;
1462
0
        lbl_return_to_caller:
1463
0
            --iDim;
1464
0
            --anStackCount[iDim];
1465
0
            if (anStackCount[iDim] == 0)
1466
0
                break;
1467
0
            IncrPointer(abyStackSrcPtr[iDim], arrayStep[iDim],
1468
0
                        m_anInlinedArrayStrideInBytes[iDim]);
1469
0
            IncrPointer(abyStackDstPtr[iDim], bufferStride[iDim],
1470
0
                        nBufferDataTypeSize);
1471
0
        }
1472
0
    }
1473
0
    if (iDim > 0)
1474
0
        goto lbl_return_to_caller;
1475
1476
0
    return true;
1477
0
}
1478
1479
/************************************************************************/
1480
/*                             Serialize()                              */
1481
/************************************************************************/
1482
1483
void VRTMDArraySourceInlinedValues::Serialize(CPLXMLNode *psParent,
1484
                                              const char *) const
1485
0
{
1486
0
    const auto &dt(m_poDstArray->GetDataType());
1487
0
    CPLXMLNode *psSource = CPLCreateXMLNode(psParent, CXT_Element,
1488
0
                                            m_bIsConstantValue ? "ConstantValue"
1489
0
                                            : dt.GetClass() == GEDTC_STRING
1490
0
                                                ? "InlineValuesWithValueElement"
1491
0
                                                : "InlineValues");
1492
1493
0
    std::string osOffset;
1494
0
    for (auto nOffset : m_anOffset)
1495
0
    {
1496
0
        if (!osOffset.empty())
1497
0
            osOffset += ',';
1498
0
        osOffset += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nOffset));
1499
0
    }
1500
0
    if (!osOffset.empty())
1501
0
    {
1502
0
        CPLAddXMLAttributeAndValue(psSource, "offset", osOffset.c_str());
1503
0
    }
1504
1505
0
    std::string osCount;
1506
0
    size_t nValues = 1;
1507
0
    for (auto nCount : m_anCount)
1508
0
    {
1509
0
        if (!osCount.empty())
1510
0
            osCount += ',';
1511
0
        nValues *= nCount;
1512
0
        osCount += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nCount));
1513
0
    }
1514
0
    if (!osCount.empty())
1515
0
    {
1516
0
        CPLAddXMLAttributeAndValue(psSource, "count", osCount.c_str());
1517
0
    }
1518
1519
0
    const auto dtString(GDALExtendedDataType::CreateString());
1520
0
    const size_t nDTSize(dt.GetSize());
1521
0
    if (dt.GetClass() == GEDTC_STRING)
1522
0
    {
1523
0
        CPLXMLNode *psLast = psSource->psChild;
1524
0
        if (psLast)
1525
0
        {
1526
0
            while (psLast->psNext)
1527
0
                psLast = psLast->psNext;
1528
0
        }
1529
0
        for (size_t i = 0; i < (m_bIsConstantValue ? 1 : nValues); ++i)
1530
0
        {
1531
0
            char *pszStr = nullptr;
1532
0
            GDALExtendedDataType::CopyValue(&m_abyValues[i * nDTSize], dt,
1533
0
                                            &pszStr, dtString);
1534
0
            if (pszStr)
1535
0
            {
1536
0
                auto psNode =
1537
0
                    CPLCreateXMLElementAndValue(nullptr, "Value", pszStr);
1538
0
                if (psLast)
1539
0
                    psLast->psNext = psNode;
1540
0
                else
1541
0
                    psSource->psChild = psNode;
1542
0
                psLast = psNode;
1543
0
                CPLFree(pszStr);
1544
0
            }
1545
0
        }
1546
0
    }
1547
0
    else
1548
0
    {
1549
0
        std::string osValues;
1550
0
        for (size_t i = 0; i < (m_bIsConstantValue ? 1 : nValues); ++i)
1551
0
        {
1552
0
            if (i > 0)
1553
0
                osValues += ' ';
1554
0
            char *pszStr = nullptr;
1555
0
            GDALExtendedDataType::CopyValue(&m_abyValues[i * nDTSize], dt,
1556
0
                                            &pszStr, dtString);
1557
0
            if (pszStr)
1558
0
            {
1559
0
                osValues += pszStr;
1560
0
                CPLFree(pszStr);
1561
0
            }
1562
0
        }
1563
0
        CPLCreateXMLNode(psSource, CXT_Text, osValues.c_str());
1564
0
    }
1565
0
}
1566
1567
/************************************************************************/
1568
/*                              Create()                                */
1569
/************************************************************************/
1570
1571
std::unique_ptr<VRTMDArraySourceFromArray>
1572
VRTMDArraySourceFromArray::Create(const VRTMDArray *poDstArray,
1573
                                  const CPLXMLNode *psNode)
1574
0
{
1575
0
    const char *pszFilename = CPLGetXMLValue(psNode, "SourceFilename", nullptr);
1576
0
    if (pszFilename == nullptr)
1577
0
    {
1578
0
        CPLError(CE_Failure, CPLE_AppDefined, "SourceFilename element missing");
1579
0
        return nullptr;
1580
0
    }
1581
0
    const char *pszRelativeToVRT =
1582
0
        CPLGetXMLValue(psNode, "SourceFilename.relativetoVRT", nullptr);
1583
0
    const bool bRelativeToVRTSet = pszRelativeToVRT != nullptr;
1584
0
    const bool bRelativeToVRT =
1585
0
        pszRelativeToVRT ? CPL_TO_BOOL(atoi(pszRelativeToVRT)) : false;
1586
0
    const char *pszArray = CPLGetXMLValue(psNode, "SourceArray", "");
1587
0
    const char *pszSourceBand = CPLGetXMLValue(psNode, "SourceBand", "");
1588
0
    if (pszArray[0] == '\0' && pszSourceBand[0] == '\0')
1589
0
    {
1590
0
        CPLError(CE_Failure, CPLE_AppDefined,
1591
0
                 "SourceArray or SourceBand element missing or empty");
1592
0
        return nullptr;
1593
0
    }
1594
0
    if (pszArray[0] != '\0' && pszSourceBand[0] != '\0')
1595
0
    {
1596
0
        CPLError(CE_Failure, CPLE_AppDefined,
1597
0
                 "SourceArray and SourceBand are exclusive");
1598
0
        return nullptr;
1599
0
    }
1600
1601
0
    const char *pszTranspose = CPLGetXMLValue(psNode, "SourceTranspose", "");
1602
0
    std::vector<int> anTransposedAxis;
1603
0
    CPLStringList aosTransposedAxis(CSLTokenizeString2(pszTranspose, ",", 0));
1604
0
    for (int i = 0; i < aosTransposedAxis.size(); i++)
1605
0
        anTransposedAxis.push_back(atoi(aosTransposedAxis[i]));
1606
1607
0
    const char *pszView = CPLGetXMLValue(psNode, "SourceView", "");
1608
1609
0
    const int nDimCount = static_cast<int>(poDstArray->GetDimensionCount());
1610
0
    std::vector<GUInt64> anSrcOffset(nDimCount);
1611
0
    std::vector<GUInt64> anCount(nDimCount);
1612
0
    std::vector<GUInt64> anStep(nDimCount, 1);
1613
0
    std::vector<GUInt64> anDstOffset(nDimCount);
1614
1615
0
    if (nDimCount > 0)
1616
0
    {
1617
0
        const CPLXMLNode *psSourceSlab = CPLGetXMLNode(psNode, "SourceSlab");
1618
0
        if (psSourceSlab)
1619
0
        {
1620
0
            const char *pszOffset =
1621
0
                CPLGetXMLValue(psSourceSlab, "offset", nullptr);
1622
0
            if (pszOffset != nullptr)
1623
0
            {
1624
0
                CPLStringList aosTokensOffset(
1625
0
                    CSLTokenizeString2(pszOffset, ", ", 0));
1626
0
                if (aosTokensOffset.size() != nDimCount)
1627
0
                {
1628
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1629
0
                             "Wrong number of values in offset");
1630
0
                    return nullptr;
1631
0
                }
1632
0
                for (int i = 0; i < nDimCount; ++i)
1633
0
                {
1634
0
                    anSrcOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1635
0
                        aosTokensOffset[i],
1636
0
                        static_cast<int>(strlen(aosTokensOffset[i]))));
1637
0
                    if (aosTokensOffset[i][0] == '-')
1638
0
                    {
1639
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1640
0
                                 "Wrong value in offset");
1641
0
                        return nullptr;
1642
0
                    }
1643
0
                }
1644
0
            }
1645
1646
0
            const char *pszStep = CPLGetXMLValue(psSourceSlab, "step", nullptr);
1647
0
            if (pszStep != nullptr)
1648
0
            {
1649
0
                CPLStringList aosTokensStep(
1650
0
                    CSLTokenizeString2(pszStep, ", ", 0));
1651
0
                if (aosTokensStep.size() != nDimCount)
1652
0
                {
1653
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1654
0
                             "Wrong number of values in step");
1655
0
                    return nullptr;
1656
0
                }
1657
0
                for (int i = 0; i < nDimCount; ++i)
1658
0
                {
1659
0
                    anStep[i] = static_cast<GUInt64>(CPLScanUIntBig(
1660
0
                        aosTokensStep[i],
1661
0
                        static_cast<int>(strlen(aosTokensStep[i]))));
1662
0
                    if (aosTokensStep[i][0] == '-')
1663
0
                    {
1664
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1665
0
                                 "Wrong value in step");
1666
0
                        return nullptr;
1667
0
                    }
1668
0
                }
1669
0
            }
1670
1671
0
            const char *pszCount =
1672
0
                CPLGetXMLValue(psSourceSlab, "count", nullptr);
1673
0
            if (pszCount != nullptr)
1674
0
            {
1675
0
                CPLStringList aosTokensCount(
1676
0
                    CSLTokenizeString2(pszCount, ", ", 0));
1677
0
                if (aosTokensCount.size() != nDimCount)
1678
0
                {
1679
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1680
0
                             "Wrong number of values in count");
1681
0
                    return nullptr;
1682
0
                }
1683
0
                for (int i = 0; i < nDimCount; ++i)
1684
0
                {
1685
0
                    anCount[i] = static_cast<GUInt64>(CPLScanUIntBig(
1686
0
                        aosTokensCount[i],
1687
0
                        static_cast<int>(strlen(aosTokensCount[i]))));
1688
0
                    if (aosTokensCount[i][0] == '-')
1689
0
                    {
1690
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1691
0
                                 "Wrong value in count");
1692
0
                        return nullptr;
1693
0
                    }
1694
0
                }
1695
0
            }
1696
0
        }
1697
1698
0
        const CPLXMLNode *psDestSlab = CPLGetXMLNode(psNode, "DestSlab");
1699
0
        if (psDestSlab)
1700
0
        {
1701
0
            const auto &dims(poDstArray->GetDimensions());
1702
0
            const char *pszOffset =
1703
0
                CPLGetXMLValue(psDestSlab, "offset", nullptr);
1704
0
            if (pszOffset != nullptr)
1705
0
            {
1706
0
                CPLStringList aosTokensOffset(
1707
0
                    CSLTokenizeString2(pszOffset, ", ", 0));
1708
0
                if (aosTokensOffset.size() != nDimCount)
1709
0
                {
1710
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1711
0
                             "Wrong number of values in offset");
1712
0
                    return nullptr;
1713
0
                }
1714
0
                for (int i = 0; i < nDimCount; ++i)
1715
0
                {
1716
0
                    anDstOffset[i] = static_cast<GUInt64>(CPLScanUIntBig(
1717
0
                        aosTokensOffset[i],
1718
0
                        static_cast<int>(strlen(aosTokensOffset[i]))));
1719
0
                    if (aosTokensOffset[i][0] == '-' ||
1720
0
                        anDstOffset[i] >= dims[i]->GetSize())
1721
0
                    {
1722
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1723
0
                                 "Wrong value in offset");
1724
0
                        return nullptr;
1725
0
                    }
1726
0
                }
1727
0
            }
1728
0
        }
1729
0
    }
1730
1731
0
    return std::make_unique<VRTMDArraySourceFromArray>(
1732
0
        poDstArray, bRelativeToVRTSet, bRelativeToVRT, pszFilename, pszArray,
1733
0
        pszSourceBand, std::move(anTransposedAxis), pszView,
1734
0
        std::move(anSrcOffset), std::move(anCount), std::move(anStep),
1735
0
        std::move(anDstOffset));
1736
0
}
1737
1738
/************************************************************************/
1739
/*                             Serialize()                              */
1740
/************************************************************************/
1741
1742
void VRTMDArraySourceFromArray::Serialize(CPLXMLNode *psParent,
1743
                                          const char *pszVRTPath) const
1744
0
{
1745
0
    CPLXMLNode *psSource = CPLCreateXMLNode(psParent, CXT_Element, "Source");
1746
1747
0
    if (m_bRelativeToVRTSet)
1748
0
    {
1749
0
        auto psSourceFilename = CPLCreateXMLElementAndValue(
1750
0
            psSource, "SourceFilename", m_osFilename.c_str());
1751
0
        if (m_bRelativeToVRT)
1752
0
        {
1753
0
            CPLAddXMLAttributeAndValue(psSourceFilename, "relativetoVRT", "1");
1754
0
        }
1755
0
    }
1756
0
    else
1757
0
    {
1758
0
        int bRelativeToVRT = FALSE;
1759
0
        const char *pszSourceFilename = CPLExtractRelativePath(
1760
0
            pszVRTPath, m_osFilename.c_str(), &bRelativeToVRT);
1761
0
        auto psSourceFilename = CPLCreateXMLElementAndValue(
1762
0
            psSource, "SourceFilename", pszSourceFilename);
1763
0
        if (bRelativeToVRT)
1764
0
        {
1765
0
            CPLAddXMLAttributeAndValue(psSourceFilename, "relativetoVRT", "1");
1766
0
        }
1767
0
    }
1768
1769
0
    if (!m_osArray.empty())
1770
0
        CPLCreateXMLElementAndValue(psSource, "SourceArray", m_osArray.c_str());
1771
0
    else
1772
0
        CPLCreateXMLElementAndValue(psSource, "SourceBand", m_osBand.c_str());
1773
1774
0
    if (!m_anTransposedAxis.empty())
1775
0
    {
1776
0
        std::string str;
1777
0
        for (size_t i = 0; i < m_anTransposedAxis.size(); i++)
1778
0
        {
1779
0
            if (i > 0)
1780
0
                str += ',';
1781
0
            str += CPLSPrintf("%d", m_anTransposedAxis[i]);
1782
0
        }
1783
0
        CPLCreateXMLElementAndValue(psSource, "SourceTranspose", str.c_str());
1784
0
    }
1785
1786
0
    if (!m_osViewExpr.empty())
1787
0
    {
1788
0
        CPLCreateXMLElementAndValue(psSource, "SourceView",
1789
0
                                    m_osViewExpr.c_str());
1790
0
    }
1791
1792
0
    if (m_poDstArray->GetDimensionCount() > 0)
1793
0
    {
1794
0
        CPLXMLNode *psSourceSlab =
1795
0
            CPLCreateXMLNode(psSource, CXT_Element, "SourceSlab");
1796
0
        {
1797
0
            std::string str;
1798
0
            for (size_t i = 0; i < m_anSrcOffset.size(); i++)
1799
0
            {
1800
0
                if (i > 0)
1801
0
                    str += ',';
1802
0
                str += CPLSPrintf(CPL_FRMT_GUIB,
1803
0
                                  static_cast<GUIntBig>(m_anSrcOffset[i]));
1804
0
            }
1805
0
            CPLAddXMLAttributeAndValue(psSourceSlab, "offset", str.c_str());
1806
0
        }
1807
0
        {
1808
0
            std::string str;
1809
0
            for (size_t i = 0; i < m_anCount.size(); i++)
1810
0
            {
1811
0
                if (i > 0)
1812
0
                    str += ',';
1813
0
                str += CPLSPrintf(CPL_FRMT_GUIB,
1814
0
                                  static_cast<GUIntBig>(m_anCount[i]));
1815
0
            }
1816
0
            CPLAddXMLAttributeAndValue(psSourceSlab, "count", str.c_str());
1817
0
        }
1818
0
        {
1819
0
            std::string str;
1820
0
            for (size_t i = 0; i < m_anStep.size(); i++)
1821
0
            {
1822
0
                if (i > 0)
1823
0
                    str += ',';
1824
0
                str += CPLSPrintf(CPL_FRMT_GUIB,
1825
0
                                  static_cast<GUIntBig>(m_anStep[i]));
1826
0
            }
1827
0
            CPLAddXMLAttributeAndValue(psSourceSlab, "step", str.c_str());
1828
0
        }
1829
1830
0
        CPLXMLNode *psDestSlab =
1831
0
            CPLCreateXMLNode(psSource, CXT_Element, "DestSlab");
1832
0
        {
1833
0
            std::string str;
1834
0
            for (size_t i = 0; i < m_anDstOffset.size(); i++)
1835
0
            {
1836
0
                if (i > 0)
1837
0
                    str += ',';
1838
0
                str += CPLSPrintf(CPL_FRMT_GUIB,
1839
0
                                  static_cast<GUIntBig>(m_anDstOffset[i]));
1840
0
            }
1841
0
            CPLAddXMLAttributeAndValue(psDestSlab, "offset", str.c_str());
1842
0
        }
1843
0
    }
1844
0
}
1845
1846
/************************************************************************/
1847
/*                      ~VRTMDArraySourceFromArray()                    */
1848
/************************************************************************/
1849
1850
VRTMDArraySourceFromArray::~VRTMDArraySourceFromArray()
1851
0
{
1852
0
    std::lock_guard<std::mutex> oGuard(g_cacheLock);
1853
1854
    // Remove from the cache datasets that are only used by this array
1855
    // or drop our reference to those datasets
1856
0
    std::unordered_set<std::string> oSetKeysToRemove;
1857
0
    std::unordered_set<std::string> oSetKeysToDropReference;
1858
0
    auto lambda = [&oSetKeysToRemove, &oSetKeysToDropReference,
1859
0
                   this](const decltype(g_cacheSources)::node_type &key_value)
1860
0
    {
1861
0
        auto &listOfArrays(key_value.value.second);
1862
0
        auto oIter = listOfArrays.find(this);
1863
0
        if (oIter != listOfArrays.end())
1864
0
        {
1865
0
            if (listOfArrays.size() == 1)
1866
0
                oSetKeysToRemove.insert(key_value.key);
1867
0
            else
1868
0
                oSetKeysToDropReference.insert(key_value.key);
1869
0
        }
1870
0
    };
1871
0
    g_cacheSources.cwalk(lambda);
1872
0
    for (const auto &key : oSetKeysToRemove)
1873
0
    {
1874
0
        CPLDebug("VRT", "Dropping %s", key.c_str());
1875
0
        g_cacheSources.remove(key);
1876
0
    }
1877
0
    for (const auto &key : oSetKeysToDropReference)
1878
0
    {
1879
0
        CPLDebug("VRT", "Dropping reference to %s", key.c_str());
1880
0
        CacheEntry oPair;
1881
0
        g_cacheSources.tryGet(key, oPair);
1882
0
        oPair.second.erase(this);
1883
0
        g_cacheSources.insert(key, oPair);
1884
0
    }
1885
0
}
1886
1887
/************************************************************************/
1888
/*                                   Read()                             */
1889
/************************************************************************/
1890
1891
static std::string CreateKey(const std::string &filename)
1892
0
{
1893
0
    return filename + CPLSPrintf("__thread_" CPL_FRMT_GIB, CPLGetPID());
1894
0
}
1895
1896
bool VRTMDArraySourceFromArray::Read(const GUInt64 *arrayStartIdx,
1897
                                     const size_t *count,
1898
                                     const GInt64 *arrayStep,
1899
                                     const GPtrDiff_t *bufferStride,
1900
                                     const GDALExtendedDataType &bufferDataType,
1901
                                     void *pDstBuffer) const
1902
0
{
1903
    // Preliminary check without trying to open source array
1904
0
    const auto nDims(m_poDstArray->GetDimensionCount());
1905
0
    for (size_t i = 0; i < nDims; i++)
1906
0
    {
1907
0
        auto start_i = arrayStartIdx[i];
1908
0
        auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1909
0
        if (arrayStep[i] < 0)
1910
0
        {
1911
            // For negative step request, temporarily simulate a positive step
1912
0
            start_i = start_i - (m_anCount[i] - 1) * (-step_i);
1913
0
            step_i = -step_i;
1914
0
        }
1915
0
        if (start_i + (count[i] - 1) * step_i < m_anDstOffset[i])
1916
0
        {
1917
0
            return true;
1918
0
        }
1919
0
    }
1920
1921
0
    for (size_t i = 0; i < nDims; i++)
1922
0
    {
1923
0
        if (m_anCount[i] == 0)  // we need to open the array...
1924
0
            break;
1925
1926
0
        auto start_i = arrayStartIdx[i];
1927
0
        auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
1928
0
        if (arrayStep[i] < 0)
1929
0
        {
1930
            // For negative step request, temporarily simulate a positive step
1931
0
            start_i = start_i - (m_anCount[i] - 1) * (-step_i);
1932
            // step_i = -step_i;
1933
0
        }
1934
0
        if (start_i >= m_anDstOffset[i] + m_anCount[i])
1935
0
        {
1936
0
            return true;
1937
0
        }
1938
0
    }
1939
1940
0
    const std::string osFilename =
1941
0
        m_bRelativeToVRT
1942
0
            ? CPLProjectRelativeFilenameSafe(m_poDstArray->GetVRTPath().c_str(),
1943
0
                                             m_osFilename.c_str())
1944
0
            : m_osFilename;
1945
0
    const std::string key(CreateKey(osFilename));
1946
1947
0
    std::shared_ptr<VRTArrayDatasetWrapper> poSrcDSWrapper;
1948
0
    GDALDataset *poSrcDS;
1949
0
    CacheEntry oPair;
1950
0
    {
1951
0
        std::lock_guard<std::mutex> oGuard(g_cacheLock);
1952
0
        if (g_cacheSources.tryGet(key, oPair))
1953
0
        {
1954
0
            poSrcDSWrapper = oPair.first;
1955
0
            poSrcDS = poSrcDSWrapper.get()->get();
1956
0
            if (oPair.second.find(this) == oPair.second.end())
1957
0
            {
1958
0
                oPair.second.insert(this);
1959
0
                g_cacheSources.insert(key, oPair);
1960
0
            }
1961
0
        }
1962
0
        else
1963
0
        {
1964
0
            poSrcDS = GDALDataset::Open(
1965
0
                osFilename.c_str(),
1966
0
                (m_osBand.empty() ? GDAL_OF_MULTIDIM_RASTER : GDAL_OF_RASTER) |
1967
0
                    GDAL_OF_INTERNAL | GDAL_OF_VERBOSE_ERROR,
1968
0
                nullptr, nullptr, nullptr);
1969
0
            if (!poSrcDS)
1970
0
                return false;
1971
0
            poSrcDSWrapper = std::make_shared<VRTArrayDatasetWrapper>(poSrcDS);
1972
0
            oPair.first = std::move(poSrcDSWrapper);
1973
0
            oPair.second.insert(this);
1974
0
            g_cacheSources.insert(key, oPair);
1975
0
        }
1976
0
    }
1977
1978
0
    std::shared_ptr<GDALMDArray> poArray;
1979
0
    if (m_osBand.empty())
1980
0
    {
1981
0
        auto rg(poSrcDS->GetRootGroup());
1982
0
        if (rg == nullptr)
1983
0
            return false;
1984
1985
0
        auto curGroup(rg);
1986
0
        std::string arrayName(m_osArray);
1987
0
        poArray = m_osArray[0] == '/' ? rg->OpenMDArrayFromFullname(arrayName)
1988
0
                                      : curGroup->OpenMDArray(arrayName);
1989
0
        if (poArray == nullptr)
1990
0
        {
1991
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s",
1992
0
                     m_osArray.c_str());
1993
0
            return false;
1994
0
        }
1995
0
    }
1996
0
    else
1997
0
    {
1998
0
        int nSrcBand = atoi(m_osBand.c_str());
1999
0
        auto poBand = poSrcDS->GetRasterBand(nSrcBand);
2000
0
        if (poBand == nullptr)
2001
0
            return false;
2002
0
        poArray = poBand->AsMDArray();
2003
0
        CPLAssert(poArray);
2004
0
    }
2005
2006
0
    std::string osViewExpr = m_osViewExpr;
2007
0
    if (STARTS_WITH(osViewExpr.c_str(), "resample=true,") ||
2008
0
        osViewExpr == "resample=true")
2009
0
    {
2010
0
        poArray =
2011
0
            poArray->GetResampled(std::vector<std::shared_ptr<GDALDimension>>(
2012
0
                                      poArray->GetDimensionCount()),
2013
0
                                  GRIORA_NearestNeighbour, nullptr, nullptr);
2014
0
        if (poArray == nullptr)
2015
0
        {
2016
0
            return false;
2017
0
        }
2018
0
        if (osViewExpr == "resample=true")
2019
0
            osViewExpr.clear();
2020
0
        else
2021
0
            osViewExpr = osViewExpr.substr(strlen("resample=true,"));
2022
0
    }
2023
2024
0
    if (!m_anTransposedAxis.empty())
2025
0
    {
2026
0
        poArray = poArray->Transpose(m_anTransposedAxis);
2027
0
        if (poArray == nullptr)
2028
0
        {
2029
0
            return false;
2030
0
        }
2031
0
    }
2032
0
    if (!osViewExpr.empty())
2033
0
    {
2034
0
        poArray = poArray->GetView(osViewExpr);
2035
0
        if (poArray == nullptr)
2036
0
        {
2037
0
            return false;
2038
0
        }
2039
0
    }
2040
0
    if (m_poDstArray->GetDimensionCount() != poArray->GetDimensionCount())
2041
0
    {
2042
0
        CPLError(CE_Failure, CPLE_AppDefined,
2043
0
                 "Inconsistent number of dimensions");
2044
0
        return false;
2045
0
    }
2046
2047
0
    const auto &srcDims(poArray->GetDimensions());
2048
0
    std::vector<GUInt64> anReqDstStart(nDims);
2049
0
    std::vector<size_t> anReqCount(nDims);
2050
    // Compute the intersection between the inline value slab and the
2051
    // request slab.
2052
0
    for (size_t i = 0; i < nDims; i++)
2053
0
    {
2054
0
        if (m_anSrcOffset[i] >= srcDims[i]->GetSize())
2055
0
        {
2056
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid SourceSlab.offset");
2057
0
            return false;
2058
0
        }
2059
0
        auto start_i = arrayStartIdx[i];
2060
0
        auto step_i = arrayStep[i] == 0 ? 1 : arrayStep[i];
2061
0
        if (arrayStep[i] < 0)
2062
0
        {
2063
0
            if (m_anCount[i] == 0)
2064
0
                m_anCount[i] = (m_anSrcOffset[i] + 1) / -step_i;
2065
            // For negative step request, temporarily simulate a positive step
2066
            // and fix up the start at the end of the loop.
2067
0
            start_i = start_i - (m_anCount[i] - 1) * (-step_i);
2068
0
            step_i = -step_i;
2069
0
        }
2070
0
        else
2071
0
        {
2072
0
            if (m_anCount[i] == 0)
2073
0
                m_anCount[i] =
2074
0
                    (srcDims[i]->GetSize() - m_anSrcOffset[i]) / step_i;
2075
0
        }
2076
2077
0
        const auto nRightDstOffsetFromConfig = m_anDstOffset[i] + m_anCount[i];
2078
0
        if (start_i >= nRightDstOffsetFromConfig)
2079
0
        {
2080
0
            return true;
2081
0
        }
2082
0
        if (start_i < m_anDstOffset[i])
2083
0
        {
2084
0
            anReqDstStart[i] =
2085
0
                m_anDstOffset[i] +
2086
0
                (step_i - ((m_anDstOffset[i] - start_i) % step_i)) % step_i;
2087
0
        }
2088
0
        else
2089
0
        {
2090
0
            anReqDstStart[i] = start_i;
2091
0
        }
2092
0
        anReqCount[i] = 1 + static_cast<size_t>(
2093
0
                                (std::min(nRightDstOffsetFromConfig - 1,
2094
0
                                          start_i + (count[i] - 1) * step_i) -
2095
0
                                 anReqDstStart[i]) /
2096
0
                                step_i);
2097
0
        if (arrayStep[i] < 0)
2098
0
        {
2099
0
            anReqDstStart[i] = anReqDstStart[i] + (anReqCount[i] - 1) * step_i;
2100
0
        }
2101
0
    }
2102
2103
0
    GPtrDiff_t nDstOffset = 0;
2104
0
    const auto nBufferDataTypeSize(bufferDataType.GetSize());
2105
0
    std::vector<GUInt64> anSrcArrayOffset(nDims);
2106
0
    std::vector<GInt64> anSrcArrayStep(nDims);
2107
0
    for (size_t i = 0; i < nDims; i++)
2108
0
    {
2109
0
        const size_t nRelStartDst =
2110
0
            static_cast<size_t>(anReqDstStart[i] - arrayStartIdx[i]);
2111
0
        nDstOffset += nRelStartDst * bufferStride[i] * nBufferDataTypeSize;
2112
0
        anSrcArrayOffset[i] =
2113
0
            m_anSrcOffset[i] +
2114
0
            (anReqDstStart[i] - m_anDstOffset[i]) * m_anStep[i];
2115
0
        if (arrayStep[i] < 0)
2116
0
            anSrcArrayStep[i] = -static_cast<GInt64>(
2117
0
                m_anStep[i] * static_cast<GUInt64>(-arrayStep[i]));
2118
0
        else
2119
0
            anSrcArrayStep[i] = m_anStep[i] * arrayStep[i];
2120
0
    }
2121
0
    return poArray->Read(anSrcArrayOffset.data(), anReqCount.data(),
2122
0
                         anSrcArrayStep.data(), bufferStride, bufferDataType,
2123
0
                         static_cast<GByte *>(pDstBuffer) + nDstOffset);
2124
0
}
2125
2126
/************************************************************************/
2127
/*                                   IRead()                            */
2128
/************************************************************************/
2129
2130
bool VRTMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
2131
                       const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
2132
                       const GDALExtendedDataType &bufferDataType,
2133
                       void *pDstBuffer) const
2134
0
{
2135
0
    const auto nDims(m_dims.size());
2136
2137
    // Initialize pDstBuffer
2138
0
    bool bFullyCompactStride = true;
2139
0
    std::map<size_t, size_t> mapStrideToIdx;
2140
0
    for (size_t i = 0; i < nDims; i++)
2141
0
    {
2142
0
        if (bufferStride[i] < 0 ||
2143
0
            mapStrideToIdx.find(static_cast<size_t>(bufferStride[i])) !=
2144
0
                mapStrideToIdx.end())
2145
0
        {
2146
0
            bFullyCompactStride = false;
2147
0
            break;
2148
0
        }
2149
0
        mapStrideToIdx[static_cast<size_t>(bufferStride[i])] = i;
2150
0
    }
2151
0
    size_t nAccStride = 1;
2152
0
    if (bFullyCompactStride)
2153
0
    {
2154
0
        for (size_t i = 0; i < nDims; i++)
2155
0
        {
2156
0
            auto oIter = mapStrideToIdx.find(nAccStride);
2157
0
            if (oIter == mapStrideToIdx.end())
2158
0
            {
2159
0
                bFullyCompactStride = false;
2160
0
                break;
2161
0
            }
2162
0
            nAccStride = nAccStride * count[oIter->second];
2163
0
        }
2164
0
    }
2165
2166
0
    const auto nDTSize(m_dt.GetSize());
2167
0
    const auto nBufferDTSize(bufferDataType.GetSize());
2168
0
    const GByte *pabyNoData = static_cast<const GByte *>(GetRawNoDataValue());
2169
0
    std::vector<GByte> abyFill;
2170
0
    if (pabyNoData)
2171
0
    {
2172
0
        bool bAllZero = true;
2173
0
        for (size_t i = 0; i < nDTSize; i++)
2174
0
        {
2175
0
            if (pabyNoData[i])
2176
0
            {
2177
0
                bAllZero = false;
2178
0
                break;
2179
0
            }
2180
0
        }
2181
0
        if (bAllZero)
2182
0
        {
2183
0
            pabyNoData = nullptr;
2184
0
        }
2185
0
        else
2186
0
        {
2187
0
            abyFill.resize(nBufferDTSize);
2188
0
            GDALExtendedDataType::CopyValue(pabyNoData, m_dt, &abyFill[0],
2189
0
                                            bufferDataType);
2190
0
        }
2191
0
    }
2192
2193
0
    if (bFullyCompactStride)
2194
0
    {
2195
0
        if (pabyNoData == nullptr)
2196
0
        {
2197
0
            memset(pDstBuffer, 0, nAccStride * nBufferDTSize);
2198
0
        }
2199
0
        else if (bufferDataType.NeedsFreeDynamicMemory())
2200
0
        {
2201
0
            GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2202
0
            for (size_t i = 0; i < nAccStride; i++)
2203
0
            {
2204
0
                GDALExtendedDataType::CopyValue(pabyDstBuffer, bufferDataType,
2205
0
                                                &abyFill[0], bufferDataType);
2206
0
                pabyDstBuffer += nBufferDTSize;
2207
0
            }
2208
0
        }
2209
0
        else
2210
0
        {
2211
0
            GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2212
0
            for (size_t i = 0; i < nAccStride; i++)
2213
0
            {
2214
0
                memcpy(pabyDstBuffer, &abyFill[0], nBufferDTSize);
2215
0
                pabyDstBuffer += nBufferDTSize;
2216
0
            }
2217
0
        }
2218
0
    }
2219
0
    else
2220
0
    {
2221
0
        const bool bNeedsDynamicMemory =
2222
0
            bufferDataType.NeedsFreeDynamicMemory();
2223
0
        std::vector<size_t> anStackCount(nDims);
2224
0
        std::vector<GByte *> abyStackDstPtr;
2225
0
        size_t iDim = 0;
2226
0
        abyStackDstPtr.push_back(static_cast<GByte *>(pDstBuffer));
2227
        // GCC 15.1 on msys2-mingw64
2228
0
#if defined(__GNUC__)
2229
0
#pragma GCC diagnostic push
2230
0
#pragma GCC diagnostic ignored "-Warray-bounds"
2231
0
#endif
2232
0
        abyStackDstPtr.resize(nDims + 1);
2233
0
#if defined(__GNUC__)
2234
0
#pragma GCC diagnostic pop
2235
0
#endif
2236
0
    lbl_next_depth:
2237
0
        if (iDim == nDims)
2238
0
        {
2239
0
            if (pabyNoData == nullptr)
2240
0
            {
2241
0
                memset(abyStackDstPtr[nDims], 0, nBufferDTSize);
2242
0
            }
2243
0
            else if (bNeedsDynamicMemory)
2244
0
            {
2245
0
                GDALExtendedDataType::CopyValue(abyStackDstPtr[nDims],
2246
0
                                                bufferDataType, &abyFill[0],
2247
0
                                                bufferDataType);
2248
0
            }
2249
0
            else
2250
0
            {
2251
0
                memcpy(abyStackDstPtr[nDims], &abyFill[0], nBufferDTSize);
2252
0
            }
2253
0
        }
2254
0
        else
2255
0
        {
2256
0
            anStackCount[iDim] = count[iDim];
2257
0
            while (true)
2258
0
            {
2259
0
                ++iDim;
2260
0
                abyStackDstPtr[iDim] = abyStackDstPtr[iDim - 1];
2261
0
                goto lbl_next_depth;
2262
0
            lbl_return_to_caller:
2263
0
                --iDim;
2264
0
                --anStackCount[iDim];
2265
0
                if (anStackCount[iDim] == 0)
2266
0
                    break;
2267
0
                abyStackDstPtr[iDim] += bufferStride[iDim] * nBufferDTSize;
2268
0
            }
2269
0
        }
2270
0
        if (iDim > 0)
2271
0
            goto lbl_return_to_caller;
2272
0
    }
2273
2274
0
    if (!abyFill.empty())
2275
0
    {
2276
0
        bufferDataType.FreeDynamicMemory(&abyFill[0]);
2277
0
    }
2278
2279
0
    for (const auto &poSource : m_sources)
2280
0
    {
2281
0
        if (!poSource->Read(arrayStartIdx, count, arrayStep, bufferStride,
2282
0
                            bufferDataType, pDstBuffer))
2283
0
        {
2284
0
            return false;
2285
0
        }
2286
0
    }
2287
0
    return true;
2288
0
}
2289
2290
/************************************************************************/
2291
/*                             SetDirty()                               */
2292
/************************************************************************/
2293
2294
void VRTMDArray::SetDirty()
2295
0
{
2296
0
    auto poGroup(GetGroup());
2297
0
    if (poGroup)
2298
0
    {
2299
0
        poGroup->SetDirty();
2300
0
    }
2301
0
}
2302
2303
/************************************************************************/
2304
/*                                GetGroup()                            */
2305
/************************************************************************/
2306
2307
VRTGroup *VRTMDArray::GetGroup() const
2308
0
{
2309
0
    auto ref = m_poGroupRef.lock();
2310
0
    return ref ? ref->m_ptr : nullptr;
2311
0
}
2312
2313
/************************************************************************/
2314
/*                           CreateAttribute()                          */
2315
/************************************************************************/
2316
2317
std::shared_ptr<GDALAttribute>
2318
VRTMDArray::CreateAttribute(const std::string &osName,
2319
                            const std::vector<GUInt64> &anDimensions,
2320
                            const GDALExtendedDataType &oDataType, CSLConstList)
2321
0
{
2322
0
    if (!VRTAttribute::CreationCommonChecks(osName, anDimensions,
2323
0
                                            m_oMapAttributes))
2324
0
    {
2325
0
        return nullptr;
2326
0
    }
2327
0
    SetDirty();
2328
0
    auto newAttr(std::make_shared<VRTAttribute>(
2329
0
        GetFullName(), osName, anDimensions.empty() ? 0 : anDimensions[0],
2330
0
        oDataType));
2331
0
    m_oMapAttributes[osName] = newAttr;
2332
0
    return newAttr;
2333
0
}
2334
2335
/************************************************************************/
2336
/*                               CopyFrom()                             */
2337
/************************************************************************/
2338
2339
bool VRTMDArray::CopyFrom(GDALDataset *poSrcDS, const GDALMDArray *poSrcArray,
2340
                          bool bStrict, GUInt64 &nCurCost,
2341
                          const GUInt64 nTotalCost,
2342
                          GDALProgressFunc pfnProgress, void *pProgressData)
2343
0
{
2344
0
    if (pfnProgress == nullptr)
2345
0
        pfnProgress = GDALDummyProgress;
2346
2347
0
    nCurCost += GDALMDArray::COPY_COST;
2348
2349
0
    if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
2350
0
                                 pfnProgress, pProgressData))
2351
0
    {
2352
0
        return false;
2353
0
    }
2354
2355
0
    nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
2356
2357
0
    if (poSrcDS)
2358
0
    {
2359
0
        const auto nDims(GetDimensionCount());
2360
0
        if (nDims == 1 && m_dims[0]->GetSize() > 2 &&
2361
0
            m_dims[0]->GetSize() < 10 * 1000 * 1000)
2362
0
        {
2363
0
            std::vector<double> adfTmp(
2364
0
                static_cast<size_t>(m_dims[0]->GetSize()));
2365
0
            const GUInt64 anStart[] = {0};
2366
0
            const size_t nCount = adfTmp.size();
2367
0
            const size_t anCount[] = {nCount};
2368
0
            if (poSrcArray->Read(anStart, anCount, nullptr, nullptr,
2369
0
                                 GDALExtendedDataType::Create(GDT_Float64),
2370
0
                                 &adfTmp[0]))
2371
0
            {
2372
0
                bool bRegular = true;
2373
0
                const double dfSpacing =
2374
0
                    (adfTmp.back() - adfTmp[0]) / (nCount - 1);
2375
0
                for (size_t i = 1; i < nCount; i++)
2376
0
                {
2377
0
                    if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfSpacing) >
2378
0
                        1e-3 * fabs(dfSpacing))
2379
0
                    {
2380
0
                        bRegular = false;
2381
0
                        break;
2382
0
                    }
2383
0
                }
2384
0
                if (bRegular)
2385
0
                {
2386
0
                    std::unique_ptr<VRTMDArraySourceRegularlySpaced> poSource(
2387
0
                        new VRTMDArraySourceRegularlySpaced(adfTmp[0],
2388
0
                                                            dfSpacing));
2389
0
                    AddSource(std::move(poSource));
2390
0
                }
2391
0
            }
2392
0
        }
2393
2394
0
        if (m_sources.empty())
2395
0
        {
2396
0
            std::vector<GUInt64> anSrcOffset(nDims);
2397
0
            std::vector<GUInt64> anCount(nDims);
2398
0
            std::vector<GUInt64> anStep(nDims, 1);
2399
0
            std::vector<GUInt64> anDstOffset(nDims);
2400
0
            for (size_t i = 0; i < nDims; i++)
2401
0
                anCount[i] = m_dims[i]->GetSize();
2402
2403
0
            std::unique_ptr<VRTMDArraySource> poSource(
2404
0
                new VRTMDArraySourceFromArray(
2405
0
                    this, false, false, poSrcDS->GetDescription(),
2406
0
                    poSrcArray->GetFullName(),
2407
0
                    std::string(),       // osBand
2408
0
                    std::vector<int>(),  // anTransposedAxis,
2409
0
                    std::string(),       // osViewExpr
2410
0
                    std::move(anSrcOffset), std::move(anCount),
2411
0
                    std::move(anStep), std::move(anDstOffset)));
2412
0
            AddSource(std::move(poSource));
2413
0
        }
2414
0
    }
2415
2416
0
    return true;
2417
0
}
2418
2419
/************************************************************************/
2420
/*                          GetRawNoDataValue()                         */
2421
/************************************************************************/
2422
2423
const void *VRTMDArray::GetRawNoDataValue() const
2424
0
{
2425
0
    return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
2426
0
}
2427
2428
/************************************************************************/
2429
/*                          SetRawNoDataValue()                         */
2430
/************************************************************************/
2431
2432
bool VRTMDArray::SetRawNoDataValue(const void *pNoData)
2433
0
{
2434
0
    SetDirty();
2435
2436
0
    if (!m_abyNoData.empty())
2437
0
    {
2438
0
        m_dt.FreeDynamicMemory(&m_abyNoData[0]);
2439
0
    }
2440
2441
0
    if (pNoData == nullptr)
2442
0
    {
2443
0
        m_abyNoData.clear();
2444
0
    }
2445
0
    else
2446
0
    {
2447
0
        const auto nSize = m_dt.GetSize();
2448
0
        m_abyNoData.resize(nSize);
2449
0
        memset(&m_abyNoData[0], 0, nSize);
2450
0
        GDALExtendedDataType::CopyValue(pNoData, m_dt, &m_abyNoData[0], m_dt);
2451
0
    }
2452
0
    return true;
2453
0
}
2454
2455
/************************************************************************/
2456
/*                           SetSpatialRef()                            */
2457
/************************************************************************/
2458
2459
bool VRTMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
2460
0
{
2461
0
    SetDirty();
2462
2463
0
    m_poSRS.reset();
2464
0
    if (poSRS)
2465
0
    {
2466
0
        m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
2467
0
    }
2468
0
    return true;
2469
0
}
2470
2471
/************************************************************************/
2472
/*                            AddSource()                               */
2473
/************************************************************************/
2474
2475
void VRTMDArray::AddSource(std::unique_ptr<VRTMDArraySource> &&poSource)
2476
0
{
2477
0
    SetDirty();
2478
2479
0
    m_sources.emplace_back(std::move(poSource));
2480
0
}
2481
2482
/************************************************************************/
2483
/*                             Serialize()                              */
2484
/************************************************************************/
2485
2486
void VRTMDArray::Serialize(CPLXMLNode *psParent, const char *pszVRTPath) const
2487
0
{
2488
0
    CPLXMLNode *psArray = CPLCreateXMLNode(psParent, CXT_Element, "Array");
2489
0
    CPLAddXMLAttributeAndValue(psArray, "name", GetName().c_str());
2490
0
    CPLXMLNode *psDataType = CPLCreateXMLNode(psArray, CXT_Element, "DataType");
2491
0
    if (m_dt.GetClass() == GEDTC_STRING)
2492
0
        CPLCreateXMLNode(psDataType, CXT_Text, "String");
2493
0
    else
2494
0
        CPLCreateXMLNode(psDataType, CXT_Text,
2495
0
                         GDALGetDataTypeName(m_dt.GetNumericDataType()));
2496
0
    for (const auto &dim : m_dims)
2497
0
    {
2498
0
        auto vrtDim(std::dynamic_pointer_cast<VRTDimension>(dim));
2499
0
        CPLAssert(vrtDim);
2500
0
        auto poGroup(GetGroup());
2501
0
        bool bSerializeDim = true;
2502
0
        if (poGroup)
2503
0
        {
2504
0
            auto groupDim(
2505
0
                poGroup->GetDimensionFromFullName(dim->GetFullName(), false));
2506
0
            if (groupDim && groupDim->GetSize() == dim->GetSize())
2507
0
            {
2508
0
                bSerializeDim = false;
2509
0
                CPLAssert(groupDim->GetGroup());
2510
0
                CPLXMLNode *psDimRef =
2511
0
                    CPLCreateXMLNode(psArray, CXT_Element, "DimensionRef");
2512
0
                CPLAddXMLAttributeAndValue(psDimRef, "ref",
2513
0
                                           groupDim->GetGroup() == poGroup
2514
0
                                               ? dim->GetName().c_str()
2515
0
                                               : dim->GetFullName().c_str());
2516
0
            }
2517
0
        }
2518
0
        if (bSerializeDim)
2519
0
        {
2520
0
            vrtDim->Serialize(psArray);
2521
0
        }
2522
0
    }
2523
2524
0
    if (m_poSRS && !m_poSRS->IsEmpty())
2525
0
    {
2526
0
        char *pszWKT = nullptr;
2527
0
        const char *const apszOptions[2] = {"FORMAT=WKT2_2018", nullptr};
2528
0
        m_poSRS->exportToWkt(&pszWKT, apszOptions);
2529
0
        CPLXMLNode *psSRSNode =
2530
0
            CPLCreateXMLElementAndValue(psArray, "SRS", pszWKT);
2531
0
        CPLFree(pszWKT);
2532
0
        const auto &mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
2533
0
        CPLString osMapping;
2534
0
        for (size_t i = 0; i < mapping.size(); ++i)
2535
0
        {
2536
0
            if (!osMapping.empty())
2537
0
                osMapping += ",";
2538
0
            osMapping += CPLSPrintf("%d", mapping[i]);
2539
0
        }
2540
0
        CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
2541
0
                                   osMapping.c_str());
2542
0
    }
2543
2544
0
    if (!m_osUnit.empty())
2545
0
    {
2546
0
        CPLCreateXMLElementAndValue(psArray, "Unit", m_osUnit.c_str());
2547
0
    }
2548
2549
0
    bool bHasNodata = false;
2550
0
    double dfNoDataValue = GetNoDataValueAsDouble(&bHasNodata);
2551
0
    if (bHasNodata)
2552
0
    {
2553
0
        CPLSetXMLValue(
2554
0
            psArray, "NoDataValue",
2555
0
            VRTSerializeNoData(dfNoDataValue, m_dt.GetNumericDataType(), 18)
2556
0
                .c_str());
2557
0
    }
2558
2559
0
    if (m_bHasOffset)
2560
0
    {
2561
0
        CPLCreateXMLElementAndValue(psArray, "Offset",
2562
0
                                    CPLSPrintf("%.17g", m_dfOffset));
2563
0
    }
2564
2565
0
    if (m_bHasScale)
2566
0
    {
2567
0
        CPLCreateXMLElementAndValue(psArray, "Scale",
2568
0
                                    CPLSPrintf("%.17g", m_dfScale));
2569
0
    }
2570
2571
0
    for (const auto &poSource : m_sources)
2572
0
    {
2573
0
        poSource->Serialize(psArray, pszVRTPath);
2574
0
    }
2575
2576
0
    for (const auto &iter : m_oMapAttributes)
2577
0
    {
2578
0
        iter.second->Serialize(psArray);
2579
0
    }
2580
0
}
2581
2582
/************************************************************************/
2583
/*                           VRTArraySource()                           */
2584
/************************************************************************/
2585
2586
class VRTArraySource : public VRTSource
2587
{
2588
    std::unique_ptr<CPLXMLNode, CPLXMLTreeCloserDeleter> m_poXMLTree{};
2589
    std::unique_ptr<GDALDataset> m_poDS{};
2590
    std::unique_ptr<VRTSimpleSource> m_poSimpleSource{};
2591
2592
  public:
2593
0
    VRTArraySource() = default;
2594
2595
    CPLErr RasterIO(GDALDataType eBandDataType, int nXOff, int nYOff,
2596
                    int nXSize, int nYSize, void *pData, int nBufXSize,
2597
                    int nBufYSize, GDALDataType eBufType, GSpacing nPixelSpace,
2598
                    GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
2599
                    WorkingState &oWorkingState) override;
2600
2601
    double GetMinimum(int nXSize, int nYSize, int *pbSuccess) override
2602
0
    {
2603
0
        return m_poSimpleSource->GetMinimum(nXSize, nYSize, pbSuccess);
2604
0
    }
2605
2606
    double GetMaximum(int nXSize, int nYSize, int *pbSuccess) override
2607
0
    {
2608
0
        return m_poSimpleSource->GetMaximum(nXSize, nYSize, pbSuccess);
2609
0
    }
2610
2611
    CPLErr GetHistogram(int nXSize, int nYSize, double dfMin, double dfMax,
2612
                        int nBuckets, GUIntBig *panHistogram,
2613
                        int bIncludeOutOfRange, int bApproxOK,
2614
                        GDALProgressFunc pfnProgress,
2615
                        void *pProgressData) override
2616
0
    {
2617
0
        return m_poSimpleSource->GetHistogram(
2618
0
            nXSize, nYSize, dfMin, dfMax, nBuckets, panHistogram,
2619
0
            bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
2620
0
    }
2621
2622
    const char *GetType() const override
2623
0
    {
2624
0
        return "ArraySource";
2625
0
    }
2626
2627
    CPLErr XMLInit(const CPLXMLNode *psTree, const char *pszVRTPath,
2628
                   VRTMapSharedResources &oMapSharedSources) override;
2629
    CPLXMLNode *SerializeToXML(const char *pszVRTPath) override;
2630
};
2631
2632
/************************************************************************/
2633
/*                              RasterIO()                              */
2634
/************************************************************************/
2635
2636
CPLErr VRTArraySource::RasterIO(GDALDataType eBandDataType, int nXOff,
2637
                                int nYOff, int nXSize, int nYSize, void *pData,
2638
                                int nBufXSize, int nBufYSize,
2639
                                GDALDataType eBufType, GSpacing nPixelSpace,
2640
                                GSpacing nLineSpace,
2641
                                GDALRasterIOExtraArg *psExtraArg,
2642
                                WorkingState &oWorkingState)
2643
0
{
2644
0
    return m_poSimpleSource->RasterIO(eBandDataType, nXOff, nYOff, nXSize,
2645
0
                                      nYSize, pData, nBufXSize, nBufYSize,
2646
0
                                      eBufType, nPixelSpace, nLineSpace,
2647
0
                                      psExtraArg, oWorkingState);
2648
0
}
2649
2650
/************************************************************************/
2651
/*                        ParseSingleSourceArray()                      */
2652
/************************************************************************/
2653
2654
static std::shared_ptr<GDALMDArray>
2655
ParseSingleSourceArray(const CPLXMLNode *psSingleSourceArray,
2656
                       const char *pszVRTPath)
2657
0
{
2658
0
    const auto psSourceFileNameNode =
2659
0
        CPLGetXMLNode(psSingleSourceArray, "SourceFilename");
2660
0
    if (!psSourceFileNameNode)
2661
0
    {
2662
0
        CPLError(CE_Failure, CPLE_AppDefined,
2663
0
                 "Cannot find <SourceFilename> in <SingleSourceArray>");
2664
0
        return nullptr;
2665
0
    }
2666
0
    const char *pszSourceFilename =
2667
0
        CPLGetXMLValue(psSourceFileNameNode, nullptr, "");
2668
0
    const bool bRelativeToVRT = CPL_TO_BOOL(
2669
0
        atoi(CPLGetXMLValue(psSourceFileNameNode, "relativeToVRT", "0")));
2670
2671
0
    const char *pszSourceArray =
2672
0
        CPLGetXMLValue(psSingleSourceArray, "SourceArray", nullptr);
2673
0
    if (!pszSourceArray)
2674
0
    {
2675
0
        CPLError(CE_Failure, CPLE_AppDefined,
2676
0
                 "Cannot find <SourceArray> in <SingleSourceArray>");
2677
0
        return nullptr;
2678
0
    }
2679
0
    const std::string osSourceFilename(
2680
0
        bRelativeToVRT
2681
0
            ? CPLProjectRelativeFilenameSafe(pszVRTPath, pszSourceFilename)
2682
0
            : std::string(pszSourceFilename));
2683
0
    auto poDS = std::unique_ptr<GDALDataset>(
2684
0
        GDALDataset::Open(osSourceFilename.c_str(),
2685
0
                          GDAL_OF_MULTIDIM_RASTER | GDAL_OF_VERBOSE_ERROR,
2686
0
                          nullptr, nullptr, nullptr));
2687
0
    if (!poDS)
2688
0
        return nullptr;
2689
0
    auto poRG = poDS->GetRootGroup();
2690
0
    if (!poRG)
2691
0
        return nullptr;
2692
0
    auto poArray = poRG->OpenMDArrayFromFullname(pszSourceArray);
2693
0
    if (!poArray)
2694
0
    {
2695
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array '%s' in %s",
2696
0
                 pszSourceArray, osSourceFilename.c_str());
2697
0
    }
2698
0
    return poArray;
2699
0
}
2700
2701
/************************************************************************/
2702
/*                              XMLInit()                               */
2703
/************************************************************************/
2704
2705
CPLErr VRTArraySource::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPath,
2706
                               VRTMapSharedResources & /*oMapSharedSources*/)
2707
0
{
2708
0
    const auto poArray = ParseArray(psTree, pszVRTPath, "ArraySource");
2709
0
    if (!poArray)
2710
0
    {
2711
0
        return CE_Failure;
2712
0
    }
2713
0
    if (poArray->GetDimensionCount() != 2)
2714
0
    {
2715
0
        CPLError(CE_Failure, CPLE_NotSupported,
2716
0
                 "Array referenced in <ArraySource> should be a "
2717
0
                 "two-dimensional array");
2718
0
        return CE_Failure;
2719
0
    }
2720
2721
0
    m_poDS.reset(poArray->AsClassicDataset(1, 0));
2722
0
    if (!m_poDS)
2723
0
        return CE_Failure;
2724
2725
0
    m_poSimpleSource = std::make_unique<VRTSimpleSource>();
2726
0
    auto poBand = m_poDS->GetRasterBand(1);
2727
0
    m_poSimpleSource->SetSrcBand(poBand);
2728
0
    m_poDS->Reference();
2729
2730
0
    if (m_poSimpleSource->ParseSrcRectAndDstRect(psTree) != CE_None)
2731
0
        return CE_Failure;
2732
0
    if (!CPLGetXMLNode(psTree, "SrcRect"))
2733
0
        m_poSimpleSource->SetSrcWindow(0, 0, poBand->GetXSize(),
2734
0
                                       poBand->GetYSize());
2735
0
    if (!CPLGetXMLNode(psTree, "DstRect"))
2736
0
        m_poSimpleSource->SetDstWindow(0, 0, poBand->GetXSize(),
2737
0
                                       poBand->GetYSize());
2738
2739
0
    m_poXMLTree.reset(CPLCloneXMLTree(psTree));
2740
0
    return CE_None;
2741
0
}
2742
2743
/************************************************************************/
2744
/*                          SerializeToXML()                            */
2745
/************************************************************************/
2746
2747
CPLXMLNode *VRTArraySource::SerializeToXML(const char * /*pszVRTPath*/)
2748
0
{
2749
0
    if (m_poXMLTree)
2750
0
    {
2751
0
        return CPLCloneXMLTree(m_poXMLTree.get());
2752
0
    }
2753
0
    else
2754
0
    {
2755
0
        CPLError(CE_Failure, CPLE_NotSupported,
2756
0
                 "VRTArraySource::SerializeToXML() not implemented");
2757
0
        return nullptr;
2758
0
    }
2759
0
}
2760
2761
/************************************************************************/
2762
/*                     VRTDerivedArrayCreate()                          */
2763
/************************************************************************/
2764
2765
std::shared_ptr<GDALMDArray> VRTDerivedArrayCreate(const char *pszVRTPath,
2766
                                                   const CPLXMLNode *psTree)
2767
0
{
2768
0
    auto poArray = ParseArray(psTree, pszVRTPath, "DerivedArray");
2769
2770
0
    const auto GetOptions =
2771
0
        [](const CPLXMLNode *psParent, CPLStringList &aosOptions)
2772
0
    {
2773
0
        for (const CPLXMLNode *psOption = CPLGetXMLNode(psParent, "Option");
2774
0
             psOption; psOption = psOption->psNext)
2775
0
        {
2776
0
            if (psOption->eType == CXT_Element &&
2777
0
                strcmp(psOption->pszValue, "Option") == 0)
2778
0
            {
2779
0
                const char *pszName = CPLGetXMLValue(psOption, "name", nullptr);
2780
0
                if (!pszName)
2781
0
                {
2782
0
                    CPLError(
2783
0
                        CE_Failure, CPLE_AppDefined,
2784
0
                        "Cannot find 'name' attribute in <Option> element");
2785
0
                    return false;
2786
0
                }
2787
0
                const char *pszValue = CPLGetXMLValue(psOption, nullptr, "");
2788
0
                aosOptions.SetNameValue(pszName, pszValue);
2789
0
            }
2790
0
        }
2791
0
        return true;
2792
0
    };
2793
2794
0
    for (const CPLXMLNode *psStep = CPLGetXMLNode(psTree, "Step");
2795
0
         psStep && poArray; psStep = psStep->psNext)
2796
0
    {
2797
0
        if (psStep->eType != CXT_Element ||
2798
0
            strcmp(psStep->pszValue, "Step") != 0)
2799
0
            continue;
2800
2801
0
        if (const CPLXMLNode *psView = CPLGetXMLNode(psStep, "View"))
2802
0
        {
2803
0
            const char *pszExpr = CPLGetXMLValue(psView, "expr", nullptr);
2804
0
            if (!pszExpr)
2805
0
            {
2806
0
                CPLError(CE_Failure, CPLE_AppDefined,
2807
0
                         "Cannot find 'expr' attribute in <View> element");
2808
0
                return nullptr;
2809
0
            }
2810
0
            poArray = poArray->GetView(pszExpr);
2811
0
        }
2812
0
        else if (const CPLXMLNode *psTranspose =
2813
0
                     CPLGetXMLNode(psStep, "Transpose"))
2814
0
        {
2815
0
            const char *pszOrder =
2816
0
                CPLGetXMLValue(psTranspose, "newOrder", nullptr);
2817
0
            if (!pszOrder)
2818
0
            {
2819
0
                CPLError(
2820
0
                    CE_Failure, CPLE_AppDefined,
2821
0
                    "Cannot find 'newOrder' attribute in <Transpose> element");
2822
0
                return nullptr;
2823
0
            }
2824
0
            std::vector<int> anMapNewAxisToOldAxis;
2825
0
            const CPLStringList aosItems(CSLTokenizeString2(pszOrder, ",", 0));
2826
0
            for (int i = 0; i < aosItems.size(); ++i)
2827
0
                anMapNewAxisToOldAxis.push_back(atoi(aosItems[i]));
2828
0
            poArray = poArray->Transpose(anMapNewAxisToOldAxis);
2829
0
        }
2830
0
        else if (const CPLXMLNode *psResample =
2831
0
                     CPLGetXMLNode(psStep, "Resample"))
2832
0
        {
2833
0
            std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
2834
0
            auto poDummyGroup = std::shared_ptr<VRTGroup>(
2835
0
                new VRTGroup(pszVRTPath ? pszVRTPath : ""));
2836
0
            for (const CPLXMLNode *psDimension =
2837
0
                     CPLGetXMLNode(psResample, "Dimension");
2838
0
                 psDimension; psDimension = psDimension->psNext)
2839
0
            {
2840
0
                if (psDimension->eType == CXT_Element &&
2841
0
                    strcmp(psDimension->pszValue, "Dimension") == 0)
2842
0
                {
2843
0
                    auto apoDim = VRTDimension::Create(
2844
0
                        poDummyGroup, std::string(), psDimension);
2845
0
                    if (!apoDim)
2846
0
                        return nullptr;
2847
0
                    apoNewDims.emplace_back(std::move(apoDim));
2848
0
                }
2849
0
            }
2850
0
            if (apoNewDims.empty())
2851
0
                apoNewDims.resize(poArray->GetDimensionCount());
2852
2853
0
            const char *pszResampleAlg =
2854
0
                CPLGetXMLValue(psResample, "ResampleAlg", "NEAR");
2855
0
            const auto eResampleAlg =
2856
0
                GDALRasterIOGetResampleAlg(pszResampleAlg);
2857
2858
0
            std::unique_ptr<OGRSpatialReference> poSRS;
2859
0
            const char *pszSRS = CPLGetXMLValue(psResample, "SRS", nullptr);
2860
0
            if (pszSRS)
2861
0
            {
2862
0
                poSRS = std::make_unique<OGRSpatialReference>();
2863
0
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2864
0
                if (poSRS->SetFromUserInput(
2865
0
                        pszSRS, OGRSpatialReference::
2866
0
                                    SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
2867
0
                    OGRERR_NONE)
2868
0
                {
2869
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2870
0
                             "Invalid value for <SRS>");
2871
0
                    return nullptr;
2872
0
                }
2873
0
            }
2874
2875
0
            CPLStringList aosOptions;
2876
0
            if (!GetOptions(psResample, aosOptions))
2877
0
                return nullptr;
2878
2879
0
            poArray = poArray->GetResampled(apoNewDims, eResampleAlg,
2880
0
                                            poSRS.get(), aosOptions.List());
2881
0
        }
2882
0
        else if (const CPLXMLNode *psGrid = CPLGetXMLNode(psStep, "Grid"))
2883
0
        {
2884
0
            const char *pszGridOptions =
2885
0
                CPLGetXMLValue(psGrid, "GridOptions", nullptr);
2886
0
            if (!pszGridOptions)
2887
0
            {
2888
0
                CPLError(CE_Failure, CPLE_AppDefined,
2889
0
                         "Cannot find <GridOptions> in <Grid> element");
2890
0
                return nullptr;
2891
0
            }
2892
2893
0
            std::shared_ptr<GDALMDArray> poXArray;
2894
0
            if (const CPLXMLNode *psXArrayNode =
2895
0
                    CPLGetXMLNode(psGrid, "XArray"))
2896
0
            {
2897
0
                poXArray = ParseArray(psXArrayNode, pszVRTPath, "XArray");
2898
0
                if (!poXArray)
2899
0
                    return nullptr;
2900
0
            }
2901
2902
0
            std::shared_ptr<GDALMDArray> poYArray;
2903
0
            if (const CPLXMLNode *psYArrayNode =
2904
0
                    CPLGetXMLNode(psGrid, "YArray"))
2905
0
            {
2906
0
                poYArray = ParseArray(psYArrayNode, pszVRTPath, "YArray");
2907
0
                if (!poYArray)
2908
0
                    return nullptr;
2909
0
            }
2910
2911
0
            CPLStringList aosOptions;
2912
0
            if (!GetOptions(psGrid, aosOptions))
2913
0
                return nullptr;
2914
2915
0
            poArray = poArray->GetGridded(pszGridOptions, poXArray, poYArray,
2916
0
                                          aosOptions.List());
2917
0
        }
2918
0
        else if (const CPLXMLNode *psGetMask = CPLGetXMLNode(psStep, "GetMask"))
2919
0
        {
2920
0
            CPLStringList aosOptions;
2921
0
            if (!GetOptions(psGetMask, aosOptions))
2922
0
                return nullptr;
2923
2924
0
            poArray = poArray->GetMask(aosOptions.List());
2925
0
        }
2926
0
        else if (CPLGetXMLNode(psStep, "GetUnscaled"))
2927
0
        {
2928
0
            poArray = poArray->GetUnscaled();
2929
0
        }
2930
0
        else
2931
0
        {
2932
0
            CPLError(CE_Failure, CPLE_NotSupported,
2933
0
                     "Unknown <Step>.<%s> element",
2934
0
                     psStep->psChild ? psStep->psChild->pszValue : "(null)");
2935
0
            return nullptr;
2936
0
        }
2937
0
    }
2938
2939
0
    return poArray;
2940
0
}
2941
2942
/************************************************************************/
2943
/*                              ParseArray()                            */
2944
/************************************************************************/
2945
2946
static std::shared_ptr<GDALMDArray> ParseArray(const CPLXMLNode *psTree,
2947
                                               const char *pszVRTPath,
2948
                                               const char *pszParentXMLNode)
2949
0
{
2950
0
    if (const CPLXMLNode *psSingleSourceArrayNode =
2951
0
            CPLGetXMLNode(psTree, "SingleSourceArray"))
2952
0
        return ParseSingleSourceArray(psSingleSourceArrayNode, pszVRTPath);
2953
2954
0
    if (const CPLXMLNode *psArrayNode = CPLGetXMLNode(psTree, "Array"))
2955
0
    {
2956
0
        return VRTMDArray::Create(pszVRTPath, psArrayNode);
2957
0
    }
2958
2959
0
    if (const CPLXMLNode *psDerivedArrayNode =
2960
0
            CPLGetXMLNode(psTree, "DerivedArray"))
2961
0
    {
2962
0
        return VRTDerivedArrayCreate(pszVRTPath, psDerivedArrayNode);
2963
0
    }
2964
2965
0
    CPLError(
2966
0
        CE_Failure, CPLE_AppDefined,
2967
0
        "Cannot find a <SimpleSourceArray>, <Array> or <DerivedArray> in <%s>",
2968
0
        pszParentXMLNode);
2969
0
    return nullptr;
2970
0
}
2971
2972
/************************************************************************/
2973
/*                       VRTParseArraySource()                          */
2974
/************************************************************************/
2975
2976
VRTSource *VRTParseArraySource(const CPLXMLNode *psChild,
2977
                               const char *pszVRTPath,
2978
                               VRTMapSharedResources &oMapSharedSources)
2979
0
{
2980
0
    VRTSource *poSource = nullptr;
2981
2982
0
    if (EQUAL(psChild->pszValue, "ArraySource"))
2983
0
    {
2984
0
        poSource = new VRTArraySource();
2985
0
    }
2986
0
    else
2987
0
    {
2988
0
        CPLError(CE_Failure, CPLE_AppDefined,
2989
0
                 "VRTParseArraySource() - Unknown source : %s",
2990
0
                 psChild->pszValue);
2991
0
        return nullptr;
2992
0
    }
2993
2994
0
    if (poSource->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
2995
0
        return poSource;
2996
2997
0
    delete poSource;
2998
0
    return nullptr;
2999
0
}
3000
3001
/*! @endcond */