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