LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1397 1572 88.9 %
Date: 2026-09-09 22:24:20 Functions: 86 91 94.5 %

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

Generated by: LCOV version 1.14