LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1302 1507 86.4 %
Date: 2026-03-04 01:45:47 Functions: 46 52 88.5 %

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

Generated by: LCOV version 1.14