LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1371 1507 91.0 %
Date: 2026-06-11 02:21:55 Functions: 61 64 95.3 %

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

Generated by: LCOV version 1.14