LCOV - code coverage report
Current view: top level - apps - gdalbuildvrt_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1002 1157 86.6 %
Date: 2025-06-19 12:30:01 Functions: 27 29 93.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  Command line application to build VRT datasets from raster products
       5             :  *           or content of SHP tile index
       6             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2007-2016, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_api.h"
      15             : #include "ogr_srs_api.h"
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "gdal_utils.h"
      19             : #include "gdal_utils_priv.h"
      20             : #include "gdalargumentparser.h"
      21             : 
      22             : #include <cassert>
      23             : #include <cmath>
      24             : #include <cstdio>
      25             : #include <cstdlib>
      26             : #include <cstring>
      27             : 
      28             : #include <algorithm>
      29             : #include <memory>
      30             : #include <set>
      31             : #include <string>
      32             : #include <vector>
      33             : 
      34             : #include "commonutils.h"
      35             : #include "cpl_conv.h"
      36             : #include "cpl_error.h"
      37             : #include "cpl_float.h"
      38             : #include "cpl_progress.h"
      39             : #include "cpl_string.h"
      40             : #include "cpl_vsi.h"
      41             : #include "cpl_vsi_virtual.h"
      42             : #include "gdal.h"
      43             : #include "gdal_vrt.h"
      44             : #include "gdal_priv.h"
      45             : #include "gdal_proxy.h"
      46             : #include "ogr_api.h"
      47             : #include "ogr_core.h"
      48             : #include "ogr_srs_api.h"
      49             : #include "ogr_spatialref.h"
      50             : #include "ogrsf_frmts.h"
      51             : #include "vrtdataset.h"
      52             : 
      53             : #define GEOTRSFRM_TOPLEFT_X 0
      54             : #define GEOTRSFRM_WE_RES 1
      55             : #define GEOTRSFRM_ROTATION_PARAM1 2
      56             : #define GEOTRSFRM_TOPLEFT_Y 3
      57             : #define GEOTRSFRM_ROTATION_PARAM2 4
      58             : #define GEOTRSFRM_NS_RES 5
      59             : 
      60             : namespace gdal::GDALBuildVRT
      61             : {
      62             : typedef enum
      63             : {
      64             :     LOWEST_RESOLUTION,
      65             :     HIGHEST_RESOLUTION,
      66             :     AVERAGE_RESOLUTION,
      67             :     SAME_RESOLUTION,
      68             :     USER_RESOLUTION,
      69             :     COMMON_RESOLUTION,
      70             : } ResolutionStrategy;
      71             : 
      72             : struct DatasetProperty
      73             : {
      74             :     int isFileOK = FALSE;
      75             :     int nRasterXSize = 0;
      76             :     int nRasterYSize = 0;
      77             :     double adfGeoTransform[6];
      78             :     int nBlockXSize = 0;
      79             :     int nBlockYSize = 0;
      80             :     std::vector<GDALDataType> aeBandType{};
      81             :     std::vector<bool> abHasNoData{};
      82             :     std::vector<double> adfNoDataValues{};
      83             :     std::vector<bool> abHasOffset{};
      84             :     std::vector<double> adfOffset{};
      85             :     std::vector<bool> abHasScale{};
      86             :     std::vector<bool> abHasMaskBand{};
      87             :     std::vector<double> adfScale{};
      88             :     int bHasDatasetMask = 0;
      89             :     bool bLastBandIsAlpha = false;
      90             :     int nMaskBlockXSize = 0;
      91             :     int nMaskBlockYSize = 0;
      92             :     std::vector<int> anOverviewFactors{};
      93             : 
      94        2360 :     DatasetProperty()
      95        2360 :     {
      96        2360 :         adfGeoTransform[0] = 0;
      97        2360 :         adfGeoTransform[1] = 0;
      98        2360 :         adfGeoTransform[2] = 0;
      99        2360 :         adfGeoTransform[3] = 0;
     100        2360 :         adfGeoTransform[4] = 0;
     101        2360 :         adfGeoTransform[5] = 0;
     102        2360 :     }
     103             : };
     104             : 
     105             : struct BandProperty
     106             : {
     107             :     GDALColorInterp colorInterpretation = GCI_Undefined;
     108             :     GDALDataType dataType = GDT_Unknown;
     109             :     std::unique_ptr<GDALColorTable> colorTable{};
     110             :     bool bHasNoData = false;
     111             :     double noDataValue = 0;
     112             :     bool bHasOffset = false;
     113             :     double dfOffset = 0;
     114             :     bool bHasScale = false;
     115             :     double dfScale = 0;
     116             : };
     117             : }  // namespace gdal::GDALBuildVRT
     118             : 
     119             : using namespace gdal::GDALBuildVRT;
     120             : 
     121             : /************************************************************************/
     122             : /*                         GetSrcDstWin()                               */
     123             : /************************************************************************/
     124             : 
     125        2341 : static int GetSrcDstWin(DatasetProperty *psDP, double we_res, double ns_res,
     126             :                         double minX, double minY, double maxX, double maxY,
     127             :                         int nTargetXSize, int nTargetYSize, double *pdfSrcXOff,
     128             :                         double *pdfSrcYOff, double *pdfSrcXSize,
     129             :                         double *pdfSrcYSize, double *pdfDstXOff,
     130             :                         double *pdfDstYOff, double *pdfDstXSize,
     131             :                         double *pdfDstYSize)
     132             : {
     133        2341 :     if (we_res == 0 || ns_res == 0)
     134             :     {
     135             :         // should not happen. to please Coverity
     136           0 :         return FALSE;
     137             :     }
     138             : 
     139             :     /* Check that the destination bounding box intersects the source bounding
     140             :      * box */
     141        2341 :     if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] +
     142        2341 :             psDP->nRasterXSize * psDP->adfGeoTransform[GEOTRSFRM_WE_RES] <=
     143             :         minX)
     144           0 :         return FALSE;
     145        2341 :     if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] >= maxX)
     146           1 :         return FALSE;
     147        2340 :     if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] +
     148        2340 :             psDP->nRasterYSize * psDP->adfGeoTransform[GEOTRSFRM_NS_RES] >=
     149             :         maxY)
     150           0 :         return FALSE;
     151        2340 :     if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] <= minY)
     152           0 :         return FALSE;
     153             : 
     154        2340 :     if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] < minX)
     155             :     {
     156           3 :         *pdfSrcXOff = (minX - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]) /
     157           3 :                       psDP->adfGeoTransform[GEOTRSFRM_WE_RES];
     158           3 :         *pdfDstXOff = 0.0;
     159             :     }
     160             :     else
     161             :     {
     162        2337 :         *pdfSrcXOff = 0.0;
     163        2337 :         *pdfDstXOff =
     164        2337 :             ((psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - minX) / we_res);
     165             :     }
     166        2340 :     if (maxY < psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y])
     167             :     {
     168           5 :         *pdfSrcYOff = (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] - maxY) /
     169           5 :                       -psDP->adfGeoTransform[GEOTRSFRM_NS_RES];
     170           5 :         *pdfDstYOff = 0.0;
     171             :     }
     172             :     else
     173             :     {
     174        2335 :         *pdfSrcYOff = 0.0;
     175        2335 :         *pdfDstYOff =
     176        2335 :             ((maxY - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res);
     177             :     }
     178             : 
     179        2340 :     *pdfSrcXSize = psDP->nRasterXSize;
     180        2340 :     *pdfSrcYSize = psDP->nRasterYSize;
     181        2340 :     if (*pdfSrcXOff > 0)
     182           3 :         *pdfSrcXSize -= *pdfSrcXOff;
     183        2340 :     if (*pdfSrcYOff > 0)
     184           5 :         *pdfSrcYSize -= *pdfSrcYOff;
     185             : 
     186        2340 :     const double dfSrcToDstXSize =
     187        2340 :         psDP->adfGeoTransform[GEOTRSFRM_WE_RES] / we_res;
     188        2340 :     *pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
     189        2340 :     const double dfSrcToDstYSize =
     190        2340 :         psDP->adfGeoTransform[GEOTRSFRM_NS_RES] / ns_res;
     191        2340 :     *pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
     192             : 
     193        2340 :     if (*pdfDstXOff + *pdfDstXSize > nTargetXSize)
     194             :     {
     195           7 :         *pdfDstXSize = nTargetXSize - *pdfDstXOff;
     196           7 :         *pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
     197             :     }
     198             : 
     199        2340 :     if (*pdfDstYOff + *pdfDstYSize > nTargetYSize)
     200             :     {
     201           5 :         *pdfDstYSize = nTargetYSize - *pdfDstYOff;
     202           5 :         *pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
     203             :     }
     204             : 
     205        4680 :     return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
     206        4680 :            *pdfDstYSize > 0;
     207             : }
     208             : 
     209             : /************************************************************************/
     210             : /*                            VRTBuilder                                */
     211             : /************************************************************************/
     212             : 
     213             : class VRTBuilder
     214             : {
     215             :     /* Input parameters */
     216             :     bool bStrict = false;
     217             :     char *pszOutputFilename = nullptr;
     218             :     int nInputFiles = 0;
     219             :     char **ppszInputFilenames = nullptr;
     220             :     int nSrcDSCount = 0;
     221             :     GDALDatasetH *pahSrcDS = nullptr;
     222             :     int nTotalBands = 0;
     223             :     bool bLastBandIsAlpha = false;
     224             :     bool bExplicitBandList = false;
     225             :     int nMaxSelectedBandNo = 0;
     226             :     int nSelectedBands = 0;
     227             :     int *panSelectedBandList = nullptr;
     228             :     ResolutionStrategy resolutionStrategy = AVERAGE_RESOLUTION;
     229             :     int nCountValid = 0;
     230             :     double we_res = 0;
     231             :     double ns_res = 0;
     232             :     int bTargetAlignedPixels = 0;
     233             :     double minX = 0;
     234             :     double minY = 0;
     235             :     double maxX = 0;
     236             :     double maxY = 0;
     237             :     int bSeparate = 0;
     238             :     int bAllowProjectionDifference = 0;
     239             :     int bAddAlpha = 0;
     240             :     int bHideNoData = 0;
     241             :     int nSubdataset = 0;
     242             :     char *pszSrcNoData = nullptr;
     243             :     char *pszVRTNoData = nullptr;
     244             :     char *pszOutputSRS = nullptr;
     245             :     char *pszResampling = nullptr;
     246             :     char **papszOpenOptions = nullptr;
     247             :     bool bUseSrcMaskBand = true;
     248             :     bool bNoDataFromMask = false;
     249             :     double dfMaskValueThreshold = 0;
     250             :     const CPLStringList aosCreateOptions;
     251             :     std::string osPixelFunction{};
     252             :     const CPLStringList aosPixelFunctionArgs;
     253             :     const bool bWriteAbsolutePath;
     254             : 
     255             :     /* Internal variables */
     256             :     char *pszProjectionRef = nullptr;
     257             :     std::vector<BandProperty> asBandProperties{};
     258             :     int bFirst = TRUE;
     259             :     int bHasGeoTransform = 0;
     260             :     int nRasterXSize = 0;
     261             :     int nRasterYSize = 0;
     262             :     std::vector<DatasetProperty> asDatasetProperties{};
     263             :     int bUserExtent = 0;
     264             :     int bAllowSrcNoData = TRUE;
     265             :     double *padfSrcNoData = nullptr;
     266             :     int nSrcNoDataCount = 0;
     267             :     int bAllowVRTNoData = TRUE;
     268             :     double *padfVRTNoData = nullptr;
     269             :     int nVRTNoDataCount = 0;
     270             :     int bHasRunBuild = 0;
     271             :     int bHasDatasetMask = 0;
     272             : 
     273             :     std::string AnalyseRaster(GDALDatasetH hDS,
     274             :                               DatasetProperty *psDatasetProperties);
     275             : 
     276             :     void CreateVRTSeparate(VRTDataset *poVTDS);
     277             :     void CreateVRTNonSeparate(VRTDataset *poVRTDS);
     278             : 
     279             :     CPL_DISALLOW_COPY_ASSIGN(VRTBuilder)
     280             : 
     281             :   public:
     282             :     VRTBuilder(bool bStrictIn, const char *pszOutputFilename, int nInputFiles,
     283             :                const char *const *ppszInputFilenames, GDALDatasetH *pahSrcDSIn,
     284             :                const int *panSelectedBandListIn, int nBandCount,
     285             :                ResolutionStrategy resolutionStrategy, double we_res,
     286             :                double ns_res, int bTargetAlignedPixels, double minX,
     287             :                double minY, double maxX, double maxY, int bSeparate,
     288             :                int bAllowProjectionDifference, int bAddAlpha, int bHideNoData,
     289             :                int nSubdataset, const char *pszSrcNoData,
     290             :                const char *pszVRTNoData, bool bUseSrcMaskBand,
     291             :                bool bNoDataFromMask, double dfMaskValueThreshold,
     292             :                const char *pszOutputSRS, const char *pszResampling,
     293             :                const char *pszPixelFunctionName,
     294             :                const CPLStringList &aosPixelFunctionArgs,
     295             :                const char *const *papszOpenOptionsIn,
     296             :                const CPLStringList &aosCreateOptionsIn,
     297             :                bool bWriteAbsolutePathIn);
     298             : 
     299             :     ~VRTBuilder();
     300             : 
     301             :     std::unique_ptr<GDALDataset> Build(GDALProgressFunc pfnProgress,
     302             :                                        void *pProgressData);
     303             : 
     304             :     std::string m_osProgramName{};
     305             : };
     306             : 
     307             : /************************************************************************/
     308             : /*                          VRTBuilder()                                */
     309             : /************************************************************************/
     310             : 
     311         220 : VRTBuilder::VRTBuilder(
     312             :     bool bStrictIn, const char *pszOutputFilenameIn, int nInputFilesIn,
     313             :     const char *const *ppszInputFilenamesIn, GDALDatasetH *pahSrcDSIn,
     314             :     const int *panSelectedBandListIn, int nBandCount,
     315             :     ResolutionStrategy resolutionStrategyIn, double we_resIn, double ns_resIn,
     316             :     int bTargetAlignedPixelsIn, double minXIn, double minYIn, double maxXIn,
     317             :     double maxYIn, int bSeparateIn, int bAllowProjectionDifferenceIn,
     318             :     int bAddAlphaIn, int bHideNoDataIn, int nSubdatasetIn,
     319             :     const char *pszSrcNoDataIn, const char *pszVRTNoDataIn,
     320             :     bool bUseSrcMaskBandIn, bool bNoDataFromMaskIn,
     321             :     double dfMaskValueThresholdIn, const char *pszOutputSRSIn,
     322             :     const char *pszResamplingIn, const char *pszPixelFunctionIn,
     323             :     const CPLStringList &aosPixelFunctionArgsIn,
     324             :     const char *const *papszOpenOptionsIn,
     325         220 :     const CPLStringList &aosCreateOptionsIn, bool bWriteAbsolutePathIn)
     326             :     : bStrict(bStrictIn), aosCreateOptions(aosCreateOptionsIn),
     327             :       aosPixelFunctionArgs(aosPixelFunctionArgsIn),
     328         220 :       bWriteAbsolutePath(bWriteAbsolutePathIn)
     329             : {
     330         220 :     pszOutputFilename = CPLStrdup(pszOutputFilenameIn);
     331         220 :     nInputFiles = nInputFilesIn;
     332         220 :     papszOpenOptions = CSLDuplicate(const_cast<char **>(papszOpenOptionsIn));
     333             : 
     334         220 :     if (pszPixelFunctionIn != nullptr)
     335             :     {
     336           3 :         osPixelFunction = pszPixelFunctionIn;
     337             :     }
     338             : 
     339         220 :     if (ppszInputFilenamesIn)
     340             :     {
     341         132 :         ppszInputFilenames =
     342         132 :             static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
     343        1353 :         for (int i = 0; i < nInputFiles; i++)
     344             :         {
     345        1221 :             ppszInputFilenames[i] = CPLStrdup(ppszInputFilenamesIn[i]);
     346             :         }
     347             :     }
     348          88 :     else if (pahSrcDSIn)
     349             :     {
     350          88 :         nSrcDSCount = nInputFiles;
     351          88 :         pahSrcDS = static_cast<GDALDatasetH *>(
     352          88 :             CPLMalloc(nInputFiles * sizeof(GDALDatasetH)));
     353          88 :         memcpy(pahSrcDS, pahSrcDSIn, nInputFiles * sizeof(GDALDatasetH));
     354          88 :         ppszInputFilenames =
     355          88 :             static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
     356        1227 :         for (int i = 0; i < nInputFiles; i++)
     357             :         {
     358        2278 :             ppszInputFilenames[i] =
     359        1139 :                 CPLStrdup(GDALGetDescription(pahSrcDSIn[i]));
     360             :         }
     361             :     }
     362             : 
     363         220 :     bExplicitBandList = nBandCount != 0;
     364         220 :     nSelectedBands = nBandCount;
     365         220 :     if (nBandCount)
     366             :     {
     367          19 :         panSelectedBandList =
     368          19 :             static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
     369          19 :         memcpy(panSelectedBandList, panSelectedBandListIn,
     370          19 :                nSelectedBands * sizeof(int));
     371             :     }
     372             : 
     373         220 :     resolutionStrategy = resolutionStrategyIn;
     374         220 :     we_res = we_resIn;
     375         220 :     ns_res = ns_resIn;
     376         220 :     bTargetAlignedPixels = bTargetAlignedPixelsIn;
     377         220 :     minX = minXIn;
     378         220 :     minY = minYIn;
     379         220 :     maxX = maxXIn;
     380         220 :     maxY = maxYIn;
     381         220 :     bSeparate = bSeparateIn;
     382         220 :     bAllowProjectionDifference = bAllowProjectionDifferenceIn;
     383         220 :     bAddAlpha = bAddAlphaIn;
     384         220 :     bHideNoData = bHideNoDataIn;
     385         220 :     nSubdataset = nSubdatasetIn;
     386         220 :     pszSrcNoData = (pszSrcNoDataIn) ? CPLStrdup(pszSrcNoDataIn) : nullptr;
     387         220 :     pszVRTNoData = (pszVRTNoDataIn) ? CPLStrdup(pszVRTNoDataIn) : nullptr;
     388         220 :     pszOutputSRS = (pszOutputSRSIn) ? CPLStrdup(pszOutputSRSIn) : nullptr;
     389         220 :     pszResampling = (pszResamplingIn) ? CPLStrdup(pszResamplingIn) : nullptr;
     390         220 :     bUseSrcMaskBand = bUseSrcMaskBandIn;
     391         220 :     bNoDataFromMask = bNoDataFromMaskIn;
     392         220 :     dfMaskValueThreshold = dfMaskValueThresholdIn;
     393         220 : }
     394             : 
     395             : /************************************************************************/
     396             : /*                         ~VRTBuilder()                                */
     397             : /************************************************************************/
     398             : 
     399         220 : VRTBuilder::~VRTBuilder()
     400             : {
     401         220 :     CPLFree(pszOutputFilename);
     402         220 :     CPLFree(pszSrcNoData);
     403         220 :     CPLFree(pszVRTNoData);
     404         220 :     CPLFree(panSelectedBandList);
     405             : 
     406         220 :     if (ppszInputFilenames)
     407             :     {
     408        2580 :         for (int i = 0; i < nInputFiles; i++)
     409             :         {
     410        2360 :             CPLFree(ppszInputFilenames[i]);
     411             :         }
     412             :     }
     413         220 :     CPLFree(ppszInputFilenames);
     414         220 :     CPLFree(pahSrcDS);
     415             : 
     416         220 :     CPLFree(pszProjectionRef);
     417         220 :     CPLFree(padfSrcNoData);
     418         220 :     CPLFree(padfVRTNoData);
     419         220 :     CPLFree(pszOutputSRS);
     420         220 :     CPLFree(pszResampling);
     421         220 :     CSLDestroy(papszOpenOptions);
     422         220 : }
     423             : 
     424             : /************************************************************************/
     425             : /*                           ProjAreEqual()                             */
     426             : /************************************************************************/
     427             : 
     428        2138 : static int ProjAreEqual(const char *pszWKT1, const char *pszWKT2)
     429             : {
     430        2138 :     if (EQUAL(pszWKT1, pszWKT2))
     431        2136 :         return TRUE;
     432             : 
     433           2 :     OGRSpatialReferenceH hSRS1 = OSRNewSpatialReference(pszWKT1);
     434           2 :     OGRSpatialReferenceH hSRS2 = OSRNewSpatialReference(pszWKT2);
     435           2 :     int bRet = hSRS1 != nullptr && hSRS2 != nullptr && OSRIsSame(hSRS1, hSRS2);
     436           2 :     if (hSRS1)
     437           2 :         OSRDestroySpatialReference(hSRS1);
     438           2 :     if (hSRS2)
     439           2 :         OSRDestroySpatialReference(hSRS2);
     440           2 :     return bRet;
     441             : }
     442             : 
     443             : /************************************************************************/
     444             : /*                         GetProjectionName()                          */
     445             : /************************************************************************/
     446             : 
     447           4 : static CPLString GetProjectionName(const char *pszProjection)
     448             : {
     449           4 :     if (!pszProjection)
     450           0 :         return "(null)";
     451             : 
     452           8 :     OGRSpatialReference oSRS;
     453           4 :     oSRS.SetFromUserInput(pszProjection);
     454           4 :     const char *pszRet = nullptr;
     455           4 :     if (oSRS.IsProjected())
     456           0 :         pszRet = oSRS.GetAttrValue("PROJCS");
     457           4 :     else if (oSRS.IsGeographic())
     458           3 :         pszRet = oSRS.GetAttrValue("GEOGCS");
     459           4 :     return pszRet ? pszRet : "(null)";
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*                           AnalyseRaster()                            */
     464             : /************************************************************************/
     465             : 
     466        2347 : static void checkNoDataValues(const std::vector<BandProperty> &asProperties)
     467             : {
     468        5990 :     for (const auto &oProps : asProperties)
     469             :     {
     470        3697 :         if (oProps.bHasNoData && GDALDataTypeIsInteger(oProps.dataType) &&
     471          54 :             !GDALIsValueExactAs(oProps.noDataValue, oProps.dataType))
     472             :         {
     473           2 :             CPLError(CE_Warning, CPLE_NotSupported,
     474             :                      "Band data type of %s cannot represent the specified "
     475             :                      "NoData value of %g",
     476           2 :                      GDALGetDataTypeName(oProps.dataType), oProps.noDataValue);
     477             :         }
     478             :     }
     479        2347 : }
     480             : 
     481        2358 : std::string VRTBuilder::AnalyseRaster(GDALDatasetH hDS,
     482             :                                       DatasetProperty *psDatasetProperties)
     483             : {
     484        2358 :     GDALDataset *poDS = GDALDataset::FromHandle(hDS);
     485        2358 :     const char *dsFileName = poDS->GetDescription();
     486        2358 :     char **papszMetadata = poDS->GetMetadata("SUBDATASETS");
     487        2358 :     if (CSLCount(papszMetadata) > 0 && poDS->GetRasterCount() == 0)
     488             :     {
     489           0 :         ppszInputFilenames = static_cast<char **>(CPLRealloc(
     490           0 :             ppszInputFilenames,
     491           0 :             sizeof(char *) * (nInputFiles + CSLCount(papszMetadata))));
     492           0 :         if (nSubdataset < 0)
     493             :         {
     494           0 :             int count = 1;
     495             :             char subdatasetNameKey[80];
     496           0 :             snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
     497             :                      "SUBDATASET_%d_NAME", count);
     498           0 :             while (*papszMetadata != nullptr)
     499             :             {
     500           0 :                 if (EQUALN(*papszMetadata, subdatasetNameKey,
     501             :                            strlen(subdatasetNameKey)))
     502             :                 {
     503           0 :                     asDatasetProperties.resize(nInputFiles + 1);
     504           0 :                     ppszInputFilenames[nInputFiles] = CPLStrdup(
     505           0 :                         *papszMetadata + strlen(subdatasetNameKey) + 1);
     506           0 :                     nInputFiles++;
     507           0 :                     count++;
     508           0 :                     snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
     509             :                              "SUBDATASET_%d_NAME", count);
     510             :                 }
     511           0 :                 papszMetadata++;
     512             :             }
     513             :         }
     514             :         else
     515             :         {
     516             :             char subdatasetNameKey[80];
     517             :             const char *pszSubdatasetName;
     518             : 
     519           0 :             snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
     520             :                      "SUBDATASET_%d_NAME", nSubdataset);
     521             :             pszSubdatasetName =
     522           0 :                 CSLFetchNameValue(papszMetadata, subdatasetNameKey);
     523           0 :             if (pszSubdatasetName)
     524             :             {
     525           0 :                 asDatasetProperties.resize(nInputFiles + 1);
     526           0 :                 ppszInputFilenames[nInputFiles] = CPLStrdup(pszSubdatasetName);
     527           0 :                 nInputFiles++;
     528             :             }
     529             :         }
     530           0 :         return "SILENTLY_IGNORE";
     531             :     }
     532             : 
     533        2358 :     const char *proj = poDS->GetProjectionRef();
     534        2358 :     double *padfGeoTransform = psDatasetProperties->adfGeoTransform;
     535        2358 :     int bGotGeoTransform = poDS->GetGeoTransform(padfGeoTransform) == CE_None;
     536        2358 :     if (bSeparate)
     537             :     {
     538          33 :         std::string osProgramName(m_osProgramName);
     539          33 :         if (osProgramName == "gdalbuildvrt")
     540          27 :             osProgramName += " -separate";
     541             : 
     542          33 :         if (bFirst)
     543             :         {
     544          18 :             bHasGeoTransform = bGotGeoTransform;
     545          18 :             if (!bHasGeoTransform)
     546             :             {
     547           1 :                 if (bUserExtent)
     548             :                 {
     549           0 :                     CPLError(CE_Warning, CPLE_NotSupported, "%s",
     550           0 :                              ("User extent ignored by " + osProgramName +
     551             :                               "with ungeoreferenced images.")
     552             :                                  .c_str());
     553             :                 }
     554           1 :                 if (resolutionStrategy == USER_RESOLUTION)
     555             :                 {
     556           0 :                     CPLError(CE_Warning, CPLE_NotSupported, "%s",
     557           0 :                              ("User resolution ignored by " + osProgramName +
     558             :                               " with ungeoreferenced images.")
     559             :                                  .c_str());
     560             :                 }
     561             :             }
     562             :         }
     563          15 :         else if (bHasGeoTransform != bGotGeoTransform)
     564             :         {
     565             :             return osProgramName + " cannot stack ungeoreferenced and "
     566           0 :                                    "georeferenced images.";
     567             :         }
     568          16 :         else if (!bHasGeoTransform && (nRasterXSize != poDS->GetRasterXSize() ||
     569           1 :                                        nRasterYSize != poDS->GetRasterYSize()))
     570             :         {
     571             :             return osProgramName + " cannot stack ungeoreferenced images "
     572           0 :                                    "that have not the same dimensions.";
     573             :         }
     574             :     }
     575             :     else
     576             :     {
     577        2325 :         if (!bGotGeoTransform)
     578             :         {
     579           0 :             return m_osProgramName + " does not support ungeoreferenced image.";
     580             :         }
     581        2325 :         bHasGeoTransform = TRUE;
     582             :     }
     583             : 
     584        2358 :     if (bGotGeoTransform)
     585             :     {
     586        2356 :         if (padfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 ||
     587        2356 :             padfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0)
     588             :         {
     589           0 :             return m_osProgramName +
     590           0 :                    " does not support rotated geo transforms.";
     591             :         }
     592        2356 :         if (padfGeoTransform[GEOTRSFRM_NS_RES] >= 0)
     593             :         {
     594           0 :             return m_osProgramName +
     595           0 :                    " does not support positive NS resolution.";
     596             :         }
     597             :     }
     598             : 
     599        2358 :     psDatasetProperties->nRasterXSize = poDS->GetRasterXSize();
     600        2358 :     psDatasetProperties->nRasterYSize = poDS->GetRasterYSize();
     601        2358 :     if (bFirst && bSeparate && !bGotGeoTransform)
     602             :     {
     603           1 :         nRasterXSize = poDS->GetRasterXSize();
     604           1 :         nRasterYSize = poDS->GetRasterYSize();
     605             :     }
     606             : 
     607        2358 :     double ds_minX = padfGeoTransform[GEOTRSFRM_TOPLEFT_X];
     608        2358 :     double ds_maxY = padfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
     609             :     double ds_maxX =
     610        2358 :         ds_minX + GDALGetRasterXSize(hDS) * padfGeoTransform[GEOTRSFRM_WE_RES];
     611             :     double ds_minY =
     612        2358 :         ds_maxY + GDALGetRasterYSize(hDS) * padfGeoTransform[GEOTRSFRM_NS_RES];
     613             : 
     614        2358 :     int _nBands = GDALGetRasterCount(hDS);
     615        2358 :     if (_nBands == 0)
     616             :     {
     617           0 :         return "Dataset has no bands";
     618             :     }
     619        2367 :     if (bNoDataFromMask &&
     620           9 :         poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
     621           3 :         _nBands--;
     622             : 
     623        2358 :     GDALRasterBand *poFirstBand = poDS->GetRasterBand(1);
     624        2358 :     poFirstBand->GetBlockSize(&psDatasetProperties->nBlockXSize,
     625             :                               &psDatasetProperties->nBlockYSize);
     626             : 
     627             :     /* For the -separate case */
     628        2358 :     psDatasetProperties->aeBandType.resize(_nBands);
     629             : 
     630        2358 :     psDatasetProperties->adfNoDataValues.resize(_nBands);
     631        2358 :     psDatasetProperties->abHasNoData.resize(_nBands);
     632             : 
     633        2358 :     psDatasetProperties->adfOffset.resize(_nBands);
     634        2358 :     psDatasetProperties->abHasOffset.resize(_nBands);
     635             : 
     636        2358 :     psDatasetProperties->adfScale.resize(_nBands);
     637        2358 :     psDatasetProperties->abHasScale.resize(_nBands);
     638             : 
     639        2358 :     psDatasetProperties->abHasMaskBand.resize(_nBands);
     640             : 
     641        2358 :     psDatasetProperties->bHasDatasetMask =
     642        2358 :         poFirstBand->GetMaskFlags() == GMF_PER_DATASET;
     643        2358 :     if (psDatasetProperties->bHasDatasetMask)
     644          16 :         bHasDatasetMask = TRUE;
     645        2358 :     poFirstBand->GetMaskBand()->GetBlockSize(
     646             :         &psDatasetProperties->nMaskBlockXSize,
     647             :         &psDatasetProperties->nMaskBlockYSize);
     648             : 
     649        2358 :     psDatasetProperties->bLastBandIsAlpha = false;
     650        2358 :     if (poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
     651          14 :         psDatasetProperties->bLastBandIsAlpha = true;
     652             : 
     653             :     // Collect overview factors. We only handle power-of-two situations for now
     654        2358 :     const int nOverviews = poFirstBand->GetOverviewCount();
     655        2358 :     int nExpectedOvFactor = 2;
     656        2370 :     for (int j = 0; j < nOverviews; j++)
     657             :     {
     658          23 :         GDALRasterBand *poOverview = poFirstBand->GetOverview(j);
     659          23 :         if (!poOverview)
     660           0 :             continue;
     661          23 :         if (poOverview->GetXSize() < 128 && poOverview->GetYSize() < 128)
     662             :         {
     663          11 :             break;
     664             :         }
     665             : 
     666          12 :         const int nOvFactor = GDALComputeOvFactor(
     667             :             poOverview->GetXSize(), poFirstBand->GetXSize(),
     668          12 :             poOverview->GetYSize(), poFirstBand->GetYSize());
     669             : 
     670          12 :         if (nOvFactor != nExpectedOvFactor)
     671           0 :             break;
     672             : 
     673          12 :         psDatasetProperties->anOverviewFactors.push_back(nOvFactor);
     674          12 :         nExpectedOvFactor *= 2;
     675             :     }
     676             : 
     677        5885 :     for (int j = 0; j < _nBands; j++)
     678             :     {
     679        3527 :         GDALRasterBand *poBand = poDS->GetRasterBand(j + 1);
     680             : 
     681        3527 :         psDatasetProperties->aeBandType[j] = poBand->GetRasterDataType();
     682             : 
     683        3527 :         if (!bSeparate && nSrcNoDataCount > 0)
     684             :         {
     685           4 :             psDatasetProperties->abHasNoData[j] = true;
     686           4 :             if (j < nSrcNoDataCount)
     687           4 :                 psDatasetProperties->adfNoDataValues[j] = padfSrcNoData[j];
     688             :             else
     689           0 :                 psDatasetProperties->adfNoDataValues[j] =
     690           0 :                     padfSrcNoData[nSrcNoDataCount - 1];
     691             :         }
     692             :         else
     693             :         {
     694        3523 :             int bHasNoData = false;
     695        7046 :             psDatasetProperties->adfNoDataValues[j] =
     696        3523 :                 poBand->GetNoDataValue(&bHasNoData);
     697        3523 :             psDatasetProperties->abHasNoData[j] = bHasNoData != 0;
     698             :         }
     699             : 
     700        3527 :         int bHasOffset = false;
     701        3527 :         psDatasetProperties->adfOffset[j] = poBand->GetOffset(&bHasOffset);
     702        7054 :         psDatasetProperties->abHasOffset[j] =
     703        3527 :             bHasOffset != 0 && psDatasetProperties->adfOffset[j] != 0.0;
     704             : 
     705        3527 :         int bHasScale = false;
     706        3527 :         psDatasetProperties->adfScale[j] = poBand->GetScale(&bHasScale);
     707        7054 :         psDatasetProperties->abHasScale[j] =
     708        3527 :             bHasScale != 0 && psDatasetProperties->adfScale[j] != 1.0;
     709             : 
     710        3527 :         const int nMaskFlags = poBand->GetMaskFlags();
     711        3527 :         psDatasetProperties->abHasMaskBand[j] =
     712        6995 :             (nMaskFlags != GMF_ALL_VALID && nMaskFlags != GMF_NODATA) ||
     713        6995 :             poBand->GetColorInterpretation() == GCI_AlphaBand;
     714             :     }
     715             : 
     716        2358 :     if (bSeparate)
     717             :     {
     718          39 :         for (int j = 0; j < nSelectedBands; j++)
     719             :         {
     720           7 :             if (panSelectedBandList[j] > _nBands)
     721             :             {
     722             :                 return CPLSPrintf("%s has %d bands, but %d is requested",
     723           1 :                                   dsFileName, _nBands, panSelectedBandList[j]);
     724             :             }
     725             :         }
     726             :     }
     727             : 
     728        2357 :     if (bFirst)
     729             :     {
     730         219 :         nTotalBands = _nBands;
     731         219 :         if (bAddAlpha && psDatasetProperties->bLastBandIsAlpha)
     732             :         {
     733           4 :             bLastBandIsAlpha = true;
     734           4 :             nTotalBands--;
     735             :         }
     736             : 
     737         219 :         if (proj)
     738         219 :             pszProjectionRef = CPLStrdup(proj);
     739         219 :         if (!bUserExtent)
     740             :         {
     741         206 :             minX = ds_minX;
     742         206 :             minY = ds_minY;
     743         206 :             maxX = ds_maxX;
     744         206 :             maxY = ds_maxY;
     745             :         }
     746             : 
     747         219 :         if (!bSeparate)
     748             :         {
     749             :             // if not provided an explicit band list, take the one of the first
     750             :             // dataset
     751         202 :             if (nSelectedBands == 0)
     752             :             {
     753         185 :                 nSelectedBands = nTotalBands;
     754         185 :                 CPLFree(panSelectedBandList);
     755         185 :                 panSelectedBandList =
     756         185 :                     static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
     757         433 :                 for (int j = 0; j < nSelectedBands; j++)
     758             :                 {
     759         248 :                     panSelectedBandList[j] = j + 1;
     760             :                 }
     761             :             }
     762         679 :             for (int j = 0; j < nSelectedBands; j++)
     763             :             {
     764         477 :                 nMaxSelectedBandNo =
     765         477 :                     std::max(nMaxSelectedBandNo, panSelectedBandList[j]);
     766             :             }
     767             : 
     768         202 :             asBandProperties.resize(nSelectedBands);
     769         678 :             for (int j = 0; j < nSelectedBands; j++)
     770             :             {
     771         477 :                 const int nSelBand = panSelectedBandList[j];
     772         477 :                 if (nSelBand <= 0 || nSelBand > nTotalBands)
     773             :                 {
     774           1 :                     return CPLSPrintf("Invalid band number: %d", nSelBand);
     775             :                 }
     776         476 :                 GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
     777         952 :                 asBandProperties[j].colorInterpretation =
     778         476 :                     poBand->GetColorInterpretation();
     779         476 :                 asBandProperties[j].dataType = poBand->GetRasterDataType();
     780         476 :                 if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
     781             :                 {
     782           2 :                     auto colorTable = poBand->GetColorTable();
     783           2 :                     if (colorTable)
     784             :                     {
     785           2 :                         asBandProperties[j].colorTable.reset(
     786             :                             colorTable->Clone());
     787             :                     }
     788             :                 }
     789             :                 else
     790         474 :                     asBandProperties[j].colorTable = nullptr;
     791             : 
     792         476 :                 if (nVRTNoDataCount > 0)
     793             :                 {
     794          26 :                     asBandProperties[j].bHasNoData = true;
     795          26 :                     if (j < nVRTNoDataCount)
     796          20 :                         asBandProperties[j].noDataValue = padfVRTNoData[j];
     797             :                     else
     798           6 :                         asBandProperties[j].noDataValue =
     799           6 :                             padfVRTNoData[nVRTNoDataCount - 1];
     800             :                 }
     801             :                 else
     802             :                 {
     803         450 :                     int bHasNoData = false;
     804         900 :                     asBandProperties[j].noDataValue =
     805         450 :                         poBand->GetNoDataValue(&bHasNoData);
     806         450 :                     asBandProperties[j].bHasNoData = bHasNoData != 0;
     807             :                 }
     808             : 
     809         476 :                 int bHasOffset = false;
     810         476 :                 asBandProperties[j].dfOffset = poBand->GetOffset(&bHasOffset);
     811         476 :                 asBandProperties[j].bHasOffset =
     812         476 :                     bHasOffset != 0 && asBandProperties[j].dfOffset != 0.0;
     813             : 
     814         476 :                 int bHasScale = false;
     815         476 :                 asBandProperties[j].dfScale = poBand->GetScale(&bHasScale);
     816         476 :                 asBandProperties[j].bHasScale =
     817         476 :                     bHasScale != 0 && asBandProperties[j].dfScale != 1.0;
     818             :             }
     819             :         }
     820             :     }
     821             :     else
     822             :     {
     823        2138 :         if ((proj != nullptr && pszProjectionRef == nullptr) ||
     824        6414 :             (proj == nullptr && pszProjectionRef != nullptr) ||
     825        2138 :             (proj != nullptr && pszProjectionRef != nullptr &&
     826        2138 :              ProjAreEqual(proj, pszProjectionRef) == FALSE))
     827             :         {
     828           2 :             if (!bAllowProjectionDifference)
     829             :             {
     830           4 :                 CPLString osExpected = GetProjectionName(pszProjectionRef);
     831           4 :                 CPLString osGot = GetProjectionName(proj);
     832           2 :                 return m_osProgramName +
     833             :                        CPLSPrintf(" does not support heterogeneous "
     834             :                                   "projection: expected %s, got %s.",
     835           2 :                                   osExpected.c_str(), osGot.c_str());
     836             :             }
     837             :         }
     838        2136 :         if (!bSeparate)
     839             :         {
     840        2121 :             if (!bExplicitBandList && _nBands != nTotalBands)
     841             :             {
     842           9 :                 if (bAddAlpha && _nBands == nTotalBands + 1 &&
     843           4 :                     psDatasetProperties->bLastBandIsAlpha)
     844             :                 {
     845           4 :                     bLastBandIsAlpha = true;
     846             :                 }
     847             :                 else
     848             :                 {
     849           5 :                     return m_osProgramName +
     850             :                            CPLSPrintf(" does not support heterogeneous band "
     851             :                                       "numbers: expected %d, got %d.",
     852           5 :                                       nTotalBands, _nBands);
     853             :                 }
     854             :             }
     855        2112 :             else if (bExplicitBandList && _nBands < nMaxSelectedBandNo)
     856             :             {
     857           0 :                 return m_osProgramName +
     858             :                        CPLSPrintf(" does not support heterogeneous band "
     859             :                                   "numbers: expected at least %d, got %d.",
     860           0 :                                   nMaxSelectedBandNo, _nBands);
     861             :             }
     862             : 
     863        5285 :             for (int j = 0; j < nSelectedBands; j++)
     864             :             {
     865        3169 :                 const int nSelBand = panSelectedBandList[j];
     866        3169 :                 CPLAssert(nSelBand >= 1 && nSelBand <= _nBands);
     867        3169 :                 GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
     868        3169 :                 if (asBandProperties[j].colorInterpretation !=
     869        3169 :                     poBand->GetColorInterpretation())
     870             :                 {
     871           0 :                     return m_osProgramName +
     872             :                            CPLSPrintf(
     873             :                                " does not support heterogeneous "
     874             :                                "band color interpretation: expected %s, got "
     875             :                                "%s.",
     876             :                                GDALGetColorInterpretationName(
     877           0 :                                    asBandProperties[j].colorInterpretation),
     878             :                                GDALGetColorInterpretationName(
     879           0 :                                    poBand->GetColorInterpretation()));
     880             :                 }
     881        3169 :                 if (asBandProperties[j].dataType != poBand->GetRasterDataType())
     882             :                 {
     883           0 :                     return m_osProgramName +
     884             :                            CPLSPrintf(" does not support heterogeneous "
     885             :                                       "band data type: expected %s, got %s.",
     886             :                                       GDALGetDataTypeName(
     887           0 :                                           asBandProperties[j].dataType),
     888             :                                       GDALGetDataTypeName(
     889           0 :                                           poBand->GetRasterDataType()));
     890             :                 }
     891        3169 :                 if (asBandProperties[j].colorTable)
     892             :                 {
     893           2 :                     const GDALColorTable *colorTable = poBand->GetColorTable();
     894             :                     int nRefColorEntryCount =
     895           2 :                         asBandProperties[j].colorTable->GetColorEntryCount();
     896           4 :                     if (colorTable == nullptr ||
     897           2 :                         colorTable->GetColorEntryCount() != nRefColorEntryCount)
     898             :                     {
     899           0 :                         return m_osProgramName +
     900             :                                " does not support rasters with "
     901             :                                "different color tables (different number of "
     902           0 :                                "color table entries)";
     903             :                     }
     904             : 
     905             :                     /* Check that the palette are the same too */
     906             :                     /* We just warn and still process the file. It is not a
     907             :                      * technical no-go, but the user */
     908             :                     /* should check that the end result is OK for him. */
     909         260 :                     for (int i = 0; i < nRefColorEntryCount; i++)
     910             :                     {
     911             :                         const GDALColorEntry *psEntry =
     912         258 :                             colorTable->GetColorEntry(i);
     913             :                         const GDALColorEntry *psEntryRef =
     914         258 :                             asBandProperties[j].colorTable->GetColorEntry(i);
     915         258 :                         if (psEntry->c1 != psEntryRef->c1 ||
     916         258 :                             psEntry->c2 != psEntryRef->c2 ||
     917         258 :                             psEntry->c3 != psEntryRef->c3 ||
     918         258 :                             psEntry->c4 != psEntryRef->c4)
     919             :                         {
     920             :                             static int bFirstWarningPCT = TRUE;
     921           0 :                             if (bFirstWarningPCT)
     922           0 :                                 CPLError(
     923             :                                     CE_Warning, CPLE_NotSupported,
     924             :                                     "%s has different values than the first "
     925             :                                     "raster for some entries in the color "
     926             :                                     "table.\n"
     927             :                                     "The end result might produce weird "
     928             :                                     "colors.\n"
     929             :                                     "You're advised to pre-process your "
     930             :                                     "rasters with other tools, such as "
     931             :                                     "pct2rgb.py or gdal_translate -expand RGB\n"
     932             :                                     "to operate %s on RGB rasters "
     933             :                                     "instead",
     934             :                                     dsFileName, m_osProgramName.c_str());
     935             :                             else
     936           0 :                                 CPLError(CE_Warning, CPLE_NotSupported,
     937             :                                          "%s has different values than the "
     938             :                                          "first raster for some entries in the "
     939             :                                          "color table.",
     940             :                                          dsFileName);
     941           0 :                             bFirstWarningPCT = FALSE;
     942           0 :                             break;
     943             :                         }
     944             :                     }
     945             :                 }
     946             : 
     947        3169 :                 if (psDatasetProperties->abHasOffset[j] !=
     948        6338 :                         asBandProperties[j].bHasOffset ||
     949        3169 :                     (asBandProperties[j].bHasOffset &&
     950           0 :                      psDatasetProperties->adfOffset[j] !=
     951           0 :                          asBandProperties[j].dfOffset))
     952             :                 {
     953           0 :                     return m_osProgramName +
     954             :                            CPLSPrintf(
     955             :                                " does not support heterogeneous "
     956             :                                "band offset: expected (%d,%f), got (%d,%f).",
     957           0 :                                static_cast<int>(asBandProperties[j].bHasOffset),
     958           0 :                                asBandProperties[j].dfOffset,
     959             :                                static_cast<int>(
     960           0 :                                    psDatasetProperties->abHasOffset[j]),
     961           0 :                                psDatasetProperties->adfOffset[j]);
     962             :                 }
     963             : 
     964        3169 :                 if (psDatasetProperties->abHasScale[j] !=
     965        6338 :                         asBandProperties[j].bHasScale ||
     966        3169 :                     (asBandProperties[j].bHasScale &&
     967           0 :                      psDatasetProperties->adfScale[j] !=
     968           0 :                          asBandProperties[j].dfScale))
     969             :                 {
     970           0 :                     return m_osProgramName +
     971             :                            CPLSPrintf(
     972             :                                " does not support heterogeneous "
     973             :                                "band scale: expected (%d,%f), got (%d,%f).",
     974           0 :                                static_cast<int>(asBandProperties[j].bHasScale),
     975           0 :                                asBandProperties[j].dfScale,
     976             :                                static_cast<int>(
     977           0 :                                    psDatasetProperties->abHasScale[j]),
     978           0 :                                psDatasetProperties->adfScale[j]);
     979             :                 }
     980             :             }
     981             :         }
     982        2131 :         if (!bUserExtent)
     983             :         {
     984        2128 :             if (ds_minX < minX)
     985           4 :                 minX = ds_minX;
     986        2128 :             if (ds_minY < minY)
     987          27 :                 minY = ds_minY;
     988        2128 :             if (ds_maxX > maxX)
     989         693 :                 maxX = ds_maxX;
     990        2128 :             if (ds_maxY > maxY)
     991          34 :                 maxY = ds_maxY;
     992             :         }
     993             :     }
     994             : 
     995        2349 :     if (resolutionStrategy == AVERAGE_RESOLUTION)
     996             :     {
     997        2282 :         ++nCountValid;
     998             :         {
     999        2282 :             const double dfDelta = padfGeoTransform[GEOTRSFRM_WE_RES] - we_res;
    1000        2282 :             we_res += dfDelta / nCountValid;
    1001             :         }
    1002             :         {
    1003        2282 :             const double dfDelta = padfGeoTransform[GEOTRSFRM_NS_RES] - ns_res;
    1004        2282 :             ns_res += dfDelta / nCountValid;
    1005             :         }
    1006             :     }
    1007          67 :     else if (resolutionStrategy == SAME_RESOLUTION)
    1008             :     {
    1009          30 :         if (bFirst)
    1010             :         {
    1011          24 :             we_res = padfGeoTransform[GEOTRSFRM_WE_RES];
    1012          24 :             ns_res = padfGeoTransform[GEOTRSFRM_NS_RES];
    1013             :         }
    1014           6 :         else if (we_res != padfGeoTransform[GEOTRSFRM_WE_RES] ||
    1015           5 :                  ns_res != padfGeoTransform[GEOTRSFRM_NS_RES])
    1016             :         {
    1017             :             return CPLSPrintf("Dataset %s has resolution %f x %f, whereas "
    1018             :                               "previous sources have resolution %f x %f",
    1019           1 :                               dsFileName, padfGeoTransform[GEOTRSFRM_WE_RES],
    1020           1 :                               padfGeoTransform[GEOTRSFRM_NS_RES], we_res,
    1021           1 :                               ns_res);
    1022             :         }
    1023             :     }
    1024          37 :     else if (resolutionStrategy != USER_RESOLUTION)
    1025             :     {
    1026          24 :         if (bFirst)
    1027             :         {
    1028          12 :             we_res = padfGeoTransform[GEOTRSFRM_WE_RES];
    1029          12 :             ns_res = padfGeoTransform[GEOTRSFRM_NS_RES];
    1030             :         }
    1031          12 :         else if (resolutionStrategy == HIGHEST_RESOLUTION)
    1032             :         {
    1033           1 :             we_res = std::min(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
    1034             :             // ns_res is negative, the highest resolution is the max value.
    1035           1 :             ns_res = std::max(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
    1036             :         }
    1037          11 :         else if (resolutionStrategy == COMMON_RESOLUTION)
    1038             :         {
    1039          20 :             we_res = CPLGreatestCommonDivisor(
    1040          10 :                 we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
    1041          10 :             if (we_res == 0)
    1042             :             {
    1043           1 :                 return "Failed to get common resolution";
    1044             :             }
    1045          18 :             ns_res = CPLGreatestCommonDivisor(
    1046           9 :                 ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
    1047           9 :             if (ns_res == 0)
    1048             :             {
    1049           0 :                 return "Failed to get common resolution";
    1050             :             }
    1051             :         }
    1052             :         else
    1053             :         {
    1054           1 :             we_res = std::max(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
    1055             :             // ns_res is negative, the lowest resolution is the min value.
    1056           1 :             ns_res = std::min(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
    1057             :         }
    1058             :     }
    1059             : 
    1060        2347 :     checkNoDataValues(asBandProperties);
    1061             : 
    1062        2347 :     return "";
    1063             : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                         WriteAbsolutePath()                          */
    1067             : /************************************************************************/
    1068             : 
    1069           7 : static void WriteAbsolutePath(VRTSimpleSource *poSource, const char *dsFileName)
    1070             : {
    1071           7 :     if (dsFileName[0])
    1072             :     {
    1073           7 :         if (CPLIsFilenameRelative(dsFileName))
    1074             :         {
    1075             :             VSIStatBufL sStat;
    1076           2 :             if (VSIStatL(dsFileName, &sStat) == 0)
    1077             :             {
    1078           2 :                 if (char *pszCurDir = CPLGetCurrentDir())
    1079             :                 {
    1080           2 :                     poSource->SetSourceDatasetName(
    1081           4 :                         CPLFormFilenameSafe(pszCurDir, dsFileName, nullptr)
    1082             :                             .c_str(),
    1083             :                         false);
    1084           2 :                     CPLFree(pszCurDir);
    1085             :                 }
    1086             :             }
    1087             :         }
    1088             :         else
    1089             :         {
    1090           5 :             poSource->SetSourceDatasetName(dsFileName, false);
    1091             :         }
    1092             :     }
    1093           7 : }
    1094             : 
    1095             : /************************************************************************/
    1096             : /*                         CreateVRTSeparate()                          */
    1097             : /************************************************************************/
    1098             : 
    1099          17 : void VRTBuilder::CreateVRTSeparate(VRTDataset *poVRTDS)
    1100             : {
    1101          17 :     int iBand = 1;
    1102          49 :     for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
    1103             :     {
    1104          32 :         DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
    1105             : 
    1106          32 :         if (psDatasetProperties->isFileOK == FALSE)
    1107           0 :             continue;
    1108             : 
    1109          32 :         const char *dsFileName = ppszInputFilenames[i];
    1110             : 
    1111             :         double dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
    1112             :             dfDstYOff, dfDstXSize, dfDstYSize;
    1113          32 :         if (bHasGeoTransform)
    1114             :         {
    1115          30 :             if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY,
    1116             :                               maxX, maxY, nRasterXSize, nRasterYSize,
    1117             :                               &dfSrcXOff, &dfSrcYOff, &dfSrcXSize, &dfSrcYSize,
    1118             :                               &dfDstXOff, &dfDstYOff, &dfDstXSize, &dfDstYSize))
    1119             :             {
    1120           0 :                 CPLDebug("BuildVRT",
    1121             :                          "Skipping %s as not intersecting area of interest",
    1122             :                          dsFileName);
    1123           0 :                 continue;
    1124             :             }
    1125             :         }
    1126             :         else
    1127             :         {
    1128           2 :             dfSrcXOff = dfSrcYOff = dfDstXOff = dfDstYOff = 0;
    1129           2 :             dfSrcXSize = dfDstXSize = nRasterXSize;
    1130           2 :             dfSrcYSize = dfDstYSize = nRasterYSize;
    1131             :         }
    1132             : 
    1133             :         GDALDatasetH hSourceDS;
    1134          32 :         bool bDropRef = false;
    1135          84 :         if (nSrcDSCount == nInputFiles &&
    1136          52 :             GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
    1137          20 :             (dsFileName[0] == '\0' ||  // could be a unnamed VRT file
    1138           2 :              EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
    1139             :                    "MEM")))
    1140             :         {
    1141          20 :             hSourceDS = pahSrcDS[i];
    1142             :         }
    1143             :         else
    1144             :         {
    1145          12 :             bDropRef = true;
    1146          24 :             GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
    1147             :                 dsFileName, psDatasetProperties->nRasterXSize,
    1148             :                 psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
    1149          12 :                 pszProjectionRef, psDatasetProperties->adfGeoTransform);
    1150          12 :             hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
    1151          12 :             reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
    1152          12 :                 papszOpenOptions);
    1153             : 
    1154          27 :             for (int jBand = 0;
    1155          27 :                  jBand <
    1156          27 :                  static_cast<int>(psDatasetProperties->aeBandType.size());
    1157             :                  ++jBand)
    1158             :             {
    1159          15 :                 GDALProxyPoolDatasetAddSrcBandDescription(
    1160          15 :                     hProxyDS, psDatasetProperties->aeBandType[jBand],
    1161             :                     psDatasetProperties->nBlockXSize,
    1162             :                     psDatasetProperties->nBlockYSize);
    1163             :             }
    1164             :         }
    1165             : 
    1166             :         const int nBandsToIter =
    1167          32 :             nSelectedBands > 0
    1168          62 :                 ? nSelectedBands
    1169          30 :                 : static_cast<int>(psDatasetProperties->aeBandType.size());
    1170          72 :         for (int iBandToIter = 0; iBandToIter < nBandsToIter; ++iBandToIter)
    1171             :         {
    1172             :             // 0-based
    1173          80 :             const int nSrcBandIdx = nSelectedBands > 0
    1174          40 :                                         ? panSelectedBandList[iBandToIter] - 1
    1175             :                                         : iBandToIter;
    1176          40 :             assert(nSrcBandIdx >= 0);
    1177          40 :             poVRTDS->AddBand(psDatasetProperties->aeBandType[nSrcBandIdx],
    1178          40 :                              nullptr);
    1179             : 
    1180             :             VRTSourcedRasterBand *poVRTBand =
    1181             :                 static_cast<VRTSourcedRasterBand *>(
    1182          40 :                     poVRTDS->GetRasterBand(iBand));
    1183             : 
    1184          40 :             if (bHideNoData)
    1185           0 :                 poVRTBand->SetMetadataItem("HideNoDataValue", "1", nullptr);
    1186             : 
    1187          40 :             if (bAllowVRTNoData)
    1188             :             {
    1189          38 :                 if (nVRTNoDataCount > 0)
    1190             :                 {
    1191           4 :                     if (iBand - 1 < nVRTNoDataCount)
    1192           4 :                         poVRTBand->SetNoDataValue(padfVRTNoData[iBand - 1]);
    1193             :                     else
    1194           0 :                         poVRTBand->SetNoDataValue(
    1195           0 :                             padfVRTNoData[nVRTNoDataCount - 1]);
    1196             :                 }
    1197          34 :                 else if (psDatasetProperties->abHasNoData[nSrcBandIdx])
    1198             :                 {
    1199           2 :                     poVRTBand->SetNoDataValue(
    1200           2 :                         psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
    1201             :                 }
    1202             :             }
    1203             : 
    1204             :             VRTSimpleSource *poSimpleSource;
    1205          78 :             if (bAllowSrcNoData &&
    1206          72 :                 (nSrcNoDataCount > 0 ||
    1207          74 :                  psDatasetProperties->abHasNoData[nSrcBandIdx]))
    1208             :             {
    1209           6 :                 auto poComplexSource = new VRTComplexSource();
    1210           6 :                 poSimpleSource = poComplexSource;
    1211           6 :                 if (nSrcNoDataCount > 0)
    1212             :                 {
    1213           4 :                     if (iBand - 1 < nSrcNoDataCount)
    1214           4 :                         poComplexSource->SetNoDataValue(
    1215           4 :                             padfSrcNoData[iBand - 1]);
    1216             :                     else
    1217           0 :                         poComplexSource->SetNoDataValue(
    1218           0 :                             padfSrcNoData[nSrcNoDataCount - 1]);
    1219             :                 }
    1220             :                 else /* if (psDatasetProperties->abHasNoData[nSrcBandIdx]) */
    1221             :                 {
    1222           2 :                     poComplexSource->SetNoDataValue(
    1223           2 :                         psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
    1224             :                 }
    1225             :             }
    1226          68 :             else if (bUseSrcMaskBand &&
    1227          68 :                      psDatasetProperties->abHasMaskBand[nSrcBandIdx])
    1228             :             {
    1229           1 :                 auto poSource = new VRTComplexSource();
    1230           1 :                 poSource->SetUseMaskBand(true);
    1231           1 :                 poSimpleSource = poSource;
    1232             :             }
    1233             :             else
    1234          33 :                 poSimpleSource = new VRTSimpleSource();
    1235             : 
    1236          40 :             if (pszResampling)
    1237           0 :                 poSimpleSource->SetResampling(pszResampling);
    1238          80 :             poVRTBand->ConfigureSource(
    1239             :                 poSimpleSource,
    1240             :                 static_cast<GDALRasterBand *>(
    1241          40 :                     GDALGetRasterBand(hSourceDS, nSrcBandIdx + 1)),
    1242             :                 FALSE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
    1243             :                 dfDstYOff, dfDstXSize, dfDstYSize);
    1244             : 
    1245          40 :             if (bWriteAbsolutePath)
    1246           3 :                 WriteAbsolutePath(poSimpleSource, dsFileName);
    1247             : 
    1248          40 :             if (psDatasetProperties->abHasOffset[nSrcBandIdx])
    1249           0 :                 poVRTBand->SetOffset(
    1250           0 :                     psDatasetProperties->adfOffset[nSrcBandIdx]);
    1251             : 
    1252          40 :             if (psDatasetProperties->abHasScale[nSrcBandIdx])
    1253           0 :                 poVRTBand->SetScale(psDatasetProperties->adfScale[nSrcBandIdx]);
    1254             : 
    1255          40 :             poVRTBand->AddSource(poSimpleSource);
    1256             : 
    1257          40 :             iBand++;
    1258             :         }
    1259             : 
    1260          32 :         if (bDropRef)
    1261             :         {
    1262          12 :             GDALDereferenceDataset(hSourceDS);
    1263             :         }
    1264             :     }
    1265          17 : }
    1266             : 
    1267             : /************************************************************************/
    1268             : /*                       CreateVRTNonSeparate()                         */
    1269             : /************************************************************************/
    1270             : 
    1271         197 : void VRTBuilder::CreateVRTNonSeparate(VRTDataset *poVRTDS)
    1272             : {
    1273         394 :     CPLStringList aosOptions;
    1274             : 
    1275         197 :     if (!osPixelFunction.empty())
    1276             :     {
    1277           3 :         aosOptions.AddNameValue("subclass", "VRTDerivedRasterBand");
    1278           3 :         aosOptions.AddNameValue("PixelFunctionType", osPixelFunction.c_str());
    1279           3 :         aosOptions.AddNameValue("SkipNonContributingSources", "1");
    1280           6 :         CPLString osName;
    1281           2 :         for (const auto &[pszKey, pszValue] :
    1282           5 :              cpl::IterateNameValue(aosPixelFunctionArgs))
    1283             :         {
    1284           1 :             osName.Printf("_PIXELFN_ARG_%s", pszKey);
    1285           1 :             aosOptions.AddNameValue(osName.c_str(), pszValue);
    1286             :         }
    1287             :     }
    1288             : 
    1289         669 :     for (int j = 0; j < nSelectedBands; j++)
    1290             :     {
    1291         472 :         const char *pszSourceTransferType = "Float64";
    1292         943 :         if (osPixelFunction == "mean" || osPixelFunction == "min" ||
    1293         471 :             osPixelFunction == "max")
    1294             :         {
    1295             :             pszSourceTransferType =
    1296           1 :                 GDALGetDataTypeName(asBandProperties[j].dataType);
    1297             :         }
    1298         472 :         aosOptions.AddNameValue("SourceTransferType", pszSourceTransferType);
    1299             : 
    1300         472 :         poVRTDS->AddBand(asBandProperties[j].dataType, aosOptions.List());
    1301         472 :         GDALRasterBand *poBand = poVRTDS->GetRasterBand(j + 1);
    1302         472 :         poBand->SetColorInterpretation(asBandProperties[j].colorInterpretation);
    1303         472 :         if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
    1304             :         {
    1305           2 :             poBand->SetColorTable(asBandProperties[j].colorTable.get());
    1306             :         }
    1307         472 :         if (bAllowVRTNoData && asBandProperties[j].bHasNoData)
    1308          39 :             poBand->SetNoDataValue(asBandProperties[j].noDataValue);
    1309         472 :         if (bHideNoData)
    1310           2 :             poBand->SetMetadataItem("HideNoDataValue", "1");
    1311             : 
    1312         472 :         if (asBandProperties[j].bHasOffset)
    1313           0 :             poBand->SetOffset(asBandProperties[j].dfOffset);
    1314             : 
    1315         472 :         if (asBandProperties[j].bHasScale)
    1316           0 :             poBand->SetScale(asBandProperties[j].dfScale);
    1317             :     }
    1318             : 
    1319         197 :     VRTSourcedRasterBand *poMaskVRTBand = nullptr;
    1320         197 :     if (bAddAlpha)
    1321             :     {
    1322          10 :         poVRTDS->AddBand(GDT_Byte);
    1323          10 :         GDALRasterBand *poBand = poVRTDS->GetRasterBand(nSelectedBands + 1);
    1324          10 :         poBand->SetColorInterpretation(GCI_AlphaBand);
    1325             :     }
    1326         187 :     else if (bHasDatasetMask)
    1327             :     {
    1328          12 :         poVRTDS->CreateMaskBand(GMF_PER_DATASET);
    1329             :         poMaskVRTBand = static_cast<VRTSourcedRasterBand *>(
    1330          12 :             poVRTDS->GetRasterBand(1)->GetMaskBand());
    1331             :     }
    1332             : 
    1333         197 :     bool bCanCollectOverviewFactors = true;
    1334         394 :     std::set<int> anOverviewFactorsSet;
    1335         394 :     std::vector<int> anIdxValidDatasets;
    1336             : 
    1337        2515 :     for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
    1338             :     {
    1339        2318 :         DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
    1340             : 
    1341        2318 :         if (psDatasetProperties->isFileOK == FALSE)
    1342           8 :             continue;
    1343             : 
    1344        2311 :         const char *dsFileName = ppszInputFilenames[i];
    1345             : 
    1346             :         double dfSrcXOff;
    1347             :         double dfSrcYOff;
    1348             :         double dfSrcXSize;
    1349             :         double dfSrcYSize;
    1350             :         double dfDstXOff;
    1351             :         double dfDstYOff;
    1352             :         double dfDstXSize;
    1353             :         double dfDstYSize;
    1354        2311 :         if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY, maxX,
    1355             :                           maxY, nRasterXSize, nRasterYSize, &dfSrcXOff,
    1356             :                           &dfSrcYOff, &dfSrcXSize, &dfSrcYSize, &dfDstXOff,
    1357             :                           &dfDstYOff, &dfDstXSize, &dfDstYSize))
    1358             :         {
    1359           1 :             CPLDebug("BuildVRT",
    1360             :                      "Skipping %s as not intersecting area of interest",
    1361             :                      dsFileName);
    1362           1 :             continue;
    1363             :         }
    1364             : 
    1365        2310 :         anIdxValidDatasets.push_back(i);
    1366             : 
    1367        2310 :         if (bCanCollectOverviewFactors)
    1368             :         {
    1369        2295 :             if (std::abs(psDatasetProperties->adfGeoTransform[1] - we_res) >
    1370        4571 :                     1e-8 * std::abs(we_res) ||
    1371        2276 :                 std::abs(psDatasetProperties->adfGeoTransform[5] - ns_res) >
    1372        2276 :                     1e-8 * std::abs(ns_res))
    1373             :             {
    1374          19 :                 bCanCollectOverviewFactors = false;
    1375          19 :                 anOverviewFactorsSet.clear();
    1376             :             }
    1377             :         }
    1378        2310 :         if (bCanCollectOverviewFactors)
    1379             :         {
    1380        2285 :             for (int nOvFactor : psDatasetProperties->anOverviewFactors)
    1381           9 :                 anOverviewFactorsSet.insert(nOvFactor);
    1382             :         }
    1383             : 
    1384             :         GDALDatasetH hSourceDS;
    1385        2310 :         bool bDropRef = false;
    1386             : 
    1387        5729 :         if (nSrcDSCount == nInputFiles &&
    1388        3419 :             GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
    1389        1109 :             (dsFileName[0] == '\0' ||  // could be a unnamed VRT file
    1390          53 :              EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
    1391             :                    "MEM")))
    1392             :         {
    1393        1090 :             hSourceDS = pahSrcDS[i];
    1394             :         }
    1395             :         else
    1396             :         {
    1397        1220 :             bDropRef = true;
    1398        2440 :             GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
    1399             :                 dsFileName, psDatasetProperties->nRasterXSize,
    1400             :                 psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
    1401        1220 :                 pszProjectionRef, psDatasetProperties->adfGeoTransform);
    1402        1220 :             reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
    1403        1220 :                 papszOpenOptions);
    1404             : 
    1405        3528 :             for (int j = 0;
    1406        3528 :                  j < nMaxSelectedBandNo +
    1407          42 :                          (bAddAlpha && psDatasetProperties->bLastBandIsAlpha
    1408        3570 :                               ? 1
    1409             :                               : 0);
    1410             :                  j++)
    1411             :             {
    1412        2308 :                 GDALProxyPoolDatasetAddSrcBandDescription(
    1413             :                     hProxyDS,
    1414        2308 :                     j < static_cast<int>(asBandProperties.size())
    1415        2303 :                         ? asBandProperties[j].dataType
    1416             :                         : GDT_Byte,
    1417             :                     psDatasetProperties->nBlockXSize,
    1418             :                     psDatasetProperties->nBlockYSize);
    1419             :             }
    1420        1220 :             if (bHasDatasetMask && !bAddAlpha)
    1421             :             {
    1422             :                 static_cast<GDALProxyPoolRasterBand *>(
    1423             :                     reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)
    1424          13 :                         ->GetRasterBand(1))
    1425          13 :                     ->AddSrcMaskBandDescription(
    1426             :                         GDT_Byte, psDatasetProperties->nMaskBlockXSize,
    1427             :                         psDatasetProperties->nMaskBlockYSize);
    1428             :             }
    1429             : 
    1430        1220 :             hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
    1431             :         }
    1432             : 
    1433        5956 :         for (int j = 0;
    1434        5956 :              j <
    1435        5956 :              nSelectedBands +
    1436        5956 :                  (bAddAlpha && psDatasetProperties->bLastBandIsAlpha ? 1 : 0);
    1437             :              j++)
    1438             :         {
    1439             :             VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
    1440        3646 :                 poVRTDS->GetRasterBand(j + 1));
    1441        3646 :             const int nSelBand = j == nSelectedBands ? nSelectedBands + 1
    1442        3638 :                                                      : panSelectedBandList[j];
    1443             : 
    1444             :             /* Place the raster band at the right position in the VRT */
    1445        3646 :             VRTSourcedRasterBand *poVRTBand =
    1446             :                 static_cast<VRTSourcedRasterBand *>(hVRTBand);
    1447             : 
    1448             :             VRTSimpleSource *poSimpleSource;
    1449        3646 :             if (bNoDataFromMask)
    1450             :             {
    1451          15 :                 auto poNoDataFromMaskSource = new VRTNoDataFromMaskSource();
    1452          15 :                 poSimpleSource = poNoDataFromMaskSource;
    1453          15 :                 poNoDataFromMaskSource->SetParameters(
    1454          15 :                     (nVRTNoDataCount > 0)
    1455          15 :                         ? ((j < nVRTNoDataCount)
    1456          15 :                                ? padfVRTNoData[j]
    1457           6 :                                : padfVRTNoData[nVRTNoDataCount - 1])
    1458             :                         : 0,
    1459             :                     dfMaskValueThreshold);
    1460             :             }
    1461        7262 :             else if (bAllowSrcNoData &&
    1462        7262 :                      psDatasetProperties->abHasNoData[nSelBand - 1])
    1463             :             {
    1464          27 :                 auto poComplexSource = new VRTComplexSource();
    1465          27 :                 poSimpleSource = poComplexSource;
    1466          27 :                 poComplexSource->SetNoDataValue(
    1467          27 :                     psDatasetProperties->adfNoDataValues[nSelBand - 1]);
    1468             :             }
    1469        7208 :             else if (bUseSrcMaskBand &&
    1470        7208 :                      psDatasetProperties->abHasMaskBand[nSelBand - 1])
    1471             :             {
    1472          57 :                 auto poSource = new VRTComplexSource();
    1473          57 :                 poSource->SetUseMaskBand(true);
    1474          57 :                 poSimpleSource = poSource;
    1475             :             }
    1476             :             else
    1477        3547 :                 poSimpleSource = new VRTSimpleSource();
    1478        3646 :             if (pszResampling)
    1479          23 :                 poSimpleSource->SetResampling(pszResampling);
    1480        3646 :             auto poSrcBand = GDALRasterBand::FromHandle(
    1481             :                 GDALGetRasterBand(hSourceDS, nSelBand));
    1482        3646 :             poVRTBand->ConfigureSource(poSimpleSource, poSrcBand, FALSE,
    1483             :                                        dfSrcXOff, dfSrcYOff, dfSrcXSize,
    1484             :                                        dfSrcYSize, dfDstXOff, dfDstYOff,
    1485             :                                        dfDstXSize, dfDstYSize);
    1486             : 
    1487        3646 :             if (bWriteAbsolutePath)
    1488           3 :                 WriteAbsolutePath(poSimpleSource, dsFileName);
    1489             : 
    1490        3646 :             poVRTBand->AddSource(poSimpleSource);
    1491             :         }
    1492             : 
    1493        2310 :         if (bAddAlpha && !psDatasetProperties->bLastBandIsAlpha)
    1494             :         {
    1495             :             VRTSourcedRasterBand *poVRTBand =
    1496             :                 static_cast<VRTSourcedRasterBand *>(
    1497          11 :                     poVRTDS->GetRasterBand(nSelectedBands + 1));
    1498             :             /* Little trick : we use an offset of 255 and a scaling of 0, so
    1499             :              * that in areas covered */
    1500             :             /* by the source, the value of the alpha band will be 255, otherwise
    1501             :              * it will be 0 */
    1502          11 :             poVRTBand->AddComplexSource(
    1503             :                 GDALRasterBand::FromHandle(GDALGetRasterBand(hSourceDS, 1)),
    1504             :                 dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
    1505          11 :                 dfDstYOff, dfDstXSize, dfDstYSize, 255, 0, VRT_NODATA_UNSET);
    1506             :         }
    1507        2299 :         else if (bHasDatasetMask)
    1508             :         {
    1509             :             VRTSimpleSource *poSource;
    1510          15 :             if (bUseSrcMaskBand)
    1511             :             {
    1512          15 :                 auto poComplexSource = new VRTComplexSource();
    1513          15 :                 poComplexSource->SetUseMaskBand(true);
    1514          15 :                 poSource = poComplexSource;
    1515             :             }
    1516             :             else
    1517             :             {
    1518           0 :                 poSource = new VRTSimpleSource();
    1519             :             }
    1520          15 :             if (pszResampling)
    1521           4 :                 poSource->SetResampling(pszResampling);
    1522          15 :             assert(poMaskVRTBand);
    1523          30 :             poMaskVRTBand->ConfigureSource(
    1524             :                 poSource,
    1525          15 :                 static_cast<GDALRasterBand *>(GDALGetRasterBand(hSourceDS, 1)),
    1526             :                 TRUE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
    1527             :                 dfDstYOff, dfDstXSize, dfDstYSize);
    1528             : 
    1529          15 :             if (bWriteAbsolutePath)
    1530           1 :                 WriteAbsolutePath(poSource, dsFileName);
    1531             : 
    1532          15 :             poMaskVRTBand->AddSource(poSource);
    1533             :         }
    1534             : 
    1535        2310 :         if (bDropRef)
    1536             :         {
    1537        1220 :             GDALDereferenceDataset(hSourceDS);
    1538             :         }
    1539             :     }
    1540             : 
    1541        2507 :     for (int i : anIdxValidDatasets)
    1542             :     {
    1543        2310 :         const DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
    1544        2310 :         for (auto oIter = anOverviewFactorsSet.begin();
    1545        2319 :              oIter != anOverviewFactorsSet.end();)
    1546             :         {
    1547           9 :             const int nGlobalOvrFactor = *oIter;
    1548           9 :             auto oIterNext = oIter;
    1549           9 :             ++oIterNext;
    1550             : 
    1551           9 :             if (psDatasetProperties->nRasterXSize / nGlobalOvrFactor < 128 &&
    1552           0 :                 psDatasetProperties->nRasterYSize / nGlobalOvrFactor < 128)
    1553             :             {
    1554           0 :                 break;
    1555             :             }
    1556           9 :             if (std::find(psDatasetProperties->anOverviewFactors.begin(),
    1557             :                           psDatasetProperties->anOverviewFactors.end(),
    1558           9 :                           nGlobalOvrFactor) ==
    1559          18 :                 psDatasetProperties->anOverviewFactors.end())
    1560             :             {
    1561           0 :                 anOverviewFactorsSet.erase(oIter);
    1562             :             }
    1563             : 
    1564           9 :             oIter = oIterNext;
    1565             :         }
    1566             :     }
    1567         200 :     if (!anOverviewFactorsSet.empty() &&
    1568           3 :         CPLTestBool(CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "YES")))
    1569             :     {
    1570           6 :         std::vector<int> anOverviewFactors;
    1571           3 :         anOverviewFactors.insert(anOverviewFactors.end(),
    1572             :                                  anOverviewFactorsSet.begin(),
    1573           6 :                                  anOverviewFactorsSet.end());
    1574           3 :         const char *const apszOptions[] = {"VRT_VIRTUAL_OVERVIEWS=YES",
    1575             :                                            nullptr};
    1576           3 :         poVRTDS->BuildOverviews(pszResampling ? pszResampling : "nearest",
    1577           3 :                                 static_cast<int>(anOverviewFactors.size()),
    1578           3 :                                 &anOverviewFactors[0], 0, nullptr, nullptr,
    1579             :                                 nullptr, apszOptions);
    1580             :     }
    1581         197 : }
    1582             : 
    1583             : /************************************************************************/
    1584             : /*                             Build()                                  */
    1585             : /************************************************************************/
    1586             : 
    1587         220 : std::unique_ptr<GDALDataset> VRTBuilder::Build(GDALProgressFunc pfnProgress,
    1588             :                                                void *pProgressData)
    1589             : {
    1590         220 :     if (bHasRunBuild)
    1591           0 :         return nullptr;
    1592         220 :     bHasRunBuild = TRUE;
    1593             : 
    1594         220 :     if (pfnProgress == nullptr)
    1595           0 :         pfnProgress = GDALDummyProgress;
    1596             : 
    1597         220 :     bUserExtent = (minX != 0 || minY != 0 || maxX != 0 || maxY != 0);
    1598         220 :     if (bUserExtent)
    1599             :     {
    1600          13 :         if (minX >= maxX || minY >= maxY)
    1601             :         {
    1602           0 :             CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user extent");
    1603           0 :             return nullptr;
    1604             :         }
    1605             :     }
    1606             : 
    1607         220 :     if (resolutionStrategy == USER_RESOLUTION)
    1608             :     {
    1609           8 :         if (we_res <= 0 || ns_res <= 0)
    1610             :         {
    1611           0 :             CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user resolution");
    1612           0 :             return nullptr;
    1613             :         }
    1614             : 
    1615             :         /* We work with negative north-south resolution in all the following
    1616             :          * code */
    1617           8 :         ns_res = -ns_res;
    1618             :     }
    1619             :     else
    1620             :     {
    1621         212 :         we_res = ns_res = 0;
    1622             :     }
    1623             : 
    1624         220 :     asDatasetProperties.resize(nInputFiles);
    1625             : 
    1626         220 :     if (pszSrcNoData != nullptr)
    1627             :     {
    1628           6 :         if (EQUAL(pszSrcNoData, "none"))
    1629             :         {
    1630           1 :             bAllowSrcNoData = FALSE;
    1631             :         }
    1632             :         else
    1633             :         {
    1634           5 :             char **papszTokens = CSLTokenizeString(pszSrcNoData);
    1635           5 :             nSrcNoDataCount = CSLCount(papszTokens);
    1636           5 :             padfSrcNoData = static_cast<double *>(
    1637           5 :                 CPLMalloc(sizeof(double) * nSrcNoDataCount));
    1638          12 :             for (int i = 0; i < nSrcNoDataCount; i++)
    1639             :             {
    1640           7 :                 if (!ArgIsNumeric(papszTokens[i]) &&
    1641           0 :                     !EQUAL(papszTokens[i], "nan") &&
    1642           7 :                     !EQUAL(papszTokens[i], "-inf") &&
    1643           0 :                     !EQUAL(papszTokens[i], "inf"))
    1644             :                 {
    1645           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1646             :                              "Invalid -srcnodata value");
    1647           0 :                     CSLDestroy(papszTokens);
    1648           0 :                     return nullptr;
    1649             :                 }
    1650           7 :                 padfSrcNoData[i] = CPLAtofM(papszTokens[i]);
    1651             :             }
    1652           5 :             CSLDestroy(papszTokens);
    1653             :         }
    1654             :     }
    1655             : 
    1656         220 :     if (pszVRTNoData != nullptr)
    1657             :     {
    1658          23 :         if (EQUAL(pszVRTNoData, "none"))
    1659             :         {
    1660           1 :             bAllowVRTNoData = FALSE;
    1661             :         }
    1662             :         else
    1663             :         {
    1664          22 :             char **papszTokens = CSLTokenizeString(pszVRTNoData);
    1665          22 :             nVRTNoDataCount = CSLCount(papszTokens);
    1666          22 :             padfVRTNoData = static_cast<double *>(
    1667          22 :                 CPLMalloc(sizeof(double) * nVRTNoDataCount));
    1668          46 :             for (int i = 0; i < nVRTNoDataCount; i++)
    1669             :             {
    1670          24 :                 if (!ArgIsNumeric(papszTokens[i]) &&
    1671           1 :                     !EQUAL(papszTokens[i], "nan") &&
    1672          25 :                     !EQUAL(papszTokens[i], "-inf") &&
    1673           0 :                     !EQUAL(papszTokens[i], "inf"))
    1674             :                 {
    1675           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1676             :                              "Invalid -vrtnodata value");
    1677           0 :                     CSLDestroy(papszTokens);
    1678           0 :                     return nullptr;
    1679             :                 }
    1680          24 :                 padfVRTNoData[i] = CPLAtofM(papszTokens[i]);
    1681             :             }
    1682          22 :             CSLDestroy(papszTokens);
    1683             :         }
    1684             :     }
    1685             : 
    1686         220 :     bool bFoundValid = false;
    1687        2576 :     for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
    1688             :     {
    1689        2360 :         const char *dsFileName = ppszInputFilenames[i];
    1690             : 
    1691        2360 :         if (!pfnProgress(1.0 * (i + 1) / nInputFiles, nullptr, pProgressData))
    1692             :         {
    1693           0 :             return nullptr;
    1694             :         }
    1695             : 
    1696        2360 :         GDALDatasetH hDS = (pahSrcDS)
    1697        2360 :                                ? pahSrcDS[i]
    1698        1221 :                                : GDALOpenEx(dsFileName, GDAL_OF_RASTER, nullptr,
    1699        1221 :                                             papszOpenOptions, nullptr);
    1700        2360 :         asDatasetProperties[i].isFileOK = FALSE;
    1701             : 
    1702        2360 :         if (hDS)
    1703             :         {
    1704        2358 :             const auto osErrorMsg = AnalyseRaster(hDS, &asDatasetProperties[i]);
    1705        2358 :             if (osErrorMsg.empty())
    1706             :             {
    1707        2347 :                 asDatasetProperties[i].isFileOK = TRUE;
    1708        2347 :                 bFoundValid = true;
    1709        2347 :                 bFirst = FALSE;
    1710             :             }
    1711        2358 :             if (pahSrcDS == nullptr)
    1712        1219 :                 GDALClose(hDS);
    1713        2358 :             if (!osErrorMsg.empty() && osErrorMsg != "SILENTLY_IGNORE")
    1714             :             {
    1715          11 :                 if (bStrict)
    1716             :                 {
    1717           3 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1718             :                              osErrorMsg.c_str());
    1719           3 :                     return nullptr;
    1720             :                 }
    1721             :                 else
    1722             :                 {
    1723           8 :                     CPLError(CE_Warning, CPLE_AppDefined, "%s Skipping %s",
    1724             :                              osErrorMsg.c_str(), dsFileName);
    1725             :                 }
    1726             :             }
    1727             :         }
    1728             :         else
    1729             :         {
    1730           2 :             if (bStrict)
    1731             :             {
    1732           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Can't open %s.",
    1733             :                          dsFileName);
    1734           1 :                 return nullptr;
    1735             :             }
    1736             :             else
    1737             :             {
    1738           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1739             :                          "Can't open %s. Skipping it", dsFileName);
    1740             :             }
    1741             :         }
    1742             :     }
    1743             : 
    1744         216 :     if (!bFoundValid)
    1745           2 :         return nullptr;
    1746             : 
    1747         214 :     if (bHasGeoTransform)
    1748             :     {
    1749         213 :         if (bTargetAlignedPixels)
    1750             :         {
    1751           2 :             minX = floor(minX / we_res) * we_res;
    1752           2 :             maxX = ceil(maxX / we_res) * we_res;
    1753           2 :             minY = floor(minY / -ns_res) * -ns_res;
    1754           2 :             maxY = ceil(maxY / -ns_res) * -ns_res;
    1755             :         }
    1756             : 
    1757         213 :         nRasterXSize = static_cast<int>(0.5 + (maxX - minX) / we_res);
    1758         213 :         nRasterYSize = static_cast<int>(0.5 + (maxY - minY) / -ns_res);
    1759             :     }
    1760             : 
    1761         214 :     if (nRasterXSize == 0 || nRasterYSize == 0)
    1762             :     {
    1763           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1764             :                  "Computed VRT dimension is invalid. You've probably "
    1765             :                  "specified inappropriate resolution.");
    1766           0 :         return nullptr;
    1767             :     }
    1768             : 
    1769         214 :     auto poDS = VRTDataset::CreateVRTDataset(pszOutputFilename, nRasterXSize,
    1770             :                                              nRasterYSize, 0, GDT_Unknown,
    1771         428 :                                              aosCreateOptions.List());
    1772         214 :     if (!poDS)
    1773             :     {
    1774           0 :         return nullptr;
    1775             :     }
    1776             : 
    1777         214 :     if (pszOutputSRS)
    1778             :     {
    1779           1 :         poDS->SetProjection(pszOutputSRS);
    1780             :     }
    1781         213 :     else if (pszProjectionRef)
    1782             :     {
    1783         213 :         poDS->SetProjection(pszProjectionRef);
    1784             :     }
    1785             : 
    1786         214 :     if (bHasGeoTransform)
    1787             :     {
    1788             :         double adfGeoTransform[6];
    1789         213 :         adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX;
    1790         213 :         adfGeoTransform[GEOTRSFRM_WE_RES] = we_res;
    1791         213 :         adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0;
    1792         213 :         adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY;
    1793         213 :         adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0;
    1794         213 :         adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res;
    1795         213 :         poDS->SetGeoTransform(adfGeoTransform);
    1796             :     }
    1797             : 
    1798         214 :     if (bSeparate)
    1799             :     {
    1800          17 :         CreateVRTSeparate(poDS.get());
    1801             :     }
    1802             :     else
    1803             :     {
    1804         197 :         CreateVRTNonSeparate(poDS.get());
    1805             :     }
    1806             : 
    1807         214 :     return poDS;
    1808             : }
    1809             : 
    1810             : /************************************************************************/
    1811             : /*                        add_file_to_list()                            */
    1812             : /************************************************************************/
    1813             : 
    1814          45 : static bool add_file_to_list(const char *filename, const char *tile_index,
    1815             :                              CPLStringList &aosList)
    1816             : {
    1817             : 
    1818          45 :     if (EQUAL(CPLGetExtensionSafe(filename).c_str(), "SHP"))
    1819             :     {
    1820             :         /* Handle gdaltindex Shapefile as a special case */
    1821           1 :         auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(filename));
    1822           1 :         if (poDS == nullptr)
    1823             :         {
    1824           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1825             :                      "Unable to open shapefile `%s'.", filename);
    1826           0 :             return false;
    1827             :         }
    1828             : 
    1829           1 :         auto poLayer = poDS->GetLayer(0);
    1830           1 :         const auto poFDefn = poLayer->GetLayerDefn();
    1831             : 
    1832           2 :         if (poFDefn->GetFieldIndex("LOCATION") >= 0 &&
    1833           1 :             strcmp("LOCATION", tile_index) != 0)
    1834             :         {
    1835           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1836             :                      "This shapefile seems to be a tile index of "
    1837             :                      "OGR features and not GDAL products.");
    1838             :         }
    1839           1 :         const int ti_field = poFDefn->GetFieldIndex(tile_index);
    1840           1 :         if (ti_field < 0)
    1841             :         {
    1842           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1843             :                      "Unable to find field `%s' in DBF file `%s'.", tile_index,
    1844             :                      filename);
    1845           0 :             return false;
    1846             :         }
    1847             : 
    1848             :         /* Load in memory existing file names in SHP */
    1849           1 :         const auto nTileIndexFiles = poLayer->GetFeatureCount(TRUE);
    1850           1 :         if (nTileIndexFiles == 0)
    1851             :         {
    1852           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1853             :                      "Tile index %s is empty. Skipping it.", filename);
    1854           0 :             return true;
    1855             :         }
    1856           1 :         if (nTileIndexFiles > 100 * 1024 * 1024)
    1857             :         {
    1858           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1859             :                      "Too large feature count in tile index");
    1860           0 :             return false;
    1861             :         }
    1862             : 
    1863           5 :         for (auto &&poFeature : poLayer)
    1864             :         {
    1865           4 :             aosList.AddString(poFeature->GetFieldAsString(ti_field));
    1866             :         }
    1867             :     }
    1868             :     else
    1869             :     {
    1870          44 :         aosList.AddString(filename);
    1871             :     }
    1872             : 
    1873          45 :     return true;
    1874             : }
    1875             : 
    1876             : /************************************************************************/
    1877             : /*                        GDALBuildVRTOptions                           */
    1878             : /************************************************************************/
    1879             : 
    1880             : /** Options for use with GDALBuildVRT(). GDALBuildVRTOptions* must be allocated
    1881             :  * and freed with GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree()
    1882             :  * respectively.
    1883             :  */
    1884             : struct GDALBuildVRTOptions
    1885             : {
    1886             :     std::string osProgramName = "gdalbuildvrt";
    1887             :     std::string osTileIndex = "location";
    1888             :     bool bStrict = false;
    1889             :     std::string osResolution{};
    1890             :     bool bSeparate = false;
    1891             :     bool bAllowProjectionDifference = false;
    1892             :     double we_res = 0;
    1893             :     double ns_res = 0;
    1894             :     bool bTargetAlignedPixels = false;
    1895             :     double xmin = 0;
    1896             :     double ymin = 0;
    1897             :     double xmax = 0;
    1898             :     double ymax = 0;
    1899             :     bool bAddAlpha = false;
    1900             :     bool bHideNoData = false;
    1901             :     int nSubdataset = -1;
    1902             :     std::string osSrcNoData{};
    1903             :     std::string osVRTNoData{};
    1904             :     std::string osOutputSRS{};
    1905             :     std::vector<int> anSelectedBandList{};
    1906             :     std::string osResampling{};
    1907             :     CPLStringList aosOpenOptions{};
    1908             :     CPLStringList aosCreateOptions{};
    1909             :     bool bUseSrcMaskBand = true;
    1910             :     bool bNoDataFromMask = false;
    1911             :     double dfMaskValueThreshold = 0;
    1912             :     bool bWriteAbsolutePath = false;
    1913             :     std::string osPixelFunction{};
    1914             :     CPLStringList aosPixelFunctionArgs{};
    1915             : 
    1916             :     /*! allow or suppress progress monitor and other non-error output */
    1917             :     bool bQuiet = true;
    1918             : 
    1919             :     /*! the progress function to use */
    1920             :     GDALProgressFunc pfnProgress = GDALDummyProgress;
    1921             : 
    1922             :     /*! pointer to the progress data variable */
    1923             :     void *pProgressData = nullptr;
    1924             : };
    1925             : 
    1926             : /************************************************************************/
    1927             : /*                           GDALBuildVRT()                             */
    1928             : /************************************************************************/
    1929             : 
    1930             : /* clang-format off */
    1931             : /**
    1932             :  * Build a VRT from a list of datasets.
    1933             :  *
    1934             :  * This is the equivalent of the
    1935             :  * <a href="/programs/gdalbuildvrt.html">gdalbuildvrt</a> utility.
    1936             :  *
    1937             :  * GDALBuildVRTOptions* must be allocated and freed with
    1938             :  * GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree() respectively. pahSrcDS
    1939             :  * and papszSrcDSNames cannot be used at the same time.
    1940             :  *
    1941             :  * @param pszDest the destination dataset path.
    1942             :  * @param nSrcCount the number of input datasets.
    1943             :  * @param pahSrcDS the list of input datasets (or NULL, exclusive with
    1944             :  * papszSrcDSNames). For practical purposes, the type
    1945             :  * of this argument should be considered as "const GDALDatasetH* const*", that
    1946             :  * is neither the array nor its values are mutated by this function.
    1947             :  * @param papszSrcDSNames the list of input dataset names (or NULL, exclusive
    1948             :  * with pahSrcDS)
    1949             :  * @param psOptionsIn the options struct returned by GDALBuildVRTOptionsNew() or
    1950             :  * NULL.
    1951             :  * @param pbUsageError pointer to a integer output variable to store if any
    1952             :  * usage error has occurred.
    1953             :  * @return the output dataset (new dataset that must be closed using
    1954             :  * GDALClose()) or NULL in case of error. If using pahSrcDS, the returned VRT
    1955             :  * dataset has a reference to each pahSrcDS[] element. Hence pahSrcDS[] elements
    1956             :  * should be closed after the returned dataset if using GDALClose().
    1957             :  * A safer alternative is to use GDALReleaseDataset() instead of using
    1958             :  * GDALClose(), in which case you can close datasets in any order.
    1959             : 
    1960             :  *
    1961             :  * @since GDAL 2.1
    1962             :  */
    1963             : /* clang-format on */
    1964             : 
    1965         221 : GDALDatasetH GDALBuildVRT(const char *pszDest, int nSrcCount,
    1966             :                           GDALDatasetH *pahSrcDS,
    1967             :                           const char *const *papszSrcDSNames,
    1968             :                           const GDALBuildVRTOptions *psOptionsIn,
    1969             :                           int *pbUsageError)
    1970             : {
    1971         221 :     if (pszDest == nullptr)
    1972           0 :         pszDest = "";
    1973             : 
    1974         221 :     if (nSrcCount == 0)
    1975             :     {
    1976           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No input dataset specified.");
    1977             : 
    1978           0 :         if (pbUsageError)
    1979           0 :             *pbUsageError = TRUE;
    1980           0 :         return nullptr;
    1981             :     }
    1982             : 
    1983             :     // cppcheck-suppress unreadVariable
    1984             :     GDALBuildVRTOptions sOptions(psOptionsIn ? *psOptionsIn
    1985         442 :                                              : GDALBuildVRTOptions());
    1986             : 
    1987           8 :     if (sOptions.we_res != 0 && sOptions.ns_res != 0 &&
    1988         229 :         !sOptions.osResolution.empty() &&
    1989           0 :         !EQUAL(sOptions.osResolution.c_str(), "user"))
    1990             :     {
    1991           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1992             :                  "-tr option is not compatible with -resolution %s",
    1993             :                  sOptions.osResolution.c_str());
    1994           0 :         if (pbUsageError)
    1995           0 :             *pbUsageError = TRUE;
    1996           0 :         return nullptr;
    1997             :     }
    1998             : 
    1999         221 :     if (sOptions.bTargetAlignedPixels && sOptions.we_res == 0 &&
    2000           1 :         sOptions.ns_res == 0)
    2001             :     {
    2002           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    2003             :                  "-tap option cannot be used without using -tr");
    2004           1 :         if (pbUsageError)
    2005           1 :             *pbUsageError = TRUE;
    2006           1 :         return nullptr;
    2007             :     }
    2008             : 
    2009         220 :     if (sOptions.bAddAlpha && sOptions.bSeparate)
    2010             :     {
    2011           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2012             :                  "-addalpha option is not compatible with -separate.");
    2013           0 :         if (pbUsageError)
    2014           0 :             *pbUsageError = TRUE;
    2015           0 :         return nullptr;
    2016             :     }
    2017             : 
    2018         220 :     ResolutionStrategy eStrategy = AVERAGE_RESOLUTION;
    2019         257 :     if (sOptions.osResolution.empty() ||
    2020          37 :         EQUAL(sOptions.osResolution.c_str(), "user"))
    2021             :     {
    2022         183 :         if (sOptions.we_res != 0 || sOptions.ns_res != 0)
    2023           8 :             eStrategy = USER_RESOLUTION;
    2024         175 :         else if (EQUAL(sOptions.osResolution.c_str(), "user"))
    2025             :         {
    2026           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2027             :                      "-tr option must be used with -resolution user.");
    2028           0 :             if (pbUsageError)
    2029           0 :                 *pbUsageError = TRUE;
    2030           0 :             return nullptr;
    2031             :         }
    2032             :     }
    2033          37 :     else if (EQUAL(sOptions.osResolution.c_str(), "average"))
    2034           1 :         eStrategy = AVERAGE_RESOLUTION;
    2035          36 :     else if (EQUAL(sOptions.osResolution.c_str(), "highest"))
    2036           1 :         eStrategy = HIGHEST_RESOLUTION;
    2037          35 :     else if (EQUAL(sOptions.osResolution.c_str(), "lowest"))
    2038           1 :         eStrategy = LOWEST_RESOLUTION;
    2039          34 :     else if (EQUAL(sOptions.osResolution.c_str(), "same"))
    2040          24 :         eStrategy = SAME_RESOLUTION;
    2041          10 :     else if (EQUAL(sOptions.osResolution.c_str(), "common"))
    2042          10 :         eStrategy = COMMON_RESOLUTION;
    2043             : 
    2044             :     /* If -srcnodata is specified, use it as the -vrtnodata if the latter is not
    2045             :      */
    2046             :     /* specified */
    2047         220 :     if (!sOptions.osSrcNoData.empty() && sOptions.osVRTNoData.empty())
    2048           2 :         sOptions.osVRTNoData = sOptions.osSrcNoData;
    2049             : 
    2050             :     VRTBuilder oBuilder(
    2051         220 :         sOptions.bStrict, pszDest, nSrcCount, papszSrcDSNames, pahSrcDS,
    2052         220 :         sOptions.anSelectedBandList.empty()
    2053             :             ? nullptr
    2054          19 :             : sOptions.anSelectedBandList.data(),
    2055         220 :         static_cast<int>(sOptions.anSelectedBandList.size()), eStrategy,
    2056         220 :         sOptions.we_res, sOptions.ns_res, sOptions.bTargetAlignedPixels,
    2057             :         sOptions.xmin, sOptions.ymin, sOptions.xmax, sOptions.ymax,
    2058         220 :         sOptions.bSeparate, sOptions.bAllowProjectionDifference,
    2059         220 :         sOptions.bAddAlpha, sOptions.bHideNoData, sOptions.nSubdataset,
    2060         226 :         sOptions.osSrcNoData.empty() ? nullptr : sOptions.osSrcNoData.c_str(),
    2061          23 :         sOptions.osVRTNoData.empty() ? nullptr : sOptions.osVRTNoData.c_str(),
    2062         220 :         sOptions.bUseSrcMaskBand, sOptions.bNoDataFromMask,
    2063             :         sOptions.dfMaskValueThreshold,
    2064         221 :         sOptions.osOutputSRS.empty() ? nullptr : sOptions.osOutputSRS.c_str(),
    2065         235 :         sOptions.osResampling.empty() ? nullptr : sOptions.osResampling.c_str(),
    2066         220 :         sOptions.osPixelFunction.empty() ? nullptr
    2067           3 :                                          : sOptions.osPixelFunction.c_str(),
    2068         220 :         sOptions.aosPixelFunctionArgs, sOptions.aosOpenOptions.List(),
    2069        1126 :         sOptions.aosCreateOptions, sOptions.bWriteAbsolutePath);
    2070         220 :     oBuilder.m_osProgramName = sOptions.osProgramName;
    2071             : 
    2072         220 :     return GDALDataset::ToHandle(
    2073         440 :         oBuilder.Build(sOptions.pfnProgress, sOptions.pProgressData).release());
    2074             : }
    2075             : 
    2076             : /************************************************************************/
    2077             : /*                             SanitizeSRS                              */
    2078             : /************************************************************************/
    2079             : 
    2080           1 : static char *SanitizeSRS(const char *pszUserInput)
    2081             : 
    2082             : {
    2083             :     OGRSpatialReferenceH hSRS;
    2084           1 :     char *pszResult = nullptr;
    2085             : 
    2086           1 :     CPLErrorReset();
    2087             : 
    2088           1 :     hSRS = OSRNewSpatialReference(nullptr);
    2089           1 :     if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE)
    2090           1 :         OSRExportToWkt(hSRS, &pszResult);
    2091             :     else
    2092             :     {
    2093           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s",
    2094             :                  pszUserInput);
    2095             :     }
    2096             : 
    2097           1 :     OSRDestroySpatialReference(hSRS);
    2098             : 
    2099           1 :     return pszResult;
    2100             : }
    2101             : 
    2102             : /************************************************************************/
    2103             : /*                     GDALBuildVRTOptionsGetParser()                    */
    2104             : /************************************************************************/
    2105             : 
    2106             : static std::unique_ptr<GDALArgumentParser>
    2107         224 : GDALBuildVRTOptionsGetParser(GDALBuildVRTOptions *psOptions,
    2108             :                              GDALBuildVRTOptionsForBinary *psOptionsForBinary)
    2109             : {
    2110             :     auto argParser = std::make_unique<GDALArgumentParser>(
    2111         224 :         "gdalbuildvrt", /* bForBinary=*/psOptionsForBinary != nullptr);
    2112             : 
    2113         224 :     argParser->add_description(_("Builds a VRT from a list of datasets."));
    2114             : 
    2115         224 :     argParser->add_epilog(_(
    2116             :         "\n"
    2117             :         "e.g.\n"
    2118             :         "  % gdalbuildvrt doq_index.vrt doq/*.tif\n"
    2119             :         "  % gdalbuildvrt -input_file_list my_list.txt doq_index.vrt\n"
    2120             :         "\n"
    2121             :         "NOTES:\n"
    2122             :         "  o With -separate, each files goes into a separate band in the VRT "
    2123             :         "band.\n"
    2124             :         "    Otherwise, the files are considered as tiles of a larger mosaic.\n"
    2125             :         "  o -b option selects a band to add into vrt.  Multiple bands can be "
    2126             :         "listed.\n"
    2127             :         "    By default all bands are queried.\n"
    2128             :         "  o The default tile index field is 'location' unless otherwise "
    2129             :         "specified by\n"
    2130             :         "    -tileindex.\n"
    2131             :         "  o In case the resolution of all input files is not the same, the "
    2132             :         "-resolution\n"
    2133             :         "    flag enable the user to control the way the output resolution is "
    2134             :         "computed.\n"
    2135             :         "    Average is the default.\n"
    2136             :         "  o Input files may be any valid GDAL dataset or a GDAL raster tile "
    2137             :         "index.\n"
    2138             :         "  o For a GDAL raster tile index, all entries will be added to the "
    2139             :         "VRT.\n"
    2140             :         "  o If one GDAL dataset is made of several subdatasets and has 0 "
    2141             :         "raster bands,\n"
    2142             :         "    its datasets will be added to the VRT rather than the dataset "
    2143             :         "itself.\n"
    2144             :         "    Single subdataset could be selected by its number using the -sd "
    2145             :         "option.\n"
    2146             :         "  o By default, only datasets of same projection and band "
    2147             :         "characteristics\n"
    2148             :         "    may be added to the VRT.\n"
    2149             :         "\n"
    2150             :         "For more details, consult "
    2151         224 :         "https://gdal.org/programs/gdalbuildvrt.html"));
    2152             : 
    2153             :     argParser->add_quiet_argument(
    2154         224 :         psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
    2155             : 
    2156             :     {
    2157         224 :         auto &group = argParser->add_mutually_exclusive_group();
    2158             : 
    2159         224 :         group.add_argument("-strict")
    2160         224 :             .flag()
    2161         224 :             .store_into(psOptions->bStrict)
    2162         224 :             .help(_("Turn warnings as failures."));
    2163             : 
    2164         224 :         group.add_argument("-non_strict")
    2165         224 :             .flag()
    2166           0 :             .action([psOptions](const std::string &)
    2167         224 :                     { psOptions->bStrict = false; })
    2168             :             .help(_("Skip source datasets that have issues with warnings, and "
    2169         224 :                     "continue processing."));
    2170             :     }
    2171             : 
    2172         224 :     argParser->add_argument("-tile_index")
    2173         448 :         .metavar("<field_name>")
    2174         224 :         .store_into(psOptions->osTileIndex)
    2175             :         .help(_("Use the specified value as the tile index field, instead of "
    2176         224 :                 "the default value which is 'location'."));
    2177             : 
    2178         224 :     argParser->add_argument("-resolution")
    2179         448 :         .metavar("user|average|common|highest|lowest|same")
    2180             :         .action(
    2181         226 :             [psOptions](const std::string &s)
    2182             :             {
    2183          37 :                 psOptions->osResolution = s;
    2184          37 :                 if (!EQUAL(psOptions->osResolution.c_str(), "user") &&
    2185          37 :                     !EQUAL(psOptions->osResolution.c_str(), "average") &&
    2186          36 :                     !EQUAL(psOptions->osResolution.c_str(), "highest") &&
    2187          35 :                     !EQUAL(psOptions->osResolution.c_str(), "lowest") &&
    2188          84 :                     !EQUAL(psOptions->osResolution.c_str(), "same") &&
    2189          10 :                     !EQUAL(psOptions->osResolution.c_str(), "common"))
    2190             :                 {
    2191             :                     throw std::invalid_argument(
    2192             :                         CPLSPrintf("Illegal resolution value (%s).",
    2193           0 :                                    psOptions->osResolution.c_str()));
    2194             :                 }
    2195         261 :             })
    2196         224 :         .help(_("Control the way the output resolution is computed."));
    2197             : 
    2198         224 :     argParser->add_argument("-tr")
    2199         448 :         .metavar("<xres> <yes>")
    2200         224 :         .nargs(2)
    2201         224 :         .scan<'g', double>()
    2202         224 :         .help(_("Set target resolution."));
    2203             : 
    2204         224 :     if (psOptionsForBinary)
    2205             :     {
    2206          20 :         argParser->add_argument("-input_file_list")
    2207          40 :             .metavar("<filename>")
    2208             :             .action(
    2209           5 :                 [psOptions, psOptionsForBinary](const std::string &s)
    2210             :                 {
    2211           1 :                     const char *input_file_list = s.c_str();
    2212             :                     auto f = VSIVirtualHandleUniquePtr(
    2213           2 :                         VSIFOpenL(input_file_list, "r"));
    2214           1 :                     if (f)
    2215             :                     {
    2216             :                         while (1)
    2217             :                         {
    2218           5 :                             const char *filename = CPLReadLineL(f.get());
    2219           5 :                             if (filename == nullptr)
    2220           1 :                                 break;
    2221           4 :                             if (!add_file_to_list(
    2222             :                                     filename, psOptions->osTileIndex.c_str(),
    2223           4 :                                     psOptionsForBinary->aosSrcFiles))
    2224             :                             {
    2225             :                                 throw std::invalid_argument(
    2226           0 :                                     std::string("Cannot add ")
    2227           0 :                                         .append(filename)
    2228           0 :                                         .append(" to input file list"));
    2229             :                             }
    2230           4 :                         }
    2231             :                     }
    2232          21 :                 })
    2233          20 :             .help(_("Text file with an input filename on each line"));
    2234             :     }
    2235             : 
    2236             :     {
    2237         224 :         auto &group = argParser->add_mutually_exclusive_group();
    2238             : 
    2239         224 :         group.add_argument("-separate")
    2240         224 :             .flag()
    2241         224 :             .store_into(psOptions->bSeparate)
    2242         224 :             .help(_("Place each input file into a separate band."));
    2243             : 
    2244         224 :         group.add_argument("-pixel-function")
    2245         448 :             .metavar("<function>")
    2246             :             .action(
    2247           9 :                 [psOptions](const std::string &s)
    2248             :                 {
    2249             :                     auto *poPixFun =
    2250           5 :                         VRTDerivedRasterBand::GetPixelFunction(s.c_str());
    2251           5 :                     if (poPixFun == nullptr)
    2252             :                     {
    2253             :                         throw std::invalid_argument(
    2254           1 :                             s + " is not a registered pixel function.");
    2255             :                     }
    2256             : 
    2257           4 :                     psOptions->osPixelFunction = s;
    2258         228 :                 })
    2259             : 
    2260         224 :             .help("Function to calculate value from overlapping inputs");
    2261             :     }
    2262             : 
    2263         224 :     argParser->add_argument("-pixel-function-arg")
    2264         448 :         .metavar("<NAME>=<VALUE>")
    2265         224 :         .append()
    2266           2 :         .action([psOptions](const std::string &s)
    2267         226 :                 { psOptions->aosPixelFunctionArgs.AddString(s); })
    2268         224 :         .help(_("Pixel function argument(s)"));
    2269             : 
    2270         224 :     argParser->add_argument("-allow_projection_difference")
    2271         224 :         .flag()
    2272         224 :         .store_into(psOptions->bAllowProjectionDifference)
    2273             :         .help(_("Accept source files not in the same projection (but without "
    2274         224 :                 "reprojecting them!)."));
    2275             : 
    2276         224 :     argParser->add_argument("-sd")
    2277         448 :         .metavar("<n>")
    2278         224 :         .store_into(psOptions->nSubdataset)
    2279             :         .help(_("Use subdataset of specified index (starting at 1), instead of "
    2280         224 :                 "the source dataset itself."));
    2281             : 
    2282         224 :     argParser->add_argument("-tap")
    2283         224 :         .flag()
    2284         224 :         .store_into(psOptions->bTargetAlignedPixels)
    2285             :         .help(_("Align the coordinates of the extent of the output file to the "
    2286         224 :                 "values of the resolution."));
    2287             : 
    2288         224 :     argParser->add_argument("-te")
    2289         448 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
    2290         224 :         .nargs(4)
    2291         224 :         .scan<'g', double>()
    2292         224 :         .help(_("Set georeferenced extents of output file to be created."));
    2293             : 
    2294         224 :     argParser->add_argument("-addalpha")
    2295         224 :         .flag()
    2296         224 :         .store_into(psOptions->bAddAlpha)
    2297             :         .help(_("Adds an alpha mask band to the VRT when the source raster "
    2298         224 :                 "have none."));
    2299             : 
    2300         224 :     argParser->add_argument("-b")
    2301         448 :         .metavar("<band>")
    2302         224 :         .append()
    2303         224 :         .store_into(psOptions->anSelectedBandList)
    2304         224 :         .help(_("Specify input band(s) number."));
    2305             : 
    2306         224 :     argParser->add_argument("-hidenodata")
    2307         224 :         .flag()
    2308         224 :         .store_into(psOptions->bHideNoData)
    2309         224 :         .help(_("Makes the VRT band not report the NoData."));
    2310             : 
    2311         224 :     if (psOptionsForBinary)
    2312             :     {
    2313          20 :         argParser->add_argument("-overwrite")
    2314          20 :             .flag()
    2315          20 :             .store_into(psOptionsForBinary->bOverwrite)
    2316          20 :             .help(_("Overwrite the VRT if it already exists."));
    2317             :     }
    2318             : 
    2319         224 :     argParser->add_argument("-srcnodata")
    2320         448 :         .metavar("\"<value>[ <value>]...\"")
    2321         224 :         .store_into(psOptions->osSrcNoData)
    2322         224 :         .help(_("Set nodata values for input bands."));
    2323             : 
    2324         224 :     argParser->add_argument("-vrtnodata")
    2325         448 :         .metavar("\"<value>[ <value>]...\"")
    2326         224 :         .store_into(psOptions->osVRTNoData)
    2327         224 :         .help(_("Set nodata values at the VRT band level."));
    2328             : 
    2329         224 :     argParser->add_argument("-a_srs")
    2330         448 :         .metavar("<srs_def>")
    2331             :         .action(
    2332           2 :             [psOptions](const std::string &s)
    2333             :             {
    2334           1 :                 char *pszSRS = SanitizeSRS(s.c_str());
    2335           1 :                 if (pszSRS == nullptr)
    2336             :                 {
    2337           0 :                     throw std::invalid_argument("Invalid value for -a_srs");
    2338             :                 }
    2339           1 :                 psOptions->osOutputSRS = pszSRS;
    2340           1 :                 CPLFree(pszSRS);
    2341         225 :             })
    2342         224 :         .help(_("Override the projection for the output file.."));
    2343             : 
    2344         224 :     argParser->add_argument("-r")
    2345         448 :         .metavar("nearest|bilinear|cubic|cubicspline|lanczos|average|mode")
    2346         224 :         .store_into(psOptions->osResampling)
    2347         224 :         .help(_("Resampling algorithm."));
    2348             : 
    2349         224 :     argParser->add_open_options_argument(&psOptions->aosOpenOptions);
    2350             : 
    2351         224 :     argParser->add_creation_options_argument(psOptions->aosCreateOptions);
    2352             : 
    2353         224 :     argParser->add_argument("-write_absolute_path")
    2354         224 :         .flag()
    2355         224 :         .store_into(psOptions->bWriteAbsolutePath)
    2356             :         .help(_("Write the absolute path of the raster files in the tile index "
    2357         224 :                 "file."));
    2358             : 
    2359         224 :     argParser->add_argument("-ignore_srcmaskband")
    2360         224 :         .flag()
    2361           0 :         .action([psOptions](const std::string &)
    2362         224 :                 { psOptions->bUseSrcMaskBand = false; })
    2363         224 :         .help(_("Cause mask band of sources will not be taken into account."));
    2364             : 
    2365         224 :     argParser->add_argument("-nodata_max_mask_threshold")
    2366         448 :         .metavar("<threshold>")
    2367         224 :         .scan<'g', double>()
    2368             :         .action(
    2369          18 :             [psOptions](const std::string &s)
    2370             :             {
    2371           9 :                 psOptions->bNoDataFromMask = true;
    2372           9 :                 psOptions->dfMaskValueThreshold = CPLAtofM(s.c_str());
    2373         224 :             })
    2374             :         .help(_("Replaces the value of the source with the value of -vrtnodata "
    2375             :                 "when the value of the mask band of the source is less or "
    2376         224 :                 "equal to the threshold."));
    2377             : 
    2378         224 :     argParser->add_argument("-program_name")
    2379         224 :         .store_into(psOptions->osProgramName)
    2380         224 :         .hidden();
    2381             : 
    2382         224 :     if (psOptionsForBinary)
    2383             :     {
    2384          20 :         if (psOptionsForBinary->osDstFilename.empty())
    2385             :         {
    2386             :             // We normally go here, unless undocumented -o switch is used
    2387          20 :             argParser->add_argument("vrt_dataset_name")
    2388          40 :                 .metavar("<vrt_dataset_name>")
    2389          20 :                 .store_into(psOptionsForBinary->osDstFilename)
    2390          20 :                 .help(_("Output VRT."));
    2391             :         }
    2392             : 
    2393          20 :         argParser->add_argument("src_dataset_name")
    2394          40 :             .metavar("<src_dataset_name>")
    2395          20 :             .nargs(argparse::nargs_pattern::any)
    2396             :             .action(
    2397          41 :                 [psOptions, psOptionsForBinary](const std::string &s)
    2398             :                 {
    2399          41 :                     if (!add_file_to_list(s.c_str(),
    2400             :                                           psOptions->osTileIndex.c_str(),
    2401          41 :                                           psOptionsForBinary->aosSrcFiles))
    2402             :                     {
    2403             :                         throw std::invalid_argument(
    2404           0 :                             std::string("Cannot add ")
    2405           0 :                                 .append(s)
    2406           0 :                                 .append(" to input file list"));
    2407             :                     }
    2408          61 :                 })
    2409          20 :             .help(_("Input dataset(s)."));
    2410             :     }
    2411             : 
    2412         224 :     return argParser;
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                       GDALBuildVRTGetParserUsage()                   */
    2417             : /************************************************************************/
    2418             : 
    2419           1 : std::string GDALBuildVRTGetParserUsage()
    2420             : {
    2421             :     try
    2422             :     {
    2423           2 :         GDALBuildVRTOptions sOptions;
    2424           2 :         GDALBuildVRTOptionsForBinary sOptionsForBinary;
    2425             :         auto argParser =
    2426           2 :             GDALBuildVRTOptionsGetParser(&sOptions, &sOptionsForBinary);
    2427           1 :         return argParser->usage();
    2428             :     }
    2429           0 :     catch (const std::exception &err)
    2430             :     {
    2431           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    2432           0 :                  err.what());
    2433           0 :         return std::string();
    2434             :     }
    2435             : }
    2436             : 
    2437             : /************************************************************************/
    2438             : /*                             GDALBuildVRTOptionsNew()                  */
    2439             : /************************************************************************/
    2440             : 
    2441             : /**
    2442             :  * Allocates a GDALBuildVRTOptions struct.
    2443             :  *
    2444             :  * @param papszArgv NULL terminated list of options (potentially including
    2445             :  * filename and open options too), or NULL. The accepted options are the ones of
    2446             :  * the <a href="/programs/gdalbuildvrt.html">gdalbuildvrt</a> utility.
    2447             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    2448             :  * NULL), otherwise (gdalbuildvrt_bin.cpp use case) must be allocated with
    2449             :  * GDALBuildVRTOptionsForBinaryNew() prior to this function. Will be filled
    2450             :  * with potentially present filename, open options,...
    2451             :  * @return pointer to the allocated GDALBuildVRTOptions struct. Must be freed
    2452             :  * with GDALBuildVRTOptionsFree().
    2453             :  *
    2454             :  * @since GDAL 2.1
    2455             :  */
    2456             : 
    2457             : GDALBuildVRTOptions *
    2458         223 : GDALBuildVRTOptionsNew(char **papszArgv,
    2459             :                        GDALBuildVRTOptionsForBinary *psOptionsForBinary)
    2460             : {
    2461         446 :     auto psOptions = std::make_unique<GDALBuildVRTOptions>();
    2462             : 
    2463         446 :     CPLStringList aosArgv;
    2464         223 :     const int nArgc = CSLCount(papszArgv);
    2465        1153 :     for (int i = 0;
    2466        1153 :          i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
    2467             :     {
    2468         930 :         if (psOptionsForBinary && EQUAL(papszArgv[i], "-o") && i + 1 < nArgc &&
    2469           0 :             papszArgv[i + 1] != nullptr)
    2470             :         {
    2471             :             // Undocumented alternate way of specifying the destination file
    2472           0 :             psOptionsForBinary->osDstFilename = papszArgv[i + 1];
    2473           0 :             ++i;
    2474             :         }
    2475             :         // argparser will be confused if the value of a string argument
    2476             :         // starts with a negative sign.
    2477         930 :         else if (EQUAL(papszArgv[i], "-srcnodata") && i + 1 < nArgc)
    2478             :         {
    2479           6 :             ++i;
    2480           6 :             psOptions->osSrcNoData = papszArgv[i];
    2481             :         }
    2482             :         // argparser will be confused if the value of a string argument
    2483             :         // starts with a negative sign.
    2484         924 :         else if (EQUAL(papszArgv[i], "-vrtnodata") && i + 1 < nArgc)
    2485             :         {
    2486          21 :             ++i;
    2487          21 :             psOptions->osVRTNoData = papszArgv[i];
    2488             :         }
    2489             : 
    2490             :         else
    2491             :         {
    2492         903 :             aosArgv.AddString(papszArgv[i]);
    2493             :         }
    2494             :     }
    2495             : 
    2496             :     try
    2497             :     {
    2498             :         auto argParser =
    2499         446 :             GDALBuildVRTOptionsGetParser(psOptions.get(), psOptionsForBinary);
    2500             : 
    2501         223 :         argParser->parse_args_without_binary_name(aosArgv.List());
    2502             : 
    2503         229 :         if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
    2504             :         {
    2505           8 :             psOptions->we_res = (*adfTargetRes)[0];
    2506           8 :             psOptions->ns_res = (*adfTargetRes)[1];
    2507             :         }
    2508             : 
    2509         234 :         if (auto oTE = argParser->present<std::vector<double>>("-te"))
    2510             :         {
    2511          13 :             psOptions->xmin = (*oTE)[0];
    2512          13 :             psOptions->ymin = (*oTE)[1];
    2513          13 :             psOptions->xmax = (*oTE)[2];
    2514          13 :             psOptions->ymax = (*oTE)[3];
    2515             :         }
    2516             : 
    2517         439 :         if (psOptions->osPixelFunction.empty() &&
    2518         218 :             !psOptions->aosPixelFunctionArgs.empty())
    2519             :         {
    2520             :             throw std::runtime_error(
    2521           1 :                 "Pixel function arguments provided without a pixel function");
    2522             :         }
    2523             : 
    2524         220 :         return psOptions.release();
    2525             :     }
    2526           3 :     catch (const std::exception &err)
    2527             :     {
    2528           3 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
    2529           3 :         return nullptr;
    2530             :     }
    2531             : }
    2532             : 
    2533             : /************************************************************************/
    2534             : /*                        GDALBuildVRTOptionsFree()                     */
    2535             : /************************************************************************/
    2536             : 
    2537             : /**
    2538             :  * Frees the GDALBuildVRTOptions struct.
    2539             :  *
    2540             :  * @param psOptions the options struct for GDALBuildVRT().
    2541             :  *
    2542             :  * @since GDAL 2.1
    2543             :  */
    2544             : 
    2545         219 : void GDALBuildVRTOptionsFree(GDALBuildVRTOptions *psOptions)
    2546             : {
    2547         219 :     delete psOptions;
    2548         219 : }
    2549             : 
    2550             : /************************************************************************/
    2551             : /*                 GDALBuildVRTOptionsSetProgress()                    */
    2552             : /************************************************************************/
    2553             : 
    2554             : /**
    2555             :  * Set a progress function.
    2556             :  *
    2557             :  * @param psOptions the options struct for GDALBuildVRT().
    2558             :  * @param pfnProgress the progress callback.
    2559             :  * @param pProgressData the user data for the progress callback.
    2560             :  *
    2561             :  * @since GDAL 2.1
    2562             :  */
    2563             : 
    2564          20 : void GDALBuildVRTOptionsSetProgress(GDALBuildVRTOptions *psOptions,
    2565             :                                     GDALProgressFunc pfnProgress,
    2566             :                                     void *pProgressData)
    2567             : {
    2568          20 :     psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
    2569          20 :     psOptions->pProgressData = pProgressData;
    2570          20 :     if (pfnProgress == GDALTermProgress)
    2571          19 :         psOptions->bQuiet = false;
    2572          20 : }

Generated by: LCOV version 1.14