LCOV - code coverage report
Current view: top level - apps - gdaltindex_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 548 666 82.3 %
Date: 2024-11-21 22:18:42 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          30 : static std::unique_ptr<GDALArgumentParser> GDALTileIndexAppOptionsGetParser(
     158             :     GDALTileIndexOptions *psOptions,
     159             :     GDALTileIndexOptionsForBinary *psOptionsForBinary)
     160             : {
     161             :     auto argParser = std::make_unique<GDALArgumentParser>(
     162          30 :         "gdaltindex", /* bForBinary=*/psOptionsForBinary != nullptr);
     163             : 
     164          30 :     argParser->add_description(
     165          30 :         _("Build a tile index from a list of datasets."));
     166             : 
     167          30 :     argParser->add_epilog(
     168             :         _("For more details, see the full documentation for gdaltindex at\n"
     169          30 :           "https://gdal.org/programs/gdaltindex.html"));
     170             : 
     171          30 :     argParser->add_argument("-overwrite")
     172          30 :         .flag()
     173          30 :         .store_into(psOptions->bOverwrite)
     174          30 :         .help(_("Overwrite the output tile index file if it already exists."));
     175             : 
     176          30 :     argParser->add_argument("-recursive")
     177          30 :         .flag()
     178          30 :         .store_into(psOptions->bRecursive)
     179             :         .help(_("Whether directories specified in <file_or_dir> should be "
     180          30 :                 "explored recursively."));
     181             : 
     182          30 :     argParser->add_argument("-filename_filter")
     183          60 :         .metavar("<val>")
     184          30 :         .append()
     185          30 :         .store_into(psOptions->oSetFilenameFilters)
     186             :         .help(_("Pattern that the filenames contained in directories pointed "
     187          30 :                 "by <file_or_dir> should follow."));
     188             : 
     189          30 :     argParser->add_argument("-min_pixel_size")
     190          60 :         .metavar("<val>")
     191          30 :         .store_into(psOptions->dfMinPixelSize)
     192             :         .help(_("Minimum pixel size in term of geospatial extent per pixel "
     193          30 :                 "(resolution) that a raster should have to be selected."));
     194             : 
     195          30 :     argParser->add_argument("-max_pixel_size")
     196          60 :         .metavar("<val>")
     197          30 :         .store_into(psOptions->dfMaxPixelSize)
     198             :         .help(_("Maximum pixel size in term of geospatial extent per pixel "
     199          30 :                 "(resolution) that a raster should have to be selected."));
     200             : 
     201          30 :     argParser->add_output_format_argument(psOptions->osFormat);
     202             : 
     203          30 :     argParser->add_argument("-tileindex")
     204          60 :         .metavar("<field_name>")
     205          30 :         .store_into(psOptions->osLocationField)
     206          30 :         .help(_("Name of the layer in the tile index file."));
     207             : 
     208          30 :     argParser->add_argument("-write_absolute_path")
     209          30 :         .flag()
     210          30 :         .store_into(psOptions->bWriteAbsolutePath)
     211             :         .help(_("Write the absolute path of the raster files in the tile index "
     212          30 :                 "file."));
     213             : 
     214          30 :     argParser->add_argument("-skip_different_projection")
     215          30 :         .flag()
     216          30 :         .store_into(psOptions->bSkipDifferentProjection)
     217             :         .help(_(
     218             :             "Only files with the same projection as files already inserted in "
     219          30 :             "the tile index will be inserted (unless -t_srs is specified)."));
     220             : 
     221          30 :     argParser->add_argument("-t_srs")
     222          60 :         .metavar("<srs_def>")
     223          30 :         .store_into(psOptions->osTargetSRS)
     224             :         .help(_("Geometries of input files will be transformed to the desired "
     225          30 :                 "target coordinate reference system."));
     226             : 
     227          30 :     argParser->add_argument("-src_srs_name")
     228          60 :         .metavar("<field_name>")
     229          30 :         .store_into(psOptions->osSrcSRSFieldName)
     230             :         .help(_("Name of the field in the tile index file where the source SRS "
     231          30 :                 "will be stored."));
     232             : 
     233          30 :     argParser->add_argument("-src_srs_format")
     234          60 :         .metavar("{AUTO|WKT|EPSG|PROJ}")
     235          30 :         .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          30 :             })
     248          30 :         .help(_("Format of the source SRS to store in the tile index file."));
     249             : 
     250          30 :     argParser->add_argument("-lyr_name")
     251          60 :         .metavar("<name>")
     252          30 :         .store_into(psOptions->osIndexLayerName)
     253          30 :         .help(_("Name of the layer in the tile index file."));
     254             : 
     255          30 :     argParser->add_layer_creation_options_argument(psOptions->aosLCO);
     256             : 
     257             :     // GTI driver options
     258             : 
     259          30 :     argParser->add_argument("-gti_filename")
     260          60 :         .metavar("<filename>")
     261          30 :         .store_into(psOptions->osGTIFilename)
     262          30 :         .help(_("Filename of the XML Virtual Tile Index file to generate."));
     263             : 
     264             :     // NOTE: no store_into
     265          30 :     argParser->add_argument("-tr")
     266          60 :         .metavar("<xres> <yres>")
     267          30 :         .nargs(2)
     268          30 :         .scan<'g', double>()
     269          30 :         .help(_("Set target resolution."));
     270             : 
     271             :     // NOTE: no store_into
     272          30 :     argParser->add_argument("-te")
     273          60 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
     274          30 :         .nargs(4)
     275          30 :         .scan<'g', double>()
     276          30 :         .help(_("Set target extent in SRS unit."));
     277             : 
     278          30 :     argParser->add_argument("-ot")
     279          60 :         .metavar("<datatype>")
     280          30 :         .store_into(psOptions->osDataType)
     281          30 :         .help(_("Output data type."));
     282             : 
     283          30 :     argParser->add_argument("-bandcount")
     284          60 :         .metavar("<val>")
     285          30 :         .store_into(psOptions->osBandCount)
     286          30 :         .help(_("Number of bands of the tiles of the tile index."));
     287             : 
     288          30 :     argParser->add_argument("-nodata")
     289          60 :         .metavar("<val>")
     290          30 :         .append()
     291          30 :         .store_into(psOptions->osNodata)
     292          30 :         .help(_("Nodata value of the tiles of the tile index."));
     293             : 
     294             :     // Should we use choices here?
     295          30 :     argParser->add_argument("-colorinterp")
     296          60 :         .metavar("<val>")
     297          30 :         .append()
     298          30 :         .store_into(psOptions->osColorInterp)
     299             :         .help(_("Color interpretation of of the tiles of the tile index: red, "
     300          30 :                 "green, blue, alpha, gray, undefined."));
     301             : 
     302          30 :     argParser->add_argument("-mask")
     303          30 :         .flag()
     304          30 :         .store_into(psOptions->bMaskBand)
     305          30 :         .help(_("Add a mask band to the tiles of the tile index."));
     306             : 
     307          30 :     argParser->add_argument("-mo")
     308          60 :         .metavar("<name>=<value>")
     309          30 :         .append()
     310          30 :         .store_into(psOptions->aosMetadata)
     311             :         .help(_("Write an arbitrary layer metadata item, for formats that "
     312          30 :                 "support layer metadata."));
     313             : 
     314             :     // NOTE: no store_into
     315          30 :     argParser->add_argument("-fetch_md")
     316          30 :         .nargs(3)
     317          60 :         .metavar("<gdal_md_name> <fld_name> <fld_type>")
     318          30 :         .append()
     319             :         .help("Fetch a metadata item from the raster tile and write it as a "
     320          30 :               "field in the tile index.");
     321             : 
     322          30 :     if (psOptionsForBinary)
     323             :     {
     324           7 :         argParser->add_quiet_argument(&psOptionsForBinary->bQuiet);
     325             : 
     326           7 :         argParser->add_argument("index_file")
     327          14 :             .metavar("<index_file>")
     328           7 :             .store_into(psOptionsForBinary->osDest)
     329           7 :             .help(_("The name of the output file to create/append to."));
     330             : 
     331           7 :         argParser->add_argument("file_or_dir")
     332          14 :             .metavar("<file_or_dir>")
     333           7 :             .nargs(argparse::nargs_pattern::at_least_one)
     334          14 :             .action([psOptionsForBinary](const std::string &s)
     335          21 :                     { psOptionsForBinary->aosSrcFiles.AddString(s.c_str()); })
     336             :             .help(_(
     337             :                 "The input GDAL raster files or directory, can be multiple "
     338           7 :                 "locations separated by spaces. Wildcards may also be used."));
     339             :     }
     340             : 
     341          30 :     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 =
     462          15 :                 CPLFormFilename(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 :                              CPLGetExtension(pszDest), aoDrivers[0].c_str());
     611             :                 }
     612          21 :                 osFormat = aoDrivers[0];
     613             :             }
     614             :         }
     615             :         else
     616             :         {
     617           1 :             osFormat = psOptions->osFormat;
     618             :         }
     619          22 :         if (!EQUAL(osFormat.c_str(), "ESRI Shapefile"))
     620           6 :             nMaxFieldSize = 0;
     621             : 
     622             :         auto poDriver =
     623          22 :             GetGDALDriverManager()->GetDriverByName(osFormat.c_str());
     624          22 :         if (poDriver == nullptr)
     625             :         {
     626           0 :             CPLError(CE_Warning, CPLE_AppDefined, "%s driver not available.",
     627             :                      osFormat.c_str());
     628           0 :             return nullptr;
     629             :         }
     630             : 
     631          22 :         poTileIndexDS.reset(
     632             :             poDriver->Create(pszDest, 0, 0, 0, GDT_Unknown, nullptr));
     633          22 :         if (!poTileIndexDS)
     634           0 :             return nullptr;
     635             :     }
     636             : 
     637          29 :     if (poLayer)
     638             :     {
     639           7 :         bExistingLayer = true;
     640             :     }
     641             :     else
     642             :     {
     643          22 :         std::string osLayerName;
     644          22 :         if (psOptions->osIndexLayerName.empty())
     645             :         {
     646             :             VSIStatBuf sStat;
     647          22 :             if (EQUAL(osFormat.c_str(), "ESRI Shapefile") ||
     648           3 :                 VSIStat(pszDest, &sStat) == 0)
     649             :             {
     650          19 :                 osLayerName = CPLGetBasename(pszDest);
     651             :             }
     652             :             else
     653             :             {
     654           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     655             :                          "-lyr_name must be specified.");
     656           0 :                 if (pbUsageError)
     657           0 :                     *pbUsageError = true;
     658           0 :                 return nullptr;
     659             :             }
     660             :         }
     661             :         else
     662             :         {
     663           3 :             if (psOptions->bOverwrite)
     664             :             {
     665           0 :                 for (int i = 0; i < poTileIndexDS->GetLayerCount(); ++i)
     666             :                 {
     667           0 :                     auto poExistingLayer = poTileIndexDS->GetLayer(i);
     668           0 :                     if (poExistingLayer && poExistingLayer->GetName() ==
     669           0 :                                                psOptions->osIndexLayerName)
     670             :                     {
     671           0 :                         poTileIndexDS->DeleteLayer(i);
     672           0 :                         break;
     673             :                     }
     674             :                 }
     675             :             }
     676             : 
     677           3 :             osLayerName = psOptions->osIndexLayerName;
     678             :         }
     679             : 
     680             :         /* get spatial reference for output file from target SRS (if set) */
     681             :         /* or from first input file */
     682          22 :         OGRSpatialReference oSRS;
     683          22 :         if (!oTargetSRS.IsEmpty())
     684             :         {
     685           5 :             oSRS = oTargetSRS;
     686             :         }
     687             :         else
     688             :         {
     689          17 :             std::string osFilename = oGDALTileIndexTileIterator.next();
     690          17 :             if (osFilename.empty())
     691             :             {
     692           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find any tile");
     693           1 :                 return nullptr;
     694             :             }
     695          16 :             oGDALTileIndexTileIterator.reset();
     696             :             auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     697             :                 osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
     698          16 :                 nullptr, nullptr, nullptr));
     699          16 :             if (!poSrcDS)
     700           0 :                 return nullptr;
     701             : 
     702          16 :             auto poSrcSRS = poSrcDS->GetSpatialRef();
     703          16 :             if (poSrcSRS)
     704          16 :                 oSRS = *poSrcSRS;
     705             :         }
     706             : 
     707          42 :         poLayer = poTileIndexDS->CreateLayer(
     708          21 :             osLayerName.c_str(), oSRS.IsEmpty() ? nullptr : &oSRS, wkbPolygon,
     709          21 :             psOptions->aosLCO.List());
     710          21 :         if (!poLayer)
     711           0 :             return nullptr;
     712             : 
     713          21 :         OGRFieldDefn oLocationField(psOptions->osLocationField.c_str(),
     714          21 :                                     OFTString);
     715          21 :         oLocationField.SetWidth(nMaxFieldSize);
     716          21 :         if (poLayer->CreateField(&oLocationField) != OGRERR_NONE)
     717           0 :             return nullptr;
     718             : 
     719          21 :         if (!psOptions->osSrcSRSFieldName.empty())
     720             :         {
     721           5 :             OGRFieldDefn oSrcSRSField(psOptions->osSrcSRSFieldName.c_str(),
     722           5 :                                       OFTString);
     723           5 :             oSrcSRSField.SetWidth(nMaxFieldSize);
     724           5 :             if (poLayer->CreateField(&oSrcSRSField) != OGRERR_NONE)
     725           0 :                 return nullptr;
     726             :         }
     727             :     }
     728             : 
     729          28 :     auto poLayerDefn = poLayer->GetLayerDefn();
     730             : 
     731          32 :     for (const auto &oFetchMD : psOptions->aoFetchMD)
     732             :     {
     733           4 :         if (poLayerDefn->GetFieldIndex(oFetchMD.osFieldName.c_str()) < 0)
     734             :         {
     735           4 :             OGRFieldDefn oField(oFetchMD.osFieldName.c_str(), oFetchMD.eType);
     736           4 :             if (poLayer->CreateField(&oField) != OGRERR_NONE)
     737           0 :                 return nullptr;
     738             :         }
     739             :     }
     740             : 
     741          28 :     if (!psOptions->osGTIFilename.empty())
     742             :     {
     743           1 :         if (!psOptions->aosMetadata.empty())
     744             :         {
     745           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     746             :                      "-mo is not supported when -gti_filename is used");
     747           0 :             return nullptr;
     748             :         }
     749             :         CPLXMLNode *psRoot =
     750           1 :             CPLCreateXMLNode(nullptr, CXT_Element, "GDALTileIndexDataset");
     751           1 :         CPLCreateXMLElementAndValue(psRoot, "IndexDataset", pszDest);
     752           1 :         CPLCreateXMLElementAndValue(psRoot, "IndexLayer", poLayer->GetName());
     753           1 :         CPLCreateXMLElementAndValue(psRoot, "LocationField",
     754           1 :                                     psOptions->osLocationField.c_str());
     755           1 :         if (!std::isnan(psOptions->xres))
     756             :         {
     757           1 :             CPLCreateXMLElementAndValue(psRoot, "ResX",
     758           1 :                                         CPLSPrintf("%.18g", psOptions->xres));
     759           1 :             CPLCreateXMLElementAndValue(psRoot, "ResY",
     760           1 :                                         CPLSPrintf("%.18g", psOptions->yres));
     761             :         }
     762           1 :         if (!std::isnan(psOptions->xmin))
     763             :         {
     764           1 :             CPLCreateXMLElementAndValue(psRoot, "MinX",
     765           1 :                                         CPLSPrintf("%.18g", psOptions->xmin));
     766           1 :             CPLCreateXMLElementAndValue(psRoot, "MinY",
     767           1 :                                         CPLSPrintf("%.18g", psOptions->ymin));
     768           1 :             CPLCreateXMLElementAndValue(psRoot, "MaxX",
     769           1 :                                         CPLSPrintf("%.18g", psOptions->xmax));
     770           1 :             CPLCreateXMLElementAndValue(psRoot, "MaxY",
     771           1 :                                         CPLSPrintf("%.18g", psOptions->ymax));
     772             :         }
     773             : 
     774           1 :         int nBandCount = 0;
     775           1 :         if (!psOptions->osBandCount.empty())
     776             :         {
     777           0 :             nBandCount = atoi(psOptions->osBandCount.c_str());
     778             :         }
     779             :         else
     780             :         {
     781           1 :             if (!psOptions->osDataType.empty())
     782             :             {
     783           0 :                 nBandCount = std::max(
     784             :                     nBandCount,
     785           0 :                     CPLStringList(CSLTokenizeString2(
     786           0 :                                       psOptions->osDataType.c_str(), ", ", 0))
     787           0 :                         .size());
     788             :             }
     789           1 :             if (!psOptions->osNodata.empty())
     790             :             {
     791           1 :                 nBandCount = std::max(
     792             :                     nBandCount,
     793           2 :                     CPLStringList(CSLTokenizeString2(
     794           1 :                                       psOptions->osNodata.c_str(), ", ", 0))
     795           1 :                         .size());
     796             :             }
     797           1 :             if (!psOptions->osColorInterp.empty())
     798             :             {
     799           1 :                 nBandCount =
     800           1 :                     std::max(nBandCount,
     801           2 :                              CPLStringList(
     802             :                                  CSLTokenizeString2(
     803           1 :                                      psOptions->osColorInterp.c_str(), ", ", 0))
     804           1 :                                  .size());
     805             :             }
     806             :         }
     807             : 
     808           2 :         for (int i = 0; i < nBandCount; ++i)
     809             :         {
     810           1 :             auto psBand = CPLCreateXMLNode(psRoot, CXT_Element, "Band");
     811           1 :             CPLAddXMLAttributeAndValue(psBand, "band", CPLSPrintf("%d", i + 1));
     812           1 :             if (!psOptions->osDataType.empty())
     813             :             {
     814             :                 const CPLStringList aosTokens(
     815           0 :                     CSLTokenizeString2(psOptions->osDataType.c_str(), ", ", 0));
     816           0 :                 if (aosTokens.size() == 1)
     817           0 :                     CPLAddXMLAttributeAndValue(psBand, "dataType",
     818             :                                                aosTokens[0]);
     819           0 :                 else if (i < aosTokens.size())
     820           0 :                     CPLAddXMLAttributeAndValue(psBand, "dataType",
     821             :                                                aosTokens[i]);
     822             :             }
     823           1 :             if (!psOptions->osNodata.empty())
     824             :             {
     825             :                 const CPLStringList aosTokens(
     826           2 :                     CSLTokenizeString2(psOptions->osNodata.c_str(), ", ", 0));
     827           1 :                 if (aosTokens.size() == 1)
     828           1 :                     CPLCreateXMLElementAndValue(psBand, "NoDataValue",
     829             :                                                 aosTokens[0]);
     830           0 :                 else if (i < aosTokens.size())
     831           0 :                     CPLCreateXMLElementAndValue(psBand, "NoDataValue",
     832             :                                                 aosTokens[i]);
     833             :             }
     834           1 :             if (!psOptions->osColorInterp.empty())
     835             :             {
     836             :                 const CPLStringList aosTokens(CSLTokenizeString2(
     837           2 :                     psOptions->osColorInterp.c_str(), ", ", 0));
     838           1 :                 if (aosTokens.size() == 1)
     839           1 :                     CPLCreateXMLElementAndValue(psBand, "ColorInterp",
     840             :                                                 aosTokens[0]);
     841           0 :                 else if (i < aosTokens.size())
     842           0 :                     CPLCreateXMLElementAndValue(psBand, "ColorInterp",
     843             :                                                 aosTokens[i]);
     844             :             }
     845             :         }
     846             : 
     847           1 :         if (psOptions->bMaskBand)
     848             :         {
     849           1 :             CPLCreateXMLElementAndValue(psRoot, "MaskBand", "true");
     850             :         }
     851             :         int res =
     852           1 :             CPLSerializeXMLTreeToFile(psRoot, psOptions->osGTIFilename.c_str());
     853           1 :         CPLDestroyXMLNode(psRoot);
     854           1 :         if (!res)
     855           0 :             return nullptr;
     856             :     }
     857             :     else
     858             :     {
     859          27 :         poLayer->SetMetadataItem("LOCATION_FIELD",
     860          27 :                                  psOptions->osLocationField.c_str());
     861          27 :         if (!std::isnan(psOptions->xres))
     862             :         {
     863           1 :             poLayer->SetMetadataItem("RESX",
     864           1 :                                      CPLSPrintf("%.18g", psOptions->xres));
     865           1 :             poLayer->SetMetadataItem("RESY",
     866           1 :                                      CPLSPrintf("%.18g", psOptions->yres));
     867             :         }
     868          27 :         if (!std::isnan(psOptions->xmin))
     869             :         {
     870           1 :             poLayer->SetMetadataItem("MINX",
     871           1 :                                      CPLSPrintf("%.18g", psOptions->xmin));
     872           1 :             poLayer->SetMetadataItem("MINY",
     873           1 :                                      CPLSPrintf("%.18g", psOptions->ymin));
     874           1 :             poLayer->SetMetadataItem("MAXX",
     875           1 :                                      CPLSPrintf("%.18g", psOptions->xmax));
     876           1 :             poLayer->SetMetadataItem("MAXY",
     877           1 :                                      CPLSPrintf("%.18g", psOptions->ymax));
     878             :         }
     879          27 :         if (!psOptions->osBandCount.empty())
     880             :         {
     881           1 :             poLayer->SetMetadataItem("BAND_COUNT",
     882           1 :                                      psOptions->osBandCount.c_str());
     883             :         }
     884          27 :         if (!psOptions->osDataType.empty())
     885             :         {
     886           1 :             poLayer->SetMetadataItem("DATA_TYPE",
     887           1 :                                      psOptions->osDataType.c_str());
     888             :         }
     889          27 :         if (!psOptions->osNodata.empty())
     890             :         {
     891           1 :             poLayer->SetMetadataItem("NODATA", psOptions->osNodata.c_str());
     892             :         }
     893          27 :         if (!psOptions->osColorInterp.empty())
     894             :         {
     895           1 :             poLayer->SetMetadataItem("COLOR_INTERPRETATION",
     896           1 :                                      psOptions->osColorInterp.c_str());
     897             :         }
     898          27 :         if (psOptions->bMaskBand)
     899             :         {
     900           1 :             poLayer->SetMetadataItem("MASK_BAND", "YES");
     901             :         }
     902          54 :         const CPLStringList aosMetadata(psOptions->aosMetadata);
     903           2 :         for (const auto &[pszKey, pszValue] :
     904          29 :              cpl::IterateNameValue(aosMetadata))
     905             :         {
     906           1 :             poLayer->SetMetadataItem(pszKey, pszValue);
     907             :         }
     908             :     }
     909             : 
     910             :     const int ti_field =
     911          28 :         poLayerDefn->GetFieldIndex(psOptions->osLocationField.c_str());
     912          28 :     if (ti_field < 0)
     913             :     {
     914           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     915             :                  "Unable to find field `%s' in file `%s'.",
     916           0 :                  psOptions->osLocationField.c_str(), pszDest);
     917           0 :         return nullptr;
     918             :     }
     919             : 
     920          28 :     int i_SrcSRSName = -1;
     921          28 :     if (!psOptions->osSrcSRSFieldName.empty())
     922             :     {
     923             :         i_SrcSRSName =
     924           5 :             poLayerDefn->GetFieldIndex(psOptions->osSrcSRSFieldName.c_str());
     925           5 :         if (i_SrcSRSName < 0)
     926             :         {
     927           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     928             :                      "Unable to find field `%s' in file `%s'.",
     929           0 :                      psOptions->osSrcSRSFieldName.c_str(), pszDest);
     930           0 :             return nullptr;
     931             :         }
     932             :     }
     933             : 
     934             :     // Load in memory existing file names in tile index.
     935          56 :     std::set<std::string> oSetExistingFiles;
     936          56 :     OGRSpatialReference oAlreadyExistingSRS;
     937          28 :     if (bExistingLayer)
     938             :     {
     939          27 :         for (auto &&poFeature : poLayer)
     940             :         {
     941          20 :             if (poFeature->IsFieldSetAndNotNull(ti_field))
     942             :             {
     943          20 :                 if (oSetExistingFiles.empty())
     944             :                 {
     945             :                     auto poSrcDS =
     946             :                         std::unique_ptr<GDALDataset>(GDALDataset::Open(
     947             :                             poFeature->GetFieldAsString(ti_field),
     948          14 :                             GDAL_OF_RASTER, nullptr, nullptr, nullptr));
     949           7 :                     if (poSrcDS)
     950             :                     {
     951           7 :                         auto poSrcSRS = poSrcDS->GetSpatialRef();
     952           7 :                         if (poSrcSRS)
     953           7 :                             oAlreadyExistingSRS = *poSrcSRS;
     954             :                     }
     955             :                 }
     956          20 :                 oSetExistingFiles.insert(poFeature->GetFieldAsString(ti_field));
     957             :             }
     958             :         }
     959             :     }
     960             : 
     961          56 :     std::string osCurrentPath;
     962          28 :     if (psOptions->bWriteAbsolutePath)
     963             :     {
     964           1 :         char *pszCurrentPath = CPLGetCurrentDir();
     965           1 :         if (pszCurrentPath)
     966             :         {
     967           1 :             osCurrentPath = pszCurrentPath;
     968             :         }
     969             :         else
     970             :         {
     971           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     972             :                      "This system does not support the CPLGetCurrentDir call. "
     973             :                      "The option -bWriteAbsolutePath will have no effect.");
     974             :         }
     975           1 :         CPLFree(pszCurrentPath);
     976             :     }
     977             : 
     978             :     const bool bIsGTIContext =
     979          54 :         !std::isnan(psOptions->xres) || !std::isnan(psOptions->xmin) ||
     980          26 :         !psOptions->osBandCount.empty() || !psOptions->osNodata.empty() ||
     981          26 :         !psOptions->osColorInterp.empty() || !psOptions->osDataType.empty() ||
     982          79 :         psOptions->bMaskBand || !psOptions->aosMetadata.empty() ||
     983          53 :         !psOptions->osGTIFilename.empty();
     984             : 
     985             :     /* -------------------------------------------------------------------- */
     986             :     /*      loop over GDAL files, processing.                               */
     987             :     /* -------------------------------------------------------------------- */
     988             :     while (true)
     989             :     {
     990          91 :         const std::string osSrcFilename = oGDALTileIndexTileIterator.next();
     991          91 :         if (osSrcFilename.empty())
     992          28 :             break;
     993             : 
     994          63 :         std::string osFileNameToWrite;
     995             :         VSIStatBuf sStatBuf;
     996             : 
     997             :         // Make sure it is a file before building absolute path name.
     998          63 :         if (!osCurrentPath.empty() &&
     999          64 :             CPLIsFilenameRelative(osSrcFilename.c_str()) &&
    1000           1 :             VSIStat(osSrcFilename.c_str(), &sStatBuf) == 0)
    1001             :         {
    1002             :             osFileNameToWrite = CPLProjectRelativeFilename(
    1003           1 :                 osCurrentPath.c_str(), osSrcFilename.c_str());
    1004             :         }
    1005             :         else
    1006             :         {
    1007          62 :             osFileNameToWrite = osSrcFilename.c_str();
    1008             :         }
    1009             : 
    1010             :         // Checks that file is not already in tileindex.
    1011          63 :         if (oSetExistingFiles.find(osFileNameToWrite) !=
    1012         126 :             oSetExistingFiles.end())
    1013             :         {
    1014           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1015             :                      "File %s is already in tileindex. Skipping it.",
    1016             :                      osFileNameToWrite.c_str());
    1017           4 :             continue;
    1018             :         }
    1019             : 
    1020             :         auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1021             :             osSrcFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    1022          59 :             nullptr, nullptr, nullptr));
    1023          59 :         if (poSrcDS == nullptr)
    1024             :         {
    1025           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1026             :                      "Unable to open %s, skipping.", osSrcFilename.c_str());
    1027           0 :             continue;
    1028             :         }
    1029             : 
    1030          59 :         double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    1031          59 :         if (poSrcDS->GetGeoTransform(adfGeoTransform) != CE_None)
    1032             :         {
    1033           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1034             :                      "It appears no georeferencing is available for\n"
    1035             :                      "`%s', skipping.",
    1036             :                      osSrcFilename.c_str());
    1037           0 :             continue;
    1038             :         }
    1039             : 
    1040          59 :         auto poSrcSRS = poSrcDS->GetSpatialRef();
    1041             :         // If not set target srs, test that the current file uses same
    1042             :         // projection as others.
    1043          59 :         if (oTargetSRS.IsEmpty())
    1044             :         {
    1045          48 :             if (!oAlreadyExistingSRS.IsEmpty())
    1046             :             {
    1047          64 :                 if (poSrcSRS == nullptr ||
    1048          32 :                     !poSrcSRS->IsSame(&oAlreadyExistingSRS))
    1049             :                 {
    1050           1 :                     CPLError(
    1051             :                         CE_Warning, CPLE_AppDefined,
    1052             :                         "%s is not using the same projection system "
    1053             :                         "as other files in the tileindex.\n"
    1054             :                         "This may cause problems when using it in MapServer "
    1055             :                         "for example.\n"
    1056             :                         "Use -t_srs option to set target projection system. %s",
    1057             :                         osSrcFilename.c_str(),
    1058           1 :                         psOptions->bSkipDifferentProjection
    1059             :                             ? "Skipping this file."
    1060             :                             : "");
    1061           1 :                     if (psOptions->bSkipDifferentProjection)
    1062             :                     {
    1063           1 :                         continue;
    1064             :                     }
    1065             :                 }
    1066             :             }
    1067             :             else
    1068             :             {
    1069          16 :                 if (poSrcSRS)
    1070          16 :                     oAlreadyExistingSRS = *poSrcSRS;
    1071             :             }
    1072             :         }
    1073             : 
    1074          58 :         const int nXSize = poSrcDS->GetRasterXSize();
    1075          58 :         const int nYSize = poSrcDS->GetRasterYSize();
    1076          58 :         if (nXSize == 0 || nYSize == 0)
    1077             :         {
    1078           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1079             :                      "%s has 0 width or height. Skipping",
    1080             :                      osSrcFilename.c_str());
    1081           0 :             continue;
    1082             :         }
    1083             : 
    1084          58 :         double adfX[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
    1085          58 :         double adfY[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
    1086          58 :         adfX[0] = adfGeoTransform[0] + 0 * adfGeoTransform[1] +
    1087          58 :                   0 * adfGeoTransform[2];
    1088          58 :         adfY[0] = adfGeoTransform[3] + 0 * adfGeoTransform[4] +
    1089          58 :                   0 * adfGeoTransform[5];
    1090             : 
    1091          58 :         adfX[1] = adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
    1092          58 :                   0 * adfGeoTransform[2];
    1093          58 :         adfY[1] = adfGeoTransform[3] + nXSize * adfGeoTransform[4] +
    1094          58 :                   0 * adfGeoTransform[5];
    1095             : 
    1096          58 :         adfX[2] = adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
    1097          58 :                   nYSize * adfGeoTransform[2];
    1098          58 :         adfY[2] = adfGeoTransform[3] + nXSize * adfGeoTransform[4] +
    1099          58 :                   nYSize * adfGeoTransform[5];
    1100             : 
    1101          58 :         adfX[3] = adfGeoTransform[0] + 0 * adfGeoTransform[1] +
    1102          58 :                   nYSize * adfGeoTransform[2];
    1103          58 :         adfY[3] = adfGeoTransform[3] + 0 * adfGeoTransform[4] +
    1104          58 :                   nYSize * adfGeoTransform[5];
    1105             : 
    1106          58 :         adfX[4] = adfGeoTransform[0] + 0 * adfGeoTransform[1] +
    1107          58 :                   0 * adfGeoTransform[2];
    1108          58 :         adfY[4] = adfGeoTransform[3] + 0 * adfGeoTransform[4] +
    1109          58 :                   0 * adfGeoTransform[5];
    1110             : 
    1111             :         // If set target srs, do the forward transformation of all points.
    1112          58 :         if (!oTargetSRS.IsEmpty() && poSrcSRS)
    1113             :         {
    1114          11 :             if (!poSrcSRS->IsSame(&oTargetSRS))
    1115             :             {
    1116             :                 auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    1117           6 :                     OGRCreateCoordinateTransformation(poSrcSRS, &oTargetSRS));
    1118           6 :                 if (!poCT || !poCT->Transform(5, adfX, adfY, nullptr))
    1119             :                 {
    1120           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1121             :                              "unable to transform points from source "
    1122             :                              "SRS `%s' to target SRS `%s' for file `%s' - file "
    1123             :                              "skipped",
    1124             :                              poSrcDS->GetProjectionRef(),
    1125           0 :                              psOptions->osTargetSRS.c_str(),
    1126             :                              osFileNameToWrite.c_str());
    1127           0 :                     continue;
    1128             :                 }
    1129             :             }
    1130             :         }
    1131          56 :         else if (bIsGTIContext && !oAlreadyExistingSRS.IsEmpty() &&
    1132           9 :                  (poSrcSRS == nullptr ||
    1133           9 :                   !poSrcSRS->IsSame(&oAlreadyExistingSRS)))
    1134             :         {
    1135           0 :             CPLError(
    1136             :                 CE_Failure, CPLE_AppDefined,
    1137             :                 "%s is not using the same projection system "
    1138             :                 "as other files in the tileindex. This is not compatible of "
    1139             :                 "GTI use. Use -t_srs option to reproject tile extents "
    1140             :                 "to a common SRS.",
    1141             :                 osSrcFilename.c_str());
    1142           0 :             return nullptr;
    1143             :         }
    1144             : 
    1145             :         const double dfMinX =
    1146          58 :             std::min(std::min(adfX[0], adfX[1]), std::min(adfX[2], adfX[3]));
    1147             :         const double dfMinY =
    1148          58 :             std::min(std::min(adfY[0], adfY[1]), std::min(adfY[2], adfY[3]));
    1149             :         const double dfMaxX =
    1150          58 :             std::max(std::max(adfX[0], adfX[1]), std::max(adfX[2], adfX[3]));
    1151             :         const double dfMaxY =
    1152          58 :             std::max(std::max(adfY[0], adfY[1]), std::max(adfY[2], adfY[3]));
    1153          58 :         const double dfRes =
    1154          58 :             (dfMaxX - dfMinX) * (dfMaxY - dfMinY) / nXSize / nYSize;
    1155          66 :         if (!std::isnan(psOptions->dfMinPixelSize) &&
    1156           8 :             dfRes < psOptions->dfMinPixelSize)
    1157             :         {
    1158           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1159             :                      "%s has %f as pixel size (< %f). Skipping",
    1160           4 :                      osSrcFilename.c_str(), dfRes, psOptions->dfMinPixelSize);
    1161           4 :             continue;
    1162             :         }
    1163          62 :         if (!std::isnan(psOptions->dfMaxPixelSize) &&
    1164           8 :             dfRes > psOptions->dfMaxPixelSize)
    1165             :         {
    1166           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    1167             :                      "%s has %f as pixel size (> %f). Skipping",
    1168           4 :                      osSrcFilename.c_str(), dfRes, psOptions->dfMaxPixelSize);
    1169           4 :             continue;
    1170             :         }
    1171             : 
    1172          50 :         auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
    1173          50 :         poFeature->SetField(ti_field, osFileNameToWrite.c_str());
    1174             : 
    1175          50 :         if (i_SrcSRSName >= 0 && poSrcSRS)
    1176             :         {
    1177          10 :             const char *pszAuthorityCode = poSrcSRS->GetAuthorityCode(nullptr);
    1178          10 :             const char *pszAuthorityName = poSrcSRS->GetAuthorityName(nullptr);
    1179          10 :             if (psOptions->eSrcSRSFormat == FORMAT_AUTO)
    1180             :             {
    1181           4 :                 if (pszAuthorityName != nullptr && pszAuthorityCode != nullptr)
    1182             :                 {
    1183           4 :                     poFeature->SetField(i_SrcSRSName,
    1184             :                                         CPLSPrintf("%s:%s", pszAuthorityName,
    1185             :                                                    pszAuthorityCode));
    1186             :                 }
    1187           0 :                 else if (nMaxFieldSize == 0 ||
    1188           0 :                          strlen(poSrcDS->GetProjectionRef()) <=
    1189           0 :                              static_cast<size_t>(nMaxFieldSize))
    1190             :                 {
    1191           0 :                     poFeature->SetField(i_SrcSRSName,
    1192             :                                         poSrcDS->GetProjectionRef());
    1193             :                 }
    1194             :                 else
    1195             :                 {
    1196           0 :                     char *pszProj4 = nullptr;
    1197           0 :                     if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE)
    1198             :                     {
    1199           0 :                         poFeature->SetField(i_SrcSRSName, pszProj4);
    1200             :                     }
    1201             :                     else
    1202             :                     {
    1203           0 :                         poFeature->SetField(i_SrcSRSName,
    1204             :                                             poSrcDS->GetProjectionRef());
    1205             :                     }
    1206           0 :                     CPLFree(pszProj4);
    1207             :                 }
    1208             :             }
    1209           6 :             else if (psOptions->eSrcSRSFormat == FORMAT_WKT)
    1210             :             {
    1211           4 :                 if (nMaxFieldSize == 0 ||
    1212           2 :                     strlen(poSrcDS->GetProjectionRef()) <=
    1213           2 :                         static_cast<size_t>(nMaxFieldSize))
    1214             :                 {
    1215           0 :                     poFeature->SetField(i_SrcSRSName,
    1216             :                                         poSrcDS->GetProjectionRef());
    1217             :                 }
    1218             :                 else
    1219             :                 {
    1220           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1221             :                              "Cannot write WKT for file %s as it is too long!",
    1222             :                              osFileNameToWrite.c_str());
    1223             :                 }
    1224             :             }
    1225           4 :             else if (psOptions->eSrcSRSFormat == FORMAT_PROJ)
    1226             :             {
    1227           2 :                 char *pszProj4 = nullptr;
    1228           2 :                 if (poSrcSRS->exportToProj4(&pszProj4) == OGRERR_NONE)
    1229             :                 {
    1230           2 :                     poFeature->SetField(i_SrcSRSName, pszProj4);
    1231             :                 }
    1232           2 :                 CPLFree(pszProj4);
    1233             :             }
    1234           2 :             else if (psOptions->eSrcSRSFormat == FORMAT_EPSG)
    1235             :             {
    1236           2 :                 if (pszAuthorityName != nullptr && pszAuthorityCode != nullptr)
    1237           2 :                     poFeature->SetField(i_SrcSRSName,
    1238             :                                         CPLSPrintf("%s:%s", pszAuthorityName,
    1239             :                                                    pszAuthorityCode));
    1240             :             }
    1241             :         }
    1242             : 
    1243          54 :         for (const auto &oFetchMD : psOptions->aoFetchMD)
    1244             :         {
    1245           4 :             if (EQUAL(oFetchMD.osRasterItemName.c_str(), "{PIXEL_SIZE}"))
    1246             :             {
    1247           1 :                 poFeature->SetField(oFetchMD.osFieldName.c_str(), dfRes);
    1248           1 :                 continue;
    1249             :             }
    1250             : 
    1251             :             const char *pszMD =
    1252           3 :                 poSrcDS->GetMetadataItem(oFetchMD.osRasterItemName.c_str());
    1253           3 :             if (pszMD)
    1254             :             {
    1255           3 :                 if (EQUAL(oFetchMD.osRasterItemName.c_str(),
    1256             :                           "TIFFTAG_DATETIME"))
    1257             :                 {
    1258             :                     int nYear, nMonth, nDay, nHour, nMin, nSec;
    1259           2 :                     if (sscanf(pszMD, "%04d:%02d:%02d %02d:%02d:%02d", &nYear,
    1260           1 :                                &nMonth, &nDay, &nHour, &nMin, &nSec) == 6)
    1261             :                     {
    1262           1 :                         poFeature->SetField(
    1263             :                             oFetchMD.osFieldName.c_str(),
    1264             :                             CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", nYear,
    1265             :                                        nMonth, nDay, nHour, nMin, nSec));
    1266           1 :                         continue;
    1267             :                     }
    1268             :                 }
    1269           2 :                 poFeature->SetField(oFetchMD.osFieldName.c_str(), pszMD);
    1270             :             }
    1271             :         }
    1272             : 
    1273          50 :         auto poPoly = std::make_unique<OGRPolygon>();
    1274          50 :         auto poRing = std::make_unique<OGRLinearRing>();
    1275         300 :         for (int k = 0; k < 5; k++)
    1276         250 :             poRing->addPoint(adfX[k], adfY[k]);
    1277          50 :         poPoly->addRing(std::move(poRing));
    1278          50 :         poFeature->SetGeometryDirectly(poPoly.release());
    1279             : 
    1280          50 :         if (poLayer->CreateFeature(poFeature.get()) != OGRERR_NONE)
    1281             :         {
    1282           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1283             :                      "Failed to create feature in tile index.");
    1284           0 :             return nullptr;
    1285             :         }
    1286          63 :     }
    1287             : 
    1288          28 :     return GDALDataset::ToHandle(poTileIndexDS.release());
    1289             : }
    1290             : 
    1291             : /************************************************************************/
    1292             : /*                             SanitizeSRS                              */
    1293             : /************************************************************************/
    1294             : 
    1295           6 : static char *SanitizeSRS(const char *pszUserInput)
    1296             : 
    1297             : {
    1298             :     OGRSpatialReferenceH hSRS;
    1299           6 :     char *pszResult = nullptr;
    1300             : 
    1301           6 :     CPLErrorReset();
    1302             : 
    1303           6 :     hSRS = OSRNewSpatialReference(nullptr);
    1304           6 :     if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE)
    1305           6 :         OSRExportToWkt(hSRS, &pszResult);
    1306             :     else
    1307             :     {
    1308           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s",
    1309             :                  pszUserInput);
    1310             :     }
    1311             : 
    1312           6 :     OSRDestroySpatialReference(hSRS);
    1313             : 
    1314           6 :     return pszResult;
    1315             : }
    1316             : 
    1317             : /************************************************************************/
    1318             : /*                          GDALTileIndexOptionsNew()                   */
    1319             : /************************************************************************/
    1320             : 
    1321             : /**
    1322             :  * Allocates a GDALTileIndexOptions struct.
    1323             :  *
    1324             :  * @param papszArgv NULL terminated list of options (potentially including
    1325             :  * filename and open options too), or NULL. The accepted options are the ones of
    1326             :  * the <a href="/programs/gdaltindex.html">gdaltindex</a> utility.
    1327             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    1328             :  * NULL), otherwise (gdaltindex_bin.cpp use case) must be allocated with
    1329             :  * GDALTileIndexOptionsForBinaryNew() prior to this function. Will be filled
    1330             :  * with potentially present filename, open options,...
    1331             :  * @return pointer to the allocated GDALTileIndexOptions struct. Must be freed
    1332             :  * with GDALTileIndexOptionsFree().
    1333             :  *
    1334             :  * @since GDAL 3.9
    1335             :  */
    1336             : 
    1337             : GDALTileIndexOptions *
    1338          30 : GDALTileIndexOptionsNew(char **papszArgv,
    1339             :                         GDALTileIndexOptionsForBinary *psOptionsForBinary)
    1340             : {
    1341          59 :     auto psOptions = std::make_unique<GDALTileIndexOptions>();
    1342             : 
    1343             :     /* -------------------------------------------------------------------- */
    1344             :     /*      Parse arguments.                                                */
    1345             :     /* -------------------------------------------------------------------- */
    1346             : 
    1347          59 :     CPLStringList aosArgv;
    1348             : 
    1349          30 :     if (papszArgv)
    1350             :     {
    1351          30 :         const int nArgc = CSLCount(papszArgv);
    1352         205 :         for (int i = 0; i < nArgc; i++)
    1353             :         {
    1354         175 :             aosArgv.AddString(papszArgv[i]);
    1355             :         }
    1356             :     }
    1357             : 
    1358             :     try
    1359             :     {
    1360             :         auto argParser = GDALTileIndexAppOptionsGetParser(psOptions.get(),
    1361          30 :                                                           psOptionsForBinary);
    1362          30 :         argParser->parse_args_without_binary_name(aosArgv.List());
    1363             : 
    1364             :         // Check all no store_into args
    1365          31 :         if (auto oTr = argParser->present<std::vector<double>>("-tr"))
    1366             :         {
    1367           2 :             psOptions->xres = (*oTr)[0];
    1368           2 :             psOptions->yres = (*oTr)[1];
    1369             :         }
    1370             : 
    1371          31 :         if (auto oTargetExtent = argParser->present<std::vector<double>>("-te"))
    1372             :         {
    1373           2 :             psOptions->xmin = (*oTargetExtent)[0];
    1374           2 :             psOptions->ymin = (*oTargetExtent)[1];
    1375           2 :             psOptions->xmax = (*oTargetExtent)[2];
    1376           2 :             psOptions->ymax = (*oTargetExtent)[3];
    1377             :         }
    1378             : 
    1379          29 :         if (auto fetchMd =
    1380          29 :                 argParser->present<std::vector<std::string>>("-fetch_md"))
    1381             :         {
    1382             : 
    1383           2 :             CPLAssert(fetchMd->size() % 3 == 0);
    1384             : 
    1385             :             // Loop
    1386           6 :             for (size_t i = 0; i < fetchMd->size(); i += 3)
    1387             :             {
    1388             :                 OGRFieldType type;
    1389           4 :                 const auto &typeName{fetchMd->at(i + 2)};
    1390           4 :                 if (typeName == "String")
    1391             :                 {
    1392           2 :                     type = OFTString;
    1393             :                 }
    1394           2 :                 else if (typeName == "Integer")
    1395             :                 {
    1396           0 :                     type = OFTInteger;
    1397             :                 }
    1398           2 :                 else if (typeName == "Integer64")
    1399             :                 {
    1400           0 :                     type = OFTInteger64;
    1401             :                 }
    1402           2 :                 else if (typeName == "Real")
    1403             :                 {
    1404           1 :                     type = OFTReal;
    1405             :                 }
    1406           1 :                 else if (typeName == "Date")
    1407             :                 {
    1408           0 :                     type = OFTDate;
    1409             :                 }
    1410           1 :                 else if (typeName == "DateTime")
    1411             :                 {
    1412           1 :                     type = OFTDateTime;
    1413             :                 }
    1414             :                 else
    1415             :                 {
    1416           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1417             :                              "-fetch_md requires a valid type name as third "
    1418             :                              "argument: %s was given.",
    1419           0 :                              fetchMd->at(i).c_str());
    1420           0 :                     return nullptr;
    1421             :                 }
    1422             : 
    1423           4 :                 const GDALTileIndexRasterMetadata oMD{type, fetchMd->at(i + 1),
    1424          12 :                                                       fetchMd->at(i)};
    1425           4 :                 psOptions->aoFetchMD.push_back(oMD);
    1426             :             }
    1427             :         }
    1428             : 
    1429             :         // Check -t_srs
    1430          29 :         if (!psOptions->osTargetSRS.empty())
    1431             :         {
    1432           6 :             auto sanitized{SanitizeSRS(psOptions->osTargetSRS.c_str())};
    1433           6 :             if (sanitized)
    1434             :             {
    1435           6 :                 psOptions->osTargetSRS = sanitized;
    1436           6 :                 CPLFree(sanitized);
    1437             :             }
    1438             :             else
    1439             :             {
    1440             :                 // Error was already reported by SanitizeSRS, just return nullptr
    1441           0 :                 psOptions->osTargetSRS.clear();
    1442           0 :                 return nullptr;
    1443             :             }
    1444             :         }
    1445             :     }
    1446           0 :     catch (const std::exception &error)
    1447             :     {
    1448           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
    1449           0 :         return nullptr;
    1450             :     }
    1451             : 
    1452          29 :     return psOptions.release();
    1453             : }
    1454             : 
    1455             : /************************************************************************/
    1456             : /*                        GDALTileIndexOptionsFree()                    */
    1457             : /************************************************************************/
    1458             : 
    1459             : /**
    1460             :  * Frees the GDALTileIndexOptions struct.
    1461             :  *
    1462             :  * @param psOptions the options struct for GDALTileIndex().
    1463             :  *
    1464             :  * @since GDAL 3.9
    1465             :  */
    1466             : 
    1467          29 : void GDALTileIndexOptionsFree(GDALTileIndexOptions *psOptions)
    1468             : {
    1469          29 :     delete psOptions;
    1470          29 : }
    1471             : 
    1472             : #undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS

Generated by: LCOV version 1.14