Coverage Report

Created: 2025-11-16 06:25

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