LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1321 1451 91.0 %
Date: 2025-10-25 23:36:32 Functions: 61 64 95.3 %

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

Generated by: LCOV version 1.14