LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1329 1458 91.2 %
Date: 2025-07-02 23:05:47 Functions: 59 62 95.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTDataset
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "vrtdataset.h"
      15             : 
      16             : #include "cpl_error_internal.h"
      17             : #include "cpl_minixml.h"
      18             : #include "cpl_string.h"
      19             : #include "gdal_frmts.h"
      20             : #include "ogr_spatialref.h"
      21             : #include "gdal_thread_pool.h"
      22             : #include "gdal_utils.h"
      23             : 
      24             : #include <algorithm>
      25             : #include <cassert>
      26             : #include <cmath>
      27             : #include <set>
      28             : #include <typeinfo>
      29             : #include "gdal_proxy.h"
      30             : 
      31             : /*! @cond Doxygen_Suppress */
      32             : 
      33             : #define VRT_PROTOCOL_PREFIX "vrt://"
      34             : 
      35             : constexpr int DEFAULT_BLOCK_SIZE = 128;
      36             : 
      37             : /************************************************************************/
      38             : /*                            VRTDataset()                              */
      39             : /************************************************************************/
      40             : 
      41        6409 : VRTDataset::VRTDataset(int nXSize, int nYSize, int nBlockXSize, int nBlockYSize)
      42             : {
      43        6409 :     nRasterXSize = nXSize;
      44        6409 :     nRasterYSize = nYSize;
      45             : 
      46        6409 :     m_bBlockSizeSpecified = nBlockXSize > 0 && nBlockYSize > 0;
      47        6409 :     m_nBlockXSize =
      48        6409 :         nBlockXSize > 0 ? nBlockXSize : std::min(DEFAULT_BLOCK_SIZE, nXSize);
      49        6409 :     m_nBlockYSize =
      50        6409 :         nBlockYSize > 0 ? nBlockYSize : std::min(DEFAULT_BLOCK_SIZE, nYSize);
      51             : 
      52        6409 :     GDALRegister_VRT();
      53             : 
      54        6409 :     poDriver = static_cast<GDALDriver *>(GDALGetDriverByName("VRT"));
      55        6409 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                          IsDefaultBlockSize()                        */
      59             : /************************************************************************/
      60             : 
      61        1510 : /* static */ bool VRTDataset::IsDefaultBlockSize(int nBlockSize, int nDimension)
      62             : {
      63        2434 :     return nBlockSize == DEFAULT_BLOCK_SIZE ||
      64        2434 :            (nBlockSize < DEFAULT_BLOCK_SIZE && nBlockSize == nDimension);
      65             : }
      66             : 
      67             : /*! @endcond */
      68             : 
      69             : /************************************************************************/
      70             : /*                              VRTCreate()                             */
      71             : /************************************************************************/
      72             : 
      73             : /**
      74             :  * @see VRTDataset::VRTDataset()
      75             :  */
      76             : 
      77        2047 : VRTDatasetH CPL_STDCALL VRTCreate(int nXSize, int nYSize)
      78             : 
      79             : {
      80        2047 :     auto poDS = new VRTDataset(nXSize, nYSize);
      81        2047 :     poDS->eAccess = GA_Update;
      82        2047 :     return poDS;
      83             : }
      84             : 
      85             : /*! @cond Doxygen_Suppress */
      86             : 
      87             : /************************************************************************/
      88             : /*                            ~VRTDataset()                            */
      89             : /************************************************************************/
      90             : 
      91       11588 : VRTDataset::~VRTDataset()
      92             : 
      93             : {
      94        6409 :     VRTDataset::FlushCache(true);
      95        6409 :     CPLFree(m_pszVRTPath);
      96             : 
      97        6464 :     for (size_t i = 0; i < m_apoOverviews.size(); i++)
      98          55 :         delete m_apoOverviews[i];
      99        6416 :     for (size_t i = 0; i < m_apoOverviewsBak.size(); i++)
     100           7 :         delete m_apoOverviewsBak[i];
     101        6409 :     CSLDestroy(m_papszXMLVRTMetadata);
     102       11588 : }
     103             : 
     104             : /************************************************************************/
     105             : /*                             FlushCache()                             */
     106             : /************************************************************************/
     107             : 
     108        6761 : CPLErr VRTDataset::FlushCache(bool bAtClosing)
     109             : 
     110             : {
     111        6761 :     if (m_poRootGroup)
     112         338 :         return m_poRootGroup->Serialize() ? CE_None : CE_Failure;
     113             :     else
     114        6423 :         return VRTFlushCacheStruct<VRTDataset>::FlushCache(*this, bAtClosing);
     115             : }
     116             : 
     117             : /************************************************************************/
     118             : /*                             FlushCache()                             */
     119             : /************************************************************************/
     120             : 
     121         754 : CPLErr VRTWarpedDataset::FlushCache(bool bAtClosing)
     122             : 
     123             : {
     124         754 :     return VRTFlushCacheStruct<VRTWarpedDataset>::FlushCache(*this, bAtClosing);
     125             : }
     126             : 
     127             : /************************************************************************/
     128             : /*                             FlushCache()                             */
     129             : /************************************************************************/
     130             : 
     131         190 : CPLErr VRTPansharpenedDataset::FlushCache(bool bAtClosing)
     132             : 
     133             : {
     134         190 :     return VRTFlushCacheStruct<VRTPansharpenedDataset>::FlushCache(*this,
     135         190 :                                                                    bAtClosing);
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                             FlushCache()                             */
     140             : /************************************************************************/
     141             : 
     142         198 : CPLErr VRTProcessedDataset::FlushCache(bool bAtClosing)
     143             : 
     144             : {
     145         198 :     return VRTFlushCacheStruct<VRTProcessedDataset>::FlushCache(*this,
     146         198 :                                                                 bAtClosing);
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                             FlushCache()                             */
     151             : /************************************************************************/
     152             : 
     153             : template <class T>
     154        7565 : CPLErr VRTFlushCacheStruct<T>::FlushCache(T &obj, bool bAtClosing)
     155             : {
     156        7565 :     CPLErr eErr = obj.GDALDataset::FlushCache(bAtClosing);
     157             : 
     158        7565 :     if (!obj.m_bNeedsFlush || !obj.m_bWritable)
     159        3629 :         return eErr;
     160             : 
     161             :     // We don't write to disk if there is no filename.  This is a
     162             :     // memory only dataset.
     163        4349 :     if (strlen(obj.GetDescription()) == 0 ||
     164         413 :         STARTS_WITH_CI(obj.GetDescription(), "<VRTDataset"))
     165        3540 :         return eErr;
     166             : 
     167         396 :     obj.m_bNeedsFlush = false;
     168             : 
     169             :     // Serialize XML representation to disk
     170         396 :     const std::string osVRTPath(CPLGetPathSafe(obj.GetDescription()));
     171         396 :     CPLXMLNode *psDSTree = obj.T::SerializeToXML(osVRTPath.c_str());
     172         396 :     if (!CPLSerializeXMLTreeToFile(psDSTree, obj.GetDescription()))
     173          13 :         eErr = CE_Failure;
     174         396 :     CPLDestroyXMLNode(psDSTree);
     175         396 :     return eErr;
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                            GetMetadata()                             */
     180             : /************************************************************************/
     181             : 
     182       16753 : char **VRTDataset::GetMetadata(const char *pszDomain)
     183             : {
     184       16753 :     if (pszDomain != nullptr && EQUAL(pszDomain, "xml:VRT"))
     185             :     {
     186             :         /* ------------------------------------------------------------------ */
     187             :         /*      Convert tree to a single block of XML text.                   */
     188             :         /* ------------------------------------------------------------------ */
     189          51 :         const char *pszDescription = GetDescription();
     190          51 :         char *l_pszVRTPath = CPLStrdup(
     191          51 :             pszDescription[0] && !STARTS_WITH(pszDescription, "<VRTDataset")
     192          71 :                 ? CPLGetPathSafe(pszDescription).c_str()
     193             :                 : "");
     194          51 :         CPLXMLNode *psDSTree = SerializeToXML(l_pszVRTPath);
     195          51 :         char *pszXML = CPLSerializeXMLTree(psDSTree);
     196             : 
     197          51 :         CPLDestroyXMLNode(psDSTree);
     198             : 
     199          51 :         CPLFree(l_pszVRTPath);
     200             : 
     201          51 :         CSLDestroy(m_papszXMLVRTMetadata);
     202          51 :         m_papszXMLVRTMetadata =
     203          51 :             static_cast<char **>(CPLMalloc(2 * sizeof(char *)));
     204          51 :         m_papszXMLVRTMetadata[0] = pszXML;
     205          51 :         m_papszXMLVRTMetadata[1] = nullptr;
     206          51 :         return m_papszXMLVRTMetadata;
     207             :     }
     208             : 
     209       16702 :     return GDALDataset::GetMetadata(pszDomain);
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                          GetMetadataItem()                           */
     214             : /************************************************************************/
     215             : 
     216       25623 : const char *VRTDataset::GetMetadataItem(const char *pszName,
     217             :                                         const char *pszDomain)
     218             : 
     219             : {
     220       25623 :     if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__"))
     221             :     {
     222          10 :         if (EQUAL(pszName, "MULTI_THREADED_RASTERIO_LAST_USED"))
     223           9 :             return m_bMultiThreadedRasterIOLastUsed ? "1" : "0";
     224           1 :         else if (EQUAL(pszName, "CheckCompatibleForDatasetIO()"))
     225           1 :             return CheckCompatibleForDatasetIO() ? "1" : "0";
     226             :     }
     227       25613 :     return GDALDataset::GetMetadataItem(pszName, pszDomain);
     228             : }
     229             : 
     230             : /*! @endcond */
     231             : 
     232             : /************************************************************************/
     233             : /*                            VRTFlushCache(bool bAtClosing) */
     234             : /************************************************************************/
     235             : 
     236             : /**
     237             :  * @see VRTDataset::FlushCache(bool bAtClosing)
     238             :  */
     239             : 
     240           0 : void CPL_STDCALL VRTFlushCache(VRTDatasetH hDataset)
     241             : {
     242           0 :     VALIDATE_POINTER0(hDataset, "VRTFlushCache");
     243             : 
     244           0 :     static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
     245           0 :         ->FlushCache(false);
     246             : }
     247             : 
     248             : /*! @cond Doxygen_Suppress */
     249             : 
     250             : /************************************************************************/
     251             : /*                           SerializeToXML()                           */
     252             : /************************************************************************/
     253             : 
     254         615 : CPLXMLNode *VRTDataset::SerializeToXML(const char *pszVRTPathIn)
     255             : 
     256             : {
     257         615 :     if (m_poRootGroup)
     258          77 :         return m_poRootGroup->SerializeToXML(pszVRTPathIn);
     259             : 
     260             :     /* -------------------------------------------------------------------- */
     261             :     /*      Setup root node and attributes.                                 */
     262             :     /* -------------------------------------------------------------------- */
     263         538 :     CPLXMLNode *psDSTree = CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset");
     264             : 
     265         538 :     char szNumber[128] = {'\0'};
     266         538 :     snprintf(szNumber, sizeof(szNumber), "%d", GetRasterXSize());
     267         538 :     CPLSetXMLValue(psDSTree, "#rasterXSize", szNumber);
     268             : 
     269         538 :     snprintf(szNumber, sizeof(szNumber), "%d", GetRasterYSize());
     270         538 :     CPLSetXMLValue(psDSTree, "#rasterYSize", szNumber);
     271             : 
     272             :     /* -------------------------------------------------------------------- */
     273             :     /*      SRS                                                             */
     274             :     /* -------------------------------------------------------------------- */
     275         538 :     if (m_poSRS && !m_poSRS->IsEmpty())
     276             :     {
     277         398 :         char *pszWKT = nullptr;
     278         398 :         m_poSRS->exportToWkt(&pszWKT);
     279             :         CPLXMLNode *psSRSNode =
     280         398 :             CPLCreateXMLElementAndValue(psDSTree, "SRS", pszWKT);
     281         398 :         CPLFree(pszWKT);
     282         398 :         const auto &mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
     283         796 :         CPLString osMapping;
     284        1196 :         for (size_t i = 0; i < mapping.size(); ++i)
     285             :         {
     286         798 :             if (!osMapping.empty())
     287         400 :                 osMapping += ",";
     288         798 :             osMapping += CPLSPrintf("%d", mapping[i]);
     289             :         }
     290         398 :         CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
     291             :                                    osMapping.c_str());
     292         398 :         const double dfCoordinateEpoch = m_poSRS->GetCoordinateEpoch();
     293         398 :         if (dfCoordinateEpoch > 0)
     294             :         {
     295           2 :             std::string osCoordinateEpoch = CPLSPrintf("%f", dfCoordinateEpoch);
     296           1 :             if (osCoordinateEpoch.find('.') != std::string::npos)
     297             :             {
     298           6 :                 while (osCoordinateEpoch.back() == '0')
     299           5 :                     osCoordinateEpoch.pop_back();
     300             :             }
     301           1 :             CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
     302             :                                        osCoordinateEpoch.c_str());
     303             :         }
     304             :     }
     305             : 
     306             :     /* -------------------------------------------------------------------- */
     307             :     /*      Geotransform.                                                   */
     308             :     /* -------------------------------------------------------------------- */
     309         538 :     if (m_bGeoTransformSet)
     310             :     {
     311         471 :         CPLSetXMLValue(
     312             :             psDSTree, "GeoTransform",
     313             :             CPLSPrintf("%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
     314         471 :                        m_gt[0], m_gt[1], m_gt[2], m_gt[3], m_gt[4], m_gt[5]));
     315             :     }
     316             : 
     317             :     /* -------------------------------------------------------------------- */
     318             :     /*      Metadata                                                        */
     319             :     /* -------------------------------------------------------------------- */
     320         538 :     CPLXMLNode *psMD = oMDMD.Serialize();
     321         538 :     if (psMD != nullptr)
     322             :     {
     323         301 :         CPLAddXMLChild(psDSTree, psMD);
     324             :     }
     325             : 
     326             :     /* -------------------------------------------------------------------- */
     327             :     /*      GCPs                                                            */
     328             :     /* -------------------------------------------------------------------- */
     329         538 :     if (!m_asGCPs.empty())
     330             :     {
     331           4 :         GDALSerializeGCPListToXML(psDSTree, m_asGCPs, m_poGCP_SRS.get());
     332             :     }
     333             : 
     334             :     /* -------------------------------------------------------------------- */
     335             :     /*      Serialize bands.                                                */
     336             :     /* -------------------------------------------------------------------- */
     337         538 :     CPLXMLNode *psLastChild = psDSTree->psChild;
     338        2438 :     for (; psLastChild != nullptr && psLastChild->psNext;
     339        1900 :          psLastChild = psLastChild->psNext)
     340             :     {
     341             :     }
     342         538 :     CPLAssert(psLastChild);  // we have at least rasterXSize
     343         538 :     bool bHasWarnedAboutRAMUsage = false;
     344         538 :     size_t nAccRAMUsage = 0;
     345        1398 :     for (int iBand = 0; iBand < nBands; iBand++)
     346             :     {
     347             :         CPLXMLNode *psBandTree =
     348         860 :             static_cast<VRTRasterBand *>(papoBands[iBand])
     349        1720 :                 ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
     350         860 :                                  nAccRAMUsage);
     351             : 
     352         860 :         if (psBandTree != nullptr)
     353             :         {
     354         860 :             psLastChild->psNext = psBandTree;
     355         860 :             psLastChild = psBandTree;
     356             :         }
     357             :     }
     358             : 
     359             :     /* -------------------------------------------------------------------- */
     360             :     /*      Serialize dataset mask band.                                    */
     361             :     /* -------------------------------------------------------------------- */
     362         538 :     if (m_poMaskBand)
     363             :     {
     364          19 :         CPLXMLNode *psBandTree = m_poMaskBand->SerializeToXML(
     365          19 :             pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
     366             : 
     367          19 :         if (psBandTree != nullptr)
     368             :         {
     369             :             CPLXMLNode *psMaskBandElement =
     370          19 :                 CPLCreateXMLNode(psDSTree, CXT_Element, "MaskBand");
     371          19 :             CPLAddXMLChild(psMaskBandElement, psBandTree);
     372             :         }
     373             :     }
     374             : 
     375             :     /* -------------------------------------------------------------------- */
     376             :     /*      Overview factors.                                               */
     377             :     /* -------------------------------------------------------------------- */
     378         538 :     if (!m_anOverviewFactors.empty())
     379             :     {
     380          10 :         CPLString osOverviewList;
     381          16 :         for (int nOvFactor : m_anOverviewFactors)
     382             :         {
     383          11 :             if (!osOverviewList.empty())
     384           6 :                 osOverviewList += " ";
     385          11 :             osOverviewList += CPLSPrintf("%d", nOvFactor);
     386             :         }
     387           5 :         CPLXMLNode *psOverviewList = CPLCreateXMLElementAndValue(
     388             :             psDSTree, "OverviewList", osOverviewList);
     389           5 :         if (!m_osOverviewResampling.empty())
     390             :         {
     391           5 :             CPLAddXMLAttributeAndValue(psOverviewList, "resampling",
     392             :                                        m_osOverviewResampling);
     393             :         }
     394             :     }
     395             : 
     396         538 :     return psDSTree;
     397             : }
     398             : 
     399             : /*! @endcond */
     400             : /************************************************************************/
     401             : /*                          VRTSerializeToXML()                         */
     402             : /************************************************************************/
     403             : 
     404             : /**
     405             :  * @see VRTDataset::SerializeToXML()
     406             :  */
     407             : 
     408           0 : CPLXMLNode *CPL_STDCALL VRTSerializeToXML(VRTDatasetH hDataset,
     409             :                                           const char *pszVRTPath)
     410             : {
     411           0 :     VALIDATE_POINTER1(hDataset, "VRTSerializeToXML", nullptr);
     412             : 
     413           0 :     return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
     414           0 :         ->SerializeToXML(pszVRTPath);
     415             : }
     416             : 
     417             : /*! @cond Doxygen_Suppress */
     418             : 
     419             : /************************************************************************/
     420             : /*                        IsRawRasterBandEnabled()                      */
     421             : /************************************************************************/
     422             : 
     423             : /** Return whether VRTRawRasterBand support is enabled */
     424             : 
     425             : /* static */
     426          78 : bool VRTDataset::IsRawRasterBandEnabled()
     427             : {
     428             : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
     429          78 :     if (CPLTestBool(CPLGetConfigOption("GDAL_VRT_ENABLE_RAWRASTERBAND", "YES")))
     430             :     {
     431          76 :         return true;
     432             :     }
     433             :     else
     434             :     {
     435           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     436             :                  "VRTRawRasterBand support has been disabled at run-time.");
     437             :     }
     438           2 :     return false;
     439             : #else
     440             :     CPLError(CE_Failure, CPLE_NotSupported,
     441             :              "VRTRawRasterBand is disabled in this GDAL build");
     442             :     return false;
     443             : #endif
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                             InitBand()                               */
     448             : /************************************************************************/
     449             : 
     450             : std::unique_ptr<VRTRasterBand>
     451        3159 : VRTDataset::InitBand(const char *pszSubclass, int nBand,
     452             :                      bool bAllowPansharpenedOrProcessed)
     453             : {
     454        3159 :     if (auto poProcessedDS = dynamic_cast<VRTProcessedDataset *>(this))
     455             :     {
     456          36 :         if (bAllowPansharpenedOrProcessed &&
     457          36 :             EQUAL(pszSubclass, "VRTProcessedRasterBand"))
     458             :         {
     459          72 :             return std::make_unique<VRTProcessedRasterBand>(poProcessedDS,
     460          36 :                                                             nBand);
     461             :         }
     462             :     }
     463        3123 :     else if (EQUAL(pszSubclass, "VRTSourcedRasterBand"))
     464        1107 :         return std::make_unique<VRTSourcedRasterBand>(this, nBand);
     465        2016 :     else if (EQUAL(pszSubclass, "VRTDerivedRasterBand"))
     466        1454 :         return std::make_unique<VRTDerivedRasterBand>(this, nBand);
     467         562 :     else if (EQUAL(pszSubclass, "VRTRawRasterBand"))
     468             :     {
     469             : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
     470          33 :         if (!VRTDataset::IsRawRasterBandEnabled())
     471             :         {
     472           1 :             return nullptr;
     473             :         }
     474          32 :         return std::make_unique<VRTRawRasterBand>(this, nBand);
     475             : #else
     476             :         CPLError(CE_Failure, CPLE_NotSupported,
     477             :                  "VRTDataset::InitBand(): cannot instantiate VRTRawRasterBand, "
     478             :                  "because disabled in this GDAL build");
     479             :         return nullptr;
     480             : #endif
     481             :     }
     482         980 :     else if (EQUAL(pszSubclass, "VRTWarpedRasterBand") &&
     483         451 :              dynamic_cast<VRTWarpedDataset *>(this) != nullptr)
     484             :     {
     485         451 :         return std::make_unique<VRTWarpedRasterBand>(this, nBand);
     486             :     }
     487          78 :     else if (bAllowPansharpenedOrProcessed &&
     488         155 :              EQUAL(pszSubclass, "VRTPansharpenedRasterBand") &&
     489          77 :              dynamic_cast<VRTPansharpenedDataset *>(this) != nullptr)
     490             :     {
     491          77 :         return std::make_unique<VRTPansharpenedRasterBand>(this, nBand);
     492             :     }
     493             : 
     494           1 :     CPLError(CE_Failure, CPLE_AppDefined,
     495             :              "VRTRasterBand of unrecognized subclass '%s'.", pszSubclass);
     496           1 :     return nullptr;
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                              XMLInit()                               */
     501             : /************************************************************************/
     502             : 
     503        2774 : CPLErr VRTDataset::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPathIn)
     504             : 
     505             : {
     506        2774 :     if (pszVRTPathIn != nullptr)
     507        1189 :         m_pszVRTPath = CPLStrdup(pszVRTPathIn);
     508             : 
     509             :     /* -------------------------------------------------------------------- */
     510             :     /*      Check for an SRS node.                                          */
     511             :     /* -------------------------------------------------------------------- */
     512        2774 :     const CPLXMLNode *psSRSNode = CPLGetXMLNode(psTree, "SRS");
     513        2774 :     if (psSRSNode)
     514             :     {
     515         610 :         m_poSRS.reset(new OGRSpatialReference());
     516         610 :         m_poSRS->SetFromUserInput(
     517             :             CPLGetXMLValue(psSRSNode, nullptr, ""),
     518             :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     519             :         const char *pszMapping =
     520         610 :             CPLGetXMLValue(psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
     521         610 :         if (pszMapping)
     522             :         {
     523             :             char **papszTokens =
     524         374 :                 CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
     525         748 :             std::vector<int> anMapping;
     526        1125 :             for (int i = 0; papszTokens && papszTokens[i]; i++)
     527             :             {
     528         751 :                 anMapping.push_back(atoi(papszTokens[i]));
     529             :             }
     530         374 :             CSLDestroy(papszTokens);
     531         374 :             m_poSRS->SetDataAxisToSRSAxisMapping(anMapping);
     532             :         }
     533             :         else
     534             :         {
     535         236 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     536             :         }
     537             : 
     538             :         const char *pszCoordinateEpoch =
     539         610 :             CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
     540         610 :         if (pszCoordinateEpoch)
     541           1 :             m_poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
     542             :     }
     543             : 
     544             :     /* -------------------------------------------------------------------- */
     545             :     /*      Check for a GeoTransform node.                                  */
     546             :     /* -------------------------------------------------------------------- */
     547        2774 :     const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
     548        2774 :     if (strlen(pszGT) > 0)
     549             :     {
     550             :         const CPLStringList aosTokens(
     551        1398 :             CSLTokenizeStringComplex(pszGT, ",", FALSE, FALSE));
     552         699 :         if (aosTokens.size() != 6)
     553             :         {
     554           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     555             :                      "GeoTransform node does not have expected six values.");
     556             :         }
     557             :         else
     558             :         {
     559        4893 :             for (int iTA = 0; iTA < 6; iTA++)
     560        4194 :                 m_gt[iTA] = CPLAtof(aosTokens[iTA]);
     561         699 :             m_bGeoTransformSet = TRUE;
     562             :         }
     563             :     }
     564             : 
     565             :     /* -------------------------------------------------------------------- */
     566             :     /*      Check for GCPs.                                                 */
     567             :     /* -------------------------------------------------------------------- */
     568        2774 :     if (const CPLXMLNode *psGCPList = CPLGetXMLNode(psTree, "GCPList"))
     569             :     {
     570          48 :         OGRSpatialReference *poSRS = nullptr;
     571          48 :         GDALDeserializeGCPListFromXML(psGCPList, m_asGCPs, &poSRS);
     572          48 :         m_poGCP_SRS.reset(poSRS);
     573             :     }
     574             : 
     575             :     /* -------------------------------------------------------------------- */
     576             :     /*      Apply any dataset level metadata.                               */
     577             :     /* -------------------------------------------------------------------- */
     578        2774 :     oMDMD.XMLInit(psTree, TRUE);
     579             : 
     580             :     /* -------------------------------------------------------------------- */
     581             :     /*      Create dataset mask band.                                       */
     582             :     /* -------------------------------------------------------------------- */
     583             : 
     584             :     /* Parse dataset mask band first */
     585        2774 :     const CPLXMLNode *psMaskBandNode = CPLGetXMLNode(psTree, "MaskBand");
     586             : 
     587        2774 :     const CPLXMLNode *psChild = nullptr;
     588        2774 :     if (psMaskBandNode)
     589          23 :         psChild = psMaskBandNode->psChild;
     590             :     else
     591        2751 :         psChild = nullptr;
     592             : 
     593        2774 :     for (; psChild != nullptr; psChild = psChild->psNext)
     594             :     {
     595          23 :         if (psChild->eType == CXT_Element &&
     596          23 :             EQUAL(psChild->pszValue, "VRTRasterBand"))
     597             :         {
     598             :             const char *pszSubclass =
     599          23 :                 CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
     600             : 
     601          23 :             auto poBand = InitBand(pszSubclass, 0, false);
     602          46 :             if (poBand != nullptr &&
     603          23 :                 poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
     604             :                     CE_None)
     605             :             {
     606          23 :                 SetMaskBand(std::move(poBand));
     607          23 :                 break;
     608             :             }
     609             :             else
     610             :             {
     611           0 :                 return CE_Failure;
     612             :             }
     613             :         }
     614             :     }
     615             : 
     616             :     /* -------------------------------------------------------------------- */
     617             :     /*      Create band information objects.                                */
     618             :     /* -------------------------------------------------------------------- */
     619        2774 :     int l_nBands = 0;
     620       13863 :     for (psChild = psTree->psChild; psChild != nullptr;
     621       11089 :          psChild = psChild->psNext)
     622             :     {
     623       11129 :         if (psChild->eType == CXT_Element &&
     624        6187 :             EQUAL(psChild->pszValue, "VRTRasterBand"))
     625             :         {
     626             :             const char *pszSubclass =
     627        3137 :                 CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
     628        3137 :             if (dynamic_cast<VRTProcessedDataset *>(this) &&
     629          36 :                 !EQUAL(pszSubclass, "VRTProcessedRasterBand"))
     630             :             {
     631           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
     632             :                          "Only subClass=VRTProcessedRasterBand supported");
     633          40 :                 return CE_Failure;
     634             :             }
     635             : 
     636        4591 :             if (CPLGetXMLNode(psChild, "PixelFunctionType") != nullptr &&
     637        1454 :                 !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
     638             :             {
     639           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     640             :                          "Pixel functions may only be used with "
     641             :                          "subClass=VRTDerivedRasterBand");
     642           1 :                 return CE_Failure;
     643             :             }
     644             : 
     645        3136 :             auto poBand = InitBand(pszSubclass, l_nBands + 1, true);
     646        6270 :             if (poBand != nullptr &&
     647        3134 :                 poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
     648             :                     CE_None)
     649             :             {
     650        3097 :                 l_nBands++;
     651        3097 :                 SetBand(l_nBands, std::move(poBand));
     652             :             }
     653             :             else
     654             :             {
     655          39 :                 return CE_Failure;
     656             :             }
     657             :         }
     658             :     }
     659             : 
     660        2734 :     if (const CPLXMLNode *psGroup = CPLGetXMLNode(psTree, "Group"))
     661             :     {
     662         233 :         const char *pszName = CPLGetXMLValue(psGroup, "name", nullptr);
     663         233 :         if (pszName == nullptr || !EQUAL(pszName, "/"))
     664             :         {
     665           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     666             :                      "Missing name or not equal to '/'");
     667           2 :             return CE_Failure;
     668             :         }
     669             : 
     670         231 :         m_poRootGroup = VRTGroup::Create(std::string(), "/");
     671         231 :         m_poRootGroup->SetIsRootGroup();
     672         231 :         if (!m_poRootGroup->XMLInit(m_poRootGroup, m_poRootGroup, psGroup,
     673             :                                     pszVRTPathIn))
     674             :         {
     675          21 :             return CE_Failure;
     676             :         }
     677             :     }
     678             : 
     679             :     /* -------------------------------------------------------------------- */
     680             :     /*      Create virtual overviews.                                       */
     681             :     /* -------------------------------------------------------------------- */
     682        2711 :     const char *pszSubClass = CPLGetXMLValue(psTree, "subClass", "");
     683        2711 :     if (EQUAL(pszSubClass, ""))
     684             :     {
     685             :         m_aosOverviewList =
     686        2343 :             CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", ""));
     687             :         m_osOverviewResampling =
     688        2343 :             CPLGetXMLValue(psTree, "OverviewList.resampling", "");
     689             :     }
     690             : 
     691        2711 :     return CE_None;
     692             : }
     693             : 
     694             : /************************************************************************/
     695             : /*                            GetGCPCount()                             */
     696             : /************************************************************************/
     697             : 
     698        2188 : int VRTDataset::GetGCPCount()
     699             : 
     700             : {
     701        2188 :     return static_cast<int>(m_asGCPs.size());
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                               GetGCPs()                              */
     706             : /************************************************************************/
     707             : 
     708          76 : const GDAL_GCP *VRTDataset::GetGCPs()
     709             : 
     710             : {
     711          76 :     return gdal::GCP::c_ptr(m_asGCPs);
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                              SetGCPs()                               */
     716             : /************************************************************************/
     717             : 
     718          34 : CPLErr VRTDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
     719             :                            const OGRSpatialReference *poGCP_SRS)
     720             : 
     721             : {
     722          34 :     m_poGCP_SRS.reset(poGCP_SRS ? poGCP_SRS->Clone() : nullptr);
     723          34 :     m_asGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
     724             : 
     725          34 :     SetNeedsFlush();
     726             : 
     727          34 :     return CE_None;
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                           SetSpatialRef()                            */
     732             : /************************************************************************/
     733             : 
     734        2652 : CPLErr VRTDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     735             : 
     736             : {
     737        2652 :     m_poSRS.reset(poSRS ? poSRS->Clone() : nullptr);
     738             : 
     739        2652 :     SetNeedsFlush();
     740             : 
     741        2652 :     return CE_None;
     742             : }
     743             : 
     744             : /************************************************************************/
     745             : /*                          SetGeoTransform()                           */
     746             : /************************************************************************/
     747             : 
     748        2814 : CPLErr VRTDataset::SetGeoTransform(const GDALGeoTransform &gt)
     749             : 
     750             : {
     751        2814 :     m_gt = gt;
     752        2814 :     m_bGeoTransformSet = TRUE;
     753             : 
     754        2814 :     SetNeedsFlush();
     755             : 
     756        2814 :     return CE_None;
     757             : }
     758             : 
     759             : /************************************************************************/
     760             : /*                          GetGeoTransform()                           */
     761             : /************************************************************************/
     762             : 
     763        8299 : CPLErr VRTDataset::GetGeoTransform(GDALGeoTransform &gt) const
     764             : 
     765             : {
     766        8299 :     gt = m_gt;
     767             : 
     768        8299 :     return m_bGeoTransformSet ? CE_None : CE_Failure;
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                            SetMetadata()                             */
     773             : /************************************************************************/
     774             : 
     775        2138 : CPLErr VRTDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
     776             : 
     777             : {
     778        2138 :     SetNeedsFlush();
     779             : 
     780        2138 :     return GDALDataset::SetMetadata(papszMetadata, pszDomain);
     781             : }
     782             : 
     783             : /************************************************************************/
     784             : /*                          SetMetadataItem()                           */
     785             : /************************************************************************/
     786             : 
     787        2241 : CPLErr VRTDataset::SetMetadataItem(const char *pszName, const char *pszValue,
     788             :                                    const char *pszDomain)
     789             : 
     790             : {
     791        2241 :     SetNeedsFlush();
     792             : 
     793        2241 :     return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
     794             : }
     795             : 
     796             : /************************************************************************/
     797             : /*                              Identify()                              */
     798             : /************************************************************************/
     799             : 
     800       92648 : int VRTDataset::Identify(GDALOpenInfo *poOpenInfo)
     801             : 
     802             : {
     803       92648 :     if (poOpenInfo->nHeaderBytes > 20 &&
     804       32672 :         strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     805             :                "<VRTDataset") != nullptr)
     806        2164 :         return TRUE;
     807             : 
     808       90484 :     if (strstr(poOpenInfo->pszFilename, "<VRTDataset") != nullptr)
     809        3195 :         return TRUE;
     810             : 
     811       87289 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
     812         150 :         return TRUE;
     813             : 
     814       87139 :     return FALSE;
     815             : }
     816             : 
     817             : /************************************************************************/
     818             : /*                                Open()                                */
     819             : /************************************************************************/
     820             : 
     821        2740 : GDALDataset *VRTDataset::Open(GDALOpenInfo *poOpenInfo)
     822             : 
     823             : {
     824             :     /* -------------------------------------------------------------------- */
     825             :     /*      Does this appear to be a virtual dataset definition XML         */
     826             :     /*      file?                                                           */
     827             :     /* -------------------------------------------------------------------- */
     828        2740 :     if (!Identify(poOpenInfo))
     829           0 :         return nullptr;
     830             : 
     831        2740 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
     832          75 :         return OpenVRTProtocol(poOpenInfo->pszFilename);
     833             : 
     834             :     /* -------------------------------------------------------------------- */
     835             :     /*      Try to read the whole file into memory.                         */
     836             :     /* -------------------------------------------------------------------- */
     837        2665 :     char *pszXML = nullptr;
     838        2665 :     VSILFILE *fp = poOpenInfo->fpL;
     839             : 
     840        2665 :     char *pszVRTPath = nullptr;
     841        2665 :     if (fp != nullptr)
     842             :     {
     843        1068 :         poOpenInfo->fpL = nullptr;
     844             : 
     845        1068 :         GByte *pabyOut = nullptr;
     846        1068 :         if (!VSIIngestFile(fp, poOpenInfo->pszFilename, &pabyOut, nullptr,
     847             :                            INT_MAX - 1))
     848             :         {
     849           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     850           0 :             return nullptr;
     851             :         }
     852        1068 :         pszXML = reinterpret_cast<char *>(pabyOut);
     853             : 
     854        1068 :         char *pszCurDir = CPLGetCurrentDir();
     855             :         std::string currentVrtFilename =
     856        1068 :             CPLProjectRelativeFilenameSafe(pszCurDir, poOpenInfo->pszFilename);
     857        1068 :         CPLString osInitialCurrentVrtFilename(currentVrtFilename);
     858        1068 :         CPLFree(pszCurDir);
     859             : 
     860             : #if defined(HAVE_READLINK) && defined(HAVE_LSTAT)
     861             :         char filenameBuffer[2048];
     862             : 
     863             :         while (true)
     864             :         {
     865             :             VSIStatBuf statBuffer;
     866        1072 :             int lstatCode = lstat(currentVrtFilename.c_str(), &statBuffer);
     867        1072 :             if (lstatCode == -1)
     868             :             {
     869         295 :                 if (errno == ENOENT)
     870             :                 {
     871             :                     // File could be a virtual file, let later checks handle it.
     872         295 :                     break;
     873             :                 }
     874             :                 else
     875             :                 {
     876           0 :                     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     877           0 :                     CPLFree(pszXML);
     878           0 :                     CPLError(CE_Failure, CPLE_FileIO, "Failed to lstat %s: %s",
     879           0 :                              currentVrtFilename.c_str(), VSIStrerror(errno));
     880           0 :                     return nullptr;
     881             :                 }
     882             :             }
     883             : 
     884         777 :             if (!VSI_ISLNK(statBuffer.st_mode))
     885             :             {
     886         773 :                 break;
     887             :             }
     888             : 
     889             :             const int bufferSize = static_cast<int>(
     890           4 :                 readlink(currentVrtFilename.c_str(), filenameBuffer,
     891           4 :                          sizeof(filenameBuffer)));
     892           4 :             if (bufferSize != -1)
     893             :             {
     894           4 :                 filenameBuffer[std::min(
     895           4 :                     bufferSize, static_cast<int>(sizeof(filenameBuffer)) - 1)] =
     896             :                     0;
     897             :                 // The filename in filenameBuffer might be a relative path
     898             :                 // from the linkfile resolve it before looping
     899           8 :                 currentVrtFilename = CPLProjectRelativeFilenameSafe(
     900           8 :                     CPLGetDirnameSafe(currentVrtFilename.c_str()).c_str(),
     901           4 :                     filenameBuffer);
     902             :             }
     903             :             else
     904             :             {
     905           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     906           0 :                 CPLFree(pszXML);
     907           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     908             :                          "Failed to read filename from symlink %s: %s",
     909           0 :                          currentVrtFilename.c_str(), VSIStrerror(errno));
     910           0 :                 return nullptr;
     911             :             }
     912           4 :         }
     913             : #endif  // HAVE_READLINK && HAVE_LSTAT
     914             : 
     915        1068 :         if (osInitialCurrentVrtFilename == currentVrtFilename)
     916             :             pszVRTPath =
     917        1065 :                 CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
     918             :         else
     919             :             pszVRTPath =
     920           3 :                 CPLStrdup(CPLGetPathSafe(currentVrtFilename.c_str()).c_str());
     921             : 
     922        1068 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     923             :     }
     924             :     /* -------------------------------------------------------------------- */
     925             :     /*      Or use the filename as the XML input.                           */
     926             :     /* -------------------------------------------------------------------- */
     927             :     else
     928             :     {
     929        1597 :         pszXML = CPLStrdup(poOpenInfo->pszFilename);
     930             :     }
     931             : 
     932        2665 :     if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH") != nullptr)
     933             :     {
     934          10 :         CPLFree(pszVRTPath);
     935          10 :         pszVRTPath = CPLStrdup(
     936          10 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH"));
     937             :     }
     938             : 
     939             :     /* -------------------------------------------------------------------- */
     940             :     /*      Turn the XML representation into a VRTDataset.                  */
     941             :     /* -------------------------------------------------------------------- */
     942        5330 :     auto poDS = OpenXML(pszXML, pszVRTPath, poOpenInfo->eAccess);
     943             : 
     944        2665 :     if (poDS != nullptr)
     945        2537 :         poDS->m_bNeedsFlush = false;
     946             : 
     947        2665 :     if (poDS != nullptr)
     948             :     {
     949        2537 :         if (poDS->GetRasterCount() == 0 &&
     950        2538 :             (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) == 0 &&
     951           1 :             strstr(pszXML, "VRTPansharpenedDataset") == nullptr)
     952             :         {
     953           0 :             poDS.reset();
     954             :         }
     955        5074 :         else if (poDS->GetRootGroup() == nullptr &&
     956        5074 :                  (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     957           0 :                  (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
     958             :         {
     959           0 :             poDS.reset();
     960             :         }
     961             :     }
     962             : 
     963        2665 :     CPLFree(pszXML);
     964        2665 :     CPLFree(pszVRTPath);
     965             : 
     966             :     /* -------------------------------------------------------------------- */
     967             :     /*      Initialize info for later overview discovery.                   */
     968             :     /* -------------------------------------------------------------------- */
     969             : 
     970        2665 :     if (poDS != nullptr)
     971             :     {
     972        2537 :         if (fp != nullptr)
     973             :         {
     974        1055 :             poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     975        1055 :             if (poOpenInfo->AreSiblingFilesLoaded())
     976           2 :                 poDS->oOvManager.TransferSiblingFiles(
     977             :                     poOpenInfo->StealSiblingFiles());
     978             :         }
     979             : 
     980             :         // Creating virtual overviews, but only if there is no higher priority
     981             :         // overview source, ie. a Overview element at VRT band level,
     982             :         // or external .vrt.ovr
     983        2537 :         if (!poDS->m_aosOverviewList.empty())
     984             :         {
     985           7 :             if (poDS->nBands > 0)
     986             :             {
     987           7 :                 auto poBand = dynamic_cast<VRTRasterBand *>(poDS->papoBands[0]);
     988           7 :                 if (poBand && !poBand->m_aoOverviewInfos.empty())
     989             :                 {
     990           0 :                     poDS->m_aosOverviewList.Clear();
     991           0 :                     CPLDebug("VRT",
     992             :                              "Ignoring virtual overviews of OverviewList "
     993             :                              "because Overview element is present on VRT band");
     994             :                 }
     995          14 :                 else if (poBand &&
     996           7 :                          poBand->GDALRasterBand::GetOverviewCount() > 0)
     997             :                 {
     998           1 :                     poDS->m_aosOverviewList.Clear();
     999           1 :                     CPLDebug("VRT",
    1000             :                              "Ignoring virtual overviews of OverviewList "
    1001             :                              "because external .vrt.ovr is available");
    1002             :                 }
    1003             :             }
    1004          19 :             for (int iOverview = 0; iOverview < poDS->m_aosOverviewList.size();
    1005             :                  iOverview++)
    1006             :             {
    1007          12 :                 const int nOvFactor = atoi(poDS->m_aosOverviewList[iOverview]);
    1008          12 :                 if (nOvFactor <= 1)
    1009             :                 {
    1010           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1011             :                              "Invalid overview factor");
    1012           0 :                     return nullptr;
    1013             :                 }
    1014             : 
    1015          36 :                 poDS->AddVirtualOverview(
    1016          12 :                     nOvFactor, poDS->m_osOverviewResampling.empty()
    1017             :                                    ? "nearest"
    1018          12 :                                    : poDS->m_osOverviewResampling.c_str());
    1019             :             }
    1020           7 :             poDS->m_aosOverviewList.Clear();
    1021             :         }
    1022             : 
    1023        2615 :         if (poDS->eAccess == GA_Update && poDS->m_poRootGroup &&
    1024          78 :             !STARTS_WITH_CI(poOpenInfo->pszFilename, "<VRT"))
    1025             :         {
    1026          78 :             poDS->m_poRootGroup->SetFilename(poOpenInfo->pszFilename);
    1027             :         }
    1028             :     }
    1029             : 
    1030        2665 :     return poDS.release();
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                         OpenVRTProtocol()                            */
    1035             : /*                                                                      */
    1036             : /*      Create an open VRTDataset from a vrt:// string.                 */
    1037             : /************************************************************************/
    1038             : 
    1039          75 : GDALDataset *VRTDataset::OpenVRTProtocol(const char *pszSpec)
    1040             : 
    1041             : {
    1042          75 :     CPLAssert(STARTS_WITH_CI(pszSpec, VRT_PROTOCOL_PREFIX));
    1043         150 :     CPLString osFilename(pszSpec + strlen(VRT_PROTOCOL_PREFIX));
    1044          75 :     const auto nPosQuotationMark = osFilename.find('?');
    1045         150 :     CPLString osQueryString;
    1046          75 :     if (nPosQuotationMark != std::string::npos)
    1047             :     {
    1048          67 :         osQueryString = osFilename.substr(nPosQuotationMark + 1);
    1049          67 :         osFilename.resize(nPosQuotationMark);
    1050             :     }
    1051             : 
    1052             :     // Parse query string, get args required for initial Open()
    1053         150 :     const CPLStringList aosTokens(CSLTokenizeString2(osQueryString, "&", 0));
    1054         150 :     CPLStringList aosAllowedDrivers;
    1055         150 :     CPLStringList aosOpenOptions;
    1056             : 
    1057         170 :     for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
    1058         241 :              aosTokens, /* bReturnNullKeyIfNotNameValue = */ true))
    1059             :     {
    1060          87 :         if (!pszKey)
    1061             :         {
    1062           2 :             CPLError(CE_Failure, CPLE_NotSupported,
    1063             :                      "Invalid option specification: %s\n"
    1064             :                      "must be in the form 'key=value'",
    1065             :                      pszValue);
    1066           4 :             return nullptr;
    1067             :         }
    1068          85 :         else if (EQUAL(pszKey, "if"))
    1069             :         {
    1070           4 :             if (!aosAllowedDrivers.empty())
    1071             :             {
    1072           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1073             :                          "'if' option should be specified once, use commas "
    1074             :                          "to input multiple values.");
    1075           1 :                 return nullptr;
    1076             :             }
    1077           3 :             aosAllowedDrivers = CSLTokenizeString2(pszValue, ",", 0);
    1078             :         }
    1079          81 :         else if (EQUAL(pszKey, "oo"))
    1080             :         {
    1081           3 :             if (!aosOpenOptions.empty())
    1082             :             {
    1083           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1084             :                          "'oo' option should be specified once, use commas "
    1085             :                          "to input multiple values.");
    1086           1 :                 return nullptr;
    1087             :             }
    1088           2 :             aosOpenOptions = CSLTokenizeString2(pszValue, ",", 0);
    1089             :         }
    1090             :     }
    1091             : 
    1092             :     // We don't open in GDAL_OF_SHARED mode to avoid issues when we open a
    1093             :     // http://.jp2 file with the JP2OpenJPEG driver through the HTTP driver,
    1094             :     // which returns a /vsimem/ file
    1095             :     auto poSrcDS = std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
    1096             :         GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    1097          71 :                           aosAllowedDrivers.List(), aosOpenOptions.List(),
    1098         142 :                           nullptr));
    1099          71 :     if (poSrcDS == nullptr)
    1100             :     {
    1101           4 :         return nullptr;
    1102             :     }
    1103             : 
    1104          67 :     bool bFound_transpose = false;
    1105         146 :     for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
    1106             :     {
    1107          79 :         if (EQUAL(pszKey, "transpose"))
    1108             :         {
    1109           4 :             bFound_transpose = true;
    1110             :             const CPLStringList aosTransposeTokens(
    1111           4 :                 CSLTokenizeString2(pszValue, ":", 0));
    1112           4 :             if (aosTransposeTokens.size() != 2)
    1113             :             {
    1114           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1115             :                          "Invalid transpose option: %s", pszValue);
    1116           0 :                 return nullptr;
    1117             :             }
    1118             :             const CPLStringList aosTransposeIndex(
    1119           4 :                 CSLTokenizeString2(aosTransposeTokens[1], ",", 0));
    1120             :             // fail if not two values
    1121           4 :             if (aosTransposeIndex.size() != 2)
    1122             :             {
    1123           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1124             :                          "Invalid transpose option: %s", pszValue);
    1125           0 :                 return nullptr;
    1126             :             }
    1127           4 :             int index_x = atoi(aosTransposeIndex[0]);
    1128           4 :             int index_y = atoi(aosTransposeIndex[1]);
    1129             : 
    1130             :             auto poMDimDS = std::unique_ptr<GDALDataset>(
    1131           4 :                 GDALDataset::Open(osFilename, GDAL_OF_MULTIDIM_RASTER));
    1132           4 :             if (!poMDimDS)
    1133           0 :                 return nullptr;
    1134           4 :             auto poMdimGroup = poMDimDS->GetRootGroup();
    1135           4 :             if (!poMdimGroup)
    1136             :             {
    1137           0 :                 return nullptr;
    1138             :             }
    1139             :             auto poArray =
    1140           8 :                 poMdimGroup->OpenMDArrayFromFullname(aosTransposeTokens[0]);
    1141           4 :             if (!poArray)
    1142             :             {
    1143           0 :                 return nullptr;
    1144             :             }
    1145             : 
    1146           4 :             auto poClassicDS = poArray->AsClassicDataset(index_x, index_y);
    1147             : 
    1148           4 :             if (!poClassicDS)
    1149           0 :                 return nullptr;
    1150             :             poSrcDS =
    1151           8 :                 std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
    1152           4 :                     poClassicDS);
    1153             :         }
    1154             :     }
    1155             :     // scan for sd_name/sd in tokens, close the source dataset and reopen if found/valid
    1156          67 :     bool bFound_subdataset = false;
    1157         139 :     for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
    1158             :     {
    1159          77 :         if (EQUAL(pszKey, "sd_name"))
    1160             :         {
    1161           5 :             if (bFound_transpose)
    1162             :             {
    1163           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1164             :                          "'sd_name' is mutually exclusive with option "
    1165             :                          "'transpose'");
    1166           5 :                 return nullptr;
    1167             :             }
    1168           4 :             if (bFound_subdataset)
    1169             :             {
    1170           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1171             :                          "'sd_name' is mutually exclusive with option "
    1172             :                          "'sd'");
    1173           1 :                 return nullptr;
    1174             :             }
    1175           3 :             char **papszSubdatasets = poSrcDS->GetMetadata("SUBDATASETS");
    1176           3 :             int nSubdatasets = CSLCount(papszSubdatasets);
    1177             : 
    1178           3 :             if (nSubdatasets > 0)
    1179             :             {
    1180           3 :                 bool bFound = false;
    1181          25 :                 for (int j = 0; j < nSubdatasets && papszSubdatasets[j]; j += 2)
    1182             :                 {
    1183          24 :                     const char *pszEqual = strchr(papszSubdatasets[j], '=');
    1184          24 :                     if (!pszEqual)
    1185             :                     {
    1186           0 :                         CPLError(CE_Failure, CPLE_IllegalArg,
    1187             :                                  "'sd_name:' failed to obtain "
    1188             :                                  "subdataset string ");
    1189           0 :                         return nullptr;
    1190             :                     }
    1191          24 :                     const char *pszSubdatasetSource = pszEqual + 1;
    1192             :                     GDALSubdatasetInfoH info =
    1193          24 :                         GDALGetSubdatasetInfo(pszSubdatasetSource);
    1194             :                     char *component =
    1195          24 :                         info ? GDALSubdatasetInfoGetSubdatasetComponent(info)
    1196          24 :                              : nullptr;
    1197             : 
    1198          24 :                     bFound = component && EQUAL(pszValue, component);
    1199          24 :                     bFound_subdataset = true;
    1200          24 :                     CPLFree(component);
    1201          24 :                     GDALDestroySubdatasetInfo(info);
    1202          24 :                     if (bFound)
    1203             :                     {
    1204           2 :                         poSrcDS.reset(GDALDataset::Open(
    1205             :                             pszSubdatasetSource,
    1206             :                             GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    1207           2 :                             aosAllowedDrivers.List(), aosOpenOptions.List(),
    1208             :                             nullptr));
    1209           2 :                         if (poSrcDS == nullptr)
    1210             :                         {
    1211           0 :                             return nullptr;
    1212             :                         }
    1213             : 
    1214           2 :                         break;
    1215             :                     }
    1216             :                 }
    1217             : 
    1218           3 :                 if (!bFound)
    1219             :                 {
    1220           1 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1221             :                              "'sd_name' option should be be a valid "
    1222             :                              "subdataset component name");
    1223           1 :                     return nullptr;
    1224             :                 }
    1225             :             }
    1226             :         }
    1227             : 
    1228          74 :         if (EQUAL(pszKey, "sd"))
    1229             :         {
    1230           4 :             if (bFound_transpose)
    1231             :             {
    1232           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1233             :                          "'sd' is mutually exclusive with option "
    1234             :                          "'transpose'");
    1235           1 :                 return nullptr;
    1236             :             }
    1237           3 :             if (bFound_subdataset)
    1238             :             {
    1239           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1240             :                          "'sd' is mutually exclusive with option "
    1241             :                          "'sd_name'");
    1242           0 :                 return nullptr;
    1243             :             }
    1244           3 :             CSLConstList papszSubdatasets = poSrcDS->GetMetadata("SUBDATASETS");
    1245           3 :             int nSubdatasets = CSLCount(papszSubdatasets);
    1246             : 
    1247           3 :             if (nSubdatasets > 0)
    1248             :             {
    1249           3 :                 int iSubdataset = atoi(pszValue);
    1250           3 :                 if (iSubdataset < 1 || iSubdataset > (nSubdatasets) / 2)
    1251             :                 {
    1252           1 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1253             :                              "'sd' option should indicate a valid "
    1254             :                              "subdataset component number (starting with 1)");
    1255           1 :                     return nullptr;
    1256             :                 }
    1257             :                 const std::string osSubdatasetSource(
    1258           2 :                     strstr(papszSubdatasets[(iSubdataset - 1) * 2], "=") + 1);
    1259           2 :                 if (osSubdatasetSource.empty())
    1260             :                 {
    1261           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1262             :                              "'sd:' failed to obtain subdataset "
    1263             :                              "string ");
    1264           0 :                     return nullptr;
    1265             :                 }
    1266             : 
    1267           2 :                 poSrcDS.reset(GDALDataset::Open(
    1268             :                     osSubdatasetSource.c_str(),
    1269             :                     GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    1270           2 :                     aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr));
    1271           2 :                 if (poSrcDS == nullptr)
    1272             :                 {
    1273           0 :                     return nullptr;
    1274             :                 }
    1275           2 :                 bFound_subdataset = true;
    1276             :             }
    1277             :         }
    1278             :     }
    1279             : 
    1280         124 :     std::vector<int> anBands;
    1281             : 
    1282         124 :     CPLStringList argv;
    1283          62 :     argv.AddString("-of");
    1284          62 :     argv.AddString("VRT");
    1285             : 
    1286         123 :     for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
    1287             :     {
    1288          71 :         if (EQUAL(pszKey, "bands"))
    1289             :         {
    1290           8 :             const CPLStringList aosBands(CSLTokenizeString2(pszValue, ",", 0));
    1291          16 :             for (int j = 0; j < aosBands.size(); j++)
    1292             :             {
    1293          11 :                 if (EQUAL(aosBands[j], "mask"))
    1294             :                 {
    1295           1 :                     anBands.push_back(0);
    1296             :                 }
    1297             :                 else
    1298             :                 {
    1299          10 :                     const int nBand = atoi(aosBands[j]);
    1300          10 :                     if (nBand <= 0 || nBand > poSrcDS->GetRasterCount())
    1301             :                     {
    1302           3 :                         CPLError(CE_Failure, CPLE_IllegalArg,
    1303             :                                  "Invalid band number: %s", aosBands[j]);
    1304           3 :                         return nullptr;
    1305             :                     }
    1306           7 :                     anBands.push_back(nBand);
    1307             :                 }
    1308             :             }
    1309             : 
    1310          13 :             for (const int nBand : anBands)
    1311             :             {
    1312           8 :                 argv.AddString("-b");
    1313           8 :                 argv.AddString(nBand == 0 ? "mask" : CPLSPrintf("%d", nBand));
    1314             :             }
    1315             :         }
    1316             : 
    1317          63 :         else if (EQUAL(pszKey, "a_nodata"))
    1318             :         {
    1319           1 :             argv.AddString("-a_nodata");
    1320           1 :             argv.AddString(pszValue);
    1321             :         }
    1322             : 
    1323          62 :         else if (EQUAL(pszKey, "a_srs"))
    1324             :         {
    1325           1 :             argv.AddString("-a_srs");
    1326           1 :             argv.AddString(pszValue);
    1327             :         }
    1328             : 
    1329          61 :         else if (EQUAL(pszKey, "a_ullr"))
    1330             :         {
    1331             :             // Parse the limits
    1332           1 :             const CPLStringList aosUllr(CSLTokenizeString2(pszValue, ",", 0));
    1333             :             // fail if not four values
    1334           1 :             if (aosUllr.size() != 4)
    1335             :             {
    1336           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1337             :                          "Invalid a_ullr option: %s", pszValue);
    1338           0 :                 return nullptr;
    1339             :             }
    1340             : 
    1341           1 :             argv.AddString("-a_ullr");
    1342           1 :             argv.AddString(aosUllr[0]);
    1343           1 :             argv.AddString(aosUllr[1]);
    1344           1 :             argv.AddString(aosUllr[2]);
    1345           1 :             argv.AddString(aosUllr[3]);
    1346             :         }
    1347             : 
    1348          60 :         else if (EQUAL(pszKey, "ovr"))
    1349             :         {
    1350           1 :             argv.AddString("-ovr");
    1351           1 :             argv.AddString(pszValue);
    1352             :         }
    1353          59 :         else if (EQUAL(pszKey, "expand"))
    1354             :         {
    1355           1 :             argv.AddString("-expand");
    1356           1 :             argv.AddString(pszValue);
    1357             :         }
    1358          58 :         else if (EQUAL(pszKey, "a_scale"))
    1359             :         {
    1360           2 :             argv.AddString("-a_scale");
    1361           2 :             argv.AddString(pszValue);
    1362             :         }
    1363          56 :         else if (EQUAL(pszKey, "a_offset"))
    1364             :         {
    1365           1 :             argv.AddString("-a_offset");
    1366           1 :             argv.AddString(pszValue);
    1367             :         }
    1368          55 :         else if (EQUAL(pszKey, "ot"))
    1369             :         {
    1370           2 :             argv.AddString("-ot");
    1371           2 :             argv.AddString(pszValue);
    1372             :         }
    1373          53 :         else if (EQUAL(pszKey, "gcp"))
    1374             :         {
    1375           6 :             const CPLStringList aosGCP(CSLTokenizeString2(pszValue, ",", 0));
    1376             : 
    1377           6 :             if (aosGCP.size() < 4 || aosGCP.size() > 5)
    1378             :             {
    1379           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1380             :                          "Invalid value for GCP: %s\n  need 4, or 5 "
    1381             :                          "numbers, comma separated: "
    1382             :                          "'gcp=<pixel>,<line>,<easting>,<northing>[,<"
    1383             :                          "elevation>]'",
    1384             :                          pszValue);
    1385           1 :                 return nullptr;
    1386             :             }
    1387           5 :             argv.AddString("-gcp");
    1388          28 :             for (int j = 0; j < aosGCP.size(); j++)
    1389             :             {
    1390          23 :                 argv.AddString(aosGCP[j]);
    1391             :             }
    1392             :         }
    1393          47 :         else if (EQUAL(pszKey, "scale") || STARTS_WITH_CI(pszKey, "scale_"))
    1394             :         {
    1395             :             const CPLStringList aosScaleParams(
    1396           7 :                 CSLTokenizeString2(pszValue, ",", 0));
    1397             : 
    1398           7 :             if (!(aosScaleParams.size() == 2) &&
    1399           7 :                 !(aosScaleParams.size() == 4) && !(aosScaleParams.size() == 1))
    1400             :             {
    1401           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1402             :                          "Invalid value for scale, (or scale_bn): "
    1403             :                          "%s\n  need 'scale=true', or 2 or 4 "
    1404             :                          "numbers, comma separated: "
    1405             :                          "'scale=src_min,src_max[,dst_min,dst_max]' or "
    1406             :                          "'scale_bn=src_min,src_max[,dst_min,dst_max]'",
    1407             :                          pszValue);
    1408           0 :                 return nullptr;
    1409             :             }
    1410             : 
    1411             :             // -scale because scale=true or scale=min,max or scale=min,max,dstmin,dstmax
    1412           7 :             if (aosScaleParams.size() == 1 && CPLTestBool(aosScaleParams[0]))
    1413             :             {
    1414           2 :                 argv.AddString(CPLSPrintf("-%s", pszKey));
    1415             :             }
    1416             :             // add remaining params (length 2 or 4)
    1417           7 :             if (aosScaleParams.size() > 1)
    1418             :             {
    1419           5 :                 argv.AddString(CPLSPrintf("-%s", pszKey));
    1420          21 :                 for (int j = 0; j < aosScaleParams.size(); j++)
    1421             :                 {
    1422          16 :                     argv.AddString(aosScaleParams[j]);
    1423             :                 }
    1424           7 :             }
    1425             :         }
    1426          40 :         else if (EQUAL(pszKey, "exponent") ||
    1427          38 :                  STARTS_WITH_CI(pszKey, "exponent_"))
    1428             :         {
    1429           3 :             argv.AddString(CPLSPrintf("-%s", pszKey));
    1430           3 :             argv.AddString(pszValue);
    1431             :         }
    1432          37 :         else if (EQUAL(pszKey, "outsize"))
    1433             :         {
    1434             :             const CPLStringList aosOutSize(
    1435           4 :                 CSLTokenizeString2(pszValue, ",", 0));
    1436           4 :             if (aosOutSize.size() != 2)
    1437             :             {
    1438           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1439             :                          "Invalid outsize option: %s, must be two"
    1440             :                          "values separated by comma pixel,line or two "
    1441             :                          "fraction values with percent symbol",
    1442             :                          pszValue);
    1443           1 :                 return nullptr;
    1444             :             }
    1445           3 :             argv.AddString("-outsize");
    1446           3 :             argv.AddString(aosOutSize[0]);
    1447           3 :             argv.AddString(aosOutSize[1]);
    1448             :         }
    1449          33 :         else if (EQUAL(pszKey, "projwin"))
    1450             :         {
    1451             :             // Parse the limits
    1452             :             const CPLStringList aosProjWin(
    1453           3 :                 CSLTokenizeString2(pszValue, ",", 0));
    1454             :             // fail if not four values
    1455           3 :             if (aosProjWin.size() != 4)
    1456             :             {
    1457           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1458             :                          "Invalid projwin option: %s", pszValue);
    1459           1 :                 return nullptr;
    1460             :             }
    1461             : 
    1462           2 :             argv.AddString("-projwin");
    1463           2 :             argv.AddString(aosProjWin[0]);
    1464           2 :             argv.AddString(aosProjWin[1]);
    1465           2 :             argv.AddString(aosProjWin[2]);
    1466           2 :             argv.AddString(aosProjWin[3]);
    1467             :         }
    1468          30 :         else if (EQUAL(pszKey, "projwin_srs"))
    1469             :         {
    1470           2 :             argv.AddString("-projwin_srs");
    1471           2 :             argv.AddString(pszValue);
    1472             :         }
    1473          28 :         else if (EQUAL(pszKey, "tr"))
    1474             :         {
    1475             :             const CPLStringList aosTargetResolution(
    1476           3 :                 CSLTokenizeString2(pszValue, ",", 0));
    1477           3 :             if (aosTargetResolution.size() != 2)
    1478             :             {
    1479           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1480             :                          "Invalid tr option: %s, must be two "
    1481             :                          "values separated by comma xres,yres",
    1482             :                          pszValue);
    1483           1 :                 return nullptr;
    1484             :             }
    1485           2 :             argv.AddString("-tr");
    1486           2 :             argv.AddString(aosTargetResolution[0]);
    1487           2 :             argv.AddString(aosTargetResolution[1]);
    1488             :         }
    1489          25 :         else if (EQUAL(pszKey, "r"))
    1490             :         {
    1491           1 :             argv.AddString("-r");
    1492           1 :             argv.AddString(pszValue);
    1493             :         }
    1494             : 
    1495          24 :         else if (EQUAL(pszKey, "srcwin"))
    1496             :         {
    1497             :             // Parse the limits
    1498           6 :             const CPLStringList aosSrcWin(CSLTokenizeString2(pszValue, ",", 0));
    1499             :             // fail if not four values
    1500           6 :             if (aosSrcWin.size() != 4)
    1501             :             {
    1502           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    1503             :                          "Invalid srcwin option: %s, must be four "
    1504             :                          "values separated by comma xoff,yoff,xsize,ysize",
    1505             :                          pszValue);
    1506           1 :                 return nullptr;
    1507             :             }
    1508             : 
    1509           5 :             argv.AddString("-srcwin");
    1510           5 :             argv.AddString(aosSrcWin[0]);
    1511           5 :             argv.AddString(aosSrcWin[1]);
    1512           5 :             argv.AddString(aosSrcWin[2]);
    1513           5 :             argv.AddString(aosSrcWin[3]);
    1514             :         }
    1515             : 
    1516          18 :         else if (EQUAL(pszKey, "a_gt"))
    1517             :         {
    1518             :             // Parse the limits
    1519             :             const CPLStringList aosAGeoTransform(
    1520           2 :                 CSLTokenizeString2(pszValue, ",", 0));
    1521             :             // fail if not six values
    1522           2 :             if (aosAGeoTransform.size() != 6)
    1523             :             {
    1524           1 :                 CPLError(CE_Failure, CPLE_IllegalArg, "Invalid a_gt option: %s",
    1525             :                          pszValue);
    1526           1 :                 return nullptr;
    1527             :             }
    1528             : 
    1529           1 :             argv.AddString("-a_gt");
    1530           1 :             argv.AddString(aosAGeoTransform[0]);
    1531           1 :             argv.AddString(aosAGeoTransform[1]);
    1532           1 :             argv.AddString(aosAGeoTransform[2]);
    1533           1 :             argv.AddString(aosAGeoTransform[3]);
    1534           1 :             argv.AddString(aosAGeoTransform[4]);
    1535           1 :             argv.AddString(aosAGeoTransform[5]);
    1536             :         }
    1537          16 :         else if (EQUAL(pszKey, "oo"))
    1538             :         {
    1539             :             // do nothing, we passed this in earlier
    1540             :         }
    1541          15 :         else if (EQUAL(pszKey, "if"))
    1542             :         {
    1543             :             // do nothing, we passed this in earlier
    1544             :         }
    1545          14 :         else if (EQUAL(pszKey, "sd_name"))
    1546             :         {
    1547             :             // do nothing, we passed this in earlier
    1548             :         }
    1549          12 :         else if (EQUAL(pszKey, "sd"))
    1550             :         {
    1551             :             // do nothing, we passed this in earlier
    1552             :         }
    1553          11 :         else if (EQUAL(pszKey, "transpose"))
    1554             :         {
    1555             :             // do nothing, we passed this in earlier
    1556             :         }
    1557           9 :         else if (EQUAL(pszKey, "unscale"))
    1558             :         {
    1559           1 :             if (CPLTestBool(pszValue))
    1560             :             {
    1561           1 :                 argv.AddString("-unscale");
    1562             :             }
    1563             :         }
    1564           8 :         else if (EQUAL(pszKey, "a_coord_epoch"))
    1565             :         {
    1566           0 :             argv.AddString("-a_coord_epoch");
    1567           0 :             argv.AddString(pszValue);
    1568             :         }
    1569           8 :         else if (EQUAL(pszKey, "nogcp"))
    1570             :         {
    1571           1 :             if (CPLTestBool(pszValue))
    1572             :             {
    1573           1 :                 argv.AddString("-nogcp");
    1574             :             }
    1575             :         }
    1576           7 :         else if (EQUAL(pszKey, "epo"))
    1577             :         {
    1578           3 :             if (CPLTestBool(pszValue))
    1579             :             {
    1580           2 :                 argv.AddString("-epo");
    1581             :             }
    1582             :         }
    1583           4 :         else if (EQUAL(pszKey, "eco"))
    1584             :         {
    1585           3 :             if (CPLTestBool(pszValue))
    1586             :             {
    1587           3 :                 argv.AddString("-eco");
    1588             :             }
    1589             :         }
    1590             : 
    1591             :         else
    1592             :         {
    1593           1 :             CPLError(CE_Failure, CPLE_NotSupported, "Unknown option: %s",
    1594             :                      pszKey);
    1595           1 :             return nullptr;
    1596             :         }
    1597             :     }
    1598             : 
    1599             :     GDALTranslateOptions *psOptions =
    1600          52 :         GDALTranslateOptionsNew(argv.List(), nullptr);
    1601             : 
    1602          52 :     auto hRet = GDALTranslate("", GDALDataset::ToHandle(poSrcDS.get()),
    1603             :                               psOptions, nullptr);
    1604             : 
    1605          52 :     GDALTranslateOptionsFree(psOptions);
    1606             : 
    1607             :     // Situation where we open a http://.jp2 file with the JP2OpenJPEG driver
    1608             :     // through the HTTP driver, which returns a /vsimem/ file
    1609             :     const bool bPatchSourceFilename =
    1610          52 :         (STARTS_WITH(osFilename.c_str(), "http://") ||
    1611          53 :          STARTS_WITH(osFilename.c_str(), "https://")) &&
    1612           1 :         osFilename != poSrcDS->GetDescription();
    1613             : 
    1614          52 :     poSrcDS.reset();
    1615             : 
    1616          52 :     auto poDS = dynamic_cast<VRTDataset *>(GDALDataset::FromHandle(hRet));
    1617          52 :     if (poDS)
    1618             :     {
    1619          49 :         if (bPatchSourceFilename)
    1620             :         {
    1621           2 :             for (int i = 0; i < poDS->nBands; ++i)
    1622             :             {
    1623             :                 auto poBand =
    1624           1 :                     dynamic_cast<VRTSourcedRasterBand *>(poDS->papoBands[i]);
    1625           2 :                 if (poBand && poBand->nSources == 1 &&
    1626           1 :                     poBand->papoSources[0]->IsSimpleSource())
    1627             :                 {
    1628           2 :                     auto poSource = cpl::down_cast<VRTSimpleSource *>(
    1629           1 :                         poBand->papoSources[0]);
    1630           1 :                     poSource->m_bRelativeToVRTOri = 0;
    1631           1 :                     poSource->m_osSourceFileNameOri = osFilename;
    1632             :                 }
    1633             :             }
    1634             :         }
    1635          49 :         poDS->SetDescription(pszSpec);
    1636          49 :         poDS->SetWritable(false);
    1637             :     }
    1638          52 :     return poDS;
    1639             : }
    1640             : 
    1641             : /************************************************************************/
    1642             : /*                              OpenXML()                               */
    1643             : /*                                                                      */
    1644             : /*      Create an open VRTDataset from a supplied XML representation    */
    1645             : /*      of the dataset.                                                 */
    1646             : /************************************************************************/
    1647             : 
    1648        2666 : std::unique_ptr<VRTDataset> VRTDataset::OpenXML(const char *pszXML,
    1649             :                                                 const char *pszVRTPath,
    1650             :                                                 GDALAccess eAccessIn)
    1651             : 
    1652             : {
    1653             :     /* -------------------------------------------------------------------- */
    1654             :     /*      Parse the XML.                                                  */
    1655             :     /* -------------------------------------------------------------------- */
    1656        5332 :     CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
    1657        2666 :     if (psTree == nullptr)
    1658           0 :         return nullptr;
    1659             : 
    1660        2666 :     CPLXMLNode *psRoot = CPLGetXMLNode(psTree.get(), "=VRTDataset");
    1661        2666 :     if (psRoot == nullptr)
    1662             :     {
    1663           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing VRTDataset element.");
    1664           0 :         return nullptr;
    1665             :     }
    1666             : 
    1667        2666 :     const char *pszSubClass = CPLGetXMLValue(psRoot, "subClass", "");
    1668             : 
    1669        2666 :     const bool bIsPansharpened =
    1670        2666 :         strcmp(pszSubClass, "VRTPansharpenedDataset") == 0;
    1671        2666 :     const bool bIsProcessed = strcmp(pszSubClass, "VRTProcessedDataset") == 0;
    1672             : 
    1673        2592 :     if (!bIsPansharpened && !bIsProcessed &&
    1674        7521 :         CPLGetXMLNode(psRoot, "Group") == nullptr &&
    1675        2263 :         (CPLGetXMLNode(psRoot, "rasterXSize") == nullptr ||
    1676        2262 :          CPLGetXMLNode(psRoot, "rasterYSize") == nullptr ||
    1677        2262 :          CPLGetXMLNode(psRoot, "VRTRasterBand") == nullptr))
    1678             :     {
    1679           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1680             :                  "Missing one of rasterXSize, rasterYSize or bands on"
    1681             :                  " VRTDataset.");
    1682           2 :         return nullptr;
    1683             :     }
    1684             : 
    1685             :     /* -------------------------------------------------------------------- */
    1686             :     /*      Create the new virtual dataset object.                          */
    1687             :     /* -------------------------------------------------------------------- */
    1688        2664 :     const int nXSize = atoi(CPLGetXMLValue(psRoot, "rasterXSize", "0"));
    1689        2664 :     const int nYSize = atoi(CPLGetXMLValue(psRoot, "rasterYSize", "0"));
    1690             : 
    1691        2590 :     if (!bIsPansharpened && !bIsProcessed &&
    1692        7515 :         CPLGetXMLNode(psRoot, "VRTRasterBand") != nullptr &&
    1693        2261 :         !GDALCheckDatasetDimensions(nXSize, nYSize))
    1694             :     {
    1695           0 :         return nullptr;
    1696             :     }
    1697             : 
    1698        2664 :     std::unique_ptr<VRTDataset> poDS;
    1699        2664 :     if (strcmp(pszSubClass, "VRTWarpedDataset") == 0)
    1700         201 :         poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize);
    1701        2463 :     else if (bIsPansharpened)
    1702          74 :         poDS = std::make_unique<VRTPansharpenedDataset>(nXSize, nYSize);
    1703        2389 :     else if (bIsProcessed)
    1704          96 :         poDS = std::make_unique<VRTProcessedDataset>(nXSize, nYSize);
    1705             :     else
    1706             :     {
    1707        2293 :         poDS = std::make_unique<VRTDataset>(nXSize, nYSize);
    1708        2293 :         poDS->eAccess = eAccessIn;
    1709             :     }
    1710             : 
    1711        2664 :     if (poDS->XMLInit(psRoot, pszVRTPath) != CE_None)
    1712             :     {
    1713         127 :         poDS.reset();
    1714             :     }
    1715             : 
    1716             :     /* -------------------------------------------------------------------- */
    1717             :     /*      Try to return a regular handle on the file.                     */
    1718             :     /* -------------------------------------------------------------------- */
    1719             : 
    1720        2664 :     return poDS;
    1721             : }
    1722             : 
    1723             : /************************************************************************/
    1724             : /*                              AddBand()                               */
    1725             : /************************************************************************/
    1726             : 
    1727      138182 : CPLErr VRTDataset::AddBand(GDALDataType eType, char **papszOptions)
    1728             : 
    1729             : {
    1730      138182 :     if (eType == GDT_Unknown || eType == GDT_TypeCount)
    1731             :     {
    1732           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1733             :                     "Illegal GDT_Unknown/GDT_TypeCount argument");
    1734           1 :         return CE_Failure;
    1735             :     }
    1736             : 
    1737      138181 :     SetNeedsFlush();
    1738             : 
    1739             :     /* ==================================================================== */
    1740             :     /*      Handle a new raw band.                                          */
    1741             :     /* ==================================================================== */
    1742      138181 :     const char *pszSubClass = CSLFetchNameValue(papszOptions, "subclass");
    1743             : 
    1744      138181 :     if (pszSubClass != nullptr && EQUAL(pszSubClass, "VRTRawRasterBand"))
    1745             :     {
    1746             : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
    1747           7 :         if (!VRTDataset::IsRawRasterBandEnabled())
    1748             :         {
    1749           1 :             return CE_Failure;
    1750             :         }
    1751           6 :         const int nWordDataSize = GDALGetDataTypeSizeBytes(eType);
    1752             : 
    1753             :         /* ---------------------------------------------------------------- */
    1754             :         /*      Collect required information.                               */
    1755             :         /* ---------------------------------------------------------------- */
    1756             :         const char *pszImageOffset =
    1757           6 :             CSLFetchNameValueDef(papszOptions, "ImageOffset", "0");
    1758          12 :         vsi_l_offset nImageOffset = CPLScanUIntBig(
    1759           6 :             pszImageOffset, static_cast<int>(strlen(pszImageOffset)));
    1760             : 
    1761           6 :         int nPixelOffset = nWordDataSize;
    1762             :         const char *pszPixelOffset =
    1763           6 :             CSLFetchNameValue(papszOptions, "PixelOffset");
    1764           6 :         if (pszPixelOffset != nullptr)
    1765           4 :             nPixelOffset = atoi(pszPixelOffset);
    1766             : 
    1767             :         int nLineOffset;
    1768             :         const char *pszLineOffset =
    1769           6 :             CSLFetchNameValue(papszOptions, "LineOffset");
    1770           6 :         if (pszLineOffset != nullptr)
    1771           4 :             nLineOffset = atoi(pszLineOffset);
    1772             :         else
    1773             :         {
    1774           4 :             if (nPixelOffset > INT_MAX / GetRasterXSize() ||
    1775           2 :                 nPixelOffset < INT_MIN / GetRasterXSize())
    1776             :             {
    1777           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Int overflow");
    1778           0 :                 return CE_Failure;
    1779             :             }
    1780           2 :             nLineOffset = nPixelOffset * GetRasterXSize();
    1781             :         }
    1782             : 
    1783           6 :         const char *pszByteOrder = CSLFetchNameValue(papszOptions, "ByteOrder");
    1784             : 
    1785             :         const char *pszFilename =
    1786           6 :             CSLFetchNameValue(papszOptions, "SourceFilename");
    1787           6 :         if (pszFilename == nullptr)
    1788             :         {
    1789           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1790             :                      "AddBand() requires a SourceFilename option for "
    1791             :                      "VRTRawRasterBands.");
    1792           0 :             return CE_Failure;
    1793             :         }
    1794             : 
    1795             :         const bool bRelativeToVRT =
    1796           6 :             CPLFetchBool(papszOptions, "relativeToVRT", false);
    1797             : 
    1798             :         /* --------------------------------------------------------------- */
    1799             :         /*      Create and initialize the band.                            */
    1800             :         /* --------------------------------------------------------------- */
    1801             : 
    1802             :         auto poBand = std::make_unique<VRTRawRasterBand>(
    1803          12 :             this, GetRasterCount() + 1, eType);
    1804             : 
    1805           6 :         const std::string osPath = CPLGetPathSafe(GetDescription());
    1806          18 :         const CPLErr eErr = poBand->SetRawLink(
    1807          12 :             pszFilename, osPath.empty() ? nullptr : osPath.c_str(),
    1808             :             bRelativeToVRT, nImageOffset, nPixelOffset, nLineOffset,
    1809             :             pszByteOrder);
    1810           6 :         if (eErr == CE_None)
    1811           6 :             SetBand(GetRasterCount() + 1, std::move(poBand));
    1812             : 
    1813           6 :         return eErr;
    1814             : #else
    1815             :         CPLError(CE_Failure, CPLE_NotSupported,
    1816             :                  "VRTDataset::AddBand(): cannot instantiate VRTRawRasterBand, "
    1817             :                  "because disabled in this GDAL build");
    1818             :         return CE_Failure;
    1819             : #endif
    1820             :     }
    1821             : 
    1822             :     /* ==================================================================== */
    1823             :     /*      Handle a new "sourced" band.                                    */
    1824             :     /* ==================================================================== */
    1825             :     else
    1826             :     {
    1827      138174 :         VRTSourcedRasterBand *poBand = nullptr;
    1828             : 
    1829             :         int nBlockXSizeIn =
    1830      138174 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
    1831             :         int nBlockYSizeIn =
    1832      138174 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
    1833      138174 :         if (nBlockXSizeIn == 0 && nBlockYSizeIn == 0)
    1834             :         {
    1835       71040 :             nBlockXSizeIn = m_nBlockXSize;
    1836       71040 :             nBlockYSizeIn = m_nBlockYSize;
    1837             :         }
    1838             : 
    1839             :         /* ---- Check for our sourced band 'derived' subclass ---- */
    1840      138174 :         if (pszSubClass != nullptr &&
    1841         572 :             EQUAL(pszSubClass, "VRTDerivedRasterBand"))
    1842             :         {
    1843             : 
    1844             :             /* We'll need a pointer to the subclass in case we need */
    1845             :             /* to set the new band's pixel function below. */
    1846             :             VRTDerivedRasterBand *poDerivedBand = new VRTDerivedRasterBand(
    1847         515 :                 this, GetRasterCount() + 1, eType, GetRasterXSize(),
    1848         515 :                 GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
    1849             : 
    1850             :             /* Set the pixel function options it provided. */
    1851             :             const char *pszFuncName =
    1852         515 :                 CSLFetchNameValue(papszOptions, "PixelFunctionType");
    1853         515 :             if (pszFuncName != nullptr)
    1854         514 :                 poDerivedBand->SetPixelFunctionName(pszFuncName);
    1855             : 
    1856             :             const char *pszLanguage =
    1857         515 :                 CSLFetchNameValue(papszOptions, "PixelFunctionLanguage");
    1858         515 :             if (pszLanguage != nullptr)
    1859           1 :                 poDerivedBand->SetPixelFunctionLanguage(pszLanguage);
    1860             : 
    1861             :             const char *pszSkipNonContributingSources =
    1862         515 :                 CSLFetchNameValue(papszOptions, "SkipNonContributingSources");
    1863         515 :             if (pszSkipNonContributingSources != nullptr)
    1864             :             {
    1865           3 :                 poDerivedBand->SetSkipNonContributingSources(
    1866           3 :                     CPLTestBool(pszSkipNonContributingSources));
    1867             :             }
    1868        3884 :             for (const auto &[pszKey, pszValue] :
    1869        4399 :                  cpl::IterateNameValue(static_cast<CSLConstList>(papszOptions)))
    1870             :             {
    1871        1942 :                 if (STARTS_WITH(pszKey, "_PIXELFN_ARG_"))
    1872             :                 {
    1873         904 :                     poDerivedBand->AddPixelFunctionArgument(pszKey + 13,
    1874             :                                                             pszValue);
    1875             :                 }
    1876             :             }
    1877             : 
    1878             :             const char *pszTransferTypeName =
    1879         515 :                 CSLFetchNameValue(papszOptions, "SourceTransferType");
    1880         515 :             if (pszTransferTypeName != nullptr)
    1881             :             {
    1882             :                 const GDALDataType eTransferType =
    1883           5 :                     GDALGetDataTypeByName(pszTransferTypeName);
    1884           5 :                 if (eTransferType == GDT_Unknown)
    1885             :                 {
    1886           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1887             :                              "invalid SourceTransferType: \"%s\".",
    1888             :                              pszTransferTypeName);
    1889           1 :                     delete poDerivedBand;
    1890           1 :                     return CE_Failure;
    1891             :                 }
    1892           4 :                 poDerivedBand->SetSourceTransferType(eTransferType);
    1893             :             }
    1894             : 
    1895             :             /* We're done with the derived band specific stuff, so */
    1896             :             /* we can assign the base class pointer now. */
    1897         514 :             poBand = poDerivedBand;
    1898             :         }
    1899             :         else
    1900             :         {
    1901             :             /* ---- Standard sourced band ---- */
    1902      137659 :             poBand = new VRTSourcedRasterBand(
    1903      137659 :                 this, GetRasterCount() + 1, eType, GetRasterXSize(),
    1904      137659 :                 GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
    1905             :         }
    1906             : 
    1907      138173 :         SetBand(GetRasterCount() + 1, poBand);
    1908             : 
    1909      294954 :         for (int i = 0; papszOptions != nullptr && papszOptions[i] != nullptr;
    1910             :              i++)
    1911             :         {
    1912      156781 :             if (STARTS_WITH_CI(papszOptions[i], "AddFuncSource="))
    1913             :             {
    1914           0 :                 char **papszTokens = CSLTokenizeStringComplex(
    1915           0 :                     papszOptions[i] + 14, ",", TRUE, FALSE);
    1916           0 :                 if (CSLCount(papszTokens) < 1)
    1917             :                 {
    1918           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1919             :                              "AddFuncSource(): required argument missing.");
    1920             :                     // TODO: How should this error be handled?  Return
    1921             :                     // CE_Failure?
    1922             :                 }
    1923             : 
    1924           0 :                 VRTImageReadFunc pfnReadFunc = nullptr;
    1925           0 :                 sscanf(papszTokens[0], "%p", &pfnReadFunc);
    1926             : 
    1927           0 :                 void *pCBData = nullptr;
    1928           0 :                 if (CSLCount(papszTokens) > 1)
    1929           0 :                     sscanf(papszTokens[1], "%p", &pCBData);
    1930             : 
    1931           0 :                 const double dfNoDataValue = (CSLCount(papszTokens) > 2)
    1932           0 :                                                  ? CPLAtof(papszTokens[2])
    1933           0 :                                                  : VRT_NODATA_UNSET;
    1934             : 
    1935           0 :                 poBand->AddFuncSource(pfnReadFunc, pCBData, dfNoDataValue);
    1936             : 
    1937           0 :                 CSLDestroy(papszTokens);
    1938             :             }
    1939             :         }
    1940             : 
    1941      138173 :         return CE_None;
    1942             :     }
    1943             : }
    1944             : 
    1945             : /*! @endcond */
    1946             : /************************************************************************/
    1947             : /*                              VRTAddBand()                            */
    1948             : /************************************************************************/
    1949             : 
    1950             : /**
    1951             :  * @see VRTDataset::VRTAddBand().
    1952             :  *
    1953             :  * @note The return type of this function is int, but the actual values
    1954             :  * returned are of type CPLErr.
    1955             :  */
    1956             : 
    1957         960 : int CPL_STDCALL VRTAddBand(VRTDatasetH hDataset, GDALDataType eType,
    1958             :                            char **papszOptions)
    1959             : 
    1960             : {
    1961         960 :     VALIDATE_POINTER1(hDataset, "VRTAddBand", 0);
    1962             : 
    1963         960 :     return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
    1964         960 :         ->AddBand(eType, papszOptions);
    1965             : }
    1966             : 
    1967             : /*! @cond Doxygen_Suppress */
    1968             : 
    1969             : /************************************************************************/
    1970             : /*                               Create()                               */
    1971             : /************************************************************************/
    1972             : 
    1973         144 : GDALDataset *VRTDataset::Create(const char *pszName, int nXSize, int nYSize,
    1974             :                                 int nBandsIn, GDALDataType eType,
    1975             :                                 char **papszOptions)
    1976             : 
    1977             : {
    1978         288 :     return CreateVRTDataset(pszName, nXSize, nYSize, nBandsIn, eType,
    1979             :                             const_cast<CSLConstList>(papszOptions))
    1980         144 :         .release();
    1981             : }
    1982             : 
    1983             : /************************************************************************/
    1984             : /*                            CreateVRTDataset()                        */
    1985             : /************************************************************************/
    1986             : 
    1987             : std::unique_ptr<VRTDataset>
    1988         587 : VRTDataset::CreateVRTDataset(const char *pszName, int nXSize, int nYSize,
    1989             :                              int nBandsIn, GDALDataType eType,
    1990             :                              CSLConstList papszOptions)
    1991             : 
    1992             : {
    1993         587 :     if (STARTS_WITH_CI(pszName, "<VRTDataset"))
    1994             :     {
    1995           0 :         auto poDS = OpenXML(pszName, nullptr, GA_Update);
    1996           0 :         if (poDS != nullptr)
    1997           0 :             poDS->SetDescription("<FromXML>");
    1998           0 :         return poDS;
    1999             :     }
    2000             : 
    2001         587 :     const char *pszSubclass = CSLFetchNameValue(papszOptions, "SUBCLASS");
    2002             : 
    2003         587 :     std::unique_ptr<VRTDataset> poDS;
    2004             : 
    2005             :     const int nBlockXSize =
    2006         587 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
    2007             :     const int nBlockYSize =
    2008         587 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
    2009         587 :     if (pszSubclass == nullptr || EQUAL(pszSubclass, "VRTDataset"))
    2010         986 :         poDS = std::make_unique<VRTDataset>(nXSize, nYSize, nBlockXSize,
    2011         493 :                                             nBlockYSize);
    2012          94 :     else if (EQUAL(pszSubclass, "VRTWarpedDataset"))
    2013             :     {
    2014         188 :         poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize, nBlockXSize,
    2015          94 :                                                   nBlockYSize);
    2016             :     }
    2017             :     else
    2018             :     {
    2019           0 :         CPLError(CE_Failure, CPLE_AppDefined, "SUBCLASS=%s not recognised.",
    2020             :                  pszSubclass);
    2021           0 :         return nullptr;
    2022             :     }
    2023         587 :     poDS->eAccess = GA_Update;
    2024             : 
    2025         587 :     poDS->SetDescription(pszName);
    2026             : 
    2027         819 :     for (int iBand = 0; iBand < nBandsIn; iBand++)
    2028         232 :         poDS->AddBand(eType, nullptr);
    2029             : 
    2030         587 :     poDS->SetNeedsFlush();
    2031             : 
    2032         587 :     poDS->oOvManager.Initialize(poDS.get(), pszName);
    2033             : 
    2034         587 :     return poDS;
    2035             : }
    2036             : 
    2037             : /************************************************************************/
    2038             : /*                     CreateMultiDimensional()                         */
    2039             : /************************************************************************/
    2040             : 
    2041             : GDALDataset *
    2042         106 : VRTDataset::CreateMultiDimensional(const char *pszFilename,
    2043             :                                    CSLConstList /*papszRootGroupOptions*/,
    2044             :                                    CSLConstList /*papszOptions*/)
    2045             : {
    2046         106 :     VRTDataset *poDS = new VRTDataset(0, 0);
    2047         106 :     poDS->eAccess = GA_Update;
    2048         106 :     poDS->SetDescription(pszFilename);
    2049         106 :     poDS->m_poRootGroup = VRTGroup::Create(std::string(), "/");
    2050         106 :     poDS->m_poRootGroup->SetIsRootGroup();
    2051         106 :     poDS->m_poRootGroup->SetFilename(pszFilename);
    2052         106 :     poDS->m_poRootGroup->SetDirty();
    2053             : 
    2054         106 :     return poDS;
    2055             : }
    2056             : 
    2057             : /************************************************************************/
    2058             : /*                            GetFileList()                             */
    2059             : /************************************************************************/
    2060             : 
    2061          59 : char **VRTDataset::GetFileList()
    2062             : {
    2063          59 :     char **papszFileList = GDALDataset::GetFileList();
    2064             : 
    2065          59 :     int nSize = CSLCount(papszFileList);
    2066          59 :     int nMaxSize = nSize;
    2067             : 
    2068             :     // Do not need an element deallocator as each string points to an
    2069             :     // element of the papszFileList.
    2070             :     CPLHashSet *hSetFiles =
    2071          59 :         CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
    2072             : 
    2073         129 :     for (int iBand = 0; iBand < nBands; iBand++)
    2074             :     {
    2075          70 :         static_cast<VRTRasterBand *>(papoBands[iBand])
    2076          70 :             ->GetFileList(&papszFileList, &nSize, &nMaxSize, hSetFiles);
    2077             :     }
    2078             : 
    2079          59 :     CPLHashSetDestroy(hSetFiles);
    2080             : 
    2081          59 :     return papszFileList;
    2082             : }
    2083             : 
    2084             : /************************************************************************/
    2085             : /*                              Delete()                                */
    2086             : /************************************************************************/
    2087             : 
    2088             : /* We implement Delete() to avoid that the default implementation */
    2089             : /* in GDALDriver::Delete() destroys the source files listed by GetFileList(),*/
    2090             : /* which would be an undesired effect... */
    2091          10 : CPLErr VRTDataset::Delete(const char *pszFilename)
    2092             : {
    2093          10 :     GDALDriverH hDriver = GDALIdentifyDriver(pszFilename, nullptr);
    2094             : 
    2095          10 :     if (!hDriver || !EQUAL(GDALGetDriverShortName(hDriver), "VRT"))
    2096           0 :         return CE_Failure;
    2097             : 
    2098          19 :     if (strstr(pszFilename, "<VRTDataset") == nullptr &&
    2099           9 :         VSIUnlink(pszFilename) != 0)
    2100             :     {
    2101           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s",
    2102           0 :                  pszFilename, VSIStrerror(errno));
    2103           0 :         return CE_Failure;
    2104             :     }
    2105             : 
    2106          10 :     return CE_None;
    2107             : }
    2108             : 
    2109             : /************************************************************************/
    2110             : /*                          CreateMaskBand()                            */
    2111             : /************************************************************************/
    2112             : 
    2113          45 : CPLErr VRTDataset::CreateMaskBand(int)
    2114             : {
    2115          45 :     if (m_poMaskBand != nullptr)
    2116             :     {
    2117           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2118             :                  "This VRT dataset has already a mask band");
    2119           1 :         return CE_Failure;
    2120             :     }
    2121             : 
    2122          44 :     SetMaskBand(std::make_unique<VRTSourcedRasterBand>(this, 0));
    2123             : 
    2124          44 :     return CE_None;
    2125             : }
    2126             : 
    2127             : /************************************************************************/
    2128             : /*                           SetMaskBand()                              */
    2129             : /************************************************************************/
    2130             : 
    2131          73 : void VRTDataset::SetMaskBand(std::unique_ptr<VRTRasterBand> poMaskBandIn)
    2132             : {
    2133          73 :     m_poMaskBand = std::move(poMaskBandIn);
    2134          73 :     m_poMaskBand->SetIsMaskBand();
    2135          73 : }
    2136             : 
    2137             : /************************************************************************/
    2138             : /*                        CloseDependentDatasets()                      */
    2139             : /************************************************************************/
    2140             : 
    2141         612 : int VRTDataset::CloseDependentDatasets()
    2142             : {
    2143             :     /* We need to call it before removing the sources, otherwise */
    2144             :     /* we would remove them from the serizalized VRT */
    2145         612 :     FlushCache(true);
    2146             : 
    2147         612 :     int bHasDroppedRef = GDALDataset::CloseDependentDatasets();
    2148             : 
    2149        1683 :     for (int iBand = 0; iBand < nBands; iBand++)
    2150             :     {
    2151        2142 :         bHasDroppedRef |= static_cast<VRTRasterBand *>(papoBands[iBand])
    2152        1071 :                               ->CloseDependentDatasets();
    2153             :     }
    2154             : 
    2155         612 :     return bHasDroppedRef;
    2156             : }
    2157             : 
    2158             : /************************************************************************/
    2159             : /*                      CheckCompatibleForDatasetIO()                   */
    2160             : /************************************************************************/
    2161             : 
    2162             : /* We will return TRUE only if all the bands are VRTSourcedRasterBands */
    2163             : /* made of identical sources, that are strictly VRTSimpleSource, and that */
    2164             : /* the band number of each source is the band number of the */
    2165             : /* VRTSourcedRasterBand. */
    2166             : 
    2167        9865 : bool VRTDataset::CheckCompatibleForDatasetIO() const
    2168             : {
    2169        9865 :     int nSources = 0;
    2170        9865 :     VRTSource **papoSources = nullptr;
    2171       19730 :     CPLString osResampling;
    2172             : 
    2173        9865 :     if (m_nCompatibleForDatasetIO >= 0)
    2174             :     {
    2175        7811 :         return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
    2176             :     }
    2177             : 
    2178        2054 :     m_nCompatibleForDatasetIO = false;
    2179             : 
    2180        2054 :     GDALDataset *poFirstBandSourceDS = nullptr;
    2181        6034 :     for (int iBand = 0; iBand < nBands; iBand++)
    2182             :     {
    2183        4310 :         auto poVRTBand = static_cast<VRTRasterBand *>(papoBands[iBand]);
    2184        4310 :         assert(poVRTBand);
    2185        4310 :         if (!poVRTBand->IsSourcedRasterBand())
    2186          52 :             return false;
    2187             : 
    2188        4258 :         const VRTSourcedRasterBand *poBand =
    2189             :             static_cast<const VRTSourcedRasterBand *>(poVRTBand);
    2190             : 
    2191             :         // Do not allow VRTDerivedRasterBand for example
    2192        4258 :         if (typeid(*poBand) != typeid(VRTSourcedRasterBand))
    2193          63 :             return false;
    2194             : 
    2195        4195 :         if (iBand == 0)
    2196             :         {
    2197        1855 :             nSources = poBand->nSources;
    2198        1855 :             papoSources = poBand->papoSources;
    2199        3870 :             for (int iSource = 0; iSource < nSources; iSource++)
    2200             :             {
    2201        2143 :                 if (!papoSources[iSource]->IsSimpleSource())
    2202           4 :                     return false;
    2203             : 
    2204        2139 :                 const VRTSimpleSource *poSource =
    2205        2139 :                     static_cast<const VRTSimpleSource *>(papoSources[iSource]);
    2206        2139 :                 if (poSource->GetType() != VRTSimpleSource::GetTypeStatic())
    2207          97 :                     return false;
    2208             : 
    2209        6106 :                 if (poSource->m_nBand != iBand + 1 ||
    2210        2384 :                     poSource->m_bGetMaskBand ||
    2211         342 :                     (nSources > 1 && poSource->m_osSrcDSName.empty()))
    2212             :                 {
    2213          27 :                     return false;
    2214             :                 }
    2215        2015 :                 if (nSources == 1 && poSource->m_osSrcDSName.empty())
    2216             :                 {
    2217         950 :                     if (auto poSourceBand = poSource->GetRasterBand())
    2218             :                     {
    2219         950 :                         poFirstBandSourceDS = poSourceBand->GetDataset();
    2220             :                     }
    2221             :                     else
    2222             :                     {
    2223           0 :                         return false;
    2224             :                     }
    2225             :                 }
    2226        2015 :                 osResampling = poSource->GetResampling();
    2227             :             }
    2228             :         }
    2229        2340 :         else if (nSources != poBand->nSources)
    2230             :         {
    2231           0 :             return false;
    2232             :         }
    2233             :         else
    2234             :         {
    2235        5193 :             for (int iSource = 0; iSource < nSources; iSource++)
    2236             :             {
    2237        2940 :                 if (!poBand->papoSources[iSource]->IsSimpleSource())
    2238           0 :                     return false;
    2239        2940 :                 const VRTSimpleSource *poRefSource =
    2240        2940 :                     static_cast<const VRTSimpleSource *>(papoSources[iSource]);
    2241             : 
    2242        2940 :                 const VRTSimpleSource *poSource =
    2243             :                     static_cast<const VRTSimpleSource *>(
    2244        2940 :                         poBand->papoSources[iSource]);
    2245        2940 :                 if (poSource->GetType() != VRTSimpleSource::GetTypeStatic())
    2246          12 :                     return false;
    2247        8709 :                 if (poSource->m_nBand != iBand + 1 ||
    2248        3622 :                     poSource->m_bGetMaskBand ||
    2249         694 :                     (nSources > 1 && poSource->m_osSrcDSName.empty()))
    2250          75 :                     return false;
    2251        2853 :                 if (!poSource->IsSameExceptBandNumber(poRefSource))
    2252           0 :                     return false;
    2253        2853 :                 if (osResampling.compare(poSource->GetResampling()) != 0)
    2254           0 :                     return false;
    2255        2853 :                 if (nSources == 1 && poSource->m_osSrcDSName.empty())
    2256             :                 {
    2257        1833 :                     auto poSourceBand = poSource->GetRasterBand();
    2258        3666 :                     if (!poSourceBand ||
    2259        1833 :                         poFirstBandSourceDS != poSourceBand->GetDataset())
    2260             :                     {
    2261           0 :                         return false;
    2262             :                     }
    2263             :                 }
    2264             :             }
    2265             :         }
    2266             :     }
    2267             : 
    2268        1724 :     m_nCompatibleForDatasetIO = nSources != 0;
    2269        1724 :     return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
    2270             : }
    2271             : 
    2272             : /************************************************************************/
    2273             : /*                         GetSingleSimpleSource()                      */
    2274             : /*                                                                      */
    2275             : /* Returns a non-NULL dataset if the VRT is made of a single source     */
    2276             : /* that is a simple source, in its full extent, and with all of its     */
    2277             : /* bands. Basically something produced by :                             */
    2278             : /*   gdal_translate src dst.vrt -of VRT (-a_srs / -a_ullr)              */
    2279             : /************************************************************************/
    2280             : 
    2281        1233 : GDALDataset *VRTDataset::GetSingleSimpleSource()
    2282             : {
    2283        1233 :     if (!CheckCompatibleForDatasetIO())
    2284          90 :         return nullptr;
    2285             : 
    2286        1143 :     VRTSourcedRasterBand *poVRTBand =
    2287        1143 :         static_cast<VRTSourcedRasterBand *>(papoBands[0]);
    2288        1143 :     if (poVRTBand->nSources != 1)
    2289           1 :         return nullptr;
    2290             : 
    2291        1142 :     VRTSimpleSource *poSource =
    2292        1142 :         static_cast<VRTSimpleSource *>(poVRTBand->papoSources[0]);
    2293             : 
    2294        1142 :     GDALRasterBand *poBand = poSource->GetRasterBand();
    2295        1142 :     if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
    2296           3 :         return nullptr;
    2297             : 
    2298        1139 :     GDALDataset *poSrcDS = poBand->GetDataset();
    2299        1139 :     if (poSrcDS == nullptr)
    2300           0 :         return nullptr;
    2301             : 
    2302             :     /* Check that it uses the full source dataset */
    2303        1139 :     double dfReqXOff = 0.0;
    2304        1139 :     double dfReqYOff = 0.0;
    2305        1139 :     double dfReqXSize = 0.0;
    2306        1139 :     double dfReqYSize = 0.0;
    2307        1139 :     int nReqXOff = 0;
    2308        1139 :     int nReqYOff = 0;
    2309        1139 :     int nReqXSize = 0;
    2310        1139 :     int nReqYSize = 0;
    2311        1139 :     int nOutXOff = 0;
    2312        1139 :     int nOutYOff = 0;
    2313        1139 :     int nOutXSize = 0;
    2314        1139 :     int nOutYSize = 0;
    2315        1139 :     bool bError = false;
    2316        2278 :     if (!poSource->GetSrcDstWindow(
    2317        1139 :             0, 0, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
    2318             :             poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), &dfReqXOff,
    2319             :             &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    2320             :             &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff, &nOutXSize,
    2321             :             &nOutYSize, bError))
    2322           0 :         return nullptr;
    2323             : 
    2324         324 :     if (nReqXOff != 0 || nReqYOff != 0 ||
    2325        1682 :         nReqXSize != poSrcDS->GetRasterXSize() ||
    2326         219 :         nReqYSize != poSrcDS->GetRasterYSize())
    2327         922 :         return nullptr;
    2328             : 
    2329         217 :     if (nOutXOff != 0 || nOutYOff != 0 ||
    2330         628 :         nOutXSize != poSrcDS->GetRasterXSize() ||
    2331         194 :         nOutYSize != poSrcDS->GetRasterYSize())
    2332          23 :         return nullptr;
    2333             : 
    2334         194 :     return poSrcDS;
    2335             : }
    2336             : 
    2337             : /************************************************************************/
    2338             : /*                             AdviseRead()                             */
    2339             : /************************************************************************/
    2340             : 
    2341        3622 : CPLErr VRTDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    2342             :                               int nBufXSize, int nBufYSize, GDALDataType eDT,
    2343             :                               int nBandCount, int *panBandList,
    2344             :                               char **papszOptions)
    2345             : {
    2346        3622 :     if (!CheckCompatibleForDatasetIO())
    2347         635 :         return CE_None;
    2348             : 
    2349        2987 :     VRTSourcedRasterBand *poVRTBand =
    2350        2987 :         static_cast<VRTSourcedRasterBand *>(papoBands[0]);
    2351        2987 :     if (poVRTBand->nSources != 1)
    2352          10 :         return CE_None;
    2353             : 
    2354        2977 :     VRTSimpleSource *poSource =
    2355        2977 :         static_cast<VRTSimpleSource *>(poVRTBand->papoSources[0]);
    2356             : 
    2357             :     /* Find source window and buffer size */
    2358        2977 :     double dfReqXOff = 0.0;
    2359        2977 :     double dfReqYOff = 0.0;
    2360        2977 :     double dfReqXSize = 0.0;
    2361        2977 :     double dfReqYSize = 0.0;
    2362        2977 :     int nReqXOff = 0;
    2363        2977 :     int nReqYOff = 0;
    2364        2977 :     int nReqXSize = 0;
    2365        2977 :     int nReqYSize = 0;
    2366        2977 :     int nOutXOff = 0;
    2367        2977 :     int nOutYOff = 0;
    2368        2977 :     int nOutXSize = 0;
    2369        2977 :     int nOutYSize = 0;
    2370        2977 :     bool bError = false;
    2371        2977 :     if (!poSource->GetSrcDstWindow(nXOff, nYOff, nXSize, nYSize, nBufXSize,
    2372             :                                    nBufYSize, &dfReqXOff, &dfReqYOff,
    2373             :                                    &dfReqXSize, &dfReqYSize, &nReqXOff,
    2374             :                                    &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
    2375             :                                    &nOutYOff, &nOutXSize, &nOutYSize, bError))
    2376             :     {
    2377          74 :         return bError ? CE_Failure : CE_None;
    2378             :     }
    2379             : 
    2380        2903 :     GDALRasterBand *poBand = poSource->GetRasterBand();
    2381        2903 :     if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
    2382           0 :         return CE_None;
    2383             : 
    2384        2903 :     GDALDataset *poSrcDS = poBand->GetDataset();
    2385        2903 :     if (poSrcDS == nullptr)
    2386           0 :         return CE_None;
    2387             : 
    2388        2903 :     return poSrcDS->AdviseRead(nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    2389             :                                nOutXSize, nOutYSize, eDT, nBandCount,
    2390        2903 :                                panBandList, papszOptions);
    2391             : }
    2392             : 
    2393             : /************************************************************************/
    2394             : /*                           GetNumThreads()                            */
    2395             : /************************************************************************/
    2396             : 
    2397          37 : /* static */ int VRTDataset::GetNumThreads(GDALDataset *poDS)
    2398             : {
    2399          37 :     const char *pszNumThreads = nullptr;
    2400          37 :     if (poDS)
    2401          37 :         pszNumThreads = CSLFetchNameValueDef(poDS->GetOpenOptions(),
    2402             :                                              "NUM_THREADS", nullptr);
    2403          37 :     if (!pszNumThreads)
    2404          37 :         pszNumThreads = CPLGetConfigOption("VRT_NUM_THREADS", nullptr);
    2405          37 :     if (!pszNumThreads)
    2406          31 :         pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
    2407          37 :     if (EQUAL(pszNumThreads, "0") || EQUAL(pszNumThreads, "1"))
    2408           5 :         return atoi(pszNumThreads);
    2409          32 :     const int nMaxPoolSize = GDALGetMaxDatasetPoolSize();
    2410          32 :     const int nLimit = std::min(CPLGetNumCPUs(), nMaxPoolSize);
    2411          32 :     if (EQUAL(pszNumThreads, "ALL_CPUS"))
    2412          31 :         return nLimit;
    2413           1 :     return std::min(atoi(pszNumThreads), nLimit);
    2414             : }
    2415             : 
    2416             : /************************************************************************/
    2417             : /*                       VRTDatasetRasterIOJob                          */
    2418             : /************************************************************************/
    2419             : 
    2420             : /** Structure used to declare a threaded job to satisfy IRasterIO()
    2421             :  * on a given source.
    2422             :  */
    2423             : struct VRTDatasetRasterIOJob
    2424             : {
    2425             :     std::atomic<int> *pnCompletedJobs = nullptr;
    2426             :     std::atomic<bool> *pbSuccess = nullptr;
    2427             :     CPLErrorAccumulator *poErrorAccumulator = nullptr;
    2428             : 
    2429             :     GDALDataType eVRTBandDataType = GDT_Unknown;
    2430             :     int nXOff = 0;
    2431             :     int nYOff = 0;
    2432             :     int nXSize = 0;
    2433             :     int nYSize = 0;
    2434             :     void *pData = nullptr;
    2435             :     int nBufXSize = 0;
    2436             :     int nBufYSize = 0;
    2437             :     int nBandCount = 0;
    2438             :     BANDMAP_TYPE panBandMap = nullptr;
    2439             :     GDALDataType eBufType = GDT_Unknown;
    2440             :     GSpacing nPixelSpace = 0;
    2441             :     GSpacing nLineSpace = 0;
    2442             :     GSpacing nBandSpace = 0;
    2443             :     GDALRasterIOExtraArg *psExtraArg = nullptr;
    2444             :     VRTSimpleSource *poSource = nullptr;
    2445             : 
    2446             :     static void Func(void *pData);
    2447             : };
    2448             : 
    2449             : /************************************************************************/
    2450             : /*                     VRTDatasetRasterIOJob::Func()                    */
    2451             : /************************************************************************/
    2452             : 
    2453          67 : void VRTDatasetRasterIOJob::Func(void *pData)
    2454             : {
    2455             :     auto psJob = std::unique_ptr<VRTDatasetRasterIOJob>(
    2456         134 :         static_cast<VRTDatasetRasterIOJob *>(pData));
    2457          67 :     if (*psJob->pbSuccess)
    2458             :     {
    2459          67 :         GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
    2460          67 :         sArg.pfnProgress = nullptr;
    2461          67 :         sArg.pProgressData = nullptr;
    2462             : 
    2463         134 :         auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
    2464          67 :         CPL_IGNORE_RET_VAL(oAccumulator);
    2465             : 
    2466         134 :         if (psJob->poSource->DatasetRasterIO(
    2467          67 :                 psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
    2468          67 :                 psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
    2469          67 :                 psJob->nBufYSize, psJob->eBufType, psJob->nBandCount,
    2470          67 :                 psJob->panBandMap, psJob->nPixelSpace, psJob->nLineSpace,
    2471         134 :                 psJob->nBandSpace, &sArg) != CE_None)
    2472             :         {
    2473           0 :             *psJob->pbSuccess = false;
    2474             :         }
    2475             :     }
    2476             : 
    2477          67 :     ++(*psJob->pnCompletedJobs);
    2478          67 : }
    2479             : 
    2480             : /************************************************************************/
    2481             : /*                              IRasterIO()                             */
    2482             : /************************************************************************/
    2483             : 
    2484        6275 : CPLErr VRTDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2485             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    2486             :                              int nBufYSize, GDALDataType eBufType,
    2487             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    2488             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2489             :                              GSpacing nBandSpace,
    2490             :                              GDALRasterIOExtraArg *psExtraArg)
    2491             : {
    2492        6275 :     m_bMultiThreadedRasterIOLastUsed = false;
    2493             : 
    2494        6275 :     if (nBands == 1 && nBandCount == 1)
    2495             :     {
    2496             :         VRTSourcedRasterBand *poBand =
    2497        1318 :             dynamic_cast<VRTSourcedRasterBand *>(papoBands[0]);
    2498        1318 :         if (poBand)
    2499             :         {
    2500        1316 :             return poBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2501             :                                      pData, nBufXSize, nBufYSize, eBufType,
    2502        1316 :                                      nPixelSpace, nLineSpace, psExtraArg);
    2503             :         }
    2504             :     }
    2505             : 
    2506             :     bool bLocalCompatibleForDatasetIO =
    2507        4959 :         CPL_TO_BOOL(CheckCompatibleForDatasetIO());
    2508        4591 :     if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
    2509        9550 :         (nBufXSize < nXSize || nBufYSize < nYSize) && m_apoOverviews.empty())
    2510             :     {
    2511           3 :         int bTried = FALSE;
    2512           3 :         const CPLErr eErr = TryOverviewRasterIO(
    2513             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2514             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2515             :             nBandSpace, psExtraArg, &bTried);
    2516             : 
    2517           3 :         if (bTried)
    2518             :         {
    2519           1 :             return eErr;
    2520             :         }
    2521             : 
    2522           9 :         for (int iBand = 0; iBand < nBands; iBand++)
    2523             :         {
    2524           7 :             VRTSourcedRasterBand *poBand =
    2525           7 :                 static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
    2526             : 
    2527             :             // If there are overviews, let VRTSourcedRasterBand::IRasterIO()
    2528             :             // do the job.
    2529           7 :             if (poBand->GetOverviewCount() != 0)
    2530             :             {
    2531           0 :                 bLocalCompatibleForDatasetIO = false;
    2532           0 :                 break;
    2533             :             }
    2534             :         }
    2535             :     }
    2536             : 
    2537             :     // If resampling with non-nearest neighbour, we need to be careful
    2538             :     // if the VRT band exposes a nodata value, but the sources do not have it.
    2539             :     // To also avoid edge effects on sources when downsampling, use the
    2540             :     // base implementation of IRasterIO() (that is acquiring sources at their
    2541             :     // nominal resolution, and then downsampling), but only if none of the
    2542             :     // contributing sources have overviews.
    2543        4958 :     if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
    2544        4567 :         (nXSize != nBufXSize || nYSize != nBufYSize) &&
    2545          23 :         psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
    2546             :     {
    2547           0 :         for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
    2548             :         {
    2549             :             VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
    2550           0 :                 GetRasterBand(panBandMap[iBandIndex]));
    2551           0 :             if (!poBand->CanIRasterIOBeForwardedToEachSource(
    2552             :                     eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
    2553             :                     psExtraArg))
    2554             :             {
    2555           0 :                 bLocalCompatibleForDatasetIO = false;
    2556           0 :                 break;
    2557             :             }
    2558             :         }
    2559             :     }
    2560             : 
    2561        4958 :     if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read)
    2562             :     {
    2563       12418 :         for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
    2564             :         {
    2565             :             VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
    2566        7828 :                 GetRasterBand(panBandMap[iBandIndex]));
    2567             : 
    2568             :             /* Dirty little trick to initialize the buffer without doing */
    2569             :             /* any real I/O */
    2570        7828 :             const int nSavedSources = poBand->nSources;
    2571        7828 :             poBand->nSources = 0;
    2572             : 
    2573        7828 :             GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
    2574        7828 :             psExtraArg->pfnProgress = nullptr;
    2575             : 
    2576        7828 :             GByte *pabyBandData =
    2577        7828 :                 static_cast<GByte *>(pData) + iBandIndex * nBandSpace;
    2578             : 
    2579        7828 :             poBand->IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
    2580             :                               pabyBandData, nBufXSize, nBufYSize, eBufType,
    2581        7828 :                               nPixelSpace, nLineSpace, psExtraArg);
    2582             : 
    2583        7828 :             psExtraArg->pfnProgress = pfnProgressGlobal;
    2584             : 
    2585        7828 :             poBand->nSources = nSavedSources;
    2586             :         }
    2587             : 
    2588        4590 :         CPLErr eErr = CE_None;
    2589             : 
    2590             :         // Use the last band, because when sources reference a GDALProxyDataset,
    2591             :         // they don't necessary instantiate all underlying rasterbands.
    2592        4590 :         VRTSourcedRasterBand *poBand =
    2593        4590 :             static_cast<VRTSourcedRasterBand *>(papoBands[nBands - 1]);
    2594             : 
    2595        4590 :         double dfXOff = nXOff;
    2596        4590 :         double dfYOff = nYOff;
    2597        4590 :         double dfXSize = nXSize;
    2598        4590 :         double dfYSize = nYSize;
    2599        4590 :         if (psExtraArg->bFloatingPointWindowValidity)
    2600             :         {
    2601          11 :             dfXOff = psExtraArg->dfXOff;
    2602          11 :             dfYOff = psExtraArg->dfYOff;
    2603          11 :             dfXSize = psExtraArg->dfXSize;
    2604          11 :             dfYSize = psExtraArg->dfYSize;
    2605             :         }
    2606             : 
    2607        4590 :         int nContributingSources = 0;
    2608        4590 :         int nMaxThreads = 0;
    2609        4590 :         constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
    2610        9180 :         if ((static_cast<int64_t>(nBufXSize) * nBufYSize >=
    2611        4427 :                  MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
    2612        4427 :              static_cast<int64_t>(nXSize) * nYSize >=
    2613         163 :                  MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
    2614         163 :             poBand->CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
    2615         162 :                                            nContributingSources) &&
    2616        9184 :             nContributingSources > 1 &&
    2617           4 :             (nMaxThreads = VRTDataset::GetNumThreads(this)) > 1)
    2618             :         {
    2619           2 :             m_bMultiThreadedRasterIOLastUsed = true;
    2620           2 :             m_oMapSharedSources.InitMutex();
    2621             : 
    2622           4 :             CPLErrorAccumulator errorAccumulator;
    2623           2 :             std::atomic<bool> bSuccess = true;
    2624           2 :             CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
    2625           2 :                 std::min(nContributingSources, nMaxThreads));
    2626             : 
    2627           2 :             CPLDebugOnly(
    2628             :                 "VRT",
    2629             :                 "IRasterIO(): use optimized "
    2630             :                 "multi-threaded code path for mosaic. "
    2631             :                 "Using %d threads",
    2632             :                 std::min(nContributingSources, psThreadPool->GetThreadCount()));
    2633             : 
    2634           2 :             auto oQueue = psThreadPool->CreateJobQueue();
    2635           2 :             std::atomic<int> nCompletedJobs = 0;
    2636         132 :             for (int iSource = 0; iSource < poBand->nSources; iSource++)
    2637             :             {
    2638         130 :                 auto poSource = poBand->papoSources[iSource];
    2639         130 :                 if (!poSource->IsSimpleSource())
    2640           0 :                     continue;
    2641             :                 auto poSimpleSource =
    2642         130 :                     cpl::down_cast<VRTSimpleSource *>(poSource);
    2643         130 :                 if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
    2644             :                                                         dfYSize))
    2645             :                 {
    2646          67 :                     auto psJob = new VRTDatasetRasterIOJob();
    2647          67 :                     psJob->pbSuccess = &bSuccess;
    2648          67 :                     psJob->poErrorAccumulator = &errorAccumulator;
    2649          67 :                     psJob->pnCompletedJobs = &nCompletedJobs;
    2650          67 :                     psJob->eVRTBandDataType = poBand->GetRasterDataType();
    2651          67 :                     psJob->nXOff = nXOff;
    2652          67 :                     psJob->nYOff = nYOff;
    2653          67 :                     psJob->nXSize = nXSize;
    2654          67 :                     psJob->nYSize = nYSize;
    2655          67 :                     psJob->pData = pData;
    2656          67 :                     psJob->nBufXSize = nBufXSize;
    2657          67 :                     psJob->nBufYSize = nBufYSize;
    2658          67 :                     psJob->eBufType = eBufType;
    2659          67 :                     psJob->nBandCount = nBandCount;
    2660          67 :                     psJob->panBandMap = panBandMap;
    2661          67 :                     psJob->nPixelSpace = nPixelSpace;
    2662          67 :                     psJob->nLineSpace = nLineSpace;
    2663          67 :                     psJob->nBandSpace = nBandSpace;
    2664          67 :                     psJob->psExtraArg = psExtraArg;
    2665          67 :                     psJob->poSource = poSimpleSource;
    2666             : 
    2667          67 :                     if (!oQueue->SubmitJob(VRTDatasetRasterIOJob::Func, psJob))
    2668             :                     {
    2669           0 :                         delete psJob;
    2670           0 :                         bSuccess = false;
    2671           0 :                         break;
    2672             :                     }
    2673             :                 }
    2674             :             }
    2675             : 
    2676          60 :             while (oQueue->WaitEvent())
    2677             :             {
    2678             :                 // Quite rough progress callback. We could do better by counting
    2679             :                 // the number of contributing pixels.
    2680          58 :                 if (psExtraArg->pfnProgress)
    2681             :                 {
    2682         116 :                     psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
    2683             :                                                 nContributingSources,
    2684             :                                             "", psExtraArg->pProgressData);
    2685             :                 }
    2686             :             }
    2687             : 
    2688           2 :             errorAccumulator.ReplayErrors();
    2689           2 :             eErr = bSuccess ? CE_None : CE_Failure;
    2690             :         }
    2691             :         else
    2692             :         {
    2693        4588 :             GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
    2694        4588 :             void *pProgressDataGlobal = psExtraArg->pProgressData;
    2695             : 
    2696        9343 :             for (int iSource = 0; eErr == CE_None && iSource < poBand->nSources;
    2697             :                  iSource++)
    2698             :             {
    2699        4755 :                 psExtraArg->pfnProgress = GDALScaledProgress;
    2700        9510 :                 psExtraArg->pProgressData = GDALCreateScaledProgress(
    2701        4755 :                     1.0 * iSource / poBand->nSources,
    2702        4755 :                     1.0 * (iSource + 1) / poBand->nSources, pfnProgressGlobal,
    2703             :                     pProgressDataGlobal);
    2704             : 
    2705        4755 :                 VRTSimpleSource *poSource = static_cast<VRTSimpleSource *>(
    2706        4755 :                     poBand->papoSources[iSource]);
    2707             : 
    2708        4755 :                 eErr = poSource->DatasetRasterIO(
    2709             :                     poBand->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
    2710             :                     pData, nBufXSize, nBufYSize, eBufType, nBandCount,
    2711             :                     panBandMap, nPixelSpace, nLineSpace, nBandSpace,
    2712             :                     psExtraArg);
    2713             : 
    2714        4755 :                 GDALDestroyScaledProgress(psExtraArg->pProgressData);
    2715             :             }
    2716             : 
    2717        4588 :             psExtraArg->pfnProgress = pfnProgressGlobal;
    2718        4588 :             psExtraArg->pProgressData = pProgressDataGlobal;
    2719             :         }
    2720             : 
    2721        4590 :         if (eErr == CE_None && psExtraArg->pfnProgress)
    2722             :         {
    2723        2646 :             psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    2724             :         }
    2725             : 
    2726        4590 :         return eErr;
    2727             :     }
    2728             : 
    2729             :     CPLErr eErr;
    2730         368 :     if (eRWFlag == GF_Read &&
    2731         368 :         psExtraArg->eResampleAlg != GRIORA_NearestNeighbour &&
    2732           2 :         nBufXSize < nXSize && nBufYSize < nYSize && nBandCount > 1)
    2733             :     {
    2734             :         // Force going through VRTSourcedRasterBand::IRasterIO(), otherwise
    2735             :         // GDALDataset::IRasterIOResampled() would be used without source
    2736             :         // overviews being potentially used.
    2737           2 :         eErr = GDALDataset::BandBasedRasterIO(
    2738             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2739             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2740             :             nBandSpace, psExtraArg);
    2741             :     }
    2742             :     else
    2743             :     {
    2744         366 :         eErr = GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2745             :                                       pData, nBufXSize, nBufYSize, eBufType,
    2746             :                                       nBandCount, panBandMap, nPixelSpace,
    2747             :                                       nLineSpace, nBandSpace, psExtraArg);
    2748             :     }
    2749         368 :     return eErr;
    2750             : }
    2751             : 
    2752             : /************************************************************************/
    2753             : /*                  UnsetPreservedRelativeFilenames()                   */
    2754             : /************************************************************************/
    2755             : 
    2756         169 : void VRTDataset::UnsetPreservedRelativeFilenames()
    2757             : {
    2758         367 :     for (int iBand = 0; iBand < nBands; iBand++)
    2759             :     {
    2760         499 :         if (!static_cast<VRTRasterBand *>(papoBands[iBand])
    2761         198 :                  ->IsSourcedRasterBand())
    2762         103 :             continue;
    2763             : 
    2764          95 :         VRTSourcedRasterBand *poBand =
    2765          95 :             static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
    2766          95 :         const int nSources = poBand->nSources;
    2767          95 :         VRTSource **papoSources = poBand->papoSources;
    2768         173 :         for (int iSource = 0; iSource < nSources; iSource++)
    2769             :         {
    2770          78 :             if (!papoSources[iSource]->IsSimpleSource())
    2771           0 :                 continue;
    2772             : 
    2773          78 :             VRTSimpleSource *poSource =
    2774          78 :                 static_cast<VRTSimpleSource *>(papoSources[iSource]);
    2775          78 :             poSource->UnsetPreservedRelativeFilenames();
    2776             :         }
    2777             :     }
    2778         169 : }
    2779             : 
    2780             : /************************************************************************/
    2781             : /*                        BuildVirtualOverviews()                       */
    2782             : /************************************************************************/
    2783             : 
    2784        5610 : static bool CheckBandForOverview(GDALRasterBand *poBand,
    2785             :                                  GDALRasterBand *&poFirstBand, int &nOverviews,
    2786             :                                  std::set<std::pair<int, int>> &oSetOvrSizes,
    2787             :                                  std::vector<GDALDataset *> &apoOverviewsBak)
    2788             : {
    2789        5610 :     if (!cpl::down_cast<VRTRasterBand *>(poBand)->IsSourcedRasterBand())
    2790           0 :         return false;
    2791             : 
    2792             :     VRTSourcedRasterBand *poVRTBand =
    2793        5610 :         cpl::down_cast<VRTSourcedRasterBand *>(poBand);
    2794        5610 :     if (poVRTBand->nSources != 1)
    2795        4272 :         return false;
    2796        1338 :     if (!poVRTBand->papoSources[0]->IsSimpleSource())
    2797           0 :         return false;
    2798             : 
    2799             :     VRTSimpleSource *poSource =
    2800        1338 :         cpl::down_cast<VRTSimpleSource *>(poVRTBand->papoSources[0]);
    2801        1338 :     const char *pszType = poSource->GetType();
    2802        1388 :     if (pszType != VRTSimpleSource::GetTypeStatic() &&
    2803          50 :         pszType != VRTComplexSource::GetTypeStatic())
    2804             :     {
    2805           2 :         return false;
    2806             :     }
    2807        1336 :     GDALRasterBand *poSrcBand = poBand->GetBand() == 0
    2808        1336 :                                     ? poSource->GetMaskBandMainBand()
    2809        1326 :                                     : poSource->GetRasterBand();
    2810        1336 :     if (poSrcBand == nullptr)
    2811          79 :         return false;
    2812             : 
    2813             :     // To prevent recursion
    2814        1257 :     apoOverviewsBak.push_back(nullptr);
    2815        1257 :     const int nOvrCount = poSrcBand->GetOverviewCount();
    2816             :     oSetOvrSizes.insert(
    2817        1257 :         std::pair<int, int>(poSrcBand->GetXSize(), poSrcBand->GetYSize()));
    2818        1412 :     for (int i = 0; i < nOvrCount; ++i)
    2819             :     {
    2820         155 :         auto poSrcOvrBand = poSrcBand->GetOverview(i);
    2821         155 :         if (poSrcOvrBand)
    2822             :         {
    2823         155 :             oSetOvrSizes.insert(std::pair<int, int>(poSrcOvrBand->GetXSize(),
    2824         310 :                                                     poSrcOvrBand->GetYSize()));
    2825             :         }
    2826             :     }
    2827        1257 :     apoOverviewsBak.resize(0);
    2828             : 
    2829        1257 :     if (nOvrCount == 0)
    2830        1165 :         return false;
    2831          92 :     if (poFirstBand == nullptr)
    2832             :     {
    2833          33 :         if (poSrcBand->GetXSize() == 0 || poSrcBand->GetYSize() == 0)
    2834           0 :             return false;
    2835          33 :         poFirstBand = poSrcBand;
    2836          33 :         nOverviews = nOvrCount;
    2837             :     }
    2838          59 :     else if (nOvrCount < nOverviews)
    2839           0 :         nOverviews = nOvrCount;
    2840          92 :     return true;
    2841             : }
    2842             : 
    2843        5576 : void VRTDataset::BuildVirtualOverviews()
    2844             : {
    2845             :     // Currently we expose virtual overviews only if the dataset is made of
    2846             :     // a single SimpleSource/ComplexSource, in each band.
    2847             :     // And if the underlying sources have overviews of course
    2848        5576 :     if (!m_apoOverviews.empty() || !m_apoOverviewsBak.empty())
    2849        5554 :         return;
    2850             : 
    2851        5540 :     int nOverviews = 0;
    2852        5540 :     GDALRasterBand *poFirstBand = nullptr;
    2853        5540 :     std::set<std::pair<int, int>> oSetOvrSizes;
    2854             : 
    2855        5630 :     for (int iBand = 0; iBand < nBands; iBand++)
    2856             :     {
    2857        5600 :         if (!CheckBandForOverview(papoBands[iBand], poFirstBand, nOverviews,
    2858        5600 :                                   oSetOvrSizes, m_apoOverviewsBak))
    2859        5510 :             return;
    2860             :     }
    2861             : 
    2862          30 :     if (m_poMaskBand)
    2863             :     {
    2864          10 :         if (!CheckBandForOverview(m_poMaskBand.get(), poFirstBand, nOverviews,
    2865          10 :                                   oSetOvrSizes, m_apoOverviewsBak))
    2866           8 :             return;
    2867             :     }
    2868          22 :     if (poFirstBand == nullptr)
    2869             :     {
    2870             :         // to make cppcheck happy
    2871           0 :         CPLAssert(false);
    2872             :         return;
    2873             :     }
    2874             : 
    2875             :     VRTSourcedRasterBand *l_poVRTBand =
    2876          22 :         cpl::down_cast<VRTSourcedRasterBand *>(papoBands[0]);
    2877             :     VRTSimpleSource *poSource =
    2878          22 :         cpl::down_cast<VRTSimpleSource *>(l_poVRTBand->papoSources[0]);
    2879          22 :     const double dfDstToSrcXRatio =
    2880          22 :         poSource->m_dfDstXSize / poSource->m_dfSrcXSize;
    2881          22 :     const double dfDstToSrcYRatio =
    2882          22 :         poSource->m_dfDstYSize / poSource->m_dfSrcYSize;
    2883             : 
    2884          54 :     for (int j = 0; j < nOverviews; j++)
    2885             :     {
    2886          36 :         auto poOvrBand = poFirstBand->GetOverview(j);
    2887          36 :         if (!poOvrBand)
    2888           0 :             return;
    2889          36 :         const double dfXRatio = static_cast<double>(poOvrBand->GetXSize()) /
    2890          36 :                                 poFirstBand->GetXSize();
    2891          36 :         const double dfYRatio = static_cast<double>(poOvrBand->GetYSize()) /
    2892          36 :                                 poFirstBand->GetYSize();
    2893          36 :         if (dfXRatio >= dfDstToSrcXRatio || dfYRatio >= dfDstToSrcYRatio)
    2894             :         {
    2895           5 :             continue;
    2896             :         }
    2897          31 :         int nOvrXSize = static_cast<int>(0.5 + nRasterXSize * dfXRatio);
    2898          31 :         int nOvrYSize = static_cast<int>(0.5 + nRasterYSize * dfYRatio);
    2899          31 :         if (nOvrXSize < DEFAULT_BLOCK_SIZE || nOvrYSize < DEFAULT_BLOCK_SIZE)
    2900             :             break;
    2901             : 
    2902             :         // Look for a source overview whose size is very close to the
    2903             :         // theoretical computed one.
    2904          97 :         for (const auto &ovrSize : oSetOvrSizes)
    2905             :         {
    2906          86 :             if (std::abs(ovrSize.first - nOvrXSize) <= 1 &&
    2907          16 :                 std::abs(ovrSize.second - nOvrYSize) <= 1)
    2908             :             {
    2909          16 :                 nOvrXSize = ovrSize.first;
    2910          16 :                 nOvrYSize = ovrSize.second;
    2911          16 :                 break;
    2912             :             }
    2913             :         }
    2914             : 
    2915          27 :         int nBlockXSize = 0;
    2916          27 :         int nBlockYSize = 0;
    2917          27 :         l_poVRTBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2918          27 :         if (VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
    2919          10 :             nBlockXSize = 0;
    2920          27 :         if (VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
    2921          14 :             nBlockYSize = 0;
    2922             : 
    2923             :         VRTDataset *poOvrVDS =
    2924          27 :             new VRTDataset(nOvrXSize, nOvrYSize, nBlockXSize, nBlockYSize);
    2925          27 :         m_apoOverviews.push_back(poOvrVDS);
    2926             : 
    2927             :         const auto CreateOverviewBand =
    2928          75 :             [&poOvrVDS, nOvrXSize, nOvrYSize, dfXRatio,
    2929         150 :              dfYRatio](VRTSourcedRasterBand *poVRTBand)
    2930             :         {
    2931             :             auto poOvrVRTBand = std::make_unique<VRTSourcedRasterBand>(
    2932          75 :                 poOvrVDS, poVRTBand->GetBand(), poVRTBand->GetRasterDataType(),
    2933         150 :                 nOvrXSize, nOvrYSize);
    2934          75 :             poOvrVRTBand->CopyCommonInfoFrom(poVRTBand);
    2935          75 :             poOvrVRTBand->m_bNoDataValueSet = poVRTBand->m_bNoDataValueSet;
    2936          75 :             poOvrVRTBand->m_dfNoDataValue = poVRTBand->m_dfNoDataValue;
    2937          75 :             poOvrVRTBand->m_bHideNoDataValue = poVRTBand->m_bHideNoDataValue;
    2938             : 
    2939             :             VRTSimpleSource *poSrcSource =
    2940          75 :                 cpl::down_cast<VRTSimpleSource *>(poVRTBand->papoSources[0]);
    2941          75 :             VRTSimpleSource *poNewSource = nullptr;
    2942          75 :             const char *pszType = poSrcSource->GetType();
    2943          75 :             if (pszType == VRTSimpleSource::GetTypeStatic())
    2944             :             {
    2945          70 :                 poNewSource =
    2946          70 :                     new VRTSimpleSource(poSrcSource, dfXRatio, dfYRatio);
    2947             :             }
    2948           5 :             else if (pszType == VRTComplexSource::GetTypeStatic())
    2949             :             {
    2950           5 :                 poNewSource = new VRTComplexSource(
    2951           5 :                     cpl::down_cast<VRTComplexSource *>(poSrcSource), dfXRatio,
    2952           5 :                     dfYRatio);
    2953             :             }
    2954             :             else
    2955             :             {
    2956           0 :                 CPLAssert(false);
    2957             :             }
    2958          75 :             if (poNewSource)
    2959             :             {
    2960          75 :                 auto poNewSourceBand = poVRTBand->GetBand() == 0
    2961          75 :                                            ? poNewSource->GetMaskBandMainBand()
    2962          71 :                                            : poNewSource->GetRasterBand();
    2963          75 :                 CPLAssert(poNewSourceBand);
    2964          75 :                 auto poNewSourceBandDS = poNewSourceBand->GetDataset();
    2965          75 :                 if (poNewSourceBandDS)
    2966          75 :                     poNewSourceBandDS->Reference();
    2967          75 :                 poOvrVRTBand->AddSource(poNewSource);
    2968             :             }
    2969             : 
    2970          75 :             return poOvrVRTBand;
    2971          27 :         };
    2972             : 
    2973          98 :         for (int i = 0; i < nBands; i++)
    2974             :         {
    2975             :             VRTSourcedRasterBand *poSrcBand =
    2976          71 :                 cpl::down_cast<VRTSourcedRasterBand *>(GetRasterBand(i + 1));
    2977          71 :             poOvrVDS->SetBand(poOvrVDS->GetRasterCount() + 1,
    2978         142 :                               CreateOverviewBand(poSrcBand));
    2979             :         }
    2980             : 
    2981          27 :         if (m_poMaskBand)
    2982             :         {
    2983             :             VRTSourcedRasterBand *poSrcBand =
    2984           4 :                 cpl::down_cast<VRTSourcedRasterBand *>(m_poMaskBand.get());
    2985           4 :             poOvrVDS->SetMaskBand(CreateOverviewBand(poSrcBand));
    2986             :         }
    2987             :     }
    2988             : }
    2989             : 
    2990             : /************************************************************************/
    2991             : /*                        AddVirtualOverview()                          */
    2992             : /************************************************************************/
    2993             : 
    2994          36 : bool VRTDataset::AddVirtualOverview(int nOvFactor, const char *pszResampling)
    2995             : {
    2996          36 :     if (nRasterXSize / nOvFactor == 0 || nRasterYSize / nOvFactor == 0)
    2997             :     {
    2998           1 :         return false;
    2999             :     }
    3000             : 
    3001          70 :     CPLStringList argv;
    3002          35 :     argv.AddString("-of");
    3003          35 :     argv.AddString("VRT");
    3004          35 :     argv.AddString("-outsize");
    3005          35 :     argv.AddString(CPLSPrintf("%d", nRasterXSize / nOvFactor));
    3006          35 :     argv.AddString(CPLSPrintf("%d", nRasterYSize / nOvFactor));
    3007          35 :     argv.AddString("-r");
    3008          35 :     argv.AddString(pszResampling);
    3009             : 
    3010          35 :     int nBlockXSize = 0;
    3011          35 :     int nBlockYSize = 0;
    3012          35 :     GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    3013          35 :     if (!VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
    3014             :     {
    3015           6 :         argv.AddString("-co");
    3016           6 :         argv.AddString(CPLSPrintf("BLOCKXSIZE=%d", nBlockXSize));
    3017             :     }
    3018          35 :     if (!VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
    3019             :     {
    3020          10 :         argv.AddString("-co");
    3021          10 :         argv.AddString(CPLSPrintf("BLOCKYSIZE=%d", nBlockYSize));
    3022             :     }
    3023             : 
    3024             :     GDALTranslateOptions *psOptions =
    3025          35 :         GDALTranslateOptionsNew(argv.List(), nullptr);
    3026             : 
    3027             :     // Add a dummy overview so that BuildVirtualOverviews() doesn't trigger
    3028          35 :     m_apoOverviews.push_back(nullptr);
    3029          35 :     CPLAssert(m_bCanTakeRef);
    3030          35 :     m_bCanTakeRef =
    3031             :         false;  // we don't want hOverviewDS to take a reference on ourselves.
    3032             :     GDALDatasetH hOverviewDS =
    3033          35 :         GDALTranslate("", GDALDataset::ToHandle(this), psOptions, nullptr);
    3034          35 :     m_bCanTakeRef = true;
    3035          35 :     m_apoOverviews.pop_back();
    3036             : 
    3037          35 :     GDALTranslateOptionsFree(psOptions);
    3038          35 :     if (hOverviewDS == nullptr)
    3039           0 :         return false;
    3040             : 
    3041          35 :     m_anOverviewFactors.push_back(nOvFactor);
    3042          35 :     m_apoOverviews.push_back(GDALDataset::FromHandle(hOverviewDS));
    3043          35 :     return true;
    3044             : }
    3045             : 
    3046             : /************************************************************************/
    3047             : /*                          IBuildOverviews()                           */
    3048             : /************************************************************************/
    3049             : 
    3050          32 : CPLErr VRTDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3051             :                                    const int *panOverviewList, int nListBands,
    3052             :                                    const int *panBandList,
    3053             :                                    GDALProgressFunc pfnProgress,
    3054             :                                    void *pProgressData,
    3055             :                                    CSLConstList papszOptions)
    3056             : {
    3057          32 :     if (CPLTestBool(CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "NO")))
    3058             :     {
    3059          14 :         SetNeedsFlush();
    3060          27 :         if (nOverviews == 0 ||
    3061          13 :             (!m_apoOverviews.empty() && m_anOverviewFactors.empty()))
    3062             :         {
    3063           2 :             m_anOverviewFactors.clear();
    3064           2 :             m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
    3065             :                                      m_apoOverviews.begin(),
    3066           4 :                                      m_apoOverviews.end());
    3067           2 :             m_apoOverviews.clear();
    3068             :         }
    3069          14 :         m_osOverviewResampling = pszResampling;
    3070          38 :         for (int i = 0; i < nOverviews; i++)
    3071             :         {
    3072          24 :             if (std::find(m_anOverviewFactors.begin(),
    3073             :                           m_anOverviewFactors.end(),
    3074          24 :                           panOverviewList[i]) == m_anOverviewFactors.end())
    3075             :             {
    3076          24 :                 AddVirtualOverview(panOverviewList[i], pszResampling);
    3077             :             }
    3078             :         }
    3079          14 :         return CE_None;
    3080             :     }
    3081             : 
    3082          18 :     if (!oOvManager.IsInitialized())
    3083             :     {
    3084           0 :         const char *pszDesc = GetDescription();
    3085           0 :         if (pszDesc[0])
    3086             :         {
    3087           0 :             oOvManager.Initialize(this, pszDesc);
    3088             :         }
    3089             :     }
    3090             : 
    3091             :     // Make implicit overviews invisible, but do not destroy them in case they
    3092             :     // are already used.  Should the client do that?  Behavior might undefined
    3093             :     // in GDAL API?
    3094          18 :     if (!m_apoOverviews.empty())
    3095             :     {
    3096           2 :         m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
    3097           4 :                                  m_apoOverviews.begin(), m_apoOverviews.end());
    3098           2 :         m_apoOverviews.clear();
    3099             :     }
    3100             :     else
    3101             :     {
    3102             :         // Add a dummy overview so that GDALDataset::IBuildOverviews()
    3103             :         // doesn't manage to get a virtual implicit overview.
    3104          16 :         m_apoOverviews.push_back(nullptr);
    3105             :     }
    3106             : 
    3107          18 :     CPLErr eErr = GDALDataset::IBuildOverviews(
    3108             :         pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    3109             :         pfnProgress, pProgressData, papszOptions);
    3110             : 
    3111          18 :     m_apoOverviews.clear();
    3112          18 :     return eErr;
    3113             : }
    3114             : 
    3115             : /************************************************************************/
    3116             : /*                         GetShiftedDataset()                          */
    3117             : /*                                                                      */
    3118             : /* Returns true if the VRT is made of a single source that is a simple  */
    3119             : /* in its full resolution.                                              */
    3120             : /************************************************************************/
    3121             : 
    3122          50 : bool VRTDataset::GetShiftedDataset(int nXOff, int nYOff, int nXSize, int nYSize,
    3123             :                                    GDALDataset *&poSrcDataset, int &nSrcXOff,
    3124             :                                    int &nSrcYOff)
    3125             : {
    3126          50 :     if (!CheckCompatibleForDatasetIO())
    3127           0 :         return false;
    3128             : 
    3129          50 :     VRTSourcedRasterBand *poVRTBand =
    3130          50 :         static_cast<VRTSourcedRasterBand *>(papoBands[0]);
    3131          50 :     if (poVRTBand->nSources != 1)
    3132           0 :         return false;
    3133             : 
    3134          50 :     VRTSimpleSource *poSource =
    3135          50 :         static_cast<VRTSimpleSource *>(poVRTBand->papoSources[0]);
    3136             : 
    3137          50 :     GDALRasterBand *poBand = poSource->GetRasterBand();
    3138          50 :     if (!poBand || poSource->GetMaskBandMainBand())
    3139           4 :         return false;
    3140             : 
    3141          46 :     poSrcDataset = poBand->GetDataset();
    3142          46 :     if (!poSrcDataset)
    3143           0 :         return false;
    3144             : 
    3145          46 :     double dfReqXOff = 0.0;
    3146          46 :     double dfReqYOff = 0.0;
    3147          46 :     double dfReqXSize = 0.0;
    3148          46 :     double dfReqYSize = 0.0;
    3149          46 :     int nReqXOff = 0;
    3150          46 :     int nReqYOff = 0;
    3151          46 :     int nReqXSize = 0;
    3152          46 :     int nReqYSize = 0;
    3153          46 :     int nOutXOff = 0;
    3154          46 :     int nOutYOff = 0;
    3155          46 :     int nOutXSize = 0;
    3156          46 :     int nOutYSize = 0;
    3157          46 :     bool bError = false;
    3158          46 :     if (!poSource->GetSrcDstWindow(nXOff, nYOff, nXSize, nYSize, nXSize, nYSize,
    3159             :                                    &dfReqXOff, &dfReqYOff, &dfReqXSize,
    3160             :                                    &dfReqYSize, &nReqXOff, &nReqYOff,
    3161             :                                    &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    3162             :                                    &nOutXSize, &nOutYSize, bError))
    3163           0 :         return false;
    3164             : 
    3165          46 :     if (nReqXSize != nXSize || nReqYSize != nYSize || nReqXSize != nOutXSize ||
    3166          46 :         nReqYSize != nOutYSize)
    3167           0 :         return false;
    3168             : 
    3169          46 :     nSrcXOff = nReqXOff;
    3170          46 :     nSrcYOff = nReqYOff;
    3171          46 :     return true;
    3172             : }
    3173             : 
    3174             : /************************************************************************/
    3175             : /*                       GetCompressionFormats()                        */
    3176             : /************************************************************************/
    3177             : 
    3178           0 : CPLStringList VRTDataset::GetCompressionFormats(int nXOff, int nYOff,
    3179             :                                                 int nXSize, int nYSize,
    3180             :                                                 int nBandCount,
    3181             :                                                 const int *panBandList)
    3182             : {
    3183             :     GDALDataset *poSrcDataset;
    3184             :     int nSrcXOff;
    3185             :     int nSrcYOff;
    3186           0 :     if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
    3187             :                            nSrcYOff))
    3188           0 :         return CPLStringList();
    3189             :     return poSrcDataset->GetCompressionFormats(nSrcXOff, nSrcYOff, nXSize,
    3190           0 :                                                nYSize, nBandCount, panBandList);
    3191             : }
    3192             : 
    3193             : /************************************************************************/
    3194             : /*                       ReadCompressedData()                           */
    3195             : /************************************************************************/
    3196             : 
    3197          50 : CPLErr VRTDataset::ReadCompressedData(const char *pszFormat, int nXOff,
    3198             :                                       int nYOff, int nXSize, int nYSize,
    3199             :                                       int nBandCount, const int *panBandList,
    3200             :                                       void **ppBuffer, size_t *pnBufferSize,
    3201             :                                       char **ppszDetailedFormat)
    3202             : {
    3203             :     GDALDataset *poSrcDataset;
    3204             :     int nSrcXOff;
    3205             :     int nSrcYOff;
    3206          50 :     if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
    3207             :                            nSrcYOff))
    3208           4 :         return CE_Failure;
    3209          46 :     return poSrcDataset->ReadCompressedData(
    3210             :         pszFormat, nSrcXOff, nSrcYOff, nXSize, nYSize, nBandCount, panBandList,
    3211          46 :         ppBuffer, pnBufferSize, ppszDetailedFormat);
    3212             : }
    3213             : 
    3214             : /************************************************************************/
    3215             : /*                          ClearStatistics()                           */
    3216             : /************************************************************************/
    3217             : 
    3218           6 : void VRTDataset::ClearStatistics()
    3219             : {
    3220          18 :     for (int i = 1; i <= nBands; ++i)
    3221             :     {
    3222          12 :         bool bChanged = false;
    3223          12 :         GDALRasterBand *poBand = GetRasterBand(i);
    3224          12 :         CSLConstList papszOldMD = poBand->GetMetadata();
    3225          24 :         CPLStringList aosNewMD;
    3226          32 :         for (const char *pszMDItem : cpl::Iterate(papszOldMD))
    3227             :         {
    3228          20 :             if (STARTS_WITH_CI(pszMDItem, "STATISTICS_"))
    3229             :             {
    3230          20 :                 bChanged = true;
    3231             :             }
    3232             :             else
    3233             :             {
    3234           0 :                 aosNewMD.AddString(pszMDItem);
    3235             :             }
    3236             :         }
    3237          12 :         if (bChanged)
    3238             :         {
    3239           4 :             poBand->SetMetadata(aosNewMD.List());
    3240             :         }
    3241             :     }
    3242             : 
    3243           6 :     GDALDataset::ClearStatistics();
    3244           6 : }
    3245             : 
    3246             : /************************************************************************/
    3247             : /*                   VRTMapSharedResources::Get()                       */
    3248             : /************************************************************************/
    3249             : 
    3250      102106 : GDALDataset *VRTMapSharedResources::Get(const std::string &osKey) const
    3251             : {
    3252      102106 :     if (poMutex)
    3253         134 :         poMutex->lock();
    3254      102106 :     auto oIter = oMap.find(osKey);
    3255      102106 :     GDALDataset *poRet = nullptr;
    3256      102106 :     if (oIter != oMap.end())
    3257       98972 :         poRet = oIter->second;
    3258      102106 :     if (poMutex)
    3259         134 :         poMutex->unlock();
    3260      102106 :     return poRet;
    3261             : }
    3262             : 
    3263             : /************************************************************************/
    3264             : /*                   VRTMapSharedResources::Get()                       */
    3265             : /************************************************************************/
    3266             : 
    3267      101931 : void VRTMapSharedResources::Insert(const std::string &osKey, GDALDataset *poDS)
    3268             : {
    3269      101931 :     if (poMutex)
    3270         134 :         poMutex->lock();
    3271      101931 :     oMap[osKey] = poDS;
    3272      101931 :     if (poMutex)
    3273         134 :         poMutex->unlock();
    3274      101931 : }
    3275             : 
    3276             : /************************************************************************/
    3277             : /*                   VRTMapSharedResources::InitMutex()                 */
    3278             : /************************************************************************/
    3279             : 
    3280          20 : void VRTMapSharedResources::InitMutex()
    3281             : {
    3282          20 :     poMutex = &oMutex;
    3283          20 : }
    3284             : 
    3285             : /*! @endcond */

Generated by: LCOV version 1.14