LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_zonal_stats.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 87 94 92.6 %
Date: 2025-10-21 22:35:35 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster zonal-stats" subcommand
       5             :  * Author:   Dan Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_zonal_stats.h"
      14             : 
      15             : #include "gdal_alg.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_utils.h"
      18             : #include "ogrsf_frmts.h"
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*    GDALRasterZonalStatsAlgorithm::GDALRasterZonalStatsAlgorithm()    */
      28             : /************************************************************************/
      29             : 
      30         148 : GDALRasterZonalStatsAlgorithm::GDALRasterZonalStatsAlgorithm(bool bStandalone)
      31             :     : GDALPipelineStepAlgorithm(
      32             :           NAME, DESCRIPTION, HELP_URL,
      33           0 :           ConstructorOptions()
      34         148 :               .SetStandaloneStep(bStandalone)
      35         296 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      36             : {
      37         148 :     if (bStandalone)
      38             :     {
      39         134 :         AddRasterInputArgs(false, false);
      40         134 :         AddVectorOutputArgs(false, false);
      41             :     }
      42             : 
      43         148 :     constexpr const char *ZONES_BAND_OR_LAYER = "BAND_OR_LAYER";
      44             : 
      45         148 :     AddBandArg(&m_bands);
      46         296 :     AddArg("zones", 0, _("Dataset containing zone definitions"), &m_zones)
      47         148 :         .SetRequired();
      48             :     AddArg("zones-band", 0, _("Band from which zones should be read"),
      49         296 :            &m_zonesBand)
      50         148 :         .SetMutualExclusionGroup(ZONES_BAND_OR_LAYER);
      51             :     AddArg("zones-layer", 0, _("Layer from which zones should be read"),
      52         296 :            &m_zonesLayer)
      53         148 :         .SetMutualExclusionGroup(ZONES_BAND_OR_LAYER);
      54         148 :     AddArg("weights", 0, _("Weighting raster dataset"), &m_weights);
      55             :     AddArg("weights-band", 0, _("Band from which weights should be read"),
      56         296 :            &m_weightsBand)
      57         148 :         .SetDefault(1);
      58             :     AddArg(
      59             :         "pixels", 0,
      60             :         _("Method to determine which pixels are included in stat calculation."),
      61         296 :         &m_pixels)
      62         148 :         .SetChoices("default", "fractional", "all-touched");
      63         296 :     AddArg("stat", 0, _("Statistic(s) to compute for each zone"), &m_stats)
      64         148 :         .SetRequired()
      65         148 :         .SetMinValueIncluded(1)
      66             :         .SetChoices("center_x", "center_y", "count", "coverage", "frac", "max",
      67             :                     "max_center_x", "max_center_y", "mean", "median", "min",
      68             :                     "minority", "min_center_x", "min_center_y", "mode", "stdev",
      69             :                     "sum", "unique", "values", "variance", "variety",
      70             :                     "weighted_mean", "weighted_stdev", "weighted_sum",
      71         148 :                     "weighted_variance", "weights");
      72             :     AddArg("include-field", 0,
      73             :            _("Fields from polygon zones to include in output"),
      74         148 :            &m_includeFields);
      75             :     AddArg("strategy", 0,
      76             :            _("For polygon zones, whether to iterate over input features or "
      77             :              "raster chunks"),
      78         296 :            &m_strategy)
      79         148 :         .SetChoices("feature", "raster")
      80         148 :         .SetDefault("feature");
      81             :     AddMemorySizeArg(&m_memoryBytes, &m_memoryStr, "chunk-size",
      82         148 :                      _("Maximum size of raster chunks read into memory"));
      83         148 :     AddProgressArg();
      84         148 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                 GDALRasterZonalStatsAlgorithm::RunStep()             */
      88             : /************************************************************************/
      89             : 
      90         130 : bool GDALRasterZonalStatsAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
      91             :                                             void *pProgressData)
      92             : {
      93         130 :     GDALPipelineStepRunContext stepCtxt;
      94         130 :     stepCtxt.m_pfnProgress = pfnProgress;
      95         130 :     stepCtxt.m_pProgressData = pProgressData;
      96         130 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
      97             : }
      98             : 
      99             : template <typename T>
     100         157 : static std::string Join(const T &vec, const std::string &separator)
     101             : {
     102         314 :     std::stringstream ss;
     103         157 :     bool first = true;
     104         406 :     for (const auto &val : vec)
     105             :     {
     106             :         static_assert(!std::is_floating_point_v<decltype(val)>,
     107             :                       "Precision would be lost.");
     108             : 
     109         249 :         if (first)
     110             :         {
     111         157 :             first = false;
     112             :         }
     113             :         else
     114             :         {
     115          92 :             ss << separator;
     116             :         }
     117         249 :         ss << val;
     118             :     }
     119         314 :     return ss.str();
     120             : }
     121             : 
     122         131 : bool GDALRasterZonalStatsAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     123             : {
     124         131 :     GDALDataset *zones = m_zones.GetDatasetRef();
     125             : 
     126             :     /// Prepare the output dataset.
     127             :     /// This section copy-pasted from gdal raster as-features.
     128             :     /// Avoid duplication.
     129         131 :     GDALDataset *poDstDS = m_outputDataset.GetDatasetRef();
     130         131 :     std::unique_ptr<GDALDataset> poRetDS;
     131         131 :     if (!poDstDS)
     132             :     {
     133         129 :         std::string outputFilename = m_outputDataset.GetName();
     134         129 :         if (m_standaloneStep)
     135             :         {
     136         128 :             if (m_format.empty())
     137             :             {
     138             :                 const auto aosFormats =
     139             :                     CPLStringList(GDALGetOutputDriversForDatasetName(
     140           2 :                         m_outputDataset.GetName().c_str(), GDAL_OF_VECTOR,
     141             :                         /* bSingleMatch = */ true,
     142           2 :                         /* bWarn = */ true));
     143           2 :                 if (aosFormats.size() != 1)
     144             :                 {
     145           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     146             :                                 "Cannot guess driver for %s",
     147           0 :                                 m_outputDataset.GetName().c_str());
     148           0 :                     return false;
     149             :                 }
     150           2 :                 m_format = aosFormats[0];
     151             :             }
     152             :         }
     153             :         else
     154             :         {
     155           1 :             m_format = "MEM";
     156             :         }
     157             : 
     158             :         auto poDriver =
     159         129 :             GetGDALDriverManager()->GetDriverByName(m_format.c_str());
     160         129 :         if (!poDriver)
     161             :         {
     162             :             // shouldn't happen given checks done in GDALAlgorithm
     163           0 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     164             :                         m_format.c_str());
     165           0 :             return false;
     166             :         }
     167             : 
     168         129 :         poRetDS.reset(
     169             :             poDriver->Create(outputFilename.c_str(), 0, 0, 0, GDT_Unknown,
     170         258 :                              CPLStringList(m_creationOptions).List()));
     171         129 :         if (!poRetDS)
     172           0 :             return false;
     173             : 
     174         129 :         poDstDS = poRetDS.get();
     175             :     }
     176             :     /// End prep of output dataset.
     177             : 
     178         131 :     GDALDataset *src = m_inputDataset.front().GetDatasetRef();
     179             : 
     180         131 :     CPLStringList aosOptions;
     181         131 :     if (!m_bands.empty())
     182             :     {
     183          21 :         aosOptions.AddNameValue("BANDS", Join(m_bands, ",").c_str());
     184             :     }
     185         131 :     if (!m_includeFields.empty())
     186             :     {
     187             :         aosOptions.AddNameValue("INCLUDE_FIELDS",
     188           5 :                                 Join(m_includeFields, ",").c_str());
     189             :     }
     190         131 :     aosOptions.AddNameValue("PIXEL_INTERSECTION", m_pixels.c_str());
     191         131 :     if (m_memoryBytes != 0)
     192             :     {
     193             :         aosOptions.AddNameValue("RASTER_CHUNK_SIZE_BYTES",
     194         131 :                                 std::to_string(m_memoryBytes).c_str());
     195             :     }
     196         131 :     aosOptions.AddNameValue("STATS", Join(m_stats, ",").c_str());
     197         131 :     aosOptions.AddNameValue("STRATEGY", (m_strategy + "_SEQUENTIAL").c_str());
     198         131 :     if (m_weightsBand != 0)
     199             :     {
     200             :         aosOptions.AddNameValue("WEIGHTS_BAND",
     201         131 :                                 std::to_string(m_weightsBand).c_str());
     202             :     }
     203         131 :     if (m_zonesBand != 0)
     204             :     {
     205             :         aosOptions.AddNameValue("ZONES_BAND",
     206           1 :                                 std::to_string(m_zonesBand).c_str());
     207             :     }
     208         131 :     if (!m_zonesLayer.empty())
     209             :     {
     210           1 :         aosOptions.AddNameValue("ZONES_LAYER", m_zonesLayer.c_str());
     211             :     }
     212         149 :     for (const std::string &lco : m_layerCreationOptions)
     213             :     {
     214          18 :         aosOptions.AddString(std::string("LCO_").append(lco));
     215             :     }
     216             : 
     217         131 :     if (poRetDS)
     218             :     {
     219         129 :         m_outputDataset.Set(std::move(poRetDS));
     220             :     }
     221             : 
     222         131 :     return GDALZonalStats(src, m_weights.GetDatasetRef(), zones, poDstDS,
     223         131 :                           aosOptions.List(), ctxt.m_pfnProgress,
     224         131 :                           ctxt.m_pProgressData) == CE_None;
     225             : }
     226             : 
     227             : GDALRasterZonalStatsAlgorithmStandalone::
     228             :     ~GDALRasterZonalStatsAlgorithmStandalone() = default;
     229             : 
     230             : //! @endcond

Generated by: LCOV version 1.14