Coverage Report

Created: 2026-09-14 06:50

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