LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1403 1580 88.8 %
Date: 2026-01-30 03:16:47 Functions: 85 90 94.4 %

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

Generated by: LCOV version 1.14