LCOV - code coverage report
Current view: top level - apps - gdaltindex_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 545 660 82.6 %
Date: 2025-07-06 22:29:48 Functions: 12 13 92.3 %

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

Generated by: LCOV version 1.14