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

Generated by: LCOV version 1.14