LCOV - code coverage report
Current view: top level - apps - gdalbuildvrt_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1029 1185 86.8 %
Date: 2025-11-29 13:55:01 Functions: 26 28 92.9 %

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

Generated by: LCOV version 1.14