LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_footprint.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 166 169 98.2 %
Date: 2025-09-10 17:48:50 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster footprint" 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_footprint.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : 
      17             : #include "gdal_priv.h"
      18             : #include "gdal_utils.h"
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*      GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm()    */
      28             : /************************************************************************/
      29             : 
      30          59 : GDALRasterFootprintAlgorithm::GDALRasterFootprintAlgorithm(bool standaloneStep)
      31             :     : GDALPipelineStepAlgorithm(
      32             :           NAME, DESCRIPTION, HELP_URL,
      33           0 :           ConstructorOptions()
      34          59 :               .SetStandaloneStep(standaloneStep)
      35         118 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      36             : {
      37          59 :     AddProgressArg();
      38             : 
      39          59 :     if (standaloneStep)
      40             :     {
      41          38 :         AddOpenOptionsArg(&m_openOptions);
      42          38 :         AddInputFormatsArg(&m_inputFormats)
      43          76 :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
      44          38 :         AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
      45             : 
      46          38 :         AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
      47          38 :             .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      48             :         AddOutputFormatArg(&m_format, /* bStreamAllowed = */ false,
      49          38 :                            /* bGDALGAllowed = */ false)
      50             :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      51         114 :                              {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
      52          38 :         AddCreationOptionsArg(&m_creationOptions);
      53          38 :         AddLayerCreationOptionsArg(&m_layerCreationOptions);
      54          38 :         AddUpdateArg(&m_update)
      55          38 :             .SetHidden();  // needed for correct append execution
      56          38 :         AddAppendLayerArg(&m_appendLayer);
      57          38 :         AddOverwriteArg(&m_overwrite);
      58             :     }
      59             : 
      60          59 :     m_outputLayerName = "footprint";
      61             :     AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0, _("Output layer name"),
      62         118 :            &m_outputLayerName)
      63          59 :         .SetDefault(m_outputLayerName);
      64             : 
      65          59 :     AddBandArg(&m_bands);
      66             :     AddArg("combine-bands", 0,
      67             :            _("Defines how the mask bands of the selected bands are combined to "
      68             :              "generate a single mask band, before being vectorized."),
      69         118 :            &m_combineBands)
      70          59 :         .SetChoices("union", "intersection")
      71          59 :         .SetDefault(m_combineBands);
      72             :     AddArg("overview", 0, _("Which overview level of source file must be used"),
      73         118 :            &m_overview)
      74         118 :         .SetMutualExclusionGroup("overview-srcnodata")
      75          59 :         .SetMinValueIncluded(0);
      76             :     AddArg("src-nodata", 0, _("Set nodata values for input bands."),
      77         118 :            &m_srcNoData)
      78          59 :         .SetMinCount(1)
      79          59 :         .SetRepeatedArgAllowed(false)
      80          59 :         .SetMutualExclusionGroup("overview-srcnodata");
      81             :     AddArg("coordinate-system", 0, _("Target coordinate system"),
      82         118 :            &m_coordinateSystem)
      83          59 :         .SetChoices("georeferenced", "pixel");
      84         118 :     AddArg("dst-crs", 0, _("Destination CRS"), &m_dstCrs)
      85         118 :         .SetIsCRSArg()
      86          59 :         .AddHiddenAlias("t_srs");
      87             :     AddArg("split-multipolygons", 0,
      88             :            _("Whether to split multipolygons as several features each with one "
      89             :              "single polygon"),
      90          59 :            &m_splitMultiPolygons);
      91             :     AddArg("convex-hull", 0,
      92             :            _("Whether to compute the convex hull of the footprint"),
      93          59 :            &m_convexHull);
      94             :     AddArg("densify-distance", 0,
      95             :            _("Maximum distance between 2 consecutive points of the output "
      96             :              "geometry."),
      97         118 :            &m_densifyVal)
      98          59 :         .SetMinValueExcluded(0);
      99             :     AddArg(
     100             :         "simplify-tolerance", 0,
     101             :         _("Tolerance used to merge consecutive points of the output geometry."),
     102         118 :         &m_simplifyVal)
     103          59 :         .SetMinValueExcluded(0);
     104             :     AddArg("min-ring-area", 0, _("Minimum value for the area of a ring"),
     105         118 :            &m_minRingArea)
     106          59 :         .SetMinValueIncluded(0);
     107             :     AddArg("max-points", 0,
     108         118 :            _("Maximum number of points of each output geometry"), &m_maxPoints)
     109          59 :         .SetDefault(m_maxPoints)
     110             :         .AddValidationAction(
     111           7 :             [this]()
     112             :             {
     113           2 :                 if (m_maxPoints != "unlimited")
     114             :                 {
     115           2 :                     char *endptr = nullptr;
     116             :                     const auto nVal =
     117           2 :                         std::strtoll(m_maxPoints.c_str(), &endptr, 10);
     118           3 :                     if (nVal < 4 ||
     119           1 :                         endptr != m_maxPoints.c_str() + m_maxPoints.size())
     120             :                     {
     121           1 :                         ReportError(
     122             :                             CE_Failure, CPLE_IllegalArg,
     123             :                             "Value of 'max-points' should be a positive "
     124             :                             "integer greater or equal to 4, or 'unlimited'");
     125           1 :                         return false;
     126             :                     }
     127             :                 }
     128           1 :                 return true;
     129          59 :             });
     130             :     AddArg("location-field", 0,
     131             :            _("Name of the field where the path of the input dataset will be "
     132             :              "stored."),
     133         118 :            &m_locationField)
     134          59 :         .SetDefault(m_locationField)
     135          59 :         .SetMutualExclusionGroup("location");
     136             :     AddArg("no-location-field", 0,
     137             :            _("Disable creating a field with the path of the input dataset"),
     138         118 :            &m_noLocation)
     139          59 :         .SetMutualExclusionGroup("location");
     140          59 :     AddAbsolutePathArg(&m_writeAbsolutePaths);
     141             : 
     142          59 :     AddValidationAction(
     143         108 :         [this]
     144             :         {
     145          35 :             if (m_inputDataset.size() == 1)
     146             :             {
     147          34 :                 if (auto poSrcDS = m_inputDataset[0].GetDatasetRef())
     148             :                 {
     149             :                     const int nOvrCount =
     150          34 :                         poSrcDS->GetRasterBand(1)->GetOverviewCount();
     151          37 :                     if (m_overview >= 0 && poSrcDS->GetRasterCount() > 0 &&
     152           3 :                         m_overview >= nOvrCount)
     153             :                     {
     154           2 :                         if (nOvrCount == 0)
     155             :                         {
     156           1 :                             ReportError(
     157             :                                 CE_Failure, CPLE_IllegalArg,
     158             :                                 "Source dataset has no overviews. "
     159             :                                 "Argument 'overview' should not be specified.");
     160             :                         }
     161             :                         else
     162             :                         {
     163           1 :                             ReportError(
     164             :                                 CE_Failure, CPLE_IllegalArg,
     165             :                                 "Source dataset has only %d overview levels. "
     166             :                                 "'overview' "
     167             :                                 "value should be strictly lower than this "
     168             :                                 "number.",
     169             :                                 nOvrCount);
     170             :                         }
     171           2 :                         return false;
     172             :                     }
     173             :                 }
     174             :             }
     175          33 :             return true;
     176             :         });
     177          59 : }
     178             : 
     179             : /************************************************************************/
     180             : /*                 GDALRasterFootprintAlgorithm::RunImpl()              */
     181             : /************************************************************************/
     182             : 
     183          30 : bool GDALRasterFootprintAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     184             :                                            void *pProgressData)
     185             : {
     186          30 :     GDALPipelineStepRunContext stepCtxt;
     187          30 :     stepCtxt.m_pfnProgress = pfnProgress;
     188          30 :     stepCtxt.m_pProgressData = pProgressData;
     189          30 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                 GDALRasterFootprintAlgorithm::RunStep()              */
     194             : /************************************************************************/
     195             : 
     196          31 : bool GDALRasterFootprintAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     197             : {
     198          31 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     199          31 :     CPLAssert(poSrcDS);
     200             : 
     201          62 :     CPLStringList aosOptions;
     202             : 
     203          62 :     std::string outputFilename;
     204          31 :     if (m_standaloneStep)
     205             :     {
     206          30 :         outputFilename = m_outputDataset.GetName();
     207          30 :         if (!m_format.empty())
     208             :         {
     209          25 :             aosOptions.AddString("-of");
     210          25 :             aosOptions.AddString(m_format.c_str());
     211             :         }
     212             : 
     213          31 :         for (const auto &co : m_creationOptions)
     214             :         {
     215           1 :             aosOptions.push_back("-dsco");
     216           1 :             aosOptions.push_back(co.c_str());
     217             :         }
     218             : 
     219          31 :         for (const auto &co : m_layerCreationOptions)
     220             :         {
     221           1 :             aosOptions.push_back("-lco");
     222           1 :             aosOptions.push_back(co.c_str());
     223             :         }
     224             :     }
     225             :     else
     226             :     {
     227           1 :         if (GetGDALDriverManager()->GetDriverByName("GPKG"))
     228             :         {
     229           1 :             aosOptions.AddString("-of");
     230           1 :             aosOptions.AddString("GPKG");
     231             : 
     232             :             outputFilename =
     233           1 :                 CPLGenerateTempFilenameSafe("_footprint") + ".gpkg";
     234             :         }
     235             :         else
     236             :         {
     237           0 :             aosOptions.AddString("-of");
     238           0 :             aosOptions.AddString("MEM");
     239             :         }
     240             :     }
     241             : 
     242          37 :     for (int band : m_bands)
     243             :     {
     244           6 :         aosOptions.push_back("-b");
     245           6 :         aosOptions.push_back(CPLSPrintf("%d", band));
     246             :     }
     247             : 
     248          31 :     aosOptions.push_back("-combine_bands");
     249          31 :     aosOptions.push_back(m_combineBands);
     250             : 
     251          31 :     if (m_overview >= 0)
     252             :     {
     253           1 :         aosOptions.push_back("-ovr");
     254           1 :         aosOptions.push_back(CPLSPrintf("%d", m_overview));
     255             :     }
     256             : 
     257          31 :     if (!m_srcNoData.empty())
     258             :     {
     259           2 :         aosOptions.push_back("-srcnodata");
     260           4 :         std::string s;
     261           5 :         for (double v : m_srcNoData)
     262             :         {
     263           3 :             if (!s.empty())
     264           1 :                 s += " ";
     265           3 :             s += CPLSPrintf("%.17g", v);
     266             :         }
     267           2 :         aosOptions.push_back(s);
     268             :     }
     269             : 
     270          31 :     if (m_coordinateSystem == "pixel")
     271             :     {
     272           1 :         aosOptions.push_back("-t_cs");
     273           1 :         aosOptions.push_back("pixel");
     274             :     }
     275          30 :     else if (m_coordinateSystem == "georeferenced")
     276             :     {
     277           1 :         aosOptions.push_back("-t_cs");
     278           1 :         aosOptions.push_back("georef");
     279             :     }
     280             : 
     281          31 :     if (!m_dstCrs.empty())
     282             :     {
     283           1 :         aosOptions.push_back("-t_srs");
     284           1 :         aosOptions.push_back(m_dstCrs);
     285             :     }
     286             : 
     287          31 :     if (GetArg(GDAL_ARG_NAME_OUTPUT_LAYER)->IsExplicitlySet())
     288             :     {
     289           5 :         aosOptions.push_back("-lyr_name");
     290           5 :         aosOptions.push_back(m_outputLayerName.c_str());
     291             :     }
     292             : 
     293          31 :     if (m_splitMultiPolygons)
     294           1 :         aosOptions.push_back("-split_polys");
     295             : 
     296          31 :     if (m_convexHull)
     297           1 :         aosOptions.push_back("-convex_hull");
     298             : 
     299          31 :     if (m_densifyVal > 0)
     300             :     {
     301           1 :         aosOptions.push_back("-densify");
     302           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_densifyVal));
     303             :     }
     304             : 
     305          31 :     if (m_simplifyVal > 0)
     306             :     {
     307           1 :         aosOptions.push_back("-simplify");
     308           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_simplifyVal));
     309             :     }
     310             : 
     311          31 :     aosOptions.push_back("-min_ring_area");
     312          31 :     aosOptions.push_back(CPLSPrintf("%.17g", m_minRingArea));
     313             : 
     314          31 :     aosOptions.push_back("-max_points");
     315          31 :     aosOptions.push_back(m_maxPoints);
     316             : 
     317          31 :     if (m_noLocation)
     318             :     {
     319           1 :         aosOptions.push_back("-no_location");
     320             :     }
     321             :     else
     322             :     {
     323          30 :         aosOptions.push_back("-location_field_name");
     324          30 :         aosOptions.push_back(m_locationField);
     325             : 
     326          30 :         if (m_writeAbsolutePaths)
     327           1 :             aosOptions.push_back("-write_absolute_path");
     328             :     }
     329             : 
     330          31 :     bool bOK = false;
     331             :     std::unique_ptr<GDALFootprintOptions, decltype(&GDALFootprintOptionsFree)>
     332             :         psOptions{GDALFootprintOptionsNew(aosOptions.List(), nullptr),
     333          31 :                   GDALFootprintOptionsFree};
     334          31 :     if (psOptions)
     335             :     {
     336          31 :         GDALFootprintOptionsSetProgress(psOptions.get(), ctxt.m_pfnProgress,
     337             :                                         ctxt.m_pProgressData);
     338             : 
     339          31 :         GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
     340             :         GDALDatasetH hDstDS =
     341          31 :             GDALDataset::ToHandle(m_outputDataset.GetDatasetRef());
     342          31 :         auto poRetDS = GDALDataset::FromHandle(GDALFootprint(
     343          31 :             outputFilename.c_str(), hDstDS, hSrcDS, psOptions.get(), nullptr));
     344          31 :         bOK = poRetDS != nullptr;
     345          31 :         if (bOK && !hDstDS)
     346             :         {
     347          28 :             if (poRetDS && !m_standaloneStep && !outputFilename.empty())
     348             :             {
     349           1 :                 bOK = poRetDS->FlushCache() == CE_None;
     350           1 :                 VSIUnlink(outputFilename.c_str());
     351           1 :                 poRetDS->MarkSuppressOnClose();
     352             :             }
     353          28 :             m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
     354             :         }
     355             :     }
     356             : 
     357          62 :     return bOK;
     358             : }
     359             : 
     360             : GDALRasterFootprintAlgorithmStandalone::
     361             :     ~GDALRasterFootprintAlgorithmStandalone() = default;
     362             : 
     363             : //! @endcond

Generated by: LCOV version 1.14