LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1301 1426 91.2 %
Date: 2025-03-29 15:49:56 Functions: 78 82 95.1 %

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

Generated by: LCOV version 1.14