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

Generated by: LCOV version 1.14