LCOV - code coverage report
Current view: top level - apps - gdaltindex_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 549 668 82.2 %
Date: 2025-01-18 12:42:00 Functions: 11 12 91.7 %

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

Generated by: LCOV version 1.14