LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1303 1426 91.4 %
Date: 2025-07-01 22:47:05 Functions: 79 82 96.3 %

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

Generated by: LCOV version 1.14