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

Generated by: LCOV version 1.14