LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1299 1504 86.4 %
Date: 2026-02-03 04:34:44 Functions: 45 51 88.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTSourcedRasterBand
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_vrt.h"
      16             : #include "vrtdataset.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cassert>
      20             : #include <cmath>
      21             : #include <cstddef>
      22             : #include <cstdio>
      23             : #include <cstdlib>
      24             : #include <cstring>
      25             : #include <limits>
      26             : #include <mutex>
      27             : #include <set>
      28             : #include <string>
      29             : 
      30             : #include "cpl_conv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_error_internal.h"
      33             : #include "cpl_hash_set.h"
      34             : #include "cpl_minixml.h"
      35             : #include "cpl_progress.h"
      36             : #include "cpl_quad_tree.h"
      37             : #include "cpl_string.h"
      38             : #include "cpl_vsi.h"
      39             : #include "gdal.h"
      40             : #include "gdalantirecursion.h"
      41             : #include "gdal_priv.h"
      42             : #include "gdal_thread_pool.h"
      43             : #include "ogr_geometry.h"
      44             : 
      45             : /*! @cond Doxygen_Suppress */
      46             : 
      47             : /************************************************************************/
      48             : /* ==================================================================== */
      49             : /*                          VRTSourcedRasterBand                        */
      50             : /* ==================================================================== */
      51             : /************************************************************************/
      52             : 
      53             : /************************************************************************/
      54             : /*                        VRTSourcedRasterBand()                        */
      55             : /************************************************************************/
      56             : 
      57        2658 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn)
      58             : {
      59        2658 :     VRTRasterBand::Initialize(poDSIn->GetRasterXSize(),
      60             :                               poDSIn->GetRasterYSize());
      61             : 
      62        2658 :     poDS = poDSIn;
      63        2658 :     nBand = nBandIn;
      64        2658 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                        VRTSourcedRasterBand()                        */
      68             : /************************************************************************/
      69             : 
      70           0 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataType eType, int nXSize,
      71           0 :                                            int nYSize)
      72             : {
      73           0 :     VRTRasterBand::Initialize(nXSize, nYSize);
      74             : 
      75           0 :     eDataType = eType;
      76           0 : }
      77             : 
      78             : /************************************************************************/
      79             : /*                        VRTSourcedRasterBand()                        */
      80             : /************************************************************************/
      81             : 
      82         325 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
      83             :                                            GDALDataType eType, int nXSize,
      84         325 :                                            int nYSize)
      85         325 :     : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize, 0, 0)
      86             : {
      87         325 : }
      88             : 
      89             : /************************************************************************/
      90             : /*                        VRTSourcedRasterBand()                        */
      91             : /************************************************************************/
      92             : 
      93      139247 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
      94             :                                            GDALDataType eType, int nXSize,
      95             :                                            int nYSize, int nBlockXSizeIn,
      96      139247 :                                            int nBlockYSizeIn)
      97             : {
      98      139247 :     VRTRasterBand::Initialize(nXSize, nYSize);
      99             : 
     100      139247 :     poDS = poDSIn;
     101      139247 :     nBand = nBandIn;
     102             : 
     103      139247 :     eDataType = eType;
     104      139247 :     if (nBlockXSizeIn > 0)
     105      138869 :         nBlockXSize = nBlockXSizeIn;
     106      139247 :     if (nBlockYSizeIn > 0)
     107      138887 :         nBlockYSize = nBlockYSizeIn;
     108      139247 : }
     109             : 
     110             : /************************************************************************/
     111             : /*                       ~VRTSourcedRasterBand()                        */
     112             : /************************************************************************/
     113             : 
     114      281548 : VRTSourcedRasterBand::~VRTSourcedRasterBand()
     115             : 
     116             : {
     117      141905 :     VRTSourcedRasterBand::CloseDependentDatasets();
     118      281548 : }
     119             : 
     120             : /************************************************************************/
     121             : /*                CanIRasterIOBeForwardedToEachSource()                 */
     122             : /************************************************************************/
     123             : 
     124      255301 : bool VRTSourcedRasterBand::CanIRasterIOBeForwardedToEachSource(
     125             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     126             :     int nBufXSize, int nBufYSize, const GDALRasterIOExtraArg *psExtraArg) const
     127             : {
     128       10369 :     const auto IsNonNearestInvolved = [this, psExtraArg]
     129             :     {
     130        5192 :         if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
     131             :         {
     132          15 :             return true;
     133             :         }
     134       14512 :         for (const auto &poSource : m_papoSources)
     135             :         {
     136        9343 :             if (poSource->GetType() == VRTComplexSource::GetTypeStatic())
     137             :             {
     138             :                 const auto *const poComplexSource =
     139        3425 :                     static_cast<const VRTComplexSource *>(poSource.get());
     140             :                 const auto &osSourceResampling =
     141        3425 :                     poComplexSource->GetResampling();
     142        3433 :                 if (!osSourceResampling.empty() &&
     143           8 :                     osSourceResampling != "nearest")
     144           8 :                     return true;
     145             :             }
     146             :         }
     147        5169 :         return false;
     148      255301 :     };
     149             : 
     150             :     // If resampling with non-nearest neighbour, we need to be careful
     151             :     // if the VRT band exposes a nodata value, but the sources do not have it.
     152             :     // To also avoid edge effects on sources when downsampling, use the
     153             :     // base implementation of IRasterIO() (that is acquiring sources at their
     154             :     // nominal resolution, and then downsampling), but only if none of the
     155             :     // contributing sources have overviews.
     156      255301 :     if (eRWFlag == GF_Read && (nXSize != nBufXSize || nYSize != nBufYSize) &&
     157      510602 :         !m_papoSources.empty() && IsNonNearestInvolved())
     158             :     {
     159          23 :         bool bSourceHasOverviews = false;
     160          23 :         const bool bIsDownsampling = (nBufXSize < nXSize && nBufYSize < nYSize);
     161          23 :         int nContributingSources = 0;
     162          23 :         bool bSourceFullySatisfiesRequest = true;
     163          37 :         for (auto &poSource : m_papoSources)
     164             :         {
     165          26 :             if (!poSource->IsSimpleSource())
     166             :             {
     167          12 :                 return false;
     168             :             }
     169             :             else
     170             :             {
     171             :                 VRTSimpleSource *const poSimpleSource =
     172          26 :                     static_cast<VRTSimpleSource *>(poSource.get());
     173             : 
     174          26 :                 if (poSimpleSource->GetType() ==
     175          26 :                     VRTComplexSource::GetTypeStatic())
     176             :                 {
     177          16 :                     auto *const poComplexSource =
     178             :                         static_cast<VRTComplexSource *>(poSimpleSource);
     179             :                     const auto &osSourceResampling =
     180          16 :                         poComplexSource->GetResampling();
     181          24 :                     if (!osSourceResampling.empty() &&
     182           8 :                         osSourceResampling != "nearest")
     183             :                     {
     184             :                         const int lMaskFlags =
     185             :                             const_cast<VRTSourcedRasterBand *>(this)
     186           8 :                                 ->GetMaskFlags();
     187           6 :                         if ((lMaskFlags != GMF_ALL_VALID &&
     188          16 :                              lMaskFlags != GMF_NODATA) ||
     189           2 :                             IsMaskBand())
     190             :                         {
     191             :                             // Unfortunately this will prevent using overviews
     192             :                             // of the sources, but it is unpractical to use
     193             :                             // them without serious implementation complications
     194          12 :                             return false;
     195             :                         }
     196             :                     }
     197             :                 }
     198             : 
     199          18 :                 double dfXOff = nXOff;
     200          18 :                 double dfYOff = nYOff;
     201          18 :                 double dfXSize = nXSize;
     202          18 :                 double dfYSize = nYSize;
     203          18 :                 if (psExtraArg->bFloatingPointWindowValidity)
     204             :                 {
     205           1 :                     dfXOff = psExtraArg->dfXOff;
     206           1 :                     dfYOff = psExtraArg->dfYOff;
     207           1 :                     dfXSize = psExtraArg->dfXSize;
     208           1 :                     dfYSize = psExtraArg->dfYSize;
     209             :                 }
     210             : 
     211             :                 // The window we will actually request from the source raster
     212             :                 // band.
     213          18 :                 double dfReqXOff = 0.0;
     214          18 :                 double dfReqYOff = 0.0;
     215          18 :                 double dfReqXSize = 0.0;
     216          18 :                 double dfReqYSize = 0.0;
     217          18 :                 int nReqXOff = 0;
     218          18 :                 int nReqYOff = 0;
     219          18 :                 int nReqXSize = 0;
     220          18 :                 int nReqYSize = 0;
     221             : 
     222             :                 // The window we will actual set _within_ the pData buffer.
     223          18 :                 int nOutXOff = 0;
     224          18 :                 int nOutYOff = 0;
     225          18 :                 int nOutXSize = 0;
     226          18 :                 int nOutYSize = 0;
     227             : 
     228          18 :                 bool bError = false;
     229          18 :                 if (!poSimpleSource->GetSrcDstWindow(
     230             :                         dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
     231          18 :                         psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
     232             :                         &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
     233             :                         &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
     234             :                         &nOutXSize, &nOutYSize, bError))
     235             :                 {
     236           0 :                     continue;
     237             :                 }
     238          18 :                 auto poBand = poSimpleSource->GetRasterBand();
     239          18 :                 if (poBand == nullptr)
     240             :                 {
     241           0 :                     return false;
     242             :                 }
     243          18 :                 ++nContributingSources;
     244          18 :                 if (!(nOutXOff == 0 && nOutYOff == 0 &&
     245          17 :                       nOutXSize == nBufXSize && nOutYSize == nBufYSize))
     246           4 :                     bSourceFullySatisfiesRequest = false;
     247          18 :                 if (m_bNoDataValueSet)
     248             :                 {
     249           7 :                     int bSrcHasNoData = FALSE;
     250             :                     const double dfSrcNoData =
     251           7 :                         poBand->GetNoDataValue(&bSrcHasNoData);
     252           7 :                     if (!bSrcHasNoData || dfSrcNoData != m_dfNoDataValue)
     253             :                     {
     254           4 :                         return false;
     255             :                     }
     256             :                 }
     257          14 :                 if (bIsDownsampling)
     258             :                 {
     259          11 :                     if (poBand->GetOverviewCount() != 0)
     260             :                     {
     261           4 :                         bSourceHasOverviews = true;
     262             :                     }
     263             :                 }
     264             :             }
     265             :         }
     266          11 :         if (bIsDownsampling && !bSourceHasOverviews &&
     267           3 :             (nContributingSources > 1 || !bSourceFullySatisfiesRequest))
     268             :         {
     269           2 :             return false;
     270             :         }
     271             :     }
     272      255287 :     return true;
     273             : }
     274             : 
     275             : /************************************************************************/
     276             : /*                       CanMultiThreadRasterIO()                       */
     277             : /************************************************************************/
     278             : 
     279         515 : bool VRTSourcedRasterBand::CanMultiThreadRasterIO(
     280             :     double dfXOff, double dfYOff, double dfXSize, double dfYSize,
     281             :     int &nContributingSources) const
     282             : {
     283         515 :     int iLastSource = 0;
     284             :     CPLRectObj sSourceBounds;
     285         515 :     CPLQuadTree *hQuadTree = nullptr;
     286         515 :     bool bRet = true;
     287         515 :     std::set<std::string> oSetDSName;
     288             : 
     289         515 :     nContributingSources = 0;
     290        1317 :     for (int iSource = 0; iSource < static_cast<int>(m_papoSources.size());
     291             :          iSource++)
     292             :     {
     293         806 :         const auto &poSource = m_papoSources[iSource];
     294         806 :         if (!poSource->IsSimpleSource())
     295             :         {
     296           0 :             bRet = false;
     297           0 :             break;
     298             :         }
     299             :         const auto poSimpleSource =
     300         806 :             cpl::down_cast<VRTSimpleSource *>(poSource.get());
     301         806 :         if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
     302             :                                                 dfYSize))
     303             :         {
     304             :             // Only build hQuadTree if there are 2 or more sources
     305         541 :             if (nContributingSources == 1)
     306             :             {
     307             :                 std::string &oFirstSrcDSName =
     308          28 :                     cpl::down_cast<VRTSimpleSource *>(
     309          28 :                         m_papoSources[iLastSource].get())
     310          28 :                         ->m_osSrcDSName;
     311          28 :                 oSetDSName.insert(oFirstSrcDSName);
     312             : 
     313             :                 CPLRectObj sGlobalBounds;
     314          28 :                 sGlobalBounds.minx = dfXOff;
     315          28 :                 sGlobalBounds.miny = dfYOff;
     316          28 :                 sGlobalBounds.maxx = dfXOff + dfXSize;
     317          28 :                 sGlobalBounds.maxy = dfYOff + dfYSize;
     318          28 :                 hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
     319             : 
     320          28 :                 CPLQuadTreeInsertWithBounds(
     321             :                     hQuadTree,
     322             :                     reinterpret_cast<void *>(
     323          28 :                         static_cast<uintptr_t>(iLastSource)),
     324             :                     &sSourceBounds);
     325             :             }
     326             : 
     327             :             // Check there are not several sources with the same name, to avoid
     328             :             // the same GDALDataset* to be used from multiple threads. We may
     329             :             // be a bit too pessimistic, for example if working with unnamed
     330             :             // Memory datasets, but that would involve comparing
     331             :             // poSource->GetRasterBandNoOpen()->GetDataset()
     332         541 :             if (oSetDSName.find(poSimpleSource->m_osSrcDSName) !=
     333        1082 :                 oSetDSName.end())
     334             :             {
     335           0 :                 bRet = false;
     336           4 :                 break;
     337             :             }
     338         541 :             oSetDSName.insert(poSimpleSource->m_osSrcDSName);
     339             : 
     340             :             double dfSourceXOff;
     341             :             double dfSourceYOff;
     342             :             double dfSourceXSize;
     343             :             double dfSourceYSize;
     344         541 :             poSimpleSource->GetDstWindow(dfSourceXOff, dfSourceYOff,
     345             :                                          dfSourceXSize, dfSourceYSize);
     346         541 :             constexpr double EPSILON = 1e-1;
     347             :             // We floor/ceil to detect potential overlaps of sources whose
     348             :             // destination offset is not integer. Otherwise, in case of
     349             :             // multithreading, this could cause one line to be written
     350             :             // concurrently by multiple threads.
     351         541 :             sSourceBounds.minx = std::floor(dfSourceXOff) + EPSILON;
     352         541 :             sSourceBounds.miny = std::floor(dfSourceYOff) + EPSILON;
     353         541 :             sSourceBounds.maxx =
     354         541 :                 std::ceil(dfSourceXOff + dfSourceXSize) - EPSILON;
     355         541 :             sSourceBounds.maxy =
     356         541 :                 std::ceil(dfSourceYOff + dfSourceYSize) - EPSILON;
     357         541 :             iLastSource = iSource;
     358             : 
     359         541 :             if (hQuadTree)
     360             :             {
     361             :                 // Check that the new source doesn't overlap an existing one.
     362         280 :                 if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
     363             :                 {
     364           4 :                     bRet = false;
     365           4 :                     break;
     366             :                 }
     367             : 
     368         276 :                 CPLQuadTreeInsertWithBounds(
     369             :                     hQuadTree,
     370         276 :                     reinterpret_cast<void *>(static_cast<uintptr_t>(iSource)),
     371             :                     &sSourceBounds);
     372             :             }
     373             : 
     374         537 :             ++nContributingSources;
     375             :         }
     376             :     }
     377             : 
     378         515 :     if (hQuadTree)
     379          28 :         CPLQuadTreeDestroy(hQuadTree);
     380             : 
     381        1030 :     return bRet;
     382             : }
     383             : 
     384             : /************************************************************************/
     385             : /*                   VRTSourcedRasterBandRasterIOJob                    */
     386             : /************************************************************************/
     387             : 
     388             : /** Structure used to declare a threaded job to satisfy IRasterIO()
     389             :  * on a given source.
     390             :  */
     391             : struct VRTSourcedRasterBandRasterIOJob
     392             : {
     393             :     std::atomic<int> *pnCompletedJobs = nullptr;
     394             :     std::atomic<bool> *pbSuccess = nullptr;
     395             :     VRTDataset::QueueWorkingStates *poQueueWorkingStates = nullptr;
     396             :     CPLErrorAccumulator *poErrorAccumulator = nullptr;
     397             : 
     398             :     GDALDataType eVRTBandDataType = GDT_Unknown;
     399             :     int nXOff = 0;
     400             :     int nYOff = 0;
     401             :     int nXSize = 0;
     402             :     int nYSize = 0;
     403             :     void *pData = nullptr;
     404             :     int nBufXSize = 0;
     405             :     int nBufYSize = 0;
     406             :     GDALDataType eBufType = GDT_Unknown;
     407             :     GSpacing nPixelSpace = 0;
     408             :     GSpacing nLineSpace = 0;
     409             :     GDALRasterIOExtraArg sExtraArg{};
     410             :     VRTSimpleSource *poSource = nullptr;
     411             : 
     412             :     static void Func(void *pData);
     413             : };
     414             : 
     415             : /************************************************************************/
     416             : /*               VRTSourcedRasterBandRasterIOJob::Func()                */
     417             : /************************************************************************/
     418             : 
     419          99 : void VRTSourcedRasterBandRasterIOJob::Func(void *pData)
     420             : {
     421             :     auto psJob = std::unique_ptr<VRTSourcedRasterBandRasterIOJob>(
     422         198 :         static_cast<VRTSourcedRasterBandRasterIOJob *>(pData));
     423          99 :     if (*psJob->pbSuccess)
     424             :     {
     425          99 :         psJob->sExtraArg.pfnProgress = nullptr;
     426          99 :         psJob->sExtraArg.pProgressData = nullptr;
     427             : 
     428          99 :         std::unique_ptr<VRTSource::WorkingState> poWorkingState;
     429             :         {
     430         198 :             std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
     431             :             poWorkingState =
     432          99 :                 std::move(psJob->poQueueWorkingStates->oStates.back());
     433          99 :             psJob->poQueueWorkingStates->oStates.pop_back();
     434          99 :             CPLAssert(poWorkingState.get());
     435             :         }
     436             : 
     437         198 :         auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
     438          99 :         CPL_IGNORE_RET_VAL(oAccumulator);
     439             : 
     440         198 :         if (psJob->poSource->RasterIO(
     441          99 :                 psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
     442          99 :                 psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
     443          99 :                 psJob->nBufYSize, psJob->eBufType, psJob->nPixelSpace,
     444          99 :                 psJob->nLineSpace, &psJob->sExtraArg,
     445         198 :                 *(poWorkingState.get())) != CE_None)
     446             :         {
     447           0 :             *psJob->pbSuccess = false;
     448             :         }
     449             : 
     450             :         {
     451         198 :             std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
     452         198 :             psJob->poQueueWorkingStates->oStates.push_back(
     453          99 :                 std::move(poWorkingState));
     454             :         }
     455             :     }
     456             : 
     457          99 :     ++(*psJob->pnCompletedJobs);
     458          99 : }
     459             : 
     460             : /************************************************************************/
     461             : /*                MayMultiBlockReadingBeMultiThreaded()                 */
     462             : /************************************************************************/
     463             : 
     464           0 : bool VRTSourcedRasterBand::MayMultiBlockReadingBeMultiThreaded() const
     465             : {
     466             :     GDALRasterIOExtraArg sExtraArg;
     467           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     468           0 :     sExtraArg.dfXOff = 0;
     469           0 :     sExtraArg.dfYOff = 0;
     470           0 :     sExtraArg.dfXSize = nRasterXSize;
     471           0 :     sExtraArg.dfYSize = nRasterYSize;
     472           0 :     int nContributingSources = 0;
     473           0 :     auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
     474           0 :     return l_poDS &&
     475           0 :            CanIRasterIOBeForwardedToEachSource(GF_Read, 0, 0, nRasterXSize,
     476           0 :                                                nRasterYSize, nRasterXSize,
     477           0 :                                                nRasterYSize, &sExtraArg) &&
     478           0 :            CanMultiThreadRasterIO(0, 0, nRasterXSize, nRasterYSize,
     479           0 :                                   nContributingSources) &&
     480           0 :            nContributingSources > 1 && VRTDataset::GetNumThreads(l_poDS) > 1;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*                             IRasterIO()                              */
     485             : /************************************************************************/
     486             : 
     487      255302 : CPLErr VRTSourcedRasterBand::IRasterIO(
     488             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     489             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     490             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
     491             : 
     492             : {
     493      255302 :     if (eRWFlag == GF_Write)
     494             :     {
     495           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     496             :                  "Writing through VRTSourcedRasterBand is not supported.");
     497           0 :         return CE_Failure;
     498             :     }
     499             : 
     500      510604 :     const std::string osFctId("VRTSourcedRasterBand::IRasterIO");
     501      510604 :     GDALAntiRecursionGuard oGuard(osFctId);
     502      255302 :     if (oGuard.GetCallDepth() >= 32)
     503             :     {
     504           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
     505           0 :         return CE_Failure;
     506             :     }
     507             : 
     508      765906 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
     509             :     // Allow 2 recursion depths on the same dataset for non-nearest resampling
     510      255302 :     if (oGuard2.GetCallDepth() > 2)
     511             :     {
     512           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
     513           1 :         return CE_Failure;
     514             :     }
     515             : 
     516             :     /* ==================================================================== */
     517             :     /*      Do we have overviews that would be appropriate to satisfy       */
     518             :     /*      this request?                                                   */
     519             :     /* ==================================================================== */
     520      255301 :     auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
     521      255301 :     if (l_poDS &&
     522      510251 :         l_poDS->m_apoOverviews.empty() &&  // do not use virtual overviews
     523      510602 :         (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
     524             :     {
     525           2 :         if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     526             :                              nBufXSize, nBufYSize, eBufType, nPixelSpace,
     527           2 :                              nLineSpace, psExtraArg) == CE_None)
     528           1 :             return CE_None;
     529             :     }
     530             : 
     531             :     // If resampling with non-nearest neighbour, we need to be careful
     532             :     // if the VRT band exposes a nodata value, but the sources do not have it.
     533             :     // To also avoid edge effects on sources when downsampling, use the
     534             :     // base implementation of IRasterIO() (that is acquiring sources at their
     535             :     // nominal resolution, and then downsampling), but only if none of the
     536             :     // contributing sources have overviews.
     537      255300 :     if (l_poDS && !CanIRasterIOBeForwardedToEachSource(
     538             :                       eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize,
     539             :                       nBufYSize, psExtraArg))
     540             :     {
     541          14 :         const bool bBackupEnabledOverviews = l_poDS->AreOverviewsEnabled();
     542          14 :         if (!l_poDS->m_apoOverviews.empty() && l_poDS->AreOverviewsEnabled())
     543             :         {
     544             :             // Disable use of implicit overviews to avoid infinite
     545             :             // recursion
     546           1 :             l_poDS->SetEnableOverviews(false);
     547             :         }
     548             : 
     549          14 :         const auto eResampleAlgBackup = psExtraArg->eResampleAlg;
     550          14 :         if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
     551             :         {
     552          16 :             std::string osResampling;
     553          24 :             for (auto &poSource : m_papoSources)
     554             :             {
     555          16 :                 if (poSource->GetType() == VRTComplexSource::GetTypeStatic())
     556             :                 {
     557             :                     auto *const poComplexSource =
     558          16 :                         static_cast<VRTComplexSource *>(poSource.get());
     559          16 :                     if (!poComplexSource->GetResampling().empty())
     560             :                     {
     561          16 :                         if (osResampling.empty())
     562           8 :                             osResampling = poComplexSource->GetResampling();
     563           8 :                         else if (osResampling !=
     564           8 :                                  poComplexSource->GetResampling())
     565             :                         {
     566           0 :                             osResampling.clear();
     567           0 :                             break;
     568             :                         }
     569             :                     }
     570             :                 }
     571             :             }
     572           8 :             if (!osResampling.empty())
     573           8 :                 psExtraArg->eResampleAlg =
     574           8 :                     GDALRasterIOGetResampleAlg(osResampling.c_str());
     575             :         }
     576             : 
     577          14 :         const auto eErr = GDALRasterBand::IRasterIO(
     578             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     579             :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     580             : 
     581          14 :         psExtraArg->eResampleAlg = eResampleAlgBackup;
     582          14 :         l_poDS->SetEnableOverviews(bBackupEnabledOverviews);
     583          14 :         return eErr;
     584             :     }
     585             : 
     586             :     /* -------------------------------------------------------------------- */
     587             :     /*      Initialize the buffer to some background value. Use the         */
     588             :     /*      nodata value if available.                                      */
     589             :     /* -------------------------------------------------------------------- */
     590      255286 :     if (SkipBufferInitialization())
     591             :     {
     592             :         // Do nothing
     593             :     }
     594       26442 :     else if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
     595       21848 :              !(m_bNoDataValueSet && m_dfNoDataValue != 0.0) &&
     596       69956 :              !(m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) &&
     597       21666 :              !(m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
     598             :     {
     599       21665 :         if (nLineSpace == nBufXSize * nPixelSpace)
     600             :         {
     601       21586 :             memset(pData, 0, static_cast<size_t>(nBufYSize * nLineSpace));
     602             :         }
     603             :         else
     604             :         {
     605       11850 :             for (int iLine = 0; iLine < nBufYSize; iLine++)
     606             :             {
     607       11771 :                 memset(static_cast<GByte *>(pData) +
     608       11771 :                            static_cast<GIntBig>(iLine) * nLineSpace,
     609       11771 :                        0, static_cast<size_t>(nBufXSize * nPixelSpace));
     610             :             }
     611             :         }
     612             :     }
     613        4777 :     else if (m_bNoDataSetAsInt64)
     614             :     {
     615           7 :         for (int iLine = 0; iLine < nBufYSize; iLine++)
     616             :         {
     617           6 :             GDALCopyWords(&m_nNoDataValueInt64, GDT_Int64, 0,
     618             :                           static_cast<GByte *>(pData) +
     619           6 :                               static_cast<GIntBig>(nLineSpace) * iLine,
     620             :                           eBufType, static_cast<int>(nPixelSpace), nBufXSize);
     621             :         }
     622             :     }
     623        4776 :     else if (m_bNoDataSetAsUInt64)
     624             :     {
     625           7 :         for (int iLine = 0; iLine < nBufYSize; iLine++)
     626             :         {
     627           6 :             GDALCopyWords(&m_nNoDataValueUInt64, GDT_UInt64, 0,
     628             :                           static_cast<GByte *>(pData) +
     629           6 :                               static_cast<GIntBig>(nLineSpace) * iLine,
     630             :                           eBufType, static_cast<int>(nPixelSpace), nBufXSize);
     631             :         }
     632             :     }
     633             :     else
     634             :     {
     635        4775 :         double dfWriteValue = 0.0;
     636        4775 :         if (m_bNoDataValueSet)
     637         193 :             dfWriteValue = m_dfNoDataValue;
     638             : 
     639       84903 :         for (int iLine = 0; iLine < nBufYSize; iLine++)
     640             :         {
     641       80128 :             GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
     642             :                           static_cast<GByte *>(pData) +
     643       80128 :                               static_cast<GIntBig>(nLineSpace) * iLine,
     644             :                           eBufType, static_cast<int>(nPixelSpace), nBufXSize);
     645             :         }
     646             :     }
     647             : 
     648             :     /* -------------------------------------------------------------------- */
     649             :     /*      Overlay each source in turn over top this.                      */
     650             :     /* -------------------------------------------------------------------- */
     651      255286 :     CPLErr eErr = CE_None;
     652             : 
     653      255286 :     double dfXOff = nXOff;
     654      255286 :     double dfYOff = nYOff;
     655      255286 :     double dfXSize = nXSize;
     656      255286 :     double dfYSize = nYSize;
     657      255286 :     if (psExtraArg->bFloatingPointWindowValidity)
     658             :     {
     659         295 :         dfXOff = psExtraArg->dfXOff;
     660         295 :         dfYOff = psExtraArg->dfYOff;
     661         295 :         dfXSize = psExtraArg->dfXSize;
     662         295 :         dfYSize = psExtraArg->dfYSize;
     663             :     }
     664             : 
     665      255286 :     if (l_poDS)
     666      255286 :         l_poDS->m_bMultiThreadedRasterIOLastUsed = false;
     667             : 
     668      255286 :     int nContributingSources = 0;
     669      255286 :     int nMaxThreads = 0;
     670      255286 :     constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
     671      510572 :     if (l_poDS &&
     672      255286 :         (static_cast<int64_t>(nBufXSize) * nBufYSize >=
     673      254953 :              MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
     674      254953 :          static_cast<int64_t>(nXSize) * nYSize >=
     675         340 :              MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
     676         340 :         CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
     677         337 :                                nContributingSources) &&
     678      510592 :         nContributingSources > 1 &&
     679          20 :         (nMaxThreads = VRTDataset::GetNumThreads(l_poDS)) > 1)
     680             :     {
     681          18 :         l_poDS->m_bMultiThreadedRasterIOLastUsed = true;
     682          18 :         l_poDS->m_oMapSharedSources.InitMutex();
     683             : 
     684          36 :         CPLErrorAccumulator errorAccumulator;
     685          18 :         std::atomic<bool> bSuccess = true;
     686          18 :         CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
     687          18 :             std::min(nContributingSources, nMaxThreads));
     688             :         const int nThreads =
     689          18 :             std::min(nContributingSources, psThreadPool->GetThreadCount());
     690          18 :         CPLDebugOnly("VRT",
     691             :                      "IRasterIO(): use optimized "
     692             :                      "multi-threaded code path for mosaic. "
     693             :                      "Using %d threads",
     694             :                      nThreads);
     695             : 
     696             :         {
     697          36 :             std::lock_guard oLock(l_poDS->m_oQueueWorkingStates.oMutex);
     698          18 :             if (l_poDS->m_oQueueWorkingStates.oStates.size() <
     699          18 :                 static_cast<size_t>(nThreads))
     700             :             {
     701           6 :                 l_poDS->m_oQueueWorkingStates.oStates.resize(nThreads);
     702             :             }
     703         117 :             for (int i = 0; i < nThreads; ++i)
     704             :             {
     705          99 :                 if (!l_poDS->m_oQueueWorkingStates.oStates[i])
     706          75 :                     l_poDS->m_oQueueWorkingStates.oStates[i] =
     707         150 :                         std::make_unique<VRTSource::WorkingState>();
     708             :             }
     709             :         }
     710             : 
     711          18 :         auto oQueue = psThreadPool->CreateJobQueue();
     712          18 :         std::atomic<int> nCompletedJobs = 0;
     713         180 :         for (auto &poSource : m_papoSources)
     714             :         {
     715         162 :             if (!poSource->IsSimpleSource())
     716           0 :                 continue;
     717             :             auto poSimpleSource =
     718         162 :                 cpl::down_cast<VRTSimpleSource *>(poSource.get());
     719         162 :             if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
     720             :                                                     dfYSize))
     721             :             {
     722          99 :                 auto psJob = new VRTSourcedRasterBandRasterIOJob();
     723          99 :                 psJob->pbSuccess = &bSuccess;
     724          99 :                 psJob->pnCompletedJobs = &nCompletedJobs;
     725          99 :                 psJob->poQueueWorkingStates = &(l_poDS->m_oQueueWorkingStates);
     726          99 :                 psJob->poErrorAccumulator = &errorAccumulator;
     727          99 :                 psJob->eVRTBandDataType = eDataType;
     728          99 :                 psJob->nXOff = nXOff;
     729          99 :                 psJob->nYOff = nYOff;
     730          99 :                 psJob->nXSize = nXSize;
     731          99 :                 psJob->nYSize = nYSize;
     732          99 :                 psJob->pData = pData;
     733          99 :                 psJob->nBufXSize = nBufXSize;
     734          99 :                 psJob->nBufYSize = nBufYSize;
     735          99 :                 psJob->eBufType = eBufType;
     736          99 :                 psJob->nPixelSpace = nPixelSpace;
     737          99 :                 psJob->nLineSpace = nLineSpace;
     738          99 :                 psJob->sExtraArg = *psExtraArg;
     739          99 :                 psJob->poSource = poSimpleSource;
     740             : 
     741          99 :                 if (!oQueue->SubmitJob(VRTSourcedRasterBandRasterIOJob::Func,
     742             :                                        psJob))
     743             :                 {
     744           0 :                     delete psJob;
     745           0 :                     bSuccess = false;
     746           0 :                     break;
     747             :                 }
     748             :             }
     749             :         }
     750             : 
     751          81 :         while (oQueue->WaitEvent())
     752             :         {
     753             :             // Quite rough progress callback. We could do better by counting
     754             :             // the number of contributing pixels.
     755          63 :             if (psExtraArg->pfnProgress)
     756             :             {
     757         102 :                 psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
     758             :                                             nContributingSources,
     759             :                                         "", psExtraArg->pProgressData);
     760             :             }
     761             :         }
     762             : 
     763          18 :         errorAccumulator.ReplayErrors();
     764          18 :         eErr = bSuccess ? CE_None : CE_Failure;
     765             :     }
     766             :     else
     767             :     {
     768      255268 :         GDALProgressFunc const pfnProgressGlobal = psExtraArg->pfnProgress;
     769      255268 :         void *const pProgressDataGlobal = psExtraArg->pProgressData;
     770             : 
     771      255268 :         VRTSource::WorkingState oWorkingState;
     772      255268 :         const int nSources = static_cast<int>(m_papoSources.size());
     773      522662 :         for (int iSource = 0; eErr == CE_None && iSource < nSources; iSource++)
     774             :         {
     775      267394 :             psExtraArg->pfnProgress = GDALScaledProgress;
     776      534788 :             psExtraArg->pProgressData = GDALCreateScaledProgress(
     777      267394 :                 1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
     778             :                 pfnProgressGlobal, pProgressDataGlobal);
     779      267394 :             if (psExtraArg->pProgressData == nullptr)
     780      266853 :                 psExtraArg->pfnProgress = nullptr;
     781             : 
     782      534788 :             eErr = m_papoSources[iSource]->RasterIO(
     783             :                 eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
     784             :                 nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
     785      267394 :                 l_poDS ? l_poDS->m_oWorkingState : oWorkingState);
     786             : 
     787      267394 :             GDALDestroyScaledProgress(psExtraArg->pProgressData);
     788             :         }
     789             : 
     790      255268 :         psExtraArg->pfnProgress = pfnProgressGlobal;
     791      255268 :         psExtraArg->pProgressData = pProgressDataGlobal;
     792             :     }
     793             : 
     794      255286 :     if (eErr == CE_None && psExtraArg->pfnProgress)
     795             :     {
     796         408 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
     797             :     }
     798             : 
     799      255286 :     return eErr;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                       IGetDataCoverageStatus()                       */
     804             : /************************************************************************/
     805             : 
     806        3014 : int VRTSourcedRasterBand::IGetDataCoverageStatus(int nXOff, int nYOff,
     807             :                                                  int nXSize, int nYSize,
     808             :                                                  int nMaskFlagStop,
     809             :                                                  double *pdfDataPct)
     810             : {
     811        3014 :     if (pdfDataPct)
     812           8 :         *pdfDataPct = -1.0;
     813             : 
     814             :     // Particular case for a single simple source covering the whole dataset
     815        6013 :     if (m_papoSources.size() == 1 && m_papoSources[0]->IsSimpleSource() &&
     816        2999 :         m_papoSources[0]->GetType() == VRTSimpleSource::GetTypeStatic())
     817             :     {
     818             :         VRTSimpleSource *poSource =
     819        2960 :             static_cast<VRTSimpleSource *>(m_papoSources[0].get());
     820             : 
     821        2960 :         GDALRasterBand *poBand = poSource->GetRasterBand();
     822        2960 :         if (!poBand)
     823           0 :             poBand = poSource->GetMaskBandMainBand();
     824        2960 :         if (!poBand)
     825             :         {
     826             :             return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
     827         344 :                    GDAL_DATA_COVERAGE_STATUS_DATA;
     828             :         }
     829             : 
     830             :         /* Check that it uses the full source dataset */
     831        2960 :         double dfReqXOff = 0.0;
     832        2960 :         double dfReqYOff = 0.0;
     833        2960 :         double dfReqXSize = 0.0;
     834        2960 :         double dfReqYSize = 0.0;
     835        2960 :         int nReqXOff = 0;
     836        2960 :         int nReqYOff = 0;
     837        2960 :         int nReqXSize = 0;
     838        2960 :         int nReqYSize = 0;
     839        2960 :         int nOutXOff = 0;
     840        2960 :         int nOutYOff = 0;
     841        2960 :         int nOutXSize = 0;
     842        2960 :         int nOutYSize = 0;
     843        2960 :         bool bError = false;
     844        2960 :         if (poSource->GetSrcDstWindow(
     845        2960 :                 0, 0, GetXSize(), GetYSize(), GetXSize(), GetYSize(),
     846             :                 GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
     847             :                 &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
     848        2960 :                 &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError) &&
     849        2960 :             nReqXOff == 0 && nReqYOff == 0 && nReqXSize == GetXSize() &&
     850         459 :             nReqXSize == poBand->GetXSize() && nReqYSize == GetYSize() &&
     851         353 :             nReqYSize == poBand->GetYSize() && nOutXOff == 0 && nOutYOff == 0 &&
     852        5920 :             nOutXSize == GetXSize() && nOutYSize == GetYSize())
     853             :         {
     854         344 :             return poBand->GetDataCoverageStatus(nXOff, nYOff, nXSize, nYSize,
     855         344 :                                                  nMaskFlagStop, pdfDataPct);
     856             :         }
     857             :     }
     858             : 
     859             : #ifndef HAVE_GEOS
     860             :     return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
     861             :            GDAL_DATA_COVERAGE_STATUS_DATA;
     862             : #else
     863        2670 :     int nStatus = 0;
     864             : 
     865        5340 :     auto poPolyNonCoveredBySources = std::make_unique<OGRPolygon>();
     866             :     {
     867        5340 :         auto poLR = std::make_unique<OGRLinearRing>();
     868        2670 :         poLR->addPoint(nXOff, nYOff);
     869        2670 :         poLR->addPoint(nXOff, nYOff + nYSize);
     870        2670 :         poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
     871        2670 :         poLR->addPoint(nXOff + nXSize, nYOff);
     872        2670 :         poLR->addPoint(nXOff, nYOff);
     873        2670 :         poPolyNonCoveredBySources->addRingDirectly(poLR.release());
     874             :     }
     875             : 
     876        2680 :     for (auto &poSource : m_papoSources)
     877             :     {
     878        2675 :         if (!poSource->IsSimpleSource())
     879             :         {
     880             :             return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
     881        2665 :                    GDAL_DATA_COVERAGE_STATUS_DATA;
     882             :         }
     883        2671 :         VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(poSource.get());
     884             :         // Check if the AOI is fully inside the source
     885        2671 :         double dfDstXOff = std::max(0.0, poSS->m_dfDstXOff);
     886        2671 :         double dfDstYOff = std::max(0.0, poSS->m_dfDstYOff);
     887        2671 :         double dfDstXSize = poSS->m_dfDstXSize;
     888        2671 :         double dfDstYSize = poSS->m_dfDstYSize;
     889        2671 :         auto l_poBand = poSS->GetRasterBand();
     890        2671 :         if (!l_poBand)
     891           0 :             continue;
     892        2671 :         if (dfDstXSize == -1)
     893           8 :             dfDstXSize = l_poBand->GetXSize() - dfDstXOff;
     894        2671 :         if (dfDstYSize == -1)
     895           8 :             dfDstYSize = l_poBand->GetYSize() - dfDstYOff;
     896             : 
     897        2671 :         if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
     898        2662 :             nXOff + nXSize <= dfDstXOff + dfDstXSize &&
     899        2638 :             nYOff + nYSize <= dfDstYOff + dfDstYSize)
     900             :         {
     901        2638 :             if (pdfDataPct)
     902           1 :                 *pdfDataPct = 100.0;
     903        2638 :             return GDAL_DATA_COVERAGE_STATUS_DATA;
     904             :         }
     905             :         // Check intersection of bounding boxes.
     906          33 :         if (dfDstXOff + dfDstXSize > nXOff && dfDstYOff + dfDstYSize > nYOff &&
     907          27 :             dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
     908             :         {
     909          25 :             nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
     910          25 :             if (poPolyNonCoveredBySources)
     911             :             {
     912          25 :                 OGRPolygon oPolySource;
     913          25 :                 auto poLR = std::make_unique<OGRLinearRing>();
     914          25 :                 poLR->addPoint(dfDstXOff, dfDstYOff);
     915          25 :                 poLR->addPoint(dfDstXOff, dfDstYOff + dfDstYSize);
     916          25 :                 poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff + dfDstYSize);
     917          25 :                 poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff);
     918          25 :                 poLR->addPoint(dfDstXOff, dfDstYOff);
     919          25 :                 oPolySource.addRingDirectly(poLR.release());
     920             :                 auto poRes = std::unique_ptr<OGRGeometry>(
     921          25 :                     poPolyNonCoveredBySources->Difference(&oPolySource));
     922          25 :                 if (poRes && poRes->IsEmpty())
     923             :                 {
     924           1 :                     if (pdfDataPct)
     925           1 :                         *pdfDataPct = 100.0;
     926           1 :                     return GDAL_DATA_COVERAGE_STATUS_DATA;
     927             :                 }
     928          24 :                 else if (poRes && poRes->getGeometryType() == wkbPolygon)
     929             :                 {
     930          24 :                     poPolyNonCoveredBySources.reset(
     931             :                         poRes.release()->toPolygon());
     932             :                 }
     933             :                 else
     934             :                 {
     935           0 :                     poPolyNonCoveredBySources.reset();
     936             :                 }
     937             :             }
     938             :         }
     939          32 :         if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
     940             :         {
     941          22 :             return nStatus;
     942             :         }
     943             :     }
     944           5 :     if (poPolyNonCoveredBySources)
     945             :     {
     946           5 :         if (!poPolyNonCoveredBySources->IsEmpty())
     947           5 :             nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
     948           5 :         if (pdfDataPct)
     949           2 :             *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
     950           2 :                                              nXSize / nYSize);
     951             :     }
     952           5 :     return nStatus;
     953             : #endif  // HAVE_GEOS
     954             : }
     955             : 
     956             : /************************************************************************/
     957             : /*                             IReadBlock()                             */
     958             : /************************************************************************/
     959             : 
     960         930 : CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     961             :                                         void *pImage)
     962             : 
     963             : {
     964         930 :     const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
     965             : 
     966         930 :     int nReadXSize = 0;
     967         930 :     if ((nBlockXOff + 1) * nBlockXSize > GetXSize())
     968          16 :         nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
     969             :     else
     970         914 :         nReadXSize = nBlockXSize;
     971             : 
     972         930 :     int nReadYSize = 0;
     973         930 :     if ((nBlockYOff + 1) * nBlockYSize > GetYSize())
     974           0 :         nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
     975             :     else
     976         930 :         nReadYSize = nBlockYSize;
     977             : 
     978             :     GDALRasterIOExtraArg sExtraArg;
     979         930 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     980             : 
     981         930 :     return IRasterIO(
     982         930 :         GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
     983             :         nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
     984        1860 :         static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*                          CPLGettimeofday()                           */
     989             : /************************************************************************/
     990             : 
     991             : #if defined(_WIN32) && !defined(__CYGWIN__)
     992             : #include <sys/timeb.h>
     993             : 
     994             : namespace
     995             : {
     996             : struct CPLTimeVal
     997             : {
     998             :     time_t tv_sec; /* seconds */
     999             :     long tv_usec;  /* and microseconds */
    1000             : };
    1001             : }  // namespace
    1002             : 
    1003             : static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
    1004             : {
    1005             :     struct _timeb theTime;
    1006             : 
    1007             :     _ftime(&theTime);
    1008             :     tp->tv_sec = static_cast<time_t>(theTime.time);
    1009             :     tp->tv_usec = theTime.millitm * 1000;
    1010             :     return 0;
    1011             : }
    1012             : #else
    1013             : #include <sys/time.h> /* for gettimeofday() */
    1014             : #define CPLTimeVal timeval
    1015             : #define CPLGettimeofday(t, u) gettimeofday(t, u)
    1016             : #endif
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                 CanUseSourcesMinMaxImplementations()                 */
    1020             : /************************************************************************/
    1021             : 
    1022         109 : bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
    1023             : {
    1024             :     const char *pszUseSources =
    1025         109 :         CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", nullptr);
    1026         109 :     if (pszUseSources)
    1027           0 :         return CPLTestBool(pszUseSources);
    1028             : 
    1029             :     // Use heuristics to determine if we are going to use the source
    1030             :     // GetMinimum() or GetMaximum() implementation: all the sources must be
    1031             :     // "simple" sources with a dataset description that match a "regular" file
    1032             :     // on the filesystem, whose open time and GetMinimum()/GetMaximum()
    1033             :     // implementations we hope to be fast enough.
    1034             :     // In case of doubt return FALSE.
    1035             :     struct CPLTimeVal tvStart;
    1036         109 :     memset(&tvStart, 0, sizeof(CPLTimeVal));
    1037         109 :     if (m_papoSources.size() > 1)
    1038          10 :         CPLGettimeofday(&tvStart, nullptr);
    1039         237 :     for (auto &poSource : m_papoSources)
    1040             :     {
    1041         131 :         if (!(poSource->IsSimpleSource()))
    1042           3 :             return false;
    1043             :         VRTSimpleSource *const poSimpleSource =
    1044         129 :             static_cast<VRTSimpleSource *>(poSource.get());
    1045         129 :         const char *pszFilename = poSimpleSource->m_osSrcDSName.c_str();
    1046             :         // /vsimem/ should be fast.
    1047         129 :         if (STARTS_WITH(pszFilename, "/vsimem/"))
    1048          10 :             continue;
    1049             :         // but not other /vsi filesystems
    1050         119 :         if (STARTS_WITH(pszFilename, "/vsi"))
    1051           0 :             return false;
    1052         119 :         char ch = '\0';
    1053             :         // We will assume that filenames that are only with ascii characters
    1054             :         // are real filenames and so we will not try to 'stat' them.
    1055        1789 :         for (int i = 0; (ch = pszFilename[i]) != '\0'; i++)
    1056             :         {
    1057        1702 :             if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
    1058         399 :                   (ch >= '0' && ch <= '9') || ch == ':' || ch == '/' ||
    1059         137 :                   ch == '\\' || ch == ' ' || ch == '.' || ch == '_'))
    1060          13 :                 break;
    1061             :         }
    1062         119 :         if (ch != '\0')
    1063             :         {
    1064             :             // Otherwise do a real filesystem check.
    1065             :             VSIStatBuf sStat;
    1066          13 :             if (VSIStat(pszFilename, &sStat) != 0)
    1067           1 :                 return false;
    1068          12 :             if (m_papoSources.size() > 1)
    1069             :             {
    1070             :                 struct CPLTimeVal tvCur;
    1071          10 :                 CPLGettimeofday(&tvCur, nullptr);
    1072          10 :                 if (tvCur.tv_sec - tvStart.tv_sec +
    1073          10 :                         (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
    1074             :                     1)
    1075           0 :                     return false;
    1076             :             }
    1077             :         }
    1078             :     }
    1079         106 :     return true;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                             GetMinimum()                             */
    1084             : /************************************************************************/
    1085             : 
    1086          60 : double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
    1087             : {
    1088          60 :     const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
    1089          60 :     if (pszValue != nullptr)
    1090             :     {
    1091           5 :         if (pbSuccess != nullptr)
    1092           5 :             *pbSuccess = TRUE;
    1093             : 
    1094           5 :         return CPLAtofM(pszValue);
    1095             :     }
    1096             : 
    1097          55 :     if (!CanUseSourcesMinMaxImplementations())
    1098           2 :         return GDALRasterBand::GetMinimum(pbSuccess);
    1099             : 
    1100         106 :     const std::string osFctId("VRTSourcedRasterBand::GetMinimum");
    1101         106 :     GDALAntiRecursionGuard oGuard(osFctId);
    1102          53 :     if (oGuard.GetCallDepth() >= 32)
    1103             :     {
    1104           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1105           0 :         if (pbSuccess != nullptr)
    1106           0 :             *pbSuccess = FALSE;
    1107           0 :         return 0;
    1108             :     }
    1109             : 
    1110         159 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    1111          53 :     if (oGuard2.GetCallDepth() >= 2)
    1112             :     {
    1113           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1114           0 :         if (pbSuccess != nullptr)
    1115           0 :             *pbSuccess = FALSE;
    1116           0 :         return 0;
    1117             :     }
    1118             : 
    1119             :     struct CPLTimeVal tvStart;
    1120          53 :     memset(&tvStart, 0, sizeof(CPLTimeVal));
    1121          53 :     if (m_papoSources.size() > 1)
    1122           5 :         CPLGettimeofday(&tvStart, nullptr);
    1123          53 :     double dfMin = 0;
    1124          58 :     for (size_t iSource = 0; iSource < m_papoSources.size(); iSource++)
    1125             :     {
    1126          53 :         int bSuccess = FALSE;
    1127         106 :         double dfSourceMin = m_papoSources[iSource]->GetMinimum(
    1128          53 :             GetXSize(), GetYSize(), &bSuccess);
    1129          53 :         if (!bSuccess)
    1130             :         {
    1131          48 :             dfMin = GDALRasterBand::GetMinimum(pbSuccess);
    1132          48 :             return dfMin;
    1133             :         }
    1134             : 
    1135           5 :         if (iSource == 0 || dfSourceMin < dfMin)
    1136             :         {
    1137           4 :             dfMin = dfSourceMin;
    1138           4 :             if (dfMin == 0 && eDataType == GDT_UInt8)
    1139           0 :                 break;
    1140             :         }
    1141           5 :         if (m_papoSources.size() > 1)
    1142             :         {
    1143             :             struct CPLTimeVal tvCur;
    1144           2 :             CPLGettimeofday(&tvCur, nullptr);
    1145           2 :             if (tvCur.tv_sec - tvStart.tv_sec +
    1146           2 :                     (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
    1147             :                 1)
    1148             :             {
    1149           0 :                 return GDALRasterBand::GetMinimum(pbSuccess);
    1150             :             }
    1151             :         }
    1152             :     }
    1153             : 
    1154           5 :     if (pbSuccess != nullptr)
    1155           5 :         *pbSuccess = TRUE;
    1156             : 
    1157           5 :     return dfMin;
    1158             : }
    1159             : 
    1160             : /************************************************************************/
    1161             : /*                             GetMaximum()                             */
    1162             : /************************************************************************/
    1163             : 
    1164          58 : double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
    1165             : {
    1166          58 :     const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
    1167          58 :     if (pszValue != nullptr)
    1168             :     {
    1169           4 :         if (pbSuccess != nullptr)
    1170           4 :             *pbSuccess = TRUE;
    1171             : 
    1172           4 :         return CPLAtofM(pszValue);
    1173             :     }
    1174             : 
    1175          54 :     if (!CanUseSourcesMinMaxImplementations())
    1176           1 :         return GDALRasterBand::GetMaximum(pbSuccess);
    1177             : 
    1178         106 :     const std::string osFctId("VRTSourcedRasterBand::GetMaximum");
    1179         106 :     GDALAntiRecursionGuard oGuard(osFctId);
    1180          53 :     if (oGuard.GetCallDepth() >= 32)
    1181             :     {
    1182           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1183           0 :         if (pbSuccess != nullptr)
    1184           0 :             *pbSuccess = FALSE;
    1185           0 :         return 0;
    1186             :     }
    1187             : 
    1188         159 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    1189          53 :     if (oGuard2.GetCallDepth() >= 2)
    1190             :     {
    1191           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1192           0 :         if (pbSuccess != nullptr)
    1193           0 :             *pbSuccess = FALSE;
    1194           0 :         return 0;
    1195             :     }
    1196             : 
    1197             :     struct CPLTimeVal tvStart;
    1198          53 :     memset(&tvStart, 0, sizeof(CPLTimeVal));
    1199          53 :     if (m_papoSources.size() > 1)
    1200           5 :         CPLGettimeofday(&tvStart, nullptr);
    1201          53 :     double dfMax = 0;
    1202          55 :     for (size_t iSource = 0; iSource < m_papoSources.size(); iSource++)
    1203             :     {
    1204          52 :         int bSuccess = FALSE;
    1205         104 :         const double dfSourceMax = m_papoSources[iSource]->GetMaximum(
    1206          52 :             GetXSize(), GetYSize(), &bSuccess);
    1207          52 :         if (!bSuccess)
    1208             :         {
    1209          48 :             dfMax = GDALRasterBand::GetMaximum(pbSuccess);
    1210          48 :             return dfMax;
    1211             :         }
    1212             : 
    1213           4 :         if (iSource == 0 || dfSourceMax > dfMax)
    1214             :         {
    1215           4 :             dfMax = dfSourceMax;
    1216           4 :             if (dfMax == 255.0 && eDataType == GDT_UInt8)
    1217           2 :                 break;
    1218             :         }
    1219           2 :         if (m_papoSources.size() > 1)
    1220             :         {
    1221             :             struct CPLTimeVal tvCur;
    1222           0 :             CPLGettimeofday(&tvCur, nullptr);
    1223           0 :             if (tvCur.tv_sec - tvStart.tv_sec +
    1224           0 :                     (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
    1225             :                 1)
    1226             :             {
    1227           0 :                 return GDALRasterBand::GetMaximum(pbSuccess);
    1228             :             }
    1229             :         }
    1230             :     }
    1231             : 
    1232           5 :     if (pbSuccess != nullptr)
    1233           5 :         *pbSuccess = TRUE;
    1234             : 
    1235           5 :     return dfMax;
    1236             : }
    1237             : 
    1238             : /************************************************************************/
    1239             : /*IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange() */
    1240             : /************************************************************************/
    1241             : 
    1242             : /* Returns true if the VRT raster band consists of non-overlapping simple
    1243             :  * sources or complex sources that don't change values, and use the full extent
    1244             :  * of the source band.
    1245             :  */
    1246          54 : bool VRTSourcedRasterBand::
    1247             :     IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
    1248             :         bool bAllowMaxValAdjustment) const
    1249             : {
    1250          54 :     bool bRet = true;
    1251             :     CPLRectObj sGlobalBounds;
    1252          54 :     sGlobalBounds.minx = 0;
    1253          54 :     sGlobalBounds.miny = 0;
    1254          54 :     sGlobalBounds.maxx = nRasterXSize;
    1255          54 :     sGlobalBounds.maxy = nRasterYSize;
    1256          54 :     CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
    1257         124 :     for (int i = 0; i < static_cast<int>(m_papoSources.size()); ++i)
    1258             :     {
    1259          79 :         if (!m_papoSources[i]->IsSimpleSource())
    1260             :         {
    1261           1 :             bRet = false;
    1262           9 :             break;
    1263             :         }
    1264             : 
    1265             :         auto poSimpleSource =
    1266          78 :             cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
    1267          78 :         const char *pszType = poSimpleSource->GetType();
    1268          78 :         if (pszType == VRTSimpleSource::GetTypeStatic())
    1269             :         {
    1270             :             // ok
    1271             :         }
    1272           5 :         else if (pszType == VRTComplexSource::GetTypeStatic())
    1273             :         {
    1274             :             auto poComplexSource =
    1275           5 :                 cpl::down_cast<VRTComplexSource *>(poSimpleSource);
    1276           5 :             if (!poComplexSource->AreValuesUnchanged())
    1277             :             {
    1278           2 :                 bRet = false;
    1279           2 :                 break;
    1280             :             }
    1281             :         }
    1282             :         else
    1283             :         {
    1284           0 :             bRet = false;
    1285           0 :             break;
    1286             :         }
    1287             : 
    1288          76 :         if (!bAllowMaxValAdjustment && poSimpleSource->NeedMaxValAdjustment())
    1289             :         {
    1290           2 :             bRet = false;
    1291           2 :             break;
    1292             :         }
    1293             : 
    1294          74 :         auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    1295         148 :         if (poSimpleSourceBand == nullptr ||
    1296          74 :             poSimpleSourceBand->GetRasterDataType() != eDataType)
    1297             :         {
    1298           0 :             bRet = false;
    1299           0 :             break;
    1300             :         }
    1301             : 
    1302          74 :         double dfReqXOff = 0.0;
    1303          74 :         double dfReqYOff = 0.0;
    1304          74 :         double dfReqXSize = 0.0;
    1305          74 :         double dfReqYSize = 0.0;
    1306          74 :         int nReqXOff = 0;
    1307          74 :         int nReqYOff = 0;
    1308          74 :         int nReqXSize = 0;
    1309          74 :         int nReqYSize = 0;
    1310          74 :         int nOutXOff = 0;
    1311          74 :         int nOutYOff = 0;
    1312          74 :         int nOutXSize = 0;
    1313          74 :         int nOutYSize = 0;
    1314             : 
    1315          74 :         bool bError = false;
    1316         222 :         if (!poSimpleSource->GetSrcDstWindow(
    1317          74 :                 0, 0, nRasterXSize, nRasterYSize, nRasterXSize, nRasterYSize,
    1318             :                 GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
    1319             :                 &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
    1320          74 :                 &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError) ||
    1321          74 :             nReqXOff != 0 || nReqYOff != 0 ||
    1322          73 :             nReqXSize != poSimpleSourceBand->GetXSize() ||
    1323          73 :             nReqYSize != poSimpleSourceBand->GetYSize() ||
    1324         148 :             nOutXSize != nReqXSize || nOutYSize != nReqYSize)
    1325             :         {
    1326           1 :             bRet = false;
    1327           1 :             break;
    1328             :         }
    1329             : 
    1330             :         CPLRectObj sBounds;
    1331          73 :         constexpr double EPSILON = 1e-1;
    1332          73 :         sBounds.minx = nOutXOff + EPSILON;
    1333          73 :         sBounds.miny = nOutYOff + EPSILON;
    1334          73 :         sBounds.maxx = nOutXOff + nOutXSize - EPSILON;
    1335          73 :         sBounds.maxy = nOutYOff + nOutYSize - EPSILON;
    1336             : 
    1337             :         // Check that the new source doesn't overlap an existing one.
    1338          73 :         if (CPLQuadTreeHasMatch(hQuadTree, &sBounds))
    1339             :         {
    1340           3 :             bRet = false;
    1341           3 :             break;
    1342             :         }
    1343             : 
    1344          70 :         CPLQuadTreeInsertWithBounds(
    1345          70 :             hQuadTree, reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
    1346             :             &sBounds);
    1347             :     }
    1348          54 :     CPLQuadTreeDestroy(hQuadTree);
    1349             : 
    1350          54 :     return bRet;
    1351             : }
    1352             : 
    1353             : /************************************************************************/
    1354             : /*                        ComputeRasterMinMax()                         */
    1355             : /************************************************************************/
    1356             : 
    1357          22 : CPLErr VRTSourcedRasterBand::ComputeRasterMinMax(int bApproxOK,
    1358             :                                                  double *adfMinMax)
    1359             : {
    1360             :     /* -------------------------------------------------------------------- */
    1361             :     /*      Does the driver already know the min/max?                       */
    1362             :     /* -------------------------------------------------------------------- */
    1363          22 :     if (bApproxOK)
    1364             :     {
    1365           3 :         int bSuccessMin = FALSE;
    1366           3 :         int bSuccessMax = FALSE;
    1367             : 
    1368           3 :         const double dfMin = GetMinimum(&bSuccessMin);
    1369           3 :         const double dfMax = GetMaximum(&bSuccessMax);
    1370             : 
    1371           3 :         if (bSuccessMin && bSuccessMax)
    1372             :         {
    1373           1 :             adfMinMax[0] = dfMin;
    1374           1 :             adfMinMax[1] = dfMax;
    1375           1 :             return CE_None;
    1376             :         }
    1377             :     }
    1378             : 
    1379          42 :     const std::string osFctId("VRTSourcedRasterBand::ComputeRasterMinMax");
    1380          42 :     GDALAntiRecursionGuard oGuard(osFctId);
    1381          21 :     if (oGuard.GetCallDepth() >= 32)
    1382             :     {
    1383           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1384           0 :         return CE_Failure;
    1385             :     }
    1386             : 
    1387          63 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    1388          21 :     if (oGuard2.GetCallDepth() >= 2)
    1389             :     {
    1390           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1391           0 :         return CE_Failure;
    1392             :     }
    1393             : 
    1394             :     /* -------------------------------------------------------------------- */
    1395             :     /*      If we have overview bands, use them for min/max.                */
    1396             :     /* -------------------------------------------------------------------- */
    1397          21 :     if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
    1398             :     {
    1399             :         GDALRasterBand *const poBand =
    1400           0 :             GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
    1401             : 
    1402           0 :         if (poBand != nullptr && poBand != this)
    1403             :         {
    1404           0 :             auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
    1405           0 :             if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
    1406           0 :                 dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
    1407             :             {
    1408           0 :                 auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
    1409           0 :                 l_poDS->m_apoOverviews.clear();
    1410           0 :                 auto eErr = poBand->GDALRasterBand::ComputeRasterMinMax(
    1411             :                     TRUE, adfMinMax);
    1412           0 :                 l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
    1413           0 :                 return eErr;
    1414             :             }
    1415             :             else
    1416             :             {
    1417           0 :                 return poBand->ComputeRasterMinMax(TRUE, adfMinMax);
    1418             :             }
    1419             :         }
    1420             :     }
    1421             : 
    1422          21 :     if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
    1423             :             /*bAllowMaxValAdjustment = */ true))
    1424             :     {
    1425          18 :         CPLDebugOnly(
    1426             :             "VRT", "ComputeRasterMinMax(): use optimized code path for mosaic");
    1427             : 
    1428          18 :         uint64_t nCoveredArea = 0;
    1429             : 
    1430             :         // If source bands have nodata value, we can't use source band's
    1431             :         // ComputeRasterMinMax() as we don't know if there are pixels actually
    1432             :         // at the nodata value, so use ComputeStatistics() instead that takes
    1433             :         // into account that aspect.
    1434          18 :         bool bUseComputeStatistics = false;
    1435          37 :         for (auto &poSource : m_papoSources)
    1436             :         {
    1437             :             auto poSimpleSource =
    1438          23 :                 cpl::down_cast<VRTSimpleSource *>(poSource.get());
    1439          23 :             auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    1440             :             // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
    1441          23 :             assert(poSimpleSourceBand);
    1442          23 :             int bHasNoData = FALSE;
    1443          23 :             CPL_IGNORE_RET_VAL(poSimpleSourceBand->GetNoDataValue(&bHasNoData));
    1444          23 :             if (bHasNoData)
    1445             :             {
    1446           4 :                 bUseComputeStatistics = true;
    1447           4 :                 break;
    1448             :             }
    1449          19 :             nCoveredArea +=
    1450          19 :                 static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
    1451          19 :                 poSimpleSourceBand->GetYSize();
    1452             :         }
    1453             : 
    1454          18 :         if (bUseComputeStatistics)
    1455             :         {
    1456             :             CPLErr eErr;
    1457           4 :             std::string osLastErrorMsg;
    1458             :             {
    1459           8 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1460           4 :                 CPLErrorReset();
    1461             :                 eErr =
    1462           8 :                     ComputeStatistics(bApproxOK, &adfMinMax[0], &adfMinMax[1],
    1463           4 :                                       nullptr, nullptr, nullptr, nullptr);
    1464           4 :                 if (eErr == CE_Failure)
    1465             :                 {
    1466           1 :                     osLastErrorMsg = CPLGetLastErrorMsg();
    1467             :                 }
    1468             :             }
    1469           4 :             if (eErr == CE_Failure)
    1470             :             {
    1471           1 :                 if (strstr(osLastErrorMsg.c_str(), "no valid pixels found") !=
    1472             :                     nullptr)
    1473             :                 {
    1474           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    1475             :                                 "Failed to compute min/max, no valid pixels "
    1476             :                                 "found in sampling.");
    1477             :                 }
    1478             :                 else
    1479             :                 {
    1480           0 :                     ReportError(CE_Failure, CPLE_AppDefined, "%s",
    1481             :                                 osLastErrorMsg.c_str());
    1482             :                 }
    1483             :             }
    1484           4 :             return eErr;
    1485             :         }
    1486             : 
    1487          14 :         bool bSignedByte = false;
    1488          14 :         if (eDataType == GDT_UInt8)
    1489             :         {
    1490           8 :             EnablePixelTypeSignedByteWarning(false);
    1491             :             const char *pszPixelType =
    1492           8 :                 GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    1493           8 :             EnablePixelTypeSignedByteWarning(true);
    1494           8 :             bSignedByte =
    1495           8 :                 pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE");
    1496             :         }
    1497             : 
    1498          14 :         double dfGlobalMin = std::numeric_limits<double>::max();
    1499          14 :         double dfGlobalMax = -std::numeric_limits<double>::max();
    1500             : 
    1501             :         // If the mosaic doesn't cover the whole VRT raster, take into account
    1502             :         // VRT nodata value.
    1503          14 :         if (nCoveredArea < static_cast<uint64_t>(nRasterXSize) * nRasterYSize)
    1504             :         {
    1505           2 :             if (m_bNoDataValueSet && m_bHideNoDataValue)
    1506             :             {
    1507           1 :                 if (IsNoDataValueInDataTypeRange())
    1508             :                 {
    1509           1 :                     dfGlobalMin = std::min(dfGlobalMin, m_dfNoDataValue);
    1510           1 :                     dfGlobalMax = std::max(dfGlobalMax, m_dfNoDataValue);
    1511             :                 }
    1512             :             }
    1513           1 :             else if (!m_bNoDataValueSet)
    1514             :             {
    1515           0 :                 dfGlobalMin = std::min(dfGlobalMin, 0.0);
    1516           0 :                 dfGlobalMax = std::max(dfGlobalMax, 0.0);
    1517             :             }
    1518             :         }
    1519             : 
    1520          31 :         for (auto &poSource : m_papoSources)
    1521             :         {
    1522             :             auto poSimpleSource =
    1523          18 :                 cpl::down_cast<VRTSimpleSource *>(poSource.get());
    1524          18 :             double adfMinMaxSource[2] = {0};
    1525             : 
    1526          18 :             auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    1527             :             // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
    1528          18 :             assert(poSimpleSourceBand);
    1529          36 :             CPLErr eErr = poSimpleSourceBand->ComputeRasterMinMax(
    1530          18 :                 bApproxOK, adfMinMaxSource);
    1531          18 :             if (eErr == CE_Failure)
    1532             :             {
    1533           1 :                 return CE_Failure;
    1534             :             }
    1535             :             else
    1536             :             {
    1537          18 :                 if (poSimpleSource->NeedMaxValAdjustment())
    1538             :                 {
    1539           2 :                     const double dfMaxValue =
    1540           2 :                         static_cast<double>(poSimpleSource->m_nMaxValue);
    1541           2 :                     adfMinMaxSource[0] =
    1542           2 :                         std::min(adfMinMaxSource[0], dfMaxValue);
    1543           2 :                     adfMinMaxSource[1] =
    1544           2 :                         std::min(adfMinMaxSource[1], dfMaxValue);
    1545             :                 }
    1546             : 
    1547          18 :                 if (m_bNoDataValueSet && !m_bHideNoDataValue &&
    1548           3 :                     m_dfNoDataValue >= adfMinMaxSource[0] &&
    1549           1 :                     m_dfNoDataValue <= adfMinMaxSource[1])
    1550             :                 {
    1551           1 :                     return GDALRasterBand::ComputeRasterMinMax(bApproxOK,
    1552           1 :                                                                adfMinMax);
    1553             :                 }
    1554             : 
    1555          17 :                 dfGlobalMin = std::min(dfGlobalMin, adfMinMaxSource[0]);
    1556          17 :                 dfGlobalMax = std::max(dfGlobalMax, adfMinMaxSource[1]);
    1557             :             }
    1558             : 
    1559             :             // Early exit if we know we reached theoretical bounds
    1560          17 :             if (eDataType == GDT_UInt8 && !bSignedByte && dfGlobalMin == 0.0 &&
    1561           0 :                 dfGlobalMax == 255.0)
    1562             :             {
    1563           0 :                 break;
    1564             :             }
    1565             :         }
    1566             : 
    1567          13 :         if (dfGlobalMin > dfGlobalMax)
    1568             :         {
    1569           0 :             adfMinMax[0] = 0.0;
    1570           0 :             adfMinMax[1] = 0.0;
    1571           0 :             ReportError(CE_Failure, CPLE_AppDefined,
    1572             :                         "Failed to compute min/max, no valid pixels found in "
    1573             :                         "sampling.");
    1574           0 :             return CE_Failure;
    1575             :         }
    1576             : 
    1577          13 :         adfMinMax[0] = dfGlobalMin;
    1578          13 :         adfMinMax[1] = dfGlobalMax;
    1579          13 :         return CE_None;
    1580             :     }
    1581             :     else
    1582             :     {
    1583           3 :         return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
    1584             :     }
    1585             : }
    1586             : 
    1587             : // #define naive_update_not_used
    1588             : 
    1589             : namespace
    1590             : {
    1591             : struct Context
    1592             : {
    1593             :     CPL_DISALLOW_COPY_ASSIGN(Context)
    1594             :     Context() = default;
    1595             : 
    1596             :     // Protected by mutex
    1597             :     std::mutex oMutex{};
    1598             :     uint64_t nTotalIteratedPixels = 0;
    1599             :     uint64_t nLastReportedPixels = 0;
    1600             :     bool bFailure = false;
    1601             :     bool bFallbackToBase = false;
    1602             :     // End of protected by mutex
    1603             : 
    1604             :     int nSources = 0;
    1605             : 
    1606             :     bool bApproxOK = false;
    1607             :     GDALProgressFunc pfnProgress = nullptr;
    1608             :     void *pProgressData = nullptr;
    1609             : 
    1610             :     // VRTSourcedRasterBand parameters
    1611             :     double dfNoDataValue = 0;
    1612             :     bool bNoDataValueSet = false;
    1613             :     bool bHideNoDataValue = false;
    1614             : 
    1615             :     double dfGlobalMin = std::numeric_limits<double>::max();
    1616             :     double dfGlobalMax = -std::numeric_limits<double>::max();
    1617             : #ifdef naive_update_not_used
    1618             :     // This native method uses the fact that stddev = sqrt(sum_of_squares/N -
    1619             :     // mean^2) and that thus sum_of_squares = N * (stddev^2 + mean^2)
    1620             :     double dfGlobalSum = 0;
    1621             :     double dfGlobalSumSquare = 0;
    1622             : #else
    1623             :     // This method uses
    1624             :     // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
    1625             :     // which is more numerically robust
    1626             :     double dfGlobalMean = 0;
    1627             :     double dfGlobalM2 = 0;
    1628             : #endif
    1629             :     uint64_t nGlobalValidPixels = 0;
    1630             :     uint64_t nTotalPixelsOfSources = 0;
    1631             : 
    1632             :     // Keep original values from single source to avoid slight changes
    1633             :     // due to recomputation. Cf https://github.com/OSGeo/gdal/issues/12650
    1634             :     bool bUpdateStatsWithConstantValueRun = false;
    1635             :     double dfSingleSourceMin = 0;
    1636             :     double dfSingleSourceMax = 0;
    1637             :     double dfSingleSourceMean = 0;
    1638             :     double dfSingleSourceStdDev = 0;
    1639             : };
    1640             : }  // namespace
    1641             : 
    1642           3 : static void UpdateStatsWithConstantValue(Context &sContext, double dfVal,
    1643             :                                          uint64_t nPixelCount)
    1644             : {
    1645           3 :     sContext.bUpdateStatsWithConstantValueRun = true;
    1646           3 :     sContext.dfGlobalMin = std::min(sContext.dfGlobalMin, dfVal);
    1647           3 :     sContext.dfGlobalMax = std::max(sContext.dfGlobalMax, dfVal);
    1648             : #ifdef naive_update_not_used
    1649             :     sContext.dfGlobalSum += dfVal * nPixelCount;
    1650             :     sContext.dfGlobalSumSquare += dfVal * dfVal * nPixelCount;
    1651             : #else
    1652           3 :     const auto nNewGlobalValidPixels =
    1653           3 :         sContext.nGlobalValidPixels + nPixelCount;
    1654           3 :     const double dfDelta = dfVal - sContext.dfGlobalMean;
    1655           3 :     sContext.dfGlobalMean += nPixelCount * dfDelta / nNewGlobalValidPixels;
    1656           3 :     sContext.dfGlobalM2 += dfDelta * dfDelta * nPixelCount *
    1657           3 :                            sContext.nGlobalValidPixels / nNewGlobalValidPixels;
    1658             : #endif
    1659           3 :     sContext.nGlobalValidPixels += nPixelCount;
    1660           3 : }
    1661             : 
    1662             : /************************************************************************/
    1663             : /*                         ComputeStatistics()                          */
    1664             : /************************************************************************/
    1665             : 
    1666          35 : CPLErr VRTSourcedRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
    1667             :                                                double *pdfMax, double *pdfMean,
    1668             :                                                double *pdfStdDev,
    1669             :                                                GDALProgressFunc pfnProgress,
    1670             :                                                void *pProgressData)
    1671             : 
    1672             : {
    1673          70 :     const std::string osFctId("VRTSourcedRasterBand::ComputeStatistics");
    1674          70 :     GDALAntiRecursionGuard oGuard(osFctId);
    1675          35 :     if (oGuard.GetCallDepth() >= 32)
    1676             :     {
    1677           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1678           0 :         return CE_Failure;
    1679             :     }
    1680             : 
    1681         105 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    1682          35 :     if (oGuard2.GetCallDepth() >= 2)
    1683             :     {
    1684           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    1685           0 :         return CE_Failure;
    1686             :     }
    1687             : 
    1688             :     /* -------------------------------------------------------------------- */
    1689             :     /*      If we have overview bands, use them for statistics.             */
    1690             :     /* -------------------------------------------------------------------- */
    1691          35 :     if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
    1692             :     {
    1693             :         GDALRasterBand *const poBand =
    1694           2 :             GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
    1695             : 
    1696           2 :         if (poBand != nullptr && poBand != this)
    1697             :         {
    1698           2 :             auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
    1699             :             CPLErr eErr;
    1700           3 :             if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
    1701           1 :                 dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
    1702             :             {
    1703           2 :                 auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
    1704           1 :                 l_poDS->m_apoOverviews.clear();
    1705           1 :                 eErr = poBand->GDALRasterBand::ComputeStatistics(
    1706             :                     TRUE, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
    1707             :                     pProgressData);
    1708           1 :                 l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
    1709             :             }
    1710             :             else
    1711             :             {
    1712           1 :                 eErr = poBand->ComputeStatistics(TRUE, pdfMin, pdfMax, pdfMean,
    1713             :                                                  pdfStdDev, pfnProgress,
    1714           1 :                                                  pProgressData);
    1715             :             }
    1716           2 :             if (eErr == CE_None && pdfMin && pdfMax && pdfMean && pdfStdDev)
    1717             :             {
    1718           2 :                 SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
    1719           2 :                 SetMetadataItem(
    1720             :                     "STATISTICS_VALID_PERCENT",
    1721           2 :                     poBand->GetMetadataItem("STATISTICS_VALID_PERCENT"));
    1722           2 :                 SetStatistics(*pdfMin, *pdfMax, *pdfMean, *pdfStdDev);
    1723             :             }
    1724           2 :             return eErr;
    1725             :         }
    1726             :     }
    1727             : 
    1728          33 :     if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
    1729             :             /*bAllowMaxValAdjustment = */ false))
    1730             :     {
    1731          27 :         Context sContext;
    1732          27 :         sContext.bApproxOK = bApproxOK;
    1733          27 :         sContext.dfNoDataValue = m_dfNoDataValue;
    1734          27 :         sContext.bNoDataValueSet = m_bNoDataValueSet;
    1735          27 :         sContext.bHideNoDataValue = m_bHideNoDataValue;
    1736          27 :         sContext.pfnProgress = pfnProgress;
    1737          27 :         sContext.pProgressData = pProgressData;
    1738          27 :         sContext.nSources = static_cast<int>(m_papoSources.size());
    1739             : 
    1740             :         struct Job
    1741             :         {
    1742             :             Context *psContext = nullptr;
    1743             :             GDALRasterBand *poRasterBand = nullptr;
    1744             :             uint64_t nPixelCount = 0;
    1745             :             uint64_t nLastAddedPixels = 0;
    1746             :             uint64_t nValidPixels = 0;
    1747             :             double dfMin = 0;
    1748             :             double dfMax = 0;
    1749             :             double dfMean = 0;
    1750             :             double dfStdDev = 0;
    1751             : 
    1752          58 :             static int CPL_STDCALL ProgressFunc(double dfComplete,
    1753             :                                                 const char *pszMessage,
    1754             :                                                 void *pProgressArg)
    1755             :             {
    1756          58 :                 auto psJob = static_cast<Job *>(pProgressArg);
    1757          58 :                 auto psContext = psJob->psContext;
    1758          58 :                 const uint64_t nNewAddedPixels =
    1759             :                     dfComplete == 1.0
    1760          58 :                         ? psJob->nPixelCount
    1761             :                         : static_cast<uint64_t>(
    1762          54 :                               dfComplete * psJob->nPixelCount + 0.5);
    1763             :                 const auto nUpdateThreshold =
    1764         116 :                     std::min(psContext->nTotalPixelsOfSources / 1000,
    1765          58 :                              static_cast<uint64_t>(1000 * 1000));
    1766         116 :                 std::lock_guard<std::mutex> oLock(psContext->oMutex);
    1767          58 :                 psContext->nTotalIteratedPixels +=
    1768          58 :                     (nNewAddedPixels - psJob->nLastAddedPixels);
    1769          58 :                 psJob->nLastAddedPixels = nNewAddedPixels;
    1770          58 :                 if (psContext->nTotalIteratedPixels ==
    1771          58 :                     psContext->nTotalPixelsOfSources)
    1772             :                 {
    1773           2 :                     psContext->nLastReportedPixels =
    1774           2 :                         psContext->nTotalIteratedPixels;
    1775           2 :                     return psContext->pfnProgress(1.0, pszMessage,
    1776           2 :                                                   psContext->pProgressData);
    1777             :                 }
    1778          56 :                 else if (psContext->nTotalIteratedPixels -
    1779          56 :                              psContext->nLastReportedPixels >
    1780             :                          nUpdateThreshold)
    1781             :                 {
    1782          48 :                     psContext->nLastReportedPixels =
    1783          48 :                         psContext->nTotalIteratedPixels;
    1784          96 :                     return psContext->pfnProgress(
    1785          48 :                         static_cast<double>(psContext->nTotalIteratedPixels) /
    1786          48 :                             psContext->nTotalPixelsOfSources,
    1787          48 :                         pszMessage, psContext->pProgressData);
    1788             :                 }
    1789           8 :                 return 1;
    1790             :             }
    1791             : 
    1792          37 :             static void UpdateStats(const Job *psJob)
    1793             :             {
    1794          37 :                 const auto nValidPixels = psJob->nValidPixels;
    1795          37 :                 auto psContext = psJob->psContext;
    1796          37 :                 if (nValidPixels > 0)
    1797             :                 {
    1798          29 :                     psContext->dfGlobalMin =
    1799          29 :                         std::min(psContext->dfGlobalMin, psJob->dfMin);
    1800          29 :                     psContext->dfGlobalMax =
    1801          29 :                         std::max(psContext->dfGlobalMax, psJob->dfMax);
    1802             : #ifdef naive_update_not_used
    1803             :                     psContext->dfGlobalSum += nValidPixels * psJob->dfMean;
    1804             :                     psContext->dfGlobalSumSquare +=
    1805             :                         nValidPixels * (psJob->dfStdDev * psJob->dfStdDev +
    1806             :                                         psJob->dfMean * psJob->dfMean);
    1807             :                     psContext->nGlobalValidPixels += nValidPixels;
    1808             : #else
    1809          29 :                     const auto nNewGlobalValidPixels =
    1810          29 :                         psContext->nGlobalValidPixels + nValidPixels;
    1811          29 :                     const double dfDelta =
    1812          29 :                         psJob->dfMean - psContext->dfGlobalMean;
    1813          29 :                     psContext->dfGlobalMean +=
    1814          29 :                         nValidPixels * dfDelta / nNewGlobalValidPixels;
    1815          29 :                     psContext->dfGlobalM2 +=
    1816          29 :                         nValidPixels * psJob->dfStdDev * psJob->dfStdDev +
    1817          29 :                         dfDelta * dfDelta * nValidPixels *
    1818          29 :                             psContext->nGlobalValidPixels /
    1819             :                             nNewGlobalValidPixels;
    1820          29 :                     psContext->nGlobalValidPixels = nNewGlobalValidPixels;
    1821             : #endif
    1822             :                 }
    1823          37 :                 int bHasNoData = FALSE;
    1824             :                 const double dfNoDataValue =
    1825          37 :                     psJob->poRasterBand->GetNoDataValue(&bHasNoData);
    1826           9 :                 if (nValidPixels < psJob->nPixelCount && bHasNoData &&
    1827          55 :                     !std::isnan(dfNoDataValue) &&
    1828           9 :                     (!psContext->bNoDataValueSet ||
    1829           7 :                      dfNoDataValue != psContext->dfNoDataValue))
    1830             :                 {
    1831             :                     const auto eBandDT =
    1832           2 :                         psJob->poRasterBand->GetRasterDataType();
    1833             :                     // Check that the band nodata value is in the range of the
    1834             :                     // original raster type
    1835             :                     GByte abyTempBuffer[2 * sizeof(double)];
    1836           2 :                     CPLAssert(GDALGetDataTypeSizeBytes(eBandDT) <=
    1837             :                               static_cast<int>(sizeof(abyTempBuffer)));
    1838           2 :                     GDALCopyWords(&dfNoDataValue, GDT_Float64, 0,
    1839             :                                   &abyTempBuffer[0], eBandDT, 0, 1);
    1840           2 :                     double dfNoDataValueAfter = dfNoDataValue;
    1841           2 :                     GDALCopyWords(&abyTempBuffer[0], eBandDT, 0,
    1842             :                                   &dfNoDataValueAfter, GDT_Float64, 0, 1);
    1843           4 :                     if (!std::isfinite(dfNoDataValue) ||
    1844           2 :                         std::fabs(dfNoDataValueAfter - dfNoDataValue) < 1.0)
    1845             :                     {
    1846           2 :                         UpdateStatsWithConstantValue(
    1847             :                             *psContext, dfNoDataValueAfter,
    1848           2 :                             psJob->nPixelCount - nValidPixels);
    1849             :                     }
    1850             :                 }
    1851             : 
    1852          37 :                 if (psContext->nSources == 1)
    1853             :                 {
    1854          13 :                     psContext->dfSingleSourceMin = psJob->dfMin;
    1855          13 :                     psContext->dfSingleSourceMax = psJob->dfMax;
    1856          13 :                     psContext->dfSingleSourceMean = psJob->dfMean;
    1857          13 :                     psContext->dfSingleSourceStdDev = psJob->dfStdDev;
    1858             :                 }
    1859          37 :             }
    1860             :         };
    1861             : 
    1862          40 :         const auto JobRunner = [](void *pData)
    1863             :         {
    1864          40 :             auto psJob = static_cast<Job *>(pData);
    1865          40 :             auto psContext = psJob->psContext;
    1866             :             {
    1867          40 :                 std::lock_guard<std::mutex> oLock(psContext->oMutex);
    1868          40 :                 if (psContext->bFallbackToBase || psContext->bFailure)
    1869           0 :                     return;
    1870             :             }
    1871             : 
    1872          40 :             auto poSimpleSourceBand = psJob->poRasterBand;
    1873          40 :             psJob->nPixelCount =
    1874          40 :                 static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
    1875          40 :                 poSimpleSourceBand->GetYSize();
    1876             : 
    1877          40 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1878          40 :             CPLErr eErr = poSimpleSourceBand->ComputeStatistics(
    1879          40 :                 psContext->bApproxOK, &psJob->dfMin, &psJob->dfMax,
    1880             :                 &psJob->dfMean, &psJob->dfStdDev,
    1881          46 :                 psContext->pfnProgress == nullptr ||
    1882           6 :                         psContext->pfnProgress == GDALDummyProgress
    1883             :                     ? GDALDummyProgress
    1884             :                     : Job::ProgressFunc,
    1885          40 :                 psJob);
    1886             :             const char *pszValidPercent =
    1887          40 :                 poSimpleSourceBand->GetMetadataItem("STATISTICS_VALID_PERCENT");
    1888          40 :             psJob->nValidPixels =
    1889             :                 pszValidPercent
    1890          40 :                     ? static_cast<uint64_t>(CPLAtof(pszValidPercent) *
    1891          40 :                                             psJob->nPixelCount / 100.0)
    1892             :                     : psJob->nPixelCount;
    1893          40 :             if (eErr == CE_Failure)
    1894             :             {
    1895          16 :                 if (pszValidPercent != nullptr &&
    1896           8 :                     CPLAtof(pszValidPercent) == 0.0)
    1897             :                 {
    1898             :                     // ok: no valid sample
    1899             :                 }
    1900             :                 else
    1901             :                 {
    1902           0 :                     std::lock_guard<std::mutex> oLock(psContext->oMutex);
    1903           0 :                     psContext->bFailure = true;
    1904             :                 }
    1905             :             }
    1906             :             else
    1907             :             {
    1908          32 :                 int bHasNoData = FALSE;
    1909          32 :                 CPL_IGNORE_RET_VAL(
    1910          32 :                     psJob->poRasterBand->GetNoDataValue(&bHasNoData));
    1911          32 :                 if (!bHasNoData && psContext->bNoDataValueSet &&
    1912           9 :                     !psContext->bHideNoDataValue &&
    1913           7 :                     psContext->dfNoDataValue >= psJob->dfMin &&
    1914           2 :                     psContext->dfNoDataValue <= psJob->dfMax)
    1915             :                 {
    1916           2 :                     std::lock_guard<std::mutex> oLock(psContext->oMutex);
    1917           2 :                     psJob->psContext->bFallbackToBase = true;
    1918           2 :                     return;
    1919             :                 }
    1920             :             }
    1921             :         };
    1922             : 
    1923          27 :         CPLWorkerThreadPool *poThreadPool = nullptr;
    1924             :         int nThreads =
    1925          27 :             m_papoSources.size() > 1
    1926          27 :                 ? VRTDataset::GetNumThreads(dynamic_cast<VRTDataset *>(poDS))
    1927          27 :                 : 0;
    1928          27 :         if (nThreads > 1024)
    1929           0 :             nThreads = 1024;  // to please Coverity
    1930          27 :         if (nThreads > 1)
    1931             :         {
    1932             :             // Check that all sources refer to different datasets
    1933             :             // before allowing multithreaded access
    1934             :             // If the datasets belong to the MEM driver, check GDALDataset*
    1935             :             // pointer values. Otherwise use dataset name.
    1936          24 :             std::set<std::string> oSetDatasetNames;
    1937          24 :             std::set<GDALDataset *> oSetDatasetPointers;
    1938          36 :             for (auto &poSource : m_papoSources)
    1939             :             {
    1940             :                 auto poSimpleSource =
    1941          24 :                     static_cast<VRTSimpleSource *>(poSource.get());
    1942          24 :                 assert(poSimpleSource);
    1943          24 :                 auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    1944          24 :                 assert(poSimpleSourceBand);
    1945          24 :                 auto poSourceDataset = poSimpleSourceBand->GetDataset();
    1946          24 :                 if (poSourceDataset == nullptr)
    1947             :                 {
    1948           0 :                     nThreads = 0;
    1949           0 :                     break;
    1950             :                 }
    1951          24 :                 auto poDriver = poSourceDataset->GetDriver();
    1952          24 :                 if (poDriver && EQUAL(poDriver->GetDescription(), "MEM"))
    1953             :                 {
    1954          24 :                     if (oSetDatasetPointers.find(poSourceDataset) !=
    1955          48 :                         oSetDatasetPointers.end())
    1956             :                     {
    1957           0 :                         nThreads = 0;
    1958           0 :                         break;
    1959             :                     }
    1960          24 :                     oSetDatasetPointers.insert(poSourceDataset);
    1961             :                 }
    1962             :                 else
    1963             :                 {
    1964           0 :                     if (oSetDatasetNames.find(
    1965           0 :                             poSourceDataset->GetDescription()) !=
    1966           0 :                         oSetDatasetNames.end())
    1967             :                     {
    1968           0 :                         nThreads = 0;
    1969           0 :                         break;
    1970             :                     }
    1971           0 :                     oSetDatasetNames.insert(poSourceDataset->GetDescription());
    1972             :                 }
    1973             :             }
    1974          12 :             if (nThreads > 1)
    1975             :             {
    1976          12 :                 poThreadPool = GDALGetGlobalThreadPool(nThreads);
    1977             :             }
    1978             :         }
    1979             : 
    1980             :         // Compute total number of pixels of sources
    1981          67 :         for (auto &poSource : m_papoSources)
    1982             :         {
    1983             :             auto poSimpleSource =
    1984          40 :                 static_cast<VRTSimpleSource *>(poSource.get());
    1985          40 :             assert(poSimpleSource);
    1986          40 :             auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    1987          40 :             assert(poSimpleSourceBand);
    1988          40 :             sContext.nTotalPixelsOfSources +=
    1989          40 :                 static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
    1990          40 :                 poSimpleSourceBand->GetYSize();
    1991             :         }
    1992             : 
    1993          27 :         if (poThreadPool)
    1994             :         {
    1995          12 :             CPLDebugOnly("VRT", "ComputeStatistics(): use optimized "
    1996             :                                 "multi-threaded code path for mosaic");
    1997          24 :             std::vector<Job> asJobs(m_papoSources.size());
    1998          24 :             auto poQueue = poThreadPool->CreateJobQueue();
    1999          36 :             for (size_t i = 0; i < m_papoSources.size(); ++i)
    2000             :             {
    2001             :                 auto poSimpleSource =
    2002          24 :                     static_cast<VRTSimpleSource *>(m_papoSources[i].get());
    2003          24 :                 assert(poSimpleSource);
    2004          24 :                 auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    2005          24 :                 assert(poSimpleSourceBand);
    2006          24 :                 asJobs[i].psContext = &sContext;
    2007          24 :                 asJobs[i].poRasterBand = poSimpleSourceBand;
    2008          24 :                 if (!poQueue->SubmitJob(JobRunner, &asJobs[i]))
    2009             :                 {
    2010           0 :                     sContext.bFailure = true;
    2011           0 :                     break;
    2012             :                 }
    2013             :             }
    2014          12 :             poQueue->WaitCompletion();
    2015          12 :             if (!(sContext.bFailure || sContext.bFallbackToBase))
    2016             :             {
    2017          33 :                 for (size_t i = 0; i < m_papoSources.size(); ++i)
    2018             :                 {
    2019          22 :                     Job::UpdateStats(&asJobs[i]);
    2020             :                 }
    2021             :             }
    2022             :         }
    2023             :         else
    2024             :         {
    2025          15 :             CPLDebugOnly(
    2026             :                 "VRT",
    2027             :                 "ComputeStatistics(): use optimized code path for mosaic");
    2028          30 :             for (auto &poSource : m_papoSources)
    2029             :             {
    2030             :                 auto poSimpleSource =
    2031          16 :                     static_cast<VRTSimpleSource *>(poSource.get());
    2032          16 :                 assert(poSimpleSource);
    2033          16 :                 auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
    2034          16 :                 assert(poSimpleSourceBand);
    2035          16 :                 Job sJob;
    2036          16 :                 sJob.psContext = &sContext;
    2037          16 :                 sJob.poRasterBand = poSimpleSourceBand;
    2038          16 :                 JobRunner(&sJob);
    2039          16 :                 if (sContext.bFailure || sContext.bFallbackToBase)
    2040             :                     break;
    2041          15 :                 Job::UpdateStats(&sJob);
    2042             :             }
    2043             :         }
    2044             : 
    2045          27 :         if (sContext.bFailure)
    2046           0 :             return CE_Failure;
    2047          27 :         if (sContext.bFallbackToBase)
    2048             :         {
    2049             :             // If the VRT band nodata value is in the [min, max] range
    2050             :             // of the source and that the source has no nodata value set,
    2051             :             // then we can't use the optimization.
    2052           2 :             CPLDebugOnly("VRT", "ComputeStatistics(): revert back to "
    2053             :                                 "generic case because of nodata value in range "
    2054             :                                 "of source raster");
    2055           2 :             return GDALRasterBand::ComputeStatistics(
    2056             :                 bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
    2057           2 :                 pProgressData);
    2058             :         }
    2059             : 
    2060          25 :         const uint64_t nTotalPixels =
    2061          25 :             static_cast<uint64_t>(nRasterXSize) * nRasterYSize;
    2062           7 :         if (m_bNoDataValueSet && m_bHideNoDataValue &&
    2063          32 :             !std::isnan(m_dfNoDataValue) && IsNoDataValueInDataTypeRange())
    2064             :         {
    2065           1 :             UpdateStatsWithConstantValue(sContext, m_dfNoDataValue,
    2066             :                                          nTotalPixels -
    2067           1 :                                              sContext.nGlobalValidPixels);
    2068             :         }
    2069          24 :         else if (!m_bNoDataValueSet)
    2070             :         {
    2071          18 :             sContext.nGlobalValidPixels = nTotalPixels;
    2072             :         }
    2073             : 
    2074             : #ifdef naive_update_not_used
    2075             :         double dfGlobalMean =
    2076             :             sContext.nGlobalValidPixels > 0
    2077             :                 ? sContext.dfGlobalSum / sContext.nGlobalValidPixels
    2078             :                 : 0;
    2079             :         double dfGlobalStdDev = sContext.nGlobalValidPixels > 0
    2080             :                                     ? sqrt(sContext.dfGlobalSumSquare /
    2081             :                                                sContext.nGlobalValidPixels -
    2082             :                                            dfGlobalMean * dfGlobalMean)
    2083             :                                     : 0;
    2084             : #else
    2085          25 :         double dfGlobalMean = sContext.dfGlobalMean;
    2086             :         double dfGlobalStdDev =
    2087          25 :             sContext.nGlobalValidPixels > 0
    2088          25 :                 ? sqrt(sContext.dfGlobalM2 / sContext.nGlobalValidPixels)
    2089          25 :                 : 0;
    2090             : #endif
    2091          38 :         if (m_papoSources.size() == 1 &&
    2092          13 :             !sContext.bUpdateStatsWithConstantValueRun)
    2093             :         {
    2094          13 :             sContext.dfGlobalMin = sContext.dfSingleSourceMin;
    2095          13 :             sContext.dfGlobalMax = sContext.dfSingleSourceMax;
    2096          13 :             dfGlobalMean = sContext.dfSingleSourceMean;
    2097          13 :             dfGlobalStdDev = sContext.dfSingleSourceStdDev;
    2098             :         }
    2099             : 
    2100          25 :         if (sContext.nGlobalValidPixels > 0)
    2101             :         {
    2102          23 :             if (bApproxOK)
    2103             :             {
    2104           1 :                 SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
    2105             :             }
    2106          22 :             else if (GetMetadataItem("STATISTICS_APPROXIMATE"))
    2107             :             {
    2108           0 :                 SetMetadataItem("STATISTICS_APPROXIMATE", nullptr);
    2109             :             }
    2110          23 :             SetStatistics(sContext.dfGlobalMin, sContext.dfGlobalMax,
    2111          23 :                           dfGlobalMean, dfGlobalStdDev);
    2112             :         }
    2113             :         else
    2114             :         {
    2115           2 :             sContext.dfGlobalMin = 0.0;
    2116           2 :             sContext.dfGlobalMax = 0.0;
    2117             :         }
    2118             : 
    2119          25 :         SetValidPercent(nTotalPixels, sContext.nGlobalValidPixels);
    2120             : 
    2121          25 :         if (pdfMin)
    2122          17 :             *pdfMin = sContext.dfGlobalMin;
    2123          25 :         if (pdfMax)
    2124          17 :             *pdfMax = sContext.dfGlobalMax;
    2125          25 :         if (pdfMean)
    2126          13 :             *pdfMean = dfGlobalMean;
    2127          25 :         if (pdfStdDev)
    2128          13 :             *pdfStdDev = dfGlobalStdDev;
    2129             : 
    2130          25 :         if (sContext.nGlobalValidPixels == 0)
    2131             :         {
    2132           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    2133             :                         "Failed to compute statistics, no valid pixels found "
    2134             :                         "in sampling.");
    2135             :         }
    2136             : 
    2137          25 :         return sContext.nGlobalValidPixels > 0 ? CE_None : CE_Failure;
    2138             :     }
    2139             :     else
    2140             :     {
    2141           6 :         return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
    2142             :                                                  pdfMean, pdfStdDev,
    2143           6 :                                                  pfnProgress, pProgressData);
    2144             :     }
    2145             : }
    2146             : 
    2147             : /************************************************************************/
    2148             : /*                            GetHistogram()                            */
    2149             : /************************************************************************/
    2150             : 
    2151           7 : CPLErr VRTSourcedRasterBand::GetHistogram(double dfMin, double dfMax,
    2152             :                                           int nBuckets, GUIntBig *panHistogram,
    2153             :                                           int bIncludeOutOfRange, int bApproxOK,
    2154             :                                           GDALProgressFunc pfnProgress,
    2155             :                                           void *pProgressData)
    2156             : 
    2157             : {
    2158             :     /* -------------------------------------------------------------------- */
    2159             :     /*      If we have overviews, use them for the histogram.               */
    2160             :     /* -------------------------------------------------------------------- */
    2161           7 :     if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
    2162             :     {
    2163             :         // FIXME: Should we use the most reduced overview here or use some
    2164             :         // minimum number of samples like GDALRasterBand::ComputeStatistics()
    2165             :         // does?
    2166           1 :         GDALRasterBand *poBand = GetRasterSampleOverview(0);
    2167             : 
    2168           1 :         if (poBand != nullptr && poBand != this)
    2169             :         {
    2170           1 :             auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
    2171           2 :             if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
    2172           1 :                 dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
    2173             :             {
    2174           1 :                 auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
    2175           1 :                 l_poDS->m_apoOverviews.clear();
    2176           1 :                 auto eErr = poBand->GDALRasterBand::GetHistogram(
    2177             :                     dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
    2178             :                     bApproxOK, pfnProgress, pProgressData);
    2179           1 :                 l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
    2180           1 :                 return eErr;
    2181             :             }
    2182             :             else
    2183             :             {
    2184           0 :                 return poBand->GetHistogram(
    2185             :                     dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
    2186           0 :                     bApproxOK, pfnProgress, pProgressData);
    2187             :             }
    2188             :         }
    2189             :     }
    2190             : 
    2191           6 :     if (m_papoSources.size() != 1)
    2192           2 :         return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
    2193             :                                            bIncludeOutOfRange, bApproxOK,
    2194           2 :                                            pfnProgress, pProgressData);
    2195             : 
    2196           4 :     if (pfnProgress == nullptr)
    2197           4 :         pfnProgress = GDALDummyProgress;
    2198             : 
    2199           8 :     const std::string osFctId("VRTSourcedRasterBand::GetHistogram");
    2200           8 :     GDALAntiRecursionGuard oGuard(osFctId);
    2201           4 :     if (oGuard.GetCallDepth() >= 32)
    2202             :     {
    2203           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    2204           0 :         return CE_Failure;
    2205             :     }
    2206             : 
    2207          12 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    2208           4 :     if (oGuard2.GetCallDepth() >= 2)
    2209             :     {
    2210           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
    2211           0 :         return CE_Failure;
    2212             :     }
    2213             : 
    2214             :     /* -------------------------------------------------------------------- */
    2215             :     /*      Try with source bands.                                          */
    2216             :     /* -------------------------------------------------------------------- */
    2217           8 :     const CPLErr eErr = m_papoSources[0]->GetHistogram(
    2218             :         GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, panHistogram,
    2219           4 :         bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
    2220           4 :     if (eErr != CE_None)
    2221             :     {
    2222           0 :         const CPLErr eErr2 = GDALRasterBand::GetHistogram(
    2223             :             dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
    2224             :             pfnProgress, pProgressData);
    2225           0 :         return eErr2;
    2226             :     }
    2227             : 
    2228           4 :     SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram);
    2229             : 
    2230           4 :     return CE_None;
    2231             : }
    2232             : 
    2233             : /************************************************************************/
    2234             : /*                             AddSource()                              */
    2235             : /************************************************************************/
    2236             : 
    2237      242348 : CPLErr VRTSourcedRasterBand::AddSource(VRTSource *poNewSource)
    2238             : 
    2239             : {
    2240      242348 :     return AddSource(std::unique_ptr<VRTSource>(poNewSource));
    2241             : }
    2242             : 
    2243             : /************************************************************************/
    2244             : /*                             AddSource()                              */
    2245             : /************************************************************************/
    2246             : 
    2247      247211 : CPLErr VRTSourcedRasterBand::AddSource(std::unique_ptr<VRTSource> poNewSource)
    2248             : 
    2249             : {
    2250      247211 :     auto l_poDS = static_cast<VRTDataset *>(poDS);
    2251      247211 :     l_poDS->SetNeedsFlush();
    2252      247211 :     l_poDS->SourceAdded();
    2253             : 
    2254      247211 :     if (poNewSource->IsSimpleSource())
    2255             :     {
    2256             :         VRTSimpleSource *poSS =
    2257      247177 :             static_cast<VRTSimpleSource *>(poNewSource.get());
    2258      247177 :         if (GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr)
    2259             :         {
    2260          47 :             int nBits = atoi(GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    2261          47 :             if (nBits >= 1 && nBits <= 31)
    2262             :             {
    2263          47 :                 poSS->SetMaxValue(static_cast<int>((1U << nBits) - 1));
    2264             :             }
    2265             :         }
    2266             :     }
    2267             : 
    2268      247211 :     m_papoSources.push_back(std::move(poNewSource));
    2269             : 
    2270      247211 :     return CE_None;
    2271             : }
    2272             : 
    2273             : /*! @endcond */
    2274             : 
    2275             : /************************************************************************/
    2276             : /*                            VRTAddSource()                            */
    2277             : /************************************************************************/
    2278             : 
    2279             : /**
    2280             :  * @see VRTSourcedRasterBand::AddSource().
    2281             :  */
    2282             : 
    2283           0 : CPLErr CPL_STDCALL VRTAddSource(VRTSourcedRasterBandH hVRTBand,
    2284             :                                 VRTSourceH hNewSource)
    2285             : {
    2286           0 :     VALIDATE_POINTER1(hVRTBand, "VRTAddSource", CE_Failure);
    2287             : 
    2288           0 :     return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSource(
    2289           0 :         reinterpret_cast<VRTSource *>(hNewSource));
    2290             : }
    2291             : 
    2292             : /*! @cond Doxygen_Suppress */
    2293             : 
    2294             : /************************************************************************/
    2295             : /*                              XMLInit()                               */
    2296             : /************************************************************************/
    2297             : 
    2298        2605 : CPLErr VRTSourcedRasterBand::XMLInit(const CPLXMLNode *psTree,
    2299             :                                      const char *pszVRTPath,
    2300             :                                      VRTMapSharedResources &oMapSharedSources)
    2301             : 
    2302             : {
    2303             :     {
    2304             :         const CPLErr eErr =
    2305        2605 :             VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
    2306        2605 :         if (eErr != CE_None)
    2307           0 :             return eErr;
    2308             :     }
    2309             : 
    2310             :     /* -------------------------------------------------------------------- */
    2311             :     /*      Process sources.                                                */
    2312             :     /* -------------------------------------------------------------------- */
    2313           0 :     VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
    2314        2605 :         GetGDALDriverManager()->GetDriverByName("VRT"));
    2315             : 
    2316        2605 :     for (const CPLXMLNode *psChild = psTree->psChild;
    2317      118799 :          psChild != nullptr && poDriver != nullptr; psChild = psChild->psNext)
    2318             :     {
    2319      116215 :         if (psChild->eType != CXT_Element)
    2320        6851 :             continue;
    2321             : 
    2322      109364 :         CPLErrorReset();
    2323             :         VRTSource *const poSource =
    2324      109364 :             poDriver->ParseSource(psChild, pszVRTPath, oMapSharedSources);
    2325      109364 :         if (poSource != nullptr)
    2326      104181 :             AddSource(poSource);
    2327        5183 :         else if (CPLGetLastErrorType() != CE_None)
    2328          21 :             return CE_Failure;
    2329             :     }
    2330             : 
    2331             :     /* -------------------------------------------------------------------- */
    2332             :     /*      Done.                                                           */
    2333             :     /* -------------------------------------------------------------------- */
    2334             :     const char *pszSubclass =
    2335        2584 :         CPLGetXMLValue(psTree, "subclass", "VRTSourcedRasterBand");
    2336        2584 :     if (m_papoSources.empty() && !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
    2337         172 :         CPLDebug("VRT", "No valid sources found for band in VRT file %s",
    2338         172 :                  GetDataset() ? GetDataset()->GetDescription() : "");
    2339             : 
    2340        2584 :     return CE_None;
    2341             : }
    2342             : 
    2343             : /************************************************************************/
    2344             : /*                           SerializeToXML()                           */
    2345             : /************************************************************************/
    2346             : 
    2347         741 : CPLXMLNode *VRTSourcedRasterBand::SerializeToXML(const char *pszVRTPath,
    2348             :                                                  bool &bHasWarnedAboutRAMUsage,
    2349             :                                                  size_t &nAccRAMUsage)
    2350             : 
    2351             : {
    2352         741 :     CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
    2353             :         pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
    2354         741 :     CPLXMLNode *psLastChild = psTree->psChild;
    2355        2720 :     while (psLastChild != nullptr && psLastChild->psNext != nullptr)
    2356        1979 :         psLastChild = psLastChild->psNext;
    2357             : 
    2358             :     /* -------------------------------------------------------------------- */
    2359             :     /*      Process Sources.                                                */
    2360             :     /* -------------------------------------------------------------------- */
    2361             : 
    2362         741 :     GIntBig nUsableRAM = -1;
    2363             : 
    2364        3432 :     for (const auto &poSource : m_papoSources)
    2365             :     {
    2366        2691 :         CPLXMLNode *const psXMLSrc = poSource->SerializeToXML(pszVRTPath);
    2367             : 
    2368        2691 :         if (psXMLSrc == nullptr)
    2369           0 :             break;
    2370             : 
    2371             :         // Creating the CPLXMLNode tree representation of a VRT can easily
    2372             :         // take several times RAM usage than its string serialization, or its
    2373             :         // internal representation in the driver.
    2374             :         // We multiply the estimate by a factor of 2, experimentally found to
    2375             :         // be more realistic than the conservative raw estimate.
    2376        2691 :         nAccRAMUsage += 2 * CPLXMLNodeGetRAMUsageEstimate(psXMLSrc);
    2377        2691 :         if (!bHasWarnedAboutRAMUsage && nAccRAMUsage > 512 * 1024 * 1024)
    2378             :         {
    2379           0 :             if (nUsableRAM < 0)
    2380           0 :                 nUsableRAM = CPLGetUsablePhysicalRAM();
    2381           0 :             if (nUsableRAM > 0 &&
    2382           0 :                 nAccRAMUsage > static_cast<uint64_t>(nUsableRAM) / 10 * 8)
    2383             :             {
    2384           0 :                 bHasWarnedAboutRAMUsage = true;
    2385           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2386             :                          "Serialization of this VRT file has already consumed "
    2387             :                          "at least %.02f GB of RAM over a total of %.02f. This "
    2388             :                          "process may abort",
    2389           0 :                          double(nAccRAMUsage) / (1024 * 1024 * 1024),
    2390           0 :                          double(nUsableRAM) / (1024 * 1024 * 1024));
    2391             :             }
    2392             :         }
    2393             : 
    2394        2691 :         if (psLastChild == nullptr)
    2395           0 :             psTree->psChild = psXMLSrc;
    2396             :         else
    2397        2691 :             psLastChild->psNext = psXMLSrc;
    2398        2691 :         psLastChild = psXMLSrc;
    2399             :     }
    2400             : 
    2401         741 :     return psTree;
    2402             : }
    2403             : 
    2404             : /************************************************************************/
    2405             : /*                      SkipBufferInitialization()                      */
    2406             : /************************************************************************/
    2407             : 
    2408      255286 : bool VRTSourcedRasterBand::SkipBufferInitialization()
    2409             : {
    2410      255286 :     if (m_nSkipBufferInitialization >= 0)
    2411      250261 :         return m_nSkipBufferInitialization != 0;
    2412             :     /* -------------------------------------------------------------------- */
    2413             :     /*      Check if we can avoid buffer initialization.                    */
    2414             :     /* -------------------------------------------------------------------- */
    2415             : 
    2416             :     // Note: if one day we do alpha compositing, we will need to check that.
    2417        5025 :     m_nSkipBufferInitialization = FALSE;
    2418        5025 :     if (m_papoSources.size() != 1 || !m_papoSources[0]->IsSimpleSource())
    2419             :     {
    2420        3385 :         return false;
    2421             :     }
    2422             :     VRTSimpleSource *poSS =
    2423        1640 :         static_cast<VRTSimpleSource *>(m_papoSources[0].get());
    2424        1640 :     if (poSS->GetType() == VRTSimpleSource::GetTypeStatic())
    2425             :     {
    2426        1238 :         auto l_poBand = poSS->GetRasterBand();
    2427        1227 :         if (l_poBand != nullptr && poSS->m_dfSrcXOff >= 0.0 &&
    2428        1197 :             poSS->m_dfSrcYOff >= 0.0 &&
    2429        1195 :             poSS->m_dfSrcXOff + poSS->m_dfSrcXSize <= l_poBand->GetXSize() &&
    2430        1193 :             poSS->m_dfSrcYOff + poSS->m_dfSrcYSize <= l_poBand->GetYSize() &&
    2431        1193 :             poSS->m_dfDstXOff <= 0.0 && poSS->m_dfDstYOff <= 0.0 &&
    2432        3581 :             poSS->m_dfDstXOff + poSS->m_dfDstXSize >= nRasterXSize &&
    2433        1116 :             poSS->m_dfDstYOff + poSS->m_dfDstYSize >= nRasterYSize)
    2434             :         {
    2435        1114 :             m_nSkipBufferInitialization = TRUE;
    2436             :         }
    2437             :     }
    2438        1640 :     return m_nSkipBufferInitialization != 0;
    2439             : }
    2440             : 
    2441             : /************************************************************************/
    2442             : /*                          ConfigureSource()                           */
    2443             : /************************************************************************/
    2444             : 
    2445      142419 : void VRTSourcedRasterBand::ConfigureSource(VRTSimpleSource *poSimpleSource,
    2446             :                                            GDALRasterBand *poSrcBand,
    2447             :                                            int bAddAsMaskBand, double dfSrcXOff,
    2448             :                                            double dfSrcYOff, double dfSrcXSize,
    2449             :                                            double dfSrcYSize, double dfDstXOff,
    2450             :                                            double dfDstYOff, double dfDstXSize,
    2451             :                                            double dfDstYSize)
    2452             : {
    2453             :     /* -------------------------------------------------------------------- */
    2454             :     /*      Default source and dest rectangles.                             */
    2455             :     /* -------------------------------------------------------------------- */
    2456      142419 :     if (dfSrcYSize == -1)
    2457             :     {
    2458       66970 :         dfSrcXOff = 0;
    2459       66970 :         dfSrcYOff = 0;
    2460       66970 :         dfSrcXSize = poSrcBand->GetXSize();
    2461       66970 :         dfSrcYSize = poSrcBand->GetYSize();
    2462             :     }
    2463             : 
    2464      142419 :     if (dfDstYSize == -1)
    2465             :     {
    2466       66970 :         dfDstXOff = 0;
    2467       66970 :         dfDstYOff = 0;
    2468       66970 :         dfDstXSize = nRasterXSize;
    2469       66970 :         dfDstYSize = nRasterYSize;
    2470             :     }
    2471             : 
    2472      142419 :     if (bAddAsMaskBand)
    2473          48 :         poSimpleSource->SetSrcMaskBand(poSrcBand);
    2474             :     else
    2475      142371 :         poSimpleSource->SetSrcBand(poSrcBand);
    2476             : 
    2477      142419 :     poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
    2478      142419 :     poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
    2479             : 
    2480             :     /* -------------------------------------------------------------------- */
    2481             :     /*      If we can get the associated GDALDataset, add a reference to it.*/
    2482             :     /* -------------------------------------------------------------------- */
    2483      142419 :     GDALDataset *poSrcBandDataset = poSrcBand->GetDataset();
    2484      142419 :     if (poSrcBandDataset != nullptr)
    2485             :     {
    2486             :         VRTDataset *poVRTSrcBandDataset =
    2487      142419 :             dynamic_cast<VRTDataset *>(poSrcBandDataset);
    2488      142419 :         if (poVRTSrcBandDataset && !poVRTSrcBandDataset->m_bCanTakeRef)
    2489             :         {
    2490             :             // Situation triggered by VRTDataset::AddVirtualOverview()
    2491             :             // We create an overview dataset that is a VRT of a reduction of
    2492             :             // ourselves. But we don't want to take a reference on ourselves,
    2493             :             // otherwise this will prevent us to be closed in number of
    2494             :             // circumstances
    2495          65 :             poSimpleSource->m_bDropRefOnSrcBand = false;
    2496             :         }
    2497             :         else
    2498             :         {
    2499      142354 :             poSrcBandDataset->Reference();
    2500             :         }
    2501             :     }
    2502      142419 : }
    2503             : 
    2504             : /************************************************************************/
    2505             : /*                          AddSimpleSource()                           */
    2506             : /************************************************************************/
    2507             : 
    2508         377 : CPLErr VRTSourcedRasterBand::AddSimpleSource(
    2509             :     const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
    2510             :     double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
    2511             :     double dfDstXSize, double dfDstYSize, const char *pszResampling,
    2512             :     double dfNoDataValueIn)
    2513             : 
    2514             : {
    2515             :     /* -------------------------------------------------------------------- */
    2516             :     /*      Create source.                                                  */
    2517             :     /* -------------------------------------------------------------------- */
    2518         377 :     VRTSimpleSource *poSimpleSource = nullptr;
    2519             : 
    2520         377 :     if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
    2521             :     {
    2522           0 :         auto poAveragedSource = new VRTAveragedSource();
    2523           0 :         poSimpleSource = poAveragedSource;
    2524           0 :         if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2525           0 :             poAveragedSource->SetNoDataValue(dfNoDataValueIn);
    2526             :     }
    2527             :     else
    2528             :     {
    2529         377 :         poSimpleSource = new VRTSimpleSource();
    2530         377 :         if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2531           0 :             CPLError(
    2532             :                 CE_Warning, CPLE_AppDefined,
    2533             :                 "NODATA setting not currently supported for nearest  "
    2534             :                 "neighbour sampled simple sources on Virtual Datasources.");
    2535             :     }
    2536             : 
    2537         377 :     poSimpleSource->SetSrcBand(pszFilename, nBandIn);
    2538             : 
    2539         377 :     poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
    2540         377 :     poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
    2541             : 
    2542             :     /* -------------------------------------------------------------------- */
    2543             :     /*      add to list.                                                    */
    2544             :     /* -------------------------------------------------------------------- */
    2545         377 :     return AddSource(poSimpleSource);
    2546             : }
    2547             : 
    2548             : /************************************************************************/
    2549             : /*                          AddSimpleSource()                           */
    2550             : /************************************************************************/
    2551             : 
    2552       68243 : CPLErr VRTSourcedRasterBand::AddSimpleSource(
    2553             :     GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
    2554             :     double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
    2555             :     double dfDstXSize, double dfDstYSize, const char *pszResampling,
    2556             :     double dfNoDataValueIn)
    2557             : 
    2558             : {
    2559             :     /* -------------------------------------------------------------------- */
    2560             :     /*      Create source.                                                  */
    2561             :     /* -------------------------------------------------------------------- */
    2562       68243 :     VRTSimpleSource *poSimpleSource = nullptr;
    2563             : 
    2564       68243 :     if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
    2565             :     {
    2566           0 :         auto poAveragedSource = new VRTAveragedSource();
    2567           0 :         poSimpleSource = poAveragedSource;
    2568           0 :         if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2569           0 :             poAveragedSource->SetNoDataValue(dfNoDataValueIn);
    2570             :     }
    2571             :     else
    2572             :     {
    2573       68243 :         poSimpleSource = new VRTSimpleSource();
    2574       68243 :         if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2575           0 :             CPLError(
    2576             :                 CE_Warning, CPLE_AppDefined,
    2577             :                 "NODATA setting not currently supported for "
    2578             :                 "neighbour sampled simple sources on Virtual Datasources.");
    2579             :     }
    2580             : 
    2581       68243 :     ConfigureSource(poSimpleSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
    2582             :                     dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
    2583             :                     dfDstYSize);
    2584             : 
    2585             :     /* -------------------------------------------------------------------- */
    2586             :     /*      add to list.                                                    */
    2587             :     /* -------------------------------------------------------------------- */
    2588       68243 :     return AddSource(poSimpleSource);
    2589             : }
    2590             : 
    2591             : /************************************************************************/
    2592             : /*                         AddMaskBandSource()                          */
    2593             : /************************************************************************/
    2594             : 
    2595             : // poSrcBand is not the mask band, but the band from which the mask band is
    2596             : // taken.
    2597          32 : CPLErr VRTSourcedRasterBand::AddMaskBandSource(
    2598             :     GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
    2599             :     double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
    2600             :     double dfDstXSize, double dfDstYSize)
    2601             : {
    2602             :     /* -------------------------------------------------------------------- */
    2603             :     /*      Create source.                                                  */
    2604             :     /* -------------------------------------------------------------------- */
    2605          32 :     VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
    2606             : 
    2607          32 :     ConfigureSource(poSimpleSource, poSrcBand, TRUE, dfSrcXOff, dfSrcYOff,
    2608             :                     dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
    2609             :                     dfDstYSize);
    2610             : 
    2611             :     /* -------------------------------------------------------------------- */
    2612             :     /*      add to list.                                                    */
    2613             :     /* -------------------------------------------------------------------- */
    2614          32 :     return AddSource(poSimpleSource);
    2615             : }
    2616             : 
    2617             : /*! @endcond */
    2618             : 
    2619             : /************************************************************************/
    2620             : /*                         VRTAddSimpleSource()                         */
    2621             : /************************************************************************/
    2622             : 
    2623             : /**
    2624             :  * @see VRTSourcedRasterBand::AddSimpleSource().
    2625             :  */
    2626             : 
    2627         968 : CPLErr CPL_STDCALL VRTAddSimpleSource(VRTSourcedRasterBandH hVRTBand,
    2628             :                                       GDALRasterBandH hSrcBand, int nSrcXOff,
    2629             :                                       int nSrcYOff, int nSrcXSize,
    2630             :                                       int nSrcYSize, int nDstXOff, int nDstYOff,
    2631             :                                       int nDstXSize, int nDstYSize,
    2632             :                                       const char *pszResampling,
    2633             :                                       double dfNoDataValue)
    2634             : {
    2635         968 :     VALIDATE_POINTER1(hVRTBand, "VRTAddSimpleSource", CE_Failure);
    2636             : 
    2637         968 :     return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSimpleSource(
    2638             :         reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
    2639             :         nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
    2640         968 :         pszResampling, dfNoDataValue);
    2641             : }
    2642             : 
    2643             : /*! @cond Doxygen_Suppress */
    2644             : 
    2645             : /************************************************************************/
    2646             : /*                          AddComplexSource()                          */
    2647             : /************************************************************************/
    2648             : 
    2649          29 : CPLErr VRTSourcedRasterBand::AddComplexSource(
    2650             :     const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
    2651             :     double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
    2652             :     double dfDstXSize, double dfDstYSize, double dfScaleOff,
    2653             :     double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
    2654             : 
    2655             : {
    2656             :     /* -------------------------------------------------------------------- */
    2657             :     /*      Create source.                                                  */
    2658             :     /* -------------------------------------------------------------------- */
    2659          29 :     VRTComplexSource *const poSource = new VRTComplexSource();
    2660             : 
    2661          29 :     poSource->SetSrcBand(pszFilename, nBandIn);
    2662             : 
    2663          29 :     poSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
    2664          29 :     poSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
    2665             : 
    2666             :     /* -------------------------------------------------------------------- */
    2667             :     /*      Set complex parameters.                                         */
    2668             :     /* -------------------------------------------------------------------- */
    2669          29 :     if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2670           8 :         poSource->SetNoDataValue(dfNoDataValueIn);
    2671             : 
    2672          29 :     if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
    2673           4 :         poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
    2674             : 
    2675          29 :     poSource->SetColorTableComponent(nColorTableComponent);
    2676             : 
    2677             :     /* -------------------------------------------------------------------- */
    2678             :     /*      add to list.                                                    */
    2679             :     /* -------------------------------------------------------------------- */
    2680          29 :     return AddSource(poSource);
    2681             : }
    2682             : 
    2683             : /************************************************************************/
    2684             : /*                          AddComplexSource()                          */
    2685             : /************************************************************************/
    2686             : 
    2687          22 : CPLErr VRTSourcedRasterBand::AddComplexSource(
    2688             :     GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
    2689             :     double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
    2690             :     double dfDstXSize, double dfDstYSize, double dfScaleOff,
    2691             :     double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
    2692             : 
    2693             : {
    2694             :     /* -------------------------------------------------------------------- */
    2695             :     /*      Create source.                                                  */
    2696             :     /* -------------------------------------------------------------------- */
    2697          22 :     VRTComplexSource *const poSource = new VRTComplexSource();
    2698             : 
    2699          22 :     ConfigureSource(poSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
    2700             :                     dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
    2701             :                     dfDstYSize);
    2702             : 
    2703             :     /* -------------------------------------------------------------------- */
    2704             :     /*      Set complex parameters.                                         */
    2705             :     /* -------------------------------------------------------------------- */
    2706          22 :     if (dfNoDataValueIn != VRT_NODATA_UNSET)
    2707          11 :         poSource->SetNoDataValue(dfNoDataValueIn);
    2708             : 
    2709          22 :     if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
    2710          13 :         poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
    2711             : 
    2712          22 :     poSource->SetColorTableComponent(nColorTableComponent);
    2713             : 
    2714             :     /* -------------------------------------------------------------------- */
    2715             :     /*      add to list.                                                    */
    2716             :     /* -------------------------------------------------------------------- */
    2717          22 :     return AddSource(poSource);
    2718             : }
    2719             : 
    2720             : /*! @endcond */
    2721             : 
    2722             : /************************************************************************/
    2723             : /*                        VRTAddComplexSource()                         */
    2724             : /************************************************************************/
    2725             : 
    2726             : /**
    2727             :  * @see VRTSourcedRasterBand::AddComplexSource().
    2728             :  */
    2729             : 
    2730           0 : CPLErr CPL_STDCALL VRTAddComplexSource(
    2731             :     VRTSourcedRasterBandH hVRTBand, GDALRasterBandH hSrcBand, int nSrcXOff,
    2732             :     int nSrcYOff, int nSrcXSize, int nSrcYSize, int nDstXOff, int nDstYOff,
    2733             :     int nDstXSize, int nDstYSize, double dfScaleOff, double dfScaleRatio,
    2734             :     double dfNoDataValue)
    2735             : {
    2736           0 :     VALIDATE_POINTER1(hVRTBand, "VRTAddComplexSource", CE_Failure);
    2737             : 
    2738           0 :     return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
    2739             :         reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
    2740             :         nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
    2741           0 :         dfScaleOff, dfScaleRatio, dfNoDataValue);
    2742             : }
    2743             : 
    2744             : /*! @cond Doxygen_Suppress */
    2745             : 
    2746             : /************************************************************************/
    2747             : /*                           AddFuncSource()                            */
    2748             : /************************************************************************/
    2749             : 
    2750          12 : CPLErr VRTSourcedRasterBand::AddFuncSource(VRTImageReadFunc pfnReadFunc,
    2751             :                                            void *pCBData,
    2752             :                                            double dfNoDataValueIn)
    2753             : 
    2754             : {
    2755             :     /* -------------------------------------------------------------------- */
    2756             :     /*      Create source.                                                  */
    2757             :     /* -------------------------------------------------------------------- */
    2758          12 :     VRTFuncSource *const poFuncSource = new VRTFuncSource;
    2759             : 
    2760          12 :     poFuncSource->fNoDataValue = static_cast<float>(dfNoDataValueIn);
    2761          12 :     poFuncSource->pfnReadFunc = pfnReadFunc;
    2762          12 :     poFuncSource->pCBData = pCBData;
    2763          12 :     poFuncSource->eType = GetRasterDataType();
    2764             : 
    2765             :     /* -------------------------------------------------------------------- */
    2766             :     /*      add to list.                                                    */
    2767             :     /* -------------------------------------------------------------------- */
    2768          12 :     return AddSource(poFuncSource);
    2769             : }
    2770             : 
    2771             : /*! @endcond */
    2772             : 
    2773             : /************************************************************************/
    2774             : /*                          VRTAddFuncSource()                          */
    2775             : /************************************************************************/
    2776             : 
    2777             : /**
    2778             :  * @see VRTSourcedRasterBand::AddFuncSource().
    2779             :  */
    2780             : 
    2781           0 : CPLErr CPL_STDCALL VRTAddFuncSource(VRTSourcedRasterBandH hVRTBand,
    2782             :                                     VRTImageReadFunc pfnReadFunc, void *pCBData,
    2783             :                                     double dfNoDataValue)
    2784             : {
    2785           0 :     VALIDATE_POINTER1(hVRTBand, "VRTAddFuncSource", CE_Failure);
    2786             : 
    2787           0 :     return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddFuncSource(
    2788           0 :         pfnReadFunc, pCBData, dfNoDataValue);
    2789             : }
    2790             : 
    2791             : /*! @cond Doxygen_Suppress */
    2792             : 
    2793             : /************************************************************************/
    2794             : /*                       GetMetadataDomainList()                        */
    2795             : /************************************************************************/
    2796             : 
    2797           0 : char **VRTSourcedRasterBand::GetMetadataDomainList()
    2798             : {
    2799           0 :     return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
    2800           0 :                         "LocationInfo");
    2801             : }
    2802             : 
    2803             : /************************************************************************/
    2804             : /*                          GetMetadataItem()                           */
    2805             : /************************************************************************/
    2806             : 
    2807      255754 : const char *VRTSourcedRasterBand::GetMetadataItem(const char *pszName,
    2808             :                                                   const char *pszDomain)
    2809             : 
    2810             : {
    2811             :     /* ==================================================================== */
    2812             :     /*      LocationInfo handling.                                          */
    2813             :     /* ==================================================================== */
    2814      255754 :     if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
    2815           3 :         (STARTS_WITH_CI(pszName, "Pixel_") ||
    2816           0 :          STARTS_WITH_CI(pszName, "GeoPixel_")))
    2817             :     {
    2818             :         /* --------------------------------------------------------------------
    2819             :          */
    2820             :         /*      What pixel are we aiming at? */
    2821             :         /* --------------------------------------------------------------------
    2822             :          */
    2823           3 :         int iPixel = 0;
    2824           3 :         int iLine = 0;
    2825             : 
    2826           3 :         if (STARTS_WITH_CI(pszName, "Pixel_"))
    2827             :         {
    2828             :             // TODO(schwehr): Replace sscanf.
    2829           3 :             if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
    2830           0 :                 return nullptr;
    2831             :         }
    2832           0 :         else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
    2833             :         {
    2834           0 :             const double dfGeoX = CPLAtof(pszName + 9);
    2835           0 :             const char *const pszUnderscore = strchr(pszName + 9, '_');
    2836           0 :             if (!pszUnderscore)
    2837           0 :                 return nullptr;
    2838           0 :             const double dfGeoY = CPLAtof(pszUnderscore + 1);
    2839             : 
    2840           0 :             if (GetDataset() == nullptr)
    2841           0 :                 return nullptr;
    2842             : 
    2843           0 :             GDALGeoTransform gt, invGT;
    2844           0 :             if (GetDataset()->GetGeoTransform(gt) != CE_None ||
    2845           0 :                 !gt.GetInverse(invGT))
    2846             :             {
    2847           0 :                 return nullptr;
    2848             :             }
    2849             : 
    2850           0 :             iPixel = static_cast<int>(
    2851           0 :                 floor(invGT[0] + invGT[1] * dfGeoX + invGT[2] * dfGeoY));
    2852           0 :             iLine = static_cast<int>(
    2853           0 :                 floor(invGT[3] + invGT[4] * dfGeoX + invGT[5] * dfGeoY));
    2854             :         }
    2855             :         else
    2856             :         {
    2857           0 :             return nullptr;
    2858             :         }
    2859             : 
    2860           6 :         if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
    2861           3 :             iLine >= GetYSize())
    2862           0 :             return nullptr;
    2863             : 
    2864             :         /* --------------------------------------------------------------------
    2865             :          */
    2866             :         /*      Find the file(s) at this location. */
    2867             :         /* --------------------------------------------------------------------
    2868             :          */
    2869           3 :         char **papszFileList = nullptr;
    2870           3 :         int nListSize = 0;     // keep it in this scope
    2871           3 :         int nListMaxSize = 0;  // keep it in this scope
    2872             :         CPLHashSet *const hSetFiles =
    2873           3 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
    2874             : 
    2875           6 :         for (auto &poSource : m_papoSources)
    2876             :         {
    2877           3 :             if (!poSource->IsSimpleSource())
    2878           0 :                 continue;
    2879             : 
    2880             :             VRTSimpleSource *const poSrc =
    2881           3 :                 static_cast<VRTSimpleSource *>(poSource.get());
    2882             : 
    2883           3 :             double dfReqXOff = 0.0;
    2884           3 :             double dfReqYOff = 0.0;
    2885           3 :             double dfReqXSize = 0.0;
    2886           3 :             double dfReqYSize = 0.0;
    2887           3 :             int nReqXOff = 0;
    2888           3 :             int nReqYOff = 0;
    2889           3 :             int nReqXSize = 0;
    2890           3 :             int nReqYSize = 0;
    2891           3 :             int nOutXOff = 0;
    2892           3 :             int nOutYOff = 0;
    2893           3 :             int nOutXSize = 0;
    2894           3 :             int nOutYSize = 0;
    2895             : 
    2896           3 :             bool bError = false;
    2897           3 :             if (!poSrc->GetSrcDstWindow(
    2898             :                     iPixel, iLine, 1, 1, 1, 1, GRIORA_NearestNeighbour,
    2899             :                     &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
    2900             :                     &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    2901             :                     &nOutXSize, &nOutYSize, bError))
    2902             :             {
    2903           0 :                 if (bError)
    2904             :                 {
    2905           0 :                     CSLDestroy(papszFileList);
    2906           0 :                     CPLHashSetDestroy(hSetFiles);
    2907           0 :                     return nullptr;
    2908             :                 }
    2909           0 :                 continue;
    2910             :             }
    2911             : 
    2912           3 :             poSrc->GetFileList(&papszFileList, &nListSize, &nListMaxSize,
    2913           3 :                                hSetFiles);
    2914             :         }
    2915             : 
    2916             :         /* --------------------------------------------------------------------
    2917             :          */
    2918             :         /*      Format into XML. */
    2919             :         /* --------------------------------------------------------------------
    2920             :          */
    2921           3 :         m_osLastLocationInfo = "<LocationInfo>";
    2922           6 :         for (int i = 0; i < nListSize && papszFileList[i] != nullptr; i++)
    2923             :         {
    2924           3 :             m_osLastLocationInfo += "<File>";
    2925             :             char *const pszXMLEscaped =
    2926           3 :                 CPLEscapeString(papszFileList[i], -1, CPLES_XML);
    2927           3 :             m_osLastLocationInfo += pszXMLEscaped;
    2928           3 :             CPLFree(pszXMLEscaped);
    2929           3 :             m_osLastLocationInfo += "</File>";
    2930             :         }
    2931           3 :         m_osLastLocationInfo += "</LocationInfo>";
    2932             : 
    2933           3 :         CSLDestroy(papszFileList);
    2934           3 :         CPLHashSetDestroy(hSetFiles);
    2935             : 
    2936           3 :         return m_osLastLocationInfo.c_str();
    2937             :     }
    2938             : 
    2939             :     /* ==================================================================== */
    2940             :     /*      Other domains.                                                  */
    2941             :     /* ==================================================================== */
    2942             : 
    2943      255751 :     return GDALRasterBand::GetMetadataItem(pszName, pszDomain);
    2944             : }
    2945             : 
    2946             : /************************************************************************/
    2947             : /*                            GetMetadata()                             */
    2948             : /************************************************************************/
    2949             : 
    2950       14340 : CSLConstList VRTSourcedRasterBand::GetMetadata(const char *pszDomain)
    2951             : 
    2952             : {
    2953             :     /* ==================================================================== */
    2954             :     /*      vrt_sources domain handling.                                    */
    2955             :     /* ==================================================================== */
    2956       14340 :     if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
    2957             :     {
    2958           1 :         if (static_cast<size_t>(m_aosSourceList.size()) != m_papoSources.size())
    2959             :         {
    2960           1 :             m_aosSourceList.clear();
    2961             : 
    2962             :             // Process SimpleSources
    2963           2 :             for (int iSource = 0;
    2964           2 :                  iSource < static_cast<int>(m_papoSources.size()); iSource++)
    2965             :             {
    2966             :                 CPLXMLNode *const psXMLSrc =
    2967           1 :                     m_papoSources[iSource]->SerializeToXML(nullptr);
    2968           1 :                 if (psXMLSrc == nullptr)
    2969           0 :                     continue;
    2970             : 
    2971           1 :                 char *const pszXML = CPLSerializeXMLTree(psXMLSrc);
    2972             : 
    2973             :                 m_aosSourceList.AddString(
    2974           1 :                     CPLSPrintf("source_%d=%s", iSource, pszXML));
    2975           1 :                 CPLFree(pszXML);
    2976           1 :                 CPLDestroyXMLNode(psXMLSrc);
    2977             :             }
    2978             :         }
    2979           1 :         return m_aosSourceList.List();
    2980             :     }
    2981             : 
    2982             :     /* ==================================================================== */
    2983             :     /*      Other domains.                                                  */
    2984             :     /* ==================================================================== */
    2985             : 
    2986       14339 :     return GDALRasterBand::GetMetadata(pszDomain);
    2987             : }
    2988             : 
    2989             : /************************************************************************/
    2990             : /*                          SetMetadataItem()                           */
    2991             : /************************************************************************/
    2992             : 
    2993      133623 : CPLErr VRTSourcedRasterBand::SetMetadataItem(const char *pszName,
    2994             :                                              const char *pszValue,
    2995             :                                              const char *pszDomain)
    2996             : 
    2997             : {
    2998             : #if DEBUG_VERBOSE
    2999             :     CPLDebug("VRT", "VRTSourcedRasterBand::SetMetadataItem(%s,%s,%s)\n",
    3000             :              pszName, pszValue ? pszValue : "(null)",
    3001             :              pszDomain ? pszDomain : "(null)");
    3002             : #endif
    3003             : 
    3004      133623 :     if (pszDomain != nullptr && EQUAL(pszDomain, "new_vrt_sources"))
    3005             :     {
    3006           0 :         VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
    3007           1 :             GetGDALDriverManager()->GetDriverByName("VRT"));
    3008           1 :         if (poDriver)
    3009             :         {
    3010           1 :             CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
    3011           1 :             if (psTree == nullptr)
    3012           0 :                 return CE_Failure;
    3013             : 
    3014           1 :             auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
    3015           1 :             if (l_poDS == nullptr)
    3016             :             {
    3017           0 :                 CPLDestroyXMLNode(psTree);
    3018           0 :                 return CE_Failure;
    3019             :             }
    3020           2 :             VRTSource *const poSource = poDriver->ParseSource(
    3021           1 :                 psTree, nullptr, l_poDS->m_oMapSharedSources);
    3022           1 :             CPLDestroyXMLNode(psTree);
    3023             : 
    3024           1 :             if (poSource != nullptr)
    3025           1 :                 return AddSource(poSource);
    3026             :         }
    3027             : 
    3028           0 :         return CE_Failure;
    3029             :     }
    3030      133622 :     else if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
    3031             :     {
    3032           1 :         int iSource = 0;
    3033             :         // TODO(schwehr): Replace sscanf.
    3034           2 :         if (sscanf(pszName, "source_%d", &iSource) != 1 || iSource < 0 ||
    3035           1 :             iSource >= static_cast<int>(m_papoSources.size()))
    3036             :         {
    3037           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3038             :                      "%s metadata item name is not recognized. "
    3039             :                      "Should be between source_0 and source_%d",
    3040           0 :                      pszName, static_cast<int>(m_papoSources.size()) - 1);
    3041           0 :             return CE_Failure;
    3042             :         }
    3043           0 :         VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
    3044           1 :             GetGDALDriverManager()->GetDriverByName("VRT"));
    3045           1 :         if (poDriver)
    3046             :         {
    3047           1 :             CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
    3048           1 :             if (psTree == nullptr)
    3049           1 :                 return CE_Failure;
    3050             : 
    3051           1 :             auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
    3052           1 :             if (l_poDS == nullptr)
    3053             :             {
    3054           0 :                 CPLDestroyXMLNode(psTree);
    3055           0 :                 return CE_Failure;
    3056             :             }
    3057             :             auto poSource = std::unique_ptr<VRTSource>(poDriver->ParseSource(
    3058           1 :                 psTree, nullptr, l_poDS->m_oMapSharedSources));
    3059           1 :             CPLDestroyXMLNode(psTree);
    3060             : 
    3061           1 :             if (poSource != nullptr)
    3062             :             {
    3063           1 :                 m_papoSources[iSource] = std::move(poSource);
    3064           1 :                 static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
    3065           1 :                 return CE_None;
    3066             :             }
    3067             :         }
    3068           0 :         return CE_Failure;
    3069             :     }
    3070             : 
    3071      133621 :     return VRTRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
    3072             : }
    3073             : 
    3074             : /************************************************************************/
    3075             : /*                            SetMetadata()                             */
    3076             : /************************************************************************/
    3077             : 
    3078       70592 : CPLErr VRTSourcedRasterBand::SetMetadata(CSLConstList papszNewMD,
    3079             :                                          const char *pszDomain)
    3080             : 
    3081             : {
    3082       70592 :     if (pszDomain != nullptr && (EQUAL(pszDomain, "new_vrt_sources") ||
    3083       70592 :                                  EQUAL(pszDomain, "vrt_sources")))
    3084             :     {
    3085           0 :         VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
    3086           8 :             GetGDALDriverManager()->GetDriverByName("VRT"));
    3087           8 :         if (!poDriver)
    3088           0 :             return CE_Failure;
    3089             : 
    3090           8 :         if (EQUAL(pszDomain, "vrt_sources"))
    3091             :         {
    3092           8 :             m_papoSources.clear();
    3093             :         }
    3094             : 
    3095           6 :         for (const char *const pszMDItem :
    3096          20 :              cpl::Iterate(CSLConstList(papszNewMD)))
    3097             :         {
    3098           8 :             const char *const pszXML = CPLParseNameValue(pszMDItem, nullptr);
    3099           8 :             CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
    3100           8 :             if (!psTree)
    3101           0 :                 return CE_Failure;
    3102             : 
    3103           8 :             auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
    3104           8 :             if (l_poDS == nullptr)
    3105             :             {
    3106           0 :                 return CE_Failure;
    3107             :             }
    3108           8 :             VRTSource *const poSource = poDriver->ParseSource(
    3109           8 :                 psTree.get(), nullptr, l_poDS->m_oMapSharedSources);
    3110           8 :             if (poSource == nullptr)
    3111           2 :                 return CE_Failure;
    3112             : 
    3113           6 :             const CPLErr eErr = AddSource(poSource);
    3114             :             // cppcheck-suppress knownConditionTrueFalse
    3115           6 :             if (eErr != CE_None)
    3116           0 :                 return eErr;
    3117             :         }
    3118             : 
    3119           6 :         return CE_None;
    3120             :     }
    3121             : 
    3122       70584 :     return VRTRasterBand::SetMetadata(papszNewMD, pszDomain);
    3123             : }
    3124             : 
    3125             : /************************************************************************/
    3126             : /*                            GetFileList()                             */
    3127             : /************************************************************************/
    3128             : 
    3129          68 : void VRTSourcedRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
    3130             :                                        int *pnMaxSize, CPLHashSet *hSetFiles)
    3131             : {
    3132         179 :     for (auto &poSource : m_papoSources)
    3133             :     {
    3134         111 :         poSource->GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
    3135             :     }
    3136             : 
    3137          68 :     VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
    3138          68 : }
    3139             : 
    3140             : /************************************************************************/
    3141             : /*                       CloseDependentDatasets()                       */
    3142             : /************************************************************************/
    3143             : 
    3144      141974 : int VRTSourcedRasterBand::CloseDependentDatasets()
    3145             : {
    3146      141974 :     int ret = VRTRasterBand::CloseDependentDatasets();
    3147             : 
    3148      141974 :     if (m_papoSources.empty())
    3149         432 :         return ret;
    3150             : 
    3151      141542 :     m_papoSources.clear();
    3152             : 
    3153      141542 :     return TRUE;
    3154             : }
    3155             : 
    3156             : /************************************************************************/
    3157             : /*                             FlushCache()                             */
    3158             : /************************************************************************/
    3159             : 
    3160      208125 : CPLErr VRTSourcedRasterBand::FlushCache(bool bAtClosing)
    3161             : {
    3162      208125 :     CPLErr eErr = VRTRasterBand::FlushCache(bAtClosing);
    3163      521510 :     for (size_t i = 0; i < m_papoSources.size() && eErr == CE_None; i++)
    3164             :     {
    3165      313385 :         eErr = m_papoSources[i]->FlushCache(bAtClosing);
    3166             :     }
    3167      208125 :     return eErr;
    3168             : }
    3169             : 
    3170             : /************************************************************************/
    3171             : /*                        RemoveCoveredSources()                        */
    3172             : /************************************************************************/
    3173             : 
    3174             : /** Remove sources that are covered by other sources.
    3175             :  *
    3176             :  * This method removes sources that are covered entirely by (one or several)
    3177             :  * sources of higher priority (even if they declare a nodata setting).
    3178             :  * This optimizes the size of the VRT and the rendering time.
    3179             :  */
    3180          14 : void VRTSourcedRasterBand::RemoveCoveredSources(CSLConstList papszOptions)
    3181             : {
    3182             : #ifndef HAVE_GEOS
    3183             :     if (CPLTestBool(CSLFetchNameValueDef(
    3184             :             papszOptions, "EMIT_ERROR_IF_GEOS_NOT_AVAILABLE", "TRUE")))
    3185             :     {
    3186             :         CPLError(CE_Failure, CPLE_NotSupported,
    3187             :                  "RemoveCoveredSources() not implemented in builds "
    3188             :                  "without GEOS support");
    3189             :     }
    3190             : #else
    3191             :     (void)papszOptions;
    3192             : 
    3193             :     CPLRectObj globalBounds;
    3194          14 :     globalBounds.minx = 0;
    3195          14 :     globalBounds.miny = 0;
    3196          14 :     globalBounds.maxx = nRasterXSize;
    3197          14 :     globalBounds.maxy = nRasterYSize;
    3198             : 
    3199             :     // Create an index with the bbox of all sources
    3200          14 :     CPLQuadTree *hTree = CPLQuadTreeCreate(&globalBounds, nullptr);
    3201          38 :     for (int i = 0; i < static_cast<int>(m_papoSources.size()); i++)
    3202             :     {
    3203          24 :         if (m_papoSources[i]->IsSimpleSource())
    3204             :         {
    3205             :             VRTSimpleSource *poSS =
    3206          24 :                 cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
    3207          24 :             void *hFeature =
    3208          24 :                 reinterpret_cast<void *>(static_cast<uintptr_t>(i));
    3209             :             CPLRectObj rect;
    3210          24 :             rect.minx = std::max(0.0, poSS->m_dfDstXOff);
    3211          24 :             rect.miny = std::max(0.0, poSS->m_dfDstYOff);
    3212          48 :             rect.maxx = std::min(double(nRasterXSize),
    3213          24 :                                  poSS->m_dfDstXOff + poSS->m_dfDstXSize);
    3214          48 :             rect.maxy = std::min(double(nRasterYSize),
    3215          24 :                                  poSS->m_dfDstYOff + poSS->m_dfDstYSize);
    3216          24 :             CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
    3217             :         }
    3218             :     }
    3219             : 
    3220          38 :     for (int i = 0; i < static_cast<int>(m_papoSources.size()); i++)
    3221             :     {
    3222          24 :         if (m_papoSources[i]->IsSimpleSource())
    3223             :         {
    3224             :             VRTSimpleSource *poSS =
    3225          24 :                 cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
    3226             :             CPLRectObj rect;
    3227          24 :             rect.minx = std::max(0.0, poSS->m_dfDstXOff);
    3228          24 :             rect.miny = std::max(0.0, poSS->m_dfDstYOff);
    3229          48 :             rect.maxx = std::min(double(nRasterXSize),
    3230          24 :                                  poSS->m_dfDstXOff + poSS->m_dfDstXSize);
    3231          48 :             rect.maxy = std::min(double(nRasterYSize),
    3232          24 :                                  poSS->m_dfDstYOff + poSS->m_dfDstYSize);
    3233             : 
    3234             :             // Find sources whose extent intersect with the current one
    3235          24 :             int nFeatureCount = 0;
    3236             :             void **pahFeatures =
    3237          24 :                 CPLQuadTreeSearch(hTree, &rect, &nFeatureCount);
    3238             : 
    3239             :             // Compute the bounding box of those sources, only if they are
    3240             :             // on top of the current one
    3241             :             CPLRectObj rectIntersecting;
    3242          24 :             rectIntersecting.minx = std::numeric_limits<double>::max();
    3243          24 :             rectIntersecting.miny = std::numeric_limits<double>::max();
    3244          24 :             rectIntersecting.maxx = -std::numeric_limits<double>::max();
    3245          24 :             rectIntersecting.maxy = -std::numeric_limits<double>::max();
    3246          61 :             for (int j = 0; j < nFeatureCount; j++)
    3247             :             {
    3248          37 :                 const int curFeature = static_cast<int>(
    3249          37 :                     reinterpret_cast<uintptr_t>(pahFeatures[j]));
    3250          37 :                 if (curFeature > i)
    3251             :                 {
    3252             :                     VRTSimpleSource *poOtherSS =
    3253          13 :                         cpl::down_cast<VRTSimpleSource *>(
    3254          13 :                             m_papoSources[curFeature].get());
    3255          13 :                     rectIntersecting.minx =
    3256          13 :                         std::min(rectIntersecting.minx, poOtherSS->m_dfDstXOff);
    3257          13 :                     rectIntersecting.miny =
    3258          13 :                         std::min(rectIntersecting.miny, poOtherSS->m_dfDstYOff);
    3259          13 :                     rectIntersecting.maxx = std::max(
    3260             :                         rectIntersecting.maxx,
    3261          13 :                         poOtherSS->m_dfDstXOff + poOtherSS->m_dfDstXSize);
    3262          13 :                     rectIntersecting.maxy = std::max(
    3263             :                         rectIntersecting.maxy,
    3264          13 :                         poOtherSS->m_dfDstYOff + poOtherSS->m_dfDstXSize);
    3265             :                 }
    3266             :             }
    3267             : 
    3268             :             // If the boundinx box of those sources overlap the current one,
    3269             :             // then compute their union, and check if it contains the current
    3270             :             // source
    3271          24 :             if (rectIntersecting.minx <= rect.minx &&
    3272           7 :                 rectIntersecting.miny <= rect.miny &&
    3273           7 :                 rectIntersecting.maxx >= rect.maxx &&
    3274           7 :                 rectIntersecting.maxy >= rect.maxy)
    3275             :             {
    3276          14 :                 OGRPolygon oPoly;
    3277             :                 {
    3278           7 :                     auto poLR = new OGRLinearRing();
    3279           7 :                     poLR->addPoint(rect.minx, rect.miny);
    3280           7 :                     poLR->addPoint(rect.minx, rect.maxy);
    3281           7 :                     poLR->addPoint(rect.maxx, rect.maxy);
    3282           7 :                     poLR->addPoint(rect.maxx, rect.miny);
    3283           7 :                     poLR->addPoint(rect.minx, rect.miny);
    3284           7 :                     oPoly.addRingDirectly(poLR);
    3285             :                 }
    3286             : 
    3287           7 :                 std::unique_ptr<OGRGeometry> poUnion;
    3288          24 :                 for (int j = 0; j < nFeatureCount; j++)
    3289             :                 {
    3290          17 :                     const int curFeature = static_cast<int>(
    3291          17 :                         reinterpret_cast<uintptr_t>(pahFeatures[j]));
    3292          17 :                     if (curFeature > i)
    3293             :                     {
    3294             :                         VRTSimpleSource *poOtherSS =
    3295          10 :                             cpl::down_cast<VRTSimpleSource *>(
    3296          10 :                                 m_papoSources[curFeature].get());
    3297             :                         CPLRectObj otherRect;
    3298          10 :                         otherRect.minx = std::max(0.0, poOtherSS->m_dfDstXOff);
    3299          10 :                         otherRect.miny = std::max(0.0, poOtherSS->m_dfDstYOff);
    3300          20 :                         otherRect.maxx = std::min(double(nRasterXSize),
    3301          20 :                                                   poOtherSS->m_dfDstXOff +
    3302          10 :                                                       poOtherSS->m_dfDstXSize);
    3303          20 :                         otherRect.maxy = std::min(double(nRasterYSize),
    3304          20 :                                                   poOtherSS->m_dfDstYOff +
    3305          10 :                                                       poOtherSS->m_dfDstYSize);
    3306          20 :                         OGRPolygon oOtherPoly;
    3307             :                         {
    3308          10 :                             auto poLR = new OGRLinearRing();
    3309          10 :                             poLR->addPoint(otherRect.minx, otherRect.miny);
    3310          10 :                             poLR->addPoint(otherRect.minx, otherRect.maxy);
    3311          10 :                             poLR->addPoint(otherRect.maxx, otherRect.maxy);
    3312          10 :                             poLR->addPoint(otherRect.maxx, otherRect.miny);
    3313          10 :                             poLR->addPoint(otherRect.minx, otherRect.miny);
    3314          10 :                             oOtherPoly.addRingDirectly(poLR);
    3315             :                         }
    3316          10 :                         if (poUnion == nullptr)
    3317           7 :                             poUnion.reset(oOtherPoly.clone());
    3318             :                         else
    3319           3 :                             poUnion.reset(oOtherPoly.Union(poUnion.get()));
    3320             :                     }
    3321             :                 }
    3322             : 
    3323           7 :                 if (poUnion != nullptr && poUnion->Contains(&oPoly))
    3324             :                 {
    3325             :                     // We can remove the current source
    3326           7 :                     m_papoSources[i].reset();
    3327             :                 }
    3328             :             }
    3329          24 :             CPLFree(pahFeatures);
    3330             : 
    3331          24 :             void *hFeature =
    3332          24 :                 reinterpret_cast<void *>(static_cast<uintptr_t>(i));
    3333          24 :             CPLQuadTreeRemove(hTree, hFeature, &rect);
    3334             :         }
    3335             :     }
    3336             : 
    3337             :     // Compact the papoSources array
    3338           0 :     m_papoSources.erase(std::remove_if(m_papoSources.begin(),
    3339             :                                        m_papoSources.end(),
    3340          24 :                                        [](const std::unique_ptr<VRTSource> &src)
    3341          38 :                                        { return src.get() == nullptr; }),
    3342          28 :                         m_papoSources.end());
    3343             : 
    3344          14 :     CPLQuadTreeDestroy(hTree);
    3345             : #endif
    3346          14 : }
    3347             : 
    3348             : /*! @endcond */

Generated by: LCOV version 1.14