LCOV - code coverage report
Current view: top level - frmts/vrt - vrtmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1302 1425 91.4 %
Date: 2024-05-06 22:33:47 Functions: 78 81 96.3 %

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

Generated by: LCOV version 1.14