LCOV - code coverage report
Current view: top level - frmts/vrt - vrtdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1321 1451 91.0 %
Date: 2026-01-23 00:08:21 Functions: 61 64 95.3 %

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

Generated by: LCOV version 1.14