LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1249 1439 86.8 %
Date: 2024-11-21 22:18:42 Functions: 42 47 89.4 %

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

Generated by: LCOV version 1.14