LCOV - code coverage report
Current view: top level - frmts/vrt - vrtwarped.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 694 897 77.4 %
Date: 2024-11-21 22:18:42 Functions: 41 47 87.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTWarpedRasterBand *and VRTWarpedDataset.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2004, 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 "vrtdataset.h"
      16             : 
      17             : #include <stddef.h>
      18             : #include <stdio.h>
      19             : #include <stdlib.h>
      20             : #include <string.h>
      21             : #include <algorithm>
      22             : #include <map>
      23             : 
      24             : // Suppress deprecation warning for GDALOpenVerticalShiftGrid and
      25             : // GDALApplyVerticalShiftGrid
      26             : #ifndef CPL_WARN_DEPRECATED_GDALOpenVerticalShiftGrid
      27             : #define CPL_WARN_DEPRECATED_GDALOpenVerticalShiftGrid(x)
      28             : #define CPL_WARN_DEPRECATED_GDALApplyVerticalShiftGrid(x)
      29             : #endif
      30             : 
      31             : #include "cpl_conv.h"
      32             : #include "cpl_error.h"
      33             : #include "cpl_minixml.h"
      34             : #include "cpl_progress.h"
      35             : #include "cpl_string.h"
      36             : #include "cpl_vsi.h"
      37             : #include "gdal.h"
      38             : #include "gdal_alg.h"
      39             : #include "gdal_alg_priv.h"
      40             : #include "gdal_priv.h"
      41             : #include "gdalwarper.h"
      42             : #include "ogr_geometry.h"
      43             : 
      44             : /************************************************************************/
      45             : /*                      GDALAutoCreateWarpedVRT()                       */
      46             : /************************************************************************/
      47             : 
      48             : /**
      49             :  * Create virtual warped dataset automatically.
      50             :  *
      51             :  * This function will create a warped virtual file representing the
      52             :  * input image warped into the target coordinate system.  A GenImgProj
      53             :  * transformation is created to accomplish any required GCP/Geotransform
      54             :  * warp and reprojection to the target coordinate system.  The output virtual
      55             :  * dataset will be "northup" in the target coordinate system.   The
      56             :  * GDALSuggestedWarpOutput() function is used to determine the bounds and
      57             :  * resolution of the output virtual file which should be large enough to
      58             :  * include all the input image
      59             :  *
      60             :  * If you want to create an alpha band if the source dataset has none, set
      61             :  * psOptionsIn->nDstAlphaBand = GDALGetRasterCount(hSrcDS) + 1.
      62             :  *
      63             :  * Note that the constructed GDALDatasetH will acquire one or more references
      64             :  * to the passed in hSrcDS.  Reference counting semantics on the source
      65             :  * dataset should be honoured.  That is, don't just GDALClose() it unless it
      66             :  * was opened with GDALOpenShared().
      67             :  *
      68             :  * It is possible to "transfer" the ownership of the source dataset
      69             :  * to the warped dataset in the following way:
      70             :  *
      71             :  * \code{.c}
      72             :  *      GDALDatasetH src_ds = GDALOpen("source.tif");
      73             :  *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
      74             :  *      GDALReleaseDataset(src_ds); // src_ds is not "owned" fully by warped_ds.
      75             :  * Do NOT use GDALClose(src_ds) here
      76             :  *      ...
      77             :  *      ...
      78             :  *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
      79             :  * \endcode
      80             :  *
      81             :  * Traditonal nested calls are also possible of course:
      82             :  *
      83             :  * \code{.c}
      84             :  *      GDALDatasetH src_ds = GDALOpen("source.tif");
      85             :  *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
      86             :  *      ...
      87             :  *      ...
      88             :  *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
      89             :  *      GDALReleaseDataset(src_ds); // or GDALClose(src_ds);
      90             :  * \endcode
      91             :  *
      92             :  * The returned dataset will have no associated filename for itself.  If you
      93             :  * want to write the virtual dataset description to a file, use the
      94             :  * GDALSetDescription() function (or SetDescription() method) on the dataset
      95             :  * to assign a filename before it is closed.
      96             :  *
      97             :  * @param hSrcDS The source dataset.
      98             :  *
      99             :  * @param pszSrcWKT The coordinate system of the source image.  If NULL, it
     100             :  * will be read from the source image.
     101             :  *
     102             :  * @param pszDstWKT The coordinate system to convert to.  If NULL no change
     103             :  * of coordinate system will take place.
     104             :  *
     105             :  * @param eResampleAlg One of GRA_NearestNeighbour, GRA_Bilinear, GRA_Cubic,
     106             :  * GRA_CubicSpline, GRA_Lanczos, GRA_Average, GRA_RMS or GRA_Mode.
     107             :  * Controls the sampling method used.
     108             :  *
     109             :  * @param dfMaxError Maximum error measured in input pixels that is allowed in
     110             :  * approximating the transformation (0.0 for exact calculations).
     111             :  *
     112             :  * @param psOptionsIn Additional warp options, normally NULL.
     113             :  *
     114             :  * @return NULL on failure, or a new virtual dataset handle on success.
     115             :  */
     116             : 
     117             : GDALDatasetH CPL_STDCALL
     118          24 : GDALAutoCreateWarpedVRT(GDALDatasetH hSrcDS, const char *pszSrcWKT,
     119             :                         const char *pszDstWKT, GDALResampleAlg eResampleAlg,
     120             :                         double dfMaxError, const GDALWarpOptions *psOptionsIn)
     121             : {
     122          24 :     return GDALAutoCreateWarpedVRTEx(hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg,
     123          24 :                                      dfMaxError, psOptionsIn, nullptr);
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                     GDALAutoCreateWarpedVRTEx()                      */
     128             : /************************************************************************/
     129             : 
     130             : /**
     131             :  * Create virtual warped dataset automatically.
     132             :  *
     133             :  * Compared to GDALAutoCreateWarpedVRT() this function adds one extra
     134             :  * argument: options to be passed to GDALCreateGenImgProjTransformer2().
     135             :  *
     136             :  * @since 3.2
     137             :  */
     138             : 
     139          24 : GDALDatasetH CPL_STDCALL GDALAutoCreateWarpedVRTEx(
     140             :     GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT,
     141             :     GDALResampleAlg eResampleAlg, double dfMaxError,
     142             :     const GDALWarpOptions *psOptionsIn, CSLConstList papszTransformerOptions)
     143             : {
     144          24 :     VALIDATE_POINTER1(hSrcDS, "GDALAutoCreateWarpedVRT", nullptr);
     145             : 
     146             :     /* -------------------------------------------------------------------- */
     147             :     /*      Populate the warp options.                                      */
     148             :     /* -------------------------------------------------------------------- */
     149          24 :     GDALWarpOptions *psWO = nullptr;
     150          24 :     if (psOptionsIn != nullptr)
     151           1 :         psWO = GDALCloneWarpOptions(psOptionsIn);
     152             :     else
     153          23 :         psWO = GDALCreateWarpOptions();
     154             : 
     155          24 :     psWO->eResampleAlg = eResampleAlg;
     156             : 
     157          24 :     psWO->hSrcDS = hSrcDS;
     158             : 
     159          24 :     GDALWarpInitDefaultBandMapping(psWO, GDALGetRasterCount(hSrcDS));
     160             : 
     161             :     /* -------------------------------------------------------------------- */
     162             :     /*      Setup no data values (if not done in psOptionsIn)               */
     163             :     /* -------------------------------------------------------------------- */
     164          24 :     if (psWO->padfSrcNoDataReal == nullptr &&
     165          24 :         psWO->padfDstNoDataReal == nullptr && psWO->nSrcAlphaBand == 0)
     166             :     {
     167             :         // If none of the provided input nodata values can be represented in the
     168             :         // data type of the corresponding source band, ignore them.
     169          24 :         int nCountInvalidSrcNoDataReal = 0;
     170          53 :         for (int i = 0; i < psWO->nBandCount; i++)
     171             :         {
     172             :             GDALRasterBandH rasterBand =
     173          29 :                 GDALGetRasterBand(psWO->hSrcDS, psWO->panSrcBands[i]);
     174             : 
     175             :             int hasNoDataValue;
     176             :             double noDataValue =
     177          29 :                 GDALGetRasterNoDataValue(rasterBand, &hasNoDataValue);
     178             : 
     179          29 :             if (hasNoDataValue &&
     180           6 :                 !GDALIsValueExactAs(noDataValue,
     181          29 :                                     GDALGetRasterDataType(rasterBand)))
     182             :             {
     183           5 :                 nCountInvalidSrcNoDataReal++;
     184             :             }
     185             :         }
     186             : 
     187          24 :         if (nCountInvalidSrcNoDataReal != psWO->nBandCount)
     188             :         {
     189          46 :             for (int i = 0; i < psWO->nBandCount; i++)
     190             :             {
     191             :                 GDALRasterBandH rasterBand =
     192          24 :                     GDALGetRasterBand(psWO->hSrcDS, psWO->panSrcBands[i]);
     193             : 
     194             :                 int hasNoDataValue;
     195             :                 double noDataValue =
     196          24 :                     GDALGetRasterNoDataValue(rasterBand, &hasNoDataValue);
     197             : 
     198          24 :                 if (hasNoDataValue)
     199             :                 {
     200             :                     // Check if the nodata value is out of range
     201           1 :                     int bClamped = FALSE;
     202           1 :                     int bRounded = FALSE;
     203           1 :                     CPL_IGNORE_RET_VAL(GDALAdjustValueToDataType(
     204             :                         GDALGetRasterDataType(rasterBand), noDataValue,
     205             :                         &bClamped, &bRounded));
     206           1 :                     if (!bClamped)
     207             :                     {
     208           1 :                         GDALWarpInitNoDataReal(psWO, -1e10);
     209           1 :                         if (psWO->padfSrcNoDataReal != nullptr &&
     210           1 :                             psWO->padfDstNoDataReal != nullptr)
     211             :                         {
     212           1 :                             psWO->padfSrcNoDataReal[i] = noDataValue;
     213           1 :                             psWO->padfDstNoDataReal[i] = noDataValue;
     214             :                         }
     215             :                     }
     216             :                 }
     217             :             }
     218             :         }
     219             : 
     220          24 :         if (psWO->padfDstNoDataReal != nullptr)
     221             :         {
     222           1 :             if (CSLFetchNameValue(psWO->papszWarpOptions, "INIT_DEST") ==
     223             :                 nullptr)
     224             :             {
     225           1 :                 psWO->papszWarpOptions = CSLSetNameValue(
     226             :                     psWO->papszWarpOptions, "INIT_DEST", "NO_DATA");
     227             :             }
     228             :         }
     229             :     }
     230             : 
     231             :     /* -------------------------------------------------------------------- */
     232             :     /*      Create the transformer.                                         */
     233             :     /* -------------------------------------------------------------------- */
     234          24 :     psWO->pfnTransformer = GDALGenImgProjTransform;
     235             : 
     236          24 :     char **papszOptions = nullptr;
     237          24 :     if (pszSrcWKT != nullptr)
     238           5 :         papszOptions = CSLSetNameValue(papszOptions, "SRC_SRS", pszSrcWKT);
     239          24 :     if (pszDstWKT != nullptr)
     240           8 :         papszOptions = CSLSetNameValue(papszOptions, "DST_SRS", pszDstWKT);
     241          24 :     papszOptions = CSLMerge(papszOptions, papszTransformerOptions);
     242          24 :     psWO->pTransformerArg =
     243          24 :         GDALCreateGenImgProjTransformer2(psWO->hSrcDS, nullptr, papszOptions);
     244          24 :     CSLDestroy(papszOptions);
     245             : 
     246          24 :     if (psWO->pTransformerArg == nullptr)
     247             :     {
     248           1 :         GDALDestroyWarpOptions(psWO);
     249           1 :         return nullptr;
     250             :     }
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Figure out the desired output bounds and resolution.            */
     254             :     /* -------------------------------------------------------------------- */
     255          23 :     double adfDstGeoTransform[6] = {0.0};
     256          23 :     int nDstPixels = 0;
     257          23 :     int nDstLines = 0;
     258          23 :     CPLErr eErr = GDALSuggestedWarpOutput(
     259             :         hSrcDS, psWO->pfnTransformer, psWO->pTransformerArg, adfDstGeoTransform,
     260             :         &nDstPixels, &nDstLines);
     261          23 :     if (eErr != CE_None)
     262             :     {
     263           0 :         GDALDestroyTransformer(psWO->pTransformerArg);
     264           0 :         GDALDestroyWarpOptions(psWO);
     265           0 :         return nullptr;
     266             :     }
     267             : 
     268             :     /* -------------------------------------------------------------------- */
     269             :     /*      Update the transformer to include an output geotransform        */
     270             :     /*      back to pixel/line coordinates.                                 */
     271             :     /*                                                                      */
     272             :     /* -------------------------------------------------------------------- */
     273          23 :     GDALSetGenImgProjTransformerDstGeoTransform(psWO->pTransformerArg,
     274             :                                                 adfDstGeoTransform);
     275             : 
     276             :     /* -------------------------------------------------------------------- */
     277             :     /*      Do we want to apply an approximating transformation?            */
     278             :     /* -------------------------------------------------------------------- */
     279          23 :     if (dfMaxError > 0.0)
     280             :     {
     281           3 :         psWO->pTransformerArg = GDALCreateApproxTransformer(
     282             :             psWO->pfnTransformer, psWO->pTransformerArg, dfMaxError);
     283           3 :         psWO->pfnTransformer = GDALApproxTransform;
     284           3 :         GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE);
     285             :     }
     286             : 
     287             :     /* -------------------------------------------------------------------- */
     288             :     /*      Create the VRT file.                                            */
     289             :     /* -------------------------------------------------------------------- */
     290          23 :     GDALDatasetH hDstDS = GDALCreateWarpedVRT(hSrcDS, nDstPixels, nDstLines,
     291             :                                               adfDstGeoTransform, psWO);
     292             : 
     293          23 :     GDALDestroyWarpOptions(psWO);
     294             : 
     295          23 :     if (hDstDS != nullptr)
     296             :     {
     297          23 :         if (pszDstWKT != nullptr)
     298           7 :             GDALSetProjection(hDstDS, pszDstWKT);
     299          16 :         else if (pszSrcWKT != nullptr)
     300           0 :             GDALSetProjection(hDstDS, pszSrcWKT);
     301          16 :         else if (GDALGetGCPCount(hSrcDS) > 0)
     302           2 :             GDALSetProjection(hDstDS, GDALGetGCPProjection(hSrcDS));
     303             :         else
     304          14 :             GDALSetProjection(hDstDS, GDALGetProjectionRef(hSrcDS));
     305             :     }
     306             : 
     307          23 :     return hDstDS;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                        GDALCreateWarpedVRT()                         */
     312             : /************************************************************************/
     313             : 
     314             : /**
     315             :  * Create virtual warped dataset.
     316             :  *
     317             :  * This function will create a warped virtual file representing the
     318             :  * input image warped based on a provided transformation.  Output bounds
     319             :  * and resolution are provided explicitly.
     320             :  *
     321             :  * If you want to create an alpha band if the source dataset has none, set
     322             :  * psOptions->nDstAlphaBand = GDALGetRasterCount(hSrcDS) + 1.
     323             :  *
     324             :  * Note that the constructed GDALDatasetH will acquire one or more references
     325             :  * to the passed in hSrcDS.  Reference counting semantics on the source
     326             :  * dataset should be honoured.  That is, don't just GDALClose() it unless it
     327             :  * was opened with GDALOpenShared().
     328             :  *
     329             :  * It is possible to "transfer" the ownership of the source dataset
     330             :  * to the warped dataset in the following way:
     331             :  *
     332             :  * \code{.c}
     333             :  *      GDALDatasetH src_ds = GDALOpen("source.tif");
     334             :  *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
     335             :  *      GDALReleaseDataset(src_ds); // src_ds is not "owned" fully by warped_ds.
     336             :  * Do NOT use GDALClose(src_ds) here
     337             :  *      ...
     338             :  *      ...
     339             :  *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
     340             :  * \endcode
     341             :  *
     342             :  * Traditonal nested calls are also possible of course:
     343             :  *
     344             :  * \code{.c}
     345             :  *      GDALDatasetH src_ds = GDALOpen("source.tif");
     346             :  *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
     347             :  *      ...
     348             :  *      ...
     349             :  *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
     350             :  *      GDALReleaseDataset(src_ds); // or GDALClose(src_ds);
     351             :  * \endcode
     352             :  *
     353             :  * The returned dataset will have no associated filename for itself.  If you
     354             :  * want to write the virtual dataset description to a file, use the
     355             :  * GDALSetDescription() function (or SetDescription() method) on the dataset
     356             :  * to assign a filename before it is closed.
     357             :  *
     358             :  * @param hSrcDS The source dataset.
     359             :  *
     360             :  * @param nPixels Width of the virtual warped dataset to create
     361             :  *
     362             :  * @param nLines Height of the virtual warped dataset to create
     363             :  *
     364             :  * @param padfGeoTransform Geotransform matrix of the virtual warped dataset
     365             :  * to create
     366             :  *
     367             :  * @param psOptions Warp options. Must be different from NULL.
     368             :  *
     369             :  * @return NULL on failure, or a new virtual dataset handle on success.
     370             :  */
     371             : 
     372          45 : GDALDatasetH CPL_STDCALL GDALCreateWarpedVRT(GDALDatasetH hSrcDS, int nPixels,
     373             :                                              int nLines,
     374             :                                              double *padfGeoTransform,
     375             :                                              GDALWarpOptions *psOptions)
     376             : 
     377             : {
     378          45 :     VALIDATE_POINTER1(hSrcDS, "GDALCreateWarpedVRT", nullptr);
     379          45 :     VALIDATE_POINTER1(psOptions, "GDALCreateWarpedVRT", nullptr);
     380             : 
     381             :     /* -------------------------------------------------------------------- */
     382             :     /*      Create the VRTDataset and populate it with bands.               */
     383             :     /* -------------------------------------------------------------------- */
     384          45 :     VRTWarpedDataset *poDS = new VRTWarpedDataset(nPixels, nLines);
     385             : 
     386             :     // Call this before assigning hDstDS
     387          45 :     GDALWarpResolveWorkingDataType(psOptions);
     388             : 
     389          45 :     psOptions->hDstDS = poDS;
     390          45 :     poDS->SetGeoTransform(padfGeoTransform);
     391             : 
     392          99 :     for (int i = 0; i < psOptions->nBandCount; i++)
     393             :     {
     394          54 :         int nDstBand = psOptions->panDstBands[i];
     395         108 :         while (poDS->GetRasterCount() < nDstBand)
     396             :         {
     397          54 :             poDS->AddBand(psOptions->eWorkingDataType, nullptr);
     398             :         }
     399             : 
     400             :         VRTWarpedRasterBand *poBand =
     401          54 :             static_cast<VRTWarpedRasterBand *>(poDS->GetRasterBand(nDstBand));
     402             :         GDALRasterBand *poSrcBand = static_cast<GDALRasterBand *>(
     403          54 :             GDALGetRasterBand(hSrcDS, psOptions->panSrcBands[i]));
     404             : 
     405          54 :         poBand->CopyCommonInfoFrom(poSrcBand);
     406             :     }
     407             : 
     408          46 :     while (poDS->GetRasterCount() < psOptions->nDstAlphaBand)
     409             :     {
     410           1 :         poDS->AddBand(psOptions->eWorkingDataType, nullptr);
     411             :     }
     412          45 :     if (psOptions->nDstAlphaBand)
     413             :     {
     414           1 :         poDS->GetRasterBand(psOptions->nDstAlphaBand)
     415           1 :             ->SetColorInterpretation(GCI_AlphaBand);
     416             :     }
     417             : 
     418             :     /* -------------------------------------------------------------------- */
     419             :     /*      Initialize the warp on the VRTWarpedDataset.                    */
     420             :     /* -------------------------------------------------------------------- */
     421          45 :     const CPLErr eErr = poDS->Initialize(psOptions);
     422          45 :     if (eErr == CE_Failure)
     423             :     {
     424           0 :         psOptions->hDstDS = nullptr;
     425           0 :         delete poDS;
     426           0 :         return nullptr;
     427             :     }
     428             : 
     429          45 :     return poDS;
     430             : }
     431             : 
     432             : /*! @cond Doxygen_Suppress */
     433             : 
     434             : /************************************************************************/
     435             : /* ==================================================================== */
     436             : /*                          VRTWarpedDataset                            */
     437             : /* ==================================================================== */
     438             : /************************************************************************/
     439             : 
     440             : /************************************************************************/
     441             : /*                          VRTWarpedDataset()                          */
     442             : /************************************************************************/
     443             : 
     444         344 : VRTWarpedDataset::VRTWarpedDataset(int nXSize, int nYSize, int nBlockXSize,
     445         344 :                                    int nBlockYSize)
     446             :     : VRTDataset(nXSize, nYSize,
     447         343 :                  nBlockXSize > 0 ? nBlockXSize : std::min(nXSize, 512),
     448         343 :                  nBlockYSize > 0 ? nBlockYSize : std::min(nYSize, 128)),
     449        1030 :       m_poWarper(nullptr), m_nSrcOvrLevel(-2)
     450             : {
     451         344 :     eAccess = GA_Update;
     452         344 :     DisableReadWriteMutex();
     453         344 : }
     454             : 
     455             : /************************************************************************/
     456             : /*                         ~VRTWarpedDataset()                          */
     457             : /************************************************************************/
     458             : 
     459         688 : VRTWarpedDataset::~VRTWarpedDataset()
     460             : 
     461             : {
     462         344 :     VRTWarpedDataset::FlushCache(true);
     463         344 :     VRTWarpedDataset::CloseDependentDatasets();
     464         688 : }
     465             : 
     466             : /************************************************************************/
     467             : /*                        CloseDependentDatasets()                      */
     468             : /************************************************************************/
     469             : 
     470         356 : int VRTWarpedDataset::CloseDependentDatasets()
     471             : {
     472         356 :     bool bHasDroppedRef = CPL_TO_BOOL(VRTDataset::CloseDependentDatasets());
     473             : 
     474             :     /* -------------------------------------------------------------------- */
     475             :     /*      Cleanup overviews.                                              */
     476             :     /* -------------------------------------------------------------------- */
     477         380 :     for (auto &poDS : m_apoOverviews)
     478             :     {
     479          24 :         if (poDS && poDS->Release())
     480             :         {
     481           0 :             bHasDroppedRef = true;
     482             :         }
     483             :     }
     484             : 
     485         356 :     m_apoOverviews.clear();
     486             : 
     487             :     /* -------------------------------------------------------------------- */
     488             :     /*      Cleanup warper if one is in effect.                             */
     489             :     /* -------------------------------------------------------------------- */
     490         356 :     if (m_poWarper != nullptr)
     491             :     {
     492         342 :         const GDALWarpOptions *psWO = m_poWarper->GetOptions();
     493             : 
     494             :         /* --------------------------------------------------------------------
     495             :          */
     496             :         /*      We take care to only call GDALClose() on psWO->hSrcDS if the */
     497             :         /*      reference count drops to zero.  This is makes it so that we */
     498             :         /*      can operate reference counting semantics more-or-less */
     499             :         /*      properly even if the dataset isn't open in shared mode, */
     500             :         /*      though we require that the caller also honour the reference */
     501             :         /*      counting semantics even though it isn't a shared dataset. */
     502             :         /* --------------------------------------------------------------------
     503             :          */
     504         342 :         if (psWO != nullptr && psWO->hSrcDS != nullptr)
     505             :         {
     506         341 :             if (GDALReleaseDataset(psWO->hSrcDS))
     507             :             {
     508         274 :                 bHasDroppedRef = true;
     509             :             }
     510             :         }
     511             : 
     512             :         /* --------------------------------------------------------------------
     513             :          */
     514             :         /*      We are responsible for cleaning up the transformer ourselves. */
     515             :         /* --------------------------------------------------------------------
     516             :          */
     517         342 :         if (psWO != nullptr && psWO->pTransformerArg != nullptr)
     518         341 :             GDALDestroyTransformer(psWO->pTransformerArg);
     519             : 
     520         342 :         delete m_poWarper;
     521         342 :         m_poWarper = nullptr;
     522             :     }
     523             : 
     524             :     /* -------------------------------------------------------------------- */
     525             :     /*      Destroy the raster bands if they exist.                         */
     526             :     /* -------------------------------------------------------------------- */
     527        1014 :     for (int iBand = 0; iBand < nBands; iBand++)
     528             :     {
     529         658 :         delete papoBands[iBand];
     530             :     }
     531         356 :     nBands = 0;
     532             : 
     533         356 :     return bHasDroppedRef;
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                         SetSrcOverviewLevel()                        */
     538             : /************************************************************************/
     539             : 
     540          85 : CPLErr VRTWarpedDataset::SetMetadataItem(const char *pszName,
     541             :                                          const char *pszValue,
     542             :                                          const char *pszDomain)
     543             : 
     544             : {
     545          85 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
     546          85 :         EQUAL(pszName, "SrcOvrLevel"))
     547             :     {
     548          85 :         const int nOldValue = m_nSrcOvrLevel;
     549          85 :         if (pszValue == nullptr || EQUAL(pszValue, "AUTO"))
     550           1 :             m_nSrcOvrLevel = -2;
     551          84 :         else if (STARTS_WITH_CI(pszValue, "AUTO-"))
     552           1 :             m_nSrcOvrLevel = -2 - atoi(pszValue + 5);
     553          83 :         else if (EQUAL(pszValue, "NONE"))
     554           1 :             m_nSrcOvrLevel = -1;
     555          82 :         else if (CPLGetValueType(pszValue) == CPL_VALUE_INTEGER)
     556          82 :             m_nSrcOvrLevel = atoi(pszValue);
     557          85 :         if (m_nSrcOvrLevel != nOldValue)
     558           2 :             SetNeedsFlush();
     559          85 :         return CE_None;
     560             :     }
     561           0 :     return VRTDataset::SetMetadataItem(pszName, pszValue, pszDomain);
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                        VRTWarpedAddOptions()                         */
     566             : /************************************************************************/
     567             : 
     568         342 : static char **VRTWarpedAddOptions(char **papszWarpOptions)
     569             : {
     570             :     /* Avoid errors when adding an alpha band, but source dataset has */
     571             :     /* no alpha band (#4571), and generally don't leave our buffer uninitialized
     572             :      */
     573         342 :     if (CSLFetchNameValue(papszWarpOptions, "INIT_DEST") == nullptr)
     574          51 :         papszWarpOptions = CSLSetNameValue(papszWarpOptions, "INIT_DEST", "0");
     575             : 
     576             :     /* For https://github.com/OSGeo/gdal/issues/1985 */
     577         342 :     if (CSLFetchNameValue(papszWarpOptions,
     578         342 :                           "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW") == nullptr)
     579             :     {
     580         213 :         papszWarpOptions = CSLSetNameValue(
     581             :             papszWarpOptions, "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", "FALSE");
     582             :     }
     583         342 :     return papszWarpOptions;
     584             : }
     585             : 
     586             : /************************************************************************/
     587             : /*                             Initialize()                             */
     588             : /*                                                                      */
     589             : /*      Initialize a dataset from passed in warp options.               */
     590             : /************************************************************************/
     591             : 
     592         144 : CPLErr VRTWarpedDataset::Initialize(void *psWO)
     593             : 
     594             : {
     595         144 :     if (m_poWarper != nullptr)
     596           0 :         delete m_poWarper;
     597             : 
     598         144 :     m_poWarper = new GDALWarpOperation();
     599             : 
     600             :     GDALWarpOptions *psWO_Dup =
     601         144 :         GDALCloneWarpOptions(static_cast<GDALWarpOptions *>(psWO));
     602             : 
     603         144 :     psWO_Dup->papszWarpOptions =
     604         144 :         VRTWarpedAddOptions(psWO_Dup->papszWarpOptions);
     605             : 
     606         144 :     CPLErr eErr = m_poWarper->Initialize(psWO_Dup);
     607             : 
     608             :     // The act of initializing this warped dataset with this warp options
     609             :     // will result in our assuming ownership of a reference to the
     610             :     // hSrcDS.
     611             : 
     612         144 :     if (eErr == CE_None &&
     613         143 :         static_cast<GDALWarpOptions *>(psWO)->hSrcDS != nullptr)
     614             :     {
     615         143 :         GDALReferenceDataset(psWO_Dup->hSrcDS);
     616             :     }
     617             : 
     618         144 :     GDALDestroyWarpOptions(psWO_Dup);
     619             : 
     620         144 :     if (nBands > 1)
     621             :     {
     622          30 :         GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     623             :     }
     624             : 
     625         144 :     return eErr;
     626             : }
     627             : 
     628             : /************************************************************************/
     629             : /*                        GDALWarpCoordRescaler                         */
     630             : /************************************************************************/
     631             : 
     632             : class GDALWarpCoordRescaler : public OGRCoordinateTransformation
     633             : {
     634             :     double m_dfRatioX;
     635             :     double m_dfRatioY;
     636             : 
     637             :   public:
     638           1 :     GDALWarpCoordRescaler(double dfRatioX, double dfRatioY)
     639           1 :         : m_dfRatioX(dfRatioX), m_dfRatioY(dfRatioY)
     640             :     {
     641           1 :     }
     642             : 
     643           1 :     virtual ~GDALWarpCoordRescaler()
     644           1 :     {
     645           1 :     }
     646             : 
     647           0 :     virtual const OGRSpatialReference *GetSourceCS() const override
     648             :     {
     649           0 :         return nullptr;
     650             :     }
     651             : 
     652           6 :     virtual const OGRSpatialReference *GetTargetCS() const override
     653             :     {
     654           6 :         return nullptr;
     655             :     }
     656             : 
     657           3 :     virtual int Transform(size_t nCount, double *x, double *y, double * /*z*/,
     658             :                           double * /*t*/, int *pabSuccess) override
     659             :     {
     660          17 :         for (size_t i = 0; i < nCount; i++)
     661             :         {
     662          14 :             x[i] *= m_dfRatioX;
     663          14 :             y[i] *= m_dfRatioY;
     664          14 :             if (pabSuccess)
     665          14 :                 pabSuccess[i] = TRUE;
     666             :         }
     667           3 :         return TRUE;
     668             :     }
     669             : 
     670           0 :     virtual OGRCoordinateTransformation *Clone() const override
     671             :     {
     672           0 :         return new GDALWarpCoordRescaler(*this);
     673             :     }
     674             : 
     675           0 :     virtual OGRCoordinateTransformation *GetInverse() const override
     676             :     {
     677           0 :         return nullptr;
     678             :     }
     679             : };
     680             : 
     681             : /************************************************************************/
     682             : /*                        RescaleDstGeoTransform()                      */
     683             : /************************************************************************/
     684             : 
     685          22 : static void RescaleDstGeoTransform(double adfDstGeoTransform[6],
     686             :                                    int nRasterXSize, int nDstPixels,
     687             :                                    int nRasterYSize, int nDstLines)
     688             : {
     689          22 :     adfDstGeoTransform[1] *= static_cast<double>(nRasterXSize) / nDstPixels;
     690          22 :     adfDstGeoTransform[2] *= static_cast<double>(nRasterXSize) / nDstPixels;
     691          22 :     adfDstGeoTransform[4] *= static_cast<double>(nRasterYSize) / nDstLines;
     692          22 :     adfDstGeoTransform[5] *= static_cast<double>(nRasterYSize) / nDstLines;
     693          22 : }
     694             : 
     695             : /************************************************************************/
     696             : /*                        GetSrcOverviewLevel()                         */
     697             : /************************************************************************/
     698             : 
     699          79 : int VRTWarpedDataset::GetSrcOverviewLevel(int iOvr,
     700             :                                           bool &bThisLevelOnlyOut) const
     701             : {
     702          79 :     bThisLevelOnlyOut = false;
     703          79 :     if (m_nSrcOvrLevel < -2)
     704             :     {
     705           6 :         if (iOvr + m_nSrcOvrLevel + 2 >= 0)
     706             :         {
     707           3 :             return iOvr + m_nSrcOvrLevel + 2;
     708             :         }
     709             :     }
     710          73 :     else if (m_nSrcOvrLevel == -2)
     711             :     {
     712          67 :         return iOvr;
     713             :     }
     714           6 :     else if (m_nSrcOvrLevel >= 0)
     715             :     {
     716           0 :         bThisLevelOnlyOut = true;
     717           0 :         return m_nSrcOvrLevel;
     718             :     }
     719           9 :     return -1;
     720             : }
     721             : 
     722             : /************************************************************************/
     723             : /*                            GetOverviewSize()                         */
     724             : /************************************************************************/
     725             : 
     726          73 : bool VRTWarpedDataset::GetOverviewSize(GDALDataset *poSrcDS, int iOvr,
     727             :                                        int iSrcOvr, int &nOvrXSize,
     728             :                                        int &nOvrYSize, double &dfSrcRatioX,
     729             :                                        double &dfSrcRatioY) const
     730             : {
     731             :     auto poSrcOvrBand = iSrcOvr >= 0
     732          73 :                             ? poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr)
     733           3 :                             : poSrcDS->GetRasterBand(1);
     734          73 :     if (!poSrcOvrBand)
     735             :     {
     736           0 :         return false;
     737             :     }
     738          73 :     dfSrcRatioX = static_cast<double>(poSrcDS->GetRasterXSize()) /
     739          73 :                   poSrcOvrBand->GetXSize();
     740          73 :     dfSrcRatioY = static_cast<double>(poSrcDS->GetRasterYSize()) /
     741          73 :                   poSrcOvrBand->GetYSize();
     742             :     const double dfTargetRatio =
     743          73 :         static_cast<double>(poSrcDS->GetRasterXSize()) /
     744          73 :         poSrcDS->GetRasterBand(1)->GetOverview(iOvr)->GetXSize();
     745             : 
     746          73 :     nOvrXSize = static_cast<int>(nRasterXSize / dfTargetRatio + 0.5);
     747          73 :     nOvrYSize = static_cast<int>(nRasterYSize / dfTargetRatio + 0.5);
     748          73 :     return nOvrXSize >= 1 && nOvrYSize >= 1;
     749             : }
     750             : 
     751             : /************************************************************************/
     752             : /*                        CreateImplicitOverview()                      */
     753             : /************************************************************************/
     754             : 
     755          20 : VRTWarpedDataset *VRTWarpedDataset::CreateImplicitOverview(int iOvr) const
     756             : {
     757          20 :     if (!m_poWarper)
     758           0 :         return nullptr;
     759          20 :     const GDALWarpOptions *psWO = m_poWarper->GetOptions();
     760          20 :     if (!psWO->hSrcDS || GDALGetRasterCount(psWO->hSrcDS) == 0)
     761           0 :         return nullptr;
     762          20 :     GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
     763          20 :     GDALDataset *poSrcOvrDS = poSrcDS;
     764          20 :     bool bThisLevelOnly = false;
     765          20 :     const int iSrcOvr = GetSrcOverviewLevel(iOvr, bThisLevelOnly);
     766          20 :     if (iSrcOvr >= 0)
     767             :     {
     768             :         poSrcOvrDS =
     769          17 :             GDALCreateOverviewDataset(poSrcDS, iSrcOvr, bThisLevelOnly);
     770             :     }
     771          20 :     if (poSrcOvrDS == nullptr)
     772           0 :         return nullptr;
     773          20 :     if (poSrcOvrDS == poSrcDS)
     774           3 :         poSrcOvrDS->Reference();
     775             : 
     776          20 :     int nDstPixels = 0;
     777          20 :     int nDstLines = 0;
     778          20 :     double dfSrcRatioX = 0;
     779          20 :     double dfSrcRatioY = 0;
     780             :     // Figure out the desired output bounds and resolution.
     781          20 :     if (!GetOverviewSize(poSrcDS, iOvr, iSrcOvr, nDstPixels, nDstLines,
     782             :                          dfSrcRatioX, dfSrcRatioY))
     783             :     {
     784           0 :         poSrcOvrDS->ReleaseRef();
     785           0 :         return nullptr;
     786             :     }
     787             : 
     788             :     /* --------------------------------------------------------------------
     789             :      */
     790             :     /*      Create transformer and warping options. */
     791             :     /* --------------------------------------------------------------------
     792             :      */
     793          40 :     void *pTransformerArg = GDALCreateSimilarTransformer(
     794          20 :         psWO->pTransformerArg, dfSrcRatioX, dfSrcRatioY);
     795          20 :     if (pTransformerArg == nullptr)
     796             :     {
     797           0 :         poSrcOvrDS->ReleaseRef();
     798           0 :         return nullptr;
     799             :     }
     800             : 
     801          20 :     GDALWarpOptions *psWOOvr = GDALCloneWarpOptions(psWO);
     802          20 :     psWOOvr->hSrcDS = poSrcOvrDS;
     803          20 :     psWOOvr->pfnTransformer = psWO->pfnTransformer;
     804          20 :     psWOOvr->pTransformerArg = pTransformerArg;
     805             : 
     806             :     /* --------------------------------------------------------------------
     807             :      */
     808             :     /*      We need to rescale the potential CUTLINE */
     809             :     /* --------------------------------------------------------------------
     810             :      */
     811          20 :     if (psWOOvr->hCutline)
     812             :     {
     813           2 :         GDALWarpCoordRescaler oRescaler(1.0 / dfSrcRatioX, 1.0 / dfSrcRatioY);
     814           1 :         static_cast<OGRGeometry *>(psWOOvr->hCutline)->transform(&oRescaler);
     815             :     }
     816             : 
     817             :     /* --------------------------------------------------------------------
     818             :      */
     819             :     /*      Rescale the output geotransform on the transformer. */
     820             :     /* --------------------------------------------------------------------
     821             :      */
     822          20 :     double adfDstGeoTransform[6] = {0.0};
     823          20 :     GDALGetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
     824             :                                       adfDstGeoTransform);
     825          20 :     RescaleDstGeoTransform(adfDstGeoTransform, nRasterXSize, nDstPixels,
     826          20 :                            nRasterYSize, nDstLines);
     827          20 :     GDALSetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
     828             :                                       adfDstGeoTransform);
     829             : 
     830             :     /* --------------------------------------------------------------------
     831             :      */
     832             :     /*      Create the VRT file. */
     833             :     /* --------------------------------------------------------------------
     834             :      */
     835          20 :     GDALDatasetH hDstDS = GDALCreateWarpedVRT(poSrcOvrDS, nDstPixels, nDstLines,
     836             :                                               adfDstGeoTransform, psWOOvr);
     837             : 
     838          20 :     poSrcOvrDS->ReleaseRef();
     839             : 
     840          20 :     GDALDestroyWarpOptions(psWOOvr);
     841             : 
     842          20 :     if (hDstDS == nullptr)
     843             :     {
     844           0 :         GDALDestroyTransformer(pTransformerArg);
     845           0 :         return nullptr;
     846             :     }
     847             : 
     848          20 :     auto poOvrDS = static_cast<VRTWarpedDataset *>(hDstDS);
     849          20 :     poOvrDS->m_bIsOverview = true;
     850          20 :     return poOvrDS;
     851             : }
     852             : 
     853             : /************************************************************************/
     854             : /*                           GetOverviewCount()                         */
     855             : /************************************************************************/
     856             : 
     857          58 : int VRTWarpedDataset::GetOverviewCount() const
     858             : {
     859          58 :     if (m_poWarper)
     860             :     {
     861          58 :         const GDALWarpOptions *psWO = m_poWarper->GetOptions();
     862          58 :         if (!m_bIsOverview && psWO->hSrcDS && GDALGetRasterCount(psWO->hSrcDS))
     863             :         {
     864          58 :             GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
     865             :             int nSrcOverviewCount =
     866          58 :                 poSrcDS->GetRasterBand(1)->GetOverviewCount();
     867          58 :             int nCount = 0;
     868         117 :             for (int i = 0; i < nSrcOverviewCount; ++i)
     869             :             {
     870          59 :                 bool bThisLevelOnly = false;
     871          59 :                 const int iSrcOvr = GetSrcOverviewLevel(i, bThisLevelOnly);
     872          59 :                 if (iSrcOvr >= 0)
     873             :                 {
     874          53 :                     int nDstPixels = 0;
     875          53 :                     int nDstLines = 0;
     876          53 :                     double dfSrcRatioX = 0;
     877          53 :                     double dfSrcRatioY = 0;
     878          53 :                     if (!GetOverviewSize(poSrcDS, i, iSrcOvr, nDstPixels,
     879             :                                          nDstLines, dfSrcRatioX, dfSrcRatioY))
     880             :                     {
     881           0 :                         break;
     882             :                     }
     883             :                 }
     884          59 :                 ++nCount;
     885             :             }
     886          58 :             return nCount;
     887             :         }
     888             :     }
     889           0 :     return 0;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                        CreateImplicitOverviews()                     */
     894             : /*                                                                      */
     895             : /*      For each overview of the source dataset, create an overview     */
     896             : /*      in the warped VRT dataset.                                      */
     897             : /************************************************************************/
     898             : 
     899           8 : void VRTWarpedDataset::CreateImplicitOverviews()
     900             : {
     901           8 :     if (m_bIsOverview)
     902           0 :         return;
     903           8 :     const int nOvrCount = GetOverviewCount();
     904           8 :     if (m_apoOverviews.empty())
     905           4 :         m_apoOverviews.resize(nOvrCount);
     906          18 :     for (int iOvr = 0; iOvr < nOvrCount; iOvr++)
     907             :     {
     908          10 :         if (!m_apoOverviews[iOvr])
     909             :         {
     910           2 :             m_apoOverviews[iOvr] = CreateImplicitOverview(iOvr);
     911             :         }
     912             :     }
     913             : }
     914             : 
     915             : /************************************************************************/
     916             : /*                            GetFileList()                             */
     917             : /************************************************************************/
     918             : 
     919          20 : char **VRTWarpedDataset::GetFileList()
     920             : {
     921          20 :     char **papszFileList = GDALDataset::GetFileList();
     922             : 
     923          20 :     if (m_poWarper != nullptr)
     924             :     {
     925          20 :         const GDALWarpOptions *psWO = m_poWarper->GetOptions();
     926          20 :         const char *pszFilename = nullptr;
     927             : 
     928          40 :         if (psWO->hSrcDS != nullptr &&
     929             :             (pszFilename =
     930          20 :                  static_cast<GDALDataset *>(psWO->hSrcDS)->GetDescription()) !=
     931             :                 nullptr)
     932             :         {
     933             :             VSIStatBufL sStat;
     934          20 :             if (VSIStatL(pszFilename, &sStat) == 0)
     935             :             {
     936          20 :                 papszFileList = CSLAddString(papszFileList, pszFilename);
     937             :             }
     938             :         }
     939             :     }
     940             : 
     941          20 :     return papszFileList;
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /* ==================================================================== */
     946             : /*                    VRTWarpedOverviewTransformer                      */
     947             : /* ==================================================================== */
     948             : /************************************************************************/
     949             : 
     950             : typedef struct
     951             : {
     952             :     GDALTransformerInfo sTI;
     953             : 
     954             :     GDALTransformerFunc pfnBaseTransformer;
     955             :     void *pBaseTransformerArg;
     956             :     bool bOwnSubtransformer;
     957             : 
     958             :     double dfXOverviewFactor;
     959             :     double dfYOverviewFactor;
     960             : } VWOTInfo;
     961             : 
     962             : static void *VRTCreateWarpedOverviewTransformer(
     963             :     GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformArg,
     964             :     double dfXOverviewFactor, double dfYOverviewFactor);
     965             : static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg);
     966             : 
     967             : static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
     968             :                                       int nPointCount, double *padfX,
     969             :                                       double *padfY, double *padfZ,
     970             :                                       int *panSuccess);
     971             : 
     972             : #if 0   // TODO: Why?
     973             : /************************************************************************/
     974             : /*                VRTSerializeWarpedOverviewTransformer()               */
     975             : /************************************************************************/
     976             : 
     977             : static CPLXMLNode *
     978             : VRTSerializeWarpedOverviewTransformer( void *pTransformArg )
     979             : 
     980             : {
     981             :     VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
     982             : 
     983             :     CPLXMLNode *psTree
     984             :         = CPLCreateXMLNode( NULL, CXT_Element, "WarpedOverviewTransformer" );
     985             : 
     986             :     CPLCreateXMLElementAndValue(
     987             :         psTree, "XFactor",
     988             :         CPLString().Printf("%g",psInfo->dfXOverviewFactor) );
     989             :     CPLCreateXMLElementAndValue(
     990             :         psTree, "YFactor",
     991             :         CPLString().Printf("%g",psInfo->dfYOverviewFactor) );
     992             : 
     993             : /* -------------------------------------------------------------------- */
     994             : /*      Capture underlying transformer.                                 */
     995             : /* -------------------------------------------------------------------- */
     996             :     CPLXMLNode *psTransformerContainer
     997             :         = CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" );
     998             : 
     999             :     CPLXMLNode *psTransformer
    1000             :         = GDALSerializeTransformer( psInfo->pfnBaseTransformer,
    1001             :                                     psInfo->pBaseTransformerArg );
    1002             :     if( psTransformer != NULL )
    1003             :         CPLAddXMLChild( psTransformerContainer, psTransformer );
    1004             : 
    1005             :     return psTree;
    1006             : }
    1007             : 
    1008             : /************************************************************************/
    1009             : /*           VRTWarpedOverviewTransformerOwnsSubtransformer()           */
    1010             : /************************************************************************/
    1011             : 
    1012             : static void VRTWarpedOverviewTransformerOwnsSubtransformer( void *pTransformArg,
    1013             :                                                             bool bOwnFlag )
    1014             : {
    1015             :     VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
    1016             : 
    1017             :     psInfo->bOwnSubtransformer = bOwnFlag;
    1018             : }
    1019             : 
    1020             : /************************************************************************/
    1021             : /*            VRTDeserializeWarpedOverviewTransformer()                 */
    1022             : /************************************************************************/
    1023             : 
    1024             : void* VRTDeserializeWarpedOverviewTransformer( CPLXMLNode *psTree )
    1025             : 
    1026             : {
    1027             :     const double dfXOverviewFactor =
    1028             :         CPLAtof(CPLGetXMLValue( psTree, "XFactor",  "1" ));
    1029             :     const double dfYOverviewFactor =
    1030             :         CPLAtof(CPLGetXMLValue( psTree, "YFactor",  "1" ));
    1031             :     GDALTransformerFunc pfnBaseTransform = NULL;
    1032             :     void *pBaseTransformerArg = NULL;
    1033             : 
    1034             :     CPLXMLNode *psContainer = CPLGetXMLNode( psTree, "BaseTransformer" );
    1035             : 
    1036             :     if( psContainer != NULL && psContainer->psChild != NULL )
    1037             :     {
    1038             :         GDALDeserializeTransformer( psContainer->psChild,
    1039             :                                     &pfnBaseTransform,
    1040             :                                     &pBaseTransformerArg );
    1041             :     }
    1042             : 
    1043             :     if( pfnBaseTransform == NULL )
    1044             :     {
    1045             :         CPLError( CE_Failure, CPLE_AppDefined,
    1046             :                   "Cannot get base transform for scaled coord transformer." );
    1047             :         return NULL;
    1048             :     }
    1049             :     else
    1050             :     {
    1051             :         void *pApproxCBData =
    1052             :                        VRTCreateWarpedOverviewTransformer( pfnBaseTransform,
    1053             :                                                            pBaseTransformerArg,
    1054             :                                                            dfXOverviewFactor,
    1055             :                                                            dfYOverviewFactor );
    1056             :         VRTWarpedOverviewTransformerOwnsSubtransformer( pApproxCBData, true );
    1057             : 
    1058             :         return pApproxCBData;
    1059             :     }
    1060             : }
    1061             : #endif  // TODO: Why disabled?
    1062             : 
    1063             : /************************************************************************/
    1064             : /*                   VRTCreateWarpedOverviewTransformer()               */
    1065             : /************************************************************************/
    1066             : 
    1067           4 : static void *VRTCreateWarpedOverviewTransformer(
    1068             :     GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformerArg,
    1069             :     double dfXOverviewFactor, double dfYOverviewFactor)
    1070             : 
    1071             : {
    1072           4 :     if (pfnBaseTransformer == nullptr)
    1073           0 :         return nullptr;
    1074             : 
    1075           4 :     VWOTInfo *psSCTInfo = static_cast<VWOTInfo *>(CPLMalloc(sizeof(VWOTInfo)));
    1076           4 :     psSCTInfo->pfnBaseTransformer = pfnBaseTransformer;
    1077           4 :     psSCTInfo->pBaseTransformerArg = pBaseTransformerArg;
    1078           4 :     psSCTInfo->dfXOverviewFactor = dfXOverviewFactor;
    1079           4 :     psSCTInfo->dfYOverviewFactor = dfYOverviewFactor;
    1080           4 :     psSCTInfo->bOwnSubtransformer = false;
    1081             : 
    1082           4 :     memcpy(psSCTInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
    1083             :            strlen(GDAL_GTI2_SIGNATURE));
    1084           4 :     psSCTInfo->sTI.pszClassName = "VRTWarpedOverviewTransformer";
    1085           4 :     psSCTInfo->sTI.pfnTransform = VRTWarpedOverviewTransform;
    1086           4 :     psSCTInfo->sTI.pfnCleanup = VRTDestroyWarpedOverviewTransformer;
    1087             : #if 0
    1088             :     psSCTInfo->sTI.pfnSerialize = VRTSerializeWarpedOverviewTransformer;
    1089             : #endif
    1090           4 :     return psSCTInfo;
    1091             : }
    1092             : 
    1093             : /************************************************************************/
    1094             : /*               VRTDestroyWarpedOverviewTransformer()                  */
    1095             : /************************************************************************/
    1096             : 
    1097           4 : static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg)
    1098             : {
    1099           4 :     VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
    1100             : 
    1101           4 :     if (psInfo->bOwnSubtransformer)
    1102           0 :         GDALDestroyTransformer(psInfo->pBaseTransformerArg);
    1103             : 
    1104           4 :     CPLFree(psInfo);
    1105           4 : }
    1106             : 
    1107             : /************************************************************************/
    1108             : /*                     VRTWarpedOverviewTransform()                     */
    1109             : /************************************************************************/
    1110             : 
    1111         259 : static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
    1112             :                                       int nPointCount, double *padfX,
    1113             :                                       double *padfY, double *padfZ,
    1114             :                                       int *panSuccess)
    1115             : 
    1116             : {
    1117         259 :     VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
    1118             : 
    1119         259 :     if (bDstToSrc)
    1120             :     {
    1121       63032 :         for (int i = 0; i < nPointCount; i++)
    1122             :         {
    1123       62773 :             padfX[i] *= psInfo->dfXOverviewFactor;
    1124       62773 :             padfY[i] *= psInfo->dfYOverviewFactor;
    1125             :         }
    1126             :     }
    1127             : 
    1128         259 :     const int bSuccess = psInfo->pfnBaseTransformer(
    1129             :         psInfo->pBaseTransformerArg, bDstToSrc, nPointCount, padfX, padfY,
    1130             :         padfZ, panSuccess);
    1131             : 
    1132         259 :     if (!bDstToSrc)
    1133             :     {
    1134           0 :         for (int i = 0; i < nPointCount; i++)
    1135             :         {
    1136           0 :             padfX[i] /= psInfo->dfXOverviewFactor;
    1137           0 :             padfY[i] /= psInfo->dfYOverviewFactor;
    1138             :         }
    1139             :     }
    1140             : 
    1141         259 :     return bSuccess;
    1142             : }
    1143             : 
    1144             : /************************************************************************/
    1145             : /*                           BuildOverviews()                           */
    1146             : /*                                                                      */
    1147             : /*      For overviews, we actually just build a whole new dataset       */
    1148             : /*      with an extra layer of transformation on the warper used to     */
    1149             : /*      accomplish downsampling by the desired factor.                  */
    1150             : /************************************************************************/
    1151             : 
    1152           6 : CPLErr VRTWarpedDataset::IBuildOverviews(
    1153             :     const char * /* pszResampling */, int nOverviews,
    1154             :     const int *panOverviewList, int /* nListBands */,
    1155             :     const int * /* panBandList */, GDALProgressFunc pfnProgress,
    1156             :     void *pProgressData, CSLConstList /*papszOptions*/)
    1157             : {
    1158           6 :     if (m_poWarper == nullptr || m_bIsOverview)
    1159           0 :         return CE_Failure;
    1160             : 
    1161             :     /* -------------------------------------------------------------------- */
    1162             :     /*      Initial progress result.                                        */
    1163             :     /* -------------------------------------------------------------------- */
    1164           6 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1165             :     {
    1166           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1167           0 :         return CE_Failure;
    1168             :     }
    1169             : 
    1170           6 :     CreateImplicitOverviews();
    1171             : 
    1172             :     /* -------------------------------------------------------------------- */
    1173             :     /*      Establish which of the overview levels we already have, and     */
    1174             :     /*      which are new.                                                  */
    1175             :     /* -------------------------------------------------------------------- */
    1176           6 :     int nNewOverviews = 0;
    1177             :     int *panNewOverviewList =
    1178           6 :         static_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
    1179           6 :     std::vector<bool> abFoundOverviewFactor(nOverviews);
    1180          14 :     for (int i = 0; i < nOverviews; i++)
    1181             :     {
    1182          20 :         for (GDALDataset *const poOverview : m_apoOverviews)
    1183             :         {
    1184          12 :             if (poOverview)
    1185             :             {
    1186          12 :                 const int nOvFactor = GDALComputeOvFactor(
    1187             :                     poOverview->GetRasterXSize(), GetRasterXSize(),
    1188             :                     poOverview->GetRasterYSize(), GetRasterYSize());
    1189             : 
    1190          20 :                 if (nOvFactor == panOverviewList[i] ||
    1191           8 :                     nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
    1192             :                                                     GetRasterXSize(),
    1193             :                                                     GetRasterYSize()))
    1194           4 :                     abFoundOverviewFactor[i] = true;
    1195             :             }
    1196             :         }
    1197             : 
    1198           8 :         if (!abFoundOverviewFactor[i])
    1199           4 :             panNewOverviewList[nNewOverviews++] = panOverviewList[i];
    1200             :     }
    1201             : 
    1202             :     /* -------------------------------------------------------------------- */
    1203             :     /*      Create each missing overview (we don't need to do anything      */
    1204             :     /*      to update existing overviews).                                  */
    1205             :     /* -------------------------------------------------------------------- */
    1206           6 :     CPLErr eErr = CE_None;
    1207          10 :     for (int i = 0; i < nNewOverviews; i++)
    1208             :     {
    1209             :         /* --------------------------------------------------------------------
    1210             :          */
    1211             :         /*      What size should this overview be. */
    1212             :         /* --------------------------------------------------------------------
    1213             :          */
    1214           4 :         const int nOXSize = (GetRasterXSize() + panNewOverviewList[i] - 1) /
    1215           4 :                             panNewOverviewList[i];
    1216             : 
    1217           4 :         const int nOYSize = (GetRasterYSize() + panNewOverviewList[i] - 1) /
    1218           4 :                             panNewOverviewList[i];
    1219             : 
    1220             :         /* --------------------------------------------------------------------
    1221             :          */
    1222             :         /*      Find the most appropriate base dataset onto which to build the
    1223             :          */
    1224             :         /*      new one. The preference will be an overview dataset with a
    1225             :          * ratio*/
    1226             :         /*      greater than ours, and which is not using */
    1227             :         /*      VRTWarpedOverviewTransform, since those ones are slow. The
    1228             :          * other*/
    1229             :         /*      ones are based on overviews of the source dataset. */
    1230             :         /* --------------------------------------------------------------------
    1231             :          */
    1232           4 :         VRTWarpedDataset *poBaseDataset = this;
    1233           8 :         for (auto *poOverview : m_apoOverviews)
    1234             :         {
    1235           4 :             if (poOverview && poOverview->GetRasterXSize() > nOXSize &&
    1236           4 :                 poOverview->m_poWarper->GetOptions()->pfnTransformer !=
    1237           8 :                     VRTWarpedOverviewTransform &&
    1238           4 :                 poOverview->GetRasterXSize() < poBaseDataset->GetRasterXSize())
    1239             :             {
    1240           4 :                 poBaseDataset = poOverview;
    1241             :             }
    1242             :         }
    1243             : 
    1244             :         /* --------------------------------------------------------------------
    1245             :          */
    1246             :         /*      Create the overview dataset. */
    1247             :         /* --------------------------------------------------------------------
    1248             :          */
    1249           4 :         VRTWarpedDataset *poOverviewDS = new VRTWarpedDataset(nOXSize, nOYSize);
    1250             : 
    1251           8 :         for (int iBand = 0; iBand < GetRasterCount(); iBand++)
    1252             :         {
    1253           4 :             GDALRasterBand *const poOldBand = GetRasterBand(iBand + 1);
    1254             :             VRTWarpedRasterBand *const poNewBand = new VRTWarpedRasterBand(
    1255           4 :                 poOverviewDS, iBand + 1, poOldBand->GetRasterDataType());
    1256             : 
    1257           4 :             poNewBand->CopyCommonInfoFrom(poOldBand);
    1258           4 :             poOverviewDS->SetBand(iBand + 1, poNewBand);
    1259             :         }
    1260             : 
    1261             :         /* --------------------------------------------------------------------
    1262             :          */
    1263             :         /*      Prepare update transformation information that will apply */
    1264             :         /*      the overview decimation. */
    1265             :         /* --------------------------------------------------------------------
    1266             :          */
    1267             :         GDALWarpOptions *psWO = const_cast<GDALWarpOptions *>(
    1268           4 :             poBaseDataset->m_poWarper->GetOptions());
    1269             : 
    1270             :         /* --------------------------------------------------------------------
    1271             :          */
    1272             :         /*      Initialize the new dataset with adjusted warp options, and */
    1273             :         /*      then restore to original condition. */
    1274             :         /* --------------------------------------------------------------------
    1275             :          */
    1276           4 :         GDALTransformerFunc pfnTransformerBase = psWO->pfnTransformer;
    1277           4 :         void *pTransformerBaseArg = psWO->pTransformerArg;
    1278             : 
    1279           4 :         psWO->pfnTransformer = VRTWarpedOverviewTransform;
    1280          12 :         psWO->pTransformerArg = VRTCreateWarpedOverviewTransformer(
    1281             :             pfnTransformerBase, pTransformerBaseArg,
    1282           4 :             poBaseDataset->GetRasterXSize() / static_cast<double>(nOXSize),
    1283           4 :             poBaseDataset->GetRasterYSize() / static_cast<double>(nOYSize));
    1284             : 
    1285           4 :         eErr = poOverviewDS->Initialize(psWO);
    1286             : 
    1287           4 :         psWO->pfnTransformer = pfnTransformerBase;
    1288           4 :         psWO->pTransformerArg = pTransformerBaseArg;
    1289             : 
    1290           4 :         if (eErr != CE_None)
    1291             :         {
    1292           0 :             delete poOverviewDS;
    1293           0 :             break;
    1294             :         }
    1295             : 
    1296           4 :         m_apoOverviews.push_back(poOverviewDS);
    1297             :     }
    1298             : 
    1299           6 :     CPLFree(panNewOverviewList);
    1300             : 
    1301             :     /* -------------------------------------------------------------------- */
    1302             :     /*      Progress finished.                                              */
    1303             :     /* -------------------------------------------------------------------- */
    1304           6 :     pfnProgress(1.0, nullptr, pProgressData);
    1305             : 
    1306           6 :     SetNeedsFlush();
    1307             : 
    1308           6 :     return eErr;
    1309             : }
    1310             : 
    1311             : /*! @endcond */
    1312             : 
    1313             : /************************************************************************/
    1314             : /*                      GDALInitializeWarpedVRT()                       */
    1315             : /************************************************************************/
    1316             : 
    1317             : /**
    1318             :  * Set warp info on virtual warped dataset.
    1319             :  *
    1320             :  * Initializes all the warping information for a virtual warped dataset.
    1321             :  *
    1322             :  * This method is the same as the C++ method VRTWarpedDataset::Initialize().
    1323             :  *
    1324             :  * @param hDS dataset previously created with the VRT driver, and a
    1325             :  * SUBCLASS of "VRTWarpedDataset".
    1326             :  *
    1327             :  * @param psWO the warp options to apply.  Note that ownership of the
    1328             :  * transformation information is taken over by the function though everything
    1329             :  * else remains the property of the caller.
    1330             :  *
    1331             :  * @return CE_None on success or CE_Failure if an error occurs.
    1332             :  */
    1333             : 
    1334          82 : CPLErr CPL_STDCALL GDALInitializeWarpedVRT(GDALDatasetH hDS,
    1335             :                                            GDALWarpOptions *psWO)
    1336             : 
    1337             : {
    1338          82 :     VALIDATE_POINTER1(hDS, "GDALInitializeWarpedVRT", CE_Failure);
    1339             : 
    1340          82 :     return static_cast<VRTWarpedDataset *>(GDALDataset::FromHandle(hDS))
    1341          82 :         ->Initialize(psWO);
    1342             : }
    1343             : 
    1344             : /*! @cond Doxygen_Suppress */
    1345             : 
    1346             : /************************************************************************/
    1347             : /*                              XMLInit()                               */
    1348             : /************************************************************************/
    1349             : 
    1350         200 : CPLErr VRTWarpedDataset::XMLInit(const CPLXMLNode *psTree,
    1351             :                                  const char *pszVRTPathIn)
    1352             : 
    1353             : {
    1354             : 
    1355             :     /* -------------------------------------------------------------------- */
    1356             :     /*      Initialize blocksize before calling sub-init so that the        */
    1357             :     /*      band initializers can get it from the dataset object when       */
    1358             :     /*      they are created.                                               */
    1359             :     /* -------------------------------------------------------------------- */
    1360         200 :     m_nBlockXSize = atoi(CPLGetXMLValue(psTree, "BlockXSize", "512"));
    1361         200 :     m_nBlockYSize = atoi(CPLGetXMLValue(psTree, "BlockYSize", "128"));
    1362             : 
    1363             :     /* -------------------------------------------------------------------- */
    1364             :     /*      Initialize all the general VRT stuff.  This will even           */
    1365             :     /*      create the VRTWarpedRasterBands and initialize them.            */
    1366             :     /* -------------------------------------------------------------------- */
    1367             :     {
    1368         200 :         const CPLErr eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
    1369             : 
    1370         200 :         if (eErr != CE_None)
    1371           0 :             return eErr;
    1372             :     }
    1373             : 
    1374             :     // Check that band block sizes didn't change the dataset block size.
    1375         648 :     for (int i = 1; i <= nBands; i++)
    1376             :     {
    1377         450 :         int nBlockXSize = 0;
    1378         450 :         int nBlockYSize = 0;
    1379         450 :         GetRasterBand(i)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1380         450 :         if (nBlockXSize != m_nBlockXSize || nBlockYSize != m_nBlockYSize)
    1381             :         {
    1382           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1383             :                      "Block size specified on band %d not consistent with "
    1384             :                      "dataset block size",
    1385             :                      i);
    1386           2 :             return CE_Failure;
    1387             :         }
    1388             :     }
    1389             : 
    1390         198 :     if (nBands > 1)
    1391             :     {
    1392          93 :         GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    1393             :     }
    1394             : 
    1395             :     /* -------------------------------------------------------------------- */
    1396             :     /*      Find the GDALWarpOptions XML tree.                              */
    1397             :     /* -------------------------------------------------------------------- */
    1398             :     const CPLXMLNode *const psOptionsTree =
    1399         198 :         CPLGetXMLNode(psTree, "GDALWarpOptions");
    1400         198 :     if (psOptionsTree == nullptr)
    1401             :     {
    1402           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1403             :                  "Count not find required GDALWarpOptions in XML.");
    1404           0 :         return CE_Failure;
    1405             :     }
    1406             : 
    1407             :     /* -------------------------------------------------------------------- */
    1408             :     /*      Adjust the SourceDataset in the warp options to take into       */
    1409             :     /*      account that it is relative to the VRT if appropriate.          */
    1410             :     /* -------------------------------------------------------------------- */
    1411         198 :     const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
    1412             :         CPLGetXMLValue(psOptionsTree, "SourceDataset.relativeToVRT", "0")));
    1413             : 
    1414             :     const char *pszRelativePath =
    1415         198 :         CPLGetXMLValue(psOptionsTree, "SourceDataset", "");
    1416         198 :     char *pszAbsolutePath = nullptr;
    1417             : 
    1418         198 :     if (bRelativeToVRT)
    1419         131 :         pszAbsolutePath = CPLStrdup(
    1420             :             CPLProjectRelativeFilename(pszVRTPathIn, pszRelativePath));
    1421             :     else
    1422          67 :         pszAbsolutePath = CPLStrdup(pszRelativePath);
    1423             : 
    1424         198 :     CPLXMLNode *psOptionsTreeCloned = CPLCloneXMLTree(psOptionsTree);
    1425         198 :     CPLSetXMLValue(psOptionsTreeCloned, "SourceDataset", pszAbsolutePath);
    1426         198 :     CPLFree(pszAbsolutePath);
    1427             : 
    1428             :     /* -------------------------------------------------------------------- */
    1429             :     /*      And instantiate the warp options, and corresponding warp        */
    1430             :     /*      operation.                                                      */
    1431             :     /* -------------------------------------------------------------------- */
    1432         198 :     GDALWarpOptions *psWO = GDALDeserializeWarpOptions(psOptionsTreeCloned);
    1433         198 :     CPLDestroyXMLNode(psOptionsTreeCloned);
    1434         198 :     if (psWO == nullptr)
    1435           0 :         return CE_Failure;
    1436             : 
    1437         198 :     psWO->papszWarpOptions = VRTWarpedAddOptions(psWO->papszWarpOptions);
    1438             : 
    1439         198 :     eAccess = GA_Update;
    1440             : 
    1441         198 :     if (psWO->hDstDS != nullptr)
    1442             :     {
    1443           0 :         GDALClose(psWO->hDstDS);
    1444           0 :         psWO->hDstDS = nullptr;
    1445             :     }
    1446             : 
    1447         198 :     psWO->hDstDS = this;
    1448             : 
    1449             :     /* -------------------------------------------------------------------- */
    1450             :     /*      Deserialize vertical shift grids.                               */
    1451             :     /* -------------------------------------------------------------------- */
    1452         198 :     CPLXMLNode *psIter = psTree->psChild;
    1453        2395 :     for (; psWO->hSrcDS != nullptr && psIter != nullptr;
    1454        2197 :          psIter = psIter->psNext)
    1455             :     {
    1456        2197 :         if (psIter->eType != CXT_Element ||
    1457        1603 :             !EQUAL(psIter->pszValue, "VerticalShiftGrids"))
    1458             :         {
    1459        2197 :             continue;
    1460             :         }
    1461             : 
    1462           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1463             :                  "The VerticalShiftGrids in a warped VRT is now deprecated, "
    1464             :                  "and will no longer be handled in GDAL 4.0");
    1465             : 
    1466           0 :         const char *pszVGrids = CPLGetXMLValue(psIter, "Grids", nullptr);
    1467           0 :         if (pszVGrids)
    1468             :         {
    1469             :             int bInverse =
    1470           0 :                 CSLTestBoolean(CPLGetXMLValue(psIter, "Inverse", "FALSE"));
    1471             :             double dfToMeterSrc =
    1472           0 :                 CPLAtof(CPLGetXMLValue(psIter, "ToMeterSrc", "1.0"));
    1473             :             double dfToMeterDest =
    1474           0 :                 CPLAtof(CPLGetXMLValue(psIter, "ToMeterDest", "1.0"));
    1475           0 :             char **papszOptions = nullptr;
    1476           0 :             CPLXMLNode *psIter2 = psIter->psChild;
    1477           0 :             for (; psIter2 != nullptr; psIter2 = psIter2->psNext)
    1478             :             {
    1479           0 :                 if (psIter2->eType != CXT_Element ||
    1480           0 :                     !EQUAL(psIter2->pszValue, "Option"))
    1481             :                 {
    1482           0 :                     continue;
    1483             :                 }
    1484           0 :                 const char *pszName = CPLGetXMLValue(psIter2, "name", nullptr);
    1485             :                 const char *pszValue =
    1486           0 :                     CPLGetXMLValue(psIter2, nullptr, nullptr);
    1487           0 :                 if (pszName && pszValue)
    1488             :                 {
    1489             :                     papszOptions =
    1490           0 :                         CSLSetNameValue(papszOptions, pszName, pszValue);
    1491             :                 }
    1492             :             }
    1493             : 
    1494           0 :             int bError = FALSE;
    1495             :             GDALDatasetH hGridDataset =
    1496           0 :                 GDALOpenVerticalShiftGrid(pszVGrids, &bError);
    1497           0 :             if (bError && hGridDataset == nullptr)
    1498             :             {
    1499           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1500             :                          "Cannot open %s. Source dataset will no "
    1501             :                          "be vertically adjusted regarding "
    1502             :                          "vertical datum",
    1503             :                          pszVGrids);
    1504             :             }
    1505           0 :             else if (hGridDataset != nullptr)
    1506             :             {
    1507             :                 // Transform from source vertical datum to WGS84
    1508           0 :                 GDALDatasetH hTmpDS = GDALApplyVerticalShiftGrid(
    1509             :                     psWO->hSrcDS, hGridDataset, bInverse, dfToMeterSrc,
    1510             :                     dfToMeterDest, papszOptions);
    1511           0 :                 GDALReleaseDataset(hGridDataset);
    1512           0 :                 if (hTmpDS == nullptr)
    1513             :                 {
    1514           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1515             :                              "Source dataset will no "
    1516             :                              "be vertically adjusted regarding "
    1517             :                              "vertical datum %s",
    1518             :                              pszVGrids);
    1519             :                 }
    1520             :                 else
    1521             :                 {
    1522           0 :                     CPLDebug("GDALWARP",
    1523             :                              "Adjusting source dataset "
    1524             :                              "with vertical datum using %s",
    1525             :                              pszVGrids);
    1526           0 :                     GDALReleaseDataset(psWO->hSrcDS);
    1527           0 :                     psWO->hSrcDS = hTmpDS;
    1528             :                 }
    1529             :             }
    1530             : 
    1531           0 :             CSLDestroy(papszOptions);
    1532             :         }
    1533             :     }
    1534             : 
    1535             :     /* -------------------------------------------------------------------- */
    1536             :     /*      Instantiate the warp operation.                                 */
    1537             :     /* -------------------------------------------------------------------- */
    1538         198 :     m_poWarper = new GDALWarpOperation();
    1539             : 
    1540         198 :     const CPLErr eErr = m_poWarper->Initialize(psWO);
    1541         198 :     if (eErr != CE_None)
    1542             :     {
    1543             :         /* --------------------------------------------------------------------
    1544             :          */
    1545             :         /*      We are responsible for cleaning up the transformer ourselves. */
    1546             :         /* --------------------------------------------------------------------
    1547             :          */
    1548           0 :         if (psWO->pTransformerArg != nullptr)
    1549             :         {
    1550           0 :             GDALDestroyTransformer(psWO->pTransformerArg);
    1551           0 :             psWO->pTransformerArg = nullptr;
    1552             :         }
    1553             : 
    1554           0 :         if (psWO->hSrcDS != nullptr)
    1555             :         {
    1556           0 :             GDALClose(psWO->hSrcDS);
    1557           0 :             psWO->hSrcDS = nullptr;
    1558             :         }
    1559             :     }
    1560             : 
    1561         198 :     GDALDestroyWarpOptions(psWO);
    1562         198 :     if (eErr != CE_None)
    1563             :     {
    1564           0 :         delete m_poWarper;
    1565           0 :         m_poWarper = nullptr;
    1566             :     }
    1567             : 
    1568             :     /* -------------------------------------------------------------------- */
    1569             :     /*      Deserialize SrcOvrLevel                                         */
    1570             :     /* -------------------------------------------------------------------- */
    1571         198 :     const char *pszSrcOvrLevel = CPLGetXMLValue(psTree, "SrcOvrLevel", nullptr);
    1572         198 :     if (pszSrcOvrLevel != nullptr)
    1573             :     {
    1574           0 :         SetMetadataItem("SrcOvrLevel", pszSrcOvrLevel);
    1575             :     }
    1576             : 
    1577             :     /* -------------------------------------------------------------------- */
    1578             :     /*      Generate overviews, if appropriate.                             */
    1579             :     /* -------------------------------------------------------------------- */
    1580             : 
    1581             :     // OverviewList is historical, and quite inefficient, since it uses
    1582             :     // the full resolution source dataset, so only build it afterwards.
    1583             :     const CPLStringList aosOverviews(
    1584         198 :         CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", "")));
    1585         198 :     if (!aosOverviews.empty())
    1586           2 :         CreateImplicitOverviews();
    1587             : 
    1588         202 :     for (int iOverview = 0; iOverview < aosOverviews.size(); ++iOverview)
    1589             :     {
    1590           4 :         int nOvFactor = atoi(aosOverviews[iOverview]);
    1591             : 
    1592           4 :         if (nOvFactor > 0)
    1593           4 :             BuildOverviews("NEAREST", 1, &nOvFactor, 0, nullptr, nullptr,
    1594             :                            nullptr,
    1595             :                            /*papszOptions=*/nullptr);
    1596             :         else
    1597           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1598             :                      "Bad value for overview factor : %s",
    1599             :                      aosOverviews[iOverview]);
    1600             :     }
    1601             : 
    1602         198 :     return eErr;
    1603             : }
    1604             : 
    1605             : /************************************************************************/
    1606             : /*                           SerializeToXML()                           */
    1607             : /************************************************************************/
    1608             : 
    1609          77 : CPLXMLNode *VRTWarpedDataset::SerializeToXML(const char *pszVRTPathIn)
    1610             : 
    1611             : {
    1612          77 :     CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
    1613             : 
    1614          77 :     if (psTree == nullptr)
    1615           0 :         return psTree;
    1616             : 
    1617             :     /* -------------------------------------------------------------------- */
    1618             :     /*      Set subclass.                                                   */
    1619             :     /* -------------------------------------------------------------------- */
    1620          77 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    1621             :                      CXT_Text, "VRTWarpedDataset");
    1622             : 
    1623             :     /* -------------------------------------------------------------------- */
    1624             :     /*      Serialize the block size.                                       */
    1625             :     /* -------------------------------------------------------------------- */
    1626          77 :     CPLCreateXMLElementAndValue(psTree, "BlockXSize",
    1627             :                                 CPLSPrintf("%d", m_nBlockXSize));
    1628          77 :     CPLCreateXMLElementAndValue(psTree, "BlockYSize",
    1629             :                                 CPLSPrintf("%d", m_nBlockYSize));
    1630             : 
    1631             :     /* -------------------------------------------------------------------- */
    1632             :     /*      Serialize the overview list (only for non implicit overviews)   */
    1633             :     /* -------------------------------------------------------------------- */
    1634          77 :     if (!m_apoOverviews.empty())
    1635             :     {
    1636           3 :         int nSrcDSOvrCount = 0;
    1637           3 :         if (m_poWarper != nullptr && m_poWarper->GetOptions() != nullptr &&
    1638           9 :             m_poWarper->GetOptions()->hSrcDS != nullptr &&
    1639           3 :             GDALGetRasterCount(m_poWarper->GetOptions()->hSrcDS) > 0)
    1640             :         {
    1641             :             nSrcDSOvrCount =
    1642           3 :                 static_cast<GDALDataset *>(m_poWarper->GetOptions()->hSrcDS)
    1643           3 :                     ->GetRasterBand(1)
    1644           3 :                     ->GetOverviewCount();
    1645             :         }
    1646             : 
    1647           3 :         if (static_cast<int>(m_apoOverviews.size()) != nSrcDSOvrCount)
    1648             :         {
    1649           2 :             const size_t nLen = m_apoOverviews.size() * 8 + 10;
    1650           2 :             char *pszOverviewList = static_cast<char *>(CPLMalloc(nLen));
    1651           2 :             pszOverviewList[0] = '\0';
    1652           6 :             for (auto *poOverviewDS : m_apoOverviews)
    1653             :             {
    1654           4 :                 if (poOverviewDS)
    1655             :                 {
    1656             :                     const int nOvFactor = static_cast<int>(
    1657           4 :                         0.5 +
    1658           4 :                         GetRasterXSize() / static_cast<double>(
    1659           4 :                                                poOverviewDS->GetRasterXSize()));
    1660             : 
    1661           4 :                     snprintf(pszOverviewList + strlen(pszOverviewList),
    1662           4 :                              nLen - strlen(pszOverviewList), "%d ", nOvFactor);
    1663             :                 }
    1664             :             }
    1665             : 
    1666           2 :             CPLCreateXMLElementAndValue(psTree, "OverviewList",
    1667             :                                         pszOverviewList);
    1668             : 
    1669           2 :             CPLFree(pszOverviewList);
    1670             :         }
    1671             :     }
    1672             : 
    1673             :     /* -------------------------------------------------------------------- */
    1674             :     /*      Serialize source overview level.                                */
    1675             :     /* -------------------------------------------------------------------- */
    1676          77 :     if (m_nSrcOvrLevel != -2)
    1677             :     {
    1678           0 :         if (m_nSrcOvrLevel < -2)
    1679           0 :             CPLCreateXMLElementAndValue(
    1680             :                 psTree, "SrcOvrLevel",
    1681           0 :                 CPLSPrintf("AUTO%d", m_nSrcOvrLevel + 2));
    1682           0 :         else if (m_nSrcOvrLevel == -1)
    1683           0 :             CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel", "NONE");
    1684             :         else
    1685           0 :             CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel",
    1686             :                                         CPLSPrintf("%d", m_nSrcOvrLevel));
    1687             :     }
    1688             : 
    1689             :     /* ==================================================================== */
    1690             :     /*      Serialize the warp options.                                     */
    1691             :     /* ==================================================================== */
    1692          77 :     if (m_poWarper != nullptr)
    1693             :     {
    1694             :         /* --------------------------------------------------------------------
    1695             :          */
    1696             :         /*      We reset the destination dataset name so it doesn't get */
    1697             :         /*      written out in the serialize warp options. */
    1698             :         /* --------------------------------------------------------------------
    1699             :          */
    1700          77 :         char *const pszSavedName = CPLStrdup(GetDescription());
    1701          77 :         SetDescription("");
    1702             : 
    1703             :         CPLXMLNode *const psWOTree =
    1704          77 :             GDALSerializeWarpOptions(m_poWarper->GetOptions());
    1705          77 :         CPLAddXMLChild(psTree, psWOTree);
    1706             : 
    1707          77 :         SetDescription(pszSavedName);
    1708          77 :         CPLFree(pszSavedName);
    1709             : 
    1710             :         /* --------------------------------------------------------------------
    1711             :          */
    1712             :         /*      We need to consider making the source dataset relative to */
    1713             :         /*      the VRT file if possible.  Adjust accordingly. */
    1714             :         /* --------------------------------------------------------------------
    1715             :          */
    1716          77 :         CPLXMLNode *psSDS = CPLGetXMLNode(psWOTree, "SourceDataset");
    1717          77 :         int bRelativeToVRT = FALSE;
    1718             :         VSIStatBufL sStat;
    1719             : 
    1720          77 :         if (VSIStatExL(psSDS->psChild->pszValue, &sStat,
    1721          77 :                        VSI_STAT_EXISTS_FLAG) == 0)
    1722             :         {
    1723         150 :             std::string osVRTFilename = pszVRTPathIn;
    1724          75 :             std::string osSourceDataset = psSDS->psChild->pszValue;
    1725          75 :             char *pszCurDir = CPLGetCurrentDir();
    1726          75 :             if (CPLIsFilenameRelative(osSourceDataset.c_str()) &&
    1727          75 :                 !CPLIsFilenameRelative(osVRTFilename.c_str()) &&
    1728             :                 pszCurDir != nullptr)
    1729             :             {
    1730             :                 osSourceDataset = CPLFormFilename(
    1731          24 :                     pszCurDir, osSourceDataset.c_str(), nullptr);
    1732             :             }
    1733          51 :             else if (!CPLIsFilenameRelative(osSourceDataset.c_str()) &&
    1734          51 :                      CPLIsFilenameRelative(osVRTFilename.c_str()) &&
    1735             :                      pszCurDir != nullptr)
    1736             :             {
    1737             :                 osVRTFilename =
    1738           4 :                     CPLFormFilename(pszCurDir, osVRTFilename.c_str(), nullptr);
    1739             :             }
    1740          75 :             CPLFree(pszCurDir);
    1741          75 :             char *pszRelativePath = CPLStrdup(CPLExtractRelativePath(
    1742             :                 osVRTFilename.c_str(), osSourceDataset.c_str(),
    1743             :                 &bRelativeToVRT));
    1744             : 
    1745          75 :             CPLFree(psSDS->psChild->pszValue);
    1746          75 :             psSDS->psChild->pszValue = pszRelativePath;
    1747             :         }
    1748             : 
    1749          77 :         CPLCreateXMLNode(
    1750             :             CPLCreateXMLNode(psSDS, CXT_Attribute, "relativeToVRT"), CXT_Text,
    1751          77 :             bRelativeToVRT ? "1" : "0");
    1752             :     }
    1753             : 
    1754          77 :     return psTree;
    1755             : }
    1756             : 
    1757             : /************************************************************************/
    1758             : /*                            GetBlockSize()                            */
    1759             : /************************************************************************/
    1760             : 
    1761         658 : void VRTWarpedDataset::GetBlockSize(int *pnBlockXSize, int *pnBlockYSize) const
    1762             : 
    1763             : {
    1764         658 :     CPLAssert(nullptr != pnBlockXSize);
    1765         658 :     CPLAssert(nullptr != pnBlockYSize);
    1766             : 
    1767         658 :     *pnBlockXSize = m_nBlockXSize;
    1768         658 :     *pnBlockYSize = m_nBlockYSize;
    1769         658 : }
    1770             : 
    1771             : /************************************************************************/
    1772             : /*                            ProcessBlock()                            */
    1773             : /*                                                                      */
    1774             : /*      Warp a single requested block, and then push each band of       */
    1775             : /*      the result into the block cache.                                */
    1776             : /************************************************************************/
    1777             : 
    1778         235 : CPLErr VRTWarpedDataset::ProcessBlock(int iBlockX, int iBlockY)
    1779             : 
    1780             : {
    1781         235 :     if (m_poWarper == nullptr)
    1782           0 :         return CE_Failure;
    1783             : 
    1784         235 :     int nReqXSize = m_nBlockXSize;
    1785         235 :     if (iBlockX * m_nBlockXSize + nReqXSize > nRasterXSize)
    1786          62 :         nReqXSize = nRasterXSize - iBlockX * m_nBlockXSize;
    1787         235 :     int nReqYSize = m_nBlockYSize;
    1788         235 :     if (iBlockY * m_nBlockYSize + nReqYSize > nRasterYSize)
    1789          41 :         nReqYSize = nRasterYSize - iBlockY * m_nBlockYSize;
    1790             : 
    1791             :     GByte *pabyDstBuffer = static_cast<GByte *>(
    1792         235 :         m_poWarper->CreateDestinationBuffer(nReqXSize, nReqYSize));
    1793             : 
    1794         235 :     if (pabyDstBuffer == nullptr)
    1795             :     {
    1796           0 :         return CE_Failure;
    1797             :     }
    1798             : 
    1799             :     /* -------------------------------------------------------------------- */
    1800             :     /*      Warp into this buffer.                                          */
    1801             :     /* -------------------------------------------------------------------- */
    1802             : 
    1803         235 :     const GDALWarpOptions *psWO = m_poWarper->GetOptions();
    1804         470 :     const CPLErr eErr = m_poWarper->WarpRegionToBuffer(
    1805         235 :         iBlockX * m_nBlockXSize, iBlockY * m_nBlockYSize, nReqXSize, nReqYSize,
    1806         235 :         pabyDstBuffer, psWO->eWorkingDataType);
    1807             : 
    1808         235 :     if (eErr != CE_None)
    1809             :     {
    1810           0 :         m_poWarper->DestroyDestinationBuffer(pabyDstBuffer);
    1811           0 :         return eErr;
    1812             :     }
    1813             : 
    1814             :     /* -------------------------------------------------------------------- */
    1815             :     /*      Copy out into cache blocks for each band.                       */
    1816             :     /* -------------------------------------------------------------------- */
    1817         235 :     const int nWordSize = GDALGetDataTypeSizeBytes(psWO->eWorkingDataType);
    1818         851 :     for (int i = 0; i < psWO->nBandCount; i++)
    1819             :     {
    1820         616 :         int nDstBand = psWO->panDstBands[i];
    1821         616 :         if (GetRasterCount() < nDstBand)
    1822             :         {
    1823           0 :             continue;
    1824             :         }
    1825             : 
    1826         616 :         GDALRasterBand *poBand = GetRasterBand(nDstBand);
    1827             :         GDALRasterBlock *poBlock =
    1828         616 :             poBand->GetLockedBlockRef(iBlockX, iBlockY, TRUE);
    1829             : 
    1830         616 :         const GByte *pabyDstBandBuffer =
    1831             :             pabyDstBuffer +
    1832         616 :             static_cast<GPtrDiff_t>(i) * nReqXSize * nReqYSize * nWordSize;
    1833             : 
    1834         616 :         if (poBlock != nullptr)
    1835             :         {
    1836         616 :             if (poBlock->GetDataRef() != nullptr)
    1837             :             {
    1838         616 :                 if (nReqXSize == m_nBlockXSize && nReqYSize == m_nBlockYSize)
    1839             :                 {
    1840         393 :                     GDALCopyWords64(
    1841         393 :                         pabyDstBandBuffer, psWO->eWorkingDataType, nWordSize,
    1842             :                         poBlock->GetDataRef(), poBlock->GetDataType(),
    1843             :                         GDALGetDataTypeSizeBytes(poBlock->GetDataType()),
    1844         393 :                         static_cast<GPtrDiff_t>(m_nBlockXSize) * m_nBlockYSize);
    1845             :                 }
    1846             :                 else
    1847             :                 {
    1848             :                     GByte *pabyBlock =
    1849         223 :                         static_cast<GByte *>(poBlock->GetDataRef());
    1850             :                     const int nDTSize =
    1851         223 :                         GDALGetDataTypeSizeBytes(poBlock->GetDataType());
    1852       18716 :                     for (int iY = 0; iY < nReqYSize; iY++)
    1853             :                     {
    1854       18493 :                         GDALCopyWords(
    1855       18493 :                             pabyDstBandBuffer + static_cast<GPtrDiff_t>(iY) *
    1856       18493 :                                                     nReqXSize * nWordSize,
    1857       18493 :                             psWO->eWorkingDataType, nWordSize,
    1858       18493 :                             pabyBlock + static_cast<GPtrDiff_t>(iY) *
    1859       18493 :                                             m_nBlockXSize * nDTSize,
    1860             :                             poBlock->GetDataType(), nDTSize, nReqXSize);
    1861             :                     }
    1862             :                 }
    1863             :             }
    1864             : 
    1865         616 :             poBlock->DropLock();
    1866             :         }
    1867             :     }
    1868             : 
    1869         235 :     m_poWarper->DestroyDestinationBuffer(pabyDstBuffer);
    1870             : 
    1871         235 :     return CE_None;
    1872             : }
    1873             : 
    1874             : /************************************************************************/
    1875             : /*                              IRasterIO()                             */
    1876             : /************************************************************************/
    1877             : 
    1878             : // Specialized implementation of IRasterIO() that will be faster than
    1879             : // using the VRTWarpedRasterBand::IReadBlock() method in situations where
    1880             : // - a large enough chunk of data is requested at once
    1881             : // - and multi-threaded warping is enabled (it only kicks in if the warped
    1882             : //   chunk is large enough) and/or when reading the source dataset is
    1883             : //   multi-threaded (e.g JP2KAK or JP2OpenJPEG driver).
    1884         241 : CPLErr VRTWarpedDataset::IRasterIO(
    1885             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1886             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1887             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1888             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
    1889             : {
    1890         218 :     const bool bWholeImage = nXOff == 0 && nYOff == 0 &&
    1891         459 :                              nXSize == nRasterXSize && nYSize == nRasterYSize;
    1892             : 
    1893         482 :     if (eRWFlag == GF_Write ||
    1894             :         // For too small request fall back to the block-based approach to
    1895             :         // benefit from caching
    1896         241 :         (!bWholeImage &&
    1897          45 :          (nBufXSize <= m_nBlockXSize || nBufYSize <= m_nBlockYSize)) ||
    1898             :         // Or if we don't request all bands at once
    1899         663 :         nBandCount < nBands ||
    1900         181 :         !CPLTestBool(
    1901             :             CPLGetConfigOption("GDAL_VRT_WARP_USE_DATASET_RASTERIO", "YES")))
    1902             :     {
    1903          68 :         return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1904             :                                       pData, nBufXSize, nBufYSize, eBufType,
    1905             :                                       nBandCount, panBandMap, nPixelSpace,
    1906          68 :                                       nLineSpace, nBandSpace, psExtraArg);
    1907             :     }
    1908             : 
    1909             :     // Try overviews for sub-sampled requests
    1910         173 :     if (nBufXSize < nXSize || nBufYSize < nYSize)
    1911             :     {
    1912           5 :         int bTried = FALSE;
    1913           5 :         const CPLErr eErr = TryOverviewRasterIO(
    1914             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1915             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    1916             :             nBandSpace, psExtraArg, &bTried);
    1917             : 
    1918           5 :         if (bTried)
    1919             :         {
    1920           3 :             return eErr;
    1921             :         }
    1922             :     }
    1923             : 
    1924         170 :     if (m_poWarper == nullptr)
    1925           0 :         return CE_Failure;
    1926             : 
    1927         170 :     const GDALWarpOptions *psWO = m_poWarper->GetOptions();
    1928             : 
    1929         170 :     if (nBufXSize != nXSize || nBufYSize != nYSize)
    1930             :     {
    1931           2 :         if (!bWholeImage || !GDALTransformHasFastClone(psWO->pTransformerArg))
    1932             :         {
    1933           0 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1934             :                                           pData, nBufXSize, nBufYSize, eBufType,
    1935             :                                           nBandCount, panBandMap, nPixelSpace,
    1936           0 :                                           nLineSpace, nBandSpace, psExtraArg);
    1937             :         }
    1938             : 
    1939             :         // Build a temporary dataset taking into account the rescaling
    1940           2 :         void *pTransformerArg = GDALCloneTransformer(psWO->pTransformerArg);
    1941           2 :         if (pTransformerArg == nullptr)
    1942             :         {
    1943           0 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1944             :                                           pData, nBufXSize, nBufYSize, eBufType,
    1945             :                                           nBandCount, panBandMap, nPixelSpace,
    1946           0 :                                           nLineSpace, nBandSpace, psExtraArg);
    1947             :         }
    1948             : 
    1949           2 :         GDALWarpOptions *psRescaledWO = GDALCloneWarpOptions(psWO);
    1950           2 :         psRescaledWO->hSrcDS = psWO->hSrcDS;
    1951           2 :         psRescaledWO->pfnTransformer = psWO->pfnTransformer;
    1952           2 :         psRescaledWO->pTransformerArg = pTransformerArg;
    1953             : 
    1954             :         // Rescale the output geotransform on the transformer.
    1955           2 :         double adfDstGeoTransform[6] = {0.0};
    1956           2 :         GDALGetTransformerDstGeoTransform(psRescaledWO->pTransformerArg,
    1957             :                                           adfDstGeoTransform);
    1958           2 :         RescaleDstGeoTransform(adfDstGeoTransform, nRasterXSize, nBufXSize,
    1959             :                                nRasterYSize, nBufYSize);
    1960           2 :         GDALSetTransformerDstGeoTransform(psRescaledWO->pTransformerArg,
    1961             :                                           adfDstGeoTransform);
    1962             : 
    1963             :         GDALDatasetH hDstDS =
    1964           2 :             GDALCreateWarpedVRT(psWO->hSrcDS, nBufXSize, nBufYSize,
    1965             :                                 adfDstGeoTransform, psRescaledWO);
    1966             : 
    1967           2 :         GDALDestroyWarpOptions(psRescaledWO);
    1968             : 
    1969           2 :         if (hDstDS == nullptr)
    1970             :         {
    1971             :             // Not supposed to happen in nominal circumstances. Could perhaps
    1972             :             // happen if some memory allocation error occurred in code called
    1973             :             // by GDALCreateWarpedVRT()
    1974           0 :             GDALDestroyTransformer(pTransformerArg);
    1975           0 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1976             :                                           pData, nBufXSize, nBufYSize, eBufType,
    1977             :                                           nBandCount, panBandMap, nPixelSpace,
    1978           0 :                                           nLineSpace, nBandSpace, psExtraArg);
    1979             :         }
    1980             : 
    1981           2 :         auto poOvrDS = static_cast<VRTWarpedDataset *>(hDstDS);
    1982           2 :         poOvrDS->m_bIsOverview = true;
    1983             : 
    1984             :         GDALRasterIOExtraArg sExtraArg;
    1985           2 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1986           2 :         CPLErr eErr = poOvrDS->IRasterIO(GF_Read, 0, 0, nBufXSize, nBufYSize,
    1987             :                                          pData, nBufXSize, nBufYSize, eBufType,
    1988             :                                          nBandCount, panBandMap, nPixelSpace,
    1989             :                                          nLineSpace, nBandSpace, &sExtraArg);
    1990             : 
    1991           2 :         poOvrDS->ReleaseRef();
    1992           2 :         return eErr;
    1993             :     }
    1994             : 
    1995             :     // Build a map from warped output bands to their index
    1996         336 :     std::map<int, int> oMapBandToWarpingBandIndex;
    1997         168 :     bool bAllBandsIncreasingOrder =
    1998         168 :         (psWO->nBandCount == nBands && nBands == nBandCount);
    1999         356 :     for (int i = 0; i < psWO->nBandCount; ++i)
    2000             :     {
    2001         188 :         oMapBandToWarpingBandIndex[psWO->panDstBands[i]] = i;
    2002         188 :         if (psWO->panDstBands[i] != i + 1 || panBandMap[i] != i + 1)
    2003             :         {
    2004           3 :             bAllBandsIncreasingOrder = false;
    2005             :         }
    2006             :     }
    2007             : 
    2008             :     // Check that all requested bands are actually warped output bands.
    2009         356 :     for (int i = 0; i < nBandCount; ++i)
    2010             :     {
    2011         189 :         const int nRasterIOBand = panBandMap[i];
    2012         189 :         if (oMapBandToWarpingBandIndex.find(nRasterIOBand) ==
    2013         378 :             oMapBandToWarpingBandIndex.end())
    2014             :         {
    2015             :             // Not sure if that can happen...
    2016             :             // but if that does, that will likely later fail in ProcessBlock()
    2017           1 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2018             :                                           pData, nBufXSize, nBufYSize, eBufType,
    2019             :                                           nBandCount, panBandMap, nPixelSpace,
    2020           1 :                                           nLineSpace, nBandSpace, psExtraArg);
    2021             :         }
    2022             :     }
    2023             : 
    2024         167 :     int nSrcXOff = 0;
    2025         167 :     int nSrcYOff = 0;
    2026         167 :     int nSrcXSize = 0;
    2027         167 :     int nSrcYSize = 0;
    2028         167 :     double dfSrcXExtraSize = 0;
    2029         167 :     double dfSrcYExtraSize = 0;
    2030         167 :     double dfSrcFillRatio = 0;
    2031             :     // Find the source window that corresponds to our target window
    2032         167 :     if (m_poWarper->ComputeSourceWindow(nXOff, nYOff, nXSize, nYSize, &nSrcXOff,
    2033             :                                         &nSrcYOff, &nSrcXSize, &nSrcYSize,
    2034             :                                         &dfSrcXExtraSize, &dfSrcYExtraSize,
    2035         167 :                                         &dfSrcFillRatio) != CE_None)
    2036             :     {
    2037           0 :         return CE_Failure;
    2038             :     }
    2039             : 
    2040         167 :     GByte *const pabyDst = static_cast<GByte *>(pData);
    2041         167 :     const int nWarpDTSize = GDALGetDataTypeSizeBytes(psWO->eWorkingDataType);
    2042             : 
    2043         167 :     const double dfMemRequired = m_poWarper->GetWorkingMemoryForWindow(
    2044             :         nSrcXSize, nSrcYSize, nXSize, nYSize);
    2045             :     // If we need more warp working memory than allowed, we have to use a
    2046             :     // splitting strategy until we get below the limit.
    2047         167 :     if (dfMemRequired > psWO->dfWarpMemoryLimit && nXSize >= 2 && nYSize >= 2)
    2048             :     {
    2049           3 :         CPLDebugOnly("VRT", "VRTWarpedDataset::IRasterIO(): exceeding warp "
    2050             :                             "memory. Splitting region");
    2051             : 
    2052             :         GDALRasterIOExtraArg sExtraArg;
    2053           3 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    2054             : 
    2055             :         bool bOK;
    2056             :         // Split along the longest dimension
    2057           3 :         if (nXSize >= nYSize)
    2058             :         {
    2059           2 :             const int nHalfXSize = nXSize / 2;
    2060           4 :             bOK = IRasterIO(GF_Read, nXOff, nYOff, nHalfXSize, nYSize, pabyDst,
    2061             :                             nHalfXSize, nYSize, eBufType, nBandCount,
    2062             :                             panBandMap, nPixelSpace, nLineSpace, nBandSpace,
    2063           4 :                             &sExtraArg) == CE_None &&
    2064           2 :                   IRasterIO(GF_Read, nXOff + nHalfXSize, nYOff,
    2065             :                             nXSize - nHalfXSize, nYSize,
    2066           2 :                             pabyDst + nHalfXSize * nPixelSpace,
    2067             :                             nXSize - nHalfXSize, nYSize, eBufType, nBandCount,
    2068             :                             panBandMap, nPixelSpace, nLineSpace, nBandSpace,
    2069             :                             &sExtraArg) == CE_None;
    2070             :         }
    2071             :         else
    2072             :         {
    2073           1 :             const int nHalfYSize = nYSize / 2;
    2074           2 :             bOK = IRasterIO(GF_Read, nXOff, nYOff, nXSize, nHalfYSize, pabyDst,
    2075             :                             nXSize, nHalfYSize, eBufType, nBandCount,
    2076             :                             panBandMap, nPixelSpace, nLineSpace, nBandSpace,
    2077           2 :                             &sExtraArg) == CE_None &&
    2078           1 :                   IRasterIO(GF_Read, nXOff, nYOff + nHalfYSize, nXSize,
    2079             :                             nYSize - nHalfYSize,
    2080           1 :                             pabyDst + nHalfYSize * nLineSpace, nXSize,
    2081             :                             nYSize - nHalfYSize, eBufType, nBandCount,
    2082             :                             panBandMap, nPixelSpace, nLineSpace, nBandSpace,
    2083             :                             &sExtraArg) == CE_None;
    2084             :         }
    2085           3 :         return bOK ? CE_None : CE_Failure;
    2086             :     }
    2087             : 
    2088         164 :     CPLDebugOnly("VRT",
    2089             :                  "Using optimized VRTWarpedDataset::IRasterIO() code path");
    2090             : 
    2091             :     // Allocate a warping destination buffer if needed.
    2092             :     // We can use directly the output buffer pData if:
    2093             :     // - we request exactly all warped bands, and that there are as many
    2094             :     //   warped bands as dataset bands (no alpha)
    2095             :     // - the output buffer data atype is the warping working data type
    2096             :     // - the output buffer has a band-sequential layout.
    2097             :     GByte *pabyWarpBuffer;
    2098             : 
    2099         162 :     if (bAllBandsIncreasingOrder && psWO->eWorkingDataType == eBufType &&
    2100          79 :         nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
    2101         393 :         nLineSpace == nPixelSpace * nXSize &&
    2102          67 :         (nBands == 1 || nBandSpace == nLineSpace * nYSize))
    2103             :     {
    2104          67 :         pabyWarpBuffer = static_cast<GByte *>(pData);
    2105          67 :         m_poWarper->InitializeDestinationBuffer(pabyWarpBuffer, nXSize, nYSize);
    2106             :     }
    2107             :     else
    2108             :     {
    2109             :         pabyWarpBuffer = static_cast<GByte *>(
    2110          97 :             m_poWarper->CreateDestinationBuffer(nXSize, nYSize));
    2111             : 
    2112          97 :         if (pabyWarpBuffer == nullptr)
    2113             :         {
    2114           0 :             return CE_Failure;
    2115             :         }
    2116             :     }
    2117             : 
    2118         328 :     const CPLErr eErr = m_poWarper->WarpRegionToBuffer(
    2119         164 :         nXOff, nYOff, nXSize, nYSize, pabyWarpBuffer, psWO->eWorkingDataType,
    2120             :         nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, dfSrcXExtraSize,
    2121             :         dfSrcYExtraSize);
    2122             : 
    2123         164 :     if (pabyWarpBuffer != pData)
    2124             :     {
    2125          97 :         if (eErr == CE_None)
    2126             :         {
    2127             :             // Copy warping buffer into user destination buffer
    2128         202 :             for (int i = 0; i < nBandCount; i++)
    2129             :             {
    2130         105 :                 const int nRasterIOBand = panBandMap[i];
    2131             :                 const auto oIterToWarpingBandIndex =
    2132         105 :                     oMapBandToWarpingBandIndex.find(nRasterIOBand);
    2133             :                 // cannot happen due to earlier check
    2134         105 :                 CPLAssert(oIterToWarpingBandIndex !=
    2135             :                           oMapBandToWarpingBandIndex.end());
    2136             : 
    2137             :                 const GByte *const pabyWarpBandBuffer =
    2138             :                     pabyWarpBuffer +
    2139         105 :                     static_cast<GPtrDiff_t>(oIterToWarpingBandIndex->second) *
    2140         105 :                         nXSize * nYSize * nWarpDTSize;
    2141         105 :                 GByte *const pabyDstBand = pabyDst + i * nBandSpace;
    2142             : 
    2143       17256 :                 for (int iY = 0; iY < nYSize; iY++)
    2144             :                 {
    2145       17151 :                     GDALCopyWords(pabyWarpBandBuffer +
    2146       17151 :                                       static_cast<GPtrDiff_t>(iY) * nXSize *
    2147       17151 :                                           nWarpDTSize,
    2148       17151 :                                   psWO->eWorkingDataType, nWarpDTSize,
    2149       17151 :                                   pabyDstBand + iY * nLineSpace, eBufType,
    2150             :                                   static_cast<int>(nPixelSpace), nXSize);
    2151             :                 }
    2152             :             }
    2153             :         }
    2154             : 
    2155          97 :         m_poWarper->DestroyDestinationBuffer(pabyWarpBuffer);
    2156             :     }
    2157             : 
    2158         164 :     return eErr;
    2159             : }
    2160             : 
    2161             : /************************************************************************/
    2162             : /*                              AddBand()                               */
    2163             : /************************************************************************/
    2164             : 
    2165         205 : CPLErr VRTWarpedDataset::AddBand(GDALDataType eType, char ** /* papszOptions */)
    2166             : 
    2167             : {
    2168         205 :     if (eType == GDT_Unknown || eType == GDT_TypeCount)
    2169             :     {
    2170           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    2171             :                     "Illegal GDT_Unknown/GDT_TypeCount argument");
    2172           1 :         return CE_Failure;
    2173             :     }
    2174             : 
    2175         204 :     SetBand(GetRasterCount() + 1,
    2176         204 :             new VRTWarpedRasterBand(this, GetRasterCount() + 1, eType));
    2177             : 
    2178         204 :     return CE_None;
    2179             : }
    2180             : 
    2181             : /************************************************************************/
    2182             : /* ==================================================================== */
    2183             : /*                        VRTWarpedRasterBand                           */
    2184             : /* ==================================================================== */
    2185             : /************************************************************************/
    2186             : 
    2187             : /************************************************************************/
    2188             : /*                        VRTWarpedRasterBand()                         */
    2189             : /************************************************************************/
    2190             : 
    2191         658 : VRTWarpedRasterBand::VRTWarpedRasterBand(GDALDataset *poDSIn, int nBandIn,
    2192         658 :                                          GDALDataType eType)
    2193             : 
    2194             : {
    2195         658 :     Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
    2196             : 
    2197         658 :     poDS = poDSIn;
    2198         658 :     nBand = nBandIn;
    2199         658 :     eAccess = GA_Update;
    2200             : 
    2201         658 :     static_cast<VRTWarpedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
    2202             :                                                         &nBlockYSize);
    2203             : 
    2204         658 :     if (eType != GDT_Unknown)
    2205         208 :         eDataType = eType;
    2206         658 : }
    2207             : 
    2208             : /************************************************************************/
    2209             : /*                        ~VRTWarpedRasterBand()                        */
    2210             : /************************************************************************/
    2211             : 
    2212        1316 : VRTWarpedRasterBand::~VRTWarpedRasterBand()
    2213             : 
    2214             : {
    2215         658 :     FlushCache(true);
    2216        1316 : }
    2217             : 
    2218             : /************************************************************************/
    2219             : /*                             IReadBlock()                             */
    2220             : /************************************************************************/
    2221             : 
    2222         235 : CPLErr VRTWarpedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    2223             :                                        void *pImage)
    2224             : 
    2225             : {
    2226         235 :     VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2227             :     const GPtrDiff_t nDataBytes =
    2228         235 :         static_cast<GPtrDiff_t>(GDALGetDataTypeSizeBytes(eDataType)) *
    2229         235 :         nBlockXSize * nBlockYSize;
    2230             : 
    2231         235 :     GDALRasterBlock *poBlock = GetLockedBlockRef(nBlockXOff, nBlockYOff, TRUE);
    2232         235 :     if (poBlock == nullptr)
    2233           0 :         return CE_Failure;
    2234             : 
    2235         235 :     if (poWDS->m_poWarper)
    2236             :     {
    2237         235 :         const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
    2238         235 :         if (nBand == psWO->nDstAlphaBand)
    2239             :         {
    2240             :             // For a reader starting by asking on band 1, we should normally
    2241             :             // not reach here, because ProcessBlock() on band 1 will have
    2242             :             // populated the block cache for the regular bands and the alpha
    2243             :             // band.
    2244             :             // But if there's no source window corresponding to the block,
    2245             :             // the alpha band block will not be written through RasterIO(),
    2246             :             // so we nee to initialize it.
    2247          88 :             memset(poBlock->GetDataRef(), 0, nDataBytes);
    2248             :         }
    2249             :     }
    2250             : 
    2251         235 :     const CPLErr eErr = poWDS->ProcessBlock(nBlockXOff, nBlockYOff);
    2252             : 
    2253         235 :     if (eErr == CE_None && pImage != poBlock->GetDataRef())
    2254             :     {
    2255           0 :         memcpy(pImage, poBlock->GetDataRef(), nDataBytes);
    2256             :     }
    2257             : 
    2258         235 :     poBlock->DropLock();
    2259             : 
    2260         235 :     return eErr;
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                            IWriteBlock()                             */
    2265             : /************************************************************************/
    2266             : 
    2267          81 : CPLErr VRTWarpedRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
    2268             :                                         void *pImage)
    2269             : 
    2270             : {
    2271          81 :     VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2272             : 
    2273             :     // This is a bit tricky. In the case we are warping a VRTWarpedDataset
    2274             :     // with a destination alpha band, IWriteBlock can be called on that alpha
    2275             :     // band by GDALWarpDstAlphaMasker
    2276             :     // We don't need to do anything since the data will have hopefully been
    2277             :     // read from the block cache before if the reader processes all the bands
    2278             :     // of a same block.
    2279          81 :     if (poWDS->m_poWarper->GetOptions()->nDstAlphaBand != nBand)
    2280             :     {
    2281             :         /* Otherwise, call the superclass method, that will fail of course */
    2282           0 :         return VRTRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage);
    2283             :     }
    2284             : 
    2285          81 :     return CE_None;
    2286             : }
    2287             : 
    2288             : /************************************************************************/
    2289             : /*                       GetBestOverviewLevel()                         */
    2290             : /************************************************************************/
    2291             : 
    2292           0 : int VRTWarpedRasterBand::GetBestOverviewLevel(
    2293             :     int &nXOff, int &nYOff, int &nXSize, int &nYSize, int nBufXSize,
    2294             :     int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
    2295             : {
    2296           0 :     VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2297             : 
    2298             :     /* -------------------------------------------------------------------- */
    2299             :     /*      Compute the desired downsampling factor.  It is                 */
    2300             :     /*      based on the least reduced axis, and represents the number      */
    2301             :     /*      of source pixels to one destination pixel.                      */
    2302             :     /* -------------------------------------------------------------------- */
    2303           0 :     const double dfDesiredDownsamplingFactor =
    2304           0 :         ((nXSize / static_cast<double>(nBufXSize)) <
    2305           0 :              (nYSize / static_cast<double>(nBufYSize)) ||
    2306             :          nBufYSize == 1)
    2307           0 :             ? nXSize / static_cast<double>(nBufXSize)
    2308           0 :             : nYSize / static_cast<double>(nBufYSize);
    2309             : 
    2310             :     /* -------------------------------------------------------------------- */
    2311             :     /*      Find the overview level that largest downsampling factor (most  */
    2312             :     /*      downsampled) that is still less than (or only a little more)    */
    2313             :     /*      downsampled than the request.                                   */
    2314             :     /* -------------------------------------------------------------------- */
    2315           0 :     const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
    2316           0 :     GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
    2317           0 :     const int nOverviewCount = poSrcDS->GetRasterBand(1)->GetOverviewCount();
    2318             : 
    2319           0 :     int nBestOverviewXSize = 1;
    2320           0 :     int nBestOverviewYSize = 1;
    2321           0 :     double dfBestDownsamplingFactor = 0;
    2322           0 :     int nBestOverviewLevel = -1;
    2323             : 
    2324             :     const char *pszOversampligThreshold =
    2325           0 :         CPLGetConfigOption("GDAL_OVERVIEW_OVERSAMPLING_THRESHOLD", nullptr);
    2326             : 
    2327             :     // Cf https://github.com/OSGeo/gdal/pull/9040#issuecomment-1898524693
    2328             :     // Do not exactly use a oversampling threshold of 1.0 because of numerical
    2329             :     // instability.
    2330           0 :     const auto AdjustThreshold = [](double x)
    2331             :     {
    2332           0 :         constexpr double EPS = 1e-2;
    2333           0 :         return x == 1.0 ? x + EPS : x;
    2334             :     };
    2335           0 :     const double dfOversamplingThreshold = AdjustThreshold(
    2336           0 :         pszOversampligThreshold ? CPLAtof(pszOversampligThreshold)
    2337           0 :         : psExtraArg && psExtraArg->eResampleAlg != GRIORA_NearestNeighbour
    2338           0 :             ? 1.0
    2339             :             : 1.2);
    2340           0 :     for (int iOverview = 0; iOverview < nOverviewCount; iOverview++)
    2341             :     {
    2342           0 :         const GDALRasterBand *poSrcOvrBand = this;
    2343           0 :         bool bThisLevelOnly = false;
    2344             :         const int iSrcOvr =
    2345           0 :             poWDS->GetSrcOverviewLevel(iOverview, bThisLevelOnly);
    2346           0 :         if (iSrcOvr >= 0)
    2347             :         {
    2348           0 :             poSrcOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr);
    2349             :         }
    2350           0 :         if (poSrcOvrBand == nullptr)
    2351           0 :             break;
    2352             : 
    2353           0 :         int nDstPixels = 0;
    2354           0 :         int nDstLines = 0;
    2355           0 :         double dfSrcRatioX = 0;
    2356           0 :         double dfSrcRatioY = 0;
    2357           0 :         if (!poWDS->GetOverviewSize(poSrcDS, iOverview, iSrcOvr, nDstPixels,
    2358             :                                     nDstLines, dfSrcRatioX, dfSrcRatioY))
    2359             :         {
    2360           0 :             break;
    2361             :         }
    2362             : 
    2363             :         // Compute downsampling factor of this overview
    2364             :         const double dfDownsamplingFactor =
    2365           0 :             std::min(nRasterXSize / static_cast<double>(nDstPixels),
    2366           0 :                      nRasterYSize / static_cast<double>(nDstLines));
    2367             : 
    2368             :         // Is it nearly the requested factor and better (lower) than
    2369             :         // the current best factor?
    2370           0 :         if (dfDownsamplingFactor >=
    2371           0 :                 dfDesiredDownsamplingFactor * dfOversamplingThreshold ||
    2372             :             dfDownsamplingFactor <= dfBestDownsamplingFactor)
    2373             :         {
    2374           0 :             continue;
    2375             :         }
    2376             : 
    2377             :         // Ignore AVERAGE_BIT2GRAYSCALE overviews for RasterIO purposes.
    2378             :         const char *pszResampling = const_cast<GDALRasterBand *>(poSrcOvrBand)
    2379           0 :                                         ->GetMetadataItem("RESAMPLING");
    2380             : 
    2381           0 :         if (pszResampling != nullptr &&
    2382           0 :             STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    2383           0 :             continue;
    2384             : 
    2385             :         // OK, this is our new best overview.
    2386           0 :         nBestOverviewXSize = nDstPixels;
    2387           0 :         nBestOverviewYSize = nDstLines;
    2388           0 :         nBestOverviewLevel = iOverview;
    2389           0 :         dfBestDownsamplingFactor = dfDownsamplingFactor;
    2390             :     }
    2391             : 
    2392             :     /* -------------------------------------------------------------------- */
    2393             :     /*      If we didn't find an overview that helps us, just return        */
    2394             :     /*      indicating failure and the full resolution image will be used.  */
    2395             :     /* -------------------------------------------------------------------- */
    2396           0 :     if (nBestOverviewLevel < 0)
    2397           0 :         return -1;
    2398             : 
    2399             :     /* -------------------------------------------------------------------- */
    2400             :     /*      Recompute the source window in terms of the selected            */
    2401             :     /*      overview.                                                       */
    2402             :     /* -------------------------------------------------------------------- */
    2403           0 :     const double dfXFactor =
    2404           0 :         nRasterXSize / static_cast<double>(nBestOverviewXSize);
    2405           0 :     const double dfYFactor =
    2406           0 :         nRasterYSize / static_cast<double>(nBestOverviewYSize);
    2407           0 :     CPLDebug("GDAL", "Selecting overview %d x %d", nBestOverviewXSize,
    2408             :              nBestOverviewYSize);
    2409             : 
    2410           0 :     const int nOXOff = std::min(nBestOverviewXSize - 1,
    2411           0 :                                 static_cast<int>(nXOff / dfXFactor + 0.5));
    2412           0 :     const int nOYOff = std::min(nBestOverviewYSize - 1,
    2413           0 :                                 static_cast<int>(nYOff / dfYFactor + 0.5));
    2414           0 :     int nOXSize = std::max(1, static_cast<int>(nXSize / dfXFactor + 0.5));
    2415           0 :     int nOYSize = std::max(1, static_cast<int>(nYSize / dfYFactor + 0.5));
    2416           0 :     if (nOXOff + nOXSize > nBestOverviewXSize)
    2417           0 :         nOXSize = nBestOverviewXSize - nOXOff;
    2418           0 :     if (nOYOff + nOYSize > nBestOverviewYSize)
    2419           0 :         nOYSize = nBestOverviewYSize - nOYOff;
    2420             : 
    2421           0 :     if (psExtraArg)
    2422             :     {
    2423           0 :         if (psExtraArg->bFloatingPointWindowValidity)
    2424             :         {
    2425           0 :             psExtraArg->dfXOff /= dfXFactor;
    2426           0 :             psExtraArg->dfXSize /= dfXFactor;
    2427           0 :             psExtraArg->dfYOff /= dfYFactor;
    2428           0 :             psExtraArg->dfYSize /= dfYFactor;
    2429             :         }
    2430           0 :         else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
    2431             :         {
    2432           0 :             psExtraArg->bFloatingPointWindowValidity = true;
    2433           0 :             psExtraArg->dfXOff = nXOff / dfXFactor;
    2434           0 :             psExtraArg->dfXSize = nXSize / dfXFactor;
    2435           0 :             psExtraArg->dfYOff = nYOff / dfYFactor;
    2436           0 :             psExtraArg->dfYSize = nYSize / dfYFactor;
    2437             :         }
    2438             :     }
    2439             : 
    2440           0 :     nXOff = nOXOff;
    2441           0 :     nYOff = nOYOff;
    2442           0 :     nXSize = nOXSize;
    2443           0 :     nYSize = nOYSize;
    2444             : 
    2445           0 :     return nBestOverviewLevel;
    2446             : }
    2447             : 
    2448             : /************************************************************************/
    2449             : /*                              IRasterIO()                             */
    2450             : /************************************************************************/
    2451             : 
    2452        1040 : CPLErr VRTWarpedRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2453             :                                       int nXSize, int nYSize, void *pData,
    2454             :                                       int nBufXSize, int nBufYSize,
    2455             :                                       GDALDataType eBufType,
    2456             :                                       GSpacing nPixelSpace, GSpacing nLineSpace,
    2457             :                                       GDALRasterIOExtraArg *psExtraArg)
    2458             : {
    2459        1040 :     VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2460        1040 :     if (m_nIRasterIOCounter == 0 && poWDS->GetRasterCount() == 1)
    2461             :     {
    2462         171 :         int anBandMap[] = {nBand};
    2463         171 :         ++m_nIRasterIOCounter;
    2464         171 :         const CPLErr eErr = poWDS->IRasterIO(
    2465             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2466             :             eBufType, 1, anBandMap, nPixelSpace, nLineSpace, 0, psExtraArg);
    2467         171 :         --m_nIRasterIOCounter;
    2468         171 :         return eErr;
    2469             :     }
    2470             : 
    2471             :     /* ==================================================================== */
    2472             :     /*      Do we have overviews that would be appropriate to satisfy       */
    2473             :     /*      this request?                                                   */
    2474             :     /* ==================================================================== */
    2475         869 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() &&
    2476             :         eRWFlag == GF_Read)
    2477             :     {
    2478             :         GDALRasterIOExtraArg sExtraArg;
    2479           0 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    2480             : 
    2481           0 :         const int nOverview = GetBestOverviewLevel(
    2482             :             nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, &sExtraArg);
    2483           0 :         if (nOverview >= 0)
    2484             :         {
    2485           0 :             auto poOvrBand = GetOverview(nOverview);
    2486           0 :             if (!poOvrBand)
    2487           0 :                 return CE_Failure;
    2488             : 
    2489           0 :             return poOvrBand->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2490             :                                        pData, nBufXSize, nBufYSize, eBufType,
    2491           0 :                                        nPixelSpace, nLineSpace, &sExtraArg);
    2492             :         }
    2493             :     }
    2494             : 
    2495         869 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2496             :                                      pData, nBufXSize, nBufYSize, eBufType,
    2497         869 :                                      nPixelSpace, nLineSpace, psExtraArg);
    2498             : }
    2499             : 
    2500             : /************************************************************************/
    2501             : /*                           SerializeToXML()                           */
    2502             : /************************************************************************/
    2503             : 
    2504         186 : CPLXMLNode *VRTWarpedRasterBand::SerializeToXML(const char *pszVRTPathIn,
    2505             :                                                 bool &bHasWarnedAboutRAMUsage,
    2506             :                                                 size_t &nAccRAMUsage)
    2507             : 
    2508             : {
    2509         186 :     CPLXMLNode *const psTree = VRTRasterBand::SerializeToXML(
    2510             :         pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
    2511             : 
    2512             :     /* -------------------------------------------------------------------- */
    2513             :     /*      Set subclass.                                                   */
    2514             :     /* -------------------------------------------------------------------- */
    2515         186 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    2516             :                      CXT_Text, "VRTWarpedRasterBand");
    2517             : 
    2518         186 :     return psTree;
    2519             : }
    2520             : 
    2521             : /************************************************************************/
    2522             : /*                          GetOverviewCount()                          */
    2523             : /************************************************************************/
    2524             : 
    2525          79 : int VRTWarpedRasterBand::GetOverviewCount()
    2526             : 
    2527             : {
    2528          79 :     VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2529          79 :     if (poWDS->m_bIsOverview)
    2530           1 :         return 0;
    2531             : 
    2532          78 :     if (poWDS->m_apoOverviews.empty())
    2533             :     {
    2534          50 :         return poWDS->GetOverviewCount();
    2535             :     }
    2536             : 
    2537          28 :     return static_cast<int>(poWDS->m_apoOverviews.size());
    2538             : }
    2539             : 
    2540             : /************************************************************************/
    2541             : /*                            GetOverview()                             */
    2542             : /************************************************************************/
    2543             : 
    2544          41 : GDALRasterBand *VRTWarpedRasterBand::GetOverview(int iOverview)
    2545             : 
    2546             : {
    2547          41 :     VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
    2548             : 
    2549          41 :     const int nOvrCount = GetOverviewCount();
    2550          41 :     if (iOverview < 0 || iOverview >= nOvrCount)
    2551           6 :         return nullptr;
    2552             : 
    2553          35 :     if (poWDS->m_apoOverviews.empty())
    2554          11 :         poWDS->m_apoOverviews.resize(nOvrCount);
    2555          35 :     if (!poWDS->m_apoOverviews[iOverview])
    2556          18 :         poWDS->m_apoOverviews[iOverview] =
    2557          18 :             poWDS->CreateImplicitOverview(iOverview);
    2558          35 :     if (!poWDS->m_apoOverviews[iOverview])
    2559           0 :         return nullptr;
    2560          35 :     return poWDS->m_apoOverviews[iOverview]->GetRasterBand(nBand);
    2561             : }
    2562             : 
    2563             : /*! @endcond */

Generated by: LCOV version 1.14