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

Generated by: LCOV version 1.14