LCOV - code coverage report
Current view: top level - apps - gdalbuildvrt_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 905 1058 85.5 %
Date: 2025-01-18 12:42:00 Functions: 24 26 92.3 %

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

Generated by: LCOV version 1.14