LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1308 1499 87.3 %
Date: 2025-08-01 10:10:57 Functions: 43 48 89.6 %

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

Generated by: LCOV version 1.14