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

Generated by: LCOV version 1.14