Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_simplify_coverage.cpp
Line
Count
Source
1
/******************************************************************************
2
*
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector simplify-coverage" subcommand
5
 * Author:   Daniel Baston
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, ISciences LLC
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_vector_simplify_coverage.h"
14
15
#include "cpl_error.h"
16
#include "gdal_priv.h"
17
#include "gdalalg_vector_geom.h"
18
#include "ogr_geometry.h"
19
#include "ogr_geos.h"
20
#include "ogrsf_frmts.h"
21
22
#include <cinttypes>
23
24
#ifndef _
25
0
#define _(x) (x)
26
#endif
27
28
//! @cond Doxygen_Suppress
29
30
GDALVectorSimplifyCoverageAlgorithm::GDALVectorSimplifyCoverageAlgorithm(
31
    bool standaloneStep)
32
0
    : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
33
0
                                      standaloneStep)
34
0
{
35
0
    AddActiveLayerArg(&m_activeLayer);
36
0
    AddArg("tolerance", 0, _("Distance tolerance for simplification."),
37
0
           &m_opts.tolerance)
38
0
        .SetPositional()
39
0
        .SetRequired()
40
0
        .SetMinValueIncluded(0);
41
0
    AddArg("preserve-boundary", 0,
42
0
           _("Whether the exterior boundary should be preserved."),
43
0
           &m_opts.preserveBoundary);
44
0
}
45
46
#if defined HAVE_GEOS &&                                                       \
47
    (GEOS_VERSION_MAJOR > 3 ||                                                 \
48
     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12))
49
50
class GDALVectorSimplifyCoverageOutputLayer final
51
    : public GDALGeosNonStreamingAlgorithmLayer
52
{
53
  public:
54
    GDALVectorSimplifyCoverageOutputLayer(
55
        OGRLayer &srcLayer, int geomFieldIndex,
56
        const GDALVectorSimplifyCoverageAlgorithm::Options &opts)
57
        : GDALGeosNonStreamingAlgorithmLayer(srcLayer, geomFieldIndex),
58
          m_opts(opts)
59
    {
60
    }
61
62
    ~GDALVectorSimplifyCoverageOutputLayer() override;
63
64
    const OGRFeatureDefn *GetLayerDefn() const override
65
    {
66
        return m_srcLayer.GetLayerDefn();
67
    }
68
69
    GIntBig GetFeatureCount(int bForce) override
70
    {
71
        if (!m_poAttrQuery && !m_poFilterGeom)
72
        {
73
            return m_srcLayer.GetFeatureCount(bForce);
74
        }
75
76
        return OGRLayer::GetFeatureCount(bForce);
77
    }
78
79
    bool TestCapability(const char *pszCap) const override
80
    {
81
        if (EQUAL(pszCap, OLCFastFeatureCount))
82
        {
83
            return m_srcLayer.TestCapability(pszCap);
84
        }
85
86
        return false;
87
    }
88
89
    bool PolygonsOnly() const override
90
    {
91
        return true;
92
    }
93
94
    bool SkipEmpty() const override
95
    {
96
        return false;
97
    }
98
99
    bool ProcessGeos(GDALProgressFunc pfnProgress, void *pProgressData) override
100
    {
101
        // Perform coverage simplification
102
        GEOSGeometry *coll = GEOSGeom_createCollection_r(
103
            m_poGeosContext, GEOS_GEOMETRYCOLLECTION, m_apoGeosInputs.data(),
104
            static_cast<unsigned int>(m_apoGeosInputs.size()));
105
106
        if (coll == nullptr)
107
        {
108
            return false;
109
        }
110
111
        m_apoGeosInputs.clear();
112
113
        GDALGEOSProgressReporter oReporter(m_poGeosContext, pfnProgress,
114
                                           pProgressData);
115
116
        m_poGeosResultAsCollection = GEOSCoverageSimplifyVW_r(
117
            m_poGeosContext, coll, m_opts.tolerance, m_opts.preserveBoundary);
118
        GEOSGeom_destroy_r(m_poGeosContext, coll);
119
120
        return m_poGeosResultAsCollection != nullptr;
121
    }
122
123
  private:
124
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorSimplifyCoverageOutputLayer)
125
126
    const GDALVectorSimplifyCoverageAlgorithm::Options &m_opts;
127
};
128
129
GDALVectorSimplifyCoverageOutputLayer::
130
    ~GDALVectorSimplifyCoverageOutputLayer() = default;
131
132
bool GDALVectorSimplifyCoverageAlgorithm::RunStep(
133
    GDALPipelineStepRunContext &ctxt)
134
{
135
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
136
    auto poDstDS =
137
        std::make_unique<GDALVectorNonStreamingAlgorithmDataset>(*poSrcDS);
138
139
    GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
140
141
    for (auto &&poSrcLayer : poSrcDS->GetLayers())
142
    {
143
        if ((m_activeLayer.empty() && poSrcLayer->GetGeomType() != wkbNone) ||
144
            m_activeLayer == poSrcLayer->GetDescription())
145
        {
146
            progressHelper.AddProcessedLayer(*poSrcLayer);
147
        }
148
        else
149
        {
150
            progressHelper.AddPassThroughLayer(*poSrcLayer);
151
        }
152
    }
153
154
    if (!m_activeLayer.empty() && !progressHelper.HasProcessedLayers())
155
    {
156
        ReportError(CE_Failure, CPLE_AppDefined,
157
                    "Specified layer '%s' was not found",
158
                    m_activeLayer.c_str());
159
        return false;
160
    }
161
162
    for (auto [poSrcLayer, bProcessed, layerProgressFunc, layerProgressData] :
163
         progressHelper)
164
    {
165
        if (bProcessed)
166
        {
167
            constexpr int geomFieldIndex = 0;  // TODO: parametrize
168
            auto poLayer =
169
                std::make_unique<GDALVectorSimplifyCoverageOutputLayer>(
170
                    *poSrcLayer, geomFieldIndex, m_opts);
171
172
            if (!poDstDS->AddProcessedLayer(std::move(poLayer),
173
                                            layerProgressFunc,
174
                                            layerProgressData.get()))
175
            {
176
                return false;
177
            }
178
        }
179
        else
180
        {
181
            poDstDS->AddPassThroughLayer(*poSrcLayer);
182
        }
183
    }
184
185
    m_outputDataset.Set(std::move(poDstDS));
186
187
    return true;
188
}
189
190
#else
191
192
bool GDALVectorSimplifyCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &)
193
0
{
194
0
    ReportError(CE_Failure, CPLE_AppDefined,
195
0
                "%s requires GDAL to be built against version 3.12 or later of "
196
0
                "the GEOS library.",
197
0
                NAME);
198
0
    return false;
199
0
}
200
#endif  // HAVE_GEOS
201
202
GDALVectorSimplifyCoverageAlgorithmStandalone::
203
0
    ~GDALVectorSimplifyCoverageAlgorithmStandalone() = default;
204
205
//! @endcond