Coverage Report

Created: 2026-02-14 06:52

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
    int 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() 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
        m_poGeosResultAsCollection = GEOSCoverageSimplifyVW_r(
114
            m_poGeosContext, coll, m_opts.tolerance, m_opts.preserveBoundary);
115
        GEOSGeom_destroy_r(m_poGeosContext, coll);
116
117
        return m_poGeosResultAsCollection != nullptr;
118
    }
119
120
  private:
121
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorSimplifyCoverageOutputLayer)
122
123
    const GDALVectorSimplifyCoverageAlgorithm::Options &m_opts;
124
};
125
126
GDALVectorSimplifyCoverageOutputLayer::
127
    ~GDALVectorSimplifyCoverageOutputLayer() = default;
128
129
bool GDALVectorSimplifyCoverageAlgorithm::RunStep(
130
    GDALPipelineStepRunContext &ctxt)
131
{
132
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
133
    auto poDstDS = std::make_unique<GDALVectorNonStreamingAlgorithmDataset>();
134
135
    GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
136
137
    for (auto &&poSrcLayer : poSrcDS->GetLayers())
138
    {
139
        if (m_activeLayer.empty() ||
140
            m_activeLayer == poSrcLayer->GetDescription())
141
        {
142
            progressHelper.AddProcessedLayer(*poSrcLayer);
143
        }
144
        else
145
        {
146
            progressHelper.AddPassThroughLayer(*poSrcLayer);
147
        }
148
    }
149
150
    if (!progressHelper.HasProcessedLayers())
151
    {
152
        ReportError(CE_Failure, CPLE_AppDefined,
153
                    "Specified layer '%s' was not found",
154
                    m_activeLayer.c_str());
155
        return false;
156
    }
157
158
    for (auto [poSrcLayer, bProcessed, layerProgressFunc, layerProgressData] :
159
         progressHelper)
160
    {
161
        if (bProcessed)
162
        {
163
            constexpr int geomFieldIndex = 0;  // TODO: parametrize
164
            auto poLayer =
165
                std::make_unique<GDALVectorSimplifyCoverageOutputLayer>(
166
                    *poSrcLayer, geomFieldIndex, m_opts);
167
168
            if (!poDstDS->AddProcessedLayer(std::move(poLayer),
169
                                            layerProgressFunc,
170
                                            layerProgressData.get()))
171
            {
172
                return false;
173
            }
174
        }
175
        else
176
        {
177
            poDstDS->AddPassThroughLayer(*poSrcLayer);
178
        }
179
    }
180
181
    m_outputDataset.Set(std::move(poDstDS));
182
183
    return true;
184
}
185
186
#else
187
188
bool GDALVectorSimplifyCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &)
189
0
{
190
0
    ReportError(CE_Failure, CPLE_AppDefined,
191
0
                "%s requires GDAL to be built against version 3.12 or later of "
192
0
                "the GEOS library.",
193
0
                NAME);
194
0
    return false;
195
0
}
196
#endif  // HAVE_GEOS
197
198
GDALVectorSimplifyCoverageAlgorithmStandalone::
199
0
    ~GDALVectorSimplifyCoverageAlgorithmStandalone() = default;
200
201
//! @endcond