LCOV - code coverage report
Current view: top level - apps - gdaltindex_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 575 694 82.9 %
Date: 2025-05-15 13:16:46 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.14