LCOV - code coverage report
Current view: top level - frmts/vrt - vrtwarped.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 699 902 77.5 %
Date: 2026-06-17 21:44:33 Functions: 42 49 85.7 %

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

Generated by: LCOV version 1.14