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_check_coverage.cpp
Line
Count
Source
1
/******************************************************************************
2
*
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector check-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_check_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
21
#include <cinttypes>
22
23
#ifndef _
24
0
#define _(x) (x)
25
#endif
26
27
//! @cond Doxygen_Suppress
28
29
GDALVectorCheckCoverageAlgorithm::GDALVectorCheckCoverageAlgorithm(
30
    bool standaloneStep)
31
0
    : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
32
0
                                      standaloneStep)
33
0
{
34
0
    AddArg("include-valid", 0,
35
0
           _("Include valid inputs in output, with empty geometry"),
36
0
           &m_includeValid);
37
38
0
    AddArg("geometry-field", 0, _("Name of geometry field to check"),
39
0
           &m_geomField);
40
41
0
    AddArg("maximum-gap-width", 0, _("Maximum width of a gap to be flagged"),
42
0
           &m_maximumGapWidth)
43
0
        .SetMinValueIncluded(0);
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 GDALVectorCheckCoverageOutputLayer final
51
    : public GDALGeosNonStreamingAlgorithmLayer
52
{
53
  public:
54
    explicit GDALVectorCheckCoverageOutputLayer(OGRLayer &srcLayer,
55
                                                int geomFieldIndex,
56
                                                const std::string &name,
57
                                                double maximumGapWidth,
58
                                                bool includeValid)
59
        : GDALGeosNonStreamingAlgorithmLayer(srcLayer, geomFieldIndex),
60
          m_defn(OGRFeatureDefn::CreateFeatureDefn(name.c_str())),
61
          m_maximumGapWidth(maximumGapWidth), m_includeValid(includeValid)
62
    {
63
        m_defn->Reference();
64
65
        const OGRFeatureDefn *poSrcLayerDefn = srcLayer.GetLayerDefn();
66
        m_defn->SetGeomType(wkbMultiLineString);
67
        m_defn->GetGeomFieldDefn(0)->SetSpatialRef(
68
            poSrcLayerDefn->GetGeomFieldDefn(geomFieldIndex)->GetSpatialRef());
69
    }
70
71
    ~GDALVectorCheckCoverageOutputLayer() override;
72
73
    const OGRFeatureDefn *GetLayerDefn() const override
74
    {
75
        return m_defn;
76
    }
77
78
    int TestCapability(const char *) const override
79
    {
80
        return false;
81
    }
82
83
    bool PolygonsOnly() const override
84
    {
85
        return true;
86
    }
87
88
    bool SkipEmpty() const override
89
    {
90
        return !m_includeValid;
91
    }
92
93
    bool ProcessGeos() override
94
    {
95
        // Perform coverage checking
96
        GEOSGeometry *coll = GEOSGeom_createCollection_r(
97
            m_poGeosContext, GEOS_GEOMETRYCOLLECTION, m_apoGeosInputs.data(),
98
            static_cast<unsigned int>(m_apoGeosInputs.size()));
99
100
        if (coll == nullptr)
101
        {
102
            return false;
103
        }
104
105
        m_apoGeosInputs.clear();
106
107
        int geos_result =
108
            GEOSCoverageIsValid_r(m_poGeosContext, coll, m_maximumGapWidth,
109
                                  &m_poGeosResultAsCollection);
110
        GEOSGeom_destroy_r(m_poGeosContext, coll);
111
112
        CPLDebug("CoverageIsValid", "%d", geos_result);
113
114
        return geos_result != 2;
115
    }
116
117
  private:
118
    OGRFeatureDefn *m_defn;
119
    const double m_maximumGapWidth;
120
    const bool m_includeValid;
121
122
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorCheckCoverageOutputLayer)
123
};
124
125
GDALVectorCheckCoverageOutputLayer::~GDALVectorCheckCoverageOutputLayer()
126
{
127
    if (m_defn != nullptr)
128
    {
129
        m_defn->Release();
130
    }
131
}
132
133
bool GDALVectorCheckCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
134
{
135
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
136
    auto poDstDS = std::make_unique<GDALVectorNonStreamingAlgorithmDataset>();
137
138
    const bool bSingleLayerOutput = m_inputLayerNames.empty()
139
                                        ? poSrcDS->GetLayerCount() == 1
140
                                        : m_inputLayerNames.size() == 1;
141
142
    GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
143
144
    for (auto &&poSrcLayer : poSrcDS->GetLayers())
145
    {
146
        if (m_inputLayerNames.empty() ||
147
            std::find(m_inputLayerNames.begin(), m_inputLayerNames.end(),
148
                      poSrcLayer->GetDescription()) != m_inputLayerNames.end())
149
        {
150
            const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
151
            if (poSrcLayerDefn->GetGeomFieldCount() == 0)
152
            {
153
                if (m_inputLayerNames.empty())
154
                    continue;
155
                ReportError(CE_Failure, CPLE_AppDefined,
156
                            "Specified layer '%s' has no geometry field",
157
                            poSrcLayer->GetDescription());
158
                return false;
159
            }
160
161
            progressHelper.AddProcessedLayer(*poSrcLayer);
162
        }
163
    }
164
165
    for ([[maybe_unused]] auto [poSrcLayer, bProcessed, layerProgressFunc,
166
                                layerProgressData] : progressHelper)
167
    {
168
        const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
169
        const int geomFieldIndex =
170
            m_geomField.empty()
171
                ? 0
172
                : poSrcLayerDefn->GetGeomFieldIndex(m_geomField.c_str());
173
174
        if (geomFieldIndex == -1)
175
        {
176
            ReportError(CE_Failure, CPLE_AppDefined,
177
                        "Specified geometry field '%s' does not exist in "
178
                        "layer '%s'",
179
                        m_geomField.c_str(), poSrcLayer->GetDescription());
180
            return false;
181
        }
182
183
        std::string layerName = bSingleLayerOutput
184
                                    ? "invalid_edge"
185
                                    : std::string("invalid_edge_")
186
                                          .append(poSrcLayer->GetDescription());
187
188
        auto poLayer = std::make_unique<GDALVectorCheckCoverageOutputLayer>(
189
            *poSrcLayer, geomFieldIndex, layerName, m_maximumGapWidth,
190
            m_includeValid);
191
192
        if (!poDstDS->AddProcessedLayer(std::move(poLayer), layerProgressFunc,
193
                                        layerProgressData.get()))
194
        {
195
            return false;
196
        }
197
    }
198
199
    m_outputDataset.Set(std::move(poDstDS));
200
201
    return true;
202
}
203
204
#else
205
206
bool GDALVectorCheckCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &)
207
0
{
208
0
    ReportError(CE_Failure, CPLE_AppDefined,
209
0
                "%s requires GDAL to be built against version 3.12 or later of "
210
0
                "the GEOS library.",
211
0
                NAME);
212
0
    return false;
213
0
}
214
#endif  // HAVE_GEOS
215
216
GDALVectorCheckCoverageAlgorithmStandalone::
217
0
    ~GDALVectorCheckCoverageAlgorithmStandalone() = default;
218
219
//! @endcond