LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1287 1433 89.8 %
Date: 2025-01-18 12:42:00 Functions: 57 60 95.0 %

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

Generated by: LCOV version 1.14