LCOV - code coverage report
Current view: top level - apps - gdaltindex_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 569 691 82.3 %
Date: 2025-08-19 18:03:11 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MapServer
       4             :  * Purpose:  Commandline App to build tile index for raster files.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam, DM Solutions Group Inc
       9             :  * Copyright (c) 2007-2023, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_conv.h"
      16             : #include "cpl_minixml.h"
      17             : #include "cpl_string.h"
      18             : #include "gdal_utils.h"
      19             : #include "gdal_priv.h"
      20             : #include "gdal_utils_priv.h"
      21             : #include "ogr_api.h"
      22             : #include "ogrsf_frmts.h"
      23             : #include "ogr_spatialref.h"
      24             : #include "commonutils.h"
      25             : #include "gdalargumentparser.h"
      26             : 
      27             : #include <ctype.h>
      28             : 
      29             : #include <algorithm>
      30             : #include <cmath>
      31             : #include <limits>
      32             : #include <set>
      33             : 
      34             : typedef enum
      35             : {
      36             :     FORMAT_AUTO,
      37             :     FORMAT_WKT,
      38             :     FORMAT_EPSG,
      39             :     FORMAT_PROJ
      40             : } SrcSRSFormat;
      41             : 
      42             : /************************************************************************/
      43             : /*                        GDALTileIndexRasterMetadata                   */
      44             : /************************************************************************/
      45             : 
      46             : struct GDALTileIndexRasterMetadata
      47             : {
      48             :     OGRFieldType eType = OFTString;
      49             :     std::string osFieldName{};
      50             :     std::string osRasterItemName{};
      51             : };
      52             : 
      53             : /************************************************************************/
      54             : /*                          GDALTileIndexOptions                        */
      55             : /************************************************************************/
      56             : 
      57             : struct GDALTileIndexOptions
      58             : {
      59             :     bool bInvokedFromGdalRasterIndex = false;
      60             :     bool bOverwrite = false;
      61             :     bool bSkipErrors = false;
      62             :     std::string osFormat{};
      63             :     std::string osIndexLayerName{};
      64             :     std::string osLocationField = "location";
      65             :     CPLStringList aosLCO{};
      66             :     std::string osTargetSRS{};
      67             :     bool bWriteAbsolutePath = false;
      68             :     bool bSkipDifferentProjection = false;
      69             :     std::string osSrcSRSFieldName{};
      70             :     SrcSRSFormat eSrcSRSFormat = FORMAT_AUTO;
      71             :     double xres = std::numeric_limits<double>::quiet_NaN();
      72             :     double yres = std::numeric_limits<double>::quiet_NaN();
      73             :     double xmin = std::numeric_limits<double>::quiet_NaN();
      74             :     double ymin = std::numeric_limits<double>::quiet_NaN();
      75             :     double xmax = std::numeric_limits<double>::quiet_NaN();
      76             :     double ymax = std::numeric_limits<double>::quiet_NaN();
      77             :     std::string osBandCount{};
      78             :     std::string osNodata{};
      79             :     std::string osColorInterp{};
      80             :     std::string osDataType{};
      81             :     bool bMaskBand = false;
      82             :     std::vector<std::string> aosMetadata{};
      83             :     std::string osGTIFilename{};
      84             :     bool bRecursive = false;
      85             :     double dfMinPixelSize = std::numeric_limits<double>::quiet_NaN();
      86             :     double dfMaxPixelSize = std::numeric_limits<double>::quiet_NaN();
      87             :     std::vector<GDALTileIndexRasterMetadata> aoFetchMD{};
      88             :     std::set<std::string> oSetFilenameFilters{};
      89             :     GDALProgressFunc pfnProgress = nullptr;
      90             :     void *pProgressData = nullptr;
      91             : };
      92             : 
      93             : /************************************************************************/
      94             : /*                     GDALTileIndexAppOptionsGetParser()               */
      95             : /************************************************************************/
      96             : 
      97          45 : static std::unique_ptr<GDALArgumentParser> GDALTileIndexAppOptionsGetParser(
      98             :     GDALTileIndexOptions *psOptions,
      99             :     GDALTileIndexOptionsForBinary *psOptionsForBinary)
     100             : {
     101             :     auto argParser = std::make_unique<GDALArgumentParser>(
     102          45 :         "gdaltindex", /* bForBinary=*/psOptionsForBinary != nullptr);
     103             : 
     104          45 :     argParser->add_description(
     105          45 :         _("Build a tile index from a list of datasets."));
     106             : 
     107          45 :     argParser->add_epilog(
     108             :         _("For more details, see the full documentation for gdaltindex at\n"
     109          45 :           "https://gdal.org/programs/gdaltindex.html"));
     110             : 
     111             :     // Hidden as used by gdal raster index
     112          45 :     argParser->add_argument("--invoked-from-gdal-raster-index")
     113          45 :         .store_into(psOptions->bInvokedFromGdalRasterIndex)
     114          45 :         .hidden();
     115             : 
     116             :     // Hidden as used by gdal raster index
     117          45 :     argParser->add_argument("-skip_errors")
     118          45 :         .store_into(psOptions->bSkipErrors)
     119          45 :         .hidden();
     120             : 
     121          45 :     argParser->add_argument("-overwrite")
     122          45 :         .flag()
     123          45 :         .store_into(psOptions->bOverwrite)
     124          45 :         .help(_("Overwrite the output tile index file if it already exists."));
     125             : 
     126          45 :     argParser->add_argument("-recursive")
     127          45 :         .flag()
     128          45 :         .store_into(psOptions->bRecursive)
     129             :         .help(_("Whether directories specified in <file_or_dir> should be "
     130          45 :                 "explored recursively."));
     131             : 
     132          45 :     argParser->add_argument("-filename_filter")
     133          90 :         .metavar("<val>")
     134          45 :         .append()
     135          45 :         .store_into(psOptions->oSetFilenameFilters)
     136             :         .help(_("Pattern that the filenames contained in directories pointed "
     137          45 :                 "by <file_or_dir> should follow."));
     138             : 
     139          45 :     argParser->add_argument("-min_pixel_size")
     140          90 :         .metavar("<val>")
     141          45 :         .store_into(psOptions->dfMinPixelSize)
     142             :         .help(_("Minimum pixel size in term of geospatial extent per pixel "
     143          45 :                 "(resolution) that a raster should have to be selected."));
     144             : 
     145          45 :     argParser->add_argument("-max_pixel_size")
     146          90 :         .metavar("<val>")
     147          45 :         .store_into(psOptions->dfMaxPixelSize)
     148             :         .help(_("Maximum pixel size in term of geospatial extent per pixel "
     149          45 :                 "(resolution) that a raster should have to be selected."));
     150             : 
     151          45 :     argParser->add_output_format_argument(psOptions->osFormat);
     152             : 
     153          45 :     argParser->add_argument("-tileindex")
     154          90 :         .metavar("<field_name>")
     155          45 :         .store_into(psOptions->osLocationField)
     156          45 :         .help(_("Name of the layer in the tile index file."));
     157             : 
     158          45 :     argParser->add_argument("-write_absolute_path")
     159          45 :         .flag()
     160          45 :         .store_into(psOptions->bWriteAbsolutePath)
     161             :         .help(_("Write the absolute path of the raster files in the tile index "
     162          45 :                 "file."));
     163             : 
     164          45 :     argParser->add_argument("-skip_different_projection")
     165          45 :         .flag()
     166          45 :         .store_into(psOptions->bSkipDifferentProjection)
     167             :         .help(_(
     168             :             "Only files with the same projection as files already inserted in "
     169          45 :             "the tile index will be inserted (unless -t_srs is specified)."));
     170             : 
     171          45 :     argParser->add_argument("-t_srs")
     172          90 :         .metavar("<srs_def>")
     173          45 :         .store_into(psOptions->osTargetSRS)
     174             :         .help(_("Geometries of input files will be transformed to the desired "
     175          45 :                 "target coordinate reference system."));
     176             : 
     177          45 :     argParser->add_argument("-src_srs_name")
     178          90 :         .metavar("<field_name>")
     179          45 :         .store_into(psOptions->osSrcSRSFieldName)
     180             :         .help(_("Name of the field in the tile index file where the source SRS "
     181          45 :                 "will be stored."));
     182             : 
     183          45 :     argParser->add_argument("-src_srs_format")
     184          90 :         .metavar("{AUTO|WKT|EPSG|PROJ}")
     185          45 :         .choices("AUTO", "WKT", "EPSG", "PROJ")
     186             :         .action(
     187          10 :             [psOptions](const auto &f)
     188             :             {
     189           5 :                 if (f == "WKT")
     190           1 :                     psOptions->eSrcSRSFormat = FORMAT_WKT;
     191           4 :                 else if (f == "EPSG")
     192           1 :                     psOptions->eSrcSRSFormat = FORMAT_EPSG;
     193           3 :                 else if (f == "PROJ")
     194           1 :                     psOptions->eSrcSRSFormat = FORMAT_PROJ;
     195             :                 else
     196           2 :                     psOptions->eSrcSRSFormat = FORMAT_AUTO;
     197          45 :             })
     198          45 :         .help(_("Format of the source SRS to store in the tile index file."));
     199             : 
     200          45 :     argParser->add_argument("-lyr_name")
     201          90 :         .metavar("<name>")
     202          45 :         .store_into(psOptions->osIndexLayerName)
     203          45 :         .help(_("Name of the layer in the tile index file."));
     204             : 
     205          45 :     argParser->add_layer_creation_options_argument(psOptions->aosLCO);
     206             : 
     207             :     // GTI driver options
     208             : 
     209          45 :     argParser->add_argument("-gti_filename")
     210          90 :         .metavar("<filename>")
     211          45 :         .store_into(psOptions->osGTIFilename)
     212          45 :         .help(_("Filename of the XML Virtual Tile Index file to generate."));
     213             : 
     214             :     // NOTE: no store_into
     215          45 :     argParser->add_argument("-tr")
     216          90 :         .metavar("<xres> <yres>")
     217          45 :         .nargs(2)
     218          45 :         .scan<'g', double>()
     219          45 :         .help(_("Set target resolution."));
     220             : 
     221             :     // NOTE: no store_into
     222          45 :     argParser->add_argument("-te")
     223          90 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
     224          45 :         .nargs(4)
     225          45 :         .scan<'g', double>()
     226          45 :         .help(_("Set target extent in SRS unit."));
     227             : 
     228          45 :     argParser->add_argument("-ot")
     229          90 :         .metavar("<datatype>")
     230          45 :         .store_into(psOptions->osDataType)
     231          45 :         .help(_("Output data type."));
     232             : 
     233          45 :     argParser->add_argument("-bandcount")
     234          90 :         .metavar("<val>")
     235          45 :         .store_into(psOptions->osBandCount)
     236          45 :         .help(_("Number of bands of the tiles of the tile index."));
     237             : 
     238          45 :     argParser->add_argument("-nodata")
     239          90 :         .metavar("<val>")
     240          45 :         .append()
     241          45 :         .store_into(psOptions->osNodata)
     242          45 :         .help(_("Nodata value of the tiles of the tile index."));
     243             : 
     244             :     // Should we use choices here?
     245          45 :     argParser->add_argument("-colorinterp")
     246          90 :         .metavar("<val>")
     247          45 :         .append()
     248          45 :         .store_into(psOptions->osColorInterp)
     249             :         .help(_("Color interpretation of of the tiles of the tile index: red, "
     250          45 :                 "green, blue, alpha, gray, undefined."));
     251             : 
     252          45 :     argParser->add_argument("-mask")
     253          45 :         .flag()
     254          45 :         .store_into(psOptions->bMaskBand)
     255          45 :         .help(_("Add a mask band to the tiles of the tile index."));
     256             : 
     257          45 :     argParser->add_argument("-mo")
     258          90 :         .metavar("<name>=<value>")
     259          45 :         .append()
     260          45 :         .store_into(psOptions->aosMetadata)
     261             :         .help(_("Write an arbitrary layer metadata item, for formats that "
     262          45 :                 "support layer metadata."));
     263             : 
     264             :     // NOTE: no store_into
     265          45 :     argParser->add_argument("-fetch_md")
     266          45 :         .nargs(3)
     267          90 :         .metavar("<gdal_md_name> <fld_name> <fld_type>")
     268          45 :         .append()
     269             :         .help("Fetch a metadata item from the raster tile and write it as a "
     270          45 :               "field in the tile index.");
     271             : 
     272          45 :     if (psOptionsForBinary)
     273             :     {
     274           6 :         argParser->add_quiet_argument(&psOptionsForBinary->bQuiet);
     275             : 
     276           6 :         argParser->add_argument("index_file")
     277          12 :             .metavar("<index_file>")
     278           6 :             .store_into(psOptionsForBinary->osDest)
     279           6 :             .help(_("The name of the output file to create/append to."));
     280             : 
     281           6 :         argParser->add_argument("file_or_dir")
     282          12 :             .metavar("<file_or_dir>")
     283           6 :             .nargs(argparse::nargs_pattern::at_least_one)
     284          14 :             .action([psOptionsForBinary](const std::string &s)
     285          20 :                     { psOptionsForBinary->aosSrcFiles.AddString(s.c_str()); })
     286             :             .help(_(
     287             :                 "The input GDAL raster files or directory, can be multiple "
     288           6 :                 "locations separated by spaces. Wildcards may also be used."));
     289             :     }
     290             : 
     291          45 :     return argParser;
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                  GDALTileIndexAppGetParserUsage()                    */
     296             : /************************************************************************/
     297             : 
     298           0 : std::string GDALTileIndexAppGetParserUsage()
     299             : {
     300             :     try
     301             :     {
     302           0 :         GDALTileIndexOptions sOptions;
     303           0 :         GDALTileIndexOptionsForBinary sOptionsForBinary;
     304             :         auto argParser =
     305           0 :             GDALTileIndexAppOptionsGetParser(&sOptions, &sOptionsForBinary);
     306           0 :         return argParser->usage();
     307             :     }
     308           0 :     catch (const std::exception &err)
     309             :     {
     310           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     311           0 :                  err.what());
     312           0 :         return std::string();
     313             :     }
     314             : }
     315             : 
     316             : /************************************************************************/
     317             : /*                        GDALTileIndexTileIterator                     */
     318             : /************************************************************************/
     319             : 
     320             : struct GDALTileIndexTileIterator
     321             : {
     322             :     const GDALTileIndexOptions *psOptions = nullptr;
     323             :     int nSrcCount = 0;
     324             :     const char *const *papszSrcDSNames = nullptr;
     325             :     std::string osCurDir{};
     326             :     int iCurSrc = 0;
     327             :     VSIDIR *psDir = nullptr;
     328             : 
     329             :     CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexTileIterator)
     330             : 
     331          45 :     GDALTileIndexTileIterator(const GDALTileIndexOptions *psOptionsIn,
     332             :                               int nSrcCountIn,
     333             :                               const char *const *papszSrcDSNamesIn)
     334          45 :         : psOptions(psOptionsIn), nSrcCount(nSrcCountIn),
     335          45 :           papszSrcDSNames(papszSrcDSNamesIn)
     336             :     {
     337          45 :     }
     338             : 
     339          27 :     void reset()
     340             :     {
     341          27 :         if (psDir)
     342           4 :             VSICloseDir(psDir);
     343          27 :         psDir = nullptr;
     344          27 :         iCurSrc = 0;
     345          27 :     }
     346             : 
     347        1189 :     std::string next()
     348             :     {
     349             :         while (true)
     350             :         {
     351        1189 :             if (!psDir)
     352             :             {
     353         142 :                 if (iCurSrc == nSrcCount)
     354             :                 {
     355          44 :                     break;
     356             :                 }
     357             : 
     358             :                 VSIStatBufL sStatBuf;
     359          98 :                 const char *pszCurName = papszSrcDSNames[iCurSrc++];
     360         192 :                 if (VSIStatL(pszCurName, &sStatBuf) == 0 &&
     361          94 :                     VSI_ISDIR(sStatBuf.st_mode))
     362             :                 {
     363             :                     auto poSrcDS = std::unique_ptr<GDALDataset>(
     364             :                         GDALDataset::Open(pszCurName, GDAL_OF_RASTER, nullptr,
     365           9 :                                           nullptr, nullptr));
     366           9 :                     if (poSrcDS)
     367           0 :                         return pszCurName;
     368             : 
     369           9 :                     osCurDir = pszCurName;
     370           9 :                     psDir = VSIOpenDir(
     371             :                         osCurDir.c_str(),
     372           9 :                         /*nDepth=*/psOptions->bRecursive ? -1 : 0, nullptr);
     373           9 :                     if (!psDir)
     374             :                     {
     375           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     376             :                                  "Cannot open directory %s", osCurDir.c_str());
     377           0 :                         return std::string();
     378             :                     }
     379             :                 }
     380             :                 else
     381             :                 {
     382          89 :                     return pszCurName;
     383             :                 }
     384             :             }
     385             : 
     386        1056 :             auto psEntry = VSIGetNextDirEntry(psDir);
     387        1056 :             if (!psEntry)
     388             :             {
     389           5 :                 VSICloseDir(psDir);
     390           5 :                 psDir = nullptr;
     391           5 :                 continue;
     392             :             }
     393             : 
     394        1051 :             if (!psOptions->oSetFilenameFilters.empty())
     395             :             {
     396        1041 :                 bool bMatchFound = false;
     397             :                 const std::string osFilenameOnly =
     398        1041 :                     CPLGetFilename(psEntry->pszName);
     399        2075 :                 for (const auto &osFilter : psOptions->oSetFilenameFilters)
     400             :                 {
     401        1041 :                     if (GDALPatternMatch(osFilenameOnly.c_str(),
     402             :                                          osFilter.c_str()))
     403             :                     {
     404           7 :                         bMatchFound = true;
     405           7 :                         break;
     406             :                     }
     407             :                 }
     408        1041 :                 if (!bMatchFound)
     409        1034 :                     continue;
     410             :             }
     411             : 
     412             :             std::string osFilename = CPLFormFilenameSafe(
     413          17 :                 osCurDir.c_str(), psEntry->pszName, nullptr);
     414          17 :             if (VSI_ISDIR(psEntry->nMode))
     415             :             {
     416             :                 auto poSrcDS = std::unique_ptr<GDALDataset>(
     417             :                     GDALDataset::Open(osFilename.c_str(), GDAL_OF_RASTER,
     418           0 :                                       nullptr, nullptr, nullptr));
     419           0 :                 if (poSrcDS)
     420             :                 {
     421           0 :                     return osFilename;
     422             :                 }
     423           0 :                 continue;
     424             :             }
     425             : 
     426          17 :             return osFilename;
     427        1039 :         }
     428          44 :         return std::string();
     429             :     }
     430             : };
     431             : 
     432             : /************************************************************************/
     433             : /*                           GDALTileIndex()                            */
     434             : /************************************************************************/
     435             : 
     436             : /* clang-format off */
     437             : /**
     438             :  * Build a tile index from a list of datasets.
     439             :  *
     440             :  * This is the equivalent of the
     441             :  * <a href="/programs/gdaltindex.html">gdaltindex</a> utility.
     442             :  *
     443             :  * GDALTileIndexOptions* must be allocated and freed with
     444             :  * GDALTileIndexOptionsNew() and GDALTileIndexOptionsFree() respectively.
     445             :  *
     446             :  * @param pszDest the destination dataset path.
     447             :  * @param nSrcCount the number of input datasets.
     448             :  * @param papszSrcDSNames the list of input dataset names
     449             :  * @param psOptionsIn the options struct returned by GDALTileIndexOptionsNew() or
     450             :  * NULL.
     451             :  * @param pbUsageError pointer to a integer output variable to store if any
     452             :  * usage error has occurred.
     453             :  * @return the output dataset (new dataset that must be closed using
     454             :  * GDALClose()) or NULL in case of error.
     455             :  *
     456             :  * @since GDAL3.9
     457             :  */
     458             : /* clang-format on */
     459             : 
     460          29 : GDALDatasetH GDALTileIndex(const char *pszDest, int nSrcCount,
     461             :                            const char *const *papszSrcDSNames,
     462             :                            const GDALTileIndexOptions *psOptionsIn,
     463             :                            int *pbUsageError)
     464             : {
     465          29 :     return GDALTileIndexInternal(pszDest, nullptr, nullptr, nSrcCount,
     466          29 :                                  papszSrcDSNames, psOptionsIn, pbUsageError);
     467             : }
     468             : 
     469          45 : GDALDatasetH GDALTileIndexInternal(const char *pszDest,
     470             :                                    GDALDatasetH hTileIndexDS, OGRLayerH hLayer,
     471             :                                    int nSrcCount,
     472             :                                    const char *const *papszSrcDSNames,
     473             :                                    const GDALTileIndexOptions *psOptionsIn,
     474             :                                    int *pbUsageError)
     475             : {
     476          45 :     if (nSrcCount == 0)
     477             :     {
     478           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No input dataset specified.");
     479             : 
     480           0 :         if (pbUsageError)
     481           0 :             *pbUsageError = TRUE;
     482           0 :         return nullptr;
     483             :     }
     484             : 
     485             :     auto psOptions = psOptionsIn
     486             :                          ? std::make_unique<GDALTileIndexOptions>(*psOptionsIn)
     487          90 :                          : std::make_unique<GDALTileIndexOptions>();
     488             : 
     489             :     GDALTileIndexTileIterator oGDALTileIndexTileIterator(
     490          90 :         psOptions.get(), nSrcCount, papszSrcDSNames);
     491             : 
     492             :     /* -------------------------------------------------------------------- */
     493             :     /*      Create and validate target SRS if given.                        */
     494             :     /* -------------------------------------------------------------------- */
     495          90 :     OGRSpatialReference oTargetSRS;
     496          45 :     if (!psOptions->osTargetSRS.empty())
     497             :     {
     498           9 :         if (psOptions->bSkipDifferentProjection)
     499             :         {
     500           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     501             :                      "-skip_different_projections does not apply "
     502             :                      "when -t_srs is requested.");
     503             :         }
     504           9 :         oTargetSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     505             :         // coverity[tainted_data]
     506           9 :         oTargetSRS.SetFromUserInput(psOptions->osTargetSRS.c_str());
     507             :     }
     508             : 
     509             :     /* -------------------------------------------------------------------- */
     510             :     /*      Open or create the target datasource                            */
     511             :     /* -------------------------------------------------------------------- */
     512             : 
     513          45 :     std::unique_ptr<GDALDataset> poTileIndexDSUnique;
     514          45 :     GDALDataset *poTileIndexDS = GDALDataset::FromHandle(hTileIndexDS);
     515          45 :     OGRLayer *poLayer = OGRLayer::FromHandle(hLayer);
     516          45 :     bool bExistingLayer = false;
     517          90 :     std::string osFormat;
     518             : 
     519          45 :     if (!hTileIndexDS)
     520             :     {
     521          29 :         if (psOptions->bOverwrite)
     522             :         {
     523           5 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     524           5 :             auto hDriver = GDALIdentifyDriver(pszDest, nullptr);
     525           5 :             if (hDriver)
     526           5 :                 GDALDeleteDataset(hDriver, pszDest);
     527             :             else
     528           0 :                 VSIUnlink(pszDest);
     529           5 :             CPLPopErrorHandler();
     530             :         }
     531             : 
     532          29 :         poTileIndexDSUnique.reset(
     533             :             GDALDataset::Open(pszDest, GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr,
     534             :                               nullptr, nullptr));
     535             : 
     536          29 :         if (poTileIndexDSUnique != nullptr)
     537             :         {
     538           7 :             auto poDriver = poTileIndexDSUnique->GetDriver();
     539           7 :             if (poDriver)
     540           7 :                 osFormat = poDriver->GetDescription();
     541             : 
     542           7 :             if (poTileIndexDSUnique->GetLayerCount() == 1)
     543             :             {
     544           7 :                 poLayer = poTileIndexDSUnique->GetLayer(0);
     545             :             }
     546             :             else
     547             :             {
     548           0 :                 if (psOptions->osIndexLayerName.empty())
     549             :                 {
     550           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     551             :                              "Multiple layers detected: -lyr_name must be "
     552             :                              "specified.");
     553           0 :                     if (pbUsageError)
     554           0 :                         *pbUsageError = true;
     555           0 :                     return nullptr;
     556             :                 }
     557           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
     558           0 :                 poLayer = poTileIndexDSUnique->GetLayerByName(
     559           0 :                     psOptions->osIndexLayerName.c_str());
     560           0 :                 CPLPopErrorHandler();
     561             :             }
     562             :         }
     563             :         else
     564             :         {
     565          22 :             if (psOptions->osFormat.empty())
     566             :             {
     567             :                 const auto aoDrivers =
     568          21 :                     GetOutputDriversFor(pszDest, GDAL_OF_VECTOR);
     569          21 :                 if (aoDrivers.empty())
     570             :                 {
     571           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     572             :                              "Cannot guess driver for %s", pszDest);
     573           0 :                     return nullptr;
     574             :                 }
     575             :                 else
     576             :                 {
     577          21 :                     if (aoDrivers.size() > 1)
     578             :                     {
     579           0 :                         CPLError(
     580             :                             CE_Warning, CPLE_AppDefined,
     581             :                             "Several drivers matching %s extension. Using %s",
     582           0 :                             CPLGetExtensionSafe(pszDest).c_str(),
     583           0 :                             aoDrivers[0].c_str());
     584             :                     }
     585          21 :                     osFormat = aoDrivers[0];
     586             :                 }
     587             :             }
     588             :             else
     589             :             {
     590           1 :                 osFormat = psOptions->osFormat;
     591             :             }
     592             : 
     593             :             auto poDriver =
     594          22 :                 GetGDALDriverManager()->GetDriverByName(osFormat.c_str());
     595          22 :             if (poDriver == nullptr)
     596             :             {
     597           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     598             :                          "%s driver not available.", osFormat.c_str());
     599           0 :                 return nullptr;
     600             :             }
     601             : 
     602          22 :             poTileIndexDSUnique.reset(
     603             :                 poDriver->Create(pszDest, 0, 0, 0, GDT_Unknown, nullptr));
     604          22 :             if (!poTileIndexDSUnique)
     605           0 :                 return nullptr;
     606             :         }
     607             : 
     608          29 :         poTileIndexDS = poTileIndexDSUnique.get();
     609             :     }
     610             : 
     611          45 :     auto poOutDrv = poTileIndexDS->GetDriver();
     612          45 :     if (osFormat.empty() && poOutDrv)
     613          16 :         osFormat = poOutDrv->GetDescription();
     614             : 
     615             :     const char *pszVal =
     616          45 :         poOutDrv ? poOutDrv->GetMetadataItem(GDAL_DMD_MAX_STRING_LENGTH)
     617          45 :                  : nullptr;
     618          45 :     const int nMaxFieldSize = pszVal ? atoi(pszVal) : 0;
     619             : 
     620             :     const bool bFailOnErrors =
     621          45 :         psOptions->bInvokedFromGdalRasterIndex && !psOptions->bSkipErrors;
     622          45 :     bool bSkipFirstTile = false;
     623             : 
     624          45 :     if (poLayer)
     625             :     {
     626           9 :         bExistingLayer = true;
     627             :     }
     628             :     else
     629             :     {
     630          36 :         std::string osLayerName;
     631          36 :         if (psOptions->osIndexLayerName.empty())
     632             :         {
     633             :             VSIStatBuf sStat;
     634          22 :             if (EQUAL(osFormat.c_str(), "ESRI Shapefile") ||
     635           3 :                 VSIStat(pszDest, &sStat) == 0)
     636             :             {
     637          19 :                 osLayerName = CPLGetBasenameSafe(pszDest);
     638             :             }
     639             :             else
     640             :             {
     641           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     642             :                          "-lyr_name must be specified.");
     643           0 :                 if (pbUsageError)
     644           0 :                     *pbUsageError = true;
     645           0 :                 return nullptr;
     646             :             }
     647             :         }
     648             :         else
     649             :         {
     650          17 :             if (psOptions->bOverwrite)
     651             :             {
     652           0 :                 for (int i = 0; i < poTileIndexDS->GetLayerCount(); ++i)
     653             :                 {
     654           0 :                     auto poExistingLayer = poTileIndexDS->GetLayer(i);
     655           0 :                     if (poExistingLayer && poExistingLayer->GetName() ==
     656           0 :                                                psOptions->osIndexLayerName)
     657             :                     {
     658           0 :                         if (poTileIndexDS->DeleteLayer(i) != OGRERR_NONE)
     659           0 :                             return nullptr;
     660           0 :                         break;
     661             :                     }
     662             :                 }
     663             :             }
     664             : 
     665          17 :             osLayerName = psOptions->osIndexLayerName;
     666             :         }
     667             : 
     668             :         /* get spatial reference for output file from target SRS (if set) */
     669             :         /* or from first input file */
     670          36 :         OGRSpatialReference oSRS;
     671          36 :         if (!oTargetSRS.IsEmpty())
     672             :         {
     673           8 :             oSRS = oTargetSRS;
     674             :         }
     675             :         else
     676             :         {
     677          28 :             std::string osFilename = oGDALTileIndexTileIterator.next();
     678          28 :             if (osFilename.empty())
     679             :             {
     680           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find any tile");
     681           1 :                 return nullptr;
     682             :             }
     683          27 :             oGDALTileIndexTileIterator.reset();
     684             :             std::unique_ptr<CPLTurnFailureIntoWarningBackuper>
     685           0 :                 poFailureIntoWarning;
     686          27 :             if (!bFailOnErrors)
     687             :                 poFailureIntoWarning =
     688          17 :                     std::make_unique<CPLTurnFailureIntoWarningBackuper>();
     689          27 :             CPL_IGNORE_RET_VAL(poFailureIntoWarning);
     690             :             auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     691             :                 osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
     692          27 :                 nullptr, nullptr, nullptr));
     693          27 :             if (!poSrcDS)
     694             :             {
     695           1 :                 CPLError(bFailOnErrors ? CE_Failure : CE_Warning,
     696             :                          CPLE_AppDefined, "Unable to open %s%s.",
     697             :                          osFilename.c_str(), bFailOnErrors ? "" : ", skipping");
     698           1 :                 if (bFailOnErrors)
     699           0 :                     return nullptr;
     700           1 :                 bSkipFirstTile = true;
     701             :             }
     702             :             else
     703             :             {
     704          26 :                 auto poSrcSRS = poSrcDS->GetSpatialRef();
     705          26 :                 if (poSrcSRS)
     706          26 :                     oSRS = *poSrcSRS;
     707             :             }
     708             :         }
     709             : 
     710          35 :         poLayer = poTileIndexDS->CreateLayer(
     711          35 :             osLayerName.c_str(), oSRS.IsEmpty() ? nullptr : &oSRS, wkbPolygon,
     712          35 :             psOptions->aosLCO.List());
     713          35 :         if (!poLayer)
     714           0 :             return nullptr;
     715             : 
     716          35 :         OGRFieldDefn oLocationField(psOptions->osLocationField.c_str(),
     717          35 :                                     OFTString);
     718          35 :         oLocationField.SetWidth(nMaxFieldSize);
     719          35 :         if (poLayer->CreateField(&oLocationField) != OGRERR_NONE)
     720           0 :             return nullptr;
     721             : 
     722          35 :         if (!psOptions->osSrcSRSFieldName.empty())
     723             :         {
     724           6 :             OGRFieldDefn oSrcSRSField(psOptions->osSrcSRSFieldName.c_str(),
     725           6 :                                       OFTString);
     726           6 :             oSrcSRSField.SetWidth(nMaxFieldSize);
     727           6 :             if (poLayer->CreateField(&oSrcSRSField) != OGRERR_NONE)
     728           0 :                 return nullptr;
     729             :         }
     730             :     }
     731             : 
     732          44 :     auto poLayerDefn = poLayer->GetLayerDefn();
     733             : 
     734          49 :     for (const auto &oFetchMD : psOptions->aoFetchMD)
     735             :     {
     736           5 :         if (poLayerDefn->GetFieldIndex(oFetchMD.osFieldName.c_str()) < 0)
     737             :         {
     738           5 :             OGRFieldDefn oField(oFetchMD.osFieldName.c_str(), oFetchMD.eType);
     739           5 :             if (poLayer->CreateField(&oField) != OGRERR_NONE)
     740           0 :                 return nullptr;
     741             :         }
     742             :     }
     743             : 
     744          44 :     if (!psOptions->osGTIFilename.empty())
     745             :     {
     746           2 :         if (!psOptions->aosMetadata.empty())
     747             :         {
     748           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     749             :                      "-mo is not supported when -gti_filename is used");
     750           0 :             return nullptr;
     751             :         }
     752             :         CPLXMLNode *psRoot =
     753           2 :             CPLCreateXMLNode(nullptr, CXT_Element, "GDALTileIndexDataset");
     754           2 :         CPLCreateXMLElementAndValue(psRoot, "IndexDataset", pszDest);
     755           2 :         CPLCreateXMLElementAndValue(psRoot, "IndexLayer", poLayer->GetName());
     756           2 :         CPLCreateXMLElementAndValue(psRoot, "LocationField",
     757           2 :                                     psOptions->osLocationField.c_str());
     758           2 :         if (!std::isnan(psOptions->xres))
     759             :         {
     760           1 :             CPLCreateXMLElementAndValue(psRoot, "ResX",
     761           1 :                                         CPLSPrintf("%.18g", psOptions->xres));
     762           1 :             CPLCreateXMLElementAndValue(psRoot, "ResY",
     763           1 :                                         CPLSPrintf("%.18g", psOptions->yres));
     764             :         }
     765           2 :         if (!std::isnan(psOptions->xmin))
     766             :         {
     767           1 :             CPLCreateXMLElementAndValue(psRoot, "MinX",
     768           1 :                                         CPLSPrintf("%.18g", psOptions->xmin));
     769           1 :             CPLCreateXMLElementAndValue(psRoot, "MinY",
     770           1 :                                         CPLSPrintf("%.18g", psOptions->ymin));
     771           1 :             CPLCreateXMLElementAndValue(psRoot, "MaxX",
     772           1 :                                         CPLSPrintf("%.18g", psOptions->xmax));
     773           1 :             CPLCreateXMLElementAndValue(psRoot, "MaxY",
     774           1 :                                         CPLSPrintf("%.18g", psOptions->ymax));
     775             :         }
     776             : 
     777           2 :         int nBandCount = 0;
     778           2 :         if (!psOptions->osBandCount.empty())
     779             :         {
     780           0 :             nBandCount = atoi(psOptions->osBandCount.c_str());
     781             :         }
     782             :         else
     783             :         {
     784           2 :             if (!psOptions->osDataType.empty())
     785             :             {
     786           0 :                 nBandCount = std::max(
     787             :                     nBandCount,
     788           0 :                     CPLStringList(CSLTokenizeString2(
     789           0 :                                       psOptions->osDataType.c_str(), ", ", 0))
     790           0 :                         .size());
     791             :             }
     792           2 :             if (!psOptions->osNodata.empty())
     793             :             {
     794           1 :                 nBandCount = std::max(
     795             :                     nBandCount,
     796           2 :                     CPLStringList(CSLTokenizeString2(
     797           1 :                                       psOptions->osNodata.c_str(), ", ", 0))
     798           1 :                         .size());
     799             :             }
     800           2 :             if (!psOptions->osColorInterp.empty())
     801             :             {
     802           1 :                 nBandCount =
     803           1 :                     std::max(nBandCount,
     804           2 :                              CPLStringList(
     805             :                                  CSLTokenizeString2(
     806           1 :                                      psOptions->osColorInterp.c_str(), ", ", 0))
     807           1 :                                  .size());
     808             :             }
     809             :         }
     810             : 
     811           3 :         for (int i = 0; i < nBandCount; ++i)
     812             :         {
     813           1 :             auto psBand = CPLCreateXMLNode(psRoot, CXT_Element, "Band");
     814           1 :             CPLAddXMLAttributeAndValue(psBand, "band", CPLSPrintf("%d", i + 1));
     815           1 :             if (!psOptions->osDataType.empty())
     816             :             {
     817             :                 const CPLStringList aosTokens(
     818           0 :                     CSLTokenizeString2(psOptions->osDataType.c_str(), ", ", 0));
     819           0 :                 if (aosTokens.size() == 1)
     820           0 :                     CPLAddXMLAttributeAndValue(psBand, "dataType",
     821             :                                                aosTokens[0]);
     822           0 :                 else if (i < aosTokens.size())
     823           0 :                     CPLAddXMLAttributeAndValue(psBand, "dataType",
     824             :                                                aosTokens[i]);
     825             :             }
     826           1 :             if (!psOptions->osNodata.empty())
     827             :             {
     828             :                 const CPLStringList aosTokens(
     829           2 :                     CSLTokenizeString2(psOptions->osNodata.c_str(), ", ", 0));
     830           1 :                 if (aosTokens.size() == 1)
     831           1 :                     CPLCreateXMLElementAndValue(psBand, "NoDataValue",
     832             :                                                 aosTokens[0]);
     833           0 :                 else if (i < aosTokens.size())
     834           0 :                     CPLCreateXMLElementAndValue(psBand, "NoDataValue",
     835             :                                                 aosTokens[i]);
     836             :             }
     837           1 :             if (!psOptions->osColorInterp.empty())
     838             :             {
     839             :                 const CPLStringList aosTokens(CSLTokenizeString2(
     840           2 :                     psOptions->osColorInterp.c_str(), ", ", 0));
     841           1 :                 if (aosTokens.size() == 1)
     842           1 :                     CPLCreateXMLElementAndValue(psBand, "ColorInterp",
     843             :                                                 aosTokens[0]);
     844           0 :                 else if (i < aosTokens.size())
     845           0 :                     CPLCreateXMLElementAndValue(psBand, "ColorInterp",
     846             :                                                 aosTokens[i]);
     847             :             }
     848             :         }
     849             : 
     850           2 :         if (psOptions->bMaskBand)
     851             :         {
     852           1 :             CPLCreateXMLElementAndValue(psRoot, "MaskBand", "true");
     853             :         }
     854             :         int res =
     855           2 :             CPLSerializeXMLTreeToFile(psRoot, psOptions->osGTIFilename.c_str());
     856           2 :         CPLDestroyXMLNode(psRoot);
     857           2 :         if (!res)
     858           0 :             return nullptr;
     859             :     }
     860             :     else
     861             :     {
     862          42 :         poLayer->SetMetadataItem("LOCATION_FIELD",
     863          42 :                                  psOptions->osLocationField.c_str());
     864          42 :         if (!std::isnan(psOptions->xres))
     865             :         {
     866           2 :             poLayer->SetMetadataItem("RESX",
     867           2 :                                      CPLSPrintf("%.18g", psOptions->xres));
     868           2 :             poLayer->SetMetadataItem("RESY",
     869           2 :                                      CPLSPrintf("%.18g", psOptions->yres));
     870             :         }
     871          42 :         if (!std::isnan(psOptions->xmin))
     872             :         {
     873           2 :             poLayer->SetMetadataItem("MINX",
     874           2 :                                      CPLSPrintf("%.18g", psOptions->xmin));
     875           2 :             poLayer->SetMetadataItem("MINY",
     876           2 :                                      CPLSPrintf("%.18g", psOptions->ymin));
     877           2 :             poLayer->SetMetadataItem("MAXX",
     878           2 :                                      CPLSPrintf("%.18g", psOptions->xmax));
     879           2 :             poLayer->SetMetadataItem("MAXY",
     880           2 :                                      CPLSPrintf("%.18g", psOptions->ymax));
     881             :         }
     882          42 :         if (!psOptions->osBandCount.empty())
     883             :         {
     884           2 :             poLayer->SetMetadataItem("BAND_COUNT",
     885           2 :                                      psOptions->osBandCount.c_str());
     886             :         }
     887          42 :         if (!psOptions->osDataType.empty())
     888             :         {
     889           2 :             poLayer->SetMetadataItem("DATA_TYPE",
     890           2 :                                      psOptions->osDataType.c_str());
     891             :         }
     892          42 :         if (!psOptions->osNodata.empty())
     893             :         {
     894           2 :             poLayer->SetMetadataItem("NODATA", psOptions->osNodata.c_str());
     895             :         }
     896          42 :         if (!psOptions->osColorInterp.empty())
     897             :         {
     898           2 :             poLayer->SetMetadataItem("COLOR_INTERPRETATION",
     899           2 :                                      psOptions->osColorInterp.c_str());
     900             :         }
     901          42 :         if (psOptions->bMaskBand)
     902             :         {
     903           2 :             poLayer->SetMetadataItem("MASK_BAND", "YES");
     904             :         }
     905          84 :         const CPLStringList aosMetadata(psOptions->aosMetadata);
     906           4 :         for (const auto &[pszKey, pszValue] :
     907          46 :              cpl::IterateNameValue(aosMetadata))
     908             :         {
     909           2 :             poLayer->SetMetadataItem(pszKey, pszValue);
     910             :         }
     911             :     }
     912             : 
     913             :     const int ti_field =
     914          44 :         poLayerDefn->GetFieldIndex(psOptions->osLocationField.c_str());
     915          44 :     if (ti_field < 0)
     916             :     {
     917           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     918             :                  "Unable to find field `%s' in file `%s'.",
     919           0 :                  psOptions->osLocationField.c_str(), pszDest);
     920           0 :         return nullptr;
     921             :     }
     922             : 
     923          44 :     int i_SrcSRSName = -1;
     924          44 :     if (!psOptions->osSrcSRSFieldName.empty())
     925             :     {
     926             :         i_SrcSRSName =
     927           6 :             poLayerDefn->GetFieldIndex(psOptions->osSrcSRSFieldName.c_str());
     928           6 :         if (i_SrcSRSName < 0)
     929             :         {
     930           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     931             :                      "Unable to find field `%s' in file `%s'.",
     932           0 :                      psOptions->osSrcSRSFieldName.c_str(), pszDest);
     933           0 :             return nullptr;
     934             :         }
     935             :     }
     936             : 
     937             :     // Load in memory existing file names in tile index.
     938          88 :     std::set<std::string> oSetExistingFiles;
     939          88 :     OGRSpatialReference oAlreadyExistingSRS;
     940          44 :     if (bExistingLayer)
     941             :     {
     942          31 :         for (auto &&poFeature : poLayer)
     943             :         {
     944          22 :             if (poFeature->IsFieldSetAndNotNull(ti_field))
     945             :             {
     946          22 :                 if (oSetExistingFiles.empty())
     947             :                 {
     948             :                     auto poSrcDS =
     949             :                         std::unique_ptr<GDALDataset>(GDALDataset::Open(
     950             :                             poFeature->GetFieldAsString(ti_field),
     951          18 :                             GDAL_OF_RASTER, nullptr, nullptr, nullptr));
     952           9 :                     if (poSrcDS)
     953             :                     {
     954           9 :                         auto poSrcSRS = poSrcDS->GetSpatialRef();
     955           9 :                         if (poSrcSRS)
     956           9 :                             oAlreadyExistingSRS = *poSrcSRS;
     957             :                     }
     958             :                 }
     959          22 :                 oSetExistingFiles.insert(poFeature->GetFieldAsString(ti_field));
     960             :             }
     961             :         }
     962             :     }
     963             : 
     964          88 :     std::string osCurrentPath;
     965          44 :     if (psOptions->bWriteAbsolutePath)
     966             :     {
     967           2 :         char *pszCurrentPath = CPLGetCurrentDir();
     968           2 :         if (pszCurrentPath)
     969             :         {
     970           2 :             osCurrentPath = pszCurrentPath;
     971             :         }
     972             :         else
     973             :         {
     974           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     975             :                      "This system does not support the CPLGetCurrentDir call. "
     976             :                      "The option -bWriteAbsolutePath will have no effect.");
     977             :         }
     978           2 :         CPLFree(pszCurrentPath);
     979             :     }
     980             : 
     981             :     const bool bIsGTIContext =
     982          85 :         !std::isnan(psOptions->xres) || !std::isnan(psOptions->xmin) ||
     983          41 :         !psOptions->osBandCount.empty() || !psOptions->osNodata.empty() ||
     984          41 :         !psOptions->osColorInterp.empty() || !psOptions->osDataType.empty() ||
     985         124 :         psOptions->bMaskBand || !psOptions->aosMetadata.empty() ||
     986          39 :         !psOptions->osGTIFilename.empty();
     987             : 
     988             :     /* -------------------------------------------------------------------- */
     989             :     /*      loop over GDAL files, processing.                               */
     990             :     /* -------------------------------------------------------------------- */
     991          44 :     int iCur = 0;
     992          44 :     int nTotal = nSrcCount + 1;
     993             :     while (true)
     994             :     {
     995         122 :         const std::string osSrcFilename = oGDALTileIndexTileIterator.next();
     996         122 :         if (osSrcFilename.empty())
     997          43 :             break;
     998          79 :         if (bSkipFirstTile)
     999             :         {
    1000           1 :             bSkipFirstTile = false;
    1001           1 :             continue;
    1002             :         }
    1003             : 
    1004          78 :         std::string osFileNameToWrite;
    1005             :         VSIStatBuf sStatBuf;
    1006             : 
    1007             :         // Make sure it is a file before building absolute path name.
    1008          78 :         if (!osCurrentPath.empty() &&
    1009          80 :             CPLIsFilenameRelative(osSrcFilename.c_str()) &&
    1010           2 :             VSIStat(osSrcFilename.c_str(), &sStatBuf) == 0)
    1011             :         {
    1012           4 :             osFileNameToWrite = CPLProjectRelativeFilenameSafe(
    1013           2 :                 osCurrentPath.c_str(), osSrcFilename.c_str());
    1014             :         }
    1015             :         else
    1016             :         {
    1017          76 :             osFileNameToWrite = osSrcFilename.c_str();
    1018             :         }
    1019             : 
    1020             :         // Checks that file is not already in tileindex.
    1021          78 :         if (oSetExistingFiles.find(osFileNameToWrite) !=
    1022         156 :             oSetExistingFiles.end())
    1023             :         {
    1024           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1025             :                      "File %s is already in tileindex. Skipping it.",
    1026             :                      osFileNameToWrite.c_str());
    1027           4 :             continue;
    1028             :         }
    1029             : 
    1030           0 :         std::unique_ptr<GDALDataset> poSrcDS;
    1031             :         {
    1032             :             std::unique_ptr<CPLTurnFailureIntoWarningBackuper>
    1033           0 :                 poFailureIntoWarning;
    1034          74 :             if (!bFailOnErrors)
    1035             :                 poFailureIntoWarning =
    1036          60 :                     std::make_unique<CPLTurnFailureIntoWarningBackuper>();
    1037          74 :             CPL_IGNORE_RET_VAL(poFailureIntoWarning);
    1038             : 
    1039          74 :             poSrcDS.reset(GDALDataset::Open(
    1040             :                 osSrcFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    1041             :                 nullptr, nullptr, nullptr));
    1042          74 :             if (poSrcDS == nullptr)
    1043             :             {
    1044           2 :                 CPLError(bFailOnErrors ? CE_Failure : CE_Warning,
    1045             :                          CPLE_AppDefined, "Unable to open %s%s.",
    1046             :                          osSrcFilename.c_str(),
    1047             :                          bFailOnErrors ? "" : ", skipping");
    1048           2 :                 if (bFailOnErrors)
    1049           1 :                     return nullptr;
    1050           1 :                 continue;
    1051             :             }
    1052             :         }
    1053             : 
    1054          72 :         GDALGeoTransform gt;
    1055          72 :         if (poSrcDS->GetGeoTransform(gt) != CE_None)
    1056             :         {
    1057           0 :             CPLError(bFailOnErrors ? CE_Failure : CE_Warning, CPLE_AppDefined,
    1058             :                      "It appears no georeferencing is available for\n"
    1059             :                      "`%s'%s.",
    1060             :                      osSrcFilename.c_str(), bFailOnErrors ? "" : ", skipping");
    1061           0 :             if (bFailOnErrors)
    1062           0 :                 return nullptr;
    1063           0 :             continue;
    1064             :         }
    1065             : 
    1066          72 :         auto poSrcSRS = poSrcDS->GetSpatialRef();
    1067             :         // If not set target srs, test that the current file uses same
    1068             :         // projection as others.
    1069          72 :         if (oTargetSRS.IsEmpty())
    1070             :         {
    1071          60 :             if (!oAlreadyExistingSRS.IsEmpty())
    1072             :             {
    1073          68 :                 if (poSrcSRS == nullptr ||
    1074          34 :                     !poSrcSRS->IsSame(&oAlreadyExistingSRS))
    1075             :                 {
    1076           1 :                     CPLError(
    1077             :                         CE_Warning, CPLE_AppDefined,
    1078             :                         "%s is not using the same projection system "
    1079             :                         "as other files in the tileindex.\n"
    1080             :                         "This may cause problems when using it in MapServer "
    1081             :                         "for example.\n"
    1082             :                         "Use -t_srs option to set target projection system. %s",
    1083             :                         osSrcFilename.c_str(),
    1084           1 :                         psOptions->bSkipDifferentProjection
    1085             :                             ? "Skipping this file."
    1086             :                             : "");
    1087           1 :                     if (psOptions->bSkipDifferentProjection)
    1088             :                     {
    1089           1 :                         continue;
    1090             :                     }
    1091             :                 }
    1092             :             }
    1093             :             else
    1094             :             {
    1095          26 :                 if (poSrcSRS)
    1096          26 :                     oAlreadyExistingSRS = *poSrcSRS;
    1097             :             }
    1098             :         }
    1099             : 
    1100          71 :         const int nXSize = poSrcDS->GetRasterXSize();
    1101          71 :         const int nYSize = poSrcDS->GetRasterYSize();
    1102          71 :         if (nXSize == 0 || nYSize == 0)
    1103             :         {
    1104           0 :             CPLError(bFailOnErrors ? CE_Failure : CE_Warning, CPLE_AppDefined,
    1105             :                      "%s has 0 width or height%s", osSrcFilename.c_str(),
    1106             :                      bFailOnErrors ? "" : ", skipping");
    1107           0 :             if (bFailOnErrors)
    1108           0 :                 return nullptr;
    1109           0 :             continue;
    1110             :         }
    1111             : 
    1112          71 :         double adfX[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
    1113          71 :         double adfY[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
    1114          71 :         adfX[0] = gt[0] + 0 * gt[1] + 0 * gt[2];
    1115          71 :         adfY[0] = gt[3] + 0 * gt[4] + 0 * gt[5];
    1116             : 
    1117          71 :         adfX[1] = gt[0] + nXSize * gt[1] + 0 * gt[2];
    1118          71 :         adfY[1] = gt[3] + nXSize * gt[4] + 0 * gt[5];
    1119             : 
    1120          71 :         adfX[2] = gt[0] + nXSize * gt[1] + nYSize * gt[2];
    1121          71 :         adfY[2] = gt[3] + nXSize * gt[4] + nYSize * gt[5];
    1122             : 
    1123          71 :         adfX[3] = gt[0] + 0 * gt[1] + nYSize * gt[2];
    1124          71 :         adfY[3] = gt[3] + 0 * gt[4] + nYSize * gt[5];
    1125             : 
    1126          71 :         adfX[4] = gt[0] + 0 * gt[1] + 0 * gt[2];
    1127          71 :         adfY[4] = gt[3] + 0 * gt[4] + 0 * gt[5];
    1128             : 
    1129             :         // If set target srs, do the forward transformation of all points.
    1130          71 :         if (!oTargetSRS.IsEmpty() && poSrcSRS)
    1131             :         {
    1132          12 :             if (!poSrcSRS->IsSame(&oTargetSRS))
    1133             :             {
    1134             :                 auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    1135           7 :                     OGRCreateCoordinateTransformation(poSrcSRS, &oTargetSRS));
    1136           7 :                 if (!poCT || !poCT->Transform(5, adfX, adfY, nullptr))
    1137             :                 {
    1138           0 :                     CPLError(bFailOnErrors ? CE_Failure : CE_Warning,
    1139             :                              CPLE_AppDefined,
    1140             :                              "unable to transform points from source "
    1141             :                              "SRS `%s' to target SRS `%s' for file `%s'%s",
    1142             :                              poSrcDS->GetProjectionRef(),
    1143           0 :                              psOptions->osTargetSRS.c_str(),
    1144             :                              osFileNameToWrite.c_str(),
    1145             :                              bFailOnErrors ? "" : ", skipping");
    1146           0 :                     if (bFailOnErrors)
    1147           0 :                         return nullptr;
    1148           0 :                     continue;
    1149             :                 }
    1150             :             }
    1151             :         }
    1152          71 :         else if (bIsGTIContext && !oAlreadyExistingSRS.IsEmpty() &&
    1153          12 :                  (poSrcSRS == nullptr ||
    1154          12 :                   !poSrcSRS->IsSame(&oAlreadyExistingSRS)))
    1155             :         {
    1156           0 :             CPLError(
    1157             :                 CE_Failure, CPLE_AppDefined,
    1158             :                 "%s is not using the same projection system "
    1159             :                 "as other files in the tileindex. This is not compatible of "
    1160             :                 "GTI use. Use -t_srs option to reproject tile extents "
    1161             :                 "to a common SRS.",
    1162             :                 osSrcFilename.c_str());
    1163           0 :             return nullptr;
    1164             :         }
    1165             : 
    1166             :         const double dfMinX =
    1167          71 :             std::min(std::min(adfX[0], adfX[1]), std::min(adfX[2], adfX[3]));
    1168             :         const double dfMinY =
    1169          71 :             std::min(std::min(adfY[0], adfY[1]), std::min(adfY[2], adfY[3]));
    1170             :         const double dfMaxX =
    1171          71 :             std::max(std::max(adfX[0], adfX[1]), std::max(adfX[2], adfX[3]));
    1172             :         const double dfMaxY =
    1173          71 :             std::max(std::max(adfY[0], adfY[1]), std::max(adfY[2], adfY[3]));
    1174             :         const double dfRes =
    1175          71 :             sqrt((dfMaxX - dfMinX) * (dfMaxY - dfMinY) / nXSize / nYSize);
    1176          81 :         if (!std::isnan(psOptions->dfMinPixelSize) &&
    1177          10 :             dfRes < psOptions->dfMinPixelSize)
    1178             :         {
    1179           5 :             CPLError(CE_Warning, CPLE_AppDefined,
    1180             :                      "%s has %f as pixel size (< %f). Skipping",
    1181           5 :                      osSrcFilename.c_str(), dfRes, psOptions->dfMinPixelSize);
    1182           5 :             continue;
    1183             :         }
    1184          74 :         if (!std::isnan(psOptions->dfMaxPixelSize) &&
    1185           8 :             dfRes > psOptions->dfMaxPixelSize)
    1186             :         {
    1187           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1188             :                      "%s has %f as pixel size (> %f). Skipping",
    1189           4 :                      osSrcFilename.c_str(), dfRes, psOptions->dfMaxPixelSize);
    1190           4 :             continue;
    1191             :         }
    1192             : 
    1193          62 :         auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
    1194          62 :         poFeature->SetField(ti_field, osFileNameToWrite.c_str());
    1195             : 
    1196          62 :         if (i_SrcSRSName >= 0 && poSrcSRS)
    1197             :         {
    1198          11 :             const char *pszAuthorityCode = poSrcSRS->GetAuthorityCode(nullptr);
    1199          11 :             const char *pszAuthorityName = poSrcSRS->GetAuthorityName(nullptr);
    1200          11 :             if (psOptions->eSrcSRSFormat == FORMAT_AUTO)
    1201             :             {
    1202           5 :                 if (pszAuthorityName != nullptr && pszAuthorityCode != nullptr)
    1203             :                 {
    1204           5 :                     poFeature->SetField(i_SrcSRSName,
    1205             :                                         CPLSPrintf("%s:%s", pszAuthorityName,
    1206             :                                                    pszAuthorityCode));
    1207             :                 }
    1208           0 :                 else if (nMaxFieldSize == 0 ||
    1209           0 :                          strlen(poSrcDS->GetProjectionRef()) <=
    1210           0 :                              static_cast<size_t>(nMaxFieldSize))
    1211             :                 {
    1212           0 :                     poFeature->SetField(i_SrcSRSName,
    1213             :                                         poSrcDS->GetProjectionRef());
    1214             :                 }
    1215             :                 else
    1216             :                 {
    1217           0 :                     char *pszProj4 = nullptr;
    1218           0 :                     if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE)
    1219             :                     {
    1220           0 :                         poFeature->SetField(i_SrcSRSName, pszProj4);
    1221             :                     }
    1222             :                     else
    1223             :                     {
    1224           0 :                         poFeature->SetField(i_SrcSRSName,
    1225             :                                             poSrcDS->GetProjectionRef());
    1226             :                     }
    1227           0 :                     CPLFree(pszProj4);
    1228             :                 }
    1229             :             }
    1230           6 :             else if (psOptions->eSrcSRSFormat == FORMAT_WKT)
    1231             :             {
    1232           4 :                 if (nMaxFieldSize == 0 ||
    1233           2 :                     strlen(poSrcDS->GetProjectionRef()) <=
    1234           2 :                         static_cast<size_t>(nMaxFieldSize))
    1235             :                 {
    1236           0 :                     poFeature->SetField(i_SrcSRSName,
    1237             :                                         poSrcDS->GetProjectionRef());
    1238             :                 }
    1239             :                 else
    1240             :                 {
    1241           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1242             :                              "Cannot write WKT for file %s as it is too long!",
    1243             :                              osFileNameToWrite.c_str());
    1244             :                 }
    1245             :             }
    1246           4 :             else if (psOptions->eSrcSRSFormat == FORMAT_PROJ)
    1247             :             {
    1248           2 :                 char *pszProj4 = nullptr;
    1249           2 :                 if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE)
    1250             :                 {
    1251           2 :                     poFeature->SetField(i_SrcSRSName, pszProj4);
    1252             :                 }
    1253           2 :                 CPLFree(pszProj4);
    1254             :             }
    1255           2 :             else if (psOptions->eSrcSRSFormat == FORMAT_EPSG)
    1256             :             {
    1257           2 :                 if (pszAuthorityName != nullptr && pszAuthorityCode != nullptr)
    1258           2 :                     poFeature->SetField(i_SrcSRSName,
    1259             :                                         CPLSPrintf("%s:%s", pszAuthorityName,
    1260             :                                                    pszAuthorityCode));
    1261             :             }
    1262             :         }
    1263             : 
    1264          67 :         for (const auto &oFetchMD : psOptions->aoFetchMD)
    1265             :         {
    1266           5 :             if (EQUAL(oFetchMD.osRasterItemName.c_str(), "{PIXEL_SIZE}"))
    1267             :             {
    1268           1 :                 poFeature->SetField(oFetchMD.osFieldName.c_str(), dfRes);
    1269           1 :                 continue;
    1270             :             }
    1271             : 
    1272             :             const char *pszMD =
    1273           4 :                 poSrcDS->GetMetadataItem(oFetchMD.osRasterItemName.c_str());
    1274           4 :             if (pszMD)
    1275             :             {
    1276           4 :                 if (EQUAL(oFetchMD.osRasterItemName.c_str(),
    1277             :                           "TIFFTAG_DATETIME"))
    1278             :                 {
    1279             :                     int nYear, nMonth, nDay, nHour, nMin, nSec;
    1280           2 :                     if (sscanf(pszMD, "%04d:%02d:%02d %02d:%02d:%02d", &nYear,
    1281           1 :                                &nMonth, &nDay, &nHour, &nMin, &nSec) == 6)
    1282             :                     {
    1283           1 :                         poFeature->SetField(
    1284             :                             oFetchMD.osFieldName.c_str(),
    1285             :                             CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", nYear,
    1286             :                                        nMonth, nDay, nHour, nMin, nSec));
    1287           1 :                         continue;
    1288             :                     }
    1289             :                 }
    1290           3 :                 poFeature->SetField(oFetchMD.osFieldName.c_str(), pszMD);
    1291             :             }
    1292             :         }
    1293             : 
    1294          62 :         auto poPoly = std::make_unique<OGRPolygon>();
    1295          62 :         auto poRing = std::make_unique<OGRLinearRing>();
    1296         372 :         for (int k = 0; k < 5; k++)
    1297         310 :             poRing->addPoint(adfX[k], adfY[k]);
    1298          62 :         poPoly->addRing(std::move(poRing));
    1299          62 :         poFeature->SetGeometryDirectly(poPoly.release());
    1300             : 
    1301          62 :         if (poLayer->CreateFeature(poFeature.get()) != OGRERR_NONE)
    1302             :         {
    1303           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1304             :                      "Failed to create feature in tile index.");
    1305           0 :             return nullptr;
    1306             :         }
    1307             : 
    1308          62 :         ++iCur;
    1309          77 :         if (psOptions->pfnProgress &&
    1310          30 :             !psOptions->pfnProgress(static_cast<double>(iCur) / nTotal, "",
    1311          15 :                                     psOptions->pProgressData))
    1312             :         {
    1313           0 :             return nullptr;
    1314             :         }
    1315          62 :         if (iCur >= nSrcCount)
    1316          39 :             ++nTotal;
    1317          78 :     }
    1318          43 :     if (psOptions->pfnProgress)
    1319           7 :         psOptions->pfnProgress(1.0, "", psOptions->pProgressData);
    1320             : 
    1321          43 :     if (poTileIndexDSUnique)
    1322          28 :         return GDALDataset::ToHandle(poTileIndexDSUnique.release());
    1323             :     else
    1324          15 :         return GDALDataset::ToHandle(poTileIndexDS);
    1325             : }
    1326             : 
    1327             : /************************************************************************/
    1328             : /*                             SanitizeSRS                              */
    1329             : /************************************************************************/
    1330             : 
    1331           9 : static char *SanitizeSRS(const char *pszUserInput)
    1332             : 
    1333             : {
    1334             :     OGRSpatialReferenceH hSRS;
    1335           9 :     char *pszResult = nullptr;
    1336             : 
    1337           9 :     CPLErrorReset();
    1338             : 
    1339           9 :     hSRS = OSRNewSpatialReference(nullptr);
    1340           9 :     if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE)
    1341           9 :         OSRExportToWkt(hSRS, &pszResult);
    1342             :     else
    1343             :     {
    1344           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s",
    1345             :                  pszUserInput);
    1346             :     }
    1347             : 
    1348           9 :     OSRDestroySpatialReference(hSRS);
    1349             : 
    1350           9 :     return pszResult;
    1351             : }
    1352             : 
    1353             : /************************************************************************/
    1354             : /*                          GDALTileIndexOptionsNew()                   */
    1355             : /************************************************************************/
    1356             : 
    1357             : /**
    1358             :  * Allocates a GDALTileIndexOptions struct.
    1359             :  *
    1360             :  * @param papszArgv NULL terminated list of options (potentially including
    1361             :  * filename and open options too), or NULL. The accepted options are the ones of
    1362             :  * the <a href="/programs/gdaltindex.html">gdaltindex</a> utility.
    1363             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    1364             :  * NULL), otherwise (gdaltindex_bin.cpp use case) must be allocated with
    1365             :  * GDALTileIndexOptionsForBinaryNew() prior to this function. Will be filled
    1366             :  * with potentially present filename, open options,...
    1367             :  * @return pointer to the allocated GDALTileIndexOptions struct. Must be freed
    1368             :  * with GDALTileIndexOptionsFree().
    1369             :  *
    1370             :  * @since GDAL 3.9
    1371             :  */
    1372             : 
    1373             : GDALTileIndexOptions *
    1374          45 : GDALTileIndexOptionsNew(char **papszArgv,
    1375             :                         GDALTileIndexOptionsForBinary *psOptionsForBinary)
    1376             : {
    1377          90 :     auto psOptions = std::make_unique<GDALTileIndexOptions>();
    1378             : 
    1379             :     /* -------------------------------------------------------------------- */
    1380             :     /*      Parse arguments.                                                */
    1381             :     /* -------------------------------------------------------------------- */
    1382             : 
    1383          90 :     CPLStringList aosArgv;
    1384             : 
    1385          45 :     if (papszArgv)
    1386             :     {
    1387          45 :         const int nArgc = CSLCount(papszArgv);
    1388         359 :         for (int i = 0; i < nArgc; i++)
    1389             :         {
    1390         314 :             aosArgv.AddString(papszArgv[i]);
    1391             :         }
    1392             :     }
    1393             : 
    1394             :     try
    1395             :     {
    1396             :         auto argParser = GDALTileIndexAppOptionsGetParser(psOptions.get(),
    1397          45 :                                                           psOptionsForBinary);
    1398          45 :         argParser->parse_args_without_binary_name(aosArgv.List());
    1399             : 
    1400             :         // Check all no store_into args
    1401          48 :         if (auto oTr = argParser->present<std::vector<double>>("-tr"))
    1402             :         {
    1403           3 :             psOptions->xres = (*oTr)[0];
    1404           3 :             psOptions->yres = (*oTr)[1];
    1405             :         }
    1406             : 
    1407          48 :         if (auto oTargetExtent = argParser->present<std::vector<double>>("-te"))
    1408             :         {
    1409           3 :             psOptions->xmin = (*oTargetExtent)[0];
    1410           3 :             psOptions->ymin = (*oTargetExtent)[1];
    1411           3 :             psOptions->xmax = (*oTargetExtent)[2];
    1412           3 :             psOptions->ymax = (*oTargetExtent)[3];
    1413             :         }
    1414             : 
    1415          45 :         if (auto fetchMd =
    1416          45 :                 argParser->present<std::vector<std::string>>("-fetch_md"))
    1417             :         {
    1418             : 
    1419           3 :             CPLAssert(fetchMd->size() % 3 == 0);
    1420             : 
    1421             :             // Loop
    1422           8 :             for (size_t i = 0; i < fetchMd->size(); i += 3)
    1423             :             {
    1424             :                 OGRFieldType type;
    1425           5 :                 const auto &typeName{fetchMd->at(i + 2)};
    1426           5 :                 if (typeName == "String")
    1427             :                 {
    1428           3 :                     type = OFTString;
    1429             :                 }
    1430           2 :                 else if (typeName == "Integer")
    1431             :                 {
    1432           0 :                     type = OFTInteger;
    1433             :                 }
    1434           2 :                 else if (typeName == "Integer64")
    1435             :                 {
    1436           0 :                     type = OFTInteger64;
    1437             :                 }
    1438           2 :                 else if (typeName == "Real")
    1439             :                 {
    1440           1 :                     type = OFTReal;
    1441             :                 }
    1442           1 :                 else if (typeName == "Date")
    1443             :                 {
    1444           0 :                     type = OFTDate;
    1445             :                 }
    1446           1 :                 else if (typeName == "DateTime")
    1447             :                 {
    1448           1 :                     type = OFTDateTime;
    1449             :                 }
    1450             :                 else
    1451             :                 {
    1452           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1453             :                              "-fetch_md requires a valid type name as third "
    1454             :                              "argument: %s was given.",
    1455           0 :                              fetchMd->at(i).c_str());
    1456           0 :                     return nullptr;
    1457             :                 }
    1458             : 
    1459           5 :                 const GDALTileIndexRasterMetadata oMD{type, fetchMd->at(i + 1),
    1460          15 :                                                       fetchMd->at(i)};
    1461           5 :                 psOptions->aoFetchMD.push_back(std::move(oMD));
    1462             :             }
    1463             :         }
    1464             : 
    1465             :         // Check -t_srs
    1466          45 :         if (!psOptions->osTargetSRS.empty())
    1467             :         {
    1468           9 :             auto sanitized{SanitizeSRS(psOptions->osTargetSRS.c_str())};
    1469           9 :             if (sanitized)
    1470             :             {
    1471           9 :                 psOptions->osTargetSRS = sanitized;
    1472           9 :                 CPLFree(sanitized);
    1473             :             }
    1474             :             else
    1475             :             {
    1476             :                 // Error was already reported by SanitizeSRS, just return nullptr
    1477           0 :                 psOptions->osTargetSRS.clear();
    1478           0 :                 return nullptr;
    1479             :             }
    1480             :         }
    1481             :     }
    1482           0 :     catch (const std::exception &error)
    1483             :     {
    1484           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
    1485           0 :         return nullptr;
    1486             :     }
    1487             : 
    1488          45 :     return psOptions.release();
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*                        GDALTileIndexOptionsFree()                    */
    1493             : /************************************************************************/
    1494             : 
    1495             : /**
    1496             :  * Frees the GDALTileIndexOptions struct.
    1497             :  *
    1498             :  * @param psOptions the options struct for GDALTileIndex().
    1499             :  *
    1500             :  * @since GDAL 3.9
    1501             :  */
    1502             : 
    1503          45 : void GDALTileIndexOptionsFree(GDALTileIndexOptions *psOptions)
    1504             : {
    1505          45 :     delete psOptions;
    1506          45 : }
    1507             : 
    1508             : /************************************************************************/
    1509             : /*                 GDALTileIndexOptionsSetProgress()                    */
    1510             : /************************************************************************/
    1511             : 
    1512             : /**
    1513             :  * Set a progress function.
    1514             :  *
    1515             :  * @param psOptions the options struct for GDALTileIndex().
    1516             :  * @param pfnProgress the progress callback.
    1517             :  * @param pProgressData the user data for the progress callback.
    1518             :  *
    1519             :  * @since GDAL 3.11
    1520             :  */
    1521             : 
    1522          22 : void GDALTileIndexOptionsSetProgress(GDALTileIndexOptions *psOptions,
    1523             :                                      GDALProgressFunc pfnProgress,
    1524             :                                      void *pProgressData)
    1525             : {
    1526          22 :     psOptions->pfnProgress = pfnProgress;
    1527          22 :     psOptions->pProgressData = pProgressData;
    1528          22 : }
    1529             : 
    1530             : #undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS

Generated by: LCOV version 1.14