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

Generated by: LCOV version 1.14