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 50 : GDALVectorSimplifyCoverageAlgorithm::GDALVectorSimplifyCoverageAlgorithm(
31 50 : bool standaloneStep)
32 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
33 50 : standaloneStep)
34 : {
35 50 : AddActiveLayerArg(&m_activeLayer);
36 : AddArg("tolerance", 0, _("Distance tolerance for simplification."),
37 100 : &m_opts.tolerance)
38 50 : .SetPositional()
39 50 : .SetRequired()
40 50 : .SetMinValueIncluded(0);
41 : AddArg("preserve-boundary", 0,
42 : _("Whether the exterior boundary should be preserved."),
43 50 : &m_opts.preserveBoundary);
44 50 : }
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 : int 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() 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 8 : m_poGeosResultAsCollection = GEOSCoverageSimplifyVW_r(
114 4 : m_poGeosContext, coll, m_opts.tolerance, m_opts.preserveBoundary);
115 4 : GEOSGeom_destroy_r(m_poGeosContext, coll);
116 :
117 4 : 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 8 : bool GDALVectorSimplifyCoverageAlgorithm::RunStep(
130 : GDALPipelineStepRunContext &ctxt)
131 : {
132 8 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
133 16 : auto poDstDS = std::make_unique<GDALVectorNonStreamingAlgorithmDataset>();
134 :
135 16 : GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
136 :
137 18 : for (auto &&poSrcLayer : poSrcDS->GetLayers())
138 : {
139 14 : if (m_activeLayer.empty() ||
140 4 : m_activeLayer == poSrcLayer->GetDescription())
141 : {
142 7 : progressHelper.AddProcessedLayer(*poSrcLayer);
143 : }
144 : else
145 : {
146 3 : progressHelper.AddPassThroughLayer(*poSrcLayer);
147 : }
148 : }
149 :
150 8 : if (!progressHelper.HasProcessedLayers())
151 : {
152 1 : ReportError(CE_Failure, CPLE_AppDefined,
153 : "Specified layer '%s' was not found",
154 : m_activeLayer.c_str());
155 1 : return false;
156 : }
157 :
158 13 : for (auto [poSrcLayer, bProcessed, layerProgressFunc, layerProgressData] :
159 17 : progressHelper)
160 : {
161 8 : if (bProcessed)
162 : {
163 7 : constexpr int geomFieldIndex = 0; // TODO: parametrize
164 : auto poLayer =
165 : std::make_unique<GDALVectorSimplifyCoverageOutputLayer>(
166 7 : *poSrcLayer, geomFieldIndex, m_opts);
167 :
168 7 : if (!poDstDS->AddProcessedLayer(std::move(poLayer),
169 : layerProgressFunc,
170 : layerProgressData.get()))
171 : {
172 3 : return false;
173 : }
174 : }
175 : else
176 : {
177 1 : poDstDS->AddPassThroughLayer(*poSrcLayer);
178 : }
179 : }
180 :
181 4 : m_outputDataset.Set(std::move(poDstDS));
182 :
183 4 : return true;
184 : }
185 :
186 : #else
187 :
188 : bool GDALVectorSimplifyCoverageAlgorithm::RunStep(GDALPipelineStepRunContext &)
189 : {
190 : ReportError(CE_Failure, CPLE_AppDefined,
191 : "%s requires GDAL to be built against version 3.12 or later of "
192 : "the GEOS library.",
193 : NAME);
194 : return false;
195 : }
196 : #endif // HAVE_GEOS
197 :
198 : GDALVectorSimplifyCoverageAlgorithmStandalone::
199 : ~GDALVectorSimplifyCoverageAlgorithmStandalone() = default;
200 :
201 : //! @endcond
|