LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1302 1427 91.2 %
Date: 2024-11-21 22:18:42 Functions: 78 82 95.1 %

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

Generated by: LCOV version 1.14