Coverage Report

Created: 2026-02-14 06:52

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