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

Generated by: LCOV version 1.14