Coverage Report

Created: 2026-06-30 08:33

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