LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1255 1382 90.8 %
Date: 2025-03-29 15:49:56 Functions: 56 59 94.9 %

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

Generated by: LCOV version 1.14