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