LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1292 1482 87.2 %
Date: 2025-09-10 17:48:50 Functions: 45 50 90.0 %

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

Generated by: LCOV version 1.14