LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_index.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 142 145 97.9 %
Date: 2026-04-23 19:47:11 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster index" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_index.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_utils_priv.h"
      18             : #include "ogrsf_frmts.h"
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*         GDALRasterIndexAlgorithm::GDALRasterIndexAlgorithm()         */
      28             : /************************************************************************/
      29             : 
      30          72 : GDALRasterIndexAlgorithm::GDALRasterIndexAlgorithm()
      31          72 :     : GDALVectorOutputAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL)
      32             : {
      33          72 :     AddProgressArg();
      34          72 :     AddInputDatasetArg(&m_inputDatasets, GDAL_OF_RASTER)
      35          72 :         .SetAutoOpenDataset(false)
      36          72 :         .SetDatasetInputFlags(GADV_NAME);
      37          72 :     GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs();
      38             : 
      39          72 :     AddCommonOptions();
      40             : 
      41             :     AddArg("source-crs-field-name", 0,
      42             :            _("Name of the field to store the CRS of each dataset"),
      43         144 :            &m_sourceCrsName)
      44          72 :         .SetMinCharCount(1);
      45             :     AddArg("source-crs-format", 0,
      46             :            _("Format in which the CRS of each dataset must be written"),
      47         144 :            &m_sourceCrsFormat)
      48          72 :         .SetMinCharCount(1)
      49          72 :         .SetDefault(m_sourceCrsFormat)
      50          72 :         .SetChoices("auto", "WKT", "EPSG", "PROJ");
      51          72 : }
      52             : 
      53             : /************************************************************************/
      54             : /*         GDALRasterIndexAlgorithm::GDALRasterIndexAlgorithm()         */
      55             : /************************************************************************/
      56             : 
      57         146 : GDALRasterIndexAlgorithm::GDALRasterIndexAlgorithm(
      58             :     const std::string &name, const std::string &description,
      59         146 :     const std::string &helpURL)
      60         146 :     : GDALVectorOutputAbstractAlgorithm(name, description, helpURL)
      61             : {
      62         146 : }
      63             : 
      64             : /************************************************************************/
      65             : /*             GDALRasterIndexAlgorithm::AddCommonOptions()             */
      66             : /************************************************************************/
      67             : 
      68         218 : void GDALRasterIndexAlgorithm::AddCommonOptions()
      69             : {
      70             :     AddArg("recursive", 0,
      71             :            _("Whether input directories should be explored recursively."),
      72         218 :            &m_recursive);
      73             :     AddArg("filename-filter", 0,
      74             :            _("Pattern that the filenames in input directories should follow "
      75             :              "('*' and '?' wildcard)"),
      76         218 :            &m_filenameFilter);
      77             :     AddArg("min-pixel-size", 0,
      78             :            _("Minimum pixel size in term of geospatial extent per pixel "
      79             :              "(resolution) that a raster should have to be selected."),
      80         436 :            &m_minPixelSize)
      81         218 :         .SetMinValueExcluded(0);
      82             :     AddArg("max-pixel-size", 0,
      83             :            _("Maximum pixel size in term of geospatial extent per pixel "
      84             :              "(resolution) that a raster should have to be selected."),
      85         436 :            &m_maxPixelSize)
      86         218 :         .SetMinValueExcluded(0);
      87             :     AddArg("location-name", 0, _("Name of the field with the raster path"),
      88         436 :            &m_locationName)
      89         218 :         .SetDefault(m_locationName)
      90         218 :         .SetMinCharCount(1);
      91             :     AddAbsolutePathArg(
      92             :         &m_writeAbsolutePaths,
      93             :         _("Whether the path to the input datasets should be stored as an "
      94         218 :           "absolute path"));
      95         436 :     AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 0, _("Output CRS"), &m_crs)
      96         436 :         .SetIsCRSArg()
      97         436 :         .AddHiddenAlias("dst-crs")
      98         218 :         .AddHiddenAlias("t_srs");
      99             : 
     100             :     {
     101             :         auto &arg =
     102         436 :             AddArg("metadata", 0, _("Add dataset metadata item"), &m_metadata)
     103         436 :                 .SetMetaVar("<KEY>=<VALUE>")
     104         218 :                 .SetPackedValuesAllowed(false);
     105           2 :         arg.AddValidationAction([this, &arg]()
     106         220 :                                 { return ParseAndValidateKeyValue(arg); });
     107         218 :         arg.AddHiddenAlias("mo");
     108             :     }
     109             : 
     110             :     AddArg("skip-errors", 0, _("Skip errors related to input datasets"),
     111         218 :            &m_skipErrors);
     112         436 :     AddArg("profile", 0, _("Profile of output dataset"), &m_profile)
     113         218 :         .SetDefault(m_profile)
     114         218 :         .SetChoices(PROFILE_NONE, PROFILE_STAC_GEOPARQUET);
     115         218 :     AddArg("base-url", 0, _("Base URL for STAC-GeoParquet href"), &m_baseUrl);
     116         436 :     AddArg("id-method", 0, _("How to derive STAC-GeoParquet 'id'"), &m_idMethod)
     117         218 :         .SetDefault(m_idMethod)
     118         218 :         .SetChoices(ID_METHOD_FILENAME, ID_METHOD_MD5, ID_METHOD_METADATA_ITEM);
     119             :     AddArg("id-metadata-item", 0,
     120             :            _("Name of metadata item used to set STAC-GeoParquet 'id'"),
     121         436 :            &m_idMetadataItem)
     122         218 :         .SetDefault(m_idMetadataItem)
     123             :         .AddValidationAction(
     124           2 :             [this]()
     125             :             {
     126           2 :                 m_idMethod = ID_METHOD_METADATA_ITEM;
     127           2 :                 return true;
     128         218 :             });
     129             : 
     130         218 :     AddValidationAction(
     131          98 :         [this]()
     132             :         {
     133          34 :             if (m_profile == PROFILE_STAC_GEOPARQUET)
     134             :             {
     135          11 :                 if (!m_outputFormat.empty() &&
     136           1 :                     !EQUAL(m_outputFormat.c_str(), "Parquet"))
     137             :                 {
     138           1 :                     ReportError(CE_Failure, CPLE_NotSupported,
     139             :                                 "STAC-GeoParquet profile is only compatible "
     140             :                                 "with Parquet output format");
     141           1 :                     return false;
     142             :                 }
     143          18 :                 else if (m_outputFormat.empty() &&
     144          18 :                          !EQUAL(CPLGetExtensionSafe(
     145             :                                     m_outputDataset.GetName().c_str())
     146             :                                     .c_str(),
     147             :                                 "parquet"))
     148             :                 {
     149           1 :                     ReportError(CE_Failure, CPLE_NotSupported,
     150             :                                 "STAC-GeoParquet profile is only compatible "
     151             :                                 "with Parquet output format");
     152           1 :                     return false;
     153             :                 }
     154           8 :                 m_outputFormat = "Parquet";
     155             : 
     156           8 :                 if (!m_crs.empty() && m_crs != "EPSG:4326")
     157             :                 {
     158           1 :                     OGRSpatialReference oSRS;
     159           1 :                     CPL_IGNORE_RET_VAL(oSRS.SetFromUserInput(m_crs.c_str()));
     160             :                     const char *pszCelestialBodyName =
     161           1 :                         oSRS.GetCelestialBodyName();
     162             :                     // STAC-GeoParquet requires EPSG:4326, but let be nice
     163             :                     // with planetary use cases and allow a non-Earth geographic CRS...
     164           2 :                     if (!(pszCelestialBodyName &&
     165           1 :                           !EQUAL(pszCelestialBodyName, "Earth") &&
     166           0 :                           oSRS.IsGeographic()))
     167             :                     {
     168           1 :                         ReportError(
     169             :                             CE_Failure, CPLE_NotSupported,
     170             :                             "STAC-GeoParquet profile is only compatible "
     171             :                             "with --output-crs=EPSG:4326");
     172           1 :                         return false;
     173             :                     }
     174             :                 }
     175           7 :                 if (m_crs.empty())
     176           7 :                     m_crs = "EPSG:4326";
     177             :             }
     178          31 :             return true;
     179             :         });
     180         218 : }
     181             : 
     182             : /************************************************************************/
     183             : /*                 GDALRasterIndexAlgorithm::RunImpl()                  */
     184             : /************************************************************************/
     185             : 
     186          29 : bool GDALRasterIndexAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     187             :                                        void *pProgressData)
     188             : {
     189          58 :     CPLStringList aosSources;
     190          59 :     for (auto &srcDS : m_inputDatasets)
     191             :     {
     192          30 :         CPLAssert(!srcDS.GetDatasetRef());
     193          30 :         aosSources.push_back(srcDS.GetName());
     194             :     }
     195             : 
     196          58 :     auto setupRet = SetupOutputDataset();
     197          29 :     if (!setupRet.outDS)
     198           1 :         return false;
     199             : 
     200          28 :     if (!SetDefaultOutputLayerNameIfNeeded(setupRet.outDS))
     201           1 :         return false;
     202             : 
     203          54 :     CPLStringList aosOptions;
     204          27 :     aosOptions.push_back("--invoked-from-gdal-raster-index");
     205             : 
     206          27 :     if (m_profile != PROFILE_NONE)
     207             :     {
     208           7 :         aosOptions.push_back("-profile");
     209           7 :         aosOptions.push_back(m_profile);
     210             : 
     211           7 :         if (!m_baseUrl.empty())
     212             :         {
     213           1 :             aosOptions.push_back("--base-url");
     214           1 :             aosOptions.push_back(m_baseUrl);
     215             :         }
     216             : 
     217           7 :         aosOptions.push_back("--id-metadata-item");
     218           7 :         aosOptions.push_back(m_idMetadataItem);
     219             : 
     220           7 :         aosOptions.push_back("--id-method");
     221           7 :         aosOptions.push_back(m_idMethod);
     222             :     }
     223             : 
     224          27 :     if (m_skipErrors)
     225             :     {
     226           2 :         aosOptions.push_back("-skip_errors");
     227             :     }
     228          27 :     if (m_recursive)
     229             :     {
     230           1 :         aosOptions.push_back("-recursive");
     231             :     }
     232          29 :     for (const std::string &s : m_filenameFilter)
     233             :     {
     234           2 :         aosOptions.push_back("-filename_filter");
     235           2 :         aosOptions.push_back(s);
     236             :     }
     237          27 :     if (m_minPixelSize > 0)
     238             :     {
     239           2 :         aosOptions.push_back("-min_pixel_size");
     240           2 :         aosOptions.push_back(CPLSPrintf("%.17g", m_minPixelSize));
     241             :     }
     242          27 :     if (m_maxPixelSize > 0)
     243             :     {
     244           0 :         aosOptions.push_back("-max_pixel_size");
     245           0 :         aosOptions.push_back(CPLSPrintf("%.17g", m_maxPixelSize));
     246             :     }
     247             : 
     248          27 :     if (!m_outputLayerName.empty())
     249             :     {
     250          27 :         aosOptions.push_back("-lyr_name");
     251          27 :         aosOptions.push_back(m_outputLayerName);
     252             :     }
     253             : 
     254          27 :     aosOptions.push_back("-tileindex");
     255          27 :     aosOptions.push_back(m_locationName);
     256             : 
     257          27 :     if (m_writeAbsolutePaths)
     258             :     {
     259           2 :         aosOptions.push_back("-write_absolute_path");
     260             :     }
     261          27 :     if (m_crs.empty())
     262             :     {
     263          15 :         if (m_sourceCrsName.empty())
     264          15 :             aosOptions.push_back("-skip_different_projection");
     265             :     }
     266             :     else
     267             :     {
     268          12 :         aosOptions.push_back("-t_srs");
     269          12 :         aosOptions.push_back(m_crs);
     270             :     }
     271          27 :     if (!m_sourceCrsName.empty())
     272             :     {
     273           1 :         aosOptions.push_back("-src_srs_name");
     274           1 :         aosOptions.push_back(m_sourceCrsName);
     275             : 
     276           1 :         aosOptions.push_back("-src_srs_format");
     277           1 :         aosOptions.push_back(CPLString(m_sourceCrsFormat).toupper());
     278             :     }
     279             : 
     280          28 :     for (const std::string &s : m_metadata)
     281             :     {
     282           1 :         aosOptions.push_back("-mo");
     283           1 :         aosOptions.push_back(s);
     284             :     }
     285             : 
     286          27 :     if (!AddExtraOptions(aosOptions))
     287           2 :         return false;
     288             : 
     289             :     std::unique_ptr<GDALTileIndexOptions, decltype(&GDALTileIndexOptionsFree)>
     290             :         options(GDALTileIndexOptionsNew(aosOptions.List(), nullptr),
     291          25 :                 GDALTileIndexOptionsFree);
     292             : 
     293          25 :     if (options)
     294             :     {
     295          25 :         GDALTileIndexOptionsSetProgress(options.get(), pfnProgress,
     296             :                                         pProgressData);
     297             :     }
     298             : 
     299             :     const bool ret =
     300          50 :         options && GDALTileIndexInternal(m_outputDataset.GetName().c_str(),
     301             :                                          GDALDataset::ToHandle(setupRet.outDS),
     302             :                                          OGRLayer::ToHandle(setupRet.layer),
     303          25 :                                          aosSources.size(), aosSources.List(),
     304          50 :                                          options.get(), nullptr) != nullptr;
     305             : 
     306          25 :     if (ret && setupRet.newDS)
     307             :     {
     308          21 :         m_outputDataset.Set(std::move(setupRet.newDS));
     309             :     }
     310             : 
     311          25 :     return ret;
     312             : }
     313             : 
     314             : //! @endcond

Generated by: LCOV version 1.14