LCOV - code coverage report
Current view: top level - apps - gdalbuildvrt_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 866 1039 83.3 %
Date: 2024-05-03 15:49:35 Functions: 22 25 88.0 %

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

Generated by: LCOV version 1.14