Coverage Report

Created: 2026-02-14 06:52

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