LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsourcedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1285 1477 87.0 %
Date: 2025-01-18 12:42:00 Functions: 43 48 89.6 %

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

Generated by: LCOV version 1.14