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

Generated by: LCOV version 1.14