Coverage Report

Created: 2026-07-25 06:50

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